Indoor air quality, ambient lighting, and room occupancy directly impact human productivity, health, and energy conservation. In embedded systems, managing multiple real-time sensors alongside responsive user displays and safety alarms is a classic engineering challenge.
Traditional bare-metal approaches rely on a monolithic super-loop (while(1)), where slow sensor reads (such as the DHT22's ~20 ms single-wire pulse handshake) freeze the processor, causing sluggish button responses and screen flicker.
In this project, we design and implement a fully concurrent environmental room monitor on the STM32 Blue Pill (STM32F103C8T6) using FreeRTOS and the STM32Cube framework. We eliminate blocking delays, guarantee drift-free periodic sampling, protect shared hardware resources with mutexes, and implement an automated sleep state machine.
Key Featuresβ’ Concurrent Task Architecture: 6 independent FreeRTOS tasks with prioritized preemptive scheduling.β’ Multisensor Telemetry:
- Temperature & Humidity: DHT22 single-wire protocol via ARM Cortex-M DWT cycle counter.
- Ambient Light: Analog photoresistor (LDR) sampled via 12-bit ADC1 on PA0.
- Occupancy Detection: Digital PIR motion sensor on PA3.β’ Interactive UI: SSD1306 128x64 OLED display running over 400 kHz Hardware I2C1, navigated via a KY-040 rotary encoder with forward and reverse wraparound.β’ Out-of-Bounds Safety Alarm: Active buzzer alerts occupants with a 1 kHz PWM tone if temperature drops below 18.0 Β°C or exceeds 30.0 Β°C.β’ Smart Power-Saving State Machine: Automatically puts the OLED panel into low-power standby after 15 seconds without motion. Motion or rotary knob interaction immediately restores active mode.β’ Zero Arduino Abstractions: Built strictly with ST HAL drivers and native FreeRTOS APIs.
β’ LDR Sensor: PA0 (Analog ADC1_IN0) β Ambient light level (0β100%)β’ DHT22 Sensor: PA1 (GPIO Open-Drain) β Temperature and humidity single-wire protocolβ’ Active Buzzer: PA2 (TIM2_CH3 PWM) β 1 kHz audible alarm toneβ’ PIR Sensor: PA3 (Digital In with Pulldown) β Human motion detectionβ’ Rotary Encoder CLK: PA4 (EXTI4 Falling Edge) β Quadrature step interruptβ’ Rotary Encoder DT: PA5 (Digital In with Pull-up) β Quadrature direction lineβ’ SSD1306 OLED SCL: PB6 (Hardware I2C1) β 400 kHz Fast Mode Clockβ’ SSD1306 OLED SDA: PB7 (Hardware I2C1) β 400 kHz Fast Mode Dataβ’ Serial Terminal: PA9 / PA10 (USART1) β 115200 8N1 diagnostic telemetryβ’ Status Heartbeat: PC13 (GPIO Out) β On-board LED activity pulse
The application is structured into 6 concurrent tasks with priorities assigned by scheduling urgency:
- InputTask (Priority 3, High): Debounces encoder pulses, cycles display page, wakes sleeping system.
- MotionTask (Priority 3, High): Samples PIR sensor on PA3 every 100 ms, updates event flags.
- StateTask (Priority 3, High): Central state machine, manages 15 s sleep/wake timer.
- SensorTask (Priority 2, Medium): Samples DHT22 & LDR every 2000 ms using vTaskDelayUntil to guarantee zero timing drift.
- AlarmTask (Priority 2, Medium): Evaluates 18 Β°Cβ30 Β°C temperature limits and actuates buzzer.
- DisplayTask (Priority 1, Low): Exclusively manages SSD1306 OLED and renders telemetry views.
- Drift-Free Periodic Execution (vTaskDelayUntil): Standard delays accumulate execution and preemption overhead into timing drift. Using vTaskDelayUntil enforces an exact 0.5 Hz sampling frequency anchored to absolute tick counts.
- Thread-Safe Logging (serialMutex): Concurrent tasks outputting diagnostic strings to USART1 are synchronized using a FreeRTOS recursive mutex. This prevents character interleaving and ensures clean terminal telemetry.
- Automated Unit Testing (100% Pass): Hardware-independent decision logic was decoupled into pure C modules (logic.c) and verified on the host PC using the Unity test runner (13 of 13 unit tests succeeded).
- Static Code Analysis (0 Defects): Verified using PlatformIO Check with cppcheck: 0 high, 0 medium, 0 low defects.
During Wokwi virtual simulation, we diagnosed an emulator limitation where standard Cortex-M3 exception return unstacking (EXC_RETURN via SVC 0) failed during task startup.
We engineered a direct Thread-mode context-switching port (adapted from the open-source implementation by Djaver Hassan, credit: Ni-ear):β’ The first task is launched in Thread mode on PSP using prvTaskBootstrap.β’ Task yields execute via vPortYieldDirect() without relying on PendSV.β’ The scheduler tick is driven by hardware timer TIM3 running at 20 Hz, reducing browser simulation overhead while preserving 100% functional fidelity.
Accreditation & CollaborationThis project is submitted in partial fulfillment of the requirements for BCA182: Embedded Systems Programming at Mindanao State University β Iligan Institute of Technology.
β’ Student Developer: Perch Arnel II Montefalconβ’ Course Evaluator / Collaborator: Paul Rodolf P. Castor ([email protected])β’ Complete Source Code: https://github.com/Not-to-Shabby/BCA18


















Comments