简介本资源面向电力自动化、智能计量与物联网方向的开发者聚焦62056协议族中DLMS应用层与ASN.1编码、HDLC链路控制的结合实现帮助读者理解智能电表与采集系统间的标准化数据交换机制。压缩包共24个文件约20KB以C源码与头文件为主10个.c、9个.h另含ASN.1模块定义、Makefile构建脚本及示例文件覆盖AARQ-apdu、COSEMpdu、ACSE-requirements、Application-context-name等协议对象便于在C/C环境下搭建DLMS协议栈原型。已有363人学习下载适合作为协议栈移植与编码调试的参考素材。通过研读源码读者可掌握ASN.1编解码规则、HDLC帧定界与错误检测思路以及对象访问模型在电表读写、事件报告中的落地方式为实际项目开发提供可复用的代码骨架与排错线索。1. DLMS/COSEM 协议栈拆解从 62056-47 到 ASN.1 编解码的完整链路如果你手头有一份dlms.rar里面散落着62056-47、62056-62、asn.1、hdlc这些关键词大概率你正面对一个电能表或采集终端的通信协议栈。DLMS/COSEM 是电力行业事实上的国际标准IEC 62056 系列把物理层到应用层全部定义清楚。其中 62056-47 管的是 HDLC 链路层怎么在串口上跑62056-62 管的是接口类对象怎么建模而 ASN.1 负责把数据结构序列化成字节流。这三者串起来才是一条完整的抄表或参数下发通道。很多人卡在 HDLC 帧的 AARQ 字段上不知道里面到底装了什么其实它就是 ASN.1 编码后的应用层连接请求。这篇笔记按实际调试顺序把协议栈从底层到上层拆开给出可复现的代码和参数配置适合正在对接 DLMS 电表、做采集终端固件或协议测试工具的工程师。2. HDLC 链路层在 62056-47 里的帧结构与 AARQ 定位2.1 62056-47 定义的 HDLC 帧格式与关键字段IEC 62056-47 把 HDLC 帧的每个字节都安排得明明白白。一个标准帧从标志位0x7E开始接着是帧格式域、目的地址、源地址、控制域、头校验 HCS、用户数据、帧校验 FCS最后再以0x7E结束。地址域在 DLMS 里通常是 1 字节或 4 字节取决于地址长度配置。控制域用0x10表示信息帧 I 帧0x03表示无编号帧 U 帧AARQ 就藏在 I 帧的用户数据里。实际调试时串口抓到的原始字节流经常因为转义处理出错。HDLC 规定凡是数据里出现0x7E必须转义成0x7D 0x5E出现0x7D转义成0x7D 0x5D。这个转义规则在 62056-47 里叫字节填充很多新手直接拿原始数据去解析结果帧长对不上校验全错。下面是一个用 Python 解析 HDLC 帧的最小实现重点看地址域和控制域的处理import struct def parse_hdlc_frame(raw: bytes): # 去掉首尾标志位 if raw[0] ! 0x7E or raw[-1] ! 0x7E: raise ValueError(帧标志位错误) payload raw[1:-1] # 反转义 unescaped bytearray() i 0 while i len(payload): if payload[i] 0x7D: unescaped.append(payload[i1] ^ 0x20) i 2 else: unescaped.append(payload[i]) i 1 # 解析帧格式域 frame_format unescaped[0] addr_len (frame_format 3) 0x03 # 地址长度指示 # 目的地址和源地址 dest_addr int.from_bytes(unescaped[1:1addr_len], big) src_addr int.from_bytes(unescaped[1addr_len:12*addr_len], big) # 控制域 ctrl unescaped[12*addr_len] # 用户数据从 HCS 之后开始 user_data unescaped[12*addr_len3:-2] return { dest: dest_addr, src: src_addr, ctrl: hex(ctrl), user_data: user_data.hex() }这段代码先做反转义再按帧格式域里的地址长度指示动态取地址。参数addr_len来自帧格式域的第 3、4 位常见值是 0 表示 1 字节地址1 表示 2 字节2 表示 4 字节。控制域0x10就是 I 帧用户数据里才是完整的 LLC PDUAARQ 就在其中。2.2 AARQ 在 HDLC 帧里的位置与内容构成AARQ 是应用层连接请求全称 Association Request。它不在 HDLC 层定义而是由 62056-62 和 ASN.1 描述封装在 HDLC 的信息帧里。一个典型的 AARQ 用户数据从 LLC 头开始0xE6 0xE6 0x00其中0xE6表示 LLC 帧格式0x00是 LLC 控制域。紧接着就是 AARQ 的 ASN.1 编码字节。AARQ 的内容包括应用上下文名称、发送方 ACSE 要求、用户信息。应用上下文名称通常是一个 OID比如2.16.756.5.8.1.1表示 DLMS/COSEM 的 ACSE 上下文。发送方 ACSE 要求里包含认证机制、认证值、AP 标题等。用户信息里才是 xDLMS 的初始化请求比如协商最大 PDU 长度。用asn1tools库可以把这个结构解析出来import asn1tools # 假设已经从 62056-62 标准里提取了 ASN.1 模块定义 spec asn1tools.compile_files(dlms_asn1.asn, ber) decoded spec.decode(AARQ, user_data_bytes) print(decoded)参数说明dlms_asn1.asn需要包含AARQ类型的完整定义通常从 62056-62 的 ASN.1 模块里摘录。ber是基本编码规则DLMS 在 AARQ 阶段默认用 BER后续 xDLMS 服务可能用 A-XDR。解码出来的字典里application-context-name就是 OIDsender-acse-requirements里的authentication-mechanism-name决定后续是否要跑 HLS 认证。注意AARQ 里的认证机制如果是2.16.756.5.8.1.2表示低层安全2.16.756.5.8.1.3表示高层安全。选错机制会导致后续认证帧被电表直接丢弃。3. 62056-62 接口类与 ASN.1 编解码的配合方式3.1 62056-62 定义的接口类对象模型IEC 62056-62 把电表里的所有数据都抽象成对象每个对象属于一个接口类。比如数据对象用类 ID 1寄存器用类 ID 3需求寄存器用类 ID 5活动电能表用类 ID 17。每个对象有属性、方法、事件。属性用索引访问比如逻辑名是属性 1值属性是属性 2。方法用索引调用比如复位方法索引 1。这些对象的访问路径由 OBIS 码标识比如1.0.1.8.0.255表示正向有功总电能。在协议交互里客户端先发 AARQ 建立连接再发 GET 请求读取某个对象的属性。GET 请求的 ASN.1 结构里包含类 ID、OBIS 码、属性索引。下面是一个构造 GET 请求的示例用 ASN.1 编码import asn1tools spec asn1tools.compile_files(dlms_asn1.asn, ber) get_request { class-id: 3, instance-id: bytes([0x01, 0x00, 0x01, 0x08, 0x00, 0xFF]), attribute-id: 2 } encoded spec.encode(Get-Request, get_request) print(encoded.hex())参数说明class-id是接口类编号instance-id是 OBIS 码的 6 字节表示attribute-id是属性索引。编码出来的字节流会作为 xDLMS PDU 的一部分再套上 LLC 和 HDLC 头发送。注意 OBIS 码的字节顺序标准里是从左到右依次排列但有些电表厂商会做字节序调整调试时先用已知对象验证。3.2 ASN.1 模块的提取与编译62056-62 标准附录里给出了完整的 ASN.1 模块定义但直接复制出来往往编译不过因为里面引用了其他模块的类型。常见做法是只提取需要的类型比如AARQ、AARE、Get-Request、Get-Response、Set-Request把依赖的类型也一并摘出来形成一个独立的.asn文件。用asn1tools编译时如果报Type not found说明依赖没摘全。可以先用asn1tools.compile_string快速测试asn1_str DLMS-ASN1 DEFINITIONS :: BEGIN AARQ :: [APPLICATION 0] IMPLICIT SEQUENCE { application-context-name [0] EXPLICIT ApplicationContextName, sender-acse-requirements [1] EXPLICIT ACSE-Requirements OPTIONAL, ... } ... END spec asn1tools.compile_string(asn1_str, ber)参数说明[APPLICATION 0]是 AARQ 的标签BER 编码时会变成0x60。IMPLICIT和EXPLICIT决定标签是替换还是嵌套DLMS 里 AARQ 用 IMPLICIT所以编码后第一个字节是0x60后面跟长度。如果编译时标签类型写错编码出来的字节会多一层电表解析失败。提示不同版本的 62056-62 在 ASN.1 模块上可能有细微差异比如某些可选字段的标签号变了。对接新电表时先拿 AARQ 做连通性测试确认标签和长度都对得上再往下做 GET/SET。4. 从 AARQ 到 AARE一次完整连接建立的调试步骤4.1 构造并发送 AARQ 帧的实操流程先准备串口参数波特率 9600 或 19200数据位 8停止位 1偶校验。DLMS 在串口上默认用偶校验有些电表支持无校验但 62056-47 推荐偶校验。打开串口后先发一个 U 帧做链路层握手比如0x7E 0xA0 0x1E 0x7E是 SNRM 命令用来协商 HDLC 参数。链路建立后构造 AARQ。AARQ 的 ASN.1 编码结果通常以0x60开头后面是长度和内容。把 AARQ 字节放进 HDLC 信息帧目的地址填电表地址源地址填客户端地址控制域填0x10然后计算 HCS 和 FCS。HCS 是对帧格式域到控制域做 CRC-16/X-25FCS 是对整个帧除标志位做同样的 CRC。下面是一个完整的发送函数import serial import crcmod crc16 crcmod.mkCrcFun(0x11021, initCrc0xFFFF, revTrue, xorOut0xFFFF) def build_hdlc_frame(dest, src, ctrl, user_data): frame_format 0xA0 # 地址长度 1 字节帧格式 header bytes([frame_format, dest, src, ctrl]) hcs crc16(header).to_bytes(2, little) payload header hcs user_data fcs crc16(payload).to_bytes(2, little) frame b\x7E payload fcs b\x7E # 转义 escaped bytearray() for b in frame: if b 0x7E: escaped.extend([0x7D, 0x5E]) elif b 0x7D: escaped.extend([0x7D, 0x5D]) else: escaped.append(b) return bytes(escaped) ser serial.Serial(COM3, 9600, parityserial.PARITY_EVEN, timeout1) aarq_bytes bytes.fromhex(60 36 A1 09 06 07 60 85 74 05 08 01 01 8A 02 07 80 8B 07 60 85 74 05 08 02 01 AC 0A 80 08 41 42 43 44 45 46 47 48 BE 10 04 0E 01 00 00 00 06 5F 1F 04 00 00 00 00 00 00 00) frame build_hdlc_frame(0x01, 0x10, 0x10, aarq_bytes) ser.write(frame) response ser.read(256) print(response.hex())参数说明dest是电表地址常见为 1 或 16src是客户端地址通常为 16 或 1ctrl用0x10表示 I 帧。aarq_bytes里的60 36是 AARQ 标签和长度A1 09是应用上下文名称8A 02 07 80是认证机制8B 07是认证值AC 0A是用户信息。这些字节需要根据实际电表的配置调整比如认证机制换成 HLS 时8A 02 07 80要改成对应的 OID。4.2 解析 AARE 响应与常见错误码电表收到 AARQ 后如果接受会回一个 AARE 帧。AARE 的标签是0x61内容里包含应用上下文名称、结果、诊断信息、用户信息。结果字段0x00表示接受0x01表示拒绝永久0x02表示拒绝暂时。诊断信息里会给出具体原因比如0x01表示应用上下文不支持0x02表示认证失败。解析 AARE 时先按 HDLC 帧解析拿到用户数据再用 ASN.1 解码aare_bytes bytes.fromhex(61 29 A1 09 06 07 60 85 74 05 08 01 01 A2 03 02 01 00 A3 05 A1 03 02 01 00 BE 10 04 0E 08 00 06 5F 1F 04 00 00 00 00 00 00 00) decoded spec.decode(AARE, aare_bytes) print(decoded)参数说明A2 03 02 01 00里的00就是结果字段表示接受。A3 05 A1 03 02 01 00是诊断信息00表示无诊断。如果结果是01诊断里会给出具体错误码比如01表示应用上下文不支持需要检查 AARQ 里的 OID 是否和电表匹配。注意有些电表在 AARE 之后还会发一个 HDLC 的 RR 帧做流控确认如果客户端没回 RR后续的 GET 请求可能被丢弃。调试时看到 AARE 后先别急着发 GET等一个 RR 或主动发一个 RR。5. 避坑与排查HDLC 和 ASN.1 调试中的五个血泪教训5.1 帧校验失败但数据看起来没错现象串口抓到的帧手动算 CRC 和帧里的 FCS 对不上但数据内容完全正确。原因HDLC 的 CRC 是 CRC-16/X-25多项式0x1021初始值0xFFFF结果异或0xFFFF而且输入输出都反转。很多人用标准的 CRC-16/CCITT 算结果全错。解决用crcmod时明确指定revTrue和xorOut0xFFFF或者直接查表实现。5.2 AARQ 发出去电表不回任何数据现象串口写入了 AARQ 帧但读不到任何响应超时。原因HDLC 地址配错或者串口校验位不对。DLMS 电表默认偶校验如果客户端用无校验电表会直接忽略。另外目的地址和源地址如果和电表里配置的不一致电表也不回。解决先用 U 帧 SNRM 测试链路如果 SNRM 有响应说明物理层和地址基本对再检查 AARQ 的认证机制是否和电表要求的一致。5.3 ASN.1 解码报长度错误现象用asn1tools解码 AARQ 时报Length mismatch或Unexpected end of data。原因AARQ 的 BER 编码里长度字段可能是短形式或长形式。短形式一个字节表示 0 到 127长形式第一个字节最高位为 1低 7 位表示后续长度字节数。如果手动截取用户数据时多截或少截了字节长度就对不上。解决先打印用户数据的十六进制确认0x60后面的长度字节和实际剩余字节数一致。不一致就检查 HDLC 解析里的用户数据偏移。5.4 认证阶段 HLS 失败现象AARQ 被接受但后续 HLS 认证的挑战响应帧被电表拒绝。原因HLS 认证需要客户端和电表用相同的密钥和算法。常见错误是密钥填错或者挑战字节的顺序搞反。解决先确认电表支持的 HLS 机制通常是2.16.756.5.8.1.3。然后检查密钥是否按标准要求的高层安全密钥挑战响应计算时注意字节序。5.5 GET 请求返回数据访问错误现象AARE 接受后发 GET 请求电表回错误码0x03表示对象未定义。原因OBIS 码或类 ID 写错或者属性索引不对。比如读正向有功总电能类 ID 应该是 3OBIS 是1.0.1.8.0.255属性索引是 2。如果类 ID 写成 1电表就找不到对象。解决先用电表的对象列表读取服务或者查电表手册确认 OBIS 和类 ID 的对应关系。有些电表对 OBIS 的字节序有特殊要求需要逐字节验证。6. 用 asn1tools 做协议模糊测试与自动化验证6.1 构造异常 AARQ 测试电表健壮性协议栈调通之后下一步是验证电表对异常输入的处理。用asn1tools可以方便地构造各种边界情况的 AARQ比如长度字段故意写错、标签号改成非法值、认证机制填一个不存在的 OID。观察电表是回 AARE 拒绝还是直接无响应。这个手段在对接不同厂商电表时特别有用能快速摸清电表的容错边界。import asn1tools spec asn1tools.compile_files(dlms_asn1.asn, ber) # 正常 AARQ normal { application-context-name: (2, 16, 756, 5, 8, 1, 1), sender-acse-requirements: {authentication-mechanism-name: (2, 16, 756, 5, 8, 1, 2)}, user-information: b\x04\x0E\x01\x00\x00\x00\x06\x5F\x1F\x04\x00\x00\x00\x00\x00\x00\x00 } encoded spec.encode(AARQ, normal) # 异常认证机制改成不存在的 OID abnormal dict(normal) abnormal[sender-acse-requirements] {authentication-mechanism-name: (1, 2, 3, 4)} try: bad_encoded spec.encode(AARQ, abnormal) print(异常编码:, bad_encoded.hex()) except Exception as e: print(编码失败:, e)参数说明application-context-name用元组表示 OIDsender-acse-requirements里的authentication-mechanism-name也是 OID。异常测试时把 OID 改成非法值看电表是否回 AARE 里的诊断信息。如果电表直接无响应说明它对非法 OID 不做处理实际部署时客户端要加超时重试。6.2 自动化回归测试脚本的搭建把 AARQ 发送、AARE 解析、GET 请求、响应校验串成一个脚本每次修改参数后自动跑一遍。用pytest做测试框架串口操作封装成 fixture。这样换电表或改配置时能快速知道哪一步出了问题。import pytest import serial pytest.fixture def meter(): ser serial.Serial(COM3, 9600, parityserial.PARITY_EVEN, timeout2) yield ser ser.close() def test_aarq_accept(meter): aarq build_aarq() frame build_hdlc_frame(0x01, 0x10, 0x10, aarq) meter.write(frame) resp meter.read(256) assert resp[0] 0x7E aare parse_hdlc_frame(resp)[user_data] decoded spec.decode(AARE, bytes.fromhex(aare)) assert decoded[result] 0参数说明timeout2给电表足够的响应时间有些电表在 AARQ 后要等 1 秒以上才回。assert decoded[result] 0校验 AARE 的结果字段。如果失败打印decoded看诊断信息。这个脚本可以扩展成多个测试用例覆盖不同认证机制、不同 PDU 长度、不同 OBIS 对象。我自己的习惯是每对接一款新电表先跑一遍这个回归脚本把 AARQ 和 AARE 的字节流存下来做基线。后面再出问题直接和基线对比能省很多抓包时间。希望帮到你。本文还有配套的精品资源点击获取
企业数字化 ERP 产品动态
相关推荐
G6 Dagre 层次化布局实战指南:配置项全解析与源码原理 数据可视化前端图表库 【免费下载链接】G6 ♾ A Graph Visualization Framework in JavaScript. 项目地址: https://gitcode.com/gh_mirrors/g6/G6 点击查看 免费下载 Dagre 是 G6 内置的层次化布局方案,专为有向无环图(DAG)设计… · 2026/9/23 13:09:20
多尺度边缘检测实战:尺度归一化、融合策略与金字塔加速 简介:这份资源面向图像处理与计算机视觉方向的学习者,聚焦多尺度边缘检测这一经典课题,帮助读者理解如何结合高斯滤波器与拉普拉斯算子(LoG)在不同尺度下提取稳定、鲁棒的边缘信息,适用于课程实验、算法入门… · 2026/9/23 13:09:20
二手交易场景 e-Transfer 钓鱼诈骗机理与防控研究 摘要以加拿大渥太华居民 Kimberley Bray 在 Poshmark 二手交易平台出售衣物时遭遇 e-Transfer 钓鱼诈骗、损失 1000 加元的真实案件为研究样本,完整还原该类以二手交易为掩护的电子转账钓鱼诈骗的传播途径、社会工程欺骗流程、资金窃取链路与事后处置全过程… · 2026/9/23 13:45:59
SRNet与DDSP结合:图像隐写分析去除实战指南 简介:这是一套面向本科毕业设计的图像隐写分析与去除系统项目,基于SRNet与DDSP网络实现,适合计算机、电子信息、自动化等专业学生用于毕设、课设或项目演示。整套资料包含47个Python脚本、30个Python字节码缓存、4个界面文件、24个模型配置&a… · 2026/9/23 13:45:59
gbrain 单一想法谱系追踪:idea-lineage 技能实战指南 人工智能RAGAgent 记忆MCP 服务知识管理 【免费下载链接】gbrain Garrys Opinionated OpenClaw/Hermes Agent Brain 项目地址: https://gitcode.com/gh_mirrors/gb/gbrain 点击查看 免费下载 本指南讲解 gbrain 中 idea-lineage 技能的设计与用法:如何从… · 2026/9/23 13:45:31
小小航海士手写实现:转岗后端避坑指南 小小航海士手写实现:转岗后端避坑指南 别再对着教程发呆,看了一堆视频还是不会写项目?这种挫败感我太懂了。很多转岗的朋友,卡在“知道原理但手跟不上”的瓶颈期。其实,拿《小小航海士》这类经典前端项目练手,核心不在于复刻画面,而在于 手写实现… · 2026/9/23 13:45:25
5分钟搞懂glue怎么读:从DNS原理到代码完整示例 5分钟搞懂glue怎么读:从DNS原理到代码完整示例 学会 dig 和 nslookup 命令,看着返回结果里的 glue record 却一脸懵?这就是典型的“语法熟练但工程落地难”。很多开发者在排查域名解析故障时,卡在最后一步:明明… · 2026/9/23 13:45:18
NullClaw记忆系统深度解析:SQLite混合检索(FTS5+向量)如何让AI永不失忆 NullClaw记忆系统深度解析:SQLite混合检索(FTS5向量)如何让AI永不失忆 【免费下载链接】nullclaw Fastest, smallest, and fully autonomous AI assistant infrastructure written in Zig 项目地址: https://gitcode.com/gh_mirrors/nu/nul… · 2026/9/23 13:45:18
3招搞定手机怎么下载微信面试难题实战项目解析 3招搞定手机怎么下载微信面试难题实战项目解析 面试被问“手机怎么下载微信”背后的原理,90%的人答不上来。别笑,这看似弱智的问题,实则是考察你对移动应用分发机制、安全校验及网络协议理解的试金石。我带过不少校招新人,他们背了八股文,却连一个A… · 2026/9/23 0:00:03
你有新短消息请注意查收:3个新手避坑指南搞定消息系统选型 你有新短消息请注意查收:3个新手避坑指南搞定消息系统选型 面试被问“高并发下如何保证消息不丢失”,你张口就是“用Redis”,结果面试官追问“如果Redis宕机了怎么办”,你瞬间卡壳。这种场景太常见了,很多新手在背八股文时,只记住了技术名词… · 2026/9/23 0:00:29