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

使用 Azure IoT Workbench 的 MXChip 设备(带压力、湿度、温度信息)

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.58/5 (6投票s)

2019年1月4日

CPOL

5分钟阅读

viewsIcon

9469

设置 MXChip 并使其能够将信息发送到 Azure IoT Hub。

引言

我几天前收到了我的 MXChip,并且一直在玩它。在本文中,我们将了解如何设置我们的 MXChip 并使其正常工作,将温度、湿度、压力等信息发送到我们的 Azure IoT Hub。一旦我们在 Azure IoT Hub 中收到了数据,我们就可以对它做任何事情。我之前已经写过一些关于这个主题的文章,你可以在这里 阅读。现在让我们配置我们的 IoT 设备。

背景

MXChip 是一款集成了多种传感器的微控制器。这款设备的主要优点是它连接到 Azure,这使得设备到云和云到设备的传输比以往任何时候都更容易。在本文中,我们将借助 Visual Studio Code 中一个名为 Azure IoT Workbench 的工具/扩展来配置此设备。配置完成后,我们可以修改 CPP 代码,将压力传感器发送的压力信息发送到 Azure IoT Hub。

源代码

请随时在这里 玩转这个仓库

设置系统

在开始开发我们的设备应用程序/代码之前,我们需要先设置好我们的环境。所以,请确保你遵循本文并 配置你的入门项目

Using the Code

一旦你能够看到 D2C(设备到云)和 C2D(云到设备)数据,我们就可以开始编写代码来获取压力传感器 (LPS22HB) 的压力信息了。

如果你打开由 Azure IoT Workbench 工具生成的入门教程的设备代码,你会看到如下文件:

Azure IoT Workbench 工作区文件

在这里,azureconfig.json 文件包含你的 IoT Hub、Event Hub 的连接字符串信息。

{
    "componentConfigs": [
        {
            "id": "e8d86334-156d-e40b-9618-a6a54bb94b25",
            "folder": "",
            "name": "",
            "dependencies": [],
            "type": "IoTHub",
            "componentInfo": {
                "values": {
                    "iotHubConnectionString": "",
                    "eventHubConnectionString": "",
                    "eventHubConnectionPath": ""
                }
            }
        }
    ]
}

project.code-workspace 文件将包含你的设备代码设置,包括设备路径。通常,你不需要检查这些文件,因为在入门教程中提到的配置过程中,这些值会自动创建。

Device 文件夹将包含你的 Arduino 代码,我们在这里使用 CPP (C++) 编写代码。如果你在 Visual Studio Code 中打开解决方案,它会提示你打开工作区,也就是我们的设备代码。只需点击“打开工作区”,然后我们就可以开始编码了。

Config.h 文件是我们的配置文件。

#define INTERVAL 2000
#define MESSAGE_MAX_LEN 256
#define TEMPERATURE_ALERT 30

GetStarted.ino 文件包含初始设置的代码,即让设备运行。它包含 Wi-Fi 配置、设备到云、云到设备通信等代码。下面是示例代码:

// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. 
// To get started,
// please visit https://msdocs.cn/azure-iot-developer-kit/docs/projects/
// connect-iot-hub?utm_source=ArduinoExtension&utm_medium=ReleaseNote&utm_campaign=VSCode
#include "AZ3166WiFi.h"
#include "AzureIotHub.h"
#include "DevKitMQTTClient.h"

#include "config.h"
#include "utility.h"
#include "SystemTickCounter.h"

static bool hasWifi = false;
int messageCount = 1;
static bool messageSending = true;
static uint64_t send_interval_ms;

//////////////////////////////////////////////////////////////////////////////////
// Utilities
static void InitWifi()
{
  Screen.print(2, "Connecting...");
  
  if (WiFi.begin() == WL_CONNECTED)
  {
    IPAddress ip = WiFi.localIP();
    Screen.print(1, ip.get_address());
    hasWifi = true;
    Screen.print(2, "Running... \r\n");
  }
  else
  {
    hasWifi = false;
    Screen.print(1, "No Wi-Fi\r\n ");
  }
}

static void SendConfirmationCallback(IOTHUB_CLIENT_CONFIRMATION_RESULT result)
{
  if (result == IOTHUB_CLIENT_CONFIRMATION_OK)
  {
    blinkSendConfirmation();
  }
}

static void MessageCallback(const char* payLoad, int size)
{
  blinkLED();
  Screen.print(1, payLoad, true);
}

static void DeviceTwinCallback(DEVICE_TWIN_UPDATE_STATE updateState, 
       const unsigned char *payLoad, int size)
{
  char *temp = (char *)malloc(size + 1);
  if (temp == NULL)
  {
    return;
  }
  memcpy(temp, payLoad, size);
  temp[size] = '\0';
  parseTwinMessage(updateState, temp);
  free(temp);
}

static int  DeviceMethodCallback(const char *methodName, 
     const unsigned char *payload, int size, 
     unsigned char **response, int *response_size)
{
  LogInfo("Try to invoke method %s", methodName);
  const char *responseMessage = "\"Successfully invoke device method\"";
  int result = 200;

  if (strcmp(methodName, "start") == 0)
  {
    LogInfo("Start sending temperature and humidity data");
    messageSending = true;
  }
  else if (strcmp(methodName, "stop") == 0)
  {
    LogInfo("Stop sending temperature and humidity data");
    messageSending = false;
  }
  else
  {
    LogInfo("No method %s found", methodName);
    responseMessage = "\"No method found\"";
    result = 404;
  }

  *response_size = strlen(responseMessage) + 1;
  *response = (unsigned char *)strdup(responseMessage);

  return result;
}

/////////////////////////////////////////////////////////////////////////////////
// Arduino sketch
void setup()
{
  Screen.init();
  Screen.print(0, "IoT DevKit");
  Screen.print(2, "Initializing...");
  
  Screen.print(3, " > Serial");
  Serial.begin(115200);

  // Initialize the WiFi module
  Screen.print(3, " > WiFi");
  hasWifi = false;
  InitWifi();
  if (!hasWifi)
  {
    return;
  }

  LogTrace("HappyPathSetup", NULL);

  Screen.print(3, " > Sensors");
  SensorInit();

  Screen.print(3, " > IoT Hub");
  DevKitMQTTClient_SetOption(OPTION_MINI_SOLUTION_NAME, "DevKit-GetStarted");
  DevKitMQTTClient_Init(true);

  DevKitMQTTClient_SetSendConfirmationCallback(SendConfirmationCallback);
  DevKitMQTTClient_SetMessageCallback(MessageCallback);
  DevKitMQTTClient_SetDeviceTwinCallback(DeviceTwinCallback);
  DevKitMQTTClient_SetDeviceMethodCallback(DeviceMethodCallback);

  send_interval_ms = SystemTickCounterRead();
}

void loop()
{
  if (hasWifi)
  {
    if (messageSending && 
        (int)(SystemTickCounterRead() - send_interval_ms) >= getInterval())
    {
      // Send temperature data
      char messagePayload[MESSAGE_MAX_LEN];

      bool temperatureAlert = readMessage(messageCount++, messagePayload);
      EVENT_INSTANCE* message = 
                      DevKitMQTTClient_Event_Generate(messagePayload, MESSAGE);
      DevKitMQTTClient_Event_AddProp
              (message, "temperatureAlert", temperatureAlert ? "true" : "false");
      DevKitMQTTClient_SendEventInstance(message);
      
      send_interval_ms = SystemTickCounterRead();
    }
    else
    {
      DevKitMQTTClient_Check();
    }
  }
  delay(1000);
}

以下是 utility.h 文件的内容。

#ifndef UTILITY_H
#define UTILITY_H

void parseTwinMessage(DEVICE_TWIN_UPDATE_STATE, const char *);
bool readMessage(int, char *);

void SensorInit(void);

void blinkLED(void);
void blinkSendConfirmation(void);
int getInterval(void);

我们将定义所有这些函数,并将它们放在 utility.cpp 文件中,而我们的大部分代码都将在那里。当你完成 **入门** 教程后,初始代码将包含读取传感器温度、湿度信息并发送到云的函数。正如你所猜测的,它缺少读取和发送压力信息相关的代码。在这里,我们将完成这项工作。下面是初始代码:

// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. 

#include "HTS221Sensor.h"
#include "AzureIotHub.h"
#include "Arduino.h"
#include "parson.h"
#include "config.h"
#include "RGB_LED.h"

#define RGB_LED_BRIGHTNESS 32

DevI2C *i2c;
HTS221Sensor *sensor;
static RGB_LED rgbLed;
static int interval = INTERVAL;
static float humidity;
static float temperature;

int getInterval()
{
    return interval;
}

void blinkLED()
{
    rgbLed.turnOff();
    rgbLed.setColor(RGB_LED_BRIGHTNESS, 0, 0);
    delay(500);
    rgbLed.turnOff();
}

void blinkSendConfirmation()
{
    rgbLed.turnOff();
    rgbLed.setColor(0, 0, RGB_LED_BRIGHTNESS);
    delay(500);
    rgbLed.turnOff();
}

void parseTwinMessage(DEVICE_TWIN_UPDATE_STATE updateState, const char *message)
{
    JSON_Value *root_value;
    root_value = json_parse_string(message);
    if (json_value_get_type(root_value) != JSONObject)
    {
        if (root_value != NULL)
        {
            json_value_free(root_value);
        }
        LogError("parse %s failed", message);
        return;
    }
    JSON_Object *root_object = json_value_get_object(root_value);

    double val = 0;
    if (updateState == DEVICE_TWIN_UPDATE_COMPLETE)
    {
        JSON_Object *desired_object = json_object_get_object(root_object, "desired");
        if (desired_object != NULL)
        {
            val = json_object_get_number(desired_object, "interval");
        }
    }
    else
    {
        val = json_object_get_number(root_object, "interval");
    }
    if (val > 500)
    {
        interval = (int)val;
        LogInfo(">>>Device twin updated: set interval to %d", interval);
    }
    json_value_free(root_value);
}

void SensorInit()
{
    i2c = new DevI2C(D14, D15);
    sensor = new HTS221Sensor(*i2c);
    sensor->init(NULL);

    humidity = -1;
    temperature = -1000;
}

float readTemperature()
{
    sensor->reset();

    float temperature = 0;
    sensor->getTemperature(&temperature);

    return temperature;
}

float readHumidity()
{
    sensor->reset();

    float humidity = 0;
    sensor->getHumidity(&humidity);

    return humidity;
}

bool readMessage(int messageId, char *payload)
{
    JSON_Value *root_value = json_value_init_object();
    JSON_Object *root_object = json_value_get_object(root_value);
    char *serialized_string = NULL;

    json_object_set_number(root_object, "messageId", messageId);

    float t = readTemperature();
    float h = readHumidity();
    bool temperatureAlert = false;
    if(t != temperature)
    {
        temperature = t;
        json_object_set_number(root_object, "temperature", temperature);
    }
    if(temperature > TEMPERATURE_ALERT)
    {
        temperatureAlert = true;
    }
    
    if(h != humidity)
    {
        humidity = h;
        json_object_set_number(root_object, "humidity", humidity);
    }
    serialized_string = json_serialize_to_string_pretty(root_value);

    snprintf(payload, MESSAGE_MAX_LEN, "%s", serialized_string);
    json_free_serialized_string(serialized_string);
    json_value_free(root_value);
    return temperatureAlert;
}

我们使用不同的传感器来处理不同的项目,因此为不同的事物使用不同的 `抽象` 类。例如,`抽象` 类 `HTS221Sensor` 用于 `湿度` 和 `温度`,而 `LPS22HBSensor` 传感器用于压力。

所以,让我们包含 `LPS22HBSensor`。

#include "LPS22HBSensor.h"
#include "utility.h"

现在,创建一个该类的引用。

LPS22HBSensor *pSensor;
static float pressure;

通过初始化 `LPS22HBSensor` 类来修改 `SensorInit()` 函数。

void SensorInit()
{
    i2c = new DevI2C(D14, D15);
    sensor = new HTS221Sensor(*i2c);
    sensor->init(NULL);

    pSensor = new LPS22HBSensor(*i2c);
    pSensor->init(NULL);

    humidity = -1;
    temperature = -1000;
    pressure = 0;
}

现在,创建一个新的函数来从传感器读取 `压力`。

float readPressure()
{
    float pressure = 0;
    pSensor->getPressure(&pressure);

    return pressure;
}

现在是时候使用 `json_object_set_number` 函数将压力信息添加到输出 JSON 文件中。用以下代码修改 `readMessage` 函数:

float p = readPressure();
if (p != pressure) {
    pressure = p;
    json_object_set_number(root_object, "pressure", pressure);
}

现在,我们已经完成了设备的编码,是时候将代码上传到设备了。

将代码上传到 IoT 设备

由于我们已经安装了 IoT Workbench 工具,将新代码上传到设备非常容易。在 Visual Studio Code 中按 **F1**,然后选择“**Azure IoT Device Workbench: Upload Device Code**”。此命令将编译你的代码,并在 Output 终端中抛出任何错误。它会执行以下操作:

  1. 加载配置
  2. 初始化软件包
  3. 准备好你的开发板进行上传,此时编程 LED 将会闪烁
  4. 验证一切

如果一切顺利,你应该会看到如下输出:

Global variables use 60920 bytes (23%) of dynamic memory, 
leaving 201224 bytes for local variables. Maximum is 262144 bytes.
Uploading...
Info : clock speed 1800 kHz
Info : STLINK v2 JTAG v31 API v2 SWIM v21 VID 0x0483 PID 0x374B
Info : using stlink api v2
Info : Target voltage: 3.307278
** Programming Started **
auto erase enabled
Info : device id = 0x30006441
Info : flash size = 1024kbytes
** Programming Finished **
** Verify Started **
target halted due to breakpoint, current mode: Thread 
xPSR: 0x61000000 pc: 0x2000002e msp: 0x200073fc
verified 578060 bytes in 1.120772s (503.681 KiB/s)
** Verified OK **
** Resetting Target **
shutdown command invoked
[Done] Uploaded the sketch: GetStarted.ino

检查设备到云的消息

现在,是时候检查我们发送到 Azure IoT Hub 的信息了。在 Visual Studio Code 中转到 Azure IoT Hub Devices 部分,右键单击设备,然后选择“**Start Monitoring Device to Cloud (D2C) Message**”。

开始监控设备到云 (D2C) 消息

将打开一个新的 Output 终端,你可以在其中看到我们发送到云的数据。

从 MXChip 发送温度、湿度、压力输出
[IoTHubMonitor] Start monitoring D2C message for [ml-pf] ...
[IoTHubMonitor] Created partition receiver [0] for consumerGroup [$Default]
[IoTHubMonitor] Created partition receiver [1] for consumerGroup [$Default]
[IoTHubMonitor] Created partition receiver [2] for consumerGroup [$Default]
[IoTHubMonitor] Created partition receiver [3] for consumerGroup [$Default]
[IoTHubMonitor] [11:16:45 AM] Message received from [ml-pf]:
{
  "body": {
    "messageId": 198,
    "temperature": 28,
    "pressure": 1007.074707
  },
  "applicationProperties": {
    "temperatureAlert": "false"
  }
}

结论

哇!现在我们已经学习了

  • 如何在 Visual Studio Code 中使用 IoT Workbench 工具
  • 如何设置你的 MXChip 设备
  • 如何为 Arduino 编写 C++ 代码
  • 如何从传感器获取压力信息
  • 如何将新代码上传到 MXChip 设备
  • 如何执行设备到云的消息输出。

请继续阅读我的 IoT 文章 这里 以获取后续内容。

现在轮到你了。你有什么想法?

非常感谢你的阅读。这篇文章是否遗漏了你认为需要的内容?你觉得这篇文章有用吗?请不要忘记分享你的反馈。

历史

  • 2019年1月4日:初始版本
© . All rights reserved.