侧边栏壁纸
  • 累计撰写 12 篇文章
  • 累计收到 1 条评论

RS485电表怎么把数据传到服务器?银尔达YED-DG720Y 4G透传,从接线到解析一次讲完

2026-9-3 / 0 评论 / 8 阅读

<p style="color:#2B2B2B;font-family:-apple-system, " font-size:medium;"="">

前两篇把 DLT645 的帧结构讲透了(初识协议、07 帧结构解析),不少朋友顺着文章问:表在配电房里,电脑在办公室,中间隔着半个厂区,这数据到底怎么弄回来?

这篇就把传输这一段补上。用一块银尔达 YED-DG720Y 的 4G DTU,把 485 口上的电表数据原封不动送到公网服务器,服务器端再按 DLT645 把帧解出来。链路跑通之后,采集、存储、界面就都顺了。

整体链路长这样:

电表(RS485口) --A/B-- YED-DG720Y(4G DTU) --移动网络-- 云服务器(TCP端口) --解析程序--> 数据库/展示

先想明白:DTU 到底干了什么

配电房一般没有网线,拉 WiFi 又不现实(穿墙差、跨厂区)。而电表只有 485 口,走 DLT645 协议。DTU 做的事特别简单:把 485 线上的字节流,原样通过 4G 网络搬到服务器,反过来服务器发的数据,它也原样写回 485 线。

它不做任何解析。你发什么,它传什么。这一点很重要——意味着到了服务器端,我们面对的仍然是前面几篇讲的那些 DLT645 帧,解析逻辑一点没变,变的只是传输通道。

硬件和接线

YED-DG720Y 是银尔达的工业级单 RS485 串口 DTU,官方参数里挑几个跟项目相关的:

  • 供电 5~36V 直流,推荐 12V,功率 10W。注意别用电脑 USB 供电,功率不够,模块起不来或者一加载就掉
  • 4G Cat1 全网通,移动、联通、电信都能用
  • 1 路 RS485,波特率 1200~230400
  • 尺寸 96×26×25mm,35mm 导轨安装,外壳阻燃
  • SIM 卡:内置贴片 SIM + 外置卡槽
  • 协议支持 TCP、UDP、MQTT、HTTP、WebSocket,也可以直接对接阿里云、腾讯云、OneNet、华为云、涂鸦、ThingsCloud

接线就三对:

DTU 电表/电源
VIN 12V 电源正极
GND 电源负极(和电表共地更好)
A 485 A(同名相颜色一致,别接反)
B 485 B

485 线用屏蔽双绞线,屏蔽层单端接地。距离几十米以内问题不大,线越长越要低速率和可靠接地。

SIM 卡缺口朝外插进卡槽,天线拧到 SMA 座上,上电后 PWR 灯亮。

串口参数先对齐

电表 485 口出厂一般默认 9600、8 数据位、无校验、1 停止位,也就是常说的 8N1。具体以电表铭牌或者说明书的通讯参数为准。

DTU 的串口参数必须跟电表完全一致,否则收上来的就是乱码,或者一帧都拼不齐。很多项目卡在第一晚调试,最后发现就是波特率或者校验位没对上,这类问题排查起来很浪费时间,建议上电之前先确认清楚。

DTU 配置:把服务器地址告诉它

用银尔达的配置工具(通过 USB 或串口连接模块)把这几项写进去:

  1. 工作模式选 TCP Client——我们是让 DTU 主动去连服务器
  2. 服务器地址填你的公网 IP 或域名
  3. 端口填服务器上开好的 TCP 端口
  4. 串口参数按上面电表实际参数填

保存后重启模块。看指示灯判断状态:

  • NET 灯 100ms 快闪:卡正常但还没注册上网络,检查 SIM 卡是否欠费、天线是否接好
  • NET 灯 500ms 慢闪、RDY 不亮:网络注册上了,但没连上服务器。重点查服务器地址、端口,以及服务器有没有真的在监听
  • NET 灯 1000ms 慢闪、RDY 常亮:连上服务器了,可以往下走

顺带提醒一句:如果用的是银尔达的定向流量卡,服务器域名要填 dtu.yinerda.com,端口 91,别填自己服务器,这是官方 FAQ 里明确写的。

服务器端要准备什么

一台有公网 IP 的机器(云服务器最省事),做三件事:

  1. 开一个 TCP 端口,比如 8501
  2. 防火墙和安全组放行这个端口
  3. 跑一个 TCP server 程序,等着 DTU 连上来

这里有个新手最容易绕进去的弯:DTU 是透传,不是抄表。 电表不会自己说话,你要读数,得服务器主动往下发 DLT645 读取指令,DTU 原样转发到 485 线上,电表应答,应答帧再原路回到服务器。所以服务器程序一般按固定间隔(比如每分钟)下发一次读取指令。

服务器端解析示例(Python)

下面这段代码做四件事:监听端口、定时下发"读当前正向有功总电能"指令、收应答帧、校验并解析出电量。直接能跑,改成自己的表地址和端口就行。

# -*- coding: utf-8 -*-
"""
DLT645 电表远程采集服务端
流程: 监听TCP -> 接收DTU连接 -> 定时下发读表指令 -> 收应答帧 -> 校验 -> 解析电量
"""
import socket
import threading
import time

SERVER_HOST = '0.0.0.0'
SERVER_PORT = 8501            # 云服务器上放行的端口
METER_ADDR  = b'\x20\x17\x09\x32\x00\x72'   # 你的表地址, 6字节BCD, 换成实际表号
INTERVAL    = 60              # 每60秒读一次

def checksum(data):
    """DLT645校验: 从帧起始符68到数据域末字节累加, 取低8位"""
    return sum(data) & 0xFF

def make_read_frame(addr):
    """
    构造"读当前正向有功总电能"请求帧
    请求帧: 68 地址(倒序) 68 11 04 33 33 34 33 CS 16
    """
    frame = bytearray()
    frame.append(0x68)
    frame += addr[::-1]        # 645帧里地址低字节在前, 所以倒序
    frame.append(0x68)
    frame.append(0x11)         # 控制码: 读数据
    frame.append(0x04)         # 数据长度: 4字节数据标识
    frame += b'\x33\x33\x34\x33'  # 数据标识: 当前正向有功总电能
    frame.append(checksum(frame))
    frame.append(0x16)
    return bytes(frame)

def parse_energy_frame(data):
    """
    解析应答帧, 提取当前正向有功总电能
    应答帧: 68 地址(6) 68 91 长度 数据域 CS 16
    数据域  = 数据标识(4字节) + 数据(4字节BCD, 小端, 单位0.01kWh)
    返回电量(kWh)或None
    """
    if len(data) < 16 or data[0] != 0x68:
        return None
    if data[8] != 0x91:        # 控制码, 91是正确应答
        return None
    ln = data[9]               # 数据域长度
    payload = data[10:10 + ln]
    if checksum(data[:10 + ln]) != data[10 + ln]:   # 校验不符说明帧被干扰
        return None
    # 电量是4字节BCD, 小端序: 比如 12 34 56 78 -> 78563412 -> 785634.12 kWh
    bcd = payload[4:8][::-1]
    val = 0
    for b in bcd:
        val = val * 100 + (b >> 4) * 10 + (b & 0x0F)
    return val / 100.0

def handle_client(conn, addr):
    print('[{}] DTU已连接: {}'.format(time.strftime('%H:%M:%S'), addr))
    buf = b''
    try:
        while True:
            conn.sendall(make_read_frame(METER_ADDR))   # 下发读表指令
            conn.settimeout(5.0)
            try:
                buf += conn.recv(256)
            except socket.timeout:
                print('  -> 超时无应答, 查串口参数/表地址/A B是否接反')
                continue
            energy = parse_energy_frame(buf)
            if energy is not None:
                print('  -> 当前正向有功总电能: {} kWh'.format(energy))
                buf = b''
            time.sleep(INTERVAL)
    except Exception as e:
        print('连接断开: {}'.format(e))
    finally:
        conn.close()

def main():
    srv = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    srv.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    srv.bind((SERVER_HOST, SERVER_PORT))
    srv.listen(5)
    print('监听 {} 端口, 等待DTU接入...'.format(SERVER_PORT))
    while True:
        conn, addr = srv.accept()
        threading.Thread(target=handle_client, args=(conn, addr), daemon=True).start()

if __name__ == '__main__':
    main()

解析的核心就是前面 DLT645 系列讲的那套:找 68 帧头、剥表地址、看控制码和长度、按数据标识取数据、校验对不上就丢帧。这里把电量(BCD、小端、0.01kWh)转成了人能看的数值。跑通了之后,把结果存数据库、做个展示页面,就是你自己发挥了。

常见坑,按顺序查

  1. PWR 灯都不亮:电源没接对,检查 12V 和功率
  2. NET 快闪:SIM 卡没插好、欠费,或者天线没拧
  3. 连上服务器但收不到数据:A/B 接反、串口参数不对、表地址不对(广播地址 AA AA AA AA AA AA 测试能通,不代表指定地址就行)
  4. 校验一直错:注意从 68 开始取帧,别把前导的 FE 也算进去
  5. 数据一截一截丢:485 线太长或没有终端电阻,降低波特率试试;DTU 固件 FAQ 里有 RS485 反转时间可以微调,默认 0 对应 9600 波特率下延迟约 104 微秒
  6. 读取间隔太短:电表应答要时间,问得太勤会被忽略,60 秒读一次已经足够能耗采集用了

链路通了之后,剩下的就是把解析结果存库、做页面、算能耗。下一篇可以讲讲一台 DTU 下面挂多块表时,怎么排队轮询不冲突。有需要的评论区说一声。

</p>

评论一下?

OωO
取消