Python 物联网指南(三)

原文:zh.annas-archive.org/md5/729fe5cbaba4b57748ef3646477e357a

译者:飞龙

协议:CC BY-NC-SA 4.0

第九章。与云一起工作

在本章中,我们将利用许多云服务来发布和可视化收集的传感器数据,并在互联网连接的事物之间建立双向通信。我们将涵盖以下主题:

  • 使用 dweepy 和 dweet.io 将数据发布到云

  • 使用 freeboard.io 构建基于网页的仪表板

  • 通过 PubNub 在互联网上实时发送和接收数据

  • 通过 PubNub 云发布带有命令的消息

  • 在物联网设备与其他设备之间进行双向通信

  • 使用 Python PubNub 客户端将消息发布到云

  • 使用 Mosquitto 和 Eclipse Paho 的 MQTT 协议

  • 使用 Python 客户端将消息发布到 Mosquitto 代理

使用 dweepy 将数据发布到云

在第八章中,我们使用数字温度和湿度传感器与显示屏和伺服机构一起工作。现在,我们想利用两个云服务来构建一个实时交互式的基于网页的仪表板,使我们能够在网页浏览器中查看以下信息的仪表盘:

  • 环境温度以华氏度(ºF)为单位测量

  • 环境温度以摄氏度(ºC)为单位测量

  • 环境湿度水平以百分比(%)表示

首先,我们将利用 dweet.io 发布从传感器获取的数据,并使其可供全球的计算机和设备使用。dweet.io 数据共享工具使我们能够轻松发布来自物联网设备的数据或消息和警报,然后使用其他设备订阅这些数据。dweet.io 数据共享工具将自己定义为类似于为社交机器提供的 Twitter。您可以在其网页上了解更多关于 dweet.io 的信息:dweet.io

小贴士

在我们的示例中,我们将利用 dweet.io 提供的免费服务,而不会使用一些提供数据隐私但需要付费订阅的高级功能。由于我们不使用锁定的 dweets,我们的数据将对任何可以访问 dweet.io 网页的人开放。

dweet.io 数据共享工具提供了一个 Web API,我们可以从我们的物联网设备(在 dweet.io 文档中称为 thing)发送数据。首先,我们必须为我们的 thing 选择一个独特的名称。将字符串与 GUID(即 全球唯一标识符)组合是方便的。另一种选择是点击主 dweet.io 网页上的 立即尝试 按钮,并获取网页为我们的 thing 选择的名字。这样,我们可以确保名字是唯一的,并且没有人使用这个名字为另一个 thing 发布数据到 dweet.io

一旦我们为我们的设备选择了一个独特的名称,我们就可以开始发布数据,这个过程被称为 dweeting。我们只需要在请求 URL https://dweet.io/dweet/for/my-thing-name 中组合一个 POST HTTP 动词,并在正文中包含所需的 JSON 数据。我们必须将 my-thing-name 替换为我们为设备选择的名称。在我们的示例中,我们将使用 iot_python_chapter_09_01_gaston_hillar 来命名我们的 IoT 设备,该设备将发布温度和湿度值,即将要 dweet 的设备。因此,我们必须在请求 URL https://dweet.io/dweet/for/iot_python_chapter_09_01_gaston_hillar 中组合一个 POST HTTP 动词,并在正文中包含所需的 JSON 数据。确保将名称替换为您为设备选择的名称。

Dweepy 是一个简单的 Python 客户端,用于 dweet.io,它允许我们使用 Python 容易地发布数据到 dweet.io。我们不需要手动使用 Python 构建并发送一个特定的 URL 的 HTTP 请求,而是可以使用这个有用模块提供的方法。以下是为 Dweepy 模块提供的网页:pypi.python.org/pypi/dweepy/0.2.0。在内部,Dweepy 使用流行的 requests 模块提供的流行功能来构建和发送 HTTP 请求。

小贴士

在物联网中作为我们的主要编程语言使用 Python 的一个好处是,总有软件包让事情变得简单。

在 第二章,在英特尔 Galileo Gen 2 上使用 Python 中,我们安装了 pip 安装程序,以便在板上的 Yocto Linux 中轻松安装额外的 Python 2.7.3 软件包。现在,我们将使用 pip 安装程序安装 Dweepy 0.2.0。我们只需要在 SSH 终端中运行以下命令来安装软件包:

pip install dweepy

输出的最后几行将指示 dweepy 软件包已成功安装。不要担心与构建 wheel 相关的错误消息和不安全平台警告:

Collecting dweepy
Downloading dweepy-0.2.0.tar.gz
Requirement already satisfied (use --upgrade to upgrade): requests<3,>=2 in /usr/lib/python2.7/site-packages (from dweepy)
Installing collected packages: dweepy
  Running setup.py install for dweepy
Successfully installed dweepy-0.2.0

当我们从传感器读取温度和湿度值时,我们将使用上一章编写的代码,并将此代码作为添加新功能的基线。示例代码文件为 iot_python_chapter_08_03.py

我们将使用最近安装的 dweepy 模块将数据发布到 dweet.io,并使其作为另一个云服务的数据源,该云服务将允许我们构建基于 Web 的仪表板。我们将在循环中添加必要的行,并且它将每 10 秒发布一次测量的值。示例代码文件为 iot_python_chapter_09_01.py

import pyupm_th02 as upmTh02
import pyupm_i2clcd as upmLcd
import pyupm_servo as upmServo
import dweepy
import time

if __name__ == "__main__":
    temperature_and_humidity_sensor = \
        TemperatureAndHumiditySensor(0)
    oled = TemperatureAndHumidityOled(0)
    temperature_servo = TemperatureServo(3)
 # Don't forget to replace the thing_name value
 # with your own thing name
 thing_name = "iot_python_chapter_09_01_gaston_hillar"
    while True:
        temperature_and_humidity_sensor.\
            measure_temperature_and_humidity()
        oled.print_temperature(
            temperature_and_humidity_sensor.temperature_fahrenheit,
            temperature_and_humidity_sensor.temperature_celsius)
        oled.print_humidity(
            temperature_and_humidity_sensor.humidity)
        temperature_servo.print_temperature(
            temperature_and_humidity_sensor.temperature_fahrenheit)
 # Push data to dweet.io
 dweet = {"temperature_celsius": "{:5.2f}".format(temperature_and_humidity_sensor.temperature_celsius),
 "temperature_fahrenheit": "{:5.2f}".format(temperature_and_humidity_sensor.temperature_fahrenheit),
 "humidity_level_percentage": "{:5.2f}".format(temperature_and_humidity_sensor.humidity)}
 dweepy.dweet_for(thing_name, dweet)
        print("Ambient temperature in degrees Celsius: {0}".
              format(temperature_and_humidity_sensor.temperature_celsius))
        print("Ambient temperature in degrees Fahrenheit: {0}".
              format(temperature_and_humidity_sensor.temperature_fahrenheit))
        print("Ambient humidity: {0}".
              format(temperature_and_humidity_sensor.humidity))
        # Sleep 10 seconds (10000 milliseconds)
        time.sleep(10)

高亮行显示了与上一个版本相比对__main__方法所做的更改。第一条高亮行创建了一个名为thing_name的局部变量,该变量保存了一个字符串,其中包含我们为我们的设备选择的名字,以便与dweet.io一起使用。记住,在运行示例代码之前,你必须将字符串替换为你为设备选择的名字。

然后,代码将无限循环运行,第一条高亮行创建一个字典并将其保存在dweet局部变量中。该字典定义了我们想要作为 JSON 数据发送到dweet.io的键值对。以下是要发送的键:

  • temperature_celsius

  • temperature_fahrenheit

  • humidity_level_percentage

之前列举的键的值是由传感器获取的值转换为字符串。一旦构建了包含所需 JSON 数据的字典,代码将调用dweepy.dweet_for方法,并将thing_namedweet作为参数,即设备名称和我们要为指定设备名称发布的 JSON 数据。在幕后,dweepy.dweet_for方法使用requests模块来组合一个 POST HTTP 动词,将dweet字典作为正文中的所需 JSON 数据,并且以下请求 URL:https://dweet.io/dweet/for/后跟在thing_name局部变量中指定的设备名称。这样,代码将传感器获取的温度和湿度值以不同的单位 dweet。

以下行将开始示例。

python iot_python_chapter_09_01.py

在运行示例后,打开空调或加热系统,以产生环境温度和湿度的变化。这样,我们将注意到每 10 秒发布的数据中的变化。

等待大约 20 秒,然后在任何网络浏览器中打开以下 URL:http://dweet.io/follow/iot_python_chapter_09_01_gaston_hillar。别忘了将iot_python_chapter_09_01_gaston_hillar替换为你为你的设备选择的名字。在这种情况下,我们可以在任何连接到互联网的设备上输入此 URL。我们不需要设备与板子处于同一局域网中,因为值是通过dweet.io发布的,并且可以在任何地方访问。

视觉视图将显示一条线形图,显示湿度水平和温度值随时间的变化。右侧将显示最新发布的值。当 Python 代码 dweets 新值时,视图将自动刷新。以下图片显示了带有视觉视图的屏幕截图:

https://github.com/OpenDocCN/freelearn-python-zh/raw/master/docs/iot-py/img/B05042_09_01.jpg

点击原始视图,页面将显示板子上运行的 Python 代码通过dweet.io为我们的设备发布的最新 JSON 数据。以下行显示了之前图片中显示的接收到的最新 JSON 数据示例:

{
  "humidity_level_percentage": 20.01,
  "temperature_celsius": 19.56,
  "temperature_fahrenheit": 67.21
}

在 第四章 中,使用 RESTful API 和脉冲宽度调制,我们安装了 HTTPie,这是一个用 Python 编写的命令行 HTTP 客户端,它使得发送 HTTP 请求变得容易,并且使用的语法比 curl(也称为 cURL)更容易。我们可以在任何计算机或设备上运行以下 HTTPie 命令来检索为我们的事物创建的最新 dweet。

http -b https://dweet.io:443/get/latest/dweet/for/iot_python_chapter_09_01_gaston_hillar

之前的命令将组成并发送以下 HTTP 请求:GET https://dweet.io:443/get/latest/dweet/for/iot_python_chapter_09_01_gaston_hillardweet.io API 将返回指定事物的最新 dweet。以下几行显示了 dweet.io 的一个示例响应。JSON 数据包含在 content 键的值中。

{
    "by": "getting", 
    "the": "dweets", 
    "this": "succeeded", 
    "with": [
        {
            "content": {
                "humidity_level_percentage": 19.92, 
                "temperature_celsius": 20.06, 
                "temperature_fahrenheit": 68.11
            }, 
            "created": "2016-03-27T00:11:12.598Z", 
            "thing": "iot_python_chapter_09_01_gaston_hillar"
        }
    ]
}

我们可以在任何计算机或设备上运行以下 HTTPie 命令来检索我们事物的所有已保存 dweets。

http -b https://dweet.io:443/get/ dweets/for/iot_python_chapter_09_01_gaston_hillar

之前的命令将组成并发送以下 HTTP 请求:GET https://dweet.io:443/get/dweets/for/iot_python_chapter_09_01_gaston_hillardweet.io API 将返回指定事物的长期存储中保存的 dweets。以下几行显示了 dweet.io 的一个示例响应。请注意,长期存储中存储的 dweets 数量和返回的值都有限制。

{
    "by": "getting", 
    "the": "dweets", 
    "this": "succeeded", 
    "with": [
         {
            "content": {
                "humidity_level_percentage": 19.94, 
                "temperature_celsius": 20.01, 
                "temperature_fahrenheit": 68.02
            }, 
            "created": "2016-03-27T00:11:00.554Z", 
            "thing": "iot_python_chapter_09_01_gaston_hillar"
        }, 
        {
            "content": {
                "humidity_level_percentage": 19.92, 
                "temperature_celsius": 19.98, 
                "temperature_fahrenheit": 67.96
            }, 
            "created": "2016-03-27T00:10:49.823Z", 
            "thing": "iot_python_chapter_09_01_gaston_hillar"
        }, 
        {
            "content": {
                "humidity_level_percentage": 19.92, 
                "temperature_celsius": 19.95, 
                "temperature_fahrenheit": 67.91
            }, 
            "created": "2016-03-27T00:10:39.123Z", 
            "thing": "iot_python_chapter_09_01_gaston_hillar"
        }, 
        {
            "content": {
                "humidity_level_percentage": 19.91, 
                "temperature_celsius": 19.9, 
                "temperature_fahrenheit": 67.82
            }, 
            "created": "2016-03-27T00:10:28.394Z", 
            "thing": "iot_python_chapter_09_01_gaston_hillar"
        }
    ]
}

使用 Freeboard 构建 Web 仪表板

dweet.io 数据共享工具使我们能够仅用几行代码轻松地将数据发布到云端。现在,我们准备使用 dweet.io 和我们的设备名称作为数据源来构建一个实时基于网页的仪表板。我们将利用 freeboard.io 来可视化通过传感器收集并发布到 dweet.io 的数据,并在许多仪表中展示仪表板,使其可供世界各地的不同计算机和设备使用。Freeboard.io 将自己定义为一种基于云的服务,允许我们可视化物联网。您可以在其网页上了解更多关于 freeboard.io 的信息:freeboard.io

小贴士

在我们的示例中,我们将利用 freeboard.io 提供的免费服务,而不会使用一些提供隐私但需要付费订阅的高级功能。由于我们不处理私有仪表板,因此任何拥有其唯一 URL 的人都可以访问我们的仪表板。

Freeboard 要求我们在构建基于网页的仪表板之前注册并使用有效的电子邮件地址和密码创建一个账户。我们不需要输入任何信用卡或支付信息。如果您已经在 freeboard.io 上有账户,您可以跳过下一步。

在您的网络浏览器中访问freeboard.io并点击立即开始。您也可以通过访问freeboard.io/signup达到相同的目的。在选择用户名中输入您想要的用户名,在输入您的电子邮件中输入您的电子邮件,在创建密码中输入您想要的密码。一旦填写完所有字段,点击创建我的账户

创建账户后,您可以在网络浏览器中访问freeboard.io并点击登录。您也可以通过访问freeboard.io/login达到相同的目的。然后,输入您的用户名或电子邮件和密码,并点击登录。Freeboard 将显示您的免费板,也称为仪表板。

创建新按钮左侧的输入名称文本框中输入环境温度和湿度,然后点击此按钮。Freeboard.io 将显示一个空白的仪表板,其中包含许多按钮,允许我们添加面板和数据源等。以下图片显示了空白的仪表板截图。

https://github.com/OpenDocCN/freelearn-python-zh/raw/master/docs/iot-py/img/B05042_09_02.jpg

点击数据源下方的添加,网站将打开数据源对话框。在类型下拉菜单中选择Dweet.io,对话框将显示定义dweet.io数据源所需的字段。

名称中输入环境温度和湿度,在设备名称中输入我们之前用于dweet.io的设备名称。请记住,我们曾使用iot_python_chapter_09_01_gaston_hillar来命名我们的物联网设备,但您已将其替换为不同的名称。如果您输入的名称与您在处理dweet.io时使用的名称不匹配,数据源将不会显示适当的数据。以下图片显示了使用示例设备名称的dweet.io数据源配置的截图。

https://github.com/OpenDocCN/freelearn-python-zh/raw/master/docs/iot-py/img/B05042_09_03.jpg

点击保存,数据源将出现在数据源下方的列表中。由于仪表板正在运行 dweeting 的 Python 代码,因此最后更新下显示的时间将每 10 秒更改一次。如果时间每 10 秒不更改,则意味着数据源配置错误或仪表板不再运行 dweeting 的 Python 代码。

点击添加面板以向仪表板添加一个新的空白面板。然后,点击新空白面板右上角的加号(+),Freeboard 将显示小部件对话框。

类型下拉菜单中选择量规,对话框将显示添加量规小部件到仪表板面板中所需的字段。在标题中输入华氏温度

文本框的右侧点击**+ 数据源**,选择环境温度和湿度,然后选择temperature_fahrenheit。在您做出选择后,以下文本将出现在文本框中:datasources ["Ambient temperature and humidity"] ["temperature_fahrenheit"]

单位中输入ºF,在最小值中输入-30,在最大值中输入130。然后,点击保存,Freeboard 将关闭对话框并将新的仪表添加到之前创建的仪表板窗格中。仪表将显示代码在板上最后一次 dweet 的环境温度的最新值,即代码最后发布到dweet.io的 JSON 数据中temperature_fahrenheit键的值。以下图片显示了环境温度和湿度数据源显示的最后更新时间和显示华氏度测量的环境温度的最新值的仪表。

https://github.com/OpenDocCN/freelearn-python-zh/raw/master/docs/iot-py/img/B05042_09_04.jpg

点击添加窗格以向仪表板添加另一个新的空窗格。然后,点击新空窗格右上角的加号(+),Freeboard 将显示小部件对话框。

类型下拉菜单中选择仪表,对话框将显示添加仪表小部件到仪表板窗格所需的字段。在标题中输入Humidity level in percentage

文本框的右侧点击**+ 数据源**,选择环境温度和湿度,然后选择humidity_level_percentage。在您做出选择后,以下文本将出现在文本框中:datasources ["Ambient temperature and humidity"] ["humidity_level_percentage"]

单位中输入%,在最小值中输入0,在最大值中输入100。然后,点击保存,Freeboard 将关闭对话框并将新的仪表添加到之前创建的仪表板窗格中。仪表将显示代码在板上最后一次 dweet 的周围湿度水平的最新值,即代码最后发布到dweet.io的 JSON 数据中humidity_level_percentage键的值。以下图片显示了环境温度和湿度数据源显示的最后更新时间和显示华氏度测量的环境温度的最新值的仪表。

现在,点击显示华氏度和 Freeboard 的窗格右上角的加号(+),Freeboard 将显示小部件对话框。

类型下拉菜单中选择仪表,对话框将显示添加仪表小部件到仪表板窗格所需的字段。在标题中输入Temperature in degrees Celsius

点击文本框右侧的**+ Datasource**,选择环境温度和湿度,然后选择temperature_celsius。在做出选择后,以下文本将出现在文本框中:datasources ["Ambient temperature and humidity"] ["temperature_celsius"]

单位中输入ºC,在最小值中输入-40,在最大值中输入55。然后,点击保存,Freeboard 将关闭对话框并将新的仪表添加到仪表板中之前存在的窗格内。这样,窗格将显示两个仪表,温度以两种不同的单位表示。新的仪表将显示代码在板上 dweeted 的最新值,即环境温度的值,即代码在最后一次发布到 dweet.io 的 JSON 数据中的 temperature_celsius 键的值。

现在,点击显示两个温度的窗格右侧的**+按钮旁边的配置图标。Freeboard 将显示窗格对话框。在标题中输入Temperature,然后点击保存**。

点击显示湿度水平的窗格右侧的**+按钮旁边的配置图标。Freeboard 将显示窗格对话框。在标题中输入Humidity,然后点击保存**。

将窗格拖放到位置,将湿度窗格放置在温度窗格的左侧。以下图片显示了我们所构建的仪表板,其中包含两个窗格和三个仪表,当在英特尔 Galileo Gen 2 板上运行的代码 dweets 新数据时,这些仪表会自动刷新数据。

https://github.com/OpenDocCN/freelearn-python-zh/raw/master/docs/iot-py/img/B05042_09_05.jpg

小贴士

我们可以通过输入我们在与仪表板一起工作时网页浏览器显示的 URL 来在任何设备上访问最近构建的仪表板。URL 由 https://freeboard.io/board/ 前缀后跟字母和数字组成。例如,如果 URL 是 https://freeboard.io/board/EXAMPLE,我们只需将其输入到任何设备或计算机上运行的任何网页浏览器中,我们就可以查看仪表,并且当从我们的英特尔 Galileo Gen 2 板向 dweet.io 发布新数据时,它们会刷新。

dweet.io 作为我们的数据源和 freeboard.io 作为我们的基于网页的仪表板结合起来,使我们能够轻松地使用任何提供网页浏览器的设备来监控连接到我们的英特尔 Galileo Gen 2 板的传感器获取的数据。这两个基于云的物联网服务的结合只是我们如何轻松结合不同服务的一个例子。物联网云服务的数量正在增加,这些服务可以用于我们的解决方案中。

通过 PubNub 在互联网上实时发送和接收数据

在第四章中,使用 RESTful API 和脉冲宽度调制,我们开发和使用了 RETful API,它允许我们通过 HTTP 请求控制连接到我们的 Intel Galileo Gen 2 板上的电子组件。现在,我们希望通过互联网实时发送和接收数据,而 RESTful API 并不是做这件事的最合适选项。相反,我们将使用基于比 HTTP 协议更轻量级的协议的发布/订阅模型。具体来说,我们将使用基于MQTT(即MQ Telemetry Transport)协议的服务。

MQTT 协议是一种机器到机器(简称M2M)和物联网连接协议。MQTT 是一个轻量级消息协议,它运行在 TCP/IP 协议之上,并使用发布/订阅机制。任何设备都可以订阅特定的频道(也称为主题),并会接收到发布到该频道的所有消息。此外,设备可以向该频道或其他频道发布消息。该协议在物联网和 M2M 项目中变得越来越受欢迎。你可以在以下网页上了解更多关于 MQTT 协议的信息:mqtt.org

PubNub 提供了许多基于云的服务,其中之一允许我们轻松地实时流数据和向任何设备发送信号,在底层使用 MQTT 协议。我们将利用这个 PubNub 服务通过互联网实时发送和接收数据,并使通过互联网控制我们的 Intel Galileo Gen 2 板变得容易。由于 PubNub 提供了一个具有高质量文档和示例的 Python API,因此使用 Python 来使用该服务非常简单。PubNub 将自己定义为物联网、移动和 Web 应用的全球数据流网络。你可以在其网页上了解更多关于 PubNub 的信息:www.pubnub.com

小贴士

在我们的示例中,我们将利用 PubNub 提供的免费服务,而不会使用一些可能增强我们的物联网项目连接需求但需要付费订阅的高级功能和附加服务。

PubNub 要求我们在创建应用程序之前先注册并使用有效的电子邮件和密码创建一个账户,该应用程序允许我们开始使用他们的免费服务。我们不需要输入任何信用卡或支付信息。如果你已经在 PubNub 有账户,你可以跳过下一步。

一旦你创建了账户,PubNub 将把你重定向到管理门户,该门户列出了你的 PubNub 应用程序。为了在网络上发送和接收消息,你需要生成你的 PubNub 发布和订阅密钥。管理门户中的一个新面板将代表应用程序。以下截图显示了 PubNub 管理门户中的温度控制应用程序面板:

https://github.com/OpenDocCN/freelearn-python-zh/raw/master/docs/iot-py/img/B05042_09_06.jpg

单击温度控制面板,PubNub 将显示为应用程序自动生成的演示密钥集面板。单击此面板,PubNub 将显示发布、订阅和密钥。我们必须复制并粘贴这些密钥中的每一个,以便在我们的代码中使用它们来发布消息并订阅它们。以下截图显示了密钥的前缀和图像中已擦除的剩余字符:

https://github.com/OpenDocCN/freelearn-python-zh/raw/master/docs/iot-py/img/B05042_09_07.jpg

为了复制密钥,您必须单击密钥右侧的眼睛图标,PubNub 将使所有字符可见。

在第二章,在英特尔 Galileo Gen 2 上使用 Python中,我们安装了pip安装程序,以便在板子上运行的 Yocto Linux 中轻松安装额外的 Python 2.7.3 软件包。现在,我们将使用pip安装程序安装 PubNub Python SDK 3.7.6。我们只需在 SSH 终端中运行以下命令即可安装该软件包。请注意,安装可能需要几分钟时间。

pip install pubnub

输出的最后几行将指示pubnub软件包已成功安装。不要担心与构建 wheel 相关的错误消息和不安全平台警告。

  Downloading pubnub-3.7.6.tar.gz
Collecting pycrypto>=2.6.1 (from pubnub)
  Downloading pycrypto-2.6.1.tar.gz (446kB)
    100% |################################| 446kB 25kB/s 
Requirement already satisfied (use --upgrade to upgrade): requests>=2.4.0 in /usr/lib/python2.7/site-packages (from pubnub)
Installing collected packages: pycrypto, pubnub
  Running setup.py install for pycrypto
Installing collected packages: pycrypto, pubnub
  Running setup.py install for pycrypto
Running setup.py install for pubnub
Successfully installed pubnub-3.7.6 pycrypto-2.6.1

我们将使用我们在上一章中编写的代码,当时我们从传感器读取温度和湿度值,我们在 OLED 矩阵中打印了这些值,并通过旋转伺服电机的轴来显示以华氏度表示的测量温度。示例代码文件为iot_python_chapter_08_03.py。我们将以此代码为基础,添加新功能,使我们能够执行以下操作,使用 PubNub 消息发送到特定通道的任何具有网络浏览器的设备:

  • 通过旋转伺服电机的轴来显示作为消息一部分接收到的华氏温度值。

  • 在 OLED 矩阵的底部显示作为消息一部分接收到的文本行。

我们将使用最近安装的 pubnub 模块来订阅特定频道,并在接收到该频道上的消息时运行代码。我们将创建一个 MessageChannel 类来表示通信通道,配置 PubNub 订阅并声明当某些事件被触发时要执行的回调代码。示例代码文件为 iot_python_chapter_09_02.py。请记住,我们使用代码文件 iot_python_chapter_08_03.py 作为基线,因此,我们将把类添加到现有代码文件中,并创建一个新的 Python 文件。不要忘记将 __init__ 方法中分配给 publish_keysubscribe_key 局部变量的字符串替换为从之前解释的 PubNub 密钥生成过程中检索到的值。

import time
from pubnub import Pubnub

class MessageChannel:
    command_key = "command"

    def __init__(self, channel, temperature_servo, oled):
        self.temperature_servo = temperature_servo
        self.oled = oled
        self.channel = channel
        # Publish key is the one that usually starts with the "pub-c-" prefix
        # Do not forget to replace the string with your publish key
        publish_key = "pub-c-xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
        # Subscribe key is the one that usually starts with the "sub-c" prefix
        # Do not forget to replace the string with your subscribe key
        subscribe_key = "sub-c-xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
        self.pubnub = Pubnub(publish_key=publish_key, subscribe_key=subscribe_key)
        self.pubnub.subscribe(channels=self.channel,
                              callback=self.callback,
                              error=self.callback,
                              connect=self.connect,
                              reconnect=self.reconnect,
                              disconnect=self.disconnect)

    def callback(self, message, channel):
        if channel == self.channel:
            if self.__class__.command_key in message:
                if message[self.__class__.command_key] == "print_temperature_fahrenheit":
                    self.temperature_servo.print_temperature(message["temperature_fahrenheit"])
                elif message[self.__class__.command_key] == "print_information_message":
                    self.oled.print_line(11, message["text"])
            print("I've received the following message: {0}".format(message))

    def error(self, message):
        print("Error: " + str(message))

    def connect(self, message):
        print("Connected to the {0} channel".
              format(self.channel))
        print(self.pubnub.publish(
            channel=self.channel,
            message="Listening to messages in the Intel Galileo Gen 2 board"))

    def reconnect(self, message):
        print("Reconnected to the {0} channel".
              format(self.channel))

    def disconnect(self, message):
        print("Disconnected from the {0} channel".
              Format(self.channel))

MessageChannel 类声明了 command_key 类属性,它定义了代码将理解为何种命令的关键字符串。每当收到包含指定关键字符串的消息时,我们知道与该键关联的字典中的值将指示消息希望代码在板上处理的命令。每个命令都需要额外的键值对,以提供执行命令所需的信息。

我们必须指定 PubNub 频道名称,channel 中的 TemperatureServo 实例,Oled 实例所需的 temperature_servooled 参数。构造函数,即 __init__ 方法,将接收到的参数保存在具有相同名称的三个属性中。channel 参数指定了我们将订阅以监听发送到该频道的消息的 PubNub 频道。我们还将向该频道发布消息,因此,我们将成为该频道的订阅者和发布者。

小贴士

在这种情况下,我们只会订阅一个频道。然而,非常重要的一点是,我们并不仅限于订阅单个频道,我们可能订阅多个频道。

然后,构造函数声明了两个局部变量:publish_keysubscribe_key。这些局部变量保存了我们使用 PubNub 管理门户生成的发布和订阅密钥。然后,代码使用 publish_keysubscribe_key 作为参数创建一个新的 Pubnub 实例,并将新实例的引用保存在 pubnub 属性中。最后,代码调用新实例的 subscribe 方法来订阅保存在 channel 属性中的频道上的数据。在底层,subscribe 方法使客户端创建一个到包含 MQTT 代理的 PubNub 网络的开放 TCP 套接字,并开始监听指定频道上的消息。对这个方法的调用指定了在 MessageChannel 类中声明的许多方法,以下为命名参数:

  • callback:指定在从通道接收到新消息时将被调用的函数

  • error:指定在错误事件上将被调用的函数

  • connect:指定当与 PubNub 云成功建立连接时将被调用的函数

  • reconnect:指定当与 PubNub 云成功重新连接完成后将被调用的函数

  • disconnect:指定当客户端从 PubNub 云断开连接时将被调用的函数

这样,每当之前列举的事件之一发生时,指定的方法将被执行。callback方法接收两个参数:messagechannel。首先,该方法检查接收到的channel是否与channel属性中的值匹配。在这种情况下,每当callback方法执行时,channel参数中的值将始终与channel属性中的值匹配,因为我们刚刚订阅了一个通道。然而,如果我们订阅了多个通道,则始终有必要检查消息是在哪个通道中发送的,以及我们在哪个通道接收消息。

然后,代码检查command_key类属性是否包含在message字典中。如果表达式评估为True,则表示消息包含我们必须处理的命令。然而,在我们能够处理该命令之前,我们必须检查是哪个命令,因此,有必要检索与command_key类属性等效的键的值。当值是以下两个命令中的任何一个时,代码能够执行代码:

  • print_temperature_fahrenheit:该命令必须在temperature_fahrenheit键的值中指定以华氏度表示的温度值。代码使用从字典中检索到的温度值作为参数调用self.temperature_servo.print_temperature方法。这样,代码就根据消息中包含该命令指定的温度值移动伺服电机的轴。

  • print_information_message:该命令必须在print_information_message键的值中指定要在 OLED 矩阵底部显示的文本行。代码使用self.oled.print_line方法,并带有11和从字典中检索到的文本值作为参数调用。这样,代码就在 OLED 矩阵的底部显示了包含该命令的消息中接收到的文本。

无论消息是否包含有效的命令,该方法都会在控制台输出中打印它接收到的原始消息。

connect 方法打印一条消息,表明已与通道建立了连接。然后,该方法打印调用 self.pubnub.publish 方法的结果,该方法在 self.channel 保存的通道名称中发布消息:"Listening to messages in the Intel Galileo Gen 2 board"。在这种情况下,对该方法的调用是同步执行的。我们将在下一个示例中为此方法使用异步执行。

在此时,我们已订阅此通道,因此,我们将接收到之前发布的消息,并且回调方法将使用此消息作为参数执行。然而,由于消息不包含标识命令的密钥,回调方法中的代码将仅显示接收到的消息,而不会处理之前分析过的任何命令。

MessageChannel 类中声明的其他方法只是将事件发生的信息显示到控制台输出。

现在,我们将使用之前编写的 MessageChannel 类来创建一个新版本的 __main__ 方法,该方法使用 PubNub 云接收和处理命令。新版本在环境温度变化时不会旋转伺服电机的轴,相反,它将在接收到来自连接到 PubNub 云的任何设备的适当命令时执行此操作。以下行显示了 __main__ 方法的新版本。示例的代码文件为 iot_python_chapter_09_02.py

if __name__ == "__main__":
    temperature_and_humidity_sensor = \
        TemperatureAndHumiditySensor(0)
    oled = TemperatureAndHumidityOled(0)
    temperature_servo = TemperatureServo(3)
 message_channel = MessageChannel("temperature", temperature_servo, oled)
    while True:
        temperature_and_humidity_sensor.\
            measure_temperature_and_humidity()
        oled.print_temperature(
            temperature_and_humidity_sensor.temperature_fahrenheit,
            temperature_and_humidity_sensor.temperature_celsius)
        oled.print_humidity(
            temperature_and_humidity_sensor.humidity)
        print("Ambient temperature in degrees Celsius: {0}".
              format(temperature_and_humidity_sensor.temperature_celsius))
        print("Ambient temperature in degrees Fahrenheit: {0}".
              format(temperature_and_humidity_sensor.temperature_fahrenheit))
        print("Ambient humidity: {0}".
              format(temperature_and_humidity_sensor.humidity))
        # Sleep 10 seconds (10000 milliseconds)
        time.sleep(10)

突出的行创建了一个之前编写的 MessageChannel 类的实例,参数为 "temperature"temperature_servooled。构造函数将订阅 PubNub 云中的 temperature 通道,因此,我们必须向此通道发送消息,以便发送代码将异步执行的命令。循环将读取传感器的值并将值打印到控制台,就像代码的先前版本一样,因此,我们将在循环中运行代码,同时我们也将有代码在 PubNub 云的 temperature 通道中监听消息。我们将在稍后开始示例,因为我们想在板上运行代码之前,在 PubNub 调试控制台中订阅通道。

通过 PubNub 云发布带有命令的消息

现在,我们将利用 PubNub 控制台向 temperature 通道发送带有命令的消息,并使板上的 Python 代码处理这些命令。如果你已经从 PubNub 登出,请重新登录并点击 Admin Portal 中的 Temperature Control 面板。PubNub 将显示 Demo Keyset 面板。

点击 Demo Keyset 面板,PubNub 将显示发布、订阅和密钥。这样,我们选择我们想要用于我们的 PubNub 应用的密钥集。

点击屏幕左侧侧边栏上的 调试控制台。PubNub 将为默认通道创建一个客户端,并使用我们在上一步中选择的密钥订阅此通道。我们想订阅 temperature 通道,因此,在包含 添加客户端 按钮的面板中的 默认通道 文本框中输入 temperature。然后,点击 添加客户端,PubNub 将添加一个带有随机客户端名称的新的面板,第二行是通道名称 temperature。PubNub 使客户端订阅此通道,我们就能接收发布到此通道的消息,并向此通道发送消息。以下图片显示了名为 Client-ot7pi 的生成客户端的面板,已订阅 temperature 通道。注意,当您按照解释的步骤操作时,客户端名称将不同。

https://github.com/OpenDocCN/freelearn-python-zh/raw/master/docs/iot-py/img/B05042_09_08.jpg

客户端面板显示了 PubNub 订阅客户端到通道时生成的输出。PubNub 为每个命令返回一个格式化的响应。在这种情况下,它表示状态等于 Subscribed,通道名称为 temperature

[1,"Subscribed","temperature"]

现在,是时候在英特尔 Galileo Gen 2 板上运行示例了。以下行将在 SSH 控制台中启动示例:

python iot_python_chapter_09_02.py

运行示例后,转到您正在使用 PubNub 调试控制台的 Web 浏览器。您将看到以下消息在之前创建的客户端中列出:

"Listening to messages in the Intel Galileo Gen 2 board"

在板上运行的 Python 代码发布了这条消息,具体来说,是 MessageChannel 类中的 connect 方法在应用程序与 PubNub 云建立连接后发送了这条消息。下面的图片显示了之前创建的客户端中列出的消息。注意,文本左侧的图标表示这是一条消息。另一条消息是一个调试消息,包含了订阅通道的结果。

https://github.com/OpenDocCN/freelearn-python-zh/raw/master/docs/iot-py/img/B05042_09_09.jpg

在客户端面板的底部,您将看到以下文本和位于右侧的 发送 按钮:

{"text":"Enter Message Here"}

现在,我们将用一条消息替换之前显示的文本。输入以下 JSON 代码并点击 发送

{"command":"print_temperature_fahrenheit", "temperature_fahrenheit": 50 }

小贴士

在其中输入消息的文本编辑器在某些浏览器中存在一些问题。因此,使用您喜欢的文本编辑器输入 JSON 代码,复制它,然后将其粘贴以替换消息文本中默认包含的文本是方便的。

点击发送后,客户端日志中会出现以下几行。第一行是一个包含发布消息结果的调试消息,并表明消息已被发送。格式化响应包括一个数字(1条消息)、状态(Sent)和时间戳。第二行是到达通道的消息,因为我们订阅了temperature通道,也就是说,我们也收到了我们发送的消息。

[1,"Sent","14594756860875537"]
{
  "command": "print_temperature_fahrenheit",
  "temperature_fahrenheit": 50
}

以下图片展示了点击发送按钮后 PubNub 客户端的消息和调试消息日志:

https://github.com/OpenDocCN/freelearn-python-zh/raw/master/docs/iot-py/img/B05042_09_10.jpg

在发布上一条消息后,你将在 Intel Galileo Gen 2 板的 SSH 控制台中看到以下输出。你会注意到伺服电机的轴旋转到 50 度。

I've received the following message: {u'command': u'print_temperature_fahrenheit', u'temperature_fahrenheit': 50}

现在,输入以下 JSON 代码并点击发送

{"command":"print_information_message", "text": "Client ready"}

点击发送后,客户端日志中会出现以下几行。第一行是一个调试消息,包含之前解释过的格式化响应,显示了发布消息的结果,并表明消息已被发送。格式化响应包括一个数字(1条消息)、状态(Sent)和时间戳。第二行是到达通道的消息,因为我们订阅了temperature通道,也就是说,我们也收到了我们发送的消息。

 [1,"Sent","14594794434885921"]
 {
  "command": "print_information_message",
  "text": "Client ready"
}

以下图片展示了点击发送按钮后 PubNub 客户端的消息和调试消息日志。

https://github.com/OpenDocCN/freelearn-python-zh/raw/master/docs/iot-py/img/B05042_09_11.jpg

发布上一条消息后,你将在 Intel Galileo Gen 2 板的 SSH 控制台中看到以下输出。你将在 OLED 矩阵的底部看到以下文本:Client ready

I've received the following message: {u'text': u'Client ready', u'command': u'print_information_message'}

当我们使用命令发布了两条消息时,我们肯定注意到了一个问题。我们不知道在运行在物联网设备上的代码(即 Intel Galileo Gen 2 板)中,命令是否被处理。我们知道板子已经开始监听温度通道的消息,但在命令处理完毕后,我们没有从物联网设备收到任何类型的响应。

处理双向通信

我们可以轻松地添加几行代码,在接收消息的同一条通道上发布消息,以指示命令已成功处理。我们将使用之前的示例作为基准,并创建MessageChannel类的新版本。代码文件是iot_python_chapter_09_02.py。别忘了在__init__方法中将分配给publish_keysubscribe_key局部变量的字符串替换为之前解释过的 PubNub 密钥生成过程中检索到的值。以下几行展示了发布消息后命令已成功处理的MessageChannel类的新版本。示例代码文件是iot_python_chapter_09_03.py

import time
from pubnub import Pubnub

class MessageChannel:
    command_key = "command"
 successfully_processed_command_key = "successfully_processed_command"

    def __init__(self, channel, temperature_servo, oled):
        self.temperature_servo = temperature_servo
        self.oled = oled
        self.channel = channel
        # Do not forget to replace the string with your publish key
        publish_key = "pub-c-xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
        # Subscribe key is the one that usually starts with the "sub-c" prefix
        # Do not forget to replace the string with your subscribe key
        subscribe_key = "sub-c-xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
        self.pubnub = Pubnub(publish_key=publish_key, subscribe_key=subscribe_key)
        self.pubnub.subscribe(channels=self.channel,
                              callback=self.callback,
                              error=self.callback,
                              connect=self.connect,
                              reconnect=self.reconnect,
                              disconnect=self.disconnect)

 def callback_response_message(self, message):
 print("I've received the following response from PubNub cloud: {0}".format(message))

 def error_response_message(self, message):
 print("There was an error when working with the PubNub cloud: {0}".format(message))

 def publish_response_message(self, message):
 response_message = {
 self.__class__.successfully_processed_command_key:
 message[self.__class__.command_key]}
 self.pubnub.publish(
 channel=self.channel,
 message=response_message,
 callback=self.callback_response_message,
 error=self.error_response_message)

    def callback(self, message, channel):
        if channel == self.channel:
            print("I've received the following message: {0}".format(message))
            if self.__class__.command_key in message:
                if message[self.__class__.command_key] == "print_temperature_fahrenheit":
                    self.temperature_servo.print_temperature(message["temperature_fahrenheit"])
 self.publish_response_message(message)
                elif message[self.__class__.command_key] == "print_information_message":
                    self.oled.print_line(11, message["text"])
 self.publish_response_message(message)

    def error(self, message):
        print("Error: " + str(message))

    def connect(self, message):
        print("Connected to the {0} channel".
              format(self.channel))
        print(self.pubnub.publish(
            channel=self.channel,
            message="Listening to messages in the Intel Galileo Gen 2 board"))

    def reconnect(self, message):
        print("Reconnected to the {0} channel".
              format(self.channel))

    def disconnect(self, message):
        print("Disconnected from the {0} channel".
              format(self.channel))

之前代码中突出显示的MessageChannel类新版本的代码行显示了我们对代码所做的更改。首先,代码声明了successfully_processed_command_key类属性,该属性定义了代码将在发布到通道的响应消息中使用的关键字符串,作为成功处理的命令键。每次我们发布包含指定键字符串的消息时,我们知道与该键关联的字典中的值将指示板已成功处理的命令。

代码声明了以下三个新方法:

  • callback_response_message:此方法将用作在成功处理的命令响应消息发布到通道时执行的回调。该方法仅打印 PubNub 在通道中成功发布消息时返回的格式化响应。在这种情况下,message参数不包含已发布的原始消息,而是包含格式化响应。我们使用message作为参数名称以保持与 PubNub API 的一致性。

  • error_response_message:此方法将用作在尝试将成功处理的命令响应消息发布到通道时发生错误时执行的回调。该方法仅打印 PubNub 在通道中未成功发布消息时返回的错误消息。

  • publish_response_message:此方法接收包含在message参数中成功处理的命令的消息。代码创建一个response_message字典,其中successfully_processed_command_key类属性作为键,消息字典中command_key类属性指定的键的值作为值。然后,代码调用self.pubnub.publish方法将response_message字典发布到存储在channel属性中的通道。对此方法的调用指定self.callback_response_message作为在消息成功发布时执行的回调,以及self.error_response_message作为在发布过程中发生错误时执行的回调。当我们指定回调时,发布方法以异步执行方式工作,因此执行是非阻塞的。消息的发布和指定的回调将在不同的线程中运行。

现在,在MessageChannel类中定义的callback方法向publish_response_message方法添加了一个调用,该调用以包含已成功处理命令的消息(message)作为参数。正如之前解释的那样,publish_response_message方法是非阻塞的,并且将在另一个线程中发布成功处理的消息时立即返回。

现在,是时候在 Intel Galileo Gen 2 板上运行示例了。以下行将在 SSH 控制台中启动示例:

python iot_python_chapter_09_03.py

运行示例后,转到您与 PubNub 调试控制台一起工作的 Web 浏览器。您将在之前创建的客户中看到以下消息:

"Listening to messages in the Intel Galileo Gen 2 board"

输入以下 JSON 代码并点击发送

{"command":"print_temperature_fahrenheit", "temperature_fahrenheit": 90 }

点击发送后,客户端日志中会出现以下行。最后一条消息是由板发布到频道的,表示 print_temperature_fahrenheit 命令已成功处理。

[1,"Sent","14595406989121047"]
{
  "command": "print_temperature_fahrenheit",
  "temperature_fahrenheit": 90
}
{
  "successfully_processed_command": "print_temperature_fahrenheit"
}

以下图片展示了点击发送按钮后 PubNub 客户端的消息和调试消息日志:

https://github.com/OpenDocCN/freelearn-python-zh/raw/master/docs/iot-py/img/B05042_09_12.jpg

发布之前的消息后,您将在 Intel Galileo Gen 2 板的 SSH 控制台中看到以下输出。您会注意到伺服电机的轴旋转到 90 度。该板还接收到了成功处理的命令消息,因为它订阅了发布消息的频道。

I've received the following message: {u'command': u'print_temperature_fahrenheit', u'temperature_fahrenheit': 90}
I've received the following response from PubNub cloud: [1, u'Sent', u'14595422426124592']
I've received the following message: {u'successfully_processed_command': u'print_temperature_fahrenheit'}

现在,输入以下 JSON 代码并点击发送

{"command":"print_information_message", "text": "2nd message"}

点击发送后,客户端日志中会出现以下行。最后一条消息是由板发布到频道的,表示 print_information_message 命令已成功处理。

[1,"Sent","14595434708640961"]
{
  "command": "print_information_message",
  "text": "2nd message"
}
{
  "successfully_processed_command": "print_information_message"
}

以下图片展示了点击发送按钮后 PubNub 客户端的消息和调试消息日志。

https://github.com/OpenDocCN/freelearn-python-zh/raw/master/docs/iot-py/img/B05042_09_13.jpg

发布之前的消息后,您将在 Intel Galileo Gen 2 板的 SSH 控制台中看到以下输出。您将在 OLED 矩阵的底部看到以下文本:2nd message。该板还接收到了成功处理的命令消息,因为它订阅了发布消息的频道。

I've received the following message: {u'text': u'2nd message', u'command': u'print_information_message'}
2nd message
I've received the following response from PubNub cloud: [1, u'Sent', u'14595434710438777']
I've received the following message: {u'successfully_processed_command': u'print_information_message'}

我们可以使用 PubNub 提供的不同 SDK 订阅和发布到频道。我们还可以通过向频道发布消息并处理它们,使不同的 IoT 设备相互通信。在这种情况下,我们只创建了一些命令,并没有添加关于必须处理命令的设备或生成特定消息的设备的详细信息。更复杂的 API 需要包含更多信息和安全性的命令。

使用 Python PubNub 客户端向云端发布消息

到目前为止,我们一直在使用 PubNub 调试控制台向 temperature 频道发布消息,并让 Intel Galileo Gen 2 板上的 Python 代码处理它们。现在,我们将编写一个 Python 客户端,它将向 temperature 频道发布消息。这样,我们将能够设计能够与 IoT 设备通信的应用程序,发布者端和订阅者设备端都使用 Python 代码。

我们可以在另一个 Intel Galileo Gen 2 板上或在安装了 Python 2.7.x 的任何设备上运行 Python 客户端。此外,代码将以 Python 3.x 运行。例如,我们可以在我们的计算机上运行 Python 客户端。我们只需确保在板上的 Yocto Linux 运行的 Python 版本中安装了之前用 pip 安装的 pubnub 模块。

我们将创建一个 Client 类来表示 PubNub 客户端,配置 PubNub 订阅,使其能够轻松发布带有命令和所需值的消息,并声明当某些事件触发时要执行的回调代码。示例代码文件为 iot_python_chapter_09_04.py。不要忘记将 __init__ 方法中分配给 publish_keysubscribe_key 局部变量的字符串替换为从之前解释的 PubNub 密钥生成过程中检索到的值。以下行显示了 Client 类的代码:

import time
from pubnub import Pubnub

class Client:
    command_key = "command"

    def __init__(self, channel):
        self.channel = channel
        # Publish key is the one that usually starts with the "pub-c-" prefix
        publish_key = "pub-c-xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
        # Subscribe key is the one that usually starts with the "sub-c" prefix
        # Do not forget to replace the string with your subscribe key
        subscribe_key = "sub-c-xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
        self.pubnub = Pubnub(publish_key=publish_key, subscribe_key=subscribe_key)
        self.pubnub.subscribe(channels=self.channel,
                              callback=self.callback,
                              error=self.callback,
                              connect=self.connect,
                              reconnect=self.reconnect,
                              disconnect=self.disconnect)

    def callback_command_message(self, message):
        print("I've received the following response from PubNub cloud: {0}".format(message))

    def error_command_message(self, message):
        print("There was an error when working with the PubNub cloud: {0}".format(message))

 def publish_command(self, command_name, key, value):
 command_message = {
 self.__class__.command_key: command_name,
 key: value}
 self.pubnub.publish(
 channel=self.channel,
 message=command_message,
 callback=self.callback_command_message,
 error=self.error_command_message)

    def callback(self, message, channel):
        if channel == self.channel:
            print("I've received the following message: {0}".format(message))

    def error(self, message):
        print("Error: " + str(message))

    def connect(self, message):
        print("Connected to the {0} channel".
              format(self.channel))
        print(self.pubnub.publish(
            channel=self.channel,
            message="Listening to messages in the PubNub Python Client"))

    def reconnect(self, message):
        print("Reconnected to the {0} channel".
              format(self.channel))

    def disconnect(self, message):
        print("Disconnected from the {0} channel".
              format(self.channel))

Client 类声明了 command_key 类属性,该属性定义了代码在消息中理解为何种命令的键字符串。我们的主要目标是构建并发布到指定频道的命令消息。我们必须在 channel 所需参数中指定 PubNub 频道名称。构造函数,即 __init__ 方法,将接收到的参数保存在具有相同名称的属性中。我们将成为此频道的订阅者和发布者。

然后,构造函数声明了两个局部变量:publish_keysubscribe_key。这些局部变量保存了我们使用 PubNub 管理门户生成的发布和订阅密钥。然后,代码使用 publish_keysubscribe_key 作为参数创建一个新的 Pubnub 实例,并将新实例的引用保存在 pubnub 属性中。最后,代码调用新实例的 subscribe 方法来订阅保存在 channel 属性中的频道上的数据。对这个方法的调用指定了许多在 Client 类中声明的函数,就像我们在之前的示例中所做的那样。

publish_command 方法接收一个命令名称、键和值,这些键和值提供了执行 command_namekeyvalue 所需的必要信息。在这种情况下,我们没有将命令针对特定的物联网设备,而是所有订阅该频道并在我们之前的示例中运行代码的设备都将处理我们发布的命令。我们可以使用此代码作为基准,以处理更复杂的示例,在这些示例中,我们必须生成针对特定物联网设备的命令。显然,提高安全性也是必要的。

该方法创建一个字典,并将其保存在command_message局部变量中。command_key类属性是字典的第一个键,而command_name作为参数接收的值,是构成第一个键值对的值。然后,代码调用self.pubnub.publish方法,将command_message字典发布到保存在channel属性中的频道。对这个方法的调用指定了self.callback_command_message作为在消息成功发布时执行的回调,以及self.error_command_message作为在发布过程中发生错误时执行的回调。正如我们之前的例子中发生的那样,当我们指定回调时,publish方法将以异步执行的方式工作。

现在,我们将使用之前编写的Client类来编写一个__main__方法,该方法使用 PubNub 云发布两个命令,我们的板子将处理这些命令。以下行显示了__main__方法的代码。示例的代码文件是iot_python_chapter_09_04.py

if __name__ == "__main__":
    client = Client("temperature")
    client.publish_command(
        "print_temperature_fahrenheit",
        "temperature_fahrenheit",
        45)
    client.publish_command(
        "print_information_message",
        "text",
        "Python IoT"
    )
    # Sleep 60 seconds (60000 milliseconds)
    time.sleep(60)

__main__方法中的代码非常容易理解。代码使用"temperature"作为参数创建Client类的一个实例,使其成为 PubNub 云中该频道的订阅者和发布者。代码将新实例保存在client局部变量中。

代码调用publish_command方法,并传入必要的参数来构建和发布print_temperature_fahrenheit命令,该命令的温度值为45。该方法将以异步执行的方式发布命令。然后,代码再次调用publish_command方法,并传入必要的参数来构建和发布print_information_message命令,该命令的文本值为"Python IoT"。该方法将以异步执行的方式发布第二个命令。

最后,代码暂停 1 分钟(60 秒),以便异步执行成功发布命令。在Client类中定义的不同回调将在不同事件触发时执行。由于我们也订阅了该频道,我们还将收到在temperature频道发布的消息。

保持我们在之前的例子中执行的 Python 代码在板上运行。我们希望板子处理我们的命令。此外,保持您正在使用的 Web 浏览器和 PubNub 调试控制台打开,因为我们还希望看到日志中的所有消息。

以下行将在任何您想要用作客户端的计算机或设备上启动 Python 客户端的示例。如果您想使用同一块板作为客户端,可以在另一个 SSH 终端中运行代码。

python iot_python_chapter_09_04.py

运行示例后,您将在运行 Python 客户端的 Python 控制台中看到以下输出,即iot_python_chapter_09_04.py Python 脚本。

Connected to the temperature channel
I've received the following response from PubNub cloud: [1, u'Sent', u'14596508980494876']
I've received the following response from PubNub cloud: [1, u'Sent', u'14596508980505581']
[1, u'Sent', u'14596508982165140']
I've received the following message: {u'text': u'Python IoT', u'command': u'print_information_message'}
I've received the following message: {u'command': u'print_temperature_fahrenheit', u'temperature_fahrenheit': 45}
I've received the following message: Listening to messages in the PubNub Python Client
I've received the following message: {u'successfully_processed_command': u'print_information_message'}
I've received the following message: {u'successfully_processed_command': u'print_temperature_fahrenheit'}

代码使用了 PubNub Python SDK 构建,并在 temperature 通道中发布了以下两个命令消息:

{"command":"print_temperature_fahrenheit", "temperature_fahrenheit": "45"}
{"command":"print_information_message", "text": "Python IoT"}

由于我们也订阅了温度通道,我们以异步方式接收我们发送的消息。然后,我们收到了成功处理的两个命令消息。板子已经处理了命令,并将消息发布到了 temperature 通道。

在运行示例后,转到您正在使用 PubNub 调试控制台工作的网络浏览器。您将看到以下消息列在之前创建的客户端中:

[1,"Subscribed","temperature"]
"Listening to messages in the Intel Galileo Gen 2 board"
{
  "text": "Python IoT",
  "command": "print_information_message"
}
{
  "command": "print_temperature_fahrenheit",
  "temperature_fahrenheit": 45
}
"Listening to messages in the PubNub Python Client"
{
  "successfully_processed_command": "print_information_message"
}
{
  "successfully_processed_command": "print_temperature_fahrenheit"
}

以下图片显示了在运行上一个示例后 PubNub 客户端日志中显示的最后一条消息:

https://github.com/OpenDocCN/freelearn-python-zh/raw/master/docs/iot-py/img/B05042_09_14.jpg

您将在 OLED 矩阵的底部看到以下文本:Python IoT。此外,伺服电机的轴将旋转到 45 度。

提示

我们可以使用不同编程语言中可用的 PubNub SDK 来创建应用程序和应用程序,这些应用程序可以在 PubNub 云中发布和接收消息,并与物联网设备交互。在这种情况下,我们与 Python SDK 合作创建了一个发布命令的客户端。可以创建发布命令的移动应用程序,并轻松构建一个可以与我们的物联网设备交互的应用程序。

使用 Mosquitto 和 Eclipse Paho 的 MQTT

Mosquitto 是一个开源的消息代理,实现了 MQTT 协议的 3.1 和 3.1.1 版本,因此,允许我们使用发布/订阅模型来处理消息。Mosquitto 是 iot.eclipse.org 项目,并提供了 Eclipse 公共项目 (EPL)/EDL 许可证。以下是为 Mosquitto 提供的网页:mosquitto.org

Eclipse Paho 项目提供了一个开源的 MQTT 客户端实现。该项目包括一个 Python 客户端,也称为 Paho Python 客户端或 Eclipse Paho MQTT Python 客户端库。这个 Python 客户端是由 Mosquitto 项目贡献的,最初是 Mosquitto Python 客户端。以下是为 Eclipse Paho 项目提供的网页:www.eclipse.org/paho。以下是为 Eclipse Paho MQTT Python 客户端库提供的网页,即 paho-mqtt 模块:pypi.python.org/pypi/paho-mqtt/1.1

在 第二章,在英特尔 Galileo Gen 2 上使用 Python,我们安装了 pip 安装程序,以便在板子上运行的 Yocto Linux 中轻松安装额外的 Python 2.7.3 软件包。现在,我们将使用 pip 安装程序安装 paho-mqtt 1.1。我们只需要在 SSH 终端中运行以下命令来安装该软件包。

pip install paho-mqtt

输出的最后几行将指示 paho-mqtt 包已成功安装。不要担心与构建 wheel 相关的错误消息和不安全平台警告。

Collecting paho-mqtt
  Downloading paho-mqtt-1.1.tar.gz (41kB)
    100% |################################| 45kB 147kB/s
Installing collected packages: paho-mqtt
  Running setup.py install for paho-mqtt
Successfully installed paho-mqtt-1.1

小贴士

Eclipse 允许我们使用一个公开可访问的沙盒服务器来运行 Eclipse IoT 项目,该服务器位于 iot.eclipse.org,端口号为 1883。在接下来的示例中,我们将使用这个沙盒服务器作为我们的 Mosquitto 消息代理。这样,我们就不需要设置一个 Mosquitto 消息代理来测试示例并学习如何使用 Paho Python 客户端。然而,在实际应用中,我们应该设置一个 Mosquitto 消息代理并用于我们的项目。

当我们从传感器读取温度和湿度值时,我们在上一章中编写的代码,我们将值打印在 OLED 矩阵上,并旋转伺服电机的轴来显示以华氏度表示的测量温度。该示例的代码文件为 iot_python_chapter_08_03.py。我们将使用此代码作为基准,添加我们在与 PubNub 云一起工作时添加的相同功能。然而,在这种情况下,我们将使用 Paho Python 客户端和公开可访问的沙盒服务器,该服务器为我们提供了一个 Mosquitto 消息代理。我们将能够执行以下操作,通过将 MQTT 消息发送到特定主题,从任何可以发布 MQTT 消息到我们订阅的主题的设备:

  • 旋转伺服电机的轴以显示作为消息一部分接收到的华氏度温度值

  • 在 OLED 矩阵的底部显示作为消息一部分接收到的文本行

小贴士

Paho Python 客户端使用主题名称而不是通道。你可以将主题视为一个通道。

我们将使用最近安装的 paho-mqtt 模块来订阅特定主题,并在接收到主题中的消息时运行代码。我们将创建一个 MessageTopic 类来表示通信主题,配置 MQTT 客户端、客户端的订阅以及声明当某些事件触发时要执行的回调代码。该示例的代码文件为 iot_python_chapter_09_05.py。请记住,我们使用代码文件 iot_python_chapter_08_03.py 作为基准,因此,我们将向该文件中现有的代码添加类,并创建一个新的 Python 文件。不要忘记将分配给 topic 类属性的字符串替换为你的唯一主题名称。由于我们使用的 Mosquitto 代理是公开的,你应该使用一个唯一主题,以确保你只接收你发布的消息。

import time
import paho.mqtt.client as mqtt
import json

class MessageTopic:
    command_key = "command"
    successfully_processed_command_key = "successfully_processed_command"
    # Replace with your own topic name
    topic = "iot-python-gaston-hillar/temperature"
    active_instance = None

    def __init__(self, temperature_servo, oled):
        self.temperature_servo = temperature_servo
        self.oled = oled
        self.client = mqtt.Client()
        self.client.on_connect = MessageTopic.on_connect
        self.client.on_message = MessageTopic.on_message
        self.client.connect(host="iot.eclipse.org",
                            port=1883,
                            keepalive=60)
        MessageTopic.active_instance = self

    def loop(self):
        self.client.loop()

    @staticmethod
    def on_connect(client, userdata, flags, rc):
        print("Connected to the {0} topic".
              format(MessageTopic.topic))
        subscribe_result = client.subscribe(MessageTopic.topic)
        publish_result_1 = client.publish(
            topic=MessageTopic.topic,
            payload="Listening to messages in the Intel Galileo Gen 2 board")

    @staticmethod
    def on_message(client, userdata, msg):
        if msg.topic == MessageTopic.topic:
            print("I've received the following message: {0}".format(str(msg.payload)))
            try:
                message_dictionary = json.loads(msg.payload)
                if MessageTopic.command_key in message_dictionary:
                    if message_dictionary[MessageTopic.command_key] == "print_temperature_fahrenheit":
                        MessageTopic.active_instance.temperature_servo.print_temperature(
                            message_dictionary["temperature_fahrenheit"])
                        MessageTopic.active_instance.publish_response_message(
                            message_dictionary)
                    elif message_dictionary[MessageTopic.command_key] == "print_information_message":
                        MessageTopic.active_instance.oled.print_line(
                            11, message_dictionary["text"])
                        MessageTopic.active_instance.publish_response_message(message_dictionary)
            except ValueError:
                # msg is not a dictionary
                # No JSON object could be decoded
                pass

    def publish_response_message(self, message):
        response_message = json.dumps({
            self.__class__.successfully_processed_command_key:
                message[self.__class__.command_key]})
        result = self.client.publish(topic=self.__class__.topic,
                                payload=response_message)
        return result

MessageTopic类声明了command_key类属性,该属性定义了键字符串,该字符串定义了代码将理解为什么命令。每当收到包含指定键字符串的消息时,我们知道与该键关联的字典中的值将指示消息希望代码在板子上处理的命令。在这种情况下,我们不会以字典的形式接收消息,因此当它们不是字符串时,有必要将它们从字符串转换为字典。

代码声明了successfully_processed_command_key类属性,该属性定义了键字符串,该字符串定义了代码将在发布到主题的消息中使用什么作为成功处理的命令键。每当发布包含指定键字符串的消息时,我们知道与该键关联的字典中的值将指示板已成功处理的命令。

我们必须在temperature_servooled必需参数中指定TemperatureServo实例和Oled实例。构造函数,即__init__方法,将接收到的参数保存到具有相同名称的两个属性中。topic类属性参数指定了我们将订阅以监听其他设备发送到该主题的消息的 Mosquitto 主题。我们还将向该主题发布消息,因此我们将成为该通道的订阅者和发布者。

然后,构造函数创建了一个mqtt.Client类的实例,该实例代表一个 MQTT 客户端,我们将用它与 MQTT 代理进行通信。由于我们使用默认参数创建实例,我们将创建一个paho.mqtt.client.MQTTv31实例,我们将使用 MQTT 版本 3.1。

代码还保存了这个实例在active_instance类属性中的引用,因为我们必须在静态方法中访问该实例,我们将指定为不同事件触发的 MQTT 客户端的回调。

然后,代码将self.client.on_connect属性分配给on_connect静态方法,将self.client.on_message属性分配给on_message静态方法。静态方法不接收selfcls作为第一个参数,因此我们可以使用它们作为具有所需参数数量的回调。

最后,构造函数调用self.client.connect方法,并在参数中指定了公开可访问的 Eclipse IoT 项目在 iot.eclipse.org 的沙盒服务器,端口 1883。这样,代码要求 MQTT 客户端建立与指定 MQTT 代理的连接。如果您决定使用自己的 Mosquitto 代理,只需根据 Mosquitto 代理的配置更改hostport参数的值。connect方法以异步执行方式运行,因此它是一个非阻塞调用。

在成功连接到 MQTT 代理后,self.client.on_connect 属性中指定的回调函数将被执行,即标记为 @staticmethod 装饰器的 on_connect 静态方法。这个静态方法接收 client 参数中的 mqtt.Client 实例,该实例与 MQTT 代理建立了连接。代码使用 MessageTopic.topic 作为参数调用 client.subscribe 方法,以订阅由 topic 类属性指定的主题。

小贴士

在这种情况下,我们只会订阅一个主题。然而,了解我们不仅限于订阅单个主题,我们可能通过一次调用 subscribe 方法订阅多个主题,这一点非常重要。

最后,代码使用 MessageTopic.topic 作为 topic 参数,以及一个消息字符串作为 payload 参数调用 client.publish 方法。这样,我们将一个字符串消息 "Listening to messages in the Intel Galileo Gen 2 board" 发布到由 topic 类属性指定的主题。

每当我们订阅的主题中收到新消息时,self.client.on_message 属性中指定的回调函数将被执行,即标记为 @staticmethod 装饰器的 on_message 静态方法。这个静态方法接收 client 参数中的 mqtt.Client 实例,该实例与 MQTT 代理建立了连接,以及 msg 参数中的 mqtt.MQTTMessage 实例。mqtt.MQTTMessage 类描述了一个传入的消息。首先,静态方法检查 msg.topic 属性,该属性指示接收消息的主题,是否与 topic 类属性中的值匹配。在这种情况下,每当 on_message 方法执行时,msg.topic 中的值将始终与 topic 类属性中的值匹配,因为我们只订阅了一个主题。然而,如果我们订阅了多个主题,则始终有必要检查消息发送的主题以及我们接收消息的主题。

代码打印接收到的消息,即 msg.payload 属性。然后,代码将 json.loads 函数的结果反序列化 msg.payload 到一个 Python 对象,并将结果赋值给 message_dictionary 本地变量。如果 msg.payload 的内容不是 JSON,将捕获 ValueError 异常,并且方法中不再执行更多代码。如果 msg.payload 的内容是 JSON,我们将在 message_dictionary 本地变量中有一个字典。

然后,代码检查 command_key 类属性是否包含在 message_dictionary 字典中。如果表达式评估为 True,则表示将 JSON 消息转换为字典时包含了一个我们必须处理的命令。然而,在我们能够处理命令之前,我们必须检查是哪个命令,因此有必要检索与 command_key 类属性等效的键关联的值。代码能够在值是我们在之前示例中与 PubNub 云一起工作时使用的两个命令中的任何一个时运行特定的代码。

代码使用了具有对活动 MessageTopic 实例引用的 active_instance 类属性来根据要处理的命令调用 temperature_servooled 属性所需的必要方法。我们不得不将回调声明为静态方法,因此我们使用这个类属性来访问活动实例。

一旦命令被成功处理,代码就会调用存储在 active_instance 类属性中的 MessageTopic 实例的 publish_response_message 方法。该方法接收包含在 message 参数中的带有命令接收到的消息字典。该方法调用 json.dumps 函数将字典序列化为一个带有响应消息的 JSON 格式化字符串,该消息指示命令已成功处理。最后,代码使用 topic 类属性作为 topic 参数和 JSON 格式化字符串(response_message)在 payload 参数中调用 client.publish 方法。

小贴士

在这种情况下,我们不是评估 publish 方法的响应。此外,我们正在使用 qos 参数的默认值,该参数指定了所需的服务质量。在更高级的场景中,我们应该添加代码来检查方法的结果,并可能在成功发布消息时触发的 on_publish 回调中添加代码。

现在,我们将使用之前编写的 MessageTopic 类来创建一个新的 __main__ 方法版本,该方法使用 Mosquitto 代理和 MQTT 客户端接收和处理命令。新版本在环境温度变化时不会旋转伺服电机的轴,相反,它将在接收到连接到 Mosquitto 代理的任何设备的适当命令时执行此操作。以下行显示了 __main__ 方法的新版本。示例代码文件为 iot_python_chapter_09_05.py

if __name__ == "__main__":
    temperature_and_humidity_sensor = \
        TemperatureAndHumiditySensor(0)
    oled = TemperatureAndHumidityOled(0)
    temperature_servo = TemperatureServo(3)
 message_topic = MessageTopic(temperature_servo, oled)
    while True:
        temperature_and_humidity_sensor.\
            measure_temperature_and_humidity()
        oled.print_temperature(
            temperature_and_humidity_sensor.temperature_fahrenheit,
            temperature_and_humidity_sensor.temperature_celsius)
        oled.print_humidity(
            temperature_and_humidity_sensor.humidity)
        print("Ambient temperature in degrees Celsius: {0}".
              format(temperature_and_humidity_sensor.temperature_celsius))
        print("Ambient temperature in degrees Fahrenheit: {0}".
              format(temperature_and_humidity_sensor.temperature_fahrenheit))
        print("Ambient humidity: {0}".
              format(temperature_and_humidity_sensor.humidity))
        # Sleep 10 seconds (10000 milliseconds) but process messages every 1 second
 for i in range(0, 10):
 message_channel.loop()
 time.sleep(1)

突出的行创建了一个之前编写的MessageTopic类的实例,其中temperature_servooled作为参数。构造函数将订阅 Mosquitto 代理中的"iot-python-gaston-hillar/temperature"主题,因此,我们必须向这个主题发布消息,以便发送代码将处理的命令。循环将读取传感器的值并将值打印到控制台,就像代码的先前的版本一样,因此,我们将在循环中运行代码,我们也将有代码在 Mosquitto 代理的"iot-python-gaston-hillar/temperature"主题上监听消息。循环的最后几行调用message_channel.loop方法 10 次,每次调用之间暂停 1 秒。loop方法调用 MQTT 客户端的循环方法,并确保与代理的通信得到执行。将调用loop方法视为同步您的邮箱。任何待发送的消息将发送到发件箱,任何传入的消息将到达收件箱,我们之前分析的事件将被触发。

小贴士

此外,我们还可以通过调用 MQTT 客户端的loop_start方法来运行一个线程接口。这样,我们可以避免多次调用loop方法。

以下行将启动示例。

python iot_python_chapter_09_05.py

在板上保持代码运行。稍后我们将开始接收消息,因为我们必须编写将消息发布到这个主题并发送要处理的命令的代码。

使用 Python 客户端向 Mosquitto 代理发布消息

我们有在 Intel Galileo Gen 2 板上运行的代码,用于处理从 Mosquitto 消息代理接收到的命令消息。现在,我们将编写一个 Python 客户端,该客户端将向"iot-python-gaston-hillar/temperature"通道发布消息。这样,我们将能够设计能够通过 MQTT 消息与物联网设备通信的应用程序。具体来说,应用程序将通过 Mosquitto 消息代理与发布器和订阅设备中的 Python 代码进行通信。

我们可以在另一个 Intel Galileo Gen 2 板上或在安装了 Python 2.7.x 的任何设备上运行 Python 客户端。此外,代码将以 Python 3.x 运行。例如,我们可以在我们的计算机上运行 Python 客户端。我们只需确保在板上的 Yocto Linux 中运行的 Python 版本中安装了我们之前用 pip 安装的pubnub模块。

我们将创建许多函数,并将它们作为回调函数分配给 MQTT 客户端的各个事件。此外,我们还将声明变量和一个辅助函数,以便能够轻松地使用命令和所需的值发布消息。示例的代码文件是 iot_python_chapter_09_06.py。别忘了将分配给 topic 变量的字符串替换为你之前代码中指定的主题名称。以下行显示了定义变量和函数的代码:

command_key = "command"
topic = "iot-python-gaston-hillar/temperature"

def on_connect(client, userdata, flags, rc):
    print("Connected to the {0} topic".
          format(topic))
    subscribe_result = client.subscribe(topic)
    publish_result_1 = client.publish(
        topic=topic,
        payload="Listening to messages in the Paho Python Client")
    publish_result_2 = publish_command(
        client,
        topic,
        "print_temperature_fahrenheit",
        "temperature_fahrenheit",
        45)
    publish_result_3 = publish_command(
        client,
        topic,
        "print_information_message",
        "text",
        "Python IoT")

def on_message(client, userdata, msg):
    if msg.topic == topic:
        print("I've received the following message: {0}".format(str(msg.payload)))

def publish_command(client, topic, command_name, key, value):
    command_message = json.dumps({
        command_key: command_name,
        key: value})
    result = client.publish(topic=topic,
                            payload=command_message)
    return result

代码声明了 command_key 变量,它定义了指示代码在消息中理解为何种命令的关键字符串。我们的主要目标是构建并发布指定在 topic 变量中的主题的命令消息。我们将同时作为该主题的订阅者和发布者。

on_connect 函数是在与 Mosquitto MQTT 代理成功建立连接后执行的回调函数。代码调用 client 参数中接收到的 MQTT 客户端的 subscribe 方法,然后调用 publish 方法向主题发送以下字符串消息:“Listening to messages in the Paho Python Client”

代码使用必要的参数调用 publish_command 函数,构建并发布具有 45 度温度值的 print_temperature_fahrenheit 命令。最后,代码再次调用 publish_command 函数,使用必要的参数构建并发布具有文本值 "Python IoT"print_information_message 命令。

publish_command 函数接收 MQTT 客户端、主题、命令名称、键和值,这些参数提供了在 clienttopiccommand_namekeyvalue 中执行命令所必需的信息。在这种情况下,我们不针对特定 IoT 设备的命令,而是所有订阅该主题并运行我们之前示例中的代码的设备都将处理我们发布的命令。我们可以将此代码作为基准,用于处理更复杂的示例,在这些示例中,我们必须生成针对特定 IoT 设备的命令。正如我们之前的示例中发生的那样,提高安全性也是必要的。

该函数创建一个字典,并将将字典序列化为 JSON 格式字符串的结果保存到 command_message 本地变量中。command_key 变量是字典的第一个键,command_name 作为参数接收,它构成了第一个键值对。然后,代码调用 client.publish 方法将 command_message JSON 格式字符串发布到作为参数接收的主题。

on_message 函数将在每次有新消息到达我们订阅的主题时执行。该函数只是打印带有接收消息有效载荷的原始字符串。

现在,我们将使用之前编写的functions来编写一个__main__方法,该方法发布 MQTT 消息中包含的两个命令,我们的板子将处理这些命令。以下行显示了__main__方法的代码。示例的代码文件是iot_python_chapter_09_06.py

if __name__ == "__main__":
    client = mqtt.Client()
    client.on_connect = on_connect
    client.on_message = on_message
    client.connect(host="iot.eclipse.org",
                   port=1883,
                   keepalive=60)
    client.loop_forever()

__main__方法中的代码非常容易理解。代码创建了一个mqtt.Client类的实例,代表一个 MQTT 客户端,我们将使用它来与 MQTT 代理通信。由于我们使用默认参数创建实例,我们将创建一个paho.mqtt.client.MQTTv31的实例,我们将使用 MQTT 版本 3.1。

然后,代码将client.on_connect属性分配给之前编写的on_connect函数,将client.on_message属性分配给on_message函数。代码调用client.connect方法,并在参数中指定了 Eclipse IoT 项目在 iot.eclipse.org 上的公开可访问的沙盒服务器,端口为 1883。这样,代码请求 MQTT 客户端与指定的 MQTT 代理建立连接。如果你决定使用自己的 Mosquitto 代理,你只需根据 Mosquitto 代理的配置更改hostport参数的值。请记住,connect方法以异步执行方式运行,因此它是一个非阻塞调用。

在与 MQTT 代理成功建立连接后,client.on_connect属性中指定的回调函数将被执行,即on_connect函数。该函数接收在client参数中与 MQTT 代理建立连接的mqtt.Client实例。正如之前解释的那样,该函数订阅了一个主题,并安排向其发布三条消息。

最后,代码调用client.loop_forever方法,该方法以无限阻塞循环的方式为我们调用循环方法。在此阶段,我们只想在我们的程序中运行 MQTT 客户端循环。计划的消息将被发布,并且在我们将命令发送到板子后,我们将收到成功执行命令详情的消息。

保持我们在之前的示例中在板上运行的 Python 代码。我们希望板子处理我们的命令。以下行将在任何你想要用作客户端的计算机或设备上启动 Python 客户端的示例。如果你想在同一个板上使用相同的板子作为客户端,可以在另一个 SSH 终端中运行代码。

python iot_python_chapter_09_06.py

运行示例后,你将在运行 Python 客户端的 Python 控制台中看到以下输出,即iot_python_chapter_09_06.py Python 脚本。

Connected to the iot-python-gaston-hillar/temperature topic
I've received the following message: Listening to messages in the Paho Python Client
I've received the following message: {"command": "print_temperature_fahrenheit", "temperature_fahrenheit": 45}
I've received the following message: {"text": "Python IoT", "command": "print_information_message"}
I've received the following message: {"successfully_processed_command": "print_temperature_fahrenheit"}
I've received the following message: {"successfully_processed_command": "print_information_message"}

代码使用了 Eclipse Paho MQTT Python 客户端库,在 Mosquitto 代理的"iot-python-gaston-hillar/temperature"主题中构建并发布了以下两条命令消息:

{"command":"print_temperature_fahrenheit", "temperature_fahrenheit": "45"}
{"command":"print_information_message", "text": "Python IoT"}

由于我们也订阅了 "iot-python-gaston-hillar/temperature" 主题,我们收到了我们发送的消息。然后,我们收到了两个命令消息的成功处理命令消息。板已处理命令并将消息发布到 "iot-python-gaston-hillar/temperature" 主题。

你将在运行处理命令的板(即 iot_python_chapter_09_05.py Python 脚本)的 SSH 终端输出中看到以下消息:

I've received the following message: Listening to messages in the Intel Galileo Gen 2 board
I've received the following message: Listening to messages in the Paho Python Client
I've received the following message: {"command": "print_temperature_fahrenheit", "temperature_fahrenheit": 45}
I've received the following message: {"text": "Python IoT", "command": "print_information_message"}
I've received the following message: {"successfully_processed_command": "print_temperature_fahrenheit"}
I've received the following message: {"successfully_processed_command": "print_information_message"}

你将在 OLED 矩阵的底部看到以下文本显示:Python IoT。此外,伺服电机的轴将旋转到 45 度。

测试你的知识

  1. MQTT 是:

    1. 在 TCP/IP 协议之上运行的重量级消息协议,并使用发布/订阅机制。

    2. 在 TCP/IP 协议之上运行的轻量级消息协议,并使用发布/订阅机制。

    3. HTTP 的等价物。

  2. Mosquitto 是:

    1. 实现 MQTT 协议版本 3.1 和 3.1.1 的开源消息代理。

    2. 实现 MQTT 协议版本 3.1 和 3.1.1 的闭源消息代理。

    3. 实现 RESTful API 的开源消息代理。

  3. Eclipse Paho 项目提供:

    1. HTTP 的开源客户端实现。

    2. dweet.io 的开源客户端实现。

    3. MQTT 的开源客户端实现。

  4. 以下哪个 Python 模块是 Paho Python 客户端?

    1. paho-client-pip。

    2. paho-mqtt。

    3. paho-http。

  5. Dweepy 是:

    1. 一个简单的 dweet.io Python 客户端,允许我们使用 Python 轻松发布数据到 dweet.io

    2. 一个简单的 Mosquitto Python 客户端,允许我们轻松地将消息发布到 Mosquitto 消息代理。

    3. 一个简单的 PubNub 云 Python 客户端,允许我们轻松地将消息发布到 PubNub 云。

摘要

在本章中,我们结合了许多基于云的服务,使我们能够轻松发布从传感器收集的数据,并在基于网页的仪表板上可视化它。我们意识到总有一个 Python API,因此,编写与流行的基于云服务交互的 Python 代码很容易。

我们与 MQTT 协议及其发布/订阅模型合作,处理板上的命令,并通过消息指示命令是否成功处理。首先,我们与使用 MQTT 协议的 PubNub 云合作。然后,我们用 Mosquitto 和 Eclipse Paho 开发了相同的示例。现在,我们知道如何编写可以与我们的物联网设备建立双向通信的应用程序。此外,我们还知道如何使物联网设备与其他物联网设备通信。

既然我们能够利用许多云服务,并且我们已经与 MQTT 协议合作,我们将学习如何分析大量数据,这是下一章的主题。

第十章. 使用基于云的物联网分析分析大量数据

在本章中,我们将使用英特尔物联网分析,利用这个强大的基于云的服务来分析大量数据。我们将:

  • 理解物联网与大数据之间的关系

  • 学习英特尔物联网分析的结构

  • 在英特尔物联网分析中设置设备

  • 在英特尔物联网分析中配置组件

  • 使用英特尔物联网分析收集传感器数据

  • 使用英特尔物联网分析分析传感器数据

  • 在英特尔物联网分析中使用规则触发警报

理解物联网与大数据之间的关系

大数据在监视我们。每次我们执行一个动作,即使我们不知道,我们都在生成有价值的数据。每次我们轻触、点击、发推文、在红灯信号前停下、乘坐公交车,或者在世界任何城市的数百万个实时传感器捕捉到的任何动作,我们都在生成有价值的数据。我们与具有传感器的物联网设备互动,收集数据并将其发布到云端。为了分析和处理大数据,管理者、架构师、开发人员和系统管理员需要许多在处理较小数据集的应用程序中并不必要的技能。

我们一直在使用示例,通过传感器从现实世界收集数据并将其发布到云端。我们还发布了包含传感器数据和必须由物联网设备上运行的代码处理的命令的消息。有时,我们每秒从传感器中检索数据。因此,很容易意识到我们正在生成大量数据,因此,学习与大数据相关的大量知识非常重要。物联网包含大数据。

假设我们编写了在英特尔 Galileo Gen 2 板上运行的 Python 代码,并且每秒执行以下操作:

  • 从温度和湿度传感器中读取测量的环境温度

  • 从温度和湿度传感器中读取测量的环境湿度水平

  • 从测量值位于不同位置的十个土壤湿度传感器中读取测量的体积水含量

  • 发布包含环境温度、环境湿度和十个体积水含量的消息

可能首先出现在我们脑海中的是我们需要连接到板上的传感器数量。让我们假设所有的传感器都是数字传感器,并且我们必须将它们连接到 I²C 总线上。只要所有传感器都有不同的 I²C 总线地址,我们就可以将数字温度和湿度传感器以及十个土壤湿度传感器连接到 I²C 总线上。我们只需要确保我们可以配置土壤湿度传感器的 I²C 总线地址,并且我们可以为这些传感器中的每一个分配不同的 I²C 地址。

Catnip Electronics 设计了一种数字土壤湿度传感器,它提供了一个 I²C 接口,其一个特性是它允许更改 I²C 地址。该传感器的默认 I²C 地址为 0x20(十六进制 20),但我们可以轻松地更改它。我们只需将每个传感器连接到 I²C 总线,将新地址写入寄存器,并在重置传感器后,新地址将生效。我们只需将 6 写入传感器的 I²C 地址即可重置传感器。我们可以对所有的传感器执行相同的程序,并为它们分配不同的 I²C 地址。您可以在以下网页上了解更多关于数字土壤湿度传感器的信息:www.tindie.com/products/miceuz/i2c-soil-moisture-sensor

我们希望分析每小时、每天、每月、每季度和每年的数据。然而,我们确实需要每秒测量一次,而不是每天测量一次,因为分析数据每秒的变化非常重要。我们将收集以下数据:

  • 每分钟对所有变量进行 60 次测量

  • 每小时 3,600(60 * 60)次测量

  • 每天进行 86,400(3,600 x 24)次测量

  • 每年 31,536,000(86,400 * 365)次测量(考虑到我们不是在谈论闰年)

我们不会只有一个物联网设备收集数据并发布它。我们将有 3,000 个物联网设备运行相同的代码,并且它们每年将生成 94,608,000,000(31,356,300 * 3,000),即九百四十亿六千八百万次测量。此外,我们还有其他必须分析的数据来源:所有关于传感器捕获数据位置天气相关问题的推文。因此,我们拥有大量结构化和非结构化数据,我们希望对其进行计算分析,以揭示模式和关联。我们无疑是在谈论大数据实践。

样本数量有助于理解大数据和物联网之间的关系。在下一个示例中,我们不会部署 3,000 块板,也不会涵盖与物联网分析和大数据相关的所有主题,因为这超出了本书的范围。然而,我们将使用基于云的分析系统,该系统与我们在第二章中使用的英特尔物联网开发套件镜像中的组件一起工作,以使用 Yocto Linux 元分布启动板。

理解英特尔物联网分析结构

假设我们需要收集和分析 3,000 个物联网设备的数据,即 3,000 个运行与传感器交互的 Python 代码的 Intel Galileo Gen 2 板。我们需要投资存储和处理能力来处理如此大量的物联网分析数据。每当我们有类似的需求时,我们可以利用基于云的解决方案。Intel IoT Analytics 就是其中之一,它与 Intel Galileo Gen 2 板和 Python 非常兼容。

Intel IoT Analytics 要求我们注册,使用有效的电子邮件地址和密码创建账户,并在发布传感器数据之前点击确认电子邮件的激活链接。我们不需要输入任何信用卡或支付信息。如果您已经在 Intel IoT Analytics 上有账户,您可以跳过此步骤。您也可以使用现有的 Facebook、Google+ 或 GitHub 账户登录。以下是 Intel IoT Analytics 网站的主页:dashboard.us.enableiot.com。在使用此基于云的服务处理敏感数据之前,请务必查看条款和条件。

一旦您在 Intel IoT Analytics 上创建账户并首次登录,网站将显示创建新账户页面。输入您希望用于识别账户的名称,即您的分析项目在账户名称中。以我们的示例为例,输入温度和湿度,并保留传感器健康报告的默认选项。然后,点击创建,网站将显示为最近创建的账户的我的仪表板页面。每个账户代表一个独立的工作空间,拥有自己的传感器和相关数据集。网站允许我们创建多个账户,并轻松地在它们之间切换。以下截图显示了创建新账户后我的仪表板页面的初始视图:

https://github.com/OpenDocCN/freelearn-python-zh/raw/master/docs/iot-py/img/B05042_10_01.jpg

我的仪表板页面表明我们尚未注册任何设备,因此我们也没有传输设备或观测数据。每次我们从注册设备向 Intel IoT Analytics 发布数据时,我们都会为该设备创建一个观测数据。因此,我的仪表板页面提供了特定时间段内最后观测数据的数量。默认情况下,页面显示所有注册设备过去一小时的观测数据总和。请保持您的网页浏览器打开,因为我们稍后将继续使用它。

作为用户,我们可以与许多账户一起工作。每个账户可以包含许多设备,有一个名称和一个称为accountId的标识符。每个设备都有一个称为deviceId的全局唯一标识符。因此,每个包含传感器的英特尔 Galileo Gen 2 板将为我们创建的账户成为一台设备。在我们的情况下,我们只需与一个英特尔 Galileo Gen 2 板一起工作。然而,请记住,我们的目标是展示我们如何与单个账户处理的 3,000 个物联网设备一起工作。

我们可以将每个设备视为一个端点,该端点包含一个或多个组件,这些组件可以在英特尔物联网分析中提供以下之一:

  • 执行器:可以在设备上修改的设置。例如,旋转伺服轴的角度或打开 LED。

  • 时间序列:从传感器捕获的一系列值,即观测值的集合。例如,使用温度和湿度传感器检索的环境温度值集合,以华氏度表示,包括时间戳。

在我们的情况下,我们需要一个设备来使用以下组件,这些组件将从连接到我们板上的数字温度和湿度传感器中检索值:

  • 一个以华氏度(ºF)表示的环境温度观测的时间序列

  • 一个以摄氏度(ºC)表示的环境温度观测的时间序列

  • 一个以百分比表示的环境湿度水平观测的时间序列

首先,我们将结合使用英特尔物联网分析网站提供的 UI 和iotkit-admin实用程序来设置设备、激活它并注册之前列表中包含的三个组件。这样,我们将学会与英特尔物联网分析所需的结构一起工作。然后,我们将编写 Python 代码,使用 REST API 为属于我们最近创建的账户中已激活设备的定义组件创建观测值。

我们也可以通过编写 Python 代码来使用 REST API 执行之前解释的设置任务。如果我们必须与超过一打设备一起工作,我们不会想通过使用英特尔物联网分析网站提供的 UI 来执行设置任务,我们肯定会想编写自动化设置任务的代码。

在英特尔物联网分析中设置设备

我们用来启动英特尔 Galileo Gen 2 板的映像中预装了英特尔物联网分析的本地代理。除非我们对 Yocto Linux meta 分布进行了特定更改以禁用特定组件,否则代理将以守护进程的形式在设备上运行。代理包括iotkit-admin命令行实用程序,允许我们与英特尔物联网分析进行特定交互。我们将使用此命令行实用程序执行以下任务:

  • 测试与英特尔物联网分析的正确通信

  • 获取设备 ID

  • 激活设备

  • 为设备注册三个时间序列组件。

  • 发送测试观测值

首先,我们将检查iotkit-admin命令行工具是否能够与英特尔物联网分析服务建立适当的通信。我们只需在 SSH 终端中运行以下命令:

iotkit-admin test

如果连接成功,我们将看到类似以下的一些行。最后一行提供了有关构建的信息,即版本。

2016-04-05T02:17:49.573Z - info: Trying to connect to host ...
2016-04-05T02:17:56.780Z - info: Connected to dashboard.us.enableiot.com
2016-04-05T02:17:56.799Z - info: Environment: prod
2016-04-05T02:17:56.807Z - info: Build: 0.14.5

现在,在 SSH 终端中运行以下命令以获取设备 ID,也称为deviceId

iotkit-admin device-id

上一条命令将生成类似以下行的输出行,其中包含设备 ID。默认情况下,设备 ID 等于网络接口卡的 MAC 地址。

2016-04-05T02:23:23.170Z - info: Device ID: 98-4F-EE-01-75-72

您可以使用以下命令将设备 ID 更改为不同的一个:iotkit-admin set-device-id new-device-id。您只需将new-device-id替换为您为设备设置的新的设备 ID。然而,请注意,新的设备 ID 必须是一个全局唯一标识符。

在这种情况下,我们将使用kansas-temperature-humidity-01作为我们所有示例的设备 ID。您必须在所有命令中替换它,然后包括您检索到的设备名称或分配给设备的新的设备 ID。

在 SSH 终端中运行的以下命令将重命名设备:

iotkit-admin set-device-id kansas-temperature-humidity-01

以下行显示了上一条命令的输出:

2016-04-08T17:56:15.355Z - info: Device ID set to: kansas-temperature-humidity

前往您正在使用的英特尔物联网分析仪表板的网页浏览器,点击菜单图标(位于左上角的一个有三个横线的按钮)。选择账户,网站将显示我们之前创建的账户的我的账户页面,其中包含详细的账户信息。

初始视图将显示详细信息选项卡。如果激活码包含**(代码已过期)文本,则意味着激活码已不再有效,并且有必要点击位于激活码文本框右侧的刷新图标(第二个带有两个箭头的图标)。我们必须确保激活码尚未过期,以便成功激活设备。以下截图显示了我的账户页面的初始视图,对于温度和湿度**账户,激活码已过期:

https://github.com/OpenDocCN/freelearn-python-zh/raw/master/docs/iot-py/img/B05042_10_02.jpg

一旦您通过点击刷新按钮刷新激活码,倒计时计时器将指示激活码到期前的剩余时间。您在点击刷新按钮后将有一个小时。点击眼睛图标以查看隐藏的激活码并复制它。我们将使用01aCti0e作为我们的示例激活码,您必须将其替换为您自己的激活码。

现在,在 SSH 终端中运行以下命令以使用之前生成的激活码激活设备。将01aCti0e替换为您的激活码。

iotkit-admin activate 01aCti0e

上一条命令将生成类似以下行的输出:

2016-04-05T02:24:46.449Z - info: Activating ...
2016-04-05T02:24:49.817Z - info: Saving device token...
2016-04-05T02:24:50.646Z - info: Updating metadata...
2016-04-05T02:24:50.691Z - info: Metadata updated.

我们现在将英特尔 Galileo Gen 2 板,即设备,与提供激活代码并生成必要安全凭证(即设备令牌)的温度和湿度账户关联起来。

打开您正在使用英特尔物联网分析仪表板的浏览器,点击菜单图标(位于左上角的一个有三个水平线的按钮)。选择设备,网站将显示包含我们为当前账户激活的所有设备的我的设备页面。之前激活的 kansas-temperature-humidity-01 设备将出现在列表中,其名称列为Kansas-temperature-humidity-01-NAME状态列为active。以下截图显示了我的设备页面中的设备列表:

https://github.com/OpenDocCN/freelearn-python-zh/raw/master/docs/iot-py/img/B05042_10_03.jpg

点击前一个列表中的设备IDkansas-temperature-humidity-01)以查看和编辑设备详细信息。您可以添加标签和属性,以便更容易过滤前一个列表中的设备。当我们需要处理超过一打设备时,这些功能非常有用,因为它们使我们能够轻松过滤列表中的设备。

在英特尔物联网分析中设置组件

打开您正在使用英特尔物联网分析仪表板的浏览器,点击菜单图标,选择账户,网站将显示我的账户页面。然后,点击目录选项卡,网站将显示按以下三个类别分组的目录中注册的组件:

  • 湿度

  • Powerswitch

  • 温度

确保已展开湿度组件面板,然后点击humidity.v1.0。网站将显示humidity.v1.0组件的组件定义对话框,即版本为 1.0 的名为humidity的组件。以下截图显示了组件定义中不同字段的值:

https://github.com/OpenDocCN/freelearn-python-zh/raw/master/docs/iot-py/img/B05042_10_04.jpg

版本为 1.0humidity 组件表示一个以百分比表示的环境湿度水平的时间序列。数据类型数字计量单位百分比 (%)格式浮点数显示时间序列。我们可以使用此组件来观察我们的环境湿度水平。

点击关闭,确保温度组件面板已展开,然后点击temperature.v1.0。网站将显示temperature.v1.0组件的组件定义对话框,即版本为 1.0 的名为temperature的组件。以下截图显示了组件定义中不同字段的值:

https://github.com/OpenDocCN/freelearn-python-zh/raw/master/docs/iot-py/img/B05042_10_05.jpg

temperature组件版本1.0代表一个以摄氏度表示的温度的时间序列。数据类型数字测量单位摄氏度格式浮点数显示时间序列。我们可以使用此组件来表示以摄氏度表示的环境温度观测。

点击关闭并确保温度组件面板已展开。没有其他温度组件,因此,我们必须为以华氏度表示的环境温度观测创建一个新的组件。

在页面底部点击添加新目录项,网站将显示包含所有字段为空的组件定义对话框,除了版本号将具有固定的1.0值。我们正在创建新目录项的第一个版本。输入并选择以下值:

  • 组件名称中输入temperaturef

  • 类型中选择传感器

  • 数据类型中选择数量

  • 测量单位中输入华氏度

  • 格式中选择浮点数

  • 显示中选择时间序列

最后,点击保存,网站将在列表底部添加名为temperaturef.v.1.0的新组件定义。

现在我们确信目录中包含所有必需的组件定义,我们必须注册设备将用于创建观测的组件。我们必须为每个注册的组件提供一个名称或别名,并必须指定来自上一目录的组件类型和版本。以下表格总结了我们将为设备注册的组件:

组件名称或别名组件类型描述
temperaturectemperature.v1.0一个以摄氏度(ºC)表示的环境温度观测的时间序列
temperatureftemperaturef.v1.0一个以华氏度(ºF)表示的环境温度观测的时间序列
humidityhumidity.v1.0一个以百分比表示的环境湿度水平观测的时间序列

我们可以使用以下命令来注册每个组件:iotkit-admin register component-name component-type。我们只需要将component-name替换为将标识组件的名称,将component-type替换为标识组件类型(包括版本号)的名称。

在 SSH 终端中的以下命令将注册来自上一表的temperaturec组件:

iotkit-admin register temperaturec temperature.v1.0

以下行显示了上一条命令的输出。

2016-04-08T22:40:04.581Z - info: Starting registration ...
2016-04-08T22:40:04.711Z - info: Device has already been activated. Updating ...
2016-04-08T22:40:04.739Z - info: Updating metadata...
2016-04-08T22:40:04.920Z - info: Metadata updated.
Attributes sent
2016-04-08T22:40:10.167Z - info: Component registered name=temperaturec, type=temperature.v1.0, cid=c37cb57d-002c-4a66-866e-ce66bc3b2340, d_id=kansas-temperature-humidity-01

最后一行为我们提供了组件 ID,即cid=之后和下一个逗号(,)之前的值。在上一个输出中,组件 ID 是c37cb57d-002c-4a66-866e-ce66bc3b2340。我们必须保存每个组件 ID,因为我们稍后需要它来编写使用 REST API 创建观测的代码。

在 SSH 终端中,以下命令将注册来自上一张表的temperaturef组件:

iotkit-admin register temperaturef temperaturef.v1.0

以下几行显示了上一条命令的输出结果:

2016-04-08T22:40:20.510Z - info: Starting registration ...
2016-04-08T22:40:20.641Z - info: Device has already been activated. Updating ...
2016-04-08T22:40:20.669Z - info: Updating metadata...
2016-04-08T22:40:20.849Z - info: Metadata updated.
Attributes sent
2016-04-08T22:40:26.156Z - info: Component registered name=temperaturef, type=temperaturef.v1.0, cid=0f3b3aae-ce40-4fb4-a939-e7c705915f0c, d_id=kansas-temperature-humidity-01

与其他命令一样,最后一行为我们提供了组件 ID,即cid=之后和下一个逗号(,)之前的值。在上一个输出中,组件 ID 是0f3b3aae-ce40-4fb4-a939-e7c705915f0c。我们必须保存这个 ID,以便在代码中的后续使用。

在 SSH 终端中,以下命令将注册来自上一张表的humidity组件:

iotkit-admin register humidity humidity.v1.0

以下几行显示了上一条命令的输出结果,最后一行包括组件 ID。

2016-04-08T22:40:36.512Z - info: Starting registration ...
2016-04-08T22:40:36.643Z - info: Device has already been activated. Updating ...
2016-04-08T22:40:36.670Z - info: Updating metadata...
2016-04-08T22:40:36.849Z - info: Metadata updated.
Attributes sent
2016-04-08T22:40:43.003Z - info: Component registered name=humidity, type=humidity.v1.0, cid=71aba984-c485-4ced-bf19-c0f32649bcee, d_id=kansas-temperature-humidity-01

小贴士

组件 ID 将与之前输出中指示的值不同,您需要记录由之前命令生成的每个组件 ID。

访问您正在使用的 Intel IoT Analytics 仪表板的网页浏览器,点击菜单图标。选择设备,站点将显示我的设备页面。点击之前列表中的设备IDkansas-temperature-humidity-01)以查看和编辑设备详细信息。点击**+组件**以展开为设备注册的组件,您将看到一个包含以下三个组件的列表:

  • temperaturec

  • temperaturef

  • humidity

以下截图显示了为所选设备注册的三个组件:

https://github.com/OpenDocCN/freelearn-python-zh/raw/master/docs/iot-py/img/B05042_10_06.jpg

我们可以点击这三个组件中的任何一个,并检查注册组件的详细信息。如果我们丢失了组件 ID,我们可以通过点击组件并显示组件定义对话框来检索它,组件 ID 将显示在组件类型描述下方。以下截图显示了temperaturef组件的组件定义。组件 ID 0f3b3aae-ce40-4fb4-a939-e7c705915f0c出现在右侧的自定义组件标签下方。

https://github.com/OpenDocCN/freelearn-python-zh/raw/master/docs/iot-py/img/B05042_10_07.jpg

不幸的是,我们没有方法检索在激活设备时生成的设备令牌,该设备具有站点中包含的功能。我们需要设备令牌来为注册的组件创建观测值。Intel IoT Analytics 代理将设备令牌与其他配置值一起保存在device.json文件中,其默认路径是/usr/lib/node_modules/iotkit-agent/data/device.json。正如文件名所暗示的,该文件包含 JSON 代码。我们只需在 SSH 终端中运行以下命令,即可显示上一个文件的文本内容,并允许我们检索设备令牌:

cat /usr/lib/node_modules/iotkit-agent/data/device.json

以下几行显示了上一条命令的输出结果,其中包括我们迄今为止为设备所做的所有配置。定义设备令牌值的行被突出显示。

{
    "activation_retries": 10,
    "activation_code": null,
    "device_id": "kansas-temperature-humidity-01",
    "device_name": false,
    "device_loc": [
        88.34,
        64.22047,
        0
    ],
    "gateway_id": false,
 "device_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJqdGkiOiJjOTNmMTJhMy05MWZlLTQ3MWYtODM4OS02OGM1NDYxNDIxMDUiLCJpc3MiOiJodHRwOi8vZW5hYmxlaW90LmNvbSIsInN1YiI6ImthbnNhcy10ZW1wZXJhdHVyZS1odW1pZGl0eS0wMSIsImV4cCI6IjIwMjYtMDQtMDZUMTk6MDA6MTkuNzA0WiJ9.PH5yQas2FiQvUSR9V2pa3n3kIYZvmSe_xXY7QkFjlXUVUcyy9Sk_eVF4AL6qpZlBC9vjtd0L-VMZiULC9YXxAVl9s5Cl8ZqpQs36E1ssv_1H9CBFXKiiPArplzaWXVzvIRBVVzwfQrGrMoD_l4DcHlH2zgn5UGxhZ3RMPUvqgeneG3P-hSbPScPQL1pW85VT2IHT3seWyW1c637I_MDpHbJJCbkytPVpJpwKBxrCiKlGhvsh5pl4eLUXYUPlQAzB9QzC_ohujG23b-ApfHZugYD7zJa-05u0lkt93EEnuCk39o5SmPmIiuBup-k_mLn_VMde5fUvbxDt_SMI0XY3_Q",
    "account_id": "22612154-0f71-4f64-a68e-e116771115d5",
    "sensor_list": [
        {
            "name": "temperaturec",
            "type": "temperature.v1.0",
            "cid": "c37cb57d-002c-4a66-866e-ce66bc3b2340",
            "d_id": "kansas-temperature-humidity-01"
        },
        {
            "name": "temperaturef",
            "type": "temperaturef.v1.0",
            "cid": "0f3b3aae-ce40-4fb4-a939-e7c705915f0c",
            "d_id": "kansas-temperature-humidity-01"
        },
        {
            "name": "humidity",
            "type": "humidity.v1.0",
            "cid": "71aba984-c485-4ced-bf19-c0f32649bcee",
            "d_id": "kansas-temperature-humidity-01"
        }
    ]
}

前几行还显示了我们所注册的每个组件的组件 ID。因此,我们只需在一个地方就可以找到所有必要的配置值,这些值将用于我们的代码中。在这种情况下,设备令牌如下,即"device_token"键的字符串值。然而,您将检索到的值将不同。

"eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJqdGkiOiJjOTNmMTJhMy05MWZlLTQ3MWYtODM4OS02OGM1NDYxNDIxMDUiLCJpc3MiOiJodHRwOi8vZW5hYmxlaW90LmNvbSIsInN1YiI6ImthbnNhcy10ZW1wZXJhdHVyZS1odW1pZGl0eS0wMSIsImV4cCI6IjIwMjYtMDQtMDZUMTk6MDA6MTkuNzA0WiJ9.PH5yQas2FiQvUSR9V2pa3n3kIYZvmSe_xXY7QkFjlXUVUcyy9Sk_eVF4AL6qpZlBC9vjtd0L-VMZiULC9YXxAVl9s5Cl8ZqpQs36E1ssv_1H9CBFXKiiPArplzaWXVzvIRBVVzwfQrGrMoD_l4DcHlH2zgn5UGxhZ3RMPUvqgeneG3P-hSbPScPQL1pW85VT2IHT3seWyW1c637I_MDpHbJJCbkytPVpJpwKBxrCiKlGhvsh5pl4eLUXYUPlQAzB9QzC_ohujG23b-ApfHZugYD7zJa-05u0lkt93EEnuCk39o5SmPmIiuBup-k_mLn_VMde5fUvbxDt_SMI0XY3_Q"

使用英特尔物联网分析收集传感器数据

我们将使用我们在第八章中编写的代码,显示信息和执行操作,当我们从传感器读取温度和湿度值时,我们在 OLED 矩阵中打印了这些值,并旋转了伺服轴以显示以华氏度表示的测量温度。该示例的代码文件为iot_python_chapter_08_03.py。我们将使用此代码作为基准,添加新功能,使我们能够为我们激活的设备注册的三个组件创建观测值。

在第二章,在英特尔 Galileo Gen 2 上使用 Python中,我们确保了pip安装程序在板上运行的 Yocto Linux 中可用,以便安装额外的 Python 2.7.3 包。现在,我们将使用pip安装程序确保安装了requests包。这个包是一个非常流行的 Python HTTP 库,它允许我们使用极其容易理解的语法轻松构建和发送 HTTP 请求。

如果您已经处理了上一章的示例,您将已经安装了此包。然而,如果您刚刚跳到本章,可能需要安装它。我们只需在 SSH 终端中运行以下命令即可安装该包。请注意,安装可能需要几分钟时间。

pip install requests

如果您看到以下输出,则表示requests包已经安装,您可以继续下一步。

Requirement already satisfied (use --upgrade to upgrade): requests in /usr/lib/python2.7/site-packages

我们将创建一个IntelIotAnalytics类来表示英特尔物联网分析接口,并使我们能够轻松地为三个组件发布观测值。然而,在我们编写类代码之前,我们必须确保我们可以替换与我们的账户、组件和设备相关的重要值的相关类属性的内容。您必须将以下类属性指定的字符串替换为适当的值:

  • account_name: 账户名称字段在我的账户页面中的值。在我们的例子中,我们为账户名称使用了"Temperature and humidity"

  • account_id: 账户 ID字段在我的账户页面中的值。在我们的例子中,我们为账户 ID 使用了"22612154-0f71-4f64-a68e-e116771115d5"。我们还可以通过读取device.json文件中指定的"account_id"键的字符串值来检索账户 ID 值。

  • device_id: 在我们点击我的设备页面中显示的列表中的设备名称时,添加/编辑设备页面中ID字段的值。在我们的例子中,我们使用"kansas-temperature-humidity-01"作为我们的设备 ID。我们也可以通过在 SSH 终端中运行以下命令来检索device_idiotkit-admin device-id,或者通过读取device.json文件中指定的"device_id"键的字符串值。

  • device_token: 当我们激活设备时生成的设备令牌的值。如前所述,我们可以通过读取device.json文件中指定的"device_token"键的字符串值来检索设备令牌。

  • component_id_temperature_fahrenheit: 当我们注册了temperaturef组件时生成的组件 ID 的值。组件 ID 显示在组件定义对话框中组件类型下方。在我们的例子中,我们使用"0f3b3aae-ce40-4fb4-a939-e7c705915f0c"作为这个值。我们也可以通过读取device.json文件中声明"name": "temperaturef"键值对同一块中指定的"cid"键的字符串值来检索组件 ID 值。

  • component_id_temperature_celsius: 当我们注册了temperaturec组件时生成的组件 ID 的值。在我们的例子中,我们使用"c37cb57d-002c-4a66-866e-ce66bc3b2340"作为这个值。

  • component_id_humidity_level_percentage: 当我们注册了humidity组件时生成的组件 ID 的值。在我们的例子中,我们使用"71aba984-c485-4ced-bf19-c0f32649bcee"作为这个值。

样本的代码文件是iot_python_chapter_10_01.py。请记住,我们使用代码文件iot_python_chapter_08_03.py作为基线,因此,我们将把IntelIotAnalytics类添加到该文件中的现有代码中,并创建一个新的 Python 文件。以下代码展示了允许我们通过 REST API 发布temperatureftemperaturechumidity组件观察结果的IntelIotAnalytics类。

import time
import json
import requests

class IntelIotAnalytics:
    base_url = "https://dashboard.us.enableiot.com/v1/api"
    # You can retrieve the following information from the My Account page
    account_name = "Temperature and humidity"
    account_id = "22612154-0f71-4f64-a68e-e116771115d5"
    # You can retrieve the device token with the following command:
    # cat /usr/lib/node_modules/iotkit-agent/data/device.json
    device_token = "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJqdGkiOiJjOTNmMTJhMy05MWZlLTQ3MWYtODM4OS02OGM1NDYxNDIxMDUiLCJpc3MiOiJodHRwOi8vZW5hYmxlaW90LmNvbSIsInN1YiI6ImthbnNhcy10ZW1wZXJhdHVyZS1odW1pZGl0eS0wMSIsImV4cCI6IjIwMjYtMDQtMDZUMTk6MDA6MTkuNzA0WiJ9.PH5yQas2FiQvUSR9V2pa3n3kIYZvmSe_xXY7QkFjlXUVUcyy9Sk_eVF4AL6qpZlBC9vjtd0L-VMZiULC9YXxAVl9s5Cl8ZqpQs36E1ssv_1H9CBFXKiiPArplzaWXVzvIRBVVzwfQrGrMoD_l4DcHlH2zgn5UGxhZ3RMPUvqgeneG3P-hSbPScPQL1pW85VT2IHT3seWyW1c637I_MDpHbJJCbkytPVpJpwKBxrCiKlGhvsh5pl4eLUXYUPlQAzB9QzC_ohujG23b-ApfHZugYD7zJa-05u0lkt93EEnuCk39o5SmPmIiuBup-k_mLn_VMde5fUvbxDt_SMI0XY3_Q"
    device_id = "kansas-temperature-humidity-01"
    component_id_temperature_fahrenheit = "0f3b3aae-ce40-4fb4-a939-e7c705915f0c"
    component_id_temperature_celsius = "c37cb57d-002c-4a66-866e-ce66bc3b2340"
    component_id_humidity_level_percentage = "71aba984-c485-4ced-bf19-c0f32649bcee"

    def publish_observation(self,
                            temperature_fahrenheit,
                            temperature_celsius,
                            humidity_level):
        url = "{0}/data/{1}".\
            format(self.__class__.base_url, self.__class__.device_id)
        now = int(time.time()) * 1000
        body = {
            "on": now,
            "accountId": self.__class__.account_id,
            "data": []
        }
        temperature_celsius_data = {
            "componentId": self.__class__.component_id_temperature_celsius,
            "on": now,
            "value": str(temperature_celsius)
        }
        temperature_fahrenheit_data = {
            "componentId": self.__class__.component_id_temperature_fahrenheit,
            "on": now,
            "value": str(temperature_fahrenheit)
        }
        humidity_level_percentage_data = {
            "componentId": self.__class__.component_id_humidity_level_percentage,
            "on": now,
            "value": str(humidity_level)
        }
        body["data"].append(temperature_celsius_data)
        body["data"].append(temperature_fahrenheit_data)
        body["data"].append(humidity_level_percentage_data)
        data = json.dumps(body)
        headers = {
            'Authorization': 'Bearer ' + self.__class__.device_token,
            'content-type': 'application/json'
        }
        response = requests.post(url, data=data, headers=headers, proxies={}, verify=True)
        if response.status_code != 201:
            print "The request failed. Status code: {0}. Response text: {1}.".\
                format(response.status_code, response.text)

IntelIotAnalytics类声明了许多我们之前解释过的类属性,您需要用您自己的字符串值替换它们:account_nameaccount_iddevice_tokendevice_idcomponent_id_temperature_fahrenheitcomponent_id_temperature_celsiuscomponent_id_humidity_level_percentagebase_url类属性定义了访问 REST API 的基本 URL:https://dashboard.us.enableiot.com/v1/api。我们将使用这个值与data路径和device_id类属性结合来构建我们将发送 HTTP 请求以发布观察结果的 URL。

类声明了publish_observation方法,该方法接收华氏度表示的温度、摄氏度表示的温度和湿度百分比,参数为temperature_fahrenheittemperature_celsiushumidity_level。该方法构建我们将发送 HTTP 请求以创建设备及其三个组件观察值的 URL。URL 由base_url类属性、/data/device_id类属性组成。正如许多 REST API 所发生的那样,base_url类属性指定了 API 的版本号。这样,我们确保我们始终在特定版本上工作,并且我们的请求与该版本兼容。代码将构建的 URL 的值保存到url局部变量中。

然后,代码将板当前时间(以秒为单位乘以 1000)保存到now局部变量中。代码创建一个body字典,该字典代表请求的正文,并包含以下键值对:

  • "on": 存储在now局部变量中的值,即板的当前时间。它是观察的时间。

  • "accountId": 存储在accountId类属性中的值,即我们将发布观察值的 Intel IoT Analytics 账户。

  • "data": 一个空数组,我们将在稍后用每个组件的一个观察值填充。

然后,代码创建三个字典,包含以下键值对,代表特定组件的观察值:

  • "componentId": 存储在指定要发布观察值的组件 ID 的类属性中的值。

  • "on": 存储在now局部变量中的值,即板的当前时间。它是观察的时间。我们使用相同的变量对所有观察值进行操作,因此它们以相同的时间注册。

  • "value": 方法中作为参数接收的值的字符串表示。

然后,代码调用append方法将三个字典添加到body字典中的data键。这样,data键将有一个包含三个字典的数组作为其值。代码调用json.dumps函数将body字典序列化为 JSON 格式的字符串,并将其保存到data局部变量中。

下一行创建一个headers字典,包含以下键值对,代表 HTTP 请求的头部:

  • "Authorization": 由"Bearer"和保存在device_token类属性中的设备令牌连接而成的授权字符串

  • "content-type": 声明内容类型为 JSON: "application/json"

在这个阶段,代码已经构建了用于发布观测数据到英特尔物联网分析服务的 HTTP 请求的头部和主体。下一行调用requests.post函数,向由url局部变量指定的 URL 发送一个 HTTP POST 请求,其中data字典作为 JSON 主体数据,headers字典作为头部。

requests.post方法返回一个保存在response局部变量的响应,代码评估响应的 code 属性是否不等于 201。如果 code 与 201 不同,这意味着观测数据没有成功发布,即出了问题。在这种情况下,代码将响应的status_codetext属性的值打印到控制台输出,以便我们了解出了什么问题。如果我们使用错误的设备令牌或账户、设备或组件的 id,我们将收到错误。

现在,我们将使用之前编写的IntelIoTAnalytics类来创建一个新的__main__方法版本,该方法每 5 秒向英特尔物联网分析服务发布一次观测数据。以下行显示了新的__main__方法。示例的代码文件为iot_python_chapter_10_01.py

if __name__ == "__main__":
    temperature_and_humidity_sensor = \
        TemperatureAndHumiditySensor(0)
    oled = TemperatureAndHumidityOled(0)
 intel_iot_analytics = IntelIotAnalytics()
    while True:
        temperature_and_humidity_sensor.\
            measure_temperature_and_humidity()
        oled.print_temperature(
            temperature_and_humidity_sensor.temperature_fahrenheit,
            temperature_and_humidity_sensor.temperature_celsius)
        oled.print_humidity(
            temperature_and_humidity_sensor.humidity)
        print("Ambient temperature in degrees Celsius: {0}".
              format(temperature_and_humidity_sensor.temperature_celsius))
        print("Ambient temperature in degrees Fahrenheit: {0}".
              format(temperature_and_humidity_sensor.temperature_fahrenheit))
        print("Ambient humidity: {0}".
              format(temperature_and_humidity_sensor.humidity))
 intel_iot_analytics.publish_observation(
 temperature_and_humidity_sensor.temperature_fahrenheit,
 temperature_and_humidity_sensor.temperature_celsius,
 temperature_and_humidity_sensor.humidity
 )
 # Sleep 5 seconds (5000 milliseconds)
 time.sleep(5)

突出的行显示了创建之前创建的IntelIoTAnalytics类实例并将其引用保存到intel_iot_analytics局部变量的代码。然后,在每 5 秒运行一次的循环中的代码使用从温度和湿度传感器检索的温度和湿度值作为参数调用publish_observation方法。

以下行将启动示例:

python iot_python_chapter_10_01.py

运行示例后,打开空调或加热系统,以产生环境温度和湿度的变化。这样,我们将注意到每 5 秒发布的数据中的变化。在我们探索英特尔物联网分析服务中包含的不同功能时,请保持代码运行。

前往您正在使用的英特尔物联网分析仪表板的网页浏览器,点击菜单图标并选择仪表板。网站将显示我的仪表板页面,它将指示您有一个活动设备,并且当它从板子上接收观测数据时,它将更新过去一小时发布的观测数据数量。以下图片显示了包含活动设备和包含过去一小时发布945次观测数据的计数器的仪表板:

https://github.com/OpenDocCN/freelearn-python-zh/raw/master/docs/iot-py/img/B05042_10_08.jpg

保持浏览器打开并显示仪表板视图,您会注意到在过去一小时中观察值增加,因为代码在板上继续运行。您可以在显示观察数量和上下文菜单的面板右上角点击配置图标,上下文菜单允许您配置在此面板中查看的观察周期。例如,您可以将上一小时更改为上一周以显示设备在过去一周内注册的观察数量。

使用英特尔物联网分析工具分析传感器数据

英特尔物联网分析工具允许我们为具有特定设备观察数据的每个组件生成图表。首先,我们必须选择设备,然后我们必须选择一个或多个组件以生成带有历史时间序列或由板上运行的代码生成的时间序列的图表,即组件的实时数据。

打开您正在使用英特尔物联网分析仪表板的浏览器,点击菜单图标并选择图表。网站将显示我的图表页面,允许您使用许多搜索条件搜索设备,例如设备名称、相关标签及其属性。

在这种情况下,我们只有一个激活的设备,因此我们可以从网站在选择设备部分下方显示的设备列表中选择设备。此部分在复选框的右侧显示设备名称的首个字符,并在文本的右侧显示已为此设备注册的组件数量。以下图片显示了选择设备部分,其中**kansas-temp…代表kansas-temperature-humidity-01设备。如果您将鼠标悬停在复选框上或轻触文本,网站将显示一个弹出窗口,显示设备的完整名称和已注册组件的类型。以下截图显示了包含此信息的弹出窗口,显示在kansas-temp…**复选框上。

https://github.com/OpenDocCN/freelearn-python-zh/raw/master/docs/iot-py/img/B05042_10_09.jpg

选择**kansas-temp…**复选框,网站将显示所选设备的三个注册组件。在这种情况下,网站显示组件名称(temperaturectemperaturefhumidity),而在之前解释的弹出窗口中,网站显示了组件类型(temperature.v1.0temperaturef.v1.0humidity.v1.0)。

选择temperaturef复选框,网站将显示过去一小时测量的环境温度图表,单位为华氏度。默认情况下,图表使用线条并生成过去一小时记录的时间序列值图表。默认情况下,图表的刷新率为 30 秒,因此图表将每 30 秒更新一次,并显示在此期间通过 REST API 由板发布的所有新观测值。

https://github.com/OpenDocCN/freelearn-python-zh/raw/master/docs/iot-py/img/B05042_10_10.jpg

我们可以使用图表顶部的不同按钮来更改图表类型,并选择我们想要在图表中显示的时间范围。我们还可以将刷新率更改为低至 5 秒或高达 60 秒。如果我们将图表保存为收藏夹,网站将在我的仪表板中将其显示为仪表板的一部分。

点击位于图表按钮(一个带有山脉的图片图标)右侧的原始数据按钮(一个子弹图标)。网站将显示一个列表,其中包含用于构建时间序列的原始数据,即所选组件接收到的所有观测值。以下截图显示了过去一小时temperaturef组件的原始数据视图的第一页。

https://github.com/OpenDocCN/freelearn-python-zh/raw/master/docs/iot-py/img/B05042_10_11.jpg

在这个例子中,生成一个包含温度和湿度水平的图表非常有用。通过点击图表按钮(一个带有山脉的图片图标)返回图表视图,并勾选湿度复选框。这样,网站将生成一个图表,结合华氏度表示的温度和百分比表示的湿度水平。以下截图显示了当temperaturefhumidity都勾选时生成的图表:

https://github.com/OpenDocCN/freelearn-python-zh/raw/master/docs/iot-py/img/B05042_10_12.jpg

使用英特尔物联网分析中的规则触发警报

英特尔物联网分析允许我们定义可以触发以下任何通知类型的规则:

  • 电子邮件

  • HTTP 端点

  • 动作

打开你正在使用的英特尔物联网分析仪表板的网页浏览器,点击菜单图标并选择规则。网站将显示我的规则页面,允许你为激活的设备定义规则。在这种情况下,我们将定义一个规则,当湿度水平低于 10%时,将发送电子邮件给我们。

点击添加规则,网站将显示一个表单,允许我们输入新规则的详细信息。在规则名称中输入非常低的湿度水平,在优先级中选择,在通知类型中选择电子邮件。在通知对象面板的下拉菜单中选择你想要接收通知的电子邮件地址。

点击下一步,网站将要求我们选择要应用新规则的设备。在这种情况下,我们只有一个激活的设备,因此我们可以从网站在选择设备部分下方显示的设备列表中选择设备。如前述设备选择页面所示,此部分在复选框的右侧显示设备名称的第一个字符,在文本的右侧显示已为此设备注册的组件数量。勾选kansas-temp…复选框,名称将出现在已选设备列表中。

点击下一步,网站将要求我们指定新规则的条件。不要勾选启用自动重置复选框,因为我们希望规则在每次警报后变为不活动状态,直到被确认。这样,在收到警报后,我们只有在确认了第一个生成的警报后才会收到额外的警报。

监控度量中选择湿度(数字),在触发条件中选择基本条件。然后,在出现的附加下拉菜单中选择<,并在输入值文本框中输入10。这样,我们正在创建一个当湿度观测值低于 10(湿度 < 10)时将触发的规则。以下截图显示了定义的条件:

https://github.com/OpenDocCN/freelearn-python-zh/raw/master/docs/iot-py/img/B05042_10_13.jpg

点击完成,规则将被添加到我的规则中显示的列表中。以下截图显示了定义后包含在此列表中的规则定义:

https://github.com/OpenDocCN/freelearn-python-zh/raw/master/docs/iot-py/img/B05042_10_14.jpg

当湿度低于 10%时,将触发警报,我们将在警报图标(铃铛)中看到一个数字 1。点击图标后,网站将显示我们所有未读的警报。以下截图显示了包含一个未读警报的我的仪表盘页面:

https://github.com/OpenDocCN/freelearn-python-zh/raw/master/docs/iot-py/img/B05042_10_15.jpg

如果我们点击警报,网站将显示触发警报的情况的详细信息。我们还可以在菜单中选择警报,查看接收到的警报列表。以下截图显示了包含在接收到的警报列表中的警报:

https://github.com/OpenDocCN/freelearn-python-zh/raw/master/docs/iot-py/img/B05042_10_16.jpg

如果我们点击警报编号,网站将显示警报的详细信息,包括触发警报的规则中定义的条件和测量的值。在这种情况下,测量的值是7.99。可以向警报添加注释。以下截图显示了警报的详细信息:

https://github.com/OpenDocCN/freelearn-python-zh/raw/master/docs/iot-py/img/B05042_10_17.jpg

此外,我们还会收到一封包含以下文本的电子邮件:

Alert Monitor has received an alert. Alert data: 

- Alert Id: 1 
- Device: kansas-temperature-humidity-01 
- Reason: humidity < 10 
- Priority: Low 

Alert Data
Component Name	Values
humidity	7.99; 

You can go here to check it on Dashboard 
Regards

在这种情况下,我们在规则中定义了一个非常简单的条件。然而,我们可以定义一个更复杂的条件,该条件可以包括以下任何条件:

  • 基于时间的条件

  • 基于统计的条件

  • 单传感器变化检测

  • 多感官变化检测

我们可以玩转不同的选项,为具有多个传感器和大量数据的众多设备触发警报。英特尔物联网分析最有趣的功能之一是我们可以用图表、规则和警报轻松地处理大量数据。

测试你的知识

  1. 英特尔物联网分析中每个设备的组件可以是:

    1. 执行器或时间序列。

    2. 账户、执行器或时间序列。

    3. 代理、账户、执行器或时间序列。

  2. 每次我们将从注册设备发布数据到英特尔物联网分析时,我们都会创建:

    1. 执行器。

    2. 账户。

    3. 观察结果。

  3. 时间序列是:

    1. 执行器执行的一系列动作,即动作的集合。

    2. 从传感器捕获的一系列值,即观察的集合。

    3. 一系列触发的警报,即警报的集合。

  4. 我们可以使用以下命令行工具激活我们的板作为英特尔物联网分析账户中的一个设备:

    1. iotkit-admin

    2. iotkit-configure

    3. iotkit-setup

  5. 为了使用英特尔物联网分析提供的 REST API 从设备发送观察结果,我们需要以下令牌:

    1. 传感器令牌。

    2. 观察令牌。

    3. 设备令牌。

摘要

在本章中,我们了解了物联网与大数据之间的紧密关系。我们与一个基于云的服务合作,该服务允许我们组织由多个设备和其传感器收集的大量数据。我们利用requests包编写了几行 Python 代码,可以与英特尔物联网分析 REST API 交互。

我们使用英特尔物联网分析网站来设置设备和其组件。然后,我们对我们的一个示例进行了修改,以从传感器收集数据并将观察结果发布到英特尔物联网分析。然后,我们了解了英特尔物联网分析为我们提供的不同选项,以分析大量数据。最后,我们定义了触发警报的规则。现在,我们能够利用英特尔物联网分析来分析大量数据,我们准备部署成千上万的物联网设备,这些设备从多个传感器收集数据。

我们学会了使用 Python 和英特尔 Galileo Gen 2 板来创建低成本设备,这些设备可以收集大量数据,相互交互,并利用云服务和基于云的存储。我们可以使用 Python 2.7.3 及其库和工具从硬件选择到所有必要的堆栈开发物联网原型。如果我们需要更小的板或不同的替代品,我们可以切换到任何兼容的英特尔 Edison 板,因此,如果我们需要,我们可以切换到这块板。

我们能够利用现有的 Python 知识从现实世界中捕获数据,与物理对象交互,开发 API 并使用不同的物联网协议。我们学会了使用特定的库来处理底层硬件、传感器、执行器、总线和显示器。我们准备好成为创造者,并成为激动人心的物联网世界的一部分。

我们可以开始着手于将日常物体转变为带有传感器和执行器的智能设备的项目。我们准备好开始构建由数千个物联网设备组成的生态系统,Python 作为我们的主要编程语言。

附录 A. 练习答案

第一章,理解和设置基础物联网硬件

Q12
Q21
Q32
Q43
Q51

第二章,在英特尔 Galileo Gen 2 上使用 Python

Q12
Q21
Q32
Q43
Q51

第三章,使用 Python 与数字输出交互

Q13
Q21
Q31
Q42
Q52

第四章,使用 RESTful API 和脉宽调制进行工作

Q13
Q23
Q32
Q41
Q52

第五章,使用数字输入、轮询和中断

Q11
Q22
Q31
Q42
Q53

第六章,使用模拟输入和本地存储

Q13
Q21
Q32
Q41
Q53

第七章,使用传感器从现实世界获取数据

Q12
Q21
Q32
Q43
Q51

第八章,显示信息和执行操作

Q11
Q21
Q33
Q43
Q52

第九章,使用云服务

Q12
Q21
Q33
Q42
Q51

第十章,使用基于云的物联网分析分析大量数据

Q11
Q23
Q32
Q41
Q53
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值