【物聯網iot】樹莓派連接阿里雲iot平臺定時上傳溫溼度信息

前提

本文操作的前提是已經在樹莓派上完成了DHT11溫溼度傳感器的安裝和數據讀取。

資料可以參考

設備

  • 樹莓派3b+
  • DHT11

接入物聯網平臺

爲什麼是阿里雲物聯網平臺

我選擇使用阿里雲的原因有以下幾點

  • 阿里雲作爲國內比較完善雲服務商,功能比較完善
  • 阿里雲的iot已經完成產品探索階段,開始正式商用,我之前的公司的產品也在一直使用,個人比較熟悉

創建產品

在這裏插入圖片描述
物聯網平臺啓動商用之後,新增的設備品類,裏面的選項很多,如果沒有合適的還可以自定義。

在設備界面,我們就可以連接設備了。
在這裏插入圖片描述

註冊設備

下載SDK

目前的SDK支持下面平臺

  • Linux Ubuntu 16.04 64-bit

  • Windows Widows 7 64 bit

  • Mac High Sierra

支持下面語言

  • C SDK
  • Android SDK
  • NodeJS SDK
  • Java SDK
  • Python SDK
  • iOS SDK

我們的設備是通過python和傳感器交互的,所以這裏我們使用Python SDK 。

Python SDK

安裝Python

這裏使用的python版本最低爲3.6,沒有環境的需要安裝環境,之前我們已經完成了和傳感器的交互,所以這部分直接跳過。

wget https://bootstrap.pypa.io/get-pip.py
sudo python3 get-pip.py
sudo python3 -m pip install --upgrade pip setuptools wheel
sudo apt-get install python3-venv

下面是官方給的安裝方式。

sudo add-apt-repository ppa:deadsnakes/ppa
sudo apt-get update
sudo apt-get install python3.6
wget https://bootstrap.pypa.io/get-pip.py
sudo python3.6 get-pip.py
python3.6 -m pip install --upgrade pip setuptools wheel
sudo apt-get install python3.6-venv

創建和激活 VirtualEnvironments

mkdir work_dir
cd work_dir
python3 -m venv test_env
source test_env/bin/activate

自動安裝linkkit

pip install aliyun-iot-linkkit

下載官方事例

文件位置爲 http://iotx-pop-quickstart-shanghai.oss-cn-shanghai.aliyuncs.com/linkkit/sdk/python/python-linkkit-1.0.0-example.tar.gz?spm=a2c4g.11186623.2.26.756756bbr9vPXW&file=python-linkkit-1.0.0-example.tar.gz

解壓後得到文件

在這裏插入圖片描述

  • dynamic_register.py 動態獲取deviceScret
  • mqtt_connect_TCP.py 通過TCP方式建立MQTT連接
  • mqtt_connect_TLS.py 通過TLS加密方式建立MQTT連接
  • mqtt_sub_pub_on.py 展示mqtt訂閱,發佈,接收消息
  • thing_alink.py 通過透傳協議,展示物模型能力,進行屬性上報,屬性設置,事件上報,服務響應
  • tsl.json thing_alink.py使用的物模型文件
  • thing_custom.py 通過用戶自定義協議進行展示屬性設置及屬性上報
  • model_raw.json thing_custom.py 使用的物模型文件
  • data_transfer.js thing_custom.py 對應雲端的數據解析腳本

註冊設備

通過dynamic_register.py註冊設備,修改文件中的參數,對應參數去產品頁面查找。

lk = linkkit.LinkKit(
    host_name="cn-shanghai",
    product_key="xxxxxxxxxxx",
    device_name="device-name",
    device_secret="",
    product_secret="yyyyyyyyyyyyyyyy")

device_name對應設備信息,需要我們先在平臺註冊設備。

在這裏插入圖片描述
在這裏插入圖片描述
修改之後的文件,執行

(test_env) pi@raspberrypi:~/work_dir/examples $ python3 dynamic_register.py 
2020-03-01 19:18:32,104-1308-1996303056 - linkkit:linkkit:debug - DEBUG - connect_async
2020-03-01 19:18:32,106-1308-1968174176 - linkkit:linkkit:debug - DEBUG - LoopThread thread enter
2020-03-01 19:18:32,107-1308-1968174176 - linkkit:linkkit:debug - DEBUG - enter
2020-03-01 19:18:32,108-1308-1968174176 - linkkit:linkkit:info - INFO - start connect
2020-03-01 19:18:32,111-1308-1968174176 - linkkit:linkkit:debug - DEBUG - current working directory:/home/pi/work_dir/examples
2020-03-01 19:18:37,337-1308-1968174176 - Paho:client:_easy_log - DEBUG - Sending CONNECT (u1, p1, wr0, wq0, wf0, c1, k60) client_id=b'a1Gb6Hee6D8&dht11|securemode=2,signmethod=hmacsha1,ext=1,lan=Python,_v=1.2.0,timestamp=1583061512|'
2020-03-01 19:18:37,448-1308-1968174176 - Paho:client:_easy_log - DEBUG - Received CONNACK (0, 0)
2020-03-01 19:18:37,449-1308-1968174176 - linkkit:linkkit:info - INFO - __on_internal_connect
2020-03-01 19:18:37,450-1308-1968174176 - Paho:client:_easy_log - DEBUG - Sending SUBSCRIBE (d0, m1) [(b'/sys/a1Gb6Hee6D8/dht11/thing/deviceinfo/update_reply', 0)]
2020-03-01 19:18:37,451-1308-1968174176 - linkkit:linkkit:debug - DEBUG - post_message :'on_connect' 
2020-03-01 19:18:37,451-1308-1968174176 - linkkit:linkkit:debug - DEBUG - post_message success
2020-03-01 19:18:37,452-1308-1978537056 - linkkit:linkkit:debug - DEBUG - thread runnable pop cmd:'on_connect'
2020-03-01 19:18:37,453-1308-1978537056 - linkkit:linkkit:info - INFO - __on_internal_connect enter
2020-03-01 19:18:37,454-1308-1978537056 - linkkit:linkkit:debug - DEBUG - session:0, return code:0
on_connect:0,rc:0,userdata:
2020-03-01 19:18:37,502-1308-1968174176 - Paho:client:_easy_log - DEBUG - Received SUBACK
2020-03-01 19:18:37,502-1308-1968174176 - linkkit:linkkit:debug - DEBUG - post_message :'on_subscribe' 
2020-03-01 19:18:37,503-1308-1968174176 - linkkit:linkkit:debug - DEBUG - post_message success
2020-03-01 19:18:37,504-1308-1978537056 - linkkit:linkkit:debug - DEBUG - thread runnable pop cmd:'on_subscribe'
2020-03-01 19:18:37,505-1308-1978537056 - linkkit:linkkit:debug - DEBUG - __on_internal_subscribe mid:1  granted_qos:1
2020-03-01 19:19:37,560-1308-1968174176 - Paho:client:_easy_log - DEBUG - Sending PINGREQ
2020-03-01 19:19:37,597-1308-1968174176 - Paho:client:_easy_log - DEBUG - Received PINGRESP

再次刷新頁面,看到設備已經被激活~

在這裏插入圖片描述

上傳溫溼度信息

獲取物模型文件

在物聯網平臺上,你可以發現設備上面直接有溫溼度數據的展示。

在這裏插入圖片描述
這個設置,是在產品上,有一個概念是物模型。

在這裏插入圖片描述
通過物模型,可以定義設備的字段,點擊物模型TSL下載json文件。

在這裏插入圖片描述
json文件如下

{
	"schema":"https://iotx-tsl.oss-ap-southeast-1.aliyuncs.com/schema.json",
	"profile":{
		"productKey":"a1Gb6Hee6D8"
	},
	"properties":[
		{
			"identifier":"CurrentTemperature",
			"name":"當前溫度",
			"accessMode":"r",
			"required":true,
			"dataType":{
				"type":"float",
				"specs":{
					"min":"-40",
					"max":"120",
					"unit":"°C",
					"unitName":"攝氏度",
					"step":"0.1"
				}
			}
		},
		{
			"identifier":"CurrentHumidity",
			"name":"當前溼度",
			"accessMode":"r",
			"required":true,
			"dataType":{
				"type":"float",
				"specs":{
					"min":"0",
					"max":"100",
					"unit":"%",
					"unitName":"百分比",
					"step":"0.1"
				}
			}
		},
		{
			"identifier":"GeoLocation",
			"name":"地理位置",
			"accessMode":"r",
			"required":true,
			"dataType":{
				"type":"struct",
				"specs":[
					{
						"identifier":"Longitude",
						"name":"經度",
						"dataType":{
							"type":"double",
							"specs":{
								"min":"-180",
								"max":"180",
								"unit":"°",
								"unitName":"度",
								"step":"0.01"
							}
						}
					},
					{
						"identifier":"Latitude",
						"name":"緯度",
						"dataType":{
							"type":"double",
							"specs":{
								"min":"-90",
								"max":"90",
								"unit":"°",
								"unitName":"度",
								"step":"0.01"
							}
						}
					},
					{
						"identifier":"Altitude",
						"name":"海拔",
						"dataType":{
							"type":"double",
							"specs":{
								"min":"0",
								"max":"9999",
								"unit":"m",
								"unitName":"米",
								"step":"0.01"
							}
						}
					},
					{
						"identifier":"CoordinateSystem",
						"name":"座標系統",
						"dataType":{
							"type":"enum",
							"specs":{
								"1":"WGS_84",
								"2":"GCJ_02"
							}
						}
					}
				]
			}
		}
	],
	"events":[
		{
			"identifier":"post",
			"name":"post",
			"type":"info",
			"required":true,
			"desc":"屬性上報",
			"method":"thing.event.property.post",
			"outputData":[
				{
					"identifier":"CurrentTemperature",
					"name":"當前溫度",
					"dataType":{
						"type":"float",
						"specs":{
							"min":"-40",
							"max":"120",
							"unit":"°C",
							"unitName":"攝氏度",
							"step":"0.1"
						}
					}
				},
				{
					"identifier":"CurrentHumidity",
					"name":"當前溼度",
					"dataType":{
						"type":"float",
						"specs":{
							"min":"0",
							"max":"100",
							"unit":"%",
							"unitName":"百分比",
							"step":"0.1"
						}
					}
				},
				{
					"identifier":"GeoLocation",
					"name":"地理位置",
					"dataType":{
						"type":"struct",
						"specs":[
							{
								"identifier":"Longitude",
								"name":"經度",
								"dataType":{
									"type":"double",
									"specs":{
										"min":"-180",
										"max":"180",
										"unit":"°",
										"unitName":"度",
										"step":"0.01"
									}
								}
							},
							{
								"identifier":"Latitude",
								"name":"緯度",
								"dataType":{
									"type":"double",
									"specs":{
										"min":"-90",
										"max":"90",
										"unit":"°",
										"unitName":"度",
										"step":"0.01"
									}
								}
							},
							{
								"identifier":"Altitude",
								"name":"海拔",
								"dataType":{
									"type":"double",
									"specs":{
										"min":"0",
										"max":"9999",
										"unit":"m",
										"unitName":"米",
										"step":"0.01"
									}
								}
							},
							{
								"identifier":"CoordinateSystem",
								"name":"座標系統",
								"dataType":{
									"type":"enum",
									"specs":{
										"1":"WGS_84",
										"2":"GCJ_02"
									}
								}
							}
						]
					}
				}
			]
		}
	],
	"services":[
		{
			"identifier":"set",
			"name":"set",
			"required":true,
			"callType":"async",
			"desc":"屬性設置",
			"method":"thing.service.property.set",
			"inputData":[
				
			],
			"outputData":[
				
			]
		},
		{
			"identifier":"get",
			"name":"get",
			"required":true,
			"callType":"async",
			"desc":"屬性獲取",
			"method":"thing.service.property.get",
			"inputData":[
				"CurrentTemperature",
				"CurrentHumidity",
				"GeoLocation"
			],
			"outputData":[
				{
					"identifier":"CurrentTemperature",
					"name":"當前溫度",
					"dataType":{
						"type":"float",
						"specs":{
							"min":"-40",
							"max":"120",
							"unit":"°C",
							"unitName":"攝氏度",
							"step":"0.1"
						}
					}
				},
				{
					"identifier":"CurrentHumidity",
					"name":"當前溼度",
					"dataType":{
						"type":"float",
						"specs":{
							"min":"0",
							"max":"100",
							"unit":"%",
							"unitName":"百分比",
							"step":"0.1"
						}
					}
				},
				{
					"identifier":"GeoLocation",
					"name":"地理位置",
					"dataType":{
						"type":"struct",
						"specs":[
							{
								"identifier":"Longitude",
								"name":"經度",
								"dataType":{
									"type":"double",
									"specs":{
										"min":"-180",
										"max":"180",
										"unit":"°",
										"unitName":"度",
										"step":"0.01"
									}
								}
							},
							{
								"identifier":"Latitude",
								"name":"緯度",
								"dataType":{
									"type":"double",
									"specs":{
										"min":"-90",
										"max":"90",
										"unit":"°",
										"unitName":"度",
										"step":"0.01"
									}
								}
							},
							{
								"identifier":"Altitude",
								"name":"海拔",
								"dataType":{
									"type":"double",
									"specs":{
										"min":"0",
										"max":"9999",
										"unit":"m",
										"unitName":"米",
										"step":"0.01"
									}
								}
							},
							{
								"identifier":"CoordinateSystem",
								"name":"座標系統",
								"dataType":{
									"type":"enum",
									"specs":{
										"1":"WGS_84",
										"2":"GCJ_02"
									}
								}
							}
						]
					}
				}
			]
		}
	]
}

我們可以根據自己的需求增減裏面的字段,我在這裏刪掉了地理位置。

加載物模型文件

配置模型文件

lk.thing_setup("tsl.json"

上傳數據

event_data = {
    "power": 10,
    "power_style": 1
}
rc, request_id = lk.thing_trigger_event(("power_state", event_data))

我們可以直接參考事例文件中的,thing_alink.py,修改加載的模型文件

合併溫溼度獲取數據邏輯

參考文章 https://blog.csdn.net/diandianxiyu_geek/article/details/104547804

這裏要注意的是,設備連接需要時間,如果直接調用會提示未連接狀態。

最終實現效果的python代碼如下

import time

import Adafruit_GPIO.SPI as SPI
import Adafruit_SSD1306
import Adafruit_DHT

from PIL import Image
from PIL import ImageDraw
from PIL import ImageFont

import subprocess
import sys
from linkkit import linkkit
import threading
import traceback
import inspect
import time
import logging

# Raspberry Pi pin configuration:
RST = None     # on the PiOLED this pin isnt used
# Note the following are only used with SPI:
DC = 23
SPI_PORT = 0
SPI_DEVICE = 0

disp = Adafruit_SSD1306.SSD1306_128_32(rst=RST)

# Initialize library.
disp.begin()

# Clear display.
disp.clear()
disp.display()

# Create blank image for drawing.
# Make sure to create image with mode '1' for 1-bit color.
width = disp.width
height = disp.height
image = Image.new('1', (width, height))

# Get drawing object to draw on image.
draw = ImageDraw.Draw(image)

# Draw a black filled box to clear the image.
draw.rectangle((0,0,width,height), outline=0, fill=0)

# Draw some shapes.
# First define some constants to allow easy resizing of shapes.
padding = -2
top = padding
bottom = height-padding
# Move left to right keeping track of the current x position for drawing shapes.
x = 0


# Load default font.
font = ImageFont.load_default()

# Alternatively load a TTF font.  Make sure the .ttf font file is in the same directory as the python script!
# Some other nice fonts to try: http://www.dafont.com/bitmap.php
# font = ImageFont.truetype('Minecraftia.ttf', 8)


# config log
__log_format = '%(asctime)s-%(process)d-%(thread)d - %(name)s:%(module)s:%(funcName)s - %(levelname)s - %(message)s'
logging.basicConfig(format=__log_format)

class CustomerThing(object):
    def __init__(self):
        self.__linkkit = linkkit.LinkKit(
            host_name="cn-shanghai",
            product_key="*****",
            device_name="*****",
            device_secret="********")
        self.__linkkit.enable_logger(logging.DEBUG)
        self.__linkkit.on_device_dynamic_register = self.on_device_dynamic_register
        self.__linkkit.on_connect = self.on_connect
        self.__linkkit.on_disconnect = self.on_disconnect
        self.__linkkit.on_topic_message = self.on_topic_message
        self.__linkkit.on_subscribe_topic = self.on_subscribe_topic
        self.__linkkit.on_unsubscribe_topic = self.on_unsubscribe_topic
        self.__linkkit.on_publish_topic = self.on_publish_topic
        self.__linkkit.on_thing_enable = self.on_thing_enable
        self.__linkkit.on_thing_disable = self.on_thing_disable
        self.__linkkit.on_thing_event_post = self.on_thing_event_post
        self.__linkkit.on_thing_prop_post = self.on_thing_prop_post
        self.__linkkit.on_thing_prop_changed = self.on_thing_prop_changed
        self.__linkkit.on_thing_call_service = self.on_thing_call_service
        self.__linkkit.on_thing_raw_data_post = self.on_thing_raw_data_post
        self.__linkkit.on_thing_raw_data_arrived = self.on_thing_raw_data_arrived
        self.__linkkit.thing_setup("model.json")
        self.__linkkit.config_device_info("Eth|03ACDEFF0032|Eth|03ACDEFF0031")
        self.__call_service_request_id = 0
        self.__linkkit.connect_async()
        
    def on_device_dynamic_register(self, rc, value, userdata):
        if rc == 0:
            print("dynamic register device success, value:" + value)
        else:
            print("dynamic register device fail, message:" + value)

    def on_connect(self, session_flag, rc, userdata):
        print("on_connect:%d,rc:%d,userdata:" % (session_flag, rc))

    def on_disconnect(self, rc, userdata):
        print("on_disconnect:rc:%d,userdata:" % rc)

    def on_topic_message(self, topic, payload, qos, userdata):
        print("on_topic_message:" + topic + " payload:" + str(payload) + " qos:" + str(qos))
        pass

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

    def on_unsubscribe_topic(self, mid, userdata):
        print("on_unsubscribe_topic mid:%d" % mid)
        pass

    def on_publish_topic(self, mid, userdata):
        print("on_publish_topic mid:%d" % mid)

    def on_thing_prop_changed(self, params, userdata):
        print("on_thing_prop_changed params:" + str(params))

    def on_thing_enable(self, userdata):
        print("on_thing_enable")

    def on_thing_disable(self, userdata):
        print("on_thing_disable")

    def on_thing_event_post(self, event, request_id, code, data, message, userdata):
        print("on_thing_event_post event:%s,request id:%s, code:%d, data:%s, message:%s" %
              (event, request_id, code, str(data), message))
        pass

    def on_thing_prop_post(self, request_id, code, data, message,userdata):
        print("on_thing_prop_post request id:%s, code:%d, data:%s message:%s" %
              (request_id, code, str(data), message))

    def on_thing_raw_data_arrived(self, payload, userdata):
        print("on_thing_raw_data_arrived:%s" % str(payload))

    def on_thing_raw_data_post(self, payload, userdata):
        print("on_thing_raw_data_post: %s" % str(payload))

    def on_thing_call_service(self, identifier, request_id, params, userdata):
        print("on_thing_call_service identifier:%s, request id:%s, params:%s" %
              (identifier, request_id, params))
        self.__call_service_request_id = request_id
        pass

    def update(self,CurrentTemperature,CurrentHumidity):
        prop_data = {
                        "CurrentTemperature": float(CurrentTemperature),
                        "CurrentHumidity": float(CurrentHumidity)
                    }
        self.__linkkit.thing_post_property(prop_data)

if __name__ == "__main__":
    custom_thing = CustomerThing()
    time.sleep(1)
    while True:
        # Draw a black filled box to clear the image.
        draw.rectangle((0,0,width,height), outline=0, fill=0)

        sensor  = 11 # 傳感器型號
        pin = 22 # 針腳
        
        humidity, temperature = Adafruit_DHT.read_retry(sensor, pin)

        if humidity is not None and temperature is not None:
            Temp = '{0:0.1f}'.format(temperature)
            H = '{0:0.1f}'.format(humidity)

        now = time.strftime("%Y-%m-%d-%H:%M:%S", time.localtime())
        draw.text((x, top),       str(now),  font=font, fill=255)
        draw.text((x, top+8),     str("Temp="+Temp), font=font, fill=255)
        draw.text((x, top+16),     str("Humidity="+H+"%"), font=font, fill=255)

        custom_thing.update(Temp,H)

        # Display image.
        disp.image(image)
        disp.display()
        time.sleep(3)


最終完成了設備通過屏幕展示溫溼度信息並同步到物聯網平臺的功能。

參考資料

  • https://help.aliyun.com/document_detail/98292.html?spm=a2c4g.11186623.2.20.36132cf0M0u5Wm
  • https://www.blackmoreops.com/2014/02/21/kali-linux-add-ppa-repository-add-apt-repository/
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章