You Lost It Project

"You Lost It" was a project broken into parts that culminated in the development of an Airtag-like Bluetooth tracker system that sends out a beacon to nearby phones when it enters "lost" mode. All of these parts were developed on a STM32 B-L475E-IOT01A1 board, which has all the required peripherals baked into the board itself along with the microcontroller. The IDE we used was STM32CubeIDE. This project was part of a class that I took at UCSD, CSE 190: Wireless Embedded Systems.

Part One: Blinkenlights

This part of the project involved us implementing three different components: the LEDs, the timer, and the app itself. Everything was implemented from scratch with no HAL abstraction. For the LEDs, we had to use memory-mapped IO (basically setting values in different registers to enable hardware functionality) to enable the bus and set GPIO settings for initializing the LEDs in a function called leds_init. We also used MMIO to turn the LEDs on or off, based on binary parameters that we passed to a function we wrote called leds_set. For the timer, we wrote three functions: one to initialize the timer called timer_init, another to reset the timer called timer_reset, and one more to set the amount of milliseconds that the timer would count up to before triggering an interrupt (a function that gets executed when a specific event takes place), called timer_set_ms. We used MMIO (bare-metal register access) for all of these, with the trickiest parts involving figuring out from the datasheet what registers that we needed to set to enable timer functionality, as well as figuring out the prescaler settings that we needed to use to utlize the microcontroller's inbuilt clock for incrementing the timer.

Finally, in the main file, we initialized the LEDs and timer by calling the respective functions, and set the timer to count to 50 milliseconds because we wanted the LEDs to blink at 20 Hz. We defined a message array to hold the contents of the message that we wished to transmit blinking the LED lights, and an index counter to keep track of where we were in the array. Next, we wrote out the interrupt handler function (what gets called when the interrupt is triggered), which resets the interrupt flag, transmits the respective section of the message to the LEDs for blinking, and then increments the counter. When the counter reaches the end of the message array, it is reset so that the message loops over and over. Our end result was a small embedded app the would blink the LEDs to transmit an encoded message containing an 8 bit header (4 message elements) and a 16 bit payload (8 message elements) over and over again.

Part Two: Interfacing with external peripherals

The second part of the project involved implementing the I2C and accelerometer drivers and implementing the embedded application that used the accelerometer values to detect if the tag was lost. First, for the I2C driver, the I2C bus and pins were connected to the clock tree, the pins were setup in alternate function mode, and the timing was configured to use a baud rate of 100 kHz. We then wrote out the i2c_transaction function, which would read and write data (based on the parameters) between a buffer stored on the stack and the I2C pins connected to the accelerometer peripheral. This was the trickiest part of the task, because of added complexity with the I2C read operation, where the subaddress to read from must first be written to the sensor, then the I2C mode must be switched after the TC flag is set, then only can data be read. With the I2C driver written, we then wrote the init and read_xyz functions for the accelerometer, which used the I2C transaction function and the internal registers of the accelerometer to setup the accelerometer and read the XYZ acceleration values detected by the sensor. Finally, we wrote the embedded application, which used polling to read the accelerometer values and compared the values to a threshold value to determine if the board was moving. If no movement was detected for 60 seconds (determined using the preexisting timer peripheral), then the LEDs would start blinking. If movement was detected, the LEDs would be switched off and the timer counter would be reset.

Part Three: Adding low-energy radio communication

In this part, we implemented a prewritten BLE driver into our existing code, and modified our embedded application to transmit BLE packets to nearby phones to indicate that the tag was lost instead of the LED message that would be transmitted before. This involved making modifications to our clock setup for our timer peripheral as well as I2C, since BLE required an 8 Mhz clock source. We modified the embedded application so that device would start BLE beaconing after entering lost mode (no movement for 60 seconds) and would stop BLE beaconing when movement was detected. For the BLE packet data that would be received by a phone that connected to the device, snprintf was used so that each packet would indicate the amount of time that the device had been lost, with packets being transmitted every 10 seconds.

Part Four: Power Efficiency & In-Class Hackathon

This part involved switching our peripheral interfacing strategy with the accelerometer sensor to use interrupts instead of polling to save power. This was achieved by setting a threshold value for movement and a time (60 seconds) in the sensor's internal registers. If the accelerometer did not detect movement (change in xyz values) for the given time, it would send an interrupt signal on an external interrupt pin to the MCU, which would begin beaconing over BLE. All peripherals and clocks that were not required, including the I2C pin (after the accelerometer was setup) were switched off. Finally, one of the most significant power saving methods was setting the CPU to sleep when the board was in motion and adding the Wait for Interrupt instruction at the end of the main loop. The CPU would then wake up upon receiving the interrupt from the accelerometer to begin the beaconing process.