云中树莓派(3):通过 AWS IoT 控制树莓派上的 Led
云中树莓派(2):将传感器数据上传到AWS IoT 并利用Kibana进行展示
云中树莓派(3):通过 AWS IoT 控制树莓派上的Led
1. Led 连接与测试
在某宝上买了几样配件,包括T型GPIO扩展板、40P排线、亚克力外壳、400孔面包板、若干杜邦线。现在我的树莓派长得这个样子了:
不由得感谢神奇的某宝,这些东西每一样都不超过三四块钱。
1.1 接线
以下几个简单步骤就完成了接线:
- 将排线一头插在树莓派的40个pin脚上,将另一头插在扩展板上。要注意方向,多试几次。还要注意在树莓派关机时候再插入。
- 把扩展板插在面包板上。
- 把Led 的长脚(正极)插在面包板第6行的任何一个孔内(对应GPIO18),将其短脚(负极或接地)插在第7行的任何一个孔内(对应GND)。
简单说下面包板。刚拿到手时还有点不知所措,稍微研究一下后就简单了。面包板为长方形,长边的两边是为了接电源的,每个长边都是联通的;中间区域内,每行内是联通的。
1.2 简单测试
下面的 python 能让led 灯每两秒钟亮一次:
import RPi.GPIO as GPIO
import time PIN_NO=18
GPIO.setmode(GPIO.BCM)
GPIO.setup(PIN_NO, GPIO.OUT)
loopCount = 0
for x in xrange(500):
print("Loop " + str(loopCount))
GPIO.output(PIN_NO, GPIO.HIGH)
time.sleep(2)
GPIO.output(PIN_NO, GPIO.LOW)
time.sleep(2)
loopCount += 1 GPIO.cleanup()
也就是通过控制GPIO18的电压为高还是低来控制Led 灯是亮还是灭。
2. AWS IoT Device Shadow
-
- UPDATE:如果设备的影子不存在,则创建一个该影子;如果存在,则使用请求中提供的数据更新设备的影子的内容。存储数据时使用时间戳信息,以指明最新更新时间。向所有订阅者发送消息,告知 desired 状态与 reported 状态之间的差异 (增量)。接收到消息的事物或应用程序可以根据 desired 状态和 reported 状态之间的差异执行操作。例如,设备可将其状态更新为预期状态,或者应用程序可以更新其 UI,以反映设备状态的更改。
- GET:检索设备的影子中存储的最新状态 (例如,在设备启动期间,检索配置和最新操作状态)。此操作将返回整个 JSON 文档,其中包括元数据。
- DELETE:删除设备的影子,包括其所有内容。这将从数据存储中删除 JSON 文档。您无法还原已删除的设备的影子,但可以创建具有相同名称的新影子。
也就是说,要通过 AWS IoT 来操作设备,需要通过设备影子进行。下图是控制Led 灯泡的示意图。外部应用和设备之间的交互通过设备影子进行。
Device Shadow 使用系统预留的 MQTT 主题来做应用程序和设备之间的通信:MQTT 主题 用途 $aws/things/myLightBulb/shadow/update/accepted
当设备的影子更新成功时,Device Shadow 服务将向此主题发送消息 $aws/things/myLightBulb/shadow/update/rejected
当设备的影子更新遭拒时,Device Shadow 服务将向此主题发送消息 $aws/things/myLightBulb/shadow/update/delta
当检测到设备的影子的“reported”部分与“desired”部分之间存在差异时,Device Shadow 服务将向此主题发送消息。 $aws/things/myLightBulb/shadow/get/accepted
当获取设备的影子的请求获批时,Device Shadow 服务将向此主题发送消息。
$aws/things/myLightBulb/shadow/get/rejected
当获取设备的影子的请求遭拒时,Device Shadow 服务将向此主题发送消息。
$aws/things/myLightBulb/shadow/delete/accepted
当设备的影子被删除时,Device Shadow 服务将向此主题发送消息。
$aws/things/myLightBulb/shadow/delete/rejected
当删除设备的影子的请求遭拒时,Device Shadow 服务将向此主题发送消息。
$aws/things/myLightBulb/shadow/update/documents
每次设备的影子更新成功执行时,Device Shadow 服务都会向此主题发布状态文档。
下图显示了控制流程:
- 连接着的 Led 灯发送封装在MQTT消息中的 reported 状态 『off』 到 AWS IoT
- AWS IoT 通过 led 灯使用的证书来确定它所属的虚拟事物
- AWS IoT 将 reported 状态保存在设备影子 JSON 文件中
- 一条 AWS IoT rule 正监控着 led 的 off 状态,它发送一个消息到某个 SNS topic
- 某 application 收到 SNS 消息。用户将 led 期望状态(desired state)设置为 on
- AWS IoT 收到该消息,它更新设备影子中的 desired state,同时发送包含期望状态的 message 到某些topic。此时,reported 和 desired 状态是 『Out of Sync』的
- led 收到 delta 消息,开始根据其中的 desired status 来设置其实际状态
- led 将其状态设置为 on,向 MQTT topic 发送新的 reported 状态
- AWS IoT 更新设备影子,现在该设备的 reported 和 desired 状态一致了
3. Python 代码
代码一共就两个文件。文件 ledController.py 充当Led 灯的控制器,它定期向Led 发出『开』或『关』的指令,并定期获取其状态;文件 ledSwitch.py 充当 Led 等的操纵器,它通过调用树莓派的 GPIO 接口来设置和获取 led 灯的状态,以及将其状态上报给 IoT 服务。
AWS IoT 提供了 Device Shadow python SDK,因此不需要直接操作各种 MQTT 主题,而可以使用 get,update 和 delete 这种API。其地址在 https://github.com/aws/aws-iot-device-sdk-python/tree/master/AWSIoTPythonSDK/core/shadow。
3.1 ledController.py
from AWSIoTPythonSDK.MQTTLib import AWSIoTMQTTShadowClient
import logging
import time
import json
import threading # Led shadow JSON Schema
#
#
# Name: Led
# {
# "state: {
# "desired": {
# "light": <on|off>
# }
# }
#} deviceShadowHandler = None def getDeviceStatus():
while True:
print("Getting device status...\n")
deviceShadowHandler.shadowGet(customShadowCallback_get, 50)
time.sleep(60) def customShadowCallback_get(payload, responseStatus, token):
if responseStatus == "timeout":
print("Get request with token " + token + " time out!")
if responseStatus == "accepted":
print("========== Printing Device Current Status =========")
print(payload)
payloadDict = json.loads(payload)
#{"state":{"desired":{"light":0},"reported":{"light":100}
try:
desired = payloadDict["state"]["desired"]["light"]
desiredTime = payloadDict["metadata"]["desired"]["light"]["timestamp"]
except Exception:
print("Failed to get desired state and timestamp.")
else:
print("Desired status: " + str(desired) + " @ " + time.ctime(int(desiredTime))) try:
reported = payloadDict["state"]["reported"]["light"]
#"metadata":{"desired":{"light":{"timestamp":1533893848}},"reported":{"light":{"timestamp":1533893853}}}
reportedTime = payloadDict["metadata"]["reported"]["light"]["timestamp"]
except Exception:
print("Failed to get reported time or timestamp")
else:
print("Reported status: " + str(reported) + " @ " + time.ctime(int(reportedTime))) print("=======================================\n\n")
if responseStatus == "rejected":
print("Get request with token " + token + " rejected!") def customShadowCallback_upate(payload, responseStatus, token):
# payload is a JSON string which will be parsed by jason lib
if responseStatus == "timeout":
print("Update request with " + token + " time out!")
if responseStatus == "accepted":
playloadDict = json.loads(payload)
print("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~")
print("Update request with token: " + token + " accepted!")
print("light: " + str(playloadDict["state"]["desired"]["light"]))
print("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n")
if responseStatus == "rejected":
print("Update request " + token + " rejected!") def customShadowCallback_delete(payload, responseStatus, token):
if responseStatus == "timeout":
print("Delete request " + token + " time out!")
if responseStatus == "accepted":
print("Delete request with token " + token + " accepted!")
if responseStatus == "rejected":
print("Delete request with token " + token + " rejected!") # Cofigure logging
logger = logging.getLogger("AWSIoTPythonSDK.core")
logger.setLevel(logging.ERROR)
streamHandler = logging.StreamHandler()
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
streamHandler.setFormatter(formatter)
logger.addHandler(streamHandler) # AWS IoT Core endpoint. Need change some values to yours.
awsiotHost = "**********.iot.*********.amazonaws.com"
awsiotPort = 8883;
# AWS IoT Root Certificate. Needn't change.
rootCAPath = "/home/pi/awsiot/VeriSign-Class3-Public-Primary-Certification-Authority-G5.pem"
# Device private key. Need change to yours.
privateKeyPath = "/home/pi/awsiot/aec2731afd-private.pem.key"
# Device certificate. Need change to yours.
certificatePath = "/home/pi/awsiot/aec2731afd-certificate.pem.crt"
myAWSIoTMQTTShadowClient = None;
myAWSIoTMQTTShadowClient = AWSIoTMQTTShadowClient("RaspberryLedController")
myAWSIoTMQTTShadowClient.configureEndpoint(awsiotHost, awsiotPort)
myAWSIoTMQTTShadowClient.configureCredentials(rootCAPath, privateKeyPath, certificatePath) myAWSIoTMQTTShadowClient.configureAutoReconnectBackoffTime(1, 32, 20)
myAWSIoTMQTTShadowClient.configureConnectDisconnectTimeout(60) # 10sec
myAWSIoTMQTTShadowClient.configureMQTTOperationTimeout(50) #5sec #connect to AWS IoT
myAWSIoTMQTTShadowClient.connect() #create a devcie Shadow with persistent subscription
thingName = "homepi"
deviceShadowHandler = myAWSIoTMQTTShadowClient.createShadowHandlerWithName(thingName, True) #Delete shadow JSON doc
deviceShadowHandler.shadowDelete(customShadowCallback_delete, 50) #start a thread to get device status every 5 seconds
statusLoopThread = threading.Thread(target=getDeviceStatus)
statusLoopThread.start() #update shadow in a loop
loopCount = 0
while True:
desiredState = "off"
if (loopCount % 2 == 0):
desiredState = "on"
print("To change Led desired status to \"" + desiredState + "\" ...\n")
jsonPayload = '{"state":{"desired":{"light":"' + desiredState + '"}}}'
print("payload is: " + jsonPayload + "\n")
deviceShadowHandler.shadowUpdate(jsonPayload, customShadowCallback_upate, 60)
loopCount += 1
time.sleep(60)
3.2 ledSwitch.py
import RPi.GPIO as GPIO
from AWSIoTPythonSDK.MQTTLib import AWSIoTMQTTShadowClient
import logging
import time
import json # Led shadow JSON Schema
#
#
# Name: Led
# {
# "state: {
# "desired": {
# "light": <on|off>
# }
# }
#} LED_PIN_NUM = 18 # GPIO Number of Led long pin. Change to yours.
deviceShadowHandler = None
#initialize GOPI
GPIO.setmode(GPIO.BCM)
GPIO.setup(LED_PIN_NUM, GPIO.OUT) def customShadowCallback_Delta(payload, responseStatus, token):
# payload is a JSON string which will be parsed by jason lib
print(responseStatus)
print(payload)
payloadDict = json.loads(payload)
print("++++++++ Get DELTA data +++++++++++")
desiredStatus = str(payloadDict["state"]["light"])
print("desired status: " + desiredStatus)
print("version: " + str(payloadDict["version"])) #get device current status
currentStatus = getDeviceStatus()
print("Device current status is " + currentStatus)
#udpate device current status
if (currentStatus != desiredStatus):
# update device status as desired
updateDeviceStatus(desiredStatus)
# send current status to IoT service
sendCurrentState2AWSIoT()
print("+++++++++++++++++++++++++++\n") def updateDeviceStatus(status):
print("=============================")
print("Set device status to " + status)
if (status == "on"):
turnLedOn(LED_PIN_NUM)
else:
turnLedOff(LED_PIN_NUM)
print("=============================\n") def getDeviceStatus():
return getLedStatus(LED_PIN_NUM) def turnLedOn(gpionum):
GPIO.output(gpionum, GPIO.HIGH) def turnLedOff(gpionum):
GPIO.output(gpionum, GPIO.LOW) def getLedStatus(gpionum):
outputFlag = GPIO.input(gpionum)
print("outputFlag is " + str(outputFlag))
if outputFlag:
return "on"
else:
return "off" def sendCurrentState2AWSIoT():
#check current status of device
currentStatus = getDeviceStatus()
print("Device current status is " + currentStatus)
print("Sending reported status to MQTT...")
jsonPayload = '{"state":{"reported":{"light":"' + currentStatus + '"}}}'
print("Payload is: " + jsonPayload + "\n")
deviceShadowHandler.shadowUpdate(jsonPayload, customShadowCallback_upate, 50) def customShadowCallback_upate(payload, responseStatus, token):
# payload is a JSON string which will be parsed by jason lib
if responseStatus == "timeout":
print("Update request with " + token + " time out!")
if responseStatus == "accepted":
playloadDict = json.loads(payload)
print("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~")
print(payload)
print("Update request with token: " + token + " accepted!")
print("light: " + str(playloadDict["state"]["reported"]["light"]))
print("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n")
if responseStatus == "rejected":
print("Update request " + token + " rejected!") def customShadowCallback_Get(payload, responseStatus, token):
print("responseStatus: " + responseStatus)
print("payload: " + payload)
payloadDict = json.loads(payload)
# {"state":{"desired":{"light":37},"delta":{"light":37}},"metadata":{"desired":{"light":{"timestamp":1533888405}}},"version":54
stateStr = ""
try:
stateStr = stateStr + "Desired: " + str(payloadDict["state"]["desired"]["light"]) + ", "
except Exception:
print("No desired state") try:
stateStr = stateStr + "Delta: " + str(payloadDict["state"]["delta"]["light"]) + ", "
except Exception:
print("No delta state") try:
stateStr = stateStr + "Reported: " + str(payloadDict["state"]["reported"]["light"]) + ", "
except Exception:
print("No reported state") print(stateStr + ", Version: " + str(payloadDict["version"])) def printDeviceStatus():
print("=========================")
status = getDeviceStatus()
print(" Current status: " + str(status))
print("=========================\n\n") # Cofigure logging
logger = logging.getLogger("AWSIoTPythonSDK.core")
logger.setLevel(logging.DEBUG)
streamHandler = logging.StreamHandler()
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
streamHandler.setFormatter(formatter)
logger.addHandler(streamHandler) awsiotHost = "***********.iot.********.amazonaws.com"
awsiotPort = 8883;
rootCAPath = "/home/pi/awsiot/VeriSign-Class3-Public-Primary-Certification-Authority-G5.pem"
privateKeyPath = "/home/pi/awsiot/aec2731afd-private.pem.key"
certificatePath = "/home/pi/awsiot/aec2731afd-certificate.pem.crt"
myAWSIoTMQTTShadowClient = None;
myAWSIoTMQTTShadowClient = AWSIoTMQTTShadowClient("RaspberryLedSwitch")
myAWSIoTMQTTShadowClient.configureEndpoint(awsiotHost, awsiotPort)
myAWSIoTMQTTShadowClient.configureCredentials(rootCAPath, privateKeyPath, certificatePath) myAWSIoTMQTTShadowClient.configureAutoReconnectBackoffTime(1, 32, 20)
myAWSIoTMQTTShadowClient.configureConnectDisconnectTimeout(60) # 10sec
myAWSIoTMQTTShadowClient.configureMQTTOperationTimeout(30) #5sec #connect to AWS IoT
myAWSIoTMQTTShadowClient.connect() #create a devcie Shadow with persistent subscription
thingName = "homepi"
deviceShadowHandler = myAWSIoTMQTTShadowClient.createShadowHandlerWithName(thingName, True) #listen on deleta
deviceShadowHandler.shadowRegisterDeltaCallback(customShadowCallback_Delta) #print the intital status
printDeviceStatus() #send initial status to IoT service
sendCurrentState2AWSIoT() #get the shadow after started
deviceShadowHandler.shadowGet(customShadowCallback_Get, 60) #update shadow in a loop
loopCount = 0
while True:
time.sleep(1)
3.3 主要过程
(1)ledSwitch.py 在运行后,获取led 的初始状态,并将其发给 AWS IoT 服务:
=========================
outputFlag is 0
Current status: off
=========================
outputFlag is 0
Device current status is off
Sending reported status to MQTT...
Payload is: {"state":{"reported":{"light":"off"}}}
(2)ledController.py 开始运行后,它首先获取led 的当前状态,为 『off』
(3)Controller 将其 desired 状态设置为 on
To change Led desired status to "on" ...
payload is: {"state":{"desired":{"light":"on"}}}
(4)Switch 收到 DELTA 消息,调用 GPIO 接口设置其状态,并向 IOT 服务报告其状态
{"version":513,"timestamp":1533983956,"state":{"light":"on"},"metadata":{"light":{"timestamp":1533983956}},"clientToken":"93dfc84c-c9f9-49fb-b844-d55203991208"}
++++++++ Get DELTA data +++++++++++
desired status: on
version: 513
outputFlag is 0
Device current status is off
=============================
Set device status to on
=============================
outputFlag is 1
Device current status is on
Sending reported status to MQTT...
Payload is: {"state":{"reported":{"light":"on"}}}
(5)Controller 获取最新状态
{"state":{"desired":{"light":"on"},"reported":{"light":"on"}},"metadata":{"desired":{"light":{"timestamp":1533983956}},"reported":{"light":{"timestamp":1533983957}}},"version":514,"timestamp":1533983959,"clientToken":"f24bcbbb-4b24-4354-b1df-349afdf23422"}
Desired status: on @ Sat Aug 11 18:39:16 2018
Reported status: on @ Sat Aug 11 18:39:17 2018
(6)循环往复
欢迎大家关注我的个人公众号:
参考链接:
- https://www.linkedin.com/pulse/understanding-internet-things-aws-iot-kay-lerch/
- https://iotbytes.wordpress.com/device-shadows-part-1-basics/
- https://github.com/pradeesi/AWS-IoT-Shadows
- https://github.com/aws/aws-iot-device-sdk-python/tree/master/samples/basicShadow
- https://raspi.tv/2013/rpi-gpio-basics-5-setting-up-and-using-outputs-with-rpi-gpio
云中树莓派(3):通过 AWS IoT 控制树莓派上的 Led的更多相关文章
- 云中树莓派(5):利用 AWS IoT Greengrass 进行 IoT 边缘计算
云中树莓派(1):环境准备 云中树莓派(2):将传感器数据上传到AWS IoT 并利用Kibana进行展示 云中树莓派(3):通过 AWS IoT 控制树莓派上的Led 云中树莓派(4):利用声音传感 ...
- 云中树莓派(2):将传感器数据上传到 AWS IoT 并利用Kibana进行展示
云中树莓派(1):环境准备 云中树莓派(2):将传感器数据上传到AWS IoT 并利用Kibana进行展示 1. 传感器安装及配置 1.1 DHT22 安装 DHT22 是一款温度与湿度传感器,它有3 ...
- 腾讯IOT之树莓派物联网设备
目录 腾讯IOT之树莓派物联网设备 硬件配置 软件配置 Tecent IOT 开发平台的使用 新建项目 新建产品 添加自定义功能 设备开发 微信小程序配置 面板配置 新建设备 使用设备 在线调试 设备 ...
- C#控制树莓派入门
何为树莓派 许久没有写博客了,十二月份西安疫情的影响,居家隔离了一个多月,在其期间,学习了一下树莓派,觉得硬件还是挺有意思的,刚好也看到了巨硬有提供使用c#用来开发树莓派应用的解决方案叫Net Iot ...
- node 控制 树莓派做的天气闹钟
node 控制 树莓派做的天气闹钟 在成都上班,下雨天堵车,迟到的概率会很大. 正好手上有一块树莓派 ,做了一个晴雨闹钟. 下雨天 早上 7:00叫我起床 晴天 早上 7:30叫我起床 将自己喜欢的歌 ...
- 树莓派进阶之路 (023) - Windows下用串行连接控制树莓派(转)
转载:http://shumeipai.nxez.com/2014/05/04/under-windows-serial-connection-control-raspberry-pi.html 在没 ...
- 视频图文教学 - 用最快的速度把 DotNet Core Blazor 程序安装到 树莓派中 并且用网页控制 GPIO 闪灯
前言 dotnet core 在3.0时代已经发展得很好. 尤其是在跨平台方面更已经是达到了很实用的阶段. 作为 dotnet 程序员, 应该对 Linux 有充分的了解, 也可以在业余时间玩玩硬件, ...
- AWS IoT Greengrass是什么?V1和V2版本及其差异
AWS IoT Greengrass Greengrass主要是用于边缘计算或者机器学习有关,对于详细了解请阅读结尾处的官方文档,文档内容也较为丰富. 目录 AWS IoT Greengrass ...
- 树莓派(raspberry pi)学习11: 将树莓派变成一个Web服务器(转)
将树莓派变成一个Web服务器,通过访问网页,就可以控制树莓派,比如:查看摄像头\开灯等等. 一想到Linux Web服务器,我们首先想到的是,Apache + MySql + Php. 树莓派可以安装 ...
随机推荐
- python 常用库及安装使用
#win10 + python3.5.2 #pip install xxx 自动下载的缓存位置: #win7 - c:\用户\(你的用户名)\AppData\Local\pip\cache\ #l ...
- LeetCode - Rotate String
We are given two strings, A and B. A shift on A consists of taking string A and moving the leftmost ...
- LeetCode – All Nodes Distance K in Binary Tree
We are given a binary tree (with root node root), a target node, and an integer value K. Return a li ...
- numpy.array
关于python中的二维数组,主要有list和numpy.array两种. 好吧,其实还有matrices,但它必须是2维的,而numpy arrays (ndarrays) 可以是多维的. 我们主要 ...
- 每天进步一点点-一切皆对象/一次编写,到处运行/bean工厂
只要这个配置文件一写,其他所有的java类都可以用 用法1.直接在类中getBeans,然后调用beans的方法 用法2.将这些bean进行注入,基于xml的注入<property name=& ...
- linux 文件管理操作入门
mkdir -p /root/kali/bp/shell 一路创建文件夹直到生成文件夹shell,中间没有kali文件夹的话也会自动创建生成 tar解压缩 范例一:将整个 /etc 目录下的文件全部 ...
- NPOI之Excel——设置单元格背景色
NPOI Excel 单元格颜色对照表,在引用了 NPOI.dll 后可通过 ICellStyle 接口的 FillForegroundColor 属性实现 Excel 单元格的背景色设置,FillP ...
- 比较两个ranges(equal,mismatch,lexicographical_compare)
euqal 比较两个序列是否相等,相等返回true,不相等返回false //版本一:调用重载operator==比较元素 template <class InputIterator1,clas ...
- Java IO的一些列子
Write()方法写入文件 public static void main(String[] args){ try{ BufferedWriter out = new BufferedWriter(n ...
- IntelliJ IDEA使用心得
前言:我原来一直使用的是Eclipse,但是发现有的教程上使用的是Intellij这个IDE,而且我发现Eclipse在Web编程上特别是页面上的自动补全上确实有些不足,而且Intellij这个软件的 ...