简介:面向工业自动化、能源管理及上位机集成工程师的实战文档,聚焦 DL645-2007 电能表通过 RS485 接入 KepwareEX,并最终在 Wincc 中集成显示电能数据。文中完整演示了基于 U-CON 驱动创建自定义通道、添加设备、编辑事务与配置缩放的流程,对 LBCD 数据类型、余 3 码换算、唤醒报文、校验码及结束符等关键细节都给出可复用的配置示例,能有效缩短现场调试时间。包体为 1 个 PDF 文件,容量 1.21MB,以图文步骤为主,适合已具备串口通信基础、需要接入非标准规约设备的工程师。该资料已有 2918 人学习,内容涵盖 RS485/RS232 转换器与串口服务器的硬件接线、事务报文设计直至 Wincc 绑定 OPC 条目的完整链路,是一份可直接对照实施的参考手册。
1. 先连接再谈数据:Kepware连DL645-2007电表,先跨过三道坎
现场做配电改造时经常遇到这种场景:配电柜里一排DL645规约的电能表,客户手里只有表计说明书,Kepware那边已经跑着一套OPC UA数据平台。把电表和Kepware连起来,并不像填个IP就完事,DL645-2007是串口协议,RS485总线上走的是一帧带地址、控制码、校验和的数据包。地址填错、数据标识写歪、字节序没注意,读回来的不是0就是超时,而且这类问题往往在凌晨抄表时段才暴露。这篇文章把链路从串口调试一路拆到Kepware的Tag配置,再把最常见的几个坑一次说清,适合在做配电监控、能耗采集、工厂车间电表接入的工程师参考。
2. DL645-2007电表通信要点:帧结构、数据标识与串口参数
2.1 帧结构:先看懂一帧报文是怎么拼出来的
DL645-2007的报文不是简单的一串十六进制,它有固定的帧格式。主站发一帧读请求,里面要有前导字节、起始符、地址域、控制码、数据域长度、数据域、校验和结束符。前导字节是FE,可以发2到4个,用来唤醒总线上的电表;然后是68H起始符;紧接着是6字节地址域,对应电表通讯地址的BCD码,注意发送顺序是低字节在前;再后面是控制码,比如主站读当前数据的请求是11H,从站正常应答是91H;数据域长度L表示后面数据域有多少个字节;数据域里先放4字节数据标识,再放实际数据;最后是CS校验和16H结束符。
| 字段 | 长度 | 内容 |
|---|---|---|
| 前导字节 | 2~4字节 | FE |
| 帧起始符 | 1字节 | 68H |
| 地址域 | 6字节 | 电表通讯地址BCD,低字节在前 |
| 控制码 | 1字节 | 读请求11H,读应答91H |
| 数据域长度L | 1字节 | 数据域字节数 |
| 数据域 | L字节 | 数据标识 + 数据 |
| 校验CS | 1字节 | 从起始符到数据域末尾累加,取低8位 |
| 结束符 | 1字节 | 16H |
可以直接用Python的pyserial把一帧读请求发出去,先用串口助手确认这条链路能不能通,再谈Kepware的事。下面这段代码是发一帧“读当前组合有功总电能”的请求,电表通讯地址按123456789012举例。
import serial # 电表通讯地址为123456789012(12位BCD) # 地址域按低字节在前:12 34 56 78 90 12 addr = bytes([0x12, 0x34, 0x56, 0x78, 0x90, 0x12]) frame = bytearray([0xFE, 0xFE, 0x68]) frame += addr frame += bytes([0x11, 0x04]) # 0x11 读当前数据,0x04 数据域长度 frame += bytes([0x00, 0x00, 0x00, 0x00]) # 数据标识:当前组合有功总电能 cs = sum(frame[2:]) & 0xFF # 从起始符68H到数据域末尾累加,取低8位 frame.append(cs) frame.append(0x16) # 结束符 ser = serial.Serial( port="COM5", baudrate=9600, parity=serial.PARITY_EVEN, bytesize=8, stopbits=1, timeout=2, ) ser.write(frame) resp = ser.read(64) print(resp.hex()) ser.close()这段代码里,serial.Serial的五个参数要和电表实际串口参数完全一致,波特率是9600、偶校验、8数据位、1停止位,这是DL645-2007场景里最常见的组合。timeout设为2秒,是给电表留响应时间。addr那一串不是随便填的,它是电表内保存的通讯地址做BCD编码后的结果,和液晶屏上看到的表号不一定是一个东西,后面排查环节还会再提。CS校验的计算范围是从68H起始符开始,到数据域最后一个字节为止,全部累加后取低8位,这个规则在写帧和验帧时都要用。
2.2 数据标识:你先要决定读哪一项
DL645-2007和Modbus最大的区别是它不按寄存器号寻址,而是按“数据标识”找数据对象。数据标识是4字节,平时在说明书里看到的写法往往是DI3 DI2 DI1 DI0的顺序,但报文里发送顺序是DI0、DI1、DI2、DI3,低字节在前。以读当前组合有功总电能为例子,手册标识写00000000,实际上就是4个00字节;A相电压手册标识写02010100,报文里就是00 01 01 02。
下面这些是电表采集时最常用的一组数据标识,不同表厂的实现会有差异,但大部分符合DL645-2007标准的表都能认。
| 含义 | 手册标识 | 报文数据标识(DI0 DI1 DI2 DI3) |
|---|---|---|
| 当前组合有功总电能 | 00000000 | 00 00 00 00 |
| 当前组合有功尖电量 | 00000001 | 01 00 00 00 |
| 当前组合有功峰电量 | 00000002 | 02 00 00 00 |
| 当前组合有功平电量 | 00000003 | 03 00 00 00 |
| 当前组合有功谷电量 | 00000004 | 04 00 00 00 |
| A相电压 | 02010100 | 00 01 01 02 |
| B相电压 | 02010200 | 00 02 01 02 |
| C相电压 | 02010300 | 00 03 01 02 |
| A相电流 | 02020100 | 00 01 02 02 |
| B相电流 | 02020200 | 00 02 02 02 |
| C相电流 | 02020300 | 00 03 02 02 |
拿到一块新表,我一般不会凭记忆硬填标识,而是先把表计说明书翻到数据标识那一页,找到“当前组合有功总电能”和“冻结”类数据标识,确认之后再动Kepware。因为尖峰平谷的费率顺序、冻结电量的入口,各厂家定义不完全一致,你按我的表填了读出来是乱数,不代表协议不对,很可能只是这块表的标识表不同。这也是DL645-2007项目里最容易翻车的地方之一:串口通得顺顺的,但Tag数据驴唇不对马嘴。
2.3 串口参数与接线:9600、偶校验、485总线别接反
串口参数这块,常见是波特率2400或9600,校验偶校验,8数据位,1停止位,无硬件流控。Kepware里的通道参数要和电表完全对上,否则表现是通道偶尔能通、大部分时间超时,而且越查越像玄学,最后发现是电表是2400,Kepware里填了9600。
物理层方面,RS485是差分信号,A、B两端不能接反。很多USB转485模块上丝印标的是D+、D-,对应关系是D+接A、D-接B,接反了不是完全不通,而是通讯质量差,时好时坏。长距离或多表并联时,在总线两端各并一只120Ω终端电阻能显著减少回波干扰。还要注意485总线是共地系统,多块电表之间如果地电位差大,轻则通讯不稳定,重则烧接口。一个总线上挂几十块表,尽量用带隔离的串口服务器或隔离型转换器。
另外,DL645-2007虽然规定了帧格式,但对帧间隔并没有特别激进的要求,很多电表对连续请求的间隔有下限。调试时如果发现一帧一帧发没问题、连续轮询就超时,先把间隔拉到500ms以上试,这通常能直接定位到是表处理速度的问题还是总线干扰的问题。
3. 在Kepware里建通道、建设备、建标记:完整配置路径
3.1 通道配置:选对驱动,逐个确认串口参数
Kepware连接DL645-2007最常见的路径是给Kepware装一个DL/T645驱动模块,部分发行版自带,部分需要单独安装。如果手上没有这个驱动,就用RS485转以太网网关把645协议翻译成Modbus TCP,Kepware只负责按Modbus读网关寄存器,配置思路一样,只是通道类型不同。下面按Kepware直接驱动DL645这条线来写。
在Kepware工程树里右键Channel,新建通道,通道类型选DL/T645,之后设置串口参数。这一步最容易踩的坑是通道里串口被占用,比如串口调试助手没关、其他采集软件占着COM口,通道会起不来。还有USB转485在Windows下分配的COM号不固定,换一次USB口COM号就变,Kepware里要同步改。
| 参数项 | 常见值 | 说明 |
|---|---|---|
| 串口 | COM5 | 实际COM号以系统分配为准 |
| 波特率 | 9600 | 与电表实际参数一致 |
| 数据位 | 8 | DL645-2007标准最常见 |
| 校验位 | Even | 偶校验 |
| 停止位 | 1 | 1停止位 |
| 流控 | None | 关闭硬件流控 |
3.2 设备配置:通讯地址、超时与重试
配置完通道,下一步是在通道下新建Device,每个设备对应一块电表。Device的地址要填电表的通讯地址,这个地址不是随便按表号编的,很多电表在参数设置菜单里有一个“通讯地址”项,出厂时可能和表号一致,也可能被改过。Kepware设备地址的填法常见是6字节低位在前,比如电表通讯地址是123456789012,就填12 34 56 78 90 12。如果在地址上含糊,后面多半读回来的是0,这个等会到排查章节再说。
超时和重试参数直接关系到稳定性。我的习惯是超时设2000ms,重试2次,请求间隔500ms以上,扫描周期1000ms。总线上表越多,扫描周期越要放大,不要一味追求“快”,对DL645这种串口协议来说,稳定比速度重要得多。
| 参数项 | 建议值 | 说明 |
|---|---|---|
| 超时 | 2000ms | 电表响应慢时适当调大 |
| 重试次数 | 2 | 偶发超时可自动重发一帧 |
| 请求间隔 | 500ms | 避免连续帧触发电表异常 |
| 扫描周期 | 1000ms | 多表并联时可调到2000ms |
3.3 标记地址映射:把DL645数据标识换成Kepware地址写法
设备建好后就是建Tag。Kepware里Tag的Address字段填的不是Modbus那样的寄存器号,而是DL645数据标识。以读当前组合有功总电能为例子,Address直接填00000000,数据类型根据驱动手册选,常见是Word或者String。这里有个原则:第一轮先按“原始值”读进来,不要急着做任何加工,先确认这个值和电表液晶屏显示能不能对上,然后再谈倍率、缩放和字节序。
下面是我常用的Tag清单对照表,方便新建Tag时直接抄。
| Tag名称 | Address | 数据类型 | 倍率 |
|---|---|---|---|
| Elec_Total_kWh | 00000000 | Long | 0.01 |
| Elec_Sharp_kWh | 00000001 | Long | 0.01 |
| Elec_Peak_kWh | 00000002 | Long | 0.01 |
| Elec_Flat_kWh | 00000003 | Long | 0.01 |
| Elec_Valley_kWh | 00000004 | Long | 0.01 |
| Volt_A_V | 02010100 | Long | 0.01 |
| Volt_B_V | 02010200 | Long | 0.01 |
| Volt_C_V | 02010300 | Long | 0.01 |
| Curr_A_A | 02020100 | Long | 0.001 |
| Curr_B_A | 02020200 | Long | 0.001 |
| Curr_C_A | 02020300 | Long | 0.001 |
块表几十个Tag,手动在Kepware里一个个点也行,但效率太低。我一般用脚本先把清单整理成CSV,再对照Kepware导入模板粘贴,避免手滑把Address抄错。下面这段Python脚本是生成CSV清单用的,字段按我自己的习惯来。
import csv catalog = [ ("Elec_Total_kWh", "00000000", 0.01), ("Elec_Sharp_kWh", "00000001", 0.01), ("Elec_Peak_kWh", "00000002", 0.01), ("Elec_Flat_kWh", "00000003", 0.01), ("Elec_Valley_kWh", "00000004", 0.01), ("Volt_A_V", "02010100", 0.01), ("Volt_B_V", "02010200", 0.01), ("Volt_C_V", "02010300", 0.01), ("Curr_A_A", "02020100", 0.001), ("Curr_B_A", "02020200", 0.001), ("Curr_C_A", "02020300", 0.001), ] with open("kepware_dlt645_tags.csv", "w", newline="", encoding="utf-8-sig") as f: writer = csv.writer(f) writer.writerow(["Tag Name", "Address", "Scale"]) for name, addr, scale in catalog: writer.writerow([name, addr, scale])这段脚本输出了三个字段:Tag Name、Address、Scale。Address填的就是DL645数据标识,Scale是给后面的Kepware缩放配置做备忘的。注意编码用了utf-8-sig,是因为Excel打开UTF-8的CSV时如果没有BOM,中文列名会乱码。脚本本身不做导入动作,Kepware各版本的导入模板列名不完全一致,把这三列对应到模板里再粘进去最稳。
注意:第一次连上某块表,建议只建一个Tag,先读00000000,确认数值和电表屏显一致,再批量建其余Tag。一次全建好再排查,很难分清是通讯问题还是地址问题。
4. 连接DL645-2007电表的高频踩坑与排查
4.1 通道状态正常但Tag读数为0
现象是Kepware里通道显示Running,设备也没报错,但Tag值一直是0或者空。拿串口助手手动发同一帧请求,电表正常回应。这种情况下问题往往不在物理链路,而在地址或数据标识。
原因有三个:一是Device地址和电表通讯地址不匹配,表号是表号,通讯地址是通讯地址,两者可能不一样;二是Tag的Address填错,比如把02010100填成了0201 0100之外的变体,电表根本不认这个标识;三是数据类型选错,驱动按Long返回,你选了Short,值被截断后在Kepware里显示为0。
解决方法是先回到串口助手验证。确认手动发“00 00 00 00”能收到数据,再把Kepware里那个Tag删掉重新建一次,Address只填00000000,数据类型按驱动文档改成长整型。如果手动发也读不到,说明电表里根本没有这个数据项,去翻说明书查冻结表或总电量那个标识实际是什么。
4.2 偶尔能读,连续采集几分钟就超时
现象是系统刚启动时一切正常,跑个几分钟就开始连续超时,重启Kepware通道恢复,循环往复。这个现象最让人头疼,因为不是完全不通,而是“时好时坏”。
原因主要有两类。一类是请求频率太高,Kepware扫描周期设到了100ms甚至更低,DL645-2007的电表处理速度跟不上,一帧还没处理完下一帧就来了。另一类是RS485总线物理层问题,总线没有终端电阻,或者A、B线接反但没完全断开,随着持续通讯发热、信号质量变差,问题逐渐积累。
解决方法是先软后硬。软的一面对应Kepware参数,把扫描周期调到1000ms,请求间隔调到500ms,超时调到2000ms,重试2次。硬的一面给总线两端各加120Ω终端电阻,用双绞线走线,屏蔽层单端接地。这块最玄学,但把终端电阻加上之后,很多“偶尔超时”的症状自己就消失了。
4.3 读到的数值比实际大了10倍或100倍
现象是电表液晶屏显示12.34kWh,Kepware读到1234,或者123400。这类问题在第一次接电表时特别常见,本质上是对DL645数据格式理解不到位。
原因是DL645-2007电能数据用BCD码存储,而且小数位数由电表规格决定。比如当前正向有功总电能存的是12.34,BCD原始值可能是1234,如果你按整数读显示1234,天然就大了100倍。另一个常见的翻车点是字节序,4字节BCD在Kepware里按字拼接时,如果高低字顺序反了,读出来就是另一串数。
解决方法是确认两个事:一是表计说明书里总电能的小数位数,Kepware Tag上做线性缩放,把原始值乘以0.01或0.001;二是检查设备属性里的字节序设置,改成Low Byte First或LSB在前,改完再看数值是否和表对得上。改倍率和改字节序是两件事,别混在一起调,否则永远分不清是谁的锅。
4.4 一块总线挂20块表,总有几块时好时坏
现象是单表直连全部正常,并联到一个总线后,有几块表偶尔读不到,报地址错误或超时。这类问题在批量接入时非常典型。
原因是多方面的。首先电表地址很可能有重复或错填,表号是表号,通讯地址是通讯地址,批量改过参数后两本账对不上;其次,总线上如果某块表在空闲时主动上送心跳帧或广播帧,会占据总线时间,把其他表的应答窗口挤掉;再就是个别表处理慢,驱动超时后重试,重试又和别的表请求撞在一起。
解决方法是逐个核对Device地址和表计参数,确保没有重复;扫描周期尽量错开,不要在同一时刻并发请求所有设备;驱动超时重试打开,让偶发失败的帧自动重发一次。如果还是有个别表稳定出问题,拿串口助手单独挂在那块表上观察一整晚,看它是不是在主动往外发东西。这类问题的排查只能一个个排除,没有捷径。
5. 让电表数据直接变成有用值:转换、落库与跨天校验
5.1 用倍率和数据类型把BCD原始值换算成工程量
Kepware读回来的BCD原始值不适合直接展示。我一般会在Kepware里建两个Tag,一个叫Elec_Total_Raw保留原始值,一个叫Elec_Total_kWh做线性缩放。缩放比例用0.01或表计说明书上的小数位数,这样历史追溯时还能拿原始值验算一遍,不用重新读表。注意倍率是加在Tag的缩放配置里,不是改Address。
5.2 把读数落库:OPC UA转发与定时采集
数据要落地,常见做法是把Kepware当OPC UA服务器,采集端用OPC UA客户端定时读Tag,再写入数据库。也可以直接用Kepware的日志或数据转发插件把变化写到库里。无论走哪条路,定时器要避开Kepware的扫描周期,读得太频繁会读到缓存里的旧值。我习惯整点后延迟30秒再采,正好避开电表自身的结算预处理时段。
5.3 跨天读值时要分清“当前”和“冻结”
最后说一个只在跨天时发生的怪问题:某天凌晨采到的总电能比前一天晚上小。排查到最后发现读的是“当前”数据,电表在结算日做了电量分时切换或数据更新,当前值阶段性回退。正式项目里推荐读“冻结”类数据标识做归档,或者每天固定时间读一次并做差值校验。我现在拿到新一批表,第一件事就是用凌晨的冻结值连续比对三天,确认这块表和驱动配合稳定,再扩大接入范围。这个习惯帮我省了很多半夜跑现场的麻烦,希望帮到你。
本文还有配套的精品资源,点击获取