Documentation moved to the root directory

This commit is contained in:
2026-06-15 00:29:08 +02:00
parent 9a54c8142e
commit 1f89e70808
2 changed files with 224 additions and 226 deletions

224
README.md
View File

@@ -1,2 +1,226 @@
# M1730-ESP32 # M1730-ESP32
ESP32 firmware that drives one or more **M1730 analog panel meters** (moving-coil ammeters) via PWM, with a web configuration UI and Home Assistant integration over MQTT.
## Table of contents
- [How it works](#how-it-works)
- [Hardware](#hardware)
- [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)
---
## How it works
Each meter needle is controlled by a PWM signal on a GPIO pin. The firmware maps a **0100 %** value to a PWM duty cycle, scaled by a per-meter **Max Duty** percentage that you calibrate for full-scale deflection. All settings are stored in LittleFS (`/config.json`) so they survive reboots.
```
needle position = (currentValue / 100) × (maxDuty / 100) × 1023 ticks
```
PWM runs at 5 kHz with 10-bit resolution (01023).
### Physical range mapping
Each meter has configurable **Min** / **Max** values that define its physical range (e.g. 050 A). The firmware normalises to 0100 % internally, but MQTT publishes and receives in physical units — Home Assistant never has to deal with percentages.
---
## Hardware
| Item | Details |
|------|---------|
| MCU | ESP32-S3 DevKitC-1 |
| Meter | M1730 moving-coil panel ammeter (or any PWM-driveable analog meter) |
| Max meters | 8 simultaneous (ESP32 LEDC channels 07) |
| PWM freq | 5 kHz |
| PWM resolution | 10-bit |
Connect the meter coil (via a current-limiting resistor sized for full-scale) between a GPIO pin and GND. Find the correct resistor value by raising **Max Duty** slowly until the needle reaches full scale. 660
---
## Building and flashing
The project uses [PlatformIO](https://platformio.org/).
```bash
# Build
pio run
# Flash
pio run --target upload
# Open serial monitor (115200 baud)
pio device monitor
```
Board target: `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 **M1730**. 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: `m1730`).
3. Click **Save**. The device connects to your network and restarts.
After connecting, the device is reachable at:
- `http://m1730.local` (mDNS, works on most local networks)
- `http://<IP address>` (shown in the serial monitor on boot)
---
## Web UI
Browse to the device address to open the configuration page.
### Info panel
Shows the current hostname and IP address.
### Hostname
Sets the mDNS name (`<hostname>.local`) and the MQTT device name. Saved across reboots.
### Meters
Use the **Meters** dropdown to add or remove meters (110). Each meter has:
| Field | Description |
|-------|-------------|
| Pin | GPIO pin number connected to the meter coil via current limiting resistor |
| Name | Label shown in Home Assistant and the web UI |
| Unit | Optional unit string shown in Home Assistant (e.g. `W`, `A`, `°C`) |
| Min / Max | Physical range of the meter (e.g. 050 A, 03000 W). MQTT publishes and receives values in this range; HA discovery uses these as the number entity min/max |
| Max Duty | PWM duty at full scale, as a percentage (0100). Calibrate this so the needle just reaches full deflection |
| Output slider | Moves the needle live (0100 % of the configured range). Also sent to MQTT |
Click **Save** to persist all settings. Changing the meter count also triggers an immediate save and meter re-attach.
---
## MQTT / Home Assistant
### Enabling MQTT
In the **MQTT** section of the web UI:
| Field | Description |
|-------|-------------|
| Enable | Toggle MQTT on/off |
| Broker | Hostname or IP of your MQTT broker |
| Port | Default `1883` |
| User / Pass | Broker credentials |
| Prefix | Topic prefix (default `m1730`) |
### Home Assistant auto-discovery
On connect, the device publishes discovery payloads to `homeassistant/number/…/config`. Each meter appears in HA as a **Number** entity with `min`/`max` taken from the meter's configured physical range and a step of 0.1. The entities are grouped under a single HA device named after the hostname.
### Value mapping
Internally the firmware works with a 0100 % duty value. MQTT publishes and receives **physical values** — the percentage is transparently mapped to the meter's MinMax range:
```
physicalValue = percentage / 100 × (rangeMax - rangeMin) + rangeMin
```
For example, with Min=0 and Max=50, the slider at 50 % publishes `25.0` to MQTT, and a command of `25.0` on the `/set` topic moves the slider to 50 %.
### Topics
| Direction | Topic | Description |
|-----------|-------|-------------|
| Published | `<prefix>/meter/<n>/current` | Current meter value in physical units (retained) |
| Subscribed | `<prefix>/meter/<n>/current/set` | Set meter value in physical units |
| Published | `<prefix>/status` | `online: true` on connect, `online: false` as LWT |
`<n>` is the zero-based meter index.
### Reconnection
The firmware probes the broker TCP port before attempting a full MQTT connect. If the broker is unreachable, it retries every 30 seconds without blocking the web server.
### Example HA automation
```yaml
automation:
- alias: Show solar power on meter
trigger:
platform: state
entity_id: sensor.solar_power_w
action:
service: number.set_value
target:
entity_id: number.m1730_solar
data:
value: "{{ trigger.to_state.state | float | round(1) }}"
# Values are in physical units — if the meter Min=0, Max=3000, the HA
# number entity directly accepts watts, no conversion needed.
```
---
## HTTP API
### `GET /set?i=<index>&v=<value>`
Immediately moves meter `<index>` to `<value>` (0100), updates PWM, persists the value to flash, and publishes to MQTT. Used by the live slider on the web UI.
| Parameter | Type | Description |
|-----------|------|-------------|
| `i` | integer | Meter index (0-based) |
| `v` | float | Value 0100 |
Returns `200 OK` on success, `400` on bad input.
### `GET /`
Returns the full HTML configuration page.
### `POST /config`
Saves all configuration from the HTML form and re-applies PWM to all meters. Responds with the updated configuration page.
---
## Configuration reference
Config is stored as JSON in LittleFS at `/config.json`.
```json
{
"hostname": "m1730",
"mqtt": {
"enabled": true,
"host": "192.168.1.10",
"port": 1883,
"user": "ha",
"pass": "secret",
"prefix": "m1730"
},
"meters": [
{
"pin": 4,
"maxD": 72.5,
"current": 45.0,
"name": "Solar",
"unit": "W",
"rangeMin": 0.0,
"rangeMax": 3000.0
}
]
}
```
The file is written by the web UI and should not need manual editing. To reset to factory defaults, delete the file or erase flash with `pio run --target erase`.

View File

@@ -1,226 +0,0 @@
# M1730-ESP32
ESP32 firmware that drives one or more **M1730 analog panel meters** (moving-coil ammeters) via PWM, with a web configuration UI and Home Assistant integration over MQTT.
## Table of contents
- [How it works](#how-it-works)
- [Hardware](#hardware)
- [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)
---
## How it works
Each meter needle is controlled by a PWM signal on a GPIO pin. The firmware maps a **0100 %** value to a PWM duty cycle, scaled by a per-meter **Max Duty** percentage that you calibrate for full-scale deflection. All settings are stored in LittleFS (`/config.json`) so they survive reboots.
```
needle position = (currentValue / 100) × (maxDuty / 100) × 1023 ticks
```
PWM runs at 5 kHz with 10-bit resolution (01023).
### Physical range mapping
Each meter has configurable **Min** / **Max** values that define its physical range (e.g. 050 A). The firmware normalises to 0100 % internally, but MQTT publishes and receives in physical units — Home Assistant never has to deal with percentages.
---
## Hardware
| Item | Details |
|------|---------|
| MCU | ESP32-S3 DevKitC-1 |
| Meter | M1730 moving-coil panel ammeter (or any PWM-driveable analog meter) |
| Max meters | 8 simultaneous (ESP32 LEDC channels 07) |
| PWM freq | 5 kHz |
| PWM resolution | 10-bit |
Connect the meter coil (via a current-limiting resistor sized for full-scale) between a GPIO pin and GND. Find the correct resistor value by raising **Max Duty** slowly until the needle reaches full scale. 660
---
## Building and flashing
The project uses [PlatformIO](https://platformio.org/).
```bash
# Build
pio run
# Flash
pio run --target upload
# Open serial monitor (115200 baud)
pio device monitor
```
Board target: `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 **M1730**. 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: `m1730`).
3. Click **Save**. The device connects to your network and restarts.
After connecting, the device is reachable at:
- `http://m1730.local` (mDNS, works on most local networks)
- `http://<IP address>` (shown in the serial monitor on boot)
---
## Web UI
Browse to the device address to open the configuration page.
### Info panel
Shows the current hostname and IP address.
### Hostname
Sets the mDNS name (`<hostname>.local`) and the MQTT device name. Saved across reboots.
### Meters
Use the **Meters** dropdown to add or remove meters (110). Each meter has:
| Field | Description |
|-------|-------------|
| Pin | GPIO pin number connected to the meter coil via current limiting resistor |
| Name | Label shown in Home Assistant and the web UI |
| Unit | Optional unit string shown in Home Assistant (e.g. `W`, `A`, `°C`) |
| Min / Max | Physical range of the meter (e.g. 050 A, 03000 W). MQTT publishes and receives values in this range; HA discovery uses these as the number entity min/max |
| Max Duty | PWM duty at full scale, as a percentage (0100). Calibrate this so the needle just reaches full deflection |
| Output slider | Moves the needle live (0100 % of the configured range). Also sent to MQTT |
Click **Save** to persist all settings. Changing the meter count also triggers an immediate save and meter re-attach.
---
## MQTT / Home Assistant
### Enabling MQTT
In the **MQTT** section of the web UI:
| Field | Description |
|-------|-------------|
| Enable | Toggle MQTT on/off |
| Broker | Hostname or IP of your MQTT broker |
| Port | Default `1883` |
| User / Pass | Broker credentials |
| Prefix | Topic prefix (default `m1730`) |
### Home Assistant auto-discovery
On connect, the device publishes discovery payloads to `homeassistant/number/…/config`. Each meter appears in HA as a **Number** entity with `min`/`max` taken from the meter's configured physical range and a step of 0.1. The entities are grouped under a single HA device named after the hostname.
### Value mapping
Internally the firmware works with a 0100 % duty value. MQTT publishes and receives **physical values** — the percentage is transparently mapped to the meter's MinMax range:
```
physicalValue = percentage / 100 × (rangeMax - rangeMin) + rangeMin
```
For example, with Min=0 and Max=50, the slider at 50 % publishes `25.0` to MQTT, and a command of `25.0` on the `/set` topic moves the slider to 50 %.
### Topics
| Direction | Topic | Description |
|-----------|-------|-------------|
| Published | `<prefix>/meter/<n>/current` | Current meter value in physical units (retained) |
| Subscribed | `<prefix>/meter/<n>/current/set` | Set meter value in physical units |
| Published | `<prefix>/status` | `online: true` on connect, `online: false` as LWT |
`<n>` is the zero-based meter index.
### Reconnection
The firmware probes the broker TCP port before attempting a full MQTT connect. If the broker is unreachable, it retries every 30 seconds without blocking the web server.
### Example HA automation
```yaml
automation:
- alias: Show solar power on meter
trigger:
platform: state
entity_id: sensor.solar_power_w
action:
service: number.set_value
target:
entity_id: number.m1730_solar
data:
value: "{{ trigger.to_state.state | float | round(1) }}"
# Values are in physical units — if the meter Min=0, Max=3000, the HA
# number entity directly accepts watts, no conversion needed.
```
---
## HTTP API
### `GET /set?i=<index>&v=<value>`
Immediately moves meter `<index>` to `<value>` (0100), updates PWM, persists the value to flash, and publishes to MQTT. Used by the live slider on the web UI.
| Parameter | Type | Description |
|-----------|------|-------------|
| `i` | integer | Meter index (0-based) |
| `v` | float | Value 0100 |
Returns `200 OK` on success, `400` on bad input.
### `GET /`
Returns the full HTML configuration page.
### `POST /config`
Saves all configuration from the HTML form and re-applies PWM to all meters. Responds with the updated configuration page.
---
## Configuration reference
Config is stored as JSON in LittleFS at `/config.json`.
```json
{
"hostname": "m1730",
"mqtt": {
"enabled": true,
"host": "192.168.1.10",
"port": 1883,
"user": "ha",
"pass": "secret",
"prefix": "m1730"
},
"meters": [
{
"pin": 4,
"maxD": 72.5,
"current": 45.0,
"name": "Solar",
"unit": "W",
"rangeMin": 0.0,
"rangeMax": 3000.0
}
]
}
```
The file is written by the web UI and should not need manual editing. To reset to factory defaults, delete the file or erase flash with `pio run --target erase`.