Modular, OTA-upgradable firmware suite for the LabExpert IoT sensor platform
This repository contains the complete embedded firmware ecosystem for the LabExpert platform β a modular IoT-based laboratory experiment system. Each sensor module is powered by an ESP32 and communicates with a central backend over WiFi + MQTT. Firmware is deployed wirelessly via a dual-partition OTA (Over-The-Air) update mechanism.
- π Dual-Partition OTA β Bootloader on
ota_0, experiment firmware onota_1with automatic failsafe rollback - π‘ MQTT Telemetry β Real-time sensor data streaming with binary-packed payloads
- πΆ BLE Provisioning β Zero-config WiFi setup via Bluetooth Low Energy (NimBLE)
- π UDP Auto-Discovery β Backend automatically discovers sensor modules on the local network
- π§© Modular Architecture β Shared libraries, per-sensor firmware generators, and a common hardware abstraction
- πΎ EEPROM-Based Sensor ID β Each sensor module carries a unique identity via an AT24C02 I2C EEPROM
LabExpert_Sensor_ESP32_CODES/
β
βββ ESP_32_OTA/ # π§ OTA Bootloader (always on ota_0)
β βββ src/
β β βββ main.cpp # Core bootloader logic
β β βββ wifi_credentials.cpp/h # BLE-based WiFi credential manager
β βββ partitions/
β β βββ custom_partitions.csv # Dual OTA partition table
β βββ platformio.ini
β
βββ THR_Firmware_bin_Generator/ # π‘οΈ Temperature (DS18B20) firmware
β βββ src/
β β βββ main.cpp # Entry point & init
β β βββ sensor_communication.cpp # DS18B20 OneWire driver
β β βββ experiment_manager.cpp # Sampling loop & data batching
β β βββ mqtt_handler.cpp # MQTT pub/sub & commands
β β βββ config_handler.cpp # HTTP API & experiment config
β βββ include/ # Header files
β βββ partitions/ # Partition table
β βββ platformio.ini
β
βββ TOF_Firmware_bin_Generator/ # π Time-of-Flight (VL53L1X) firmware
β βββ src/
β β βββ main_sensor.cpp # Entry point (dual-core init)
β β βββ sensor_communication.cpp # I2C VL53L1X driver (10β50 Hz)
β β βββ experiment_manager.cpp # Timer ISR + Core 0 sensor task
β β βββ motor_controller.cpp # DC motor control (PWM + encoder)
β β βββ mqtt_handler.cpp # MQTT with binary data packets
β β βββ config_handler.cpp # HTTP API, OTA upload handler
β βββ include/
β βββ partitions/
β βββ Implementation_Guide.md # Detailed I2C migration docs
β βββ platformio.ini
β
βββ OSI_Firmware_bin_Generator/ # γ°οΈ Oscillation Sensor firmware
β βββ src/
β β βββ main_sensor.cpp
β β βββ sensor_communication.cpp
β β βββ experiment_manager.cpp
β β βββ mqtt_handler.cpp
β β βββ config_handler.cpp
β βββ EEPROM_driver.cpp # AT24C02 test/debug utility
β βββ partitions/
β βββ platformio.ini
β
βββ UltraSonic_Firmware_bin_Generator/ # π Ultrasonic (HC-SR04) firmware
β βββ src/
β β βββ main_sensor.cpp
β β βββ sensor_communication.cpp
β β βββ experiment_manager.cpp
β β βββ mqtt_handler.cpp
β β βββ config_handler.cpp
β βββ partitions/
β βββ platformio.ini
β
βββ BH1750/ # π‘ Light Intensity calibration tool
β βββ src/
β β βββ main.cpp # Calibration wizard (Arduino Uno)
β βββ platformio.ini
β
βββ shared/ # π¦ Shared libraries (all firmwares)
β βββ LedController.h / .cpp # Multi-mode LED driver
β βββ nvs_wifi_credentials.h # NVS WiFi credential reader
β βββ nvs_mqtt_credentials.h # NVS MQTT credential manager
β
βββ bluetoothConfigeration/ # π² Standalone BLE provisioning tool
β βββ src/
β β βββ main.cpp # NimBLE UART service (Nordic)
β βββ platformio.ini
β
βββ R_W_E_EEPROM_arduino_uno/ # π§ EEPROM utility (Arduino Uno)
β βββ read_write_erase_for_arduino_uno/
β βββ read_write_erase_for_arduino_uno.ino
β
βββ Binn/ # π¦ Binary firmware generators
β βββ binFileGen/ # ESP32 binary builder
β βββ binFileGen - NodeMCU/ # NodeMCU binary builder
β
βββ docs/ # π Documentation & diagrams
β βββ esp32-ota/
β βββ ESP32_OTA_Documentation.md # Full OTA system documentation
β βββ api_reference.md # REST API reference
β βββ architecture.drawio # System architecture diagram
β βββ flowchart.png # Boot flow diagram
β βββ sequence_backend_push.png # Backend OTA sequence
β βββ sequence_web_upload.png # Web upload sequence
β βββ state_diagram.png # State machine diagram
β
βββ pin_usage.md # π Complete GPIO pin mapping
βββ .gitignore
βββ README.md # β You are here
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β LabExpert Backend β
β (Python Β· MQTT Broker Β· REST API Β· DB) β
ββββββββββββββββ¬βββββββββββββββββββββββββββββββ¬ββββββββββββββββββββ
β MQTT (pub/sub) β UDP Discovery
β Port 1883 β Port 8888/8889
βΌ βΌ
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β ESP32 Sensor Module β
β βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β Partition: ota_0 (1.5 MB) ota_1 (1.5 MB) β β
β β βββββββββββββββββββββββ ββββββββββββββββββββββββββββ β β
β β β ESP_32_OTA β β Sensor Firmware β β β
β β β (Bootloader) β β (THR/TOF/OSI/ULT) β β β
β β β β β β β β
β β β β’ BLE Provisioning β β β’ Sensor Reading β β β
β β β β’ WiFi Manager β β β’ MQTT Publishing β β β
β β β β’ UDP Discovery β β β’ Experiment Manager β β β
β β β β’ OTA Web Server β β β’ HTTP Config API β β β
β β β β’ EEPROM Detection β β β’ Dual-Core Processing β β β
β β βββββββββββββββββββββββ ββββββββββββββββββββββββββββ β β
β βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β
β ββββββββββββ ββββββββββββ ββββββββββββ ββββββββββββ β
β β AT24C02 β β Sensor β β Status β β Motor β β
β β EEPROM β β Module β β LEDs β β (TOF) β β
β β (I2C) β β (I2C/ β β Γ5 β β (PWM) β β
β β β β 1-Wire β β β β β β
β β β β /GPIO) β β β β β β
β ββββββββββββ ββββββββββββ ββββββββββββ ββββββββββββ β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
- Power On β ESP32 boots from
ota_0(OTA Bootloader) - EEPROM Check β Reads sensor type ID from AT24C02 (
0x50) - WiFi Connect β Loads credentials from NVS (saved via BLE provisioning)
- UDP Discovery β Responds to backend broadcast, receives MQTT broker info
- OTA Push β Backend pushes matching firmware binary to
ota_1 - Reboot β ESP32 boots into experiment firmware on
ota_1 - Failsafe β If EEPROM unreadable or firmware crashes, auto-rollback to
ota_0
| ID | Experiment | Sensor | Firmware | Communication | Frequency |
|---|---|---|---|---|---|
| THR | Temperature | DS18B20 | THR_Firmware_bin_Generator |
OneWire (Pin 23) | Configurable |
| TOF | Distance / Motion | VL53L1X (TOF400F) | TOF_Firmware_bin_Generator |
I2C (Pin 21/22) | 10β50 Hz |
| OSI | Oscillation | Digital Sensor | OSI_Firmware_bin_Generator |
GPIO (Pin 33) | Configurable |
| ULT | Distance | HC-SR04 | UltraSonic_Firmware_bin_Generator |
GPIO Trig/Echo (Pin 21/22) | Configurable |
| BH | Light Intensity | BH1750 | BH1750 (calibration only) |
I2C (Pin 21/22) | 2 Hz (cal mode) |
| Pin | Function | Used By |
|---|---|---|
| 12 | BLE Status LED (Active Low) | All |
| 13 | Sensor Connected LED (Active Low) | All |
| 14 | WiFi Status LED (Active Low) | All |
| 16 | OTA Status LED (Active Low) | All |
| 18 | EEPROM SDA (I2C) | All |
| 19 | EEPROM SCL (I2C) | All |
| 21 | Sensor SDA / Trig | TOF, ULT, BH |
| 22 | Sensor SCL / Echo | TOF, ULT, BH |
| 23 | OneWire / LDR Encoder | THR, TOF |
| 25 | EEPROM Write Protect | OTA |
| 26 | Motor Left PWM / I2S WS | TOF |
| 27 | Sensor Active LED (Active Low) | All |
| 32 | OTA Restart Trigger | All Firmware |
| 33 | Motor Right PWM / OSI Sensor | TOF, OSI |
| 34 | BLE Trigger / Factory Reset | OTA |
| 35 | Limit Switch (OR Gate) | TOF |
π Full pin documentation with LED logic states available in
pin_usage.md
| LED (Pin) | State | Meaning |
|---|---|---|
| WiFi (14) | ON | Disconnected |
| WiFi (14) | Blink (Slow) | Connected |
| WiFi (14) | OFF | Bluetooth mode active |
| BLE (12) | ON | BLE advertising |
| BLE (12) | Blink | BLE connected |
| Sensor (13) | OFF | Sensor connected |
| Sensor (13) | ON | Sensor disconnected |
| Active (27) | Blink (Fast) | Experiment running |
| OTA (16) | Blink (Fast) | OTA restart triggered |
| Tool | Version | Purpose |
|---|---|---|
| PlatformIO | Latest | Build system & dependency management |
| VS Code | Latest | IDE (recommended with PlatformIO extension) |
| ESP32 Dev Board | ESP32-WROOM-32 | Target microcontroller |
| Arduino Uno | ATmega328P | BH1750 calibration & EEPROM utility |
| USB Cable | Micro-USB / USB-C | Initial flash & serial monitor |
git clone <repository-url>
cd LabExpert_Sensor_ESP32_CODESThe OTA bootloader must be flashed via USB once. After that, all firmware updates happen wirelessly.
cd ESP_32_OTA
pio run -t upload
pio device monitor # Verify boot outputEach sensor firmware generates a .bin file for OTA deployment:
cd THR_Firmware_bin_Generator # Or TOF / OSI / UltraSonic
pio run # Compile firmwareThe compiled binary is located at:
.pio/build/esp32dev/firmware.bin
The backend automatically pushes the correct firmware via the OTA HTTP API:
# Manual OTA push (for testing)
curl -X POST "http://<esp32-ip>/update" \
-F "update=@.pio/build/esp32dev/firmware.bin"Or use the backend's programmatic OTA endpoint:
POST /ota/begin β { "size": <bytes> }
POST /ota/write β { "offset": 0, "size": 1024, "data": "<hex>" }
POST /ota/end β triggers reboot
All sensor firmwares include shared code from the shared/ directory via the -I../shared build flag.
Multi-mode LED driver supporting active-low hardware with non-blocking blink patterns.
#include "LedController.h"
LedController led(14, true); // Pin 14, active-low
led.begin();
led.set(LedController::BLINK_SLOW); // 1s on/off
led.set(LedController::BLINK_FAST); // 200ms on/off
led.set(LedController::BLINK_PULSE); // 150ms on, 2850ms off
led.update(); // Call in loop()Header-only NVS reader for WiFi credentials saved by the OTA bootloader's BLE provisioning.
#include "nvs_wifi_credentials.h"
char ssid[33], password[65];
if (loadWiFiCredentialsFromNVS(ssid, sizeof(ssid), password, sizeof(password))) {
WiFi.begin(ssid, password);
}NVS-based MQTT broker credential manager with change detection to minimize flash writes.
#include "nvs_mqtt_credentials.h"
// Save (OTA bootloader)
saveMQTTCredentialsToNVS("192.168.1.100", 1883, "aa:bb:cc:dd:ee:ff");
// Load (sensor firmware)
char broker[40]; uint16_t port; char mac[18];
loadMQTTCredentialsFromNVS(broker, sizeof(broker), &port, mac, sizeof(mac));Each sensor firmware (THR, TOF, OSI, ULT) follows a consistent modular pattern:
ββββββββββββββββββββββββββββββββββββββββββββββββ
β main.cpp β
β (Init, WiFi, LED control, loop) β
ββββββββββββ¬ββββββββββββ¬ββββββββββββ¬βββββββββββββ€
β sensor_ β experimentβ mqtt_ β config_ β
β communi β _manager β handler β handler β
β cation β β β β
β β β β β
β β’ Init β β’ Timer β β’ Setup β β’ Status β
β β’ Read β ISR β β’ Pub/Sub β β’ Config β
β β’ EEPROM β β’ Core 0 β β’ Callbackβ β’ Start β
β β’ Calib β task β β’ Binary β β’ Stop β
β β β’ Batch β packets β β’ OTA β
ββββββββββββ΄ββββββββββββ΄ββββββββββββ΄βββββββββββββ
β² β²
β β
shared/ shared/
LedController nvs_*_credentials
| Module | Responsibility |
|---|---|
main.cpp / main_sensor.cpp |
Hardware init, WiFi connection, LED management, main loop |
sensor_communication.cpp |
Sensor driver, EEPROM ID detection, device ID from MAC |
experiment_manager.cpp |
Timer ISR, sampling task (Core 0), data batching & buffering |
mqtt_handler.cpp |
MQTT connection, topic subscription, binary data publishing |
config_handler.cpp |
AsyncWebServer HTTP API (/status, /config, /start, /stop, /update) |
| Method | Endpoint | Description |
|---|---|---|
GET |
/status |
Current sensor status, experiment state, config |
GET |
/config |
Read current experiment configuration |
POST |
/config |
Set frequency, duration, mode |
POST |
/start |
Start experiment data collection |
POST |
/stop |
Stop running experiment |
POST |
/update |
Upload firmware binary (multipart) |
GET |
/id |
Get sensor type ID |
| Method | Endpoint | Description |
|---|---|---|
GET |
/ |
OTA management web page |
GET |
/info |
Sensor type & ID (JSON) |
GET |
/ping |
Health check (pong) |
POST |
/update |
Upload firmware (multipart form) |
POST |
/ota/begin |
Start chunked OTA ({ "size": N }) |
POST |
/ota/write |
Write chunk ({ "offset", "size", "data" }) |
POST |
/ota/end |
Finalize OTA & reboot |
POST |
/sensor/repair |
Rewrite EEPROM sensor ID |
GET |
/id |
Get current sensor type |
sequenceDiagram
participant Backend
participant ESP32 (ota_0)
participant ESP32 (ota_1)
Note over ESP32 (ota_0): Bootloader running
Backend->>ESP32 (ota_0): UDP Discovery broadcast
ESP32 (ota_0)-->>Backend: Device ID, IP, sensor type
Backend->>ESP32 (ota_0): POST /ota/begin {size}
Backend->>ESP32 (ota_0): POST /ota/write {chunks...}
Backend->>ESP32 (ota_0): POST /ota/end
Note over ESP32 (ota_1): Firmware written to ota_1
ESP32 (ota_0)->>ESP32 (ota_1): Reboot β ota_1
Note over ESP32 (ota_1): Sensor firmware running
ESP32 (ota_1)-->>Backend: MQTT sensor data
Interactive serial tool for calibrating a BH1750 light sensor against a reference meter (UT383). Runs on Arduino Uno.
| Command | Action |
|---|---|
c |
Collect calibration data point |
s |
Show collected data |
f |
Calculate calibration factors (linear regression) |
r |
Reset data |
m |
Show current calibrated measurement |
d |
Debug / I2C scan |
cd BH1750
pio run -t upload
pio device monitorSerial tool for programming AT24C02 EEPROM sensor IDs. Runs on Arduino Uno.
| Option | Action |
|---|---|
1 |
Write new Sensor ID |
2 |
Read current Sensor ID |
3 |
Erase EEPROM |
4 |
Full hex dump (0x00β0xFF) |
Standalone AT24C02 test utility for ESP32 with I2C scanning, page write, boundary tests, and full hex dump.
Standalone BLE provisioning tool using NimBLE Nordic UART Service (NUS). Saves WiFi credentials to NVS Preferences on confirmation.
# Name, Type, SubType, Offset, Size
nvs, data, nvs, 0x9000, 0x5000 (20 KB)
otadata, data, ota, 0xe000, 0x2000 (8 KB)
ota_0, app, ota_0, 0x10000, 0x180000 (1.5 MB) β Bootloader
ota_1, app, ota_1, 0x190000, 0x180000 (1.5 MB) β Sensor Firmware
spiffs, data, spiffs, 0x310000, 0x0F0000 (960 KB)
| Library | Version | Purpose |
|---|---|---|
| ArduinoJson | ^6.21 / ^7.4 | JSON serialization |
| ESPAsyncWebServer | Latest | Async HTTP server |
| AsyncTCP | ^1.1.1 | Async TCP for ESP32 |
| WebSockets | ^2.4.1 | WebSocket support |
| PubSubClient | ^2.8 | MQTT client |
| Library | Firmware | Purpose |
|---|---|---|
| DallasTemperature | THR | DS18B20 driver |
| OneWire | THR | OneWire protocol |
| VL53L1X | TOF | Time-of-Flight sensor |
| NimBLE-Arduino | OTA | BLE provisioning |
| BH1750 | BH1750 | Light sensor (Uno) |
| Document | Description |
|---|---|
pin_usage.md |
Complete GPIO pin mapping for all experiments |
docs/esp32-ota/ESP32_OTA_Documentation.md |
Full OTA system documentation |
docs/esp32-ota/api_reference.md |
REST API reference |
TOF_Firmware_bin_Generator/Implementation_Guide.md |
TOF I2C implementation guide |
- Fork the repository
- Create a feature branch:
git checkout -b feature/new-sensor - Follow the modular architecture pattern (see Firmware Module Architecture)
- Test with serial monitor:
pio device monitor - Submit a pull request
- Duplicate an existing firmware generator folder (e.g.,
UltraSonic_Firmware_bin_Generator) - Modify
sensor_communication.cppwith your sensor driver - Update
experiment_manager.cppfor sampling logic - Set the EEPROM sensor ID (3-char code, e.g.,
"MIC") - Update
pin_usage.mdwith your pin assignments
This project is licensed under the MIT License.
MIT License
Copyright (c) 2025 LabExpert
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
Built with β€οΈ for physics education using ESP32 + PlatformIO