An ESP32-based LED controller I built as a university student to learn embedded systems, WiFi/MQTT, BLE provisioning, and mobile app integration. It works together with a SwiftUI iOS app to control LEDs over WiFi via MQTT.
- Controls 4 LEDs (2 red, 2 blue) on ESP32
- Receives WiFi + device config over BLE (SSID, password, device id, base topic)
- Connects to WiFi and a secure MQTT broker
- Publishes device presence and listens for control messages (mode/status)
- Runs 3 LED animation modes (plus off)
- ESP32 + Arduino framework (PlatformIO)
- MQTT (PubSubClient) + JSON (ArduinoJson)
- BLE (for initial provisioning)
- SwiftUI iOS app as a controller UI
esp32/
├─ src/
│ └─ main.cpp // MQTT + BLE provisioning + LED modes
├─ include/
├─ lib/
├─ test/
└─ platformio.ini // Board + libs (PubSubClient, ArduinoJson)
-
Install PlatformIO (VS Code extension or CLI)
-
Clone and open this repo
-
Libraries (already declared in
platformio.ini
)
lib_deps =
knolleary/PubSubClient @ ^2.8
bblanchon/ArduinoJson @ ^7.0.0
- Build and upload
pio run --target upload
pio device monitor
- Provision over BLE
- Power the ESP32; it advertises as
esp32
- Using the iOS app, send JSON config via BLE with keys:
ssid
,pass
,id
,topic
- ESP32 saves the config and connects to WiFi + MQTT
Note: Credentials are read over BLE only on the first boot. They are saved in non‑volatile storage and on subsequent boots the device skips BLE and auto‑connects to Wi‑Fi and MQTT using the saved values. To re‑provision, clear the saved credentials or perform a factory reset.
Example BLE config payload:
{
"ssid": "YourWiFi",
"pass": "YourPassword",
"id": "your-device-uuid",
"topic": "home"
}
ESP32 will use final topic: home/your-device-uuid
- Secure MQTT by default (port 8883) using
WiFiClientSecure
(development mode withsetInsecure()
).
Topics:
- Base topic is provided over BLE (
topic
), device appends/<uuid>
- Final topic:
<baseTopic>/<uuid>
QoS/retain:
- LWT uses retain so offline state is visible
- Initial presence publish uses retain so new subscribers get last state
- Mode 0: Off (all LOW)
- Mode 1: Alternating blink (reds vs blues), 500 ms
- Mode 2: Cross pattern, 500 ms
- Mode 3: Sequential chase (R1 → B1 → R2 → B2), 500 ms cycle
- Provides BLE provisioning UI (send SSID, password, device id, base topic)
- Subscribes/publishes on
<baseTopic>/<uuid>
- UI to toggle status and choose modes
I am a university student building this project to improve my skills in embedded systems, IoT, networking, and SwiftUI. Feedback and suggestions are welcome!