物联网的核心是允许与物理设备交互的 web 服务。在本章中,我们将探讨如何使用 web 服务来显示来自 Raspberry Pi 的感官数据。我们还将研究 Twilio,一种短信服务,以及如何使用该服务从 Raspberry Pi 向自己发送短信。
本章将介绍以下主题:
在本章中,我们将编写代码,在物联网仪表板上显示我们的感官数据。除此之外,我们还将探索 Twilio,一种短信服务。然后我们将把这两个概念放在一起,以增强我们在第 9 章中构建的家庭安全仪表板,构建家庭安全仪表板。
为完成本项目,需要以下各项:
Raspberry Pi 3 型(2015 型或更新型)
USB 电源
计算机显示器
USB 键盘
USB 鼠标
试验板
跨接导线
DHT-11 型温度传感器
PIR 传感器
按钮(闭锁)
钥匙开关(可选)
在本节中,我们将使用 MQTT 协议将感官数据发布到在线仪表板。这将涉及到在 ThingsBoard 网站上建立一个账户,并利用demo
环境。
我们将使用 MQTT 协议与 ThingsBoard 中的仪表板进行通信。要在 Raspberry Pi 上设置库,请执行以下操作:
**sudo pip3 install pho-mqtt**
首先,导航至 ThingsBoard 网站www.ThingsBoard.io:
Room Conditions
作为名称并在添加设备对话框中选择设备类型的默认值。不要选择 Is 网关。单击添加:我们在这里所做的是建立一个 ThingsBoard 帐户,并在 ThingsBoard 中安装一个新设备。我们将使用此设备从树莓派中检索感官信息,并制作这些值的仪表板。
现在是创建电路和代码的时候了。使用 GPIO 引脚 19 安装 DHT-11 传感器(如果您不确定如何将 DHT-11 传感器连接到 Raspberry Pi,请参阅第 9 章构建家庭安全仪表板:
dht11-mqtt.py
的新文件。在文件中键入以下内容并运行它。确保从剪贴板粘贴访问令牌:from time import sleep
import Adafruit_DHT
import paho.mqtt.client as mqtt
import json
host = 'demo.thingsboard.io'
access_token = '<<access token>>'
dht_sensor = Adafruit_DHT.DHT11
pin = 19
sensor_data = {'temperature': 0, 'humidity': 0}
client = mqtt.Client()
client.username_pw_set(access_token)
while True:
humidity, temperature = Adafruit_DHT
.read_retry(dht_sensor, pin)
print(u"Temperature: {:g}\u00b0C, Humidity
{:g}%".format(temperature, humidity))
sensor_data['temperature'] = temperature
sensor_data['humidity'] = humidity
client.connect(host, 1883, 20)
client.publish('v1/devices/me/telemetry',
json.dumps(sensor_data), 1)
client.disconnect()
sleep(10)
让我们仔细看看前面的代码:
import
语句允许我们访问代码所需的模块:from time import sleep
import Adafruit_DHT
import paho.mqtt.client as mqtt
import json
我们已经熟悉了sleep
、Adafruit_DHT
和json
。通过Paho MQTT
库,我们可以访问client
对象,我们将使用该对象将感官数据发布到仪表板。
demo
服务器的 URL 和我们之前从设备检索到的访问令牌设置变量。为了连接到 MQTT 服务器并发布我们的感知数据,我们需要这两个值:host = 'demo.thingsboard.io'
access_token = '<<access token>>'
dht_sensor
变量定义为Adafruit
库中的DHT11
对象。我们使用针19
作为传感器:dht_sensor = Adafruit_DHT.DHT11
pin = 19
dictionary
对象来存储将发布到 MQTT 服务器的感知数据:sensor_data = {'temperature': 0, 'humidity': 0}
mqtt Client
类型的client
对象。用户名和密码由前面代码中定义的access_token
设置:client = mqtt.Client()
client.username_pw_set(access_token)
while
循环包含读取感知数据的代码,然后将其发布到 MQTT 服务器。通过读取read_retry
方法设置湿度和温度,我们设置相应的sensor_data
字典值如下:while True:
humidity, temperature = Adafruit_DHT
.read_retry(dht_sensor, pin)
print(u"Temperature: {:g}\u00b0C, Humidity
{:g}%".format(temperature, humidity))
sensor_data['temperature'] = temperature
sensor_data['humidity'] = humidity
client
代码是负责将我们的感知数据发布到 MQTT 服务器的代码。我们使用client
对象的connect
方法进行连接,传入主机值、端口(默认端口)和20
秒的保持时间。与许多 MQTT 示例不同,我们不创建循环并查找回调,因为我们只对发布感知值感兴趣,而不订阅主题。在本例中,根据 ThingsBoard 文档示例代码,我们发布的主题是v1/devices/me/telemetry
。然后我们断开与client
的连接:client.connect(host, 1883, 20)
client.publish('v1/devices/me/telemetry',
json.dumps(sensor_data), 1)
client.disconnect()
sleep(10)
现在,我们将在 ThingsBoard 中创建一个仪表板,以显示从代码发送的感官值。
以下是将湿度值添加到仪表板的步骤:
Room Conditions
作为名称:您现在应该看到一个仪表板,其湿度和温度值以模拟刻度盘显示。
如果要将此仪表板公开,以便其他人可以看到,则需要执行以下操作:
在本节中,我们将连接到文字信息传输服务,并从 Raspberry Pi 向手机发送文字信息。我们将利用这些信息,以及我们迄今为止在发布感官信息方面所学到的知识,在第 9 章构建家庭安全仪表板中对我们的安全仪表板进行增强
Twilio 是一项服务,它使软件开发人员能够通过使用其 web 服务 API 以编程方式创建和接收文本和电话呼叫。让我们从设置 Twilio 帐户开始:
Doorbell
。输入名称并单击“继续”:Twilio 是一种付费服务。你将得到一个初始数量的工作。请在创建应用程序之前检查使用此服务的成本。
要从 Python 访问 Twilio,我们需要安装twilio
库。打开终端并键入以下内容:
pip3 install twilio
您应该可以看到终端中安装 Twilio 的进度。
在发送文本之前,我们需要获取凭据。在您的 Twilio 帐户中,单击设置|常规,然后向下滚动到 API 凭据:
我们将同时使用 LIVE 凭据和测试凭据值。打开 Thonny 并创建一个名为twilio-test.py
的新文件。在文件中键入以下代码并运行它。请确保粘贴 LIVE 凭据(请注意,发送文本将向您的帐户收取费用):
from twilio.rest import Client
account_sid = '<<your account_sid>>'
auth_token = '<<your auth_token>>'
client = Client(account_sid, auth_token)
message = client.messages.create(
body='Twilio says hello!',
from_='<<your Twilio number>>',
to='<<your cell phone number>>'
)
print(message.sid)
你应该在手机上看到一条短信,上面写着Twilio says hello!
。
在第 9 章构建家庭安全仪表板中,我们使用 CherryPy 创建了家庭安全仪表板。物联网背后的力量是能够利用位于世界任何地方的设备构建相互连接的应用程序。我们将把这个想法带到我们的家庭安全仪表板。如果尚未组装,则使用第 9 章、构建家庭安全仪表板中的温度传感器构建家庭安全仪表板:
class
容器中来启动代码。打开 Thonny 并创建一个名为SensoryData.py
的新文件:from gpiozero import MotionSensor
import Adafruit_DHT
class SensoryData:
humidity=''
temperature=''
detected_motion=''
dht_pin = 19
dht_sensor = Adafruit_DHT.DHT11
motion_sensor = MotionSensor(4)
def __init__(self):
self.humidity, self.temperature = Adafruit_DHT
.read_retry(self.dht_sensor,
self.dht_pin)
self.motion_detected = self.motion_sensor.motion_detected
def getTemperature(self):
return self.temperature
def getHumidity(self):
return self.humidity
def getMotionDetected(self):
return self.motion_detected
if __name__ == "__main__":
while True:
sensory_data = SensoryData()
print(sensory_data.getTemperature())
print(sensory_data.getHumidity())
print(sensory_data.getMotionDetected())
SensoryDashboard.py
的新文件。代码如下:import paho.mqtt.client as mqtt
import json
from SensoryData import SensoryData
from time import sleep
class SensoryDashboard:
host = 'demo.thingsboard.io'
access_token = '<<your access_token>>'
client = mqtt.Client()
client.username_pw_set(access_token)
sensory_data = ''
def __init__(self, sensoryData):
self.sensoryData = sensoryData
def publishSensoryData(self):
sensor_data = {'temperature': 0, 'humidity': 0,
'Motion Detected':False}
sensor_data['temperature'] = self.sensoryData
.getTemperature()
sensor_data['humidity'] = self.sensoryData.getHumidity()
sensor_data['Motion Detected'] = self.sensoryData
.getMotionDetected()
self.client.connect(self.host, 1883, 20)
self.client.publish('v1/devices/me/telemetry',
json.dumps(sensor_data), 1)
self.client.disconnect()
return sensor_data['Motion Detected']
if __name__=="__main__":
while True:
sensoryData = SensoryData()
sensory_dashboard = SensoryDashboard(sensoryData)
print("Motion Detected: " +
str(sensory_dashboard.publishSensoryData()))
sleep(10)
我们在这里所做的是将前面代码中的dht-mqtt.py
文件封装在class
容器中。我们用SensoryData
对象实例化我们的对象,以便从传感器获取数据。publishSensoryData()
方法将感官数据发送到我们的 MQTT 仪表板。注意它如何返回运动传感器的状态?我们在主循环中使用这个返回值来打印运动传感器的值。然而,这个返回值在我们未来的代码中会更有用。
让我们将运动传感器添加到 ThingsBoard 仪表板:
您应该会看到添加到房间条件仪表板的新小部件。通过单击页面右下角的橙色铅笔图标,您可以移动和调整小部件的大小。编辑小部件,使其看起来像以下屏幕截图:
我们在这里所做的是从第 9 章中重新创建家庭安全仪表板的第一个版本,构建一个具有更分布式架构的家庭安全仪表板。我们不再依靠我们的树莓派通过樱桃色的网页提供感官信息。我们能够将树莓派的作用降低为感官信息的来源。可以想象,在同一个仪表板上使用多个覆盆子 PI 是非常容易的。
通过靠近 PIR 传感器测试此新仪表板。查看是否可以将运动检测小部件更改为true
。
为了使我们新的家庭安全仪表板更加分散,让我们添加在 PIR 运动传感器激活时发送文本消息的功能。打开 Thonny 并创建一个名为SecurityDashboardDist.py
的新文件。以下是要插入到文件中的代码:
from twilio.rest import Client
from SensoryData import SensoryData
from SensoryDashboard import SensoryDashboard
from gpiozero import Button
from time import time, sleep
class SecurityDashboardDist:
account_sid = ''
auth_token = ''
time_sent = 0
test_env = True
switch = Button(8)
def __init__(self, test_env = True):
self.test_env = self.setEnvironment(test_env)
def setEnvironment(self, test_env):
if test_env:
self.account_sid = '<<your Twilio test account_sid>>'
self.auth_token = '<<your Twilio test auth_token>>'
return True
else:
self.account_sid = '<<your Twilio live account_sid>>'
self.auth_token = '<<your Twilio live auth_token>>'
return False
def update_dashboard(self, sensoryDashboard):
self.sensoryDashboard = sensoryDashboard
motion_detected = self
.sensoryDashboard
.publishSensoryData()
if motion_detected:
return self.send_alert()
else:
return 'Alarm not triggered'
def send_alert(self):
if self.switch.is_pressed:
return self.sendTextMessage()
else:
return "Alarm triggered but Not Armed"
def sendTextMessage(self):
message_interval = round(time() - self.time_sent)
if message_interval > 600:
twilio_client = Client(self.account_sid,
self.auth_token)
if self.test_env:
message = twilio_client.messages.create(
body='Intruder Alert',
from_= '+15005550006',
to='<<your cell number>>'
)
else:
message = twilio_client.messages.create(
body='Intruder Alert',
from_= '<<your Twilio number>>',
to='<<your cell number>>'
)
self.time_sent=round(time())
return 'Alarm triggered and text message sent - '
+ message.sid
else:
return 'Alarm triggered and text
message sent less than 10 minutes ago'
if __name__=="__main__":
security_dashboard = SecurityDashboardDist()
while True:
sensory_data = SensoryData()
sensory_dashboard = SensoryDashboard(sensory_data)
print(security_dashboard.update_dashboard(
sensory_dashboard))
sleep(5)
利用第 9 章中的第一版家庭安全仪表板电路构建家庭安全仪表板,此代码使用按键开关,以便在运动传感器检测到运动时触发呼叫以发送文本消息。当钥匙开关处于 off(关闭)位置时,只要运动传感器检测到运动,您就会收到一条信息,内容为Alarm triggered but Not Armed
。
如果尚未接通,则接通钥匙开关以接通电路。通过四处移动来激活运动传感器。您应该会收到一条短信已发送的通知。消息的 SID 也应该显示。你可能已经注意到你实际上没有收到短信。这是因为代码默认为 Twilio 测试环境。在打开 live 环境之前,让我们先看一下代码。
我们首先导入代码所需的库:
from twilio.rest import Client
from SensoryData import SensoryData
from SensoryDashboard import SensoryDashboard
from gpiozero import Button
from time import time, sleep
这里没有太多我们以前没有见过的东西;然而,请注意SensoryData
和SensoryDashboard
进口。由于我们已经封装了读取感官数据的代码,现在我们可以把它看作一个黑匣子。我们知道我们需要用于安全仪表板的感官数据,但我们不关心如何获取这些数据以及数据将显示在何处。SensoryData
允许我们访问所需的感官数据,SensoryDashboard
将其发送到某个仪表板。我们不必在SecurityDashboardDist.py
代码中关注这些细节。
我们为分布式安全仪表板创建了一个名为SecurityDashboardDist
的类。通过名称来区分我们的类,并选择描述class
是什么的名称,这一点很重要:
class SecurityDashboardDist:
在声明了一些可在整个类中访问的类变量之后,我们将进入类初始化方法:
account_sid = ''
auth_token = ''
time_sent = 0
test_env = True
switch = Button(8)
def __init__(self, test_env = True):
self.test_env = self.setEnvironment(test_env)
在initialization
方法中,我们设置了类作用域test_env
变量(对于test
环境)。默认值为True
,这意味着我们必须认真地覆盖默认值,以便实时运行仪表板。我们使用setEnvironment()
方法设置test_env
:
def setEnvironment(self, test_env):
if test_env:
self.account_sid = '<<your Twilio test account_sid>>'
self.auth_token = '<<your Twilio test auth_token>>'
return True
else:
self.account_sid = '<<your Twilio live account_sid>>'
self.auth_token = '<<your Twilio live auth_token>>'
return False
setEnvironment()
方法根据test_env
的值,为测试环境或活动环境设置类范围的account_id
和auth_token
值。我们基本上只是通过setEnvironment()
方法传回test_env
的状态,同时设置启用测试或实时短信环境所需的变量。
update_dashboard()
方法通过使用我们传递给该方法的SensoryDashboard
对象来调用传感器和感官仪表板。这就是我们采用的面向对象方法的优点,因为我们不需要关心传感器的读取方式或仪表板的更新方式。我们只需传入一个SensoryDashboard
对象即可完成此操作:
def update_dashboard(self, sensoryDashboard):
self.sensoryDashboard = sensoryDashboard
motion_detected = self
.sensoryDashboard
.publishSensoryData()
if motion_detected:
return self.send_alert()
else:
return 'Alarm not triggered'
update_dashboard
方法还负责通过检查运动传感器的状态来确定是否发送文本消息。你还记得当我们在SensoryDashboard
类上调用publishSensoryData()
方法时,我们是如何返回运动传感器的状态的吗?这就是它真正有用的地方。我们可以使用此返回值来确定是否应发送警报。我们根本不需要在课堂上检查运动传感器的状态,因为它很容易从SensoryDashboard
课堂上获得。
send_alert()
方法检查开关的状态,以确定是否应发送文本消息:
def send_alert(self):
if self.switch.is_pressed:
return self.sendTextMessage()
else:
return "Alarm triggered but Not Armed"
您可能想知道为什么我们在这里检查传感器(本例中为开关)的状态,而不是从SensoryDashboard
类检查它。答案是什么?我们正在通过封装一个感官数据仪表板来构建一个家庭安全仪表板。SensorDashboard
类中不需要开关,因为它与打开和关闭从 GPIO 到 MQTT 仪表板的感官数据读取和传输无关。交换机是安全系统的域;在本例中,SecurityDashboardDist
类
SecurityDasboardDist
类的核心是sendTextMessage()
方法,概述如下:
def sendTextMessage(self):
message_interval = round(time() - self.time_sent)
if message_interval > 600:
twilio_client = Client(self.account_sid,
self.auth_token)
if self.test_env:
message = twilio_client.messages.create(
body='Intruder Alert',
from_= '+15005550006',
to='<<your cell number>>'
)
else:
message = twilio_client.messages.create(
body='Intruder Alert',
from_= '<<your Twilio number>>',
to='<<your cell number>>'
)
self.time_sent=round(time())
return 'Alarm triggered and text message sent - '
+ message.sid
else:
return 'Alarm triggered and text
message sent less than 10 minutes ago'
我们使用message_interval
方法变量来设置文本之间的持续时间。我们不希望每次运动传感器检测到运动时都发送文本消息。在我们的例子中,文本之间的最小间隔时间是600
秒,或者10
分钟。
如果这是第一次,或者自上次发送文本消息以来已超过 10 分钟,那么代码将在测试环境中发送文本消息,或者直接在 live 中发送。注意15005550006
电话号码如何用于测试环境。现场环境需要您的 Twilio 号码,to
字段需要您自己的电话号码。对于测试环境和实时环境,返回Alarm triggered and text message sent
消息,然后是消息的 SID。区别在于您实际上不会收到文本消息(尽管代码中有对 Twilio 的调用)。
如果距离上次发送短信不到 10 分钟,则短信将显示为Alarm triggered and text message sent less than 10 minutes ago
。
在我们的主函数中,我们创建了一个SecurityDashboardDist
对象并将其命名为security_dashboard
。通过不传入任何内容,我们允许在默认情况下为测试环境设置仪表板:
if __name__=="__main__":
security_dashboard = SecurityDashboardDist()
while True:
sensory_data = SensoryData()
sensory_dashboard = SensoryDashboard(sensory_data)
print(security_dashboard.update_dashboard(
sensory_dashboard))
sleep(5)
接下来的连续循环每 5 秒创建一个SensoryData
和SensoryDashboard
对象。SensoryData
对象(sensory_data
用于实例化SensoryDashboard
对象(sensory_dashboard
,因为前者为我们提供当前的感官数据,后者创建感官仪表板。
通过根据类的性质命名我们的类,并根据它们的作用命名我们的方法,代码变得非常不言自明。
然后我们将这个SensoryDashboard
对象(sensory_dashboard
传递给SecurityDashboard
(security_dashboard
的update_dashboard
方法。由于update_dashboard
方法返回一个字符串,我们可以使用它打印到 shell 中,因此每 5 秒可以看到仪表板的状态。我们将SecurityDashboardDist
对象的实例化排除在循环之外,因为我们只需要设置一次环境。
现在我们了解了代码,是时候在实时 Twilio 环境中运行它了。请注意,当我们切换到 live 时,代码中唯一更改的部分是实际发送的文本消息。要将仪表板变成实时文本发送机器,只需将 main 方法的第一行更改为以下内容:
security_dashboard = SecurityDashboardDist(True)
完成本章后,我们应该非常熟悉将感官数据发布到物联网仪表板。我们还应该熟悉使用 TwilioWeb 服务从 Raspberry Pi 发送文本消息。
我们将在第 11 章中查看蓝牙库,使用蓝牙创建门铃按钮,然后将这些信息和我们在本章中获得的信息结合在一起制作物联网门铃。
account_sid
编号在实时环境中与在测试环境中相同。SecurityDashboardDist.py
代码中,我们在哪里创建SecurityDashboardDist
对象?要进一步了解 Twilio 和 ThingsBoard 背后的技术,请参考以下链接: