Panel For Example Panel For Example Panel For Example

Embedded STM32 Smartwatch Implementation

Author : Adrian October 02, 2025

Embedded STM32 Smartwatch

Overview

This project implements a multi-level menu UI on an OLED driven by an embedded STM32. It is a compact STM32 project that integrates multiple sensors and an OLED display to realize a simple smart terminal. The multi-level menu UI uses a common struct-index approach to switch between functions, combined with DHT11, RTC, LED, and key inputs to provide integrated operation.

1. Introduction

The hardware can be adapted into a custom board for devices such as a smartwatch. At present the project runs bare-metal with CPU usage at 100%. Future work may add an RTOS.

2. Hardware Photo

Hardware photo

3. Hardware Pinout

OLED module: VCC --> 3.3V GND --> GND SCL --> PB10 SDA --> PB11 DHT11 module: DATA --> PB9 VCC --> 3.3V GND --> GND KEY module (this section uses pins from the development board): KEY0 --> PE4 KEY1 --> PE3 KEY_UP --> PA0

4. Multi-level Menu

With industrial automation, almost all projects require a display terminal. Multi-level menus are a common part of display projects. On larger TFT-LCD screens many open-source GUIs can be ported, for example LVGL. On a 0.96-inch OLED, the multi-level menu usually needs to be adapted and implemented manually.

5. CubeMX Configuration

  1. RCC: configure external high-speed crystal oscillator (HSE) for higher accuracy.
  2. SYS: set Debug to Serial Wire to avoid potential chip lockup.
  3. I2C2: instead of using CubeMX I2C2 peripheral, this project simulates I2C with GPIOs (PB10: CLK; PB11: SDA).
  4. RTC: configure year, month, day, hour, minute, second.
  5. TIM2: DHT11 requires microsecond-level delays, while HAL provides only millisecond delays, so a timer-based microsecond delay is implemented.
  6. KEY inputs: configure PE3, PE4, and PA0 as GPIO inputs according to the development board schematic.

6. Code

6.1 OLED Driver Header

#ifndef __OLED_H #define __OLED_H #include "main.h" #define u8 uint8_t #define u32 uint32_t #define OLED_CMD 0 // write command #define OLED_DATA 1 // write data #define OLED0561_AD 0x78 // OLED I2C address #define COM 0x00 // OLED #define DAT 0x40 // OLED #define OLED_MODE 0 #define SIZE 8 #define XLevelL 0x00 #define XLevelH 0x10 #define Max_Column 128 #define Max_Row 64 #define Brightness 0xFF #define X_WIDTH 128 #define Y_WIDTH 64 // -----------------OLED IIC GPIO emulation---------------- #define OLED_SCLK_Clr() HAL_GPIO_WritePin(GPIOB, GPIO_PIN_10, GPIO_PIN_RESET) // SCL #define OLED_SCLK_Set() HAL_GPIO_WritePin(GPIOB, GPIO_PIN_10, GPIO_PIN_SET) #define OLED_SDIN_Clr() HAL_GPIO_WritePin(GPIOB, GPIO_PIN_11, GPIO_PIN_RESET) // SDA #define OLED_SDIN_Set() HAL_GPIO_WritePin(GPIOB, GPIO_PIN_11, GPIO_PIN_SET) // I2C GPIO emulation void IIC_Start(); void IIC_Stop(); void IIC_WaitAck(); void IIC_WriteByte(unsigned char IIC_Byte); void IIC_WriteCommand(unsigned char IIC_Command); void IIC_WriteData(unsigned char IIC_Data); void OLED_WR_Byte(unsigned dat,unsigned cmd); // Function prototypes void OLED_Init(void); void OLED_WR_Byte(unsigned dat,unsigned cmd); void OLED_FillPicture(unsigned char fill_Data); void OLED_SetPos(unsigned char x, unsigned char y); void OLED_DisplayOn(void); void OLED_DisplayOff(void); void OLED_Clear(void); void OLED_On(void); void OLED_ShowChar(u8 x,u8 y,u8 chr,u8 Char_Size); u32 oled_pow(u8 m,u8 n); void OLED_ShowNum(u8 x,u8 y,u32 num,u8 len,u8 size2); void OLED_ShowString(u8 x,u8 y,u8 *chr,u8 Char_Size); #endif

6.2 Dino Game Graphics Header

#ifndef __DINOGAME_H #define __DINOGAME_H void OLED_DrawBMP(unsigned char x0, unsigned char y0,unsigned char x1, unsigned char y1,unsigned char BMP[]); void OLED_DrawBMPFast(const unsigned char BMP[]); void oled_drawbmp_block_clear(int bx, int by, int clear_size); void OLED_DrawGround(); void OLED_DrawCloud(); void OLED_DrawDino(); void OLED_DrawCactus(); int OLED_DrawCactusRandom(unsigned char ver, unsigned char reset); int OLED_DrawDinoJump(char reset); void OLED_DrawRestart(); void OLED_DrawCover(); #endif

6.3 Dino Game Control Code

#include "control.h" #include "oled.h" #include "dinogame.h" #include "stdlib.h" unsigned char key_num = 0; unsigned char cactus_category = 0; unsigned char cactus_length = 8; unsigned int score = 0; unsigned int highest_score = 0; int height = 0; int cactus_pos = 128; unsigned char cur_speed = 30; char failed = 0; char reset = 0; int get_key(){ if(HAL_GPIO_ReadPin(GPIOE, GPIO_PIN_4)==0) { HAL_Delay(10); // Delay if(HAL_GPIO_ReadPin(GPIOE, GPIO_PIN_4)==0) { return 2; } } if(HAL_GPIO_ReadPin(GPIOE, GPIO_PIN_3)==0) { HAL_Delay(10); // Delay if(HAL_GPIO_ReadPin(GPIOE, GPIO_PIN_3)==0) { return 1; } } if(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0)==1) { HAL_Delay(10); // Delay if(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0)==1) { return 3; } } return 0; } void Game_control(){ while(1) { if(get_key() == 3) // If WK_UP key is pressed, force exit one loop { break; } if (failed == 1) { OLED_DrawRestart(); key_num = get_key(); if (key_num == 2) { if(score > highest_score) highest_score = score; score = 0; failed = 0; height = 0; reset = 1; OLED_DrawDinoJump(reset); OLED_DrawCactusRandom(cactus_category, reset); OLED_Clear(); } continue; } score++; if (height <= 0) key_num = get_key(); OLED_DrawGround(); OLED_DrawCloud(); if (height>0 || key_num == 1) height = OLED_DrawDinoJump(reset); else OLED_DrawDino(); cactus_pos = OLED_DrawCactusRandom(cactus_category, reset); if(cactus_category == 0) cactus_length = 8; else if(cactus_category == 1) cactus_length = 16; else cactus_length = 24; if (cactus_pos + cactus_length < 0) { cactus_category = rand()%4; OLED_DrawCactusRandom(cactus_category, 1); } if ((height < 16) && (cactus_pos>=16 && cactus_pos <=32) || (cactus_pos + cactus_length>=16 && cactus_pos + cactus_length <=32)) { failed = 1; } OLED_ShowString(35, 0, "HI:", 12); OLED_ShowNum(58, 0, highest_score, 5, 12); OLED_ShowNum(98, 0, score, 5, 12); reset = 0; cur_speed = score/20; if (cur_speed > 29) cur_speed = 29; HAL_Delay(30 - cur_speed); key_num = 0; } }

6.4 Multi-level Menu Core Header

#ifndef __MENU_H #define __MENU_H #include "main.h" #define u8 unsigned char // Key definitions #define KEY0 HAL_GPIO_ReadPin(GPIOE, GPIO_PIN_4) // active low #define KEY1 HAL_GPIO_ReadPin(GPIOE, GPIO_PIN_3) // active low #define WK_UP HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0) // active high typedef struct{ u8 current; // current state index u8 next; // next state u8 enter; // enter u8 back; // back void (*current_operation)(void); // operation to execute for current state } Menu_table; // UI screens void home(); void Temperature(); void Palygame(); void Setting(); void Info(); void Menu_key_set(void); u8 KEY_Scan(u8 mode); void TestTemperature(); void ConrtolGame(); void Set(); void Information(); void LED(); void RTC_display(); #endif

7. Summary

This project is an initial, simple implementation of a multi-level menu UI on an OLED with an STM32. The UI currently uses static icons for battery and signal; future development may add battery coulomb counting to measure battery capacity. The article highlights areas to note and possible improvements for further development.