Guessing Game on STM32 Using Rust
Today, it’s rare to meet a developer who hasn’t heard of Rust at least once. The first thing that impressed me was an article stating that the entire Cursor infrastructure is written in Rust for the sake of extreme performance, and as we can see, the language is handling this task brilliantly. The second thing - which motivated me not just to read about Rust but to actually start writing in it - was, of course, the hardware shortage. “640 KB is actually enough for everyone,” and my interest shifted to our “little brothers” - microcontrollers. That is where you can truly feel the sheer necessity for optimization.
Of course, my introduction to Rust, like everyone else’s, began with the classics: the Rust Book for beginners and Embedded Rust for those wishing to dive into embedded systems development. In the Rust Book, beginners are offered to write a simple “Guessing Game” program—an application that thinks of a random number while the “player” has to try and guess it. But simply reading isn’t as fun as getting your own bumps and bruises, so, armed with books, documentation, a still-alive StackOverflow, and Gemini as a mentor, I decided to merge these two worlds and make a Guessing Game on an STM32.
Below, I’d like to offer a brief guide (or just a code explanation) based on what I’ve managed to figure out.
Hardware Components
The first thing needed to bring this idea to life is, of course, the microcontroller itself. In my case, it’s the STM32L476RG (Nucleo).
Second, we need a screen where we will display the information. Ever since my days of messing around with Arduino, I still had a whole box of different gizmos lying around, including an LCD with an I2C adapter. This was chosen as the main display.
And lastly, a way to interact with the program. From that same kit, I took an IR receiver and a “Car MP3” remote control.
You’ll additionally need a breadboard for conveniently hooking up the IR receiver (along with a few jumper wires for the connections). In my case, the breadboard was also from the Arduino kit.
Connections
Connecting the STM32. My board required a USB A to mini-B cable. Next, we disconnect the board from the computer and move on to connecting the pins.
Connecting the LCD with an I2C adapter. There are 4 pins here: SDA (data transfer), SCL (clock pin), VCC (power), and GND (ground). On the STM32, the pins are usually well-labeled, so we look for the matching pins on the board: SCL/D15/PB9, SDA/D15/PB8, +3V3, and GND.
Wiring them together:
Display SDA to STM32 SDA
SCL to SCL
VCC to +3V3
GND to GND
Connecting the IR receiver. An IR receiver usually looks like an LED, but black and encased. Looking at its “front side”: the leftmost pin will be the data output pin, the middle is GND, and the right is VCC. Connect the left pin to PA0 on the STM32 (it might also be labeled as A0). Connect the middle one to GND on the board, and VCC to +3V3. This is where a breadboard might come in handy to share the common +3V3 line.
Code
For further work, Rust and Cargo must be installed (if they aren’t, follow the official guide for your OS here).
Special attention should be given to the .cargo/config.toml file, which specifies exactly how to run the program, with what flags, and for which platform. This allows us to launch the program later using just the cargo run command.
Let’s go through the files:
GameInput - represents a simple enum of the inputs supported by the game.
Game - the game module, the core of which is generating a random number and checking the game status.
LCD - the module for initializing the hd44780 driver and functions for interacting with the screen: clearing data, writing to the first line, and writing to the second line.
RC - the initialization of the infrared receiver and mapping of remote control commands (the digital code of each button was previously caught manually using debugging). The most complex function here is signal decoding. It uses delta time calculations for the decoding process and compares the button press time with a certain time interval relative to the last press to prevent button “sticking” (debouncing).
Other main steps implemented in the code:
initializing the infrared receiver module
creating an asynchronous task to decode the signal received from the remote
starting the game
capturing the “player’s” answer and displaying the game status on the screen (running in a loop)
using a Channel with a Mutex to transfer data between the remote control command and the game loop (to receive and compare the “player’s” answer)
Running the Game
clone the repository and navigate to the root folder of the project in the terminal
connect the board via USB
fire up cargo run in the terminal
look at the LCD, which should display “Guess 1 to 100”. If the display is lit up but shows nothing, you might need to tweak the potentiometer on the back of the display with a screwdriver until the letters become visible.
press buttons 0–9 on the remote, trying to guess the number. To submit press the >>| (next) button, to erase a digit press the |<< (previous) button, to restart the game press the >|| (pause) button.

