基于阿里云物联网平台--使用Python模拟设备上报参数

一、前言

最近阿里云物联网平台改版,刚好利用此机会再次了解下MQTT相关内容,本文使用Python语言模拟设备进行数据上报,下一篇文章将介绍使用Python进行云端控制该模拟设备.

二、阿里物联网平台中定义产品

本文默认您已经在物联网平台创建了产品
新建产品
并在产品中定义了如下功能:
在这里插入图片描述
在设备中添加了一台设备:
在这里插入图片描述

三、Python模拟设备上报

A、启动配置

首先导入官方SK及相关需要使用的包

import configparser , time , hmac , hashlib , logging , os , sys , random
from linkkit import linkkit

进行基本的配置


LOG_FORMAT = "%(thread)d %(asctime)s  %(levelname)s %(filename)s %(lineno)d %(message)s"
DATE_FORMAT = "%m/%d/%Y-%H:%M:%S-%p"
logging.basicConfig(format=LOG_FORMAT , datefmt=DATE_FORMAT)
# 读取相关配置:HostName 、ProductKey 、DeviceName 、DeviceSecret 
conf = configparser.ConfigParser()
config_path = '../WIFI_GPRS_Config.cfg'    # 配置文件路径
conf.read(config_path)
HostName = conf.get('SETTINGS' , 'hostname')
ProductKey = conf.get('SETTINGS' , 'productkey')
DeviceName = conf.get('SETTINGS' , 'devicename')
ProductSecret = conf.get('SETTINGS' , 'productsecret')  # 一型一密
if conf.has_option('SETTINGS' , 'devicesecret'):
    DeviceSecret = conf.get('SETTINGS' , 'devicesecret')
else:
    DeviceSecret = ''

上述if else判断语句的目的:如果是一机一密,则直接获取配置文件的DeviceSecret ,否则DeviceSecret 置空,进行一型一密的设备注册获取设备密钥(具体见步骤C)
关于一机一密及一型一密,请见官方解释.。

B、初始化MQTT连接、配置物模型

lk = linkkit.LinkKit(
    host_name=HostName ,
    product_key=ProductKey ,
    device_name=DeviceName ,
    device_secret=DeviceSecret ,  # 一机一密 / 一型一密
    # product_secret = ProductSecret   #一型一密 若使用一型一密,增加此行 
)

lk.enable_logger(level=logging.DEBUG)
lk.thing_setup('../Resources/WIFI_GPRS_Data.json')   #物模型路径

注意:物模型需要使用完整物模型(在产品-功能定义-查看物模型-导出完整物模型)

C. 一型一密下注册设备

若使用一型一密,需要使用此方法(使用一机一密请跳过此步):

def on_device_dynamic_register(rc , value , userdata) :
    if rc == 0 :
        conf.set('SETTINGS' , 'DEVICESECRET' , value) #持久化device secret
        with open(config_path , 'w') as configfile :
            conf.write(configfile)
        logging.info("dynamic register device success, rc:%d, value:%s,userdata:%s" % (rc , value , userdata))
    else :
        logging.warning("dynamic register device fail,rc:%d, value:%s" % (rc , value))
  
#首次启动判断,若无DEVICESECRET,则调用如上注册函数
if not conf.has_option('SETTINGS' , 'DEVICESECRET'):
    lk.on_device_dynamic_register = on_device_dynamic_register         

一型一密 , 首次需要持久化device secret,首次之再次启动模拟设备只能使用持久化的devicesecret,否则会无法连接;
另外一型一密需要提前开启产品的动态注册接口

D、各类回调函数

@连接

def on_connect(session_flag , rc , userdata) :
    logging.info("on_connect:%d,rc:%d,userdata:" % (session_flag , rc))

@断开连接

def on_disconnect(rc , userdata) :
    logging.info("on_disconnect:rc:%d,userdata:" % rc)

@Topic消息

def on_topic_message(topic , payload , qos , userdata) :
    logging.info("on_topic_message:" + topic + " payload:" + str(payload) + " qos:" + str(qos))

@订阅

def on_subscribe_topic(mid , granted_qos , userdata) :
    logging.info("on_subscribe_topic mid:%d, granted_qos:%s" %
                 (mid , str(','.join('%s' % it for it in granted_qos))))

@取消订阅

def on_unsubscribe_topic(mid , userdata) :
    logging.info("on_unsubscribe_topic mid:%d" % mid)

@发布消息

def on_publish_topic(mid , userdata) :
    logging.info("on_publish_topic mid:%d" % mid)

@上报属性

def on_thing_prop_post(request_id , code , data , message , userdata) :
    logging.info("on_thing_prop_post request id:%s, code:%d message:%s, data:%s,userdata:%s" %
                 (request_id , code , message , data , userdata))

@云端设置属性

def on_thing_prop_changed(message , userdata) :
    if "PowerSwitch" in message.keys() :
        prop_data["PowerSwitch"] = message["PowerSwitch"]
    elif "WindSpeed" in message.keys() :
        prop_data["WindSpeed"] = message["WindSpeed"]
    elif "WorkMode" in message.keys() :
        prop_data["WorkMode"] = message["WorkMode"]
    else :
        logging.warning("wrong data:%s" % message)
    lk.thing_post_property(message)  # SDK不会主动上报属性变化,如需要修改后再次上报云端,需要调用thing_post_property()发送
    print('prop_data:' , prop_data)
    print('message:' , message)
    logging.info("on_thing_prop_changed  data:%s " % message)

== 注意SDK不会主动上报属性变化,如需要修改后再次上报云端,需要调用thing_post_property()发送==

@用户可以进行属性上报,事件上报,服务响应,此调用需要在连接前

def on_thing_enable(userdata) :
    logging.info("on_thing_enable")

E、SDK回调函数连接

SDK进行回调连接

lk.on_connect = on_connect
lk.on_disconnect = on_disconnect
lk.on_thing_enable = on_thing_enable
lk.on_subscribe_topic = on_subscribe_topic
lk.on_unsubscribe_topic = on_unsubscribe_topic
lk.on_topic_message = on_topic_message
lk.on_publish_topic = on_publish_topic
lk.on_thing_call_service = on_thing_call_service
lk.on_thing_event_post = on_thing_event_post
lk.on_thing_prop_changed = on_thing_prop_changed
lk.on_thing_prop_post = on_thing_prop_post
lk.connect_async()
lk.start_worker_loop()
time.sleep(2)  # 延时

在启动连接后增加2s的延时,否则会导致还没有连接成功就上报数据,然后报错

F、模拟设备属性

prop_data = {
    "PowerSwitch" : 1 ,
    "WindSpeed" : 1 ,
    "WorkMode" : 1 ,
    }
filter_data = {
    'CartridgesLife' : 5000.1 ,
    }

上述参数在功能定义中为:读写;故与属性上报(只读分开),便于云端设置模拟设备的参数使用
模拟循环上报100次数据(上报只读属性的参数):

counts = 100   # 模拟上报100while counts > 1 :
    pdata = {
        "MotorSpeed" : random.randint(8000 , 12000) ,
        "PM25" : random.randint(1 , 255) ,
        "CurrentPower" : round(random.uniform(2000 , 4000) , 2) ,
        "CurretWindSpeed" : round(random.uniform(0 , 450) , 1) ,
        "TVOC" : round(random.uniform(0 , 3) , 2) ,
        "CurrentTemperature" : round(random.uniform(-20 , 55) , 2) ,
        'Humidity' : random.randint(0 , 100) ,
        'GeoLocation' : {
                'Longitude' : round(random.uniform(-180 , 180) , 6) ,
                'Latitude' : round(random.uniform(-90 , 90) , 6) ,
                'Altitude' : random.randint(0 , 9999) ,
                'CoordinateSystem' : random.randint(1 , 2) ,
            }
        }
    rc , request_id = lk.thing_post_property({**prop_data , **pdata})
    if rc == 0 :
        logging.info("thing_post_property success:%r,mid:%r,\npost_data:%s" % (rc , request_id , prop_data))
    else :
        logging.warning("thing_post_property failed:%d" % rc)
    events = ("Error" , {"ErrorCode" : random.randint(0 , 5)})
    rc1 , request_id1 = lk.thing_trigger_event(events)
    if rc1 == 0 :
        logging.info("thing_trigger_event success:%r,mid:%r,\npost_data:%s" % (rc1 , request_id1 , events))
    else :
        logging.warning("thing_trigger_event failed:%d" % rc)
    time.sleep(60)
    counts -= 1

G、 通讯DEMO

为方便演示通讯及接收云端数据,将循环按步骤区分见下:

while True :

    try :
        print('''Please input operation code:
            Code example: 
            '1':disconnect,
            '2':connect,
            '3':subcribe topic,
            '4':unsubcribe topic,
            '5':publish topic,
            '6':thing_post
            ''')
        msg = input()
    except KeyboardInterrupt :
        sys.exit()
    else :
        if msg == '1' :
            lk.disconnect()
        elif msg == '2' :
            lk.connect_async()
        elif msg == '3' :
            rc , mid = lk.subscribe_topic(lk.to_full_topic("user/get"))

            if rc == 0 :
                logging.info("subcribe topic success:%r,mid:%r" % (rc , mid))
            else :
                logging.warning("subcribe topic failed:%d" % rc)
        elif msg == '4' :
            rc , mid = lk.unsubscribe_topic(lk.to_full_topic("user/get"))
            if rc == 0 :
                logging.info("unsubcribe topic success:%r,mid:%r" % (rc , mid))
            else :
                logging.warning("unsubcribe topic failed:%d" % rc)

        elif msg == '5' :
            rc , mid = lk.publish_topic(lk.to_full_topic("user/update") , '"WorkingStatus":3')
            if rc == 0 :
                logging.info("publish topic success:%r,mid:%r" % (rc , mid))
            else :
                logging.warning("publish topic failed:%d" % rc)
        elif msg == '6' :
            # 此处内容见步骤E
            pass
            # 此处内容见步骤E
        else :
            logging.warning("Error Code!!!!!!!!!!!!! ")
            continue

第六步循环上报属性见步骤E

四、上报结果

在这里插入图片描述

在这里插入图片描述
空间数据可视化:
在这里插入图片描述
结语:
本文只是简单介绍设备的模拟使用;
下一篇文章介绍使用Python云端控制模拟设备进行参数设置

参考
[1]: https://help.aliyun.com/document_detail/42648.html?spm=a2c4g.11174283.6.641.3a8b1668A6pEm6
[2]: https://help.aliyun.com/document_detail/108675.html?spm=a2c4g.11174283.6.556.2d835523OO5lpo

评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值