Files
Rotary-Dial/README.md
T

134 lines
4.4 KiB
Markdown
Raw 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.
# 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 |