ExDlt645 (ex_dlt645 v0.1.0)

Copy Markdown View Source

A complete, idiomatic Elixir/OTP communication library for the DL/T 645-2007 (and 1997) electricity meter protocol.

Architecture & Key Features

  • Pure Functional Codec (ExDlt645.Codec): Stateless packet encoding, decoding, +0x33 modulo 256 transformation, and checksum verification with binary pattern matching.
  • Data Identifier Catalog (ExDlt645.DataId): Complete coverage of DL/T 645-2007 tables (A.1 Energy, A.2 Demand, A.3 Variables, A.4 Events, A.5 Parameters, A.6 Freeze, A.7 Load).
  • BCD Number & Datetime Engine (ExDlt645.BCD): Fast little-endian BCD conversions for numeric readings, meter addresses, timestamps, and wildcard matching.
  • Streaming Framer (ExDlt645.Framer): Circuits.UART.Framing behaviour implementation featuring preamble stripping, frame length deduction, and sliding-window byte-by-byte resynchronization.
  • Pluggable Transport Layer (ExDlt645.Transport):
  • Master Collector (ExDlt645.Master): Half-duplex serialized request execution, busy guard, active (asynchronous event streaming) and passive (synchronous call) modes.
  • Slave Meter Simulator (ExDlt645.Slave): In-memory virtual meter grid component for automated testing and interactive protocol playgrounds.
  • Telemetry (ExDlt645.Telemetry): Start/stop measurements with RTT for all requests.

Quick Start

1. In-Memory Virtual Loopback Testing (Hardware-free)

# Start a virtual electric meter slave
{:ok, _slave} = ExDlt645.Slave.start_link(address: "123456789012", tty: "virtual")

# Start a master collector on the same virtual bus
{:ok, master} = ExDlt645.Master.start_link(tty: "virtual")

# Read Total Forward Active Energy (DI: 0x00010000)
{:ok, energy} = ExDlt645.read_data(master, "123456789012", 0x00010000)
# => {:ok, 1234.56}

# Read Phase A Voltage (DI: 0x02010100)
{:ok, voltage} = ExDlt645.read_data(master, "123456789012", 0x02010100)
# => {:ok, 220.0}

# Broadcast read meter address
{:ok, addr} = ExDlt645.read_address(master)
# => {:ok, "123456789012"}

2. Physical RS-485 Serial Port

{:ok, master} = ExDlt645.Master.start_link(
  tty: "/dev/ttyUSB0",
  uart_opts: [speed: 2400, parity: :even]
)

{:ok, energy} = ExDlt645.read_data(master, "001234567890", 0x00010000)

Summary

Functions

checksum(binary)

See ExDlt645.Codec.checksum/1.

custom_request(master, raw_packet, opts \\ [])

See ExDlt645.Master.custom_request/3.

decode_address(bytes)

See ExDlt645.BCD.decode_address/1.

decode_value(di, payload)

See ExDlt645.DataId.decode_value/2.

encode_address(addr_str)

See ExDlt645.BCD.encode_address/1.

encode_value(di, value)

See ExDlt645.DataId.encode_value/2.

freeze(master, address, freeze_time, opts \\ [])

See ExDlt645.Master.freeze/4.

info(di)

See ExDlt645.Query.info/1.

inspect_frame(input)

See ExDlt645.Query.inspect_frame/1.

lookup_di(di)

See ExDlt645.DataId.lookup/1.

pack_read_request(address, di, opts \\ [])

See ExDlt645.Codec.pack_read_request/3.

pack_read_response(address, di, value_bytes, opts \\ [])

See ExDlt645.Codec.pack_read_response/4.

parse_frame(binary)

See ExDlt645.Codec.parse_frame/1.

read_address(master, opts \\ [])

See ExDlt645.Master.read_address/2.

read_data(master, address, di, opts \\ [])

See ExDlt645.Master.read_data/4.

search(keyword)

See ExDlt645.Query.search/1.

show_info(di)

See ExDlt645.Query.show_info/1.

sync_time(master, datetime, opts \\ [])

See ExDlt645.Master.sync_time/3.

write_address(master, old_address, new_address, opts \\ [])

See ExDlt645.Master.write_address/4.

write_data(master, address, di, data_payload, opts \\ [])

See ExDlt645.Master.write_data/5.