134 lines
4.4 KiB
Markdown
134 lines
4.4 KiB
Markdown
# Rotary Dial
|
||
|
||
ESP32 firmware that reads a pulse-generating telephone rotary dial and publishes the dialled number to Home Assistant over MQTT.
|
||
|
||
Built on the [ESP32 Device Framework](../Device-Framework).
|
||
|
||
## How it works
|
||
|
||
A classic rotary telephone dial generates a burst of pulses as it returns to rest after each digit — one pulse for `1`, two for `2`, …, nine for `9`, ten for `0`. The firmware counts those pulses on a single GPIO pin and assembles digits into a string. When the user stops dialling (2.5 s silence), the complete number is published to MQTT.
|
||
|
||
Leading zeros are preserved: dialling `07` sends `"07"`, not `"7"`.
|
||
|
||
## Hardware
|
||
|
||
| Item | Details |
|
||
|------|---------|
|
||
| MCU | ESP32 (any variant) |
|
||
| Dial | Pulse-generating rotary telephone dial |
|
||
| Pulse pin | GPIO 4 (`DIAL_PIN` in `main.cpp`) |
|
||
| Wiring | Pulse contact between GPIO 4 and GND. Pin uses `INPUT_PULLUP`. |
|
||
|
||
The pulse contact is normally open. As the dial returns it briefly shorts GPIO 4 to GND on each pulse — the rising edge (release) is what the interrupt counts.
|
||
|
||
If your dial's contact is normally closed instead, change the interrupt trigger in `setup()` from `RISING` to `FALLING`.
|
||
|
||
## Building and flashing
|
||
|
||
```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
|
||
|
||
# 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 the device opens an access point named **Rotary-Dial**. Connect with any phone or laptop; a captive portal will appear.
|
||
|
||
1. Enter your Wi-Fi credentials.
|
||
2. Optionally set the **Device hostname** (default: `rotary-dial`, max 63 characters).
|
||
3. Save. The device joins your network and restarts.
|
||
|
||
Reachable at `http://rotary-dial.local` or the IP shown in the serial monitor.
|
||
|
||
### 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 configure hostname, authentication, and MQTT. The logo in the header is served from `data/logo.png` in the filesystem image.
|
||
|
||
Click **Save** to persist settings. Changing the hostname automatically restarts the device. Click **Reset Device** to restart immediately.
|
||
|
||
## 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 |
|
||
|
||
## MQTT
|
||
|
||
### Topics
|
||
|
||
| Direction | Topic | Description |
|
||
|-----------|-------|-------------|
|
||
| Published | `<prefix>/dialed` | Complete dialled number as a string (not retained) |
|
||
| Published | `<prefix>/status` | `online` on connect, `offline` as LWT (retained) |
|
||
|
||
Default prefix: `dial`
|
||
|
||
### Home Assistant
|
||
|
||
On MQTT connect the device publishes a discovery payload so HA automatically creates a **sensor** entity that shows the last dialled number.
|
||
|
||
Example automation:
|
||
|
||
```yaml
|
||
automation:
|
||
- alias: Act on dialled number
|
||
trigger:
|
||
platform: mqtt
|
||
topic: dial/dialed
|
||
action:
|
||
- choose:
|
||
- conditions:
|
||
- condition: template
|
||
value_template: "{{ trigger.payload == '112' }}"
|
||
sequence:
|
||
- service: notify.mobile_app
|
||
data:
|
||
message: "Emergency number dialled!"
|
||
```
|
||
|
||
## Timing constants
|
||
|
||
Adjust in `main.cpp` if your dial runs faster or slower than standard:
|
||
|
||
| Constant | Default | Description |
|
||
|----------|---------|-------------|
|
||
| `PULSE_DEBOUNCE_MS` | 15 ms | Minimum time between counted edges |
|
||
| `INTER_PULSE_MS` | 350 ms | Silence after last pulse = digit complete |
|
||
| `INTER_DIGIT_MS` | 2500 ms | Silence after last digit = number complete |
|