简介:DMLS协议中文版是一份面向电能量数据采集终端开发与调试人员的协议说明手册,针对IEC62056(DLMS)协议族在实际应用中与电能表通讯的常见场景,梳理了从物理层到应用层的完整通讯流程。资源包内仅含1个doc文档,约620KB,内容围绕协议模型、物理层、HDLC链路层、应用层展开,并重点讲解ASN.1语法、BER与AXDR编码、AARQ与AARE数据帧的构筑方式,以及请求电量、瞬时量(电压、电流、功率)、负荷曲线、时间等数据帧的实例报文。读者可借助该文档理解Client-Server结构下采集器与电表的连接建立、数据请求与链路释放过程,对照class id与OBIS标识定位不同数据对象,为协议解析、报文抓包分析和终端联调提供参考。目前已有867人学习下载,适合从事电能计量、采集终端开发及协议栈调试的工程师查阅。
1. DMLS协议中文版:从抄表到费控,一套协议栈怎么在本地跑通
电表、水表、气表这些设备每天在后台默默产生数据,但要把它们读出来、写进去,绕不开一套叫 DLMS/COSEM 的协议体系。标题里的“DMLS协议中文版”,实际指向的是 DLMS 协议的中文资料与本地化实现——很多一线工程师第一次接触时,手里只有零散的英文标准片段,面对 IEC 62056 那一摞文档和 HDLC、ASN.1、BER 这些名词,根本不知道从哪下手。这篇文章要解决的,就是让你在本地用一台电脑、一个串口工具,把 DLMS 通信的最小闭环跑起来,理解每一层在干什么,参数怎么设,坑在哪。适合做能源计量、远程抄表、费控终端的开发者和集成人员,也适合需要对接电表协议的后端工程师。
2. DLMS/COSEM 协议栈拆解:从物理串口到应用层对象
2.1 四层结构各自管什么
DLMS/COSEM 的协议栈不是单一协议,而是一组规范的组合。最底下是物理层和链路层,常见的是 HDLC(IEC 62056-46)或者 Wrapper(IEC 62056-47,走 TCP/UDP)。HDLC 负责在串口上把字节流切成帧,做地址识别和校验;Wrapper 则把 HDLC 帧直接封进 TCP 包,省掉串口的物理限制。往上是应用层,由 COSEM 对象模型和 DLMS 服务组成。COSEM 把电表里的电量、需量、事件记录都抽象成“对象”,每个对象有 OBIS 码标识,比如 1.0.1.8.0.255 代表有功总电能。DLMS 则定义了读写这些对象的服务,比如 GET、SET、ACTION。
再往上就是 ASN.1 和 BER 编码。DLMS 的报文不是纯文本,而是用 ASN.1 描述数据结构,再用 BER(Basic Encoding Rules)编成二进制。你抓包看到的十六进制流,每一段 TLV(Tag-Length-Value)都对应一个 ASN.1 类型。理解这一层,才能手动构造请求、解析响应。
2.2 一次 GET 请求的完整数据流
假设你要读电表的正向有功总电能,OBIS 码 1.0.1.8.0.255,属性 2(值)。在 HDLC 链路上,整个过程分四步:
第一步,建立链路。发送 SNRM 帧,设置最大信息字段长度和窗口大小。电表回应 UA 帧,链路进入信息传输状态。
第二步,应用层关联。发送 AARQ(Association Request),携带应用上下文、认证方式、协商的 DLMS 版本号。电表回 AARE,确认关联成功。这一步决定了后续能不能读写对象。
第三步,发送 GET 请求。用 BER 编码构造 GetRequest,包含对象 OBIS 码、属性索引、类 ID。封装进 HDLC 信息帧,发出去。
第四步,解析响应。电表返回 GetResponse,数据字段是 BER 编码的数值。你需要按 ASN.1 定义逐层解出实际电能值,注意单位倍率和标度。
这四步里,任何一步的参数不匹配,都会导致超时或拒绝。常见的新手翻车点就在 AARQ 的认证字段和 HDLC 地址上。
2.3 用 Python 搭一个最小 HDLC 帧构造器
下面这段代码演示如何手工拼一个 HDLC 信息帧,用于发送 GET 请求。实际项目中你会用现成库,但手写一遍能帮你理解帧结构。
import struct def build_hdlc_frame(addr, control, info: bytes) -> bytes: """ 构造 HDLC 帧(IEC 62056-46 风格) addr: 1字节地址,低7位有效,最高位表示方向 control: 控制字节,信息帧通常为 0x10 或 0x30 info: 已编码的 DLMS APDU """ # 帧头:标志 0x7E + 地址 + 控制 frame = bytearray() frame.append(0x7E) frame.append(addr) frame.append(control) # 信息字段 frame.extend(info) # 计算 FCS(这里简化,实际用 CRC-16/X-25) fcs = 0x0000 # 占位,真实实现需计算 frame.extend(struct.pack('<H', fcs)) frame.append(0x7E) return bytes(frame) # 示例:地址 0x01,控制 0x10,信息字段为占位 raw = build_hdlc_frame(0x01, 0x10, b'\x01\x02\x03') print(raw.hex())逻辑说明:HDLC 帧以 0x7E 开始和结束,地址字段区分主站和从站,控制字段标识帧类型。信息字段承载 DLMS APDU。FCS 校验必须正确,否则电表直接丢弃。参数方面,地址通常由电表厂商定义,控制字节 0x10 表示信息帧且发送序号为 0。实际调试时,先用串口助手发 SNRM 帧,确认能收到 UA 再继续。
2.4 ASN.1 与 BER:为什么你的解析总是错位
DLMS 报文里的数据不是固定长度,而是 BER 编码的 TLV。比如一个 32 位无符号整数,Tag 是 0x06,Length 是 0x04,后面跟 4 字节大端数值。如果你按固定偏移去读,遇到变长类型(如 octet-string)就会错位。常见做法是写一个递归下降解析器,先读 Tag,再读 Length,根据 Length 决定读多少字节 Value,然后根据 Tag 判断类型。
参数上要特别注意:BER 的 Length 字段有短形式和长形式。短形式最高位为 0,低 7 位就是长度;长形式最高位为 1,低 7 位表示后续多少字节表示长度。很多开源库在这里处理不严谨,导致解析长报文时崩溃。
3. 本地跑通 DLMS 通信:串口、TCP 与模拟器配置
3.1 硬件准备与串口参数
如果你手头有真实电表,用 USB 转 485 或 USB 转 232 线连接。串口参数通常是 9600 或 19200 波特率,8 数据位,1 停止位,偶校验(Even)或无校验。DLMS 的 HDLC 层对波特率不敏感,但校验错会导致帧丢弃。我一般先用串口助手以 9600-8-E-1 发 SNRM,看有没有 UA 返回。如果没有,换 19200 再试,同时检查 A/B 线是否接反。
如果没有硬件,用模拟器。常见的开源模拟器有 Gurux DLMS 模拟器,可以模拟电表行为,支持 HDLC 和 Wrapper 两种链路。启动后监听 TCP 端口,你用客户端连上去就能跑完整流程。
3.2 用 Gurux 库建立关联并读对象
Gurux 提供 Python 和 C# 版本,这里以 Python 为例。先安装依赖,然后写一个最小客户端。
from gurux_dlms import GXDLMSClient, GXDLMSObjectCollection from gurux_dlms.enums import Authentication, InterfaceType from gurux_net import GXNet # 创建客户端,接口类型为 HDLC client = GXDLMSClient(True) client.interfaceType = InterfaceType.HDLC client.authentication = Authentication.NONE # 无认证,低安全等级 # 连接模拟器 media = GXNet(InterfaceType.WRAPPER, "127.0.0.1", 4059) media.open() # 建立关联 reply = client.snrmRequest() media.send(reply) media.receive() # 应收到 UA # 应用层关联 aare = client.aarqRequest() media.send(aare) media.receive() # 应收到 AARE # 读取 OBIS 1.0.1.8.0.255 属性 2 objects = GXDLMSObjectCollection() obj = client.createObject(3, "1.0.1.8.0.255") # 类ID 3 是 Register objects.append(obj) get_request = client.read(obj, 2) media.send(get_request) response = media.receive() value = client.updateValue(obj, 2, response) print(value)逻辑说明:先设置接口类型和认证方式,无认证适合本地调试。SNRM 建立链路,AARQ 建立应用关联。读取时指定类 ID、OBIS 和属性索引。参数上,认证方式如果设为 LOW,需要提供密码;设为 HIGH 则涉及挑战响应。本地模拟器通常用 NONE 或 LOW。
3.3 Wrapper 模式与 TCP 端口
Wrapper 模式把 HDLC 帧直接封进 TCP,省去串口。默认端口 4059。配置时注意:Wrapper 头部有版本号和长度字段,版本号通常为 0x0001。如果你用 Wireshark 抓包,过滤 tcp.port==4059 就能看到完整的 DLMS 交互。这种模式在集中器与主站之间很常见,调试时比串口方便,因为可以远程抓包。
3.4 认证与安全等级的选择
DLMS 支持多种认证:无认证、低等级密码、高等级 HMAC、以及基于证书的对称/非对称。本地调试从 NONE 开始,跑通后再加 LOW。LOW 认证需要客户端发送密码,电表校验。如果密码错,AARE 会返回错误码。高等级认证涉及挑战随机数和密钥派生,参数多,容易翻车。建议先用模拟器把 NONE 和 LOW 跑熟,再碰 HIGH。
4. 避坑与排查:DLMS 调试中最容易翻车的五个点
4.1 现象:发送 SNRM 后无响应,串口灯闪但收不到 UA
原因:地址不匹配或波特率错误。HDLC 地址有主站地址和从站地址,电表通常只响应特定地址。另外,有些电表要求先发一个 0x7E 做同步,或者对帧间延时敏感。
解决:用示波器或串口助手确认波特率。尝试地址 0x01、0x10、0x3F 等常见值。如果仍无响应,检查 A/B 线是否反接,或者电表是否处于编程状态。
4.2 现象:AARQ 被拒绝,返回 AARE 带错误码 0x0E
原因:应用上下文或认证方式不匹配。电表可能只支持特定 DLMS 版本,或者要求低等级认证但你发了无认证。
解决:查看电表手册,确认支持的认证等级和应用上下文。把 authentication 改为 LOW,并提供正确密码。如果不知道密码,尝试默认密码如“00000000”或“12345678”。
4.3 现象:GET 请求返回数据但解析出来是乱码
原因:BER 解码错误或标度未处理。电表返回的数值可能带倍率(scaler)和单位,直接当整数读会差几个数量级。
解决:用 ASN.1 解析器逐层打印 Tag 和 Length,确认数据类型。对于 Register 对象,属性 2 是值,属性 3 是标度。读值后要结合标度计算实际物理量。
4.4 现象:通信一段时间后断连,需要重新 SNRM
原因:HDLC 窗口或超时参数不匹配。电表可能限制未确认帧的数量,或者对空闲时间有要求。
解决:调整 SNRM 中的窗口大小和 T1/T2/T3 超时。常见做法是把窗口设为 1,超时设长一点。如果走 TCP,检查是否有 NAT 超时导致连接被断。
4.5 现象:用 Gurux 库读某些 OBIS 码报“对象不存在”
原因:电表实际支持的 OBIS 码与标准文档不一致,或者该对象需要更高权限才能访问。
解决:先读电表的对象列表(类 ID 1 的 Association LN 对象),获取实际支持的 OBIS 码集合。不要硬编码标准码,要以设备返回为准。
5. 进阶:用对象列表自动发现与批量读取
跑通单个对象读取后,下一步是批量读取。手动一个个构造 GET 效率太低,而且容易漏。我一般会先读 Association LN 对象(OBIS 0.0.40.0.0.255,类 ID 15),拿到电表支持的所有对象列表,然后按类 ID 分组,批量构造读请求。
具体做法:解析 Association LN 的 object_list 属性,得到一个包含类 ID、版本号、OBIS 码的数组。然后过滤出 Register、Demand Register、Profile Generic 等需要读取的类。对于 Profile Generic(类 ID 7),还要读它的 capture_objects 属性,知道里面存了哪些通道的数据。
批量读取时注意 HDLC 帧长度限制。如果一次读太多,帧会超过最大信息字段长度,需要分片。常见做法是每帧只读一个对象,或者用 With-List 服务一次读多个但控制总长度。我习惯先读 10 个对象测试,稳定后再增加。
验证方法:把读到的值与电表面板显示对比,或者与已知标准源对比。如果偏差大,检查标度和单位。另外,用 Wireshark 抓包,看每一帧的请求和响应是否配对,有没有重传。
最后说一个血泪教训:不要在生产环境直接改参数。我见过有人在现场把电表地址改了,结果整条总线上的设备都失联,最后只能拆表恢复。本地模拟器上随便折腾,真机上每一步都要有后悔药——先备份配置,再动手。希望帮到你。
本文还有配套的精品资源,点击获取