Files
DD3-LoRa-Bridge-MultiSender/README.md

3.3 KiB

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

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