拓冰建站拓冰建站
首页 / 资讯中心 / 正文

基于STM32的DLMS/COSEM智能电表通信协议实现与调试

简介本资源是一套基于STM32平台实现DLMS/COSEM协议的智能电表完整嵌入式源码方案面向嵌入式开发工程师、电力计量设备研发人员及高校电力电子方向学习者解决智能电表通信协议栈移植、高精度电能计量与多接口数据交互等核心工程问题。压缩包含656个文件主体为223个C源文件与252个头文件.c/.h构成计量算法、RS-485/红外通信协议栈、LCD人机交互及Flash历史数据存储等关键模块另有少量构建脚本.bat、配置文件.xml/.yml和调试工程文件.ewp/.icf总大小1.87MB。已有187人学习下载提供可直接编译运行的完整工程框架涵盖ADE7758驱动、数字滤波抗谐波算法、31天负荷曲线存储逻辑及GB/T 17215-2008合规性设计要点代码结构清晰、模块解耦度高便于协议定制与硬件适配。1. 接入 DLMS 前表计开发先要认清的几个现实做智能电表开发的人听到 DLMS 第一反应往往是“这个协议是不是很复杂”。我最初接手这个基于 STM32 的 DLMS 智能电表项目时心里也没底。老板丢过来一份协议文档加一句“参考国外电表的做法把 GPRS 和 RS485 口上的通信都跑起来”然后我就开始了将近两个月的填坑之旅。后来我意识到DLMS 并不是一种写起来有多难的协议真正难的地方在于它把通信流程、数据模型、安全机制全部叠在一起任何一个环节没对齐上位机就是读不到数据。先给不熟悉的朋友说清楚背景。DLMS 是 Distribution Line Message Specification 的缩写中文一般叫配电线路报文规范和 COSEM电能计量配套规范合在一起就是国际电工委员会 IEC 62056 标准。这套标准定义了三层内容物理层和数据链路层怎么传帧应用层怎么建立关联并读写数据以及电表内部的数据怎么用“对象”的方式组织。简单说DLMS/COSEM 就是让不同厂家、不同型号的电表能用同一种“语言”被抄表系统读懂。在 STM32 上做这套东西最大的问题不是单片机性能不够。STM32F103 这种 72MHz 的芯片跑 DLMS 协议栈完全没问题真正的瓶颈在于内存和开发调试效率。DLMS 的报文使用 ASN.1 的 BER 编码一条读取响应可能几百字节如果还开了加密和事件上报缓冲区很快就会紧张。其次DLMS 协议栈不像 TCP/IP 那样有现成的轻量级实现大多数开源栈比如 Gurux 的 gxdlms 是为了 PC 和 Linux 场景写的你要拿到 STM32 上必须自己做裁剪和移植。而我们在项目中带来的价值恰恰不是把协议栈编译过而是让它在掉电、串口异常、上位机频繁断开重连的情况下依然稳定。还有一点必须提前认清DLMS 是一个“框架型”协议而不是“固定报文”协议。意思是说它允许表厂通过 COSEM 对象自定义自己的数据表。同一个 OBIS 码在这家电表里是总电量在另一家可能就是电压。所以开发前必须把数据点清单定清楚。否则协议栈调通了台架上还是收不到数据。项目启动的第一周我的时间基本都在整理数据对象清单和 PC 端仿真工具上反而没怎么写代码。这篇文章会顺着我从零把系统跑通的顺序来写硬件平台怎么选、源码怎么组织、DLMS 连接时序怎么一步步建立、OBIS 对象怎么映射到寄存器最后是调试过程中我实际踩过的坑。如果你正准备在 STM32 类 MCU 上实现一个 DLMS 从站设备这篇文章应该能帮你少走不少弯路。2. 系统架构与模块划分一个不缺不少的电表平台2.1 从整体看这套表计系统的数据流我做的这套系统不是单板方案它更像一个完整的电力终端产品。主控用的是 STM32F103RET664KB RAM、512KB Flash外挂一块 EEPROM 存储参数一块 SPI Flash 存事件记录。计量芯片用 RN8209C专门负责电压电流采样、功率计算和电能累计。主控通过 UART 与 RN8209C 通信定时读取瞬时量数据和电能寄存器。对外通信口有两个一个 RS485 接口一个红外通信口。RS485 通常接集中器或抄表手持机红外口做本地维护两者在软件里共用同一个 DLMS 应用层实例只是底层链路不同。在这个架构下光耦隔离是必须的RS485 侧用隔离电源避免电网侧的干扰通过串口打坏主控。系统中的数据流可以分成两条线。第一条是计量数据流RN8209C 每秒更新一次电能寄存器和瞬时量寄存器主控用定时器每 1 秒去读取一次刷到内存缓存。第二条是通信数据流DLMS 主站上位机发出请求帧STM32 的串口中断收到完整帧之后交给 DLMS 协议栈解析映射到刚才的内存缓存组织响应帧发出去。这两条线相互独立又互相依赖。如果计量读取线程太慢DLMS 读到的数据会有延迟感如果 DLMS 通信占用了太多 CPU计量读取又会丢帧。我用的是前后台系统主循环里跑 DLMS 状态机定时器中断里处理计量读取和 RTC 心跳优先级错开实测下来不会互相卡死。2.2 为什么主控选 STM32F103 而不是更高端的芯片很多人在选型时会想DLMS 协议栈要跑 ASN.1 编码是不是得上 Cortex-M4 或者 M7。我实际测下来在不开 DLMS 加密通信的前提下STM32F103RET6 完全跑得动。原因在于 DLMS 从站的负载并不高它不是每时每刻都在处理大量报文绝大多数时间都在等待上位机发起请求。真正费 CPU 的地方是大块数据读取比如读取 1000 条冻结记录一条条 BER 编码并拼帧这个操作才会让 CPU 占用率达到七八成。用 F103 还有个现实原因功耗和成本。电表是 24 小时不断电的在线设备M4 系列整体功耗比 F103 高不少在整表温升测试中会带来额外的设计压力。同时F103 是 ST 的常青树型号HAL 库、标准外设库、网上各种例程都非常成熟团队上手快采购渠道也稳定。当然如果项目要求必须支持 DLMS 的完整加密套件——比如 AES-GCM 加签名——那建议直接上带硬件加密的 F207 或 L4 系列软件实现虽然也可行但性能和代码量都会让人头疼。2.3 软件分层应用、协议栈、驱动要彻底切开这套系统在软件结构上分成四层应用层负责电表业务逻辑包括电量累加、事件记录、费率切换、掉电检测。DLMS/COSEM 协议层负责 BER 编解码、HDLC 组帧拆帧、连接管理、对象访问。驱动抽象层提供串口、定时器、EEPROM、SPI Flash、RTC 的统一接口。板级驱动层直接操作 STM32 寄存器和 HAL 库函数。应用层永远不直接调用 HAL 驱动。DLMS 协议层需要读一个数据对象时它调的是数据访问注册表由注册表回调到应用层对应的 get 函数。这样好处是换主控、换驱动库甚至从裸机换成 RTOSDLMS 协议层和业务代码都不用大改。这个分层思路在移植源码时帮了我大忙。当时我把协议栈从另一个平台迁移过来只需要改驱动抽象层的实现DLMS 部分基本没有动。如果你打算直接复制别人源码我建议你也按这个思路重新梳理一下哪怕工程乱一点分层边界一定要清晰。3. 源码工程的移植过程栈裁剪、内存规划和初始化顺序3.1 拿到一份 DLMS 源码后先做什么DLMS/COSEM 协议的源码网上能找到多种风格的有完整商用的也有教育用途的简化版。不管哪种第一步都不要急着建工程先把源码目录结构看清楚。我用的这份源码结构大概是这样smart_meter/ ├── app/ │ ├── main.c │ ├── metering.c │ ├── event.c │ └── flash_store.c ├── dlms/ │ ├── cosem/ │ │ ├── cosem_object.c │ │ ├── obis_map.c │ │ ├── register_object.c │ │ └── profile_object.c │ ├── stack/ │ │ ├── dlms_server.c │ │ ├── dlms_frame.c │ │ ├── hdlc_link.c │ │ └── apdu.c │ ├── port/ │ │ ├── dlms_port_cfg.c │ │ ├── dlms_uart_port.c │ │ └── dlms_time_port.c ├── drivers/ │ ├── bsp_uart.c │ ├── bsp_timer.c │ ├── bsp_spi_flash.c │ ├── bsp_eeprom.c │ └── bsp_rn8209.c └── libraries/ └── STM32F1xx_HAL_Driver/这个结构里最有价值的是dlms/port/目录它专门放平台相关的移植代码。协议栈内部不会直接调用HAL_UART_Transmit它只调用dlms_uart_send()而这个函数就是在dlms_uart_port.c里实现的。这样协议栈本身就能做到平台无关。拿到源码后我第一件事是删除不必要的功能模块。很多 DLMS 栈默认支持以太网 TCP/IP 的通信方式而我只需要 HDLC 链路层直接把 TCP 和 IPv4 相关代码从编译列表中去掉。其次有些栈带完整的文件系统用于读取配置和事件记录我也没用因为电表这边简单场景用 EEPROM 和 SPI Flash 裸读写就够了。3.2 内存预算多大的缓冲区才不会崩DLMS 一条 HDLC 帧的最大长度由通信参数决定。常见的配置是最大帧长 128 字节、256 字节或 512 字节。我实际把发送和接收缓冲区都设成了 1024 字节这样能覆盖大部分场景包括读取几十条用电记录的响应帧。STM32F103RET6 的 RAM 是 64KB看起来不小但实际分一分就很紧张。我划了 1KB 做 DLMS 接收缓冲1KB 做发送缓冲1KB 做 ASN.1 编解码临时缓冲再加上协议栈内部的对象表、事件队列、LCD 显存、串口 DMA 缓冲整体内存占用约 30KB。剩下 30KB 多留给栈和业务变量的余量。在这个规划里有一个关键点发送缓冲区不一定要独立分配。很多 DLMS 栈允许你在组帧时直接往串口 DMA 发送描述符指向的缓冲区里写数据写完立刻启动 DMA 发送。这样发送缓冲和应用缓冲可以共用能省下不少 RAM。我在代码里把发送口和接收口设计成一个双缓冲环形结构配合 DMA 空闲中断接收不定长帧确保串口数据不丢。3.3 初始化顺序错了协议栈跑不起来我最初移植时遇到一个非常诡异的现象协议栈初始化后上位机连接帧来了代码走到 HDLC 链路层就卡住一直重发 SNRM 请求。后来检查发现是我的初始化顺序不对。DLMS 协议栈初始化有几个前置条件串口驱动的 DMA 和中断必须先启动否则帧收不进来。RTC 时间要先正确设置因为 DLMS 关联建立时会校验时间如果时间异常上位机可能拒绝关联。COSEM 对象注册表必须完成注册也就是 OBIS 映射表先初始化完成否则后续 Get/Set 请求进来会报“对象不存在”。调dlms_server_init()初始化协议状态机再dlms_server_start()开始监听。正确的顺序是靠经验总结的。我当时的做法是在主函数里把外设初始化全部放在 DLMS 栈初始化之前并且单独写了几个自检函数每步初始化完都返回一个状态值。调试时把每个状态值通过串口打印出来这样能快速判断是哪一步出了问题。4. 建立一条 DLMS 应用链路SNRM、AARQ 到数据读写的完整时序4.1 HDLC 层的链路建立不是简单的 TCP 握手如果只看着 DLMS 协议文档你可能会把它类比成 TCP 的三次握手实际不是。DLMS 在 HDLC 链路层用的是“平衡链路访问过程”的机制主站发送一个 SNRM 帧Set Normal Response Mode从站如果同意回复一个 UA 帧Unnumbered Acknowledge。这一步只是把物理链路“拉起来”还没到应用层通信。HDLC 帧的格式和普通串口帧差别很大。它有一个固定的帧头包含帧起始标志 0x7E、帧长度、帧控制字节、目的地址、源地址、HCS 校验然后才是信息段最后还有 FCS 校验和结束标志 0x7E。所以调试 DLMS 通信第一步就是看 0x7E 开始的十六进制数据流而不是直接解析内容。我在源码里把 HDLC 层拆成两个函数hdlc_rx_frame()负责从环形缓冲区里攒够一帧做 HCS 和 FCS 校验hdlc_tx_frame()负责把应用层的数据包封装成 HDLC 帧并发送。这里最需要留意的是地址字节DLMS 的服务器地址字段含有“客户地址”和“服务器地址”两个子字段如果上位机配置的地址和电表端不一致链路建立就直接失败。4.2 AARQ/AARE 关联建立应用层才开始认人链路层打通之后上位机会发 AARQApplication Association Request报文。这个报文包含应用层上下文名、协议版本、发起方和响应方的身份标识。从站收到 AARQ 后要检查上下文名是否支持。如果支持就回 AAREApplication Association Response帧在“结果”字段里填 0 表示接受填非 0 表示拒绝。AARQ/AARE 这一过程里有一个容易忽略的点AARE 报文的“关联结果”association-result默认是 0x00但如果你启用了加密和认证还需要带上认证机制参数比如低等级安全Lowest Security或者高等级安全High Security。低等级一般就是明文加密码验证高等级需要计算挑战码。我做的第一版为了方便调试用的是最低安全等级这样上位机只需配置一个客户端地址就能连上。关联建立成功后协议栈内部状态机会从DLMS_STATE_IDLE切换到DLMS_STATE_ASSOCIATED之后才能正常处理 Get、Set 请求。4.3 一次完整的读取流程代码是怎么走的这里我用一段简化代码来展示 DLMS 服务端处理读请求的流程。实际的工程里肯定更复杂但核心路径就是这样void dlms_task_loop(void) { uint8_t rx_buf[1024]; uint8_t tx_buf[1024]; // 从环形缓冲取出完整一帧 uint16_t frame_len hdlc_rx_frame(rx_buf, sizeof(rx_buf)); if (frame_len 0) { return; // 还没攒够一帧 } // 解析 HDLC 帧头校验通过后得到 APDU 数据 uint16_t apdu_len 0; int ret hdlc_extract_apdu(rx_buf, frame_len, apdu, apdu_len); if (ret ! FRAME_OK) { // 帧校验失败丢弃并等待重发 return; } // 进入 APDU 处理阶段这里会解析 AARQ/Get/Set 等 uint16_t resp_len 0; dlms_server_handle_apdu(g_server, apdu, apdu_len, tx_buf, resp_len); if (resp_len 0) { hdlc_tx_frame(tx_buf, resp_len); // 组帧并发送 } }每个从站设备的主循环里基本都有类似结构。区别在于有的实现用状态机有的实现用线程加队列。裸机环境下我建议用状态机因为串口帧什么时候来是不确定的状态机可以天然处理“收到一半”“校验失败”“重发”这些异常分支。4.4 主动上报和事件推送尽量放到非主流程DLMS 除了主站请求从站应答也可以做主动上报也就是从站在某些事件发生时向主站发送一个报文。电表场景里最常见的主动上报就是“停电事件”或“上电事件”。主动上报的处理逻辑在源码里单独放了一个event_notify.c文件它负责把事件打包成 DLMS 通知报文然后再塞到 HDLC 层的发送队列。为什么说尽量不要在主流程里做因为主动上报需要先判断通信链路是否建立成功如果还没建立关联就盲目发送会浪费通信资源且可能被主站忽略。我调试时遇到过一种情况事件上报报文把正常的事务响应帧给冲掉导致上位机读数据时收到乱序数据。后来我把事件上报放到一个独立的低优先级任务里等当前事务结束再发送才稳定下来。5. OBIS 对象映射表把寄存器数据变成协议能懂的语言5.1 OBIS 码的组成规则和常见数据点OBIS 码是 DLMS/COSEM 设备的“数据字典地址”格式是 A-B:C.D.E*F。其中A 表示数值定义范畴通常为 0B 表示通道号C 是物理量类型D 是测量种类E 是费率或处理类型F 表示存储区。很多人一看到六个数字就直接头大但项目里其实不需要背所有规则只需要把几个常用码定义清楚即可。我在这套系统中使用的 OBIS 映射表如下OBIS 码物理量COSEM 接口类说明1.0.1.8.0.255正向有功总电能3Register抄表最核心的数据1.0.2.8.0.255反向有功总电能3Register光伏或双向计量用1.0.1.8.1.255正向有功尖峰电量4Extended Register费率1电量1.0.1.8.2.255正向有功峰段电量4Extended Register费率2电量1.0.1.8.3.255正向有功平段电量4Extended Register费率3电量1.0.1.8.4.255正向有功谷段电量4Extended Register费率4电量1.0.32.7.0.255当前电压3Register单位 0.1V1.0.31.7.0.255当前电流3Register单位 0.001A1.0.0.1.0.255电表资产编号3Register用于标识设备0.0.1.0.0.255软件版本3Register用于维护映射表设计的关键是每个 OBIS 码都要挂接一个“读数据”回调函数。上位机发来查询该 OBIS 码的请求协议栈查表找到回调执行函数把结果编码进响应帧。这套机制在源码里实际上就是一个结构体数组加一个查找函数。5.2 实现一个注册式数据访问机制我在代码里没有写一堆 if-else 来判断 OBIS 码而是用了注册表typedef struct { uint8_t class_id; // 接口类 uint8_t obis[6]; // OBIS 码 A-F uint8_t attribute_id; // 属性编号2 表示读取值 int (*read_value)(void *ctx, uint8_t *value_buf, uint16_t *buf_len); } obis_item_t; static obis_item_t g_obis_table[] { { CLS_REGISTER, {1,0,1,8,0,255}, 2, meter_read_total_energy }, // 正向有功总 { CLS_REGISTER, {1,0,2,8,0,255}, 2, meter_read_reverse_energy }, // 反向有功总 { CLS_REGISTER, {1,0,32,7,0,255}, 2, meter_read_voltage }, ... }; int obis_lookup_and_call(obis_item_t *table, uint8_t *obis, uint8_t attr_id) { for (uint8_t i 0; i OTBIS_TABLE_SIZE; i) { if (memcmp(table[i].obis, obis, 6) 0 table[i].attribute_id attr_id) { return table[i].read_value(NULL, out_buf, out_len); } } return DLMS_ERR_OBJECT_NOT_FOUND; // 对象不存在 }这个设计的优势是添加一个新的数据点非常快只需在表里加一行再写一个回调函数即可完全不用碰协议栈的代码。这对接入不同类型的电表非常有帮助毕竟换一个客户就可能有新的数据项要求。5.3 数据类型与单位的处理最容易让上位机“读不懂”DLMS 对数据类型有严格定义读电能数据返回的是double-long-unsigned8 字节读电压电流返回的可能是long-long或double-long。如果你把一个有符号数编码成无符号数或者在数值后面少了一个伸缩因子上位机就算收到了响应解析出来的数据也完全不对。比如电流寄存器 RN8209C 输出的原始值是 0.001A 为单位内存里保存的是整数 12345表示 12.345A。上位机通过 OBIS 读到的也是整数 12345如果协议的伸缩因子配置不一致显示可能变成 12345A 或 12.345A。为了让上位机拿到精确数值我在 OBIS 表的回调里统一做了缩放处理尽量避免协议栈自己去猜单位。还有一个细节是电能值的精度。RN8209C 的电能寄存器是固定的小数位如果直接读取原始值返回小数位数可能对不上。我维护了一个energy_scaling参数掉电时从 EEPROM 读回调函数里根据这个参数把电能值换算成协议约定的 0.001kWh 精度。6. 调试 DLMS 通信时我反复踩过的几个坑6.1 串口空闲中断 DMA 的坑帧被拆成两段DLMS 通信帧长度不定我第一版用串口逐个字节接收CPU 中断开销太大帧稍长就丢数据后来改成 DMA 接收加空闲中断。空闲中断的思路是串口收到数据存进 DMA 缓冲区当总线空闲超过一个字节时间触发空闲中断此时认为一帧接收完成禁止 DMA 传输并处理缓冲区数据。这个方案本身没问题但我在实现时忽略了一个细节DMA 接收缓冲区的指针在每次接收之前必须重置到起始位置否则第二次接收的帧会把第一次的数据覆掉或者从偏移位置继续存。处理完一次帧后我调用__HAL_UART_CLEAR_IDLEFLAG()清掉标志位但 DMA 计数器的值没有正确处理导致帧头判断出错。后来我在空闲中断里重新读取huart-hdmarx-Instance-NDTR计算实际接收字节数再重置接收缓冲这个问题才解决。6.2 HDLC 帧的分包和粘包不能只处理完整帧DLMS 主站发来的帧可能被底层串口拆成多个 TCP 包也可能连续发来两帧中间没有停顿。如果只做空闲中断帧太长时可能会出现半个帧的空闲间隔导致误认为一帧结束了。我的处理是把所有原始字节先丢进环形缓冲区然后在主循环里专门检查“是否有一帧完整 HDLC 数据”判断依据是找到 0x7E 帧起始标志并校验帧长度字段和 FCS。检查帧完整性时不能只信帧尾 0x7E因为数据区也可能出现 0x7E。DLMS 在传输时会对 0x7E 做转义处理把 0x7E 转成 0x7D 0x5E。这样接收端才能正确区分。如果对 HDLC 转义处理不熟很容易在数据里误判帧边界。这一点我建议一定要看协议文档里的“透明传输”章节别直接用网上随意找的串口例子。6.3 时间同步不通过关联总是建立失败DLMS 协议对时间比较敏感尤其在高安全等级连接、或者读取事件记录时。有些上位机会在 AARQ 之前发送一个时间同步帧如果从站的 RTC 误差太大或者 RTC 初始化失败返回一个异常时间上位机可能会判断设备状态异常不进入正常读取流程。我调试时遇到的现象是上位机一直打印“关联被拒绝”但用仿真器的串口助手看从站已经回了 AARE而且结果码是 0。后来才发现问题不在 AARE而是上位机在建立关联前会先发起一个时钟设置的请求从站的 RTC 初始化代码有 bug导致读取时间返回失败上位机因此判定表计时间不可用。解决方式是每次开机都用外部 RTC 芯片的时间校验主控内部 RTC并且增加手动校时接口保证时间字段有效。6.4 协议栈本身的错误也不好找建议用抓包工具先对照嵌入式上 DLMS 调试没有现成的 Wireshark 插件直接看串口报文但可以先用 USB 转串口把 STM32 的 TX 和 RX 都引到 PC 上用串口抓包工具记录完整报文流再用 PC 端的 DLMS 主站软件跑一遍同样的流程。对比两条报文看差异出在哪个字节。这个方法比人工看十六进制高效得多。我第二周排查的一个问题就是靠抓包对比发现的。表计端发送的 Get 响应帧看起来长度和数据都对但 PC 端 DLMS 主站一直报编码错误。对照 PC 端栈生成的响应帧才发现我在 BER 编码某些长整数时没有正确加长度字节例如数据超过 127 字节时长度字段需要多字节表示而我的代码只写了一个字节。这类错误人工看很难发现但抓包对比一秒就能定位。6.5 不要让计量芯片的通信占用 DLMS 响应时间RN8209C 读取电能寄存器需要几毫秒如果此时正好有 DLMS 请求进来可能造成响应超时。我的主循环结构中DLMS 任务优先于计量读取任务计量读取放在定时器回调里DLMS 数据处理延后一个周期执行。这样DLMS 响应的最大时延大约一个定时器周期上位机不会判定超时。掉电处理也要注意。检测到掉电时系统需要保存当前电能值和事件这个过程如果和 DLMS 通信同时发生可能把唯一的一小段时间片占用完。后来我在掉电保存函数里加了互斥标志DLMS 处理循环发现掉电标志被置位就只处理帧接收、不组帧发送优先保证数据落盘。7. 代码之外过检、安全与后续演进7.1 这个源码离量产电表还差哪些东西如果是给实验室交个 Demo做到前面几节的程度已经足够。但要做成一款能批量的智能电表还需要补几个关键模块。第一是费控功能。国内电表网关经常要求支持本地费控和远程费控包括阶梯电价、费率时段、最大需量结算甚至拉合闸控制。这些功能本质上是在 DLMS 标准的“执行对象”里扩展几个 OBIS 码但业务逻辑实现的工作量不小。第二是事件记录与冻结功能。DLMS 规范规定了很多事件类型比如电压越限、电流不平衡、开盖检测、掉电恢复等每个事件都要按时间顺序存储并支持上位机查询和清除。第三是自检和诊断功能。电表在现场不可能频繁出人维护必须能对计量芯片通信、时钟精度、存储单元健康状态做自检并把结果通过 DLMS 数据对象暴露给主站。如果有量产计划还不要忘了硬件上的电磁兼容和功耗验证。RS485 通信口的防雷、红外头的抗光干扰、电源模块的纹波这些都直接影响 DLMS 通信的稳定性。这一块我在项目后期吃过大亏实验室一切正常现场一接三相负载就频繁掉线最后查出来是电源噪声串到 RS485 收发器导致串口误码率升高。7.2 安全机制要到什么程度DLMS 支持应用层加密常见的有 AES-GCM、ECDSA 签名和挑战应答认证。如果只是用于居民用户抄表低安全等级通常够用但如果涉及预付费、远程拉合闸或者关键数据配置那就必须启用高安全等级。我在系统中预留了完整的密钥管理接口但没有把加密环节全部打开。原因是STM32F103 软件实现 AES 加解密会带来较大的算力消耗一旦开启每帧报文都需要额外 10~30 毫秒的处理时间低速串口下还可能引发超时。量产版本建议直接换带硬件 AES 的 STM32L4 或 STM32F2 系列算力余量更充裕也更容易通过安全检测。7.3 后续还能怎么扩展这套源码的平台化能力不错尤其是注册式 OBIS 映射表换计量芯片、换通信模块都很方便。如果后面想把 4G 模块接进来只需要在dlms/port/下新增一个 socket 适配层把 4G 模块的 AT 指令封装成 HDLC 帧的收发接口即可业务部分完全不用动。另外如果产品需要对接的是欧洲或东南亚市场建议提前把多语言、多费率、多时区的参数配置抽成独立的配置区和代码解耦。这样换一个客户、换一套费率只需更新参数区内容不用重新烧录固件。我个人的体会是这类项目最费时间的往往不是协议本身而是把协议和产品业务、硬件限制、现场环境捏合在一起的过程。如果你做的第一块 STM32 电表能稳定跑通 DLMS 的读数据、写参数、事件上报这三条主链路后面再多的功能都只是在这个骨架上加肌肉而已。最后再分享一个非常小的实操技巧调试阶段尽量把 DLMS 的日志输出到独立串口并且分等级打印尤其是 HDLC 帧收发和 BER 编解码模块这两块的日志能拯救你无数个加班的夜晚。本文还有配套的精品资源点击获取
分享:

看完干货,该让你的企业上线了

免费需求沟通 · 48 小时内出具建站方案 · 河南本地可上门