Let's IoT Hub: 教程 1
设置 Azure IoT Hub 并通过 Raspberry Pi 3 连接。
前言
本教程假设您具备一定的编程背景知识,但没有树莓派(或任何 Linux 发行版)、GPIO 微处理器(Pi、Arduino 等)或 Python 的经验。可能会引用之前的教程。
我主要使用的编程语言是 Java、C#.NET 和 VB.NET。您可能会注意到我的 Python 代码与 Python 的标准约定和格式之间存在一些细微差异。这不应影响您对代码的理解。
目标
使用 Microsoft Azure 的 IoT Hub 发送云到设备的消息,该消息将改变连接到树莓派 3 的 LED 的物理状态。
示例用例
- 用于网络摄像头的远程控制舵机
- 简易壁挂式数字留言板
- 智能家居系统,如自动窗帘
入门
您首先需要为本教程设置好正确的编码环境。相关的说明包含在 Pi 的在线文档中,并在教程的第 0 部分进行了描述,因此这里是基本步骤:
- 安装 Raspbian Jessie
- 检查发行版的更新
- 使用连接到 Pi 引脚的万用板或任何其他接线方式,在物理上准备好您的设备。
现在您的 Pi 已经启动并运行,我们需要安装 IoTHub 库。有两种方法可以做到这一点;您可以访问 Github(https://github.com/Azure-Samples/iot-hub-python-raspberrypi-client-app),将存储库下载为 Zip 文件并解压缩文件,或者;您可以通过终端克隆存储库。
sudo apt-get install git-core
git clone https://github.com/Azure-Samples/iot-hub-python-raspberrypi-client-app.git
要编译 Python 库,请运行以下命令(根据需要调整目录路径):
cd ./iot-hub-python-raspberrypi-client-app
sudo chmod u+x setup.sh
sudo ./setup.sh
技巧
- 如果您没有其他同名目录,可以使用通配符更改目录。在上面的示例中:“
cd ./iot-*
” 将带您进入正确的目录。 - 这里的基本目录“./” 指的是“/home/pi”。如果您使用了 git clone,将在此处找到新的文件夹和文件。否则,请将其替换为您下载并解压缩的 zip 文件夹位置。
- 正如 Github 文档所述,您可能会发现
iothub_client.so
在编译时卡住。为防止这种情况发生,请打开第二个终端并准备运行
每当您看到编译速度变慢时,运行“free -m
”。可能活动 VNC 连接会导致此问题;如果冻结持续存在,请尝试通过 HDMI 查看您的 Pi 或通过 SSH 进行控制。如果系统完全冻结,可以通过断开和重新连接电源来强制重启 Pi。
对于本教程的目的,任何一种方法都可以,因为我们将从示例目录中提取一个文件来使用。
您还需要为本教程在 Azure 上设置一个 IoT Hub。相关的步骤在此 Microsoft 帮助文档中进行了详细介绍:在到达“设置树莓派”部分之前停止。
如果您此刻无法访问该网页,请遵循以下基本说明。创建一个新资源,即 IoT Hub。设置正确的订阅(免费订阅或即用即付),以及离您最近的区域。继续设置您所需的定价和规模,并完成创建。部署需要一些时间。之后,打开您的 IoT Hub,在“资源管理器”下的菜单中选择“IoT 设备”。添加一个新设备,并记下此新设备的连接字符串。
请务必保持您的IoT Hub 设备详细信息页面打开,可以在单独的设备上或在您的 Pi 上打开。稍后您将需要使用一些值来设置与您的 Pi 的正确身份验证。
库
编译后,您会发现一个大小约为 3.6 MB 的文件“iothub_client.so”,它被描述为一个共享库。这是从原始 C 代码编译的 Python 就绪库。您还会在“device/iothub_python”目录中找到 Python 包装器源代码。它为编写 IoTHub 设备所需的所有方法和参数提供了不错的参考。
这个 iothub_client.so 库(以下简称“库”)包含网络协议和各种类,可以帮助我们立即编写有用的应用程序。尽管您可以使用的函数和回调方法有很多,但对于本入门教程,我们将只使用其中的几个。当然,我强烈建议您探索该库的许多方面,并随意添加新代码 - 但我现在不会这样做。
设置代码
重要提示:Python 3 在初始化东西时使用与 Python 2 不同的方法调用集。此库和本教程不适用于 Python 3。确保您使用的是 Python 2。在 Raspbian Jessie 中,这两个选项都可以在“编程”菜单项下找到 - 确保选择“Python 2 (IDLE)”。
启动 Python 后,您需要通过“文件”>“新建文件”创建一个新文档。让我们用重要的 import
语句来设置文件:
from iothub_client import IoTHubClient, IoTHubClientError, IoTHubTransportProvider,
IoTHubClientResult, IoTHubMessage, IoTHubMessageDispositionResult, IoTHubError,
DeviceMethodReturnValue
import sys
import time
import Pi.GPIO as GPIO
提示:不要直接复制粘贴此处的代码片段。由于特殊格式,它们在 IDE 中可能无法按预期工作。
我们在这里使用的最重要的 iothub_client
导入是 IoTHubClient
(提供消息传递功能的客户端类)和 IoTHubMessageDispositionResult
(正如我们稍后将看到的,它非常重要)。
我们还可以创建一些变量来保持代码的整洁。我们将存储设备的连接字符串以及(尚未连接的)LED 的状态。
# Replace the string with the string provided by Azure
CONNSTR = “HostName=hubname.azure-devices.net;...;...”
LIGHTON = True
CLIENT = 0
连接字符串的格式应为“HostName=…;DeviceId=…;SharedAccessKey=…
”。如果格式不正确,您的客户端将无法初始化。
提示:如果您的客户端实例化失败并抛出异常,但字符串格式与上述匹配,请确保您复制的是设备详细信息页面上的密钥,而不是 IoTHub 页面上的密钥。密钥格式相同,但用途不同。
或者,如果您愿意,可以创建三个变量来存储连接字符串的每个部分。但是,客户端仅接受上述格式的连接字符串。
现在我们已经设置好了文档,可以开始创建将与我们的 Hub 通信的客户端了。我建议定期保存并运行模块以确保您的代码正常工作。继续尝试一下。
如果您按照上述步骤操作,您可能会注意到 Python 无法识别您的导入!这是因为您没有在 Python 文件所在的同一目录中创建该库的副本。找到“iothub_client.so”所在的位置,并将其复制到您保存代码的目录中。请记住,这应该在存储库目录中,并且在运行“setup.sh”脚本之前不会存在。
某些文件和文件夹可能是隐藏的,您可能需要运行提升的文件管理器才能看到它们。如果是这种情况,请在终端中执行“sudo pcmanfm
”。
如果第一次运行模块失败,请再次尝试。它应该可以正常运行,然后立即停止执行。如果不行,请仔细检查错误并根据需要解决。
创建客户端
为了保持代码的整洁和有条理,我们将为每个任务创建函数。眼前的第一个任务是初始化一个 IoTHubClient
。
def setupClient():
global CONNSTR # We need to use this previously declared variable
# Create a client with the connection string and use MQTT
client = IoTHubClient(CONNSTR, IoTHubTransportProvider.MQTT)
# Optional key-value settings
client.set_option(“product_info”, “RPI-Python”)
return client
此函数为我们提供了一个 IoTHubClient
对象,该对象可以发送和接收消息。让我们也创建 main
方法来运行此代码。最后,在文件末尾,让它运行 main()
。请记住,Python 是一个对缩进敏感的语言。您的代码应如下所示:
def main():
global CLIENT
# Instantiate a client and store in global CLIENT variable.
# If CLIENT isn’t global, the object gets disposed immediately
# after exiting the method.
CLIENT = setupClient()
# Begin code execution
print (“Starting IoT Hub Client...”)
main()
这不是很激动人心,但还是运行一下。如果您的连接字符串正确且您的 Pi 具有互联网访问权限,您的代码将运行并顺利退出。
现在我们已经建立了到云的连接。现在我们只需要接收来自 Hub 的消息,并在发生这种情况时执行操作。
设置回调方法
IoTHubClient
利用回调方法来触发事件,接收到的消息需要有自己的回调方法。让我们进行设置。
def gotMail(message, context):
# These arguments are required by IoTHubClient.
# ‘message’ contains the details of the message
# sent from the server.
# ‘context’ is the user-provided context of an undefined type
# Read the bytes and deserialize it into a string
message_buffer = message.get_bytearray()
size = len(message_buffer)
msg = message_buffer[:size].decode(“utf-8”)
print (“Server says: %s” % (msg))
提示:Python 的 string
格式如下:“%s abcdef %s …” % (arg1, arg2, …)
现在我们有了这个函数,我们需要通知 IoTHubClient
。我们将在实例化 setupClient()
中的客户端时进行。修改您之前编写的 setupClient
方法。
def setupClient():
global CONNSTR, gotMail
# Create a client with the connection string and use MQTT
client = IoTHubClient(CONNSTR, IoTHubTransportProvider.MQTT)
# Optional key-value settings
client.set_option(“product_info”, “RPI-Python”)
# Set callback method for messages
client.set_message_callback(gotMail, 0) #(context := 0)
return client
提示:虽然 Python 对缩进敏感,但它不对行敏感。您可以根据需要调整代码、注释和函数之间的间距。
注意:已更新全局变量以包含 gotMail。
运行代码。同样,代码似乎几乎立即停止执行,甚至没有抛出任何错误。这是因为在到达最后一行代码时,Python 没有考虑到我们正在等待服务器给我们发送消息。让我们创建一些代码来保持应用程序运行,即使没有剩余要做的任务。
...
# Begin code execution
print (“Starting IoT Hub Client...”)
main()
while True:
time.sleep(1)
提示:time.sleep(n)
以秒为单位计算 n。它也可以接受浮点数,以获取秒的小数部分。
啊,臭名昭著的“while True
”循环。这将定期(每秒)使主线程进入休眠状态,一次又一次。不幸的是,这意味着您的代码永远不会自然停止。但是,我们仍然可以使用 Ctrl+C 在 Python shell 中停止代码执行。
再次运行代码。您会注意到 shell 不再立即通知我们它已重新启动。这意味着我们的代码在持续运行,即使没有物理迹象表明它在运行。让我们让代码运行一会儿,然后转向您的 IoT Hub 的 Azure 页面。
发送消息
如果您打开了设备详细信息页面,请继续阅读。如果没有,您可以通过“主页”>“您的 Hub”>“IoT 设备”>“您的设备”找到它。请记住,这是带有您设备连接字符串的页面,而不是 Hub 的连接字符串。
在设备详细信息的顶部菜单中,您会看到一个标有“发送到设备的消息”的按钮。打开它将带您到一个简单的表单,该表单支持发送消息和一些附加信息。在表单的消息正文部分输入一些内容,然后单击顶部的“发送消息”。
注意:您无法通过消息传递门户传输“未识别的 ASCII 符号”。
如果一切设置正确,您的 Python shell 应该会显示服务器已发送一条消息。恭喜您,您刚刚将数据从您的互联网浏览器发送到了您的树莓派。
设置 LED
现在我们已经建立了连接并可以将信息从服务器传输到客户端,让我们使其更具视觉体验。准备一个标准的 LED 和一个大约 100 欧姆的电阻,并找到 Pi3 上的一个空闲引脚。在本教程中,我们将使用 Pi3 引脚图上标记的GPIO 引脚 6,但您可以选择任何支持 GPIO 的编号。
将 LED 的正极(较长的一端)连接到引脚 6,将负极连接到电阻。最后,将电阻连接到地(GND)。这应该是串联电路,而不是并联电路。
Pi3 GPIO 引脚均输出 3V - 如果 LED 非常亮,请立即将其移除,并将您的电阻替换为更高值的电阻。如果您的 LED 昏暗,请减小电阻值。如果您的 LED 突然关闭,请将其断开并使用 1.5 V 电源(AA 电池或类似电源)进行测试 - 它可能已经烧毁。
注意:如果您不熟悉电阻或电路,请严格遵守所有给定的值和引脚选择。这将有助于降低故障的可能性。
现在电路已完成,我们需要在代码中为该引脚编程。
...
# Set pin numbering mode
GPIO.setmode(GPIO.BCM)
# Set pin 6 (or whatever number you’re using)
GPIO.setup(6, GPIO.OUT)
# Begin code execution
...
我们已经告知了我们的 Pi 我们要将 GPIO 6 用作输出引脚。现在,我们需要一种方法来设置我们想要的信号位置。让我们再创建一些方法。请记住,这些方法声明必须在调用它们之前编写,因此将所有声明分组在文档顶部很有帮助。
def turnLightOn(): # Regardless of state, turn the light on
global LIGHTON
# Remember the set state
LIGHTON = True
# Set pin 6 to HIGH voltage (3V)
GPIO.output(6, GPIO.HIGH)
print (“LED on!”)
def turnLightOff(): # Regardless of state, turn the light off
global LIGHTON
#Remember the set state
LIGHTON = False
# Set pin 6 to LOW voltage (0V)
GPIO.output(6, GPIO.LOW)
print (“LED off.”)
def toggleLight(): # Toggle the light state from one to the other
global LIGHTON
print (“Toggling...”)
if LIGHTON:
turnLightOff() # if ON then TURN OFF
else:
turnLightOn() # if OFF then TURN ON
为了使我们的代码确定性,让我们确保为 LED 设置一个默认状态。另外,让我们让 LED 闪烁,使其更有趣。
...
# Begin code execution
turnLightOn() # Ensure start state is ON
print (“Starting IoT Hub Client...”)
main()
while True:
toggleLight() # Blink the LED every second
time.sleep(1)
运行您的代码 - 您的 LED 应该每秒闪烁一次。如果没有,请仔细检查您的接线和引脚编号。同时要注意您的缩进。如果需要,您可以使用注释来包含方法的“假”结束。
def func():
code code code
# end func
连续执行
与其使用 Ctrl+C 停止代码执行,不如从 Hub 向您的 Pi 发送一条消息,看看会发生什么。您会注意到闪烁停止,代码也会停止,原因不明。事实证明,接收消息的回调方法必须返回一个值才能使您的线程保持运行。让我们回去编辑那段代码。
def gotMail(message, context):
...
print (“Server says: %s” % (msg))
#Allow code execution to continue
return IoTHubMessageDispositionResult.ACCEPTED
运行此代码应该可以让您的 LED 继续闪烁,即使收到了多条消息。
交互式 LED
最后,让我们连接 Hub 消息和我们的 LED。我们希望能够通过发送一个简单的 string
来通过服务器设置 LED 的状态。
首先,我们需要禁用闪烁的 LED。您可以通过用井号(#)注释掉它,或删除“while True
”循环中的该行来做到这一点。
不幸的是,Python 没有 Switch
-case 结构,所以我们不能用它来设置我们的命令。现在,我们将继续使用 If
-ElseIf
结构来确定收到的消息内容。
def gotMail(message, context):
...
# print (“Server says: %s” % (msg))
# Message interpretation
if msg == “turn on” or msg == “on”:
turnLightOn()
elif msg == “turn off” or msg == “off”:
turnLightOff()
elif msg == “toggle”:
toggleLight()
else:
print (“I don’t know how to ‘%s’.” % (msg))
return ...
提示:Python 不使用“elseif
”或“else if
”,而是使用“elif
”。它也不使用 &&
或 ||
。
注意:请注意,这里已注释掉不必要的初始打印语句。
运行模块,然后从 Hub 网页开始发送消息。您应该看到您的 LED 的行为与您告诉它的一样;发送“turn on”会点亮 LED,而“toggle”会在打开和关闭之间切换。如果您看到 Python shell 宣布它正在打开和关闭 LED,但您在现实中看不到它,请检查您的接线并确保您的 LED 没有烧毁。
结论
恭喜!您已经设置了一个 Azure Internet of Things Hub,将您的树莓派 3 连接为 IoT 设备,从 Hub 网页接收了服务器发送的消息,并通过专门编写的命令改变了 LED 的状态。
接下来可以做什么?
探索 Python 语言并清理您编写的代码。在代码中添加详细信息,例如省略的“context”字段。尝试将第二个 LED 连接到另一个 GPIO 引脚,并创建一个命令解释器,该解释器可以根据 LED 的编号控制特定 LED 的状态。使用 Try
-Except
块添加一些错误处理程序以自动处理异常。