
现场:跨城多机房/库房,每个机房部署 PoE 网口温湿度变送器(RJ45,Modbus TCP),机房内已有内网和 PoE 交换机。目标:统一接入腾讯云 IoT Explorer,集中监控、告警、可视化。
关键决策:传感器不直接暴露公网,边缘网关收敛协议后上云。
PoE交换机 ── Modbus TCP ──> 边缘网关(机房内)
│
MQTT over TLS (8883)
▼
腾讯云 IoT Explorer
│ 规则引擎
▼
云函数/CKafka → CTSDB/InfluxDB → 云图/Grafana为什么不直接让设备连云:
产品信息:
$thing/up/property 腾讯云数据模板(TSL)描述在控制台 GUI 填写,导出后如下(精简):
{
"version": "1.0",
"properties": [
{ "id": "temperature", "name": "温度", "define": { "type": "float", "min": "-40", "max": "85", "step": "0.1", "unit": "°C" } },
{ "id": "humidity", "name": "湿度", "define": { "type": "float", "min": "0", "max": "100", "step": "0.1", "unit": "%RH" } },
{ "id": "sensor_status", "name": "传感器状态", "define": { "type": "enum", "mapping": { "0": "正常", "1": "故障", "2": "离线", "3": "越限" } } },
{ "id": "power_voltage", "name": "供电电压", "define": { "type": "float", "min": "9", "max": "36", "step": "0.1", "unit": "V" } },
{ "id": "room_id", "name": "机房编号", "define": { "type": "string", "len": 64 } },
{ "id": "last_collect_ts", "name": "采集时间戳", "define": { "type": "int64", "unit": "ms" } }
],
"events": [
{ "id": "temp_high_alarm", "name": "温度高告警", "type": "alert", "params": [
{ "id": "value", "define": { "type": "float" } },
{ "id": "threshold", "define": { "type": "float" } }
]},
{ "id": "humidity_high_alarm", "name": "湿度高告警", "type": "alert", "params": [
{ "id": "value", "define": { "type": "float" } }
]},
{ "id": "sensor_offline", "name": "传感器离线", "type": "alert", "params": [
{ "id": "sensor_id", "define": { "type": "string" } }
]}
],
"actions": []
}注意点:
sensors 用自定义透传。数据模板不支持嵌套数组时,两种办法: temp_s1,humi_s1,temp_s2,humi_s2 ${ProductID}.iotcloud.tencentdevices.com,端口 8883 ${ProductID}${DeviceName} ${ProductID}${DeviceName};${SignMethod} tencent-iot-explorer-sdk 或直接 paho + 手动签名。 手动签名要点:
deviceName=${DeviceName}&productId=${ProductID}&expiry=${expiry}&nonce=${nonce} 省事做法:用官方 Python SDK qcloud_iot_explorer(或 C-SDK 封装),避免手误。
import time, json, logging
from pymodbus.client import ModbusTcpClient
import paho.mqtt.client as mqtt
from hashlib import sha256
import hmac, base64, os
PRODUCT_ID = os.environ["PRODUCT_ID"]
DEVICE_NAME = os.environ["DEVICE_NAME"]
DEVICE_SECRET = os.environ["DEVICE_SECRET"]
BROKER = f"{PRODUCT_ID}.iotcloud.tencentdevices.com"
PORT = 8883
CA_FILE = "root.ca.pem" # 腾讯云根证书或Amazon CA1可用,按文档
SENSORS = [{"id": "s1", "ip": "192.168.10.10", "port": 502, "unit": 1}]
ROOM_ID = os.environ.get("ROOM_ID", "room-default")
REPORT_INT = 15.0
def sign_password():
# 简化:使用腾讯云推荐的密钥认证方式,paho_tls_set + username/password
# 实际部署建议用SDK;此处给手动构造示例
conn_id = "edgegw"
expiry = 0
nonce = str(int(time.time()))
sign_content = f"deviceName={DEVICE_NAME}&productId={PRODUCT_ID}&expiry={expiry}&nonce={nonce}"
raw = hmac.new(DEVICE_SECRET.encode(), sign_content.encode(), sha256).digest()
sig = base64.b64encode(raw).decode()
username = f"{DEVICE_NAME};hmacsha256"
password = f"{sig}"
return username, password
client = mqtt.Client(client_id=f"{PRODUCT_ID}{DEVICE_NAME}", transport="tcp")
client.tls_set(ca_certs=CA_FILE, cert_reqs=2)
uname, pwd = sign_password()
client.username_pw_set(uname, pwd)
client.connect_async(BROKER, PORT, keepalive=60)
# Explorer 属性上报主题
UP_TOPIC = f"$thing/up/property/{PRODUCT_ID}/{DEVICE_NAME}"
DOWN_TOPIC = f"$thing/down/property/{PRODUCT_ID}/{DEVICE_NAME}"
def on_connect(c, u, f, rc):
logging.info("connected %s", rc)
c.subscribe(DOWN_TOPIC)
# 上线后上报一次
report()
def read_sensor(s):
c = ModbusTcpClient(host=s["ip"], port=s["port"], timeout=1.0)
try:
if not c.connect():
return None
r = c.read_holding_registers(0, 3, unit=s["unit"])
if r.is_error():
return None
temp = r.registers[0]/10.0
humi = r.registers[1]/10.0
status_word = r.registers[2]
return temp, humi, status_word
finally:
c.close()
def build_payload():
temps, humis, st = [], [], 0
sensor_status = 0
last_ts = int(time.time()*1000)
detail = []
for s in SENSORS:
d = read_sensor(s)
if d is None:
sensor_status = 2 # 离线
detail.append({"sensor_id": s["id"], "status": "offline"})
continue
t, h, sw = d
temps.append(t); humis.append(h)
if sw & 0x1: sensor_status = 1
detail.append({"sensor_id": s["id"], "temperature": t, "humidity": h, "sw": sw})
payload = {
"method": "report",
"clientToken": f"{DEVICE_NAME}-{int(time.time())}",
"params": {
"temperature": round(sum(temps)/len(temps), 1) if temps else 0.0,
"humidity": round(sum(humis)/len(humis), 1) if humis else 0.0,
"sensor_status": sensor_status,
"room_id": ROOM_ID,
"last_collect_ts": last_ts
}
}
# 扩展明细走自定义topic或事件,此处附在property下行调试用event上报
return payload, detail
def report():
payload, detail = build_payload()
client.publish(UP_TOPIC, json.dumps(payload), qos=1)
# 可选:上报事件
evt = {"method": "event_post", "clientToken": payload["clientToken"],
"params": {"sensors_detail": detail}}
client.publish(f"$thing/up/event/{PRODUCT_ID}/{DEVICE_NAME}", json.dumps(evt), qos=1)
def loop():
while True:
try:
if client.is_connected():
report()
except Exception as e:
logging.warning("report err %s", e)
client.loop_start()
time.sleep(REPORT_INT)
if __name__ == "__main__":
client.on_connect = on_connect
client.loop_start()
while True:
time.sleep(1)注意:
CallbackAPIVersion 需适配;上面为示意,生产用 SDK 或更健壮封装 read_holding_registers(0,3) 对应协议地址 40001 起;pymodbus 默认地址=寄存器号-1,即 0 开始。以厂家点表核对 控制台/云API改阈值、远程复位继电器:
$thing/down/property 收到期望属性,网关解析后写 Modbus 保持寄存器(40101/40102)或触发 DO $thing/up/property 带 reported,完成闭环 Explorer 控制台 → 规则引擎 → 创建规则:
示例 SCF(Python)伪代码:
def main(event, context):
for msg in parse_kafka_or_direct(event):
dev = msg["deviceName"]
props = msg["payload"]["params"]
point = {
"measurement": "env",
"tags": {"room": props.get("room_id"), "dev": dev},
"fields": {
"temp": props["temperature"],
"humi": props["humidity"],
"status": props["sensor_status"]
},
"time": props.get("last_collect_ts", int(time.time()*1000))
}
write_influx(point) # influxdb-client v2
return "ok"$thing/up/property/{ProductID}/{DeviceName},注意 ProductID 是大写字母+数字,别带产品名 tls_set 需传 CA bundle,否则 ssl handshake fail 接入本质是把工业以太网传感器经边缘网关转成 IoT Explorer 数据模板属性流。物模型别过度设计,聚合上报 + 事件补明细;边缘侧保活、缓存、本地联动,云侧做汇聚、存储、告警、可视化。批量部署时,配置注入和运维通道比单点联通更重要。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。