A desktop lighthouse lamp: ESP32-S3 drives an 8×8 WS2812B LED matrix mounted inside a Fresnel lens to simulate a rotating lighthouse beam — no moving parts.
A real lighthouse rotates its optics around a fixed light source. This project inverts that: the lens is static, and LEDs fire in sequence to synthesize the sweeping beam effect electronically.
| Component | Part | Notes |
|---|---|---|
| MCU + LED panel | Waveshare ESP32-S3-Matrix | ESP32-S3 + 8×8 WS2812B, USB-C |
| Optics | Fresnel lens | Mounted over the LED matrix |
| Power | USB-C 5V | Via onboard connector |
| Enclosure | TBD | Lighthouse-shaped, 3D printed |
- Framework: ESP-IDF v5.3 (FreeRTOS)
- LED driver: RMT peripheral → WS2812B
- Watchdog: esp_task_wdt (5 s timeout)
- Language: C
- Core beacon animation (rotating beam sweep with quadratic falloff)
- 4 animation modes — BEACON, STROBE, AMBIENT, OFF
- Input validation — speed, brightness, color, mode clamping with warnings
- Error handling — esp_err_t propagation, cleanup on failure
- Unit tests (beacon math, LED driver logic)
- CI pipeline (build, static analysis, Wokwi simulation)
- Task watchdog and stack monitoring
- Phase 2: Runtime configuration via REST API + Web UI
- IPC queue for thread-safe config updates
- NVS persistent storage (speed, mode, brightness, color)
- WiFi STA + AP fallback (AP password derived from MAC)
- HTTP server with embedded Web UI
- Configurable rotation speed (0.5-20 sec per rotation)
- Brightness control (0.0-1.0)
- Color picker with presets and live preview
- HTTP Basic Auth for REST API
- OTA updates (HTTPS-only)
- mDNS service discovery (fresnel-beacon.local)
- Renderer abstraction (mock + LED renderers)
app_main
├── config_manager_init() — load config from NVS
├── wifi_manager_init() — STA + AP fallback
├── http_server_start() — REST API + Web UI
├── mdns_service_init() — mDNS advertisement
└── beacon_animation_task — main animation loop
├── Per-frame: clear matrix, compute beam, set pixels, flush
├── process_ipc_commands() — apply config updates from queue
├── esp_task_wdt: subscribe + periodic reset
└── Stack high-water mark logging (every 30 s)
fresnel-beacon/
├── main/
│ ├── main.c — app_main: init all subsystems
│ ├── credentials.h.example — WiFi credentials template
│ └── CMakeLists.txt
├── components/
│ ├── led_driver/ — RMT-based WS2812B driver
│ │ ├── led_driver.c
│ │ └── include/led_driver.h
│ ├── beacon_animation/ — Animation mode state machine
│ │ ├── beacon_animation.c
│ │ └── include/
│ │ ├── beacon_animation.h
│ │ └── beacon_math.h — Inline math (pixel_index, angle_diff)
│ ├── config_manager/ — Runtime config + NVS persistence
│ │ ├── config_manager.c
│ │ └── include/config_manager.h
│ ├── ipc/ — Inter-process communication queue
│ │ ├── ipc.c
│ │ └── include/ipc.h
│ ├── http_server/ — REST API + embedded Web UI
│ │ ├── http_server.c
│ │ └── include/http_server.h
│ ├── wifi_manager/ — WiFi STA/AP + event handling
│ │ ├── wifi_manager.c
│ │ └── include/wifi_manager.h
│ ├── renderer/ — Renderer abstraction (vtable)
│ │ ├── renderer.c
│ │ └── include/renderer.h
│ ├── auth/ — HTTP Basic Auth
│ │ ├── auth.c
│ │ └── include/auth.h
│ ├── ota_manager/ — OTA updates (HTTPS)
│ │ ├── ota_manager.c
│ │ └── include/ota_manager.h
│ └── mdns_service/ — mDNS service discovery
│ ├── mdns_service.c
│ └── include/mdns_service.h
├── test/
│ ├── test_beacon_math.c — Host unit tests (angle math, pixel mapping)
│ ├── test_led_driver.c — Host unit tests (GRB order, bounds)
│ ├── test_config_manager.c — ESP-IDF Unity tests (NVS roundtrip)
│ ├── test_ipc.c — ESP-IDF Unity tests (queue ops)
│ ├── test_esp_http_server.c — ESP-IDF compile-check
│ ├── test_esp_led_driver.c — ESP-IDF compile-check
│ └── test_esp_wifi_manager.c — ESP-IDF compile-check
├── CMakeLists.txt
├── sdkconfig.defaults
├── diagram.json — Wokwi simulation layout
├── wokwi.toml — Wokwi config
├── scenario.yaml — Wokwi test scenario
├── .clang-format — Code style (LLVM, 4-space, 100 col)
├── .clang-tidy — Static analysis config
└── .github/workflows/
├── ci.yml — Test, analyze, build + simulate
└── release.yml — Build + publish on v* tags
| Method | Endpoint | Auth | Description |
|---|---|---|---|
| GET | /api/status |
No | Current config + WiFi status (JSON) |
| POST | /api/config |
Yes | Update config (speed, mode, brightness, color) |
| POST | /api/ota |
Yes | Start OTA update (HTTPS URL required) |
POST /api/config example:
curl -X POST http://fresnel-beacon.local/api/config \
-H "Content-Type: application/json" \
-H "Authorization: Basic $(echo -n 'admin:fresnel' | base64)" \
-d '{"speed_rpm":12.5,"mode":0,"brightness":0.8,"color":"0xFFA028"}'POST /api/ota example:
curl -X POST http://fresnel-beacon.local/api/ota \
-H "Content-Type: application/json" \
-H "Authorization: Basic $(echo -n 'admin:fresnel' | base64)" \
-d '{"url":"https://example.com/firmware.bin"}'AP mode: When WiFi connection fails, device starts AP with SSID Fresnel-Beacon-XXYY and password derived from MAC address (printed in serial log on boot).
- HTTP Basic Auth — All mutating endpoints require authentication
- HTTPS-only OTA — HTTP URLs rejected with 400 error
- Input validation — All config parameters clamped to safe ranges
- WiFi credential isolation — Separate struct with mutex-protected API
- Rate limiting — Atomic counter for HTTP requests
- ESP-IDF v5.3.x
- Target:
esp32s3 - Flash via USB-C (native USB, no UART adapter needed)
idf.py set-target esp32s3
idf.py build
idf.py flashHost unit tests run without ESP-IDF or hardware:
gcc -Icomponents/led_driver/include -Icomponents/beacon_animation/include -I. test/test_beacon_math.c -lm -o test_beacon_math && ./test_beacon_math
gcc -Icomponents/led_driver/include -I. test/test_led_driver.c -lm -o test_led_driver && ./test_led_driverFormat all C/H files before committing:
find components main test -name "*.c" -o -name "*.h" | xargs clang-format -iStyle config: .clang-format (LLVM-based, 4-space indent, 100 column limit).
Three jobs on every push to main:
| Job | Description |
|---|---|
| test | Compile and run host unit tests + formatting check |
| analyze | cppcheck + clang-tidy static analysis |
| build-and-simulate | ESP-IDF build, size report, Wokwi simulation |
Releases are published automatically on v* tags.
MIT — see LICENSE.