Files
2026-06-29 01:10:47 +02:00

190 lines
6.1 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# ESP32 Device Framework
A reusable ESP32 firmware base with Wi-Fi, web configuration UI, authentication, MQTT / Home Assistant integration, and factory reset. Add device-specific logic on top without re-implementing the boilerplate.
## Table of contents
- [Features](#features)
- [Building and flashing](#building-and-flashing)
- [First-time Wi-Fi setup](#first-time-wi-fi-setup)
- [Web UI](#web-ui)
- [MQTT / Home Assistant](#mqtt--home-assistant)
- [HTTP API](#http-api)
- [Configuration reference](#configuration-reference)
- [Extending the framework](#extending-the-framework)
---
## Features
- **Wi-Fi** via WiFiManager — captive-portal setup on first boot, credentials stored in flash
- **mDNS** — device reachable as `<hostname>.local`
- **Web UI** — responsive configuration page served from the device, with logo support
- **Authentication** — optional HTTP Basic Auth to protect the web UI
- **MQTT** — connects to any broker, publishes an availability topic, reconnects automatically without blocking the web server
- **Home Assistant discovery** — stub ready for your entities
- **Factory reset** — rapid power-cycling clears settings (5× config, 10× config + Wi-Fi credentials)
- **Config persistence** — all settings stored as JSON in LittleFS (`/config.json`)
---
## Building and flashing
The project uses [PlatformIO](https://platformio.org/).
```bash
# Build
pio run -e esp32dev
# Flash firmware
pio run -e esp32dev --target upload
# Upload filesystem image (logo, etc.) — only needed when data/ changes
pio run -e esp32dev --target uploadfs
# Open serial monitor (115 200 baud)
pio device monitor
```
> **Note:** close the serial monitor before uploading — it holds the serial port exclusively.
Board targets: `esp32dev`, `esp32-s3-devkitc-1`
---
## First-time Wi-Fi setup
On first boot (or when stored Wi-Fi credentials are missing), the device starts an access point named **ESP32-Device**. Connect to it with any phone or laptop — a captive portal will appear automatically.
1. Enter your Wi-Fi SSID and password.
2. Optionally change the **Device hostname** (default: `esp32-device`, max 63 characters).
3. Click **Save**. The device connects to your network and restarts.
After connecting, the device is reachable at:
- `http://<hostname>.local` (default `http://esp32-device.local`) — mDNS, works on most local networks
- `http://<IP address>` — shown in the serial monitor on boot
### Factory reset
Power-cycle (or press EN) rapidly before the 3-second boot window expires. The reset counter accumulates across rapid reboots:
| Cycles | Effect |
|--------|--------|
| 5 9 | Clears hostname, auth, and MQTT config; restarts into normal Wi-Fi |
| 10+ | Clears all of the above **plus** stored Wi-Fi credentials; restarts into captive portal |
Serial output shows the accumulated count:
```
[BOOT] reset count 3
[BOOT] reset count 4
...
[BOOT] reset count 5, triggering factory reset
[BOOT] factory reset triggered (clearWifi=0)!
[BOOT] factory reset complete, restarting...
```
---
## Web UI
Browse to the device address to open the configuration page.
### Logo
Place a `logo.png` in the `data/` directory and upload the filesystem image once. The logo appears in the top-left of the configuration page. The `data/logo.png` included in this repo is a 200×200 px transparent PNG.
### Info panel
Shows the current hostname and IP address.
### Hostname
Sets the mDNS name (`<hostname>.local`). Changing the hostname automatically restarts the device so mDNS re-registers with the new name.
### Security
| Field | Description |
|-------|-------------|
| Require password | Enables HTTP Basic Auth on the web UI |
| Password | Password for the `admin` account. Leave blank to keep the existing password |
### MQTT
| Field | Description |
|-------|-------------|
| Enable | Toggle MQTT on/off |
| Broker | Hostname or IP of your MQTT broker |
| Port | Default `1883` |
| User / Pass | Broker credentials (leave Pass blank to keep the existing value) |
| Prefix | Topic prefix (default `device`) |
Click **Save** to persist all settings. Click **Reset Device** to restart the device immediately.
---
## MQTT / Home Assistant
### Connection behaviour
On connect the device publishes `online` to `<prefix>/status` (retained) and sets `offline` as the LWT. If the broker is unreachable, the firmware probes the TCP port before attempting a full MQTT connect and retries every 30 seconds without blocking the web server.
### Extending with discovery and topics
Three stubs in `main.cpp` are the intended extension points:
| Function | Purpose |
|----------|---------|
| `mqttSubscribe()` | Subscribe to command topics after connecting |
| `mqttCallback()` | Handle incoming messages |
| `mqttPublishDiscovery()` | Publish Home Assistant discovery payloads |
---
## HTTP API
| Method | Path | Description |
|--------|------|-------------|
| `GET` | `/` | HTML configuration page |
| `GET` | `/logo.png` | Logo image served from LittleFS |
| `POST` | `/config` | Save configuration; redirects to `/`. Restarts if hostname changed |
| `POST` | `/reset` | Restart the device immediately |
---
## Configuration reference
Config is stored as JSON in LittleFS at `/config.json`.
```json
{
"hostname": "esp32-device",
"mqtt": {
"en": true,
"host": "192.168.1.10",
"port": 1883,
"user": "ha",
"pass": "secret",
"prefix": "device"
},
"auth": {
"en": true,
"pass": "secret"
}
}
```
The file is written by the web UI. To erase everything including flash, use `pio run --target erase`.
---
## Extending the framework
1. Add your hardware setup in `setup()` after the framework initialises.
2. Add your per-loop logic in `loop()` alongside `server.handleClient()` and `mqttLoop()`.
3. Fill in the three MQTT stubs to subscribe, receive, and publish discovery payloads.
4. Add extra web routes with `server.on(...)` in `startServer()` if you need device-specific endpoints.
5. Persist extra config fields by adding keys to `loadConfig()` and `saveConfig()`.
6. Place static files (images, etc.) in `data/` and upload the filesystem image with `pio run --target uploadfs`.