The Modbus RTU Engine is the primary data-acquisition subsystem of the Aslandi Edge Converter. It manages serial communication across the isolated RS-485 physical bus, sequentially polling connected energy meters and validating frame integrity.
1. Physical Bus Configuration (Channel 0)
In production release v1, communication is strictly bound to Bus Channel 0 (Waveshare Pico-2CH-RS485 transceiver Channel 0 via RP2350 UART0):
| Parameter | Specification | Notes |
|---|
| Physical Layer | RS-485 Differential (A / B lines) | Requires 120 Ω termination resistor at both bus ends. |
| Baud Rate | 9600 bps (configurable 4800–115200) | Default factory baseline is 9600. |
| Data Framing | 8 Data Bits, No Parity, 1 Stop Bit (8N1) | Compliant with LEM-60 and DTSU666 standards. |
| Bus Channel | Channel 0 exclusively | Channel 1 is formally disabled in release v1. |
| Capacity Limit | Up to 50 energy meters | Tested on 50-meter simulated and physical bus networks. |
| Response Timeout | 200 ms per slave device | 3 retries before flagging communication timeout. |
flowchart LR
subgraph Converter["Converter (RP2350)"]
UART0["UART0 Controller"]
TXEN["GPIO GPIO_RS485_TX_EN"]
RS485["Waveshare Transceiver (Ch 0)"]
UART0 --- RS485
TXEN --> RS485
end
subgraph RS485Bus["RS-485 Daisy Chain (Channel 0)"]
M1["Meter 1 (Addr 0x01)"]
M2["Meter 2 (Addr 0x02)"]
M50["Meter 50 (Addr 0x32)"]
RS485 === M1
M1 === M2
M2 === M50
end
2. Supported Meter Profiles & Register Maps
A. Zamel LEM-60 (Single-Phase Meter)
- Modbus Function:
0x03 (Read Holding Registers) or 0x04 (Read Input Registers).
- Endianness: Big-Endian (MSB first) 32-bit Float or 32-bit Integer.
| Parameter | Modbus Address | Type | Unit | Multiplier / Scale |
|---|
| Active Energy Import | 0x0000 | 32-bit uint | kWh | $0.01\text{ kWh}$ |
| Active Energy Export | 0x0002 | 32-bit uint | kWh | $0.01\text{ kWh}$ |
| Voltage L1 | 0x0004 | 16-bit uint | V | $0.1\text{ V}$ |
| Current L1 | 0x0005 | 16-bit uint | A | $0.01\text{ A}$ |
| Active Power | 0x0006 | 32-bit int | W | $1\text{ W}$ |
| Frequency | 0x0008 | 16-bit uint | Hz | $0.1\text{ Hz}$ |
B. CHINT DTSU666 (Three-Phase Meter)
- Modbus Function:
0x03 (Read Holding Registers) / 0x04 (Read Input Registers).
- Format: IEEE 754 32-bit Floating Point (High word first).
| Parameter | Modbus Address | Type | Unit | Description |
|---|
| Total Active Energy | 0x2000 | Float32 | kWh | Combined L1 + L2 + L3 imported energy |
| Total Reactive Energy | 0x200A | Float32 | kVARh | Combined reactive power import |
| Voltage L1 / L2 / L3 | 0x200C - 0x2010 | Float32 | V | Phase voltages (RMS) |
| Current L1 / L2 / L3 | 0x2012 - 0x2016 | Float32 | A | Phase currents (RMS) |
| Total Active Power | 0x2018 | Float32 | kW | Net three-phase active power |
| Total Power Factor | 0x2022 | Float32 | - | Cos phi network power factor |
3. Polling Cycle & Timeout Handling
flowchart TD
Start["Poll Timer Fires (e.g. every 60s)"] --> SelectMeter["Select Next Meter from Registry (1..N)"]
SelectMeter --> PreparePacket["Build Modbus Request Frame + CRC16"]
PreparePacket --> Send["Assert TX_EN, Transmit over RS-485, Deassert TX_EN"]
Send --> WaitResponse{"Response Received within 200 ms?"}
WaitResponse -->|Yes| ValidateCRC{"Valid CRC16 & Slave ID?"}
ValidateCRC -->|Yes| ParseValues["Parse Registers, Apply Scale & Timestamp"]
ParseValues --> Enqueue["Push to Telemetry Buffer"]
WaitResponse -->|"No / Timeout"| IncRetry{"Retry Count < 3?"}
ValidateCRC -->|CRC Error| IncRetry
IncRetry -->|Yes| Send
IncRetry -->|No| FlagFault["Record Device Offline Alarm"]
Enqueue --> HasMore{"More Meters in List?"}
FlagFault --> HasMore
HasMore -->|Yes| SelectMeter
HasMore -->|No| SleepUntilNext["Yield FreeRTOS Task until Next Cycle"]
- Bus Collision Prevention: Strict 3.5-character silent interval ($t_{3.5}$) enforced between consecutive frame transmissions.
- Fail-Safe Isolation: A malfunctioning or unresponsive meter on the bus does not block the polling loop. After 3 timed-out attempts ($600\text{ ms}$ total), the meter is marked with a communication fault flag and the polling task immediately advances to the next device.