前提条件:
- 请确保服务的前、后端服务已经正常启动且能正常登录平台。
- 已经完成网络组件、数据解析协议、设备接入网关、产品和设备的创建。
- 已经能够使用python脚本连接到物联网平台。
接下来使用python语言编写代码与平台进行功能测试,主要测试数据的上报即模拟设备定时周期发送数据至物联网平台,平台展示接收到的数据。
(1)定义产品的物模型
在设备列表页面选择某产品卡片单击进入产品配置页面,选择“物模型->属性定义”,定义如下两个属性值:
- temperature 温度值 float类型 单位摄氏度 属性来源:设备 可读、写、上报
- humidity 湿度值 float类型 单位百分比 属性来源:设备 可读、写、上报
temperature温度的配置截图如下图所示:
humidity 湿度的配置截图如下图所示:
产品物模型定义完成后,属于该产品类型的设备会自动同步物模型的定义,如下图所示:
(2)编写python脚本
在Thonny软件中新建文件,文件名为:testReport.py,代码如下
# -*- coding:utf-8 -*-
"""
无锡匠客物联网科技有限公司
技术支持:@NanGe(微信号:nange2012014158 | 公众号:南哥物联网笔记)
脚本名称: 数据上报至物联网平台
脚本执行现象: 运行脚本后,实现定时周期采集数据上报至物联网平台
"""
# 导入软件包
# 线程相关
import threading
# 日志相关
import logging
# 定时器相关
import time
# MQTT客户端服务相关
from paho.mqtt.client import Client
import paho.mqtt.client as mqtt
# json数据格式化相关
import json
# MD5加密相关
import hashlib
# 测试生成随机数
import random
# 全局设置log信息的展示规范
FORMAT = '%(asctime)s - %(name)s - %(levelname)s - %(message)s'
logging.basicConfig(format=FORMAT, level=logging.INFO)
logger = logging.getLogger()
# 设置一个标志位
# 默认设备上电只开启一个线程 设备断网重连后不开启新的线程
flagThread=True
# 设置一个标志位 判断是否断网 决定是否上传数据
flagUploadData = False
# 数据上传状态指示计数
count = 1
# 全局配置数据采集周期 默认10s
deviceGetTime = 10
# 需要根据物联网平台中的相关设置信息进行修改
# MQTT服务器IP
mqttHost = "127.0.0.1"
# MQTT服务器端口
mqttPort = 1884
# 保活时间 单位s
keepalive = 60
# 全局配置产品鉴权Id
secureId = "admin"
# 全局配置产品鉴权Key
secureKey = "admin"
# 全局配置产品ID
productID = "1688364982076866560"
# 全局配置设备ID
deviceID = "1688368276962705408"
#获取传感数据信息并发布topic 数据类型json
def publish_readSensorData(client, productID, deviceID):
while True:
# 周期数据采集
global deviceGetTime
if flagUploadData == True:
#设置周期采集数据 单位s
time.sleep(deviceGetTime)
#组织json格式的数据
message = {
"deviceId": deviceID,
"properties": {
"temperature": random.randint(0,9),
"humidity": random.randint(0,9)
}
}
array = json.dumps(message)
logger.info('当前数据采集正常,正常上传数据,%s' % array)
# 发布数据主题 按照平台topic规范属性上报主题
client.publish(topic= '/'+ productID + '/' + deviceID +'/properties/report',payload=array,qos=0)
else:
time.sleep(2)
logger.info('当前网络异常,停止上传数据')
class MyMQTTClass(Client):
def __init__(self,productID,deviceID):
# MQTT初始化
super(MyMQTTClass, self).__init__(deviceID, protocol=mqtt.MQTTv311, clean_session=False)
# 连接成功回调函数
def on_connect(self, client, obj, flags, rc):
# 申明全局变量
global flagUploadData,flagThread,productID,deviceID,deviceType
# 初始化flagUploadData状态为True
flagUploadData = True
logger.info('设备连接服务器成功!')
#logger.info("on connect, rc: %s, flags: %s" % (rc, flags))
# 启动线程 开启发布主题
if flagThread == True:
t1 = threading.Thread(target=publish_readSensorData, args=(client, productID, deviceID))
t1.start()
self.worker1 = t1
flagThread=False
# 消息推送回调函数 订阅的主题逻辑处理
def on_message(self, client, obj, msg):
logger.debug("on message, topic: %s, qos: %s, data: %s" % (msg.topic, msg.qos, msg.payload))
def on_publish(self, client, obj, mid):
global count
logger.debug("publish -> ,mid: %s" % mid)
logger.info("发送-----OK!%d",count)
count = count + 1
def on_subscribe(self, client, obj, mid, granted_qos):
logger.debug("subscribed <- ,mid: %s, qos: %s" %(mid, granted_qos))
def on_log(self, mqttc, obj, level, string):
logger.debug("mqtt debug: %s, %s" % (level, string))
def on_disconnect(self, client, userdata, rc):
global flagUploadData
flagUploadData = False
logger.info('设备连接服务器失败!')
#logger.info("disconnect: %s" % rc)
while rc == 1:
try:
client.reconnect()
#logger.info("reconnect success")
rc = 0
logger.info('设备恢复连接服务器成功!')
except Exception as e:
#logger.error("reconnect error, %s retry after 3s" % e)
logger.info('连接失败---%s--on_disconnect_内,3S后重新连接----' % e)
time.sleep(3)
def run(self, mqttHost, mqttPort, keepalive, username, password):
# 上电后程序入口
# 设置产品ID和密码 上电自检 检测设备是否联到网络 若开机未连接到网络 3s重连
flag = 1
while flag == 1:
try:
self.username_pw_set(username, password)
self.connect(mqttHost, mqttPort, keepalive)
flag = 0
# 此处可以添加系统运行正常指示灯
logger.info('连接成功-----run')
except Exception as e:
#logger.info("reconnect error, retry after 3s")
# 只需要在此处加 连接失败的指示灯即可 常灭
logger.info('连接失败-----run')
time.sleep(3)
while True:
rc = self.loop()
#logger.info("打印当前的rc值是 %s" % rc)
if rc != 0:
logger.info("重新连接网络成功-------run")
time.sleep(1)
rc = self.loop()
logger.info("recovery from error loop, %s" % rc)
def main():
#申明全局变量
global mqttHost, mqttPort, keepalive, productID, deviceID, secureId, secureKey
#生成username和password的算法如下:
#1、获取当前的时间戳(毫秒级)
#2、按照如下拼接用户名:平台提供的产品secureId+"|"+当前获取时间戳(毫秒级)
#3、按照如下拼接用户名的密码:md5(用户名+"|"+平台提供的产品secureKey),使用md5加密生成摘要
username = secureId + "|" + str(int(round(time.time() * 1000)))
src = username + "|" + secureKey
m = hashlib.md5()
m.update(src.encode('utf-8'))
password = m.hexdigest()
logger.info("获取到当前username:%s、password:%s" % (username, password))
client = MyMQTTClass(productID,deviceID)
client.run(mqttHost, mqttPort, keepalive, username, password)
if __name__ == "__main__":
main()
其中,参数如下所示:
- clientid:设备的ID
- username和password:python直接实现算法获取
- secureId:admin
- secureKey:admin
(3)运行脚本
在Thonny软件中单击运行按钮,执行脚本,运行成功如下图所示。
执行python代码,运行成功如下图所示。程序每10s上报一次数据到物联网平台。
连接成功后,查看平台设备情况,此时设备已经显示在线状态。
也可以单击设备卡片进入设备详情页面查看,在运行状态中可查看到上报的数据。
单击温度值卡片中的“详情”按钮进入温度数据统计页面
单击湿度值卡片中的“详情”按钮进入湿度数据统计页面