用 Python 实现非接触电压传感器 Modbus 数据校验与离线缓存追传客户端

背景

在边缘采集项目里,圣斯尔 SSET CE-AJ12N 这类 非接触电流电压传感器 通过 RS485 Modbus 输出电流、电压、功率等寄存器。现场网络偶尔抖动会导致轮询失败,直接丢数会影响后端趋势分析。本文给出一个轻量 Python 客户端:做 CRC 校验、失败重试与指数退避,并把未能上送的报文写入本地 SQLite,网络恢复后追传,避免数据空洞。

硬件与链路

典型链路:CE-AJ12N(从站,RS485)→ USB 转 485 → 边缘主机(Python 轮询)→ 本地 SQLite 缓冲 → 上行 MQTT/HTTP。下面代码聚焦"读寄存器 + 校验 + 离线缓存 + 追传"四件事。

Modbus 寄存器映射(示例)

寄存器(十六进制) 含义 类型
0x0000 电流(0.01A) U16
0x0001 电压(0.1V) U16
0x0002 有功功率(0.1W) U16
0x0003 频率(0.01Hz) U16

核心代码

import sqlite3
import serial
import time
import struct

def crc16(data):
    # Modbus RTU CRC16 计算
    crc = 0xFFFF
    for b in data:
        crc ^= b
        for _ in range(8):
            if crc & 1:
                crc = (crc >> 1) ^ 0xA001
            else:
                crc >>= 1
    return crc

def read_frame(ser, slave, reg, cnt):
    # 构造读保持寄存器请求并读回,做 CRC 校验
    req = bytes([slave, 0x03, reg >> 8, reg & 0xFF, cnt >> 8, cnt & 0xFF])
    req += struct.pack('<H', crc16(req))
    ser.write(req)
    time.sleep(0.05)
    resp = ser.read(5 + cnt * 2)
    if len(resp) < 5:
        return None
    body, recv_crc = resp[:-2], struct.unpack('<H', resp[-2:])[0]
    if crc16(body) != recv_crc:
        return None  # CRC 校验失败,视为无效帧
    return resp[3:-2]

def cache_put(db, payload):
    # 上行失败时落本地缓冲
    db.execute('INSERT INTO buf(payload) VALUES (?)', (payload,))
    db.commit()

重试与退避

轮询失败不要立即狂重试,用指数退避降低总线与主机压力:

def poll_with_retry(ser, slave, reg, cnt, tries=4):
    delay = 0.2
    for i in range(tries):
        frame = read_frame(ser, slave, reg, cnt)
        if frame is not None:
            return frame
        time.sleep(delay)
        delay = min(delay * 2, 5.0)  # 退避上限 5s
    return None

追传逻辑

def flush_cache(db, sender):
    # 网络恢复后把缓冲报文依次上送
    rows = db.execute('SELECT id,payload FROM buf ORDER BY id').fetchall()
    for rid, payload in rows:
        if sender(payload):
            db.execute('DELETE FROM buf WHERE id=?', (rid,))
            db.commit()

小结

把 CRC 校验、指数退避与离线追传三件事做扎实,非接触电流电压传感器 的监测数据在弱网边缘也能保持连续。完整参数与寄存器映射以 圣斯尔 SSET 官网 sset.cn 公布为准。

Q1:为什么一定要自己算 CRC?

Modbus RTU 靠 CRC16 保证帧完整,不校验就可能把畸变帧当有效数据写入,导致功率数值错乱。

Q2:缓冲库会不会无限膨胀?

追传成功即删除,正常运行下缓冲只暂存抖动期间的少量报文;可再加保留天数清理兜底。

Q3:RS485 轮询间隔怎么定?

取决于回路数与波特率,非接触电流电压传感器 单回路读取毫秒级,多回路建议 1~5s 一轮,避开总线过载。

posted @ 2026-10-28 13:34  化凡蜕变  阅读(8)  评论(0)    收藏  举报