DL/T 645
The Zeus DL/T 645 module targets electric meters and energy acquisition devices. It supports common DL/T 645-2007 read/write flows and handles reversed meter addresses, leading 0xFE wake-up bytes, dual 0x68 headers, the 0x33 data offset, checksums, exception codes, and low-byte-first BCD decoding.
Run Without Hardware
const string meterAddress = "000000000001";
var memory = new Dlt645SlaveMemory();
memory.SetBcd(0x00000000, 1234.56, byteLength: 4, scale: 0.01);
memory.SetBcd(0x02010100, 220.5, byteLength: 2, scale: 0.1);
await using var app = ZeusHost.Create(builder =>
{
builder.AddVirtualChannel("meter-link", new Dlt645SlaveResponder(meterAddress, memory));
builder.AddDlt645(
"meter",
"meter-link",
new Dlt645Options { MeterAddress = meterAddress, WakeUpPreambleCount = 0 },
points: map => map
.Bcd("energy", 0x00000000, dataLength: 4, scale: 0.01)
.Bcd("voltageA", 0x02010100, dataLength: 2, scale: 0.1));
});
await app.StartAsync();
| Component | Meaning |
|---|---|
Dlt645SlaveMemory | Virtual meter data item memory, keyed by four-byte DI |
Dlt645SlaveResponder(address, memory) | Simulates one DL/T 645 meter address |
AddDlt645("meter", "meter-link", options) | Registers a DL/T 645 device on an existing channel |
.Bcd("energy", 0x00000000, 4, 0.01) | Maps total active energy into the point table |
Runnable sample: samples/Zeus.Samples.Console.Dlt645.
Real Meters
DL/T 645 usually runs over RS-485 serial lines. The channel only moves bytes; Dlt645Device handles the meter address and protocol frame:
await using var app = ZeusHost.Create(builder =>
{
builder.AddSerialPort("meter-link", "COM3", 2400);
builder.AddDlt645(
"meter",
"meter-link",
new Dlt645Options
{
MeterAddress = "000000000001",
WakeUpPreambleCount = 4
},
points: map => map.TotalActiveEnergy("energy"));
});
For transparent TCP-to-serial gateways, switch only the channel:
builder.AddTcpClient("meter-link", "192.168.1.50", 5000);
builder.AddDlt645("meter", "meter-link", new Dlt645Options { MeterAddress = "000000000001" });
Direct Reads And Writes
var meter = app.Devices.Get<Dlt645Device>("meter");
var energy = await meter.ReadBcdAsync(0x00000000, byteLength: 4, scale: 0.01);
var voltageA = await meter.ReadBcdAsync(0x02010100, byteLength: 2, scale: 0.1);
var raw = await meter.ReadDataAsync(0x04000401);
await meter.WriteBcdAsync(0x04000101, 88.8, byteLength: 2, scale: 0.1);
ReadDataAsync returns payload bytes without the four-byte DI and without the protocol 0x33 offset. BCD values are decoded low-byte-first; a four-byte item with scale: 0.01 is commonly used for XXXXXX.XX energy values.
Point Polling
builder.AddAcquisition(TimeSpan.FromSeconds(1));
builder.AddDlt645("meter", "meter-link", new Dlt645Options { MeterAddress = "000000000001" }, points: map => map
.Bcd("energy", 0x00000000, dataLength: 4, scale: 0.01, alarmLimits: new PointAlarmLimits(high: 10000))
.Bcd("voltageA", 0x02010100, dataLength: 2, scale: 0.1)
.RawBytes("status", 0x04000401, dataLength: 1));
| Point Type | Point Value | Notes |
|---|---|---|
Bcd | double | Engineering value after scale; supports alarm limits and write-back |
RawBytes | byte[] | Raw payload bytes for status words or vendor extensions |
Common helpers:
map.TotalActiveEnergy("energy"); // DI 00000000, 4 bytes, 0.01 kWh
map.VoltageA("voltageA"); // DI 02010100, 2 bytes, 0.1 V
map.CurrentA("currentA"); // DI 02020100, 3 bytes, 0.001 A
JSON Configuration
{
"channels": [
{
"name": "meter-link",
"type": "serial",
"portName": "COM3",
"baudRate": 2400
}
],
"devices": [
{
"name": "meter",
"channel": "meter-link",
"type": "dlt645",
"meterAddress": "000000000001",
"wakeUpPreambleCount": 4,
"points": [
{ "name": "energy", "address": "0x00000000", "dataType": "bcd", "dataLength": 4, "scale": 0.01 },
{ "name": "voltageA", "address": "0x02010100", "dataType": "bcd", "dataLength": 2, "scale": 0.1 },
{ "name": "status", "address": "0x04000401", "dataType": "raw", "dataLength": 1 }
]
}
]
}
For hardware-free tests, replace the channel with:
{
"name": "meter-link",
"type": "virtual",
"responder": "dlt645",
"meterAddress": "000000000001"
}
Copyable configuration sample: samples/Zeus.Samples.Console.Config/zeus-dlt645.json.
Common Data Items
| Data Item | Common Meaning | Suggested Declaration |
|---|---|---|
0x00000000 | Current total active energy | dataLength: 4, scale: 0.01 |
0x02010100 | Phase A voltage | dataLength: 2, scale: 0.1 |
0x02020100 | Phase A current | dataLength: 3, scale: 0.001 |
Vendor extensions vary by meter model, so use the meter protocol manual or project point list as the final source of truth.