MXChip Device With Pressure, Humidity, Temperature Info Using Azure IoT Workbench

In this article, we will see how to set up our MXChip and get all the information, such as - Temperature, Humidity, Pressure etc. received to our Azure IoT Hub.

Introduction

I got my MXChip a few days ago and I have been playing with this since. Here, in this article, we will see how we can set up our MXChip and get it working to send Temperature, Humidity, Pressure etc. information to our Azure IoT Hub. Once we have received the data in our Azure IoT Hub, we can do anything with that. I have already written some articles on the same topic. Let’s configure our IoT device now. Please feel free to see this article on my blog here.

Background

MXChip is a microcontroller with a bunch of sensors in it. The main advantage of this device is that it is connected to Azure, which makes the device-to-cloud and cloud-to-device transmission easier than ever. Here, in this article, we will configure this device with the help of a tool/extension called Azure IoT Workbench in Visual Studio Code. Once we are done configuring, we can change the CPP code to send the pressure information from the pressure sensor to the Azure IoT Hub.

Source Code

Please feel free to play with this repository here.

Set Up the System

Before we get started with developing our device application/code, we need to set our environment first. So, please make sure that you are following this article and configure your Getting Started project.

Using the Code

Once you are able to see the D2C (Device to Cloud) and C2D (Cloud to Device) data, you are good to go and write some code to get the pressure information from the Pressure Sensor (LPS22HB).

If you open the device code from the "Getting Started" tutorial that got generated while using the Azure IoT Workbench tool, you can see the files as below.

MXChip Device with Pressure, Humidity, Temperature Info using Azure IoT Workbench 
Azure IoT Workbench Workspace Files

Here, the azureconfig.json file has the connection string information to your IoT Hub, Event Hub.

  1. {  
  2.     "componentConfigs": [{  
  3.         "id""e8d86334-156d-e40b-9618-a6a54bb94b25",  
  4.         "folder""",  
  5.         "name""",  
  6.         "dependencies": [],  
  7.         "type""IoTHub",  
  8.         "componentInfo": {  
  9.             "values": {  
  10.                 "iotHubConnectionString""",  
  11.                 "eventHubConnectionString""",  
  12.                 "eventHubConnectionPath"""  
  13.             }  
  14.         }  
  15.     }]  
  16. }  

The file project.code-workspace will have your device code settings, including the device path. Usually, you wouldn’t need to check these files, as these values are automatically created when you are doing the provisioning as mentioned in the "Get started" tutorial.

The folder "Device" will have your Arduino code. Here, we are writing the codes in CPP (C++). If you open the solution in Visual Studio code, it will ask you to open the workspace, which is nothing but your Device code. Just click on "Open Workspace", then you can start coding.

The Config.h file is our configuration file.

  1. #define INTERVAL 2000   
  2. #define MESSAGE_MAX_LEN 256   
  3. #define TEMPERATURE_ALERT 30     

The GetStarted.ino file contains the code for initial set up, that is, to make the device run. It contains the code for WiFi configuration, and Device-to-Cloud, Cloud-to-Device communication etc. Below is the sample code.

  1. // Copyright (c) Microsoft. All rights reserved.   
  2. // Licensed under the MIT license.   
  3. // To get started please visit https://microsoft.github.io/azure-iot-developer-kit/docs/projects/connect-iot-hub?utm_source=ArduinoExtension&utm_medium=ReleaseNote&utm_campaign=VSCode   
  4. #include "AZ3166WiFi.h"   
  5. #include "AzureIotHub.h"   
  6. #include "DevKitMQTTClient.h"   
  7. #include "config.h"   
  8. #include "utility.h"   
  9. #include "SystemTickCounter.h"   
  10.   
  11. static bool hasWifi = false;   
  12. int messageCount = 1;   
  13. static bool messageSending = true;   
  14. static uint64_t send_interval_ms;   
  15. //////////////////////////////////////////////////////////////////////////////////////////////////////////   
  16. // Utilities static void InitWifi()   
  17. {   
  18.   Screen.print(2, "Connecting...");   
  19.   if (WiFi.begin() == WL_CONNECTED)   
  20.   {   
  21.     IPAddress ip = WiFi.localIP();   
  22.     Screen.print(1, ip.get_address());   
  23.     hasWifi = true;   
  24.     Screen.print(2, "Running... \r\n");   
  25.   }  
  26.   else   
  27.   {   
  28.     hasWifi = false;   
  29.     Screen.print(1, "No Wi-Fi\r\n ");  
  30.   }   
  31. }   
  32. static void SendConfirmationCallback(IOTHUB_CLIENT_CONFIRMATION_RESULT result)   
  33. {   
  34.   if   
  35.     (result == IOTHUB_CLIENT_CONFIRMATION_OK)   
  36.   {   
  37.     blinkSendConfirmation();   
  38.   }   
  39. }   
  40. static void MessageCallback(const char* payLoad, int size)   
  41. {   
  42.   blinkLED();   
  43.   Screen.print(1, payLoad, true);   
  44. }   
  45. static void DeviceTwinCallback(DEVICE_TWIN_UPDATE_STATE updateState, const unsigned char *payLoad, int size)   
  46. {   
  47.   char *temp = (char *)malloc(size + 1);   
  48.   if (temp == NULL)   
  49.   {   
  50.     return;   
  51.   }   
  52.   memcpy(temp, payLoad, size);   
  53.   temp[size] = '\0';   
  54.   parseTwinMessage(updateState, temp);   
  55.   free(temp);   
  56. }   
  57. static int DeviceMethodCallback(const char *methodName, const unsigned char *payload, int size, unsigned char **response, int *response_size)   
  58. {   
  59.   LogInfo("Try to invoke method %s", methodName);   
  60.   const char *responseMessage = "\"Successfully invoke device method\"";   
  61.   int result = 200;   
  62.   if (strcmp(methodName, "start") == 0)   
  63.   {   
  64.     LogInfo("Start sending temperature and humidity data");   
  65.     messageSending = true;   
  66.   }   
  67.   else if (strcmp(methodName, "stop") == 0)   
  68.   {   
  69.     LogInfo("Stop sending temperature and humidity data");   
  70.     messageSending = false;  
  71.   }   
  72.   else   
  73.   {   
  74.     LogInfo("No method %s found", methodName);   
  75.     responseMessage = "\"No method found\"";   
  76.     result = 404;   
  77.   }   
  78.   *response_size = strlen(responseMessage) + 1;   
  79.   *response = (unsigned char *)strdup(responseMessage);   
  80.   return result;   
  81. ////////////////////////////////////////////////////////////////////////////////////////////////////////// //   
  82. Arduino sketch void setup()   
  83. {   
  84.   Screen.init();   
  85.   Screen.print(0, "IoT DevKit");   
  86.   Screen.print(2, "Initializing...");   
  87.   Screen.print(3, " > Serial");   
  88.   Serial.begin(115200);   
  89.   // Initialize the WiFi module   
  90.   Screen.print(3, " > WiFi");   
  91.   hasWifi = false; InitWifi();   
  92.   if (!hasWifi)   
  93.   {   
  94.     return;   
  95.   }   
  96.   LogTrace("HappyPathSetup", NULL);   
  97.   Screen.print(3, " > Sensors");   
  98.   SensorInit();   
  99.   Screen.print(3, " > IoT Hub");   
  100.   DevKitMQTTClient_SetOption(OPTION_MINI_SOLUTION_NAME, "DevKit-GetStarted");   
  101.   DevKitMQTTClient_Init(true);   
  102.   DevKitMQTTClient_SetSendConfirmationCallback(SendConfirmationCallback);   
  103.   DevKitMQTTClient_SetMessageCallback(MessageCallback);   
  104.   DevKitMQTTClient_SetDeviceTwinCallback(DeviceTwinCallback);   
  105.   DevKitMQTTClient_SetDeviceMethodCallback(DeviceMethodCallback);   
  106.   send_interval_ms = SystemTickCounterRead();   
  107. }   
  108. void loop()  
  109. {   
  110.   if (hasWifi)   
  111.   {  
  112.     if (messageSending && (int)(SystemTickCounterRead() - send_interval_ms) >= getInterval())  
  113.     {   
  114.       // Send teperature data   
  115.       char messagePayload[MESSAGE_MAX_LEN];   
  116.       bool temperatureAlert = readMessage(messageCount++, messagePayload);   
  117.       EVENT_INSTANCE* message = DevKitMQTTClient_Event_Generate(messagePayload, MESSAGE);   
  118.       DevKitMQTTClient_Event_AddProp(message, "temperatureAlert", temperatureAlert ? "true" : "false");   
  119.       DevKitMQTTClient_SendEventInstance(message);   
  120.       send_interval_ms = SystemTickCounterRead();   
  121.     }   
  122.     else  
  123.     {   
  124.       DevKitMQTTClient_Check();  
  125.     }   
  126.   } delay(1000);  
  127. }  

Below is the content of the file utility.h.

  1. #ifndef UTILITY_H  
  2. #define UTILITY_H   
  3. void parseTwinMessage(DEVICE_TWIN_UPDATE_STATE, const char * );  
  4. bool readMessage(intchar * );  
  5. void SensorInit(void);  
  6. void blinkLED(void);  
  7. void blinkSendConfirmation(void);  
  8. int getInterval(void);  

We will be defining all these functions inside the file called utility.cpp. That’s where most of our code is going to be. When you are finished with the "Get Started" tutorial, the initial code will contain the functions which can read the temperature, humidity information from the sensor and send to the cloud. And as you have guessed, it is missing the code for reading and sending the pressure information. Here, we are going to do that. Below is the initial code.

  1. // Copyright (c) Microsoft. All rights reserved.   
  2. // Licensed under the MIT license.   
  3. #include "HTS221Sensor.h"   
  4. #include "AzureIotHub.h"   
  5. #include "Arduino.h"   
  6. #include "parson.h"   
  7. #include "config.h"   
  8. #include "RGB_LED.h"   
  9. #define RGB_LED_BRIGHTNESS 32 DevI2C *i2c;  
  10. HTS221Sensor *sensor;   
  11. static RGB_LED rgbLed;   
  12. static int interval = INTERVAL;   
  13. static float humidity;   
  14. static float temperature;   
  15. int getInterval()   
  16. {   
  17.   return interval;   
  18. }   
  19. void blinkLED()   
  20. {   
  21.   rgbLed.turnOff();   
  22.   rgbLed.setColor(RGB_LED_BRIGHTNESS, 0, 0);   
  23.   delay(500);   
  24.   rgbLed.turnOff();   
  25. }   
  26. void blinkSendConfirmation()   
  27. {   
  28.   rgbLed.turnOff();   
  29.   rgbLed.setColor(0, 0, RGB_LED_BRIGHTNESS);   
  30.   delay(500);   
  31.   rgbLed.turnOff();   
  32. }  
  33. void parseTwinMessage(DEVICE_TWIN_UPDATE_STATE updateState, const char *message)   
  34. {   
  35.   JSON_Value *root_value;   
  36.   root_value = json_parse_string(message);  
  37.   if (json_value_get_type(root_value) != JSONObject)   
  38.   {   
  39.     if (root_value != NULL)   
  40.     {   
  41.       json_value_free(root_value);   
  42.      }   
  43.     LogError("parse %s failed", message);   
  44.     return;   
  45.   }   
  46.   JSON_Object *root_object = json_value_get_object(root_value);   
  47.   double val = 0;   
  48.   if (updateState == DEVICE_TWIN_UPDATE_COMPLETE)  
  49.   {  
  50.     JSON_Object *desired_object = json_object_get_object(root_object, "desired");   
  51.     if (desired_object != NULL)   
  52.     {   
  53.       val = json_object_get_number(desired_object, "interval");  
  54.     }   
  55.   }   
  56.   else  
  57.   {   
  58.     val = json_object_get_number(root_object, "interval");   
  59.   }   
  60.   if (val > 500)   
  61.   {   
  62.     interval = (int)val;   
  63.     LogInfo(">>>Device twin updated: set interval to %d", interval);   
  64.   }   
  65.   json_value_free(root_value);   
  66. }   
  67. void SensorInit()   
  68. {   
  69.   i2c = new DevI2C(D14, D15);   
  70.   sensor = new HTS221Sensor(*i2c);   
  71.   sensor->init(NULL);   
  72.   humidity = -1;   
  73.   temperature = -1000;   
  74. }   
  75. float readTemperature()  
  76. {   
  77.   sensor->reset();   
  78.   float temperature = 0;   
  79.     
  80.   sensor->getTemperature(&temperature);   
  81.   return temperature;  
  82. }  
  83. float readHumidity()   
  84. {   
  85.   sensor->reset();   
  86.   float humidity = 0;   
  87.   sensor->getHumidity(&humidity);   
  88.   return humidity;  
  89. }   
  90. bool readMessage(int messageId, char *payload)  
  91. {   
  92.   JSON_Value *root_value = json_value_init_object();   
  93.   JSON_Object *root_object = json_value_get_object(root_value);   
  94.   char *serialized_string = NULL;   
  95.   json_object_set_number(root_object, "messageId", messageId);   
  96.   float t = readTemperature();   
  97.   float h = readHumidity();   
  98.   bool temperatureAlert = false;   
  99.   if(t != temperature)   
  100.   {   
  101.     temperature = t;   
  102.     json_object_set_number(root_object, "temperature", temperature);  
  103.   }   
  104.   if(temperature > TEMPERATURE_ALERT)  
  105.   {   
  106.     temperatureAlert = true;  
  107.   }   
  108.   if(h != humidity)   
  109.   {   
  110.     humidity = h;   
  111.     json_object_set_number(root_object, "humidity", humidity);   
  112.   }   
  113.   serialized_string = json_serialize_to_string_pretty(root_value);   
  114.   snprintf(payload, MESSAGE_MAX_LEN, "%s", serialized_string);   
  115.   json_free_serialized_string(serialized_string);   
  116.   json_value_free(root_value);   
  117.   return temperatureAlert;   
  118. }  

We use different sensors for different items, thus different abstract classes for different things. For example, the abstract class HTS221Sensor is used for Humidity and Temperature, and LPS22HBSensor sensor for pressure.

So let’s include the LPS22HBSensor.

  1. #include "LPS22HBSensor.h"   
  2. #include "utility.h"  

Now, create a reference of the same.

  1. LPS22HBSensor *pSensor;   
  2. static float pressure;  

Modify the SensorInit() function by initializing the class LPS22HBSensor.

  1. void SensorInit() {  
  2.     i2c = new DevI2C(D14, D15);  
  3.     sensor = new HTS221Sensor( * i2c);  
  4.     sensor - > init(NULL);  
  5.     pSensor = new LPS22HBSensor( * i2c);  
  6.     pSensor - > init(NULL);  
  7.     humidity = -1;  
  8.     temperature = -1000;  
  9.     pressure = 0;  

Now, create a new function which can read the Pressure from the sensor.

  1. float readPressure() {  
  2.     float pressure = 0;  
  3.     pSensor - > getPressure( & pressure);  
  4.     return pressure;  
  5. }  

Now it is time to add the pressure information to the output JSON file, by using the function json_object_set_number. Modify the function readMessage with the following code.

  1. float p = readPressure();  
  2. if (p != pressure) {  
  3.     pressure = p;  
  4.     json_object_set_number(root_object, "pressure", pressure);  
  5. }  

Now, we have done  the coding for our device, and it is time to upload the code to the device.

Upload the Code to IoT Device

As we already have the IoT Workbench tool installed, it is super easy to upload the new code to the device. Press F1 in Visual Studio code and select ‘Azure IoT Device Workbench: Upload Device Code’. This command will compile your code, and it throws the error in the Output terminal if there are any. It does the following actions.

  1. Load the configurations
  2. Initialize the packages
  3. Prepare your board for the upload, the Programming LED will blink at this time
  4. Verify everything

If everything goes well, you should be able to see an output as below.

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
 
Check the Device to Cloud Messages
 
Now, it is time to check the information we are passing to our Azure IoT Hub. Go to Azure IoT Hub Devices section in Visual Studio Code, and right-click on the device and select ‘Start Monitoring Device to Cloud (D2C) Message’
MXChip Device with Pressure, Humidity, Temperature Info using Azure IoT Workbench
Start Monitoring Device to Cloud (D2C) Message

A new Output Terminal will get opened, where you can see the data we send to cloud.

MXChip Device with Pressure, Humidity, Temperature Info using Azure IoT Workbench
Send Temperature, Humidity, Pressure from MXChip Output
  1. [IoTHubMonitor] Start monitoring D2C message  
  2. for [ml - pf]...[IoTHubMonitor] Created partition receiver[0]  
  3. for consumerGroup[$Default][IoTHubMonitor] Created partition receiver[1]  
  4. for consumerGroup[$Default][IoTHubMonitor] Created partition receiver[2]  
  5. for consumerGroup[$Default][IoTHubMonitor] Created partition receiver[3]  
  6. for consumerGroup[$Default][IoTHubMonitor][11: 16: 45 AM] Message received from[ml - pf]: {  
  7.     "body": {  
  8.         "messageId": 198,  
  9.         "temperature": 28,  
  10.         "pressure": 1007.074707  
  11.     },  
  12.     "applicationProperties": {  
  13.         "temperatureAlert""false"  
  14.     }  
  15. }  

Conclusion

Wow! Now, we have learned the following.

  • How to use IoT Workbench tool in Visual Studio Code
  • How to set up your MXChip device
  • How to write C++ code for Arduino
  • How to get the Pressure information from the sensor
  • How to upload the new code to MXChip device
  • How to perform the Device to Cloud message output.

Please consider reading my IoT articles here for the continuation.

Your turn. What do you think?

Thanks a lot for reading. Did I miss anything that you may think is needed in this article? Didd you find this post as useful? Kindly do not forget to share your feedback.