用 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 一轮,避开总线过载。
浙公网安备 33010602011771号