{"id":620,"date":"2025-12-12T23:03:24","date_gmt":"2025-12-12T23:03:24","guid":{"rendered":"https:\/\/portfolio.wcu.edu\/dkmcqueen1\/?page_id=620"},"modified":"2025-12-12T23:03:24","modified_gmt":"2025-12-12T23:03:24","slug":"focusflow-dual-mcu-student-wellness-assistant-stm32-esp32","status":"publish","type":"page","link":"https:\/\/portfolio.wcu.edu\/dkmcqueen1\/focusflow-dual-mcu-student-wellness-assistant-stm32-esp32\/","title":{"rendered":"FocusFlow+ | Dual-MCU Student Wellness Assistant (STM32 + ESP32)"},"content":{"rendered":"<h3 data-start=\"499\" data-end=\"820\"><strong data-start=\"499\" data-end=\"511\">Overview<\/strong><\/h3>\n<p>FocusFlow+ is an embedded student wellness assistant designed to help students manage healthy desk habits during long study sessions. The system combines an STM32 Nucleo-F401RE for real-time control (OLED UI and physical inputs) with an ESP32 that provides a stand-alone Wi-Fi web interface for each wellness mode.<\/p>\n<h3 data-start=\"822\" data-end=\"847\"><strong data-start=\"822\" data-end=\"845\">System Architecture<\/strong><\/h3>\n<p>The system uses a dual-microcontroller architecture to separate real-time embedded control from web-based visualization.<\/p>\n<ul>\n<li data-start=\"850\" data-end=\"923\">STM32: OLED page UI + lock status, joystick + 5 buttons, UART messaging<\/li>\n<li data-start=\"850\" data-end=\"923\">ESP32: SoftAP web server, webpage sections (pages 0\u20134), routes STM32 button events after a UART \u201cready\u201d handshake<\/li>\n<\/ul>\n<h3 data-start=\"1041\" data-end=\"1055\"><strong data-start=\"1041\" data-end=\"1053\">Controls<\/strong><\/h3>\n<ul>\n<li data-start=\"1058\" data-end=\"1093\">Joystick Left\/Right: scroll pages<\/li>\n<li data-start=\"1058\" data-end=\"1093\">Joystick Up\/Down: unlock current page<\/li>\n<li data-start=\"1130\" data-end=\"1165\">Button 5: only way to lock a page<\/li>\n<li data-start=\"1168\" data-end=\"1214\">Buttons 1\u20134: page-specific actions when locked<\/li>\n<\/ul>\n<h3 data-start=\"1216\" data-end=\"1245\"><strong data-start=\"1216\" data-end=\"1243\">Modes (ESP32 pages 0\u20134)<\/strong><\/h3>\n<ul>\n<li data-start=\"1248\" data-end=\"1298\">Page 0: Focus Timer (start\/pause, +5\/\u22125 minutes)<\/li>\n<li data-start=\"1301\" data-end=\"1349\">Page 1: Eating Log (log\/edit, adjust goal 4\u20138)<\/li>\n<li data-start=\"1352\" data-end=\"1398\">Page 2: Exercise (log\/edit, adjust goal 1\u20134)<\/li>\n<li data-start=\"1401\" data-end=\"1455\">Page 3: Mood (log\/edit, show\/hide reflection prompt)<\/li>\n<li data-start=\"1458\" data-end=\"1538\">Page 4: Posture (designed: 20s calibration + monitoring, adjustable sensitivity)<\/li>\n<\/ul>\n<h3 data-start=\"1540\" data-end=\"1577\"><strong data-start=\"1540\" data-end=\"1575\">Planned Alerts (Designed, Not Integrated)<\/strong><\/h3>\n<ul>\n<li data-start=\"1580\" data-end=\"1610\">Focus timer completion alarm<\/li>\n<li data-start=\"1613\" data-end=\"1672\">Global inactivity alert after 5 minutes of no interaction<\/li>\n<li data-start=\"1675\" data-end=\"1719\">Posture-specific alerts (yellow vs red zone)<\/li>\n<\/ul>\n<h3><strong>Wiring &amp; Hardware Integration<\/strong><\/h3>\n<p data-start=\"280\" data-end=\"1063\">FocusFlow+ uses a shared-ground, mixed-voltage hardware setup centered on the STM32 Nucleo-F401RE, which serves as both the primary controller and power source for external devices. The STM32\u2019s <strong data-start=\"442\" data-end=\"508\">5V rail powers the ESP32 and the VL53L0X time-of-flight sensor<\/strong>, while the OLED display and joystick operate at 3.3V. The OLED and VL53L0X share an I2C bus, and a joystick provides analog navigation through ADC inputs. Five external push buttons are configured as <strong data-start=\"709\" data-end=\"753\">active-low inputs with internal pull-ups<\/strong>, ensuring reliable operation with minimal external components. The UART link between the STM32 and ESP32 enables synchronized page state and button event handling. Only the ESP32 is connected to USB during operation. The joystick\u2019s built-in push button was intentionally excluded due to unstable readings, and posture alerts via a buzzer were designed but not integrated due to time constraints.<\/p>\n<h4 data-start=\"280\" data-end=\"1063\"><strong>Pin Mapping Summary<\/strong><\/h4>\n<h5><strong>STM32 Nucleo-F401RE<\/strong><\/h5>\n<table style=\"height: 436px; width: 502px; border-color: #000000; border-style: solid;\" border=\"1\">\n<thead>\n<tr style=\"height: 23px;\">\n<th style=\"width: 127.525px; height: 23px;\">Component<\/th>\n<th style=\"width: 77.325px; height: 23px;\">Signal<\/th>\n<th style=\"width: 86.3625px; height: 23px;\">STM32 Pin<\/th>\n<th style=\"width: 186.788px; height: 23px;\">Notes<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr style=\"height: 23px;\">\n<td style=\"width: 127.525px; height: 23px;\">Joystick<\/td>\n<td style=\"width: 77.325px; height: 23px;\">VCC<\/td>\n<td style=\"width: 86.3625px; height: 23px;\">3.3V<\/td>\n<td style=\"width: 186.788px; height: 23px;\">Analog joystick power<\/td>\n<\/tr>\n<tr style=\"height: 23px;\">\n<td style=\"width: 127.525px; height: 23px;\">Joystick<\/td>\n<td style=\"width: 77.325px; height: 23px;\">GND<\/td>\n<td style=\"width: 86.3625px; height: 23px;\">GND<\/td>\n<td style=\"width: 186.788px; height: 23px;\">Common ground<\/td>\n<\/tr>\n<tr style=\"height: 22px;\">\n<td style=\"width: 127.525px; height: 22px;\">Joystick<\/td>\n<td style=\"width: 77.325px; height: 22px;\">X-Axis<\/td>\n<td style=\"width: 86.3625px; height: 22px;\">PA0 (ADC)<\/td>\n<td style=\"width: 186.788px; height: 22px;\">Page scrolling<\/td>\n<\/tr>\n<tr style=\"height: 23px;\">\n<td style=\"width: 127.525px; height: 23px;\">Joystick<\/td>\n<td style=\"width: 77.325px; height: 23px;\">Y-Axis<\/td>\n<td style=\"width: 86.3625px; height: 23px;\">PA1 (ADC)<\/td>\n<td style=\"width: 186.788px; height: 23px;\">Page unlock<\/td>\n<\/tr>\n<tr style=\"height: 23px;\">\n<td style=\"width: 127.525px; height: 23px;\">External Button 1<\/td>\n<td style=\"width: 77.325px; height: 23px;\">Input<\/td>\n<td style=\"width: 86.3625px; height: 23px;\">PA7<\/td>\n<td style=\"width: 186.788px; height: 23px;\">Active-low, pull-up<\/td>\n<\/tr>\n<tr style=\"height: 23px;\">\n<td style=\"width: 127.525px; height: 23px;\">External Button 2<\/td>\n<td style=\"width: 77.325px; height: 23px;\">Input<\/td>\n<td style=\"width: 86.3625px; height: 23px;\">PB6<\/td>\n<td style=\"width: 186.788px; height: 23px;\">Active-low, pull-up<\/td>\n<\/tr>\n<tr style=\"height: 23px;\">\n<td style=\"width: 127.525px; height: 23px;\">External Button 3<\/td>\n<td style=\"width: 77.325px; height: 23px;\">Input<\/td>\n<td style=\"width: 86.3625px; height: 23px;\">PC7<\/td>\n<td style=\"width: 186.788px; height: 23px;\">Active-low, pull-up<\/td>\n<\/tr>\n<tr style=\"height: 23px;\">\n<td style=\"width: 127.525px; height: 23px;\">External Button 4<\/td>\n<td style=\"width: 77.325px; height: 23px;\">Input<\/td>\n<td style=\"width: 86.3625px; height: 23px;\">PA9<\/td>\n<td style=\"width: 186.788px; height: 23px;\">Active-low, pull-up<\/td>\n<\/tr>\n<tr style=\"height: 23px;\">\n<td style=\"width: 127.525px; height: 23px;\">External Button 5<\/td>\n<td style=\"width: 77.325px; height: 23px;\">Page Lock<\/td>\n<td style=\"width: 86.3625px; height: 23px;\">PA6<\/td>\n<td style=\"width: 186.788px; height: 23px;\">Active-low, pull-up<\/td>\n<\/tr>\n<tr style=\"height: 23px;\">\n<td style=\"width: 127.525px; height: 23px;\">OLED Display<\/td>\n<td style=\"width: 77.325px; height: 23px;\">VCC<\/td>\n<td style=\"width: 86.3625px; height: 23px;\">3.3V<\/td>\n<td style=\"width: 186.788px; height: 23px;\">I2C OLED<\/td>\n<\/tr>\n<tr style=\"height: 23px;\">\n<td style=\"width: 127.525px; height: 23px;\">OLED Display<\/td>\n<td style=\"width: 77.325px; height: 23px;\">GND<\/td>\n<td style=\"width: 86.3625px; height: 23px;\">GND<\/td>\n<td style=\"width: 186.788px; height: 23px;\">\u2014<\/td>\n<\/tr>\n<tr style=\"height: 23px;\">\n<td style=\"width: 127.525px; height: 23px;\">OLED Display<\/td>\n<td style=\"width: 77.325px; height: 23px;\">SDA<\/td>\n<td style=\"width: 86.3625px; height: 23px;\">PB9<\/td>\n<td style=\"width: 186.788px; height: 23px;\">I2C<\/td>\n<\/tr>\n<tr style=\"height: 23px;\">\n<td style=\"width: 127.525px; height: 23px;\">OLED Display<\/td>\n<td style=\"width: 77.325px; height: 23px;\">SCL<\/td>\n<td style=\"width: 86.3625px; height: 23px;\">PB8<\/td>\n<td style=\"width: 186.788px; height: 23px;\">I2C<\/td>\n<\/tr>\n<tr style=\"height: 23px;\">\n<td style=\"width: 127.525px; height: 23px;\">VL53L0X Sensor<\/td>\n<td style=\"width: 77.325px; height: 23px;\">VIN<\/td>\n<td style=\"width: 86.3625px; height: 23px;\">5V<\/td>\n<td style=\"width: 186.788px; height: 23px;\">Powered from STM32<\/td>\n<\/tr>\n<tr style=\"height: 23px;\">\n<td style=\"width: 127.525px; height: 23px;\">VL53L0X Sensor<\/td>\n<td style=\"width: 77.325px; height: 23px;\">GND<\/td>\n<td style=\"width: 86.3625px; height: 23px;\">GND<\/td>\n<td style=\"width: 186.788px; height: 23px;\">\u2014<\/td>\n<\/tr>\n<tr style=\"height: 23px;\">\n<td style=\"width: 127.525px; height: 23px;\">VL53L0X Sensor<\/td>\n<td style=\"width: 77.325px; height: 23px;\">SDA<\/td>\n<td style=\"width: 86.3625px; height: 23px;\">PB9<\/td>\n<td style=\"width: 186.788px; height: 23px;\">Shared I2C bus<\/td>\n<\/tr>\n<tr style=\"height: 23px;\">\n<td style=\"width: 127.525px; height: 23px;\">VL53L0X Sensor<\/td>\n<td style=\"width: 77.325px; height: 23px;\">SCL<\/td>\n<td style=\"width: 86.3625px; height: 23px;\">PB8<\/td>\n<td style=\"width: 186.788px; height: 23px;\">Shared I2C bus<\/td>\n<\/tr>\n<tr style=\"height: 23px;\">\n<td style=\"width: 127.525px; height: 23px;\">VL53L0X Sensor<\/td>\n<td style=\"width: 77.325px; height: 23px;\">XSHUT<\/td>\n<td style=\"width: 86.3625px; height: 23px;\">PA5<\/td>\n<td style=\"width: 186.788px; height: 23px;\">Software-controlled enable<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<h5 data-start=\"1721\" data-end=\"2022\"><strong>STM32 \u2194 ESP32 UART &amp; Power Interface<\/strong><\/h5>\n<table style=\"height: 115px; width: 364px; background-color: #ffffff; border-color: #000000;\" border=\"1\">\n<thead>\n<tr style=\"height: 23px;\">\n<th style=\"width: 125.425px; height: 23px;\">Function<\/th>\n<th style=\"width: 105.363px; height: 23px;\">ESP32 GPIO<\/th>\n<th style=\"width: 114.412px; height: 23px;\">Connected To<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr style=\"height: 23px;\">\n<td style=\"width: 125.425px; height: 23px;\">Power<\/td>\n<td style=\"width: 105.363px; height: 23px;\">VCC<\/td>\n<td style=\"width: 114.412px; height: 23px;\">STM32 5V<\/td>\n<\/tr>\n<tr style=\"height: 23px;\">\n<td style=\"width: 125.425px; height: 23px;\">Ground<\/td>\n<td style=\"width: 105.363px; height: 23px;\">GND<\/td>\n<td style=\"width: 114.412px; height: 23px;\">STM32 GND<\/td>\n<\/tr>\n<tr style=\"height: 23px;\">\n<td style=\"width: 125.425px; height: 23px;\">UART RX (ESP32)<\/td>\n<td style=\"width: 105.363px; height: 23px;\">IO16<\/td>\n<td style=\"width: 114.412px; height: 23px;\">STM32 TX<\/td>\n<\/tr>\n<tr style=\"height: 23px;\">\n<td style=\"width: 125.425px; height: 23px;\">UART TX (ESP32)<\/td>\n<td style=\"width: 105.363px; height: 23px;\">IO17<\/td>\n<td style=\"width: 114.412px; height: 23px;\">STM32 RX<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p data-start=\"2405\" data-end=\"2545\">The STM32 provides both logic coordination and system power, while the ESP32 handles all web-based visualization through a SoftAP interface.<\/p>\n<h3 data-start=\"2405\" data-end=\"2545\"><strong>Planned (Not Integrated)<\/strong><\/h3>\n<table style=\"height: 79px; width: 264px; border-color: #000000;\" border=\"1\">\n<thead>\n<tr style=\"height: 33px;\">\n<th style=\"width: 82.775px; height: 33px;\">Component<\/th>\n<th style=\"width: 58.1875px; height: 33px;\">Signal<\/th>\n<th style=\"width: 104.238px; height: 33px;\">ESP32 GPIO<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr style=\"height: 23px;\">\n<td style=\"width: 82.775px; height: 23px;\">Buzzer<\/td>\n<td style=\"width: 58.1875px; height: 23px;\">Output<\/td>\n<td style=\"width: 104.238px; height: 23px;\">IO21<\/td>\n<\/tr>\n<tr style=\"height: 23px;\">\n<td style=\"width: 82.775px; height: 23px;\">Buzzer<\/td>\n<td style=\"width: 58.1875px; height: 23px;\">Ground<\/td>\n<td style=\"width: 104.238px; height: 23px;\">GND<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<h3 data-start=\"1721\" data-end=\"2022\"><strong data-start=\"1721\" data-end=\"1732\">Results<\/strong><\/h3>\n<p data-start=\"1721\" data-end=\"2022\">Completed: OLED navigation, locked-page input mapping, UART handshake + button routing, and dynamic webpage sections per mode.<br data-start=\"1861\" data-end=\"1864\" \/>Not completed: VL53L0X distance streaming (sensor bring-up issue during I2C initialization), so posture distance did not display on the webpage; buzzer integration was cut due to time.<\/p>\n<p data-start=\"1721\" data-end=\"2022\">Here is the video of me operating the project: <a href=\"https:\/\/www.youtube.com\/watch?v=DPg7rAUyIpc\">Video link<\/a><\/p>\n<h3 data-start=\"2024\" data-end=\"2188\"><strong data-start=\"2024\" data-end=\"2042\">Tools &amp; Skills<\/strong><\/h3>\n<p data-start=\"2024\" data-end=\"2188\">STM32Cube HAL (GPIO\/I2C\/SPI\/UART\/TIM), state-based UI design, UART messaging\/handshake, ESP32 SoftAP + web server, embedded-to-web integration. This project emphasizes embedded system architecture, real-time input handling, and reliable communication between firmware and web interfaces.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Dual-microcontroller (STM32 + ESP32) embedded wellness assistant featuring an OLED-based UI, state-based input mapping, UART communication, and a live SoftAP web dashboard.<\/p>\n","protected":false},"author":1973,"featured_media":621,"parent":0,"menu_order":0,"comment_status":"closed","ping_status":"closed","template":"","meta":{"_et_pb_use_builder":"","_et_pb_old_content":"","_et_gb_content_width":"","footnotes":""},"class_list":["post-620","page","type-page","status-publish","has-post-thumbnail","hentry"],"_links":{"self":[{"href":"https:\/\/portfolio.wcu.edu\/dkmcqueen1\/wp-json\/wp\/v2\/pages\/620","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/portfolio.wcu.edu\/dkmcqueen1\/wp-json\/wp\/v2\/pages"}],"about":[{"href":"https:\/\/portfolio.wcu.edu\/dkmcqueen1\/wp-json\/wp\/v2\/types\/page"}],"author":[{"embeddable":true,"href":"https:\/\/portfolio.wcu.edu\/dkmcqueen1\/wp-json\/wp\/v2\/users\/1973"}],"replies":[{"embeddable":true,"href":"https:\/\/portfolio.wcu.edu\/dkmcqueen1\/wp-json\/wp\/v2\/comments?post=620"}],"version-history":[{"count":10,"href":"https:\/\/portfolio.wcu.edu\/dkmcqueen1\/wp-json\/wp\/v2\/pages\/620\/revisions"}],"predecessor-version":[{"id":631,"href":"https:\/\/portfolio.wcu.edu\/dkmcqueen1\/wp-json\/wp\/v2\/pages\/620\/revisions\/631"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/portfolio.wcu.edu\/dkmcqueen1\/wp-json\/wp\/v2\/media\/621"}],"wp:attachment":[{"href":"https:\/\/portfolio.wcu.edu\/dkmcqueen1\/wp-json\/wp\/v2\/media?parent=620"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}