65.9K
CodeProject 正在变化。 阅读更多。
Home

Let's IoT Hub: 教程 1

starIconstarIconstarIcon
emptyStarIcon
starIcon
emptyStarIcon

3.35/5 (9投票s)

2018年6月23日

CPOL

12分钟阅读

viewsIcon

22103

设置 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 部分进行了描述,因此这里是基本步骤:

  1. 安装 Raspbian Jessie
  2. 检查发行版的更新
  3. 使用连接到 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 块添加一些错误处理程序以自动处理异常。

Continue

教程 2 | 教程 3

© . All rights reserved.