124 lines
3.3 KiB
Markdown
124 lines
3.3 KiB
Markdown
# DD3-LoRa-Bridge-MultiSender
|
|
|
|
Unified firmware for LilyGO T3 v1.6.1 (ESP32 + SX1276 + SSD1306) running as either:
|
|
- `Sender`: reads energy-only values from multiple IEC 62056-21 meters and sends binary batches over LoRa.
|
|
- `Receiver`: accepts batches, ACKs with optional time, publishes to MQTT/web.
|
|
|
|
## Protocol (minimal)
|
|
|
|
Frame format:
|
|
|
|
`[msg_kind:1][dev_id_short:2][payload...][crc16:2]`
|
|
|
|
`msg_kind`:
|
|
- `0`: `BATCH_UP` (Sender -> Receiver)
|
|
- `1`: `ACK_DOWN` (Receiver -> Sender)
|
|
|
|
Removed from protocol:
|
|
- protocol version field
|
|
- payload type field
|
|
- MeterData JSON/compressed LoRa path
|
|
- standalone TimeSync packets
|
|
|
|
CRC16 validation is still required on every frame.
|
|
|
|
## Payloads
|
|
|
|
### 1) `BATCH_UP`
|
|
- Uses existing binary batch/chunk transport.
|
|
- `sample_count == 0` is valid and means `SYNC_REQUEST`.
|
|
- Payload starts with `schema_id`:
|
|
- `0`: legacy schema (kept for compatibility)
|
|
- `1`: `EnergyMulti` schema (ts + integer kWh for up to 3 meters)
|
|
|
|
### 2) `ACK_DOWN` (7 bytes)
|
|
- `flags` (`u8`): bit0 = `time_valid`
|
|
- `batch_id` (`u16`, big-endian)
|
|
- `epoch_utc` (`u32`, big-endian)
|
|
|
|
Receiver sets:
|
|
- `time_valid=1` only when receiver time is authoritative and sane.
|
|
- Otherwise `time_valid=0` and `epoch_utc=0`.
|
|
|
|
## Time bootstrap safety
|
|
|
|
Sender starts with:
|
|
- `g_time_acquired=false`
|
|
- no real sampling/batching
|
|
- periodic `SYNC_REQUEST` every `SYNC_REQUEST_INTERVAL_MS` (default `15000ms`)
|
|
|
|
Sender only accepts time from `ACK_DOWN` if:
|
|
- `time_valid == 1`
|
|
- `epoch_utc >= 2026-02-01 00:00:00 UTC` (`MIN_ACCEPTED_EPOCH_UTC = 1769904000`)
|
|
|
|
Only then:
|
|
- system time is set
|
|
- `g_time_acquired=true`
|
|
- normal 1 Hz sampling + batch transmit starts
|
|
|
|
This guarantees no pre-`2026-02-01` epoch reaches MQTT or SD/DB paths.
|
|
|
|
## Multi-meter sender mode
|
|
|
|
- Meter protocol: IEC 62056-21 ASCII Mode D
|
|
- UART framing: `9600 7E1`
|
|
- Extracted OBIS: only total energy `1-0:1.8.0`
|
|
- Conversion: kWh is sent as integer using `floor` (`12345.67 -> 12345`)
|
|
|
|
### Meter count by build mode
|
|
|
|
- Debug builds (`SERIAL_DEBUG_MODE=1`): `METER_COUNT=2`
|
|
- keeps USB serial logs on UART0
|
|
- uses UART1 + UART2 for meters
|
|
- Prod builds (`SERIAL_DEBUG_MODE=0`): `METER_COUNT=3`
|
|
- no serial logging
|
|
- reclaims UART0 for meter 3 RX
|
|
|
|
### Meter RX pins (TTGO T3 v1.6.1 defaults)
|
|
|
|
- `PIN_METER1_RX = GPIO34` (UART2 RX)
|
|
- `PIN_METER2_RX = GPIO25` (UART1 RX)
|
|
- `PIN_METER3_RX = GPIO3` (UART0 RX, prod only)
|
|
|
|
All pins are configurable in `include/config.h`.
|
|
|
|
## Receiver MQTT output (EnergyMulti)
|
|
|
|
For `schema_id=1`, MQTT payload includes integer fields:
|
|
- `energy1_kwh`
|
|
- `energy2_kwh`
|
|
- `energy3_kwh` (when meter count is 3)
|
|
|
|
## Receiver behavior
|
|
|
|
On `BATCH_UP`:
|
|
1. Decode batch/chunks.
|
|
2. Send `ACK_DOWN` immediately.
|
|
3. If `sample_count == 0`: treat as `SYNC_REQUEST`, do not publish MQTT/update stats.
|
|
4. Else decode and publish samples as normal.
|
|
|
|
## Sender/Receiver debug logs (`SERIAL_DEBUG_MODE`)
|
|
|
|
Sender:
|
|
- `sync: request tx batch_id=%u`
|
|
- `ack: rx ok batch_id=%u time_valid=%u epoch=%lu set=%u`
|
|
- `ack: timeout batch_id=%u retry=%u`
|
|
|
|
Receiver:
|
|
- `ack: tx batch_id=%u time_valid=%u epoch=%lu samples=%u`
|
|
|
|
## Removed hardware dependency
|
|
|
|
DS3231 RTC support was removed:
|
|
- no RTC files
|
|
- no RTC init/load/set logic
|
|
- no `ENABLE_DS3231` flow
|
|
|
|
## Build
|
|
|
|
```bash
|
|
pio run -e lilygo-t3-v1-6-1
|
|
pio run -e lilygo-t3-v1-6-1-test
|
|
pio run -e lilygo-t3-v1-6-1-prod
|
|
```
|