Playing with Azure IoT DevKit MXChip is always fun. The device has many capabilities. For the past few days, I have been examining some of the capabilities of this device, like Atmospheric pressure, Temperature, Humidity through its sensors. In this article, I will show you how you can calculate the noise level using the microphone of the AZ3166 IoT Device. Now, let’s start implementing the same. I hope you will like it. You can always read this article on my blog
here.
Background
In our last
article, we have already seen how to read the temperature, humidity, atmospheric pressure from the MXChip AZ3166 sensors and send those to our Azure IoT Hub. Here in this article, let’s complete the following tasks.
- Find the Noise level using the AudioClassV2 class
- Send the Values to our IoT Hub
Source Code
Using the Code
Once you have your own work space, we can open the solution in VSCode and start coding.
main.ino
This is our solution's starting point. Every project must have its own sketch file. Usually, this file contains at least the functions loop() and setup().
Before we get started, let’s include the header files we are going to use.
-
- #include "AZ3166WiFi.h"
- #include "AzureIotHub.h"
- #include "DevKitMQTTClient.h"
- #include "config.h"
- #include "utility.h"
- #include "SystemTickCounter.h"
- #include "RingBuffer.h"
- #include "parson.h"
- #include "EEPROMInterface.h"
- #include "http_client.h"
- #include <Arduino.h>
- #include <stdio.h>
- #include <math.h>
- #include "OledDisplay.h"
- #include "AudioClassV2.h"
- #include "stm32412g_discovery_audio.h"
- #include "RGB_LED.h"
- #include <stdint.h>
-
- #define MFCC_WRAPPER_DEFINED
- #include "featurizer.h"
-
-
Now, we can declare our constants and variables.
-
- enum AppState
- {
- APPSTATE_Init,
- APPSTATE_Error,
- APPSTATE_Recording
- };
-
- static AppState appstate;
-
- const int SAMPLE_RATE = 16000;
- const int SAMPLE_BIT_DEPTH = 16;
- const int FEATURIZER_INPUT_SIZE = 512;
- const int FRAME_RATE = 33;
- const int FEATURIZER_OUTPUT_SIZE = 80;
- const int CLASSIFIER_OUTPUT_SIZE = 31;
- const float THRESHOLD = 0.9;
- static int scaled_input_buffer_pos = 0;
- static float scaled_input_buffer[FEATURIZER_INPUT_SIZE];
- const int MAX_FEATURE_BUFFERS = 10;
- static float featurizer_input_buffers[MAX_FEATURE_BUFFERS][FEATURIZER_INPUT_SIZE];
- static int featurizer_input_buffer_read = -1;
- static int featurizer_input_buffer_write = 0;
- static int dropped_frames = 0;
- static float featurizer_output_buffer[FEATURIZER_OUTPUT_SIZE];
- static float classifier_output_buffer[CLASSIFIER_OUTPUT_SIZE];
- static int raw_audio_count = 0;
- static char raw_audio_buffer[AUDIO_CHUNK_SIZE];
- static int prediction_count = 0;
- static int last_prediction = 0;
- static int last_confidence = 0;
- static uint8_t maxGain = 0;
- static uint8_t minGain = 0;
- int decibels = 0;
- float min_level = 100;
- float max_level = 0;
- RGB_LED rgbLed;
- static bool hasWifi = false;
- static bool messageSending = true;
- static uint64_t send_interval_ms;
- int messageCount = 0;
- bool messageReceived = false;
- char device_id[6];
- AudioClass &Audio = AudioClass::getInstance();
-
-
Now, let us can add the codes for the configuration. Usually, you wouldn’t have to edit any codes in this section.
- void setup() {
- Screen.init();
- Screen.print(0, "IoT Device Demo");
- Screen.print(2, "Initializing...");
- Screen.print(3, " > Serial");
- Serial.begin(115200);
-
- Screen.print(3, " > WiFi");
- hasWifi = false;
- initWifi();
- if (!hasWifi) {
- return;
- }
- Screen.print(3, " > Sensors");
- sensorInit();
- Screen.print(3, " > IoT Hub");
- DevKitMQTTClient_Init(true);
- DevKitMQTTClient_SetOption(OPTION_MINI_SOLUTION_NAME, "mlIoTPlatformDevice");
- DevKitMQTTClient_SetSendConfirmationCallback(sendConfirmationCallback);
- DevKitMQTTClient_SetMessageCallback(messageCallback);
- DevKitMQTTClient_SetDeviceTwinCallback(deviceTwinCallback);
- DevKitMQTTClient_SetDeviceMethodCallback(deviceMethodCallback);
- appstate = APPSTATE_Init;
- Serial.begin(115200);
- int filter_size = mfcc_GetInputSize(0);
- if (filter_size != FEATURIZER_INPUT_SIZE) {
- Serial.printf("Featurizer input size %d is not equal to %d\n", filter_size, FEATURIZER_INPUT_SIZE);
- show_error("Featurizer Error");
- }
- if (appstate != APPSTATE_Error) {
- ::memset(featurizer_input_buffers[0], 0, FEATURIZER_INPUT_SIZE);
-
- mfcc_Filter(nullptr, featurizer_input_buffers[0], featurizer_output_buffer);
-
- uint32_t id = Audio.readRegister(nau88c10_CHIPID_ADDR);
- if (id == NAU88C10_ID) {
- Serial.printf("Found audio device: NAU88C10\r\n");
- } else {
- Serial.printf("Found audio device: 0x%x\r\n", id);
- }
-
- start_recording();
- Screen.clean();
- Screen.print(0, "Listening...");
- Screen.print(1, "A = min gain");
- Screen.print(2, "B = max gain");
- minGain = 0;
- maxGain = 7;
- set_gain();
- display_gain();
- }
- send_interval_ms = SystemTickCounterRead();
- }
As I mentioned earlier, every INO file will have its own setup() and loop() function. We can modify our setup() function as below.
- void setup() {
- Screen.init();
- Screen.print(0, "IoT Device Demo");
- Screen.print(2, "Initializing...");
- Screen.print(3, " > Serial");
- Serial.begin(115200);
-
- Screen.print(3, " > WiFi");
- hasWifi = false;
- initWifi();
- if (!hasWifi) {
- return;
- }
- Screen.print(3, " > Sensors");
- sensorInit();
- Screen.print(3, " > IoT Hub");
- DevKitMQTTClient_Init(true);
- DevKitMQTTClient_SetOption(OPTION_MINI_SOLUTION_NAME, "mlIoTPlatformDevice");
- DevKitMQTTClient_SetSendConfirmationCallback(sendConfirmationCallback);
- DevKitMQTTClient_SetMessageCallback(messageCallback);
- DevKitMQTTClient_SetDeviceTwinCallback(deviceTwinCallback);
- DevKitMQTTClient_SetDeviceMethodCallback(deviceMethodCallback);
- appstate = APPSTATE_Init;
- Serial.begin(115200);
- int filter_size = mfcc_GetInputSize(0);
- if (filter_size != FEATURIZER_INPUT_SIZE) {
- Serial.printf("Featurizer input size %d is not equal to %d\n", filter_size, FEATURIZER_INPUT_SIZE);
- show_error("Featurizer Error");
- }
- if (appstate != APPSTATE_Error) {
- ::memset(featurizer_input_buffers[0], 0, FEATURIZER_INPUT_SIZE);
-
- mfcc_Filter(nullptr, featurizer_input_buffers[0], featurizer_output_buffer);
-
- uint32_t id = Audio.readRegister(nau88c10_CHIPID_ADDR);
- if (id == NAU88C10_ID) {
- Serial.printf("Found audio device: NAU88C10\r\n");
- } else {
- Serial.printf("Found audio device: 0x%x\r\n", id);
- }
-
- start_recording();
- Screen.clean();
- Screen.print(0, "Listening...");
- Screen.print(1, "A = min gain");
- Screen.print(2, "B = max gain");
- minGain = 0;
- maxGain = 7;
- set_gain();
- display_gain();
- }
- send_interval_ms = SystemTickCounterRead();
- }
The function loop() will be called each 5 seconds, as I had set the INTERVAL as 5000 milliseconds.
-
-
-
- #define INTERVAL 5000
- #define MESSAGE_MAX_LEN 256
- #define TEMPERATURE_ALERT 30
- #define DIRECT_METHOD_NAME "message"
-
- #define MESSAGE_SEND_COUNT_LIMIT 350
Now, we can edit our code of loop() function as below.
- void loop() {
- if (hasWifi) {
- if (messageSending && (int)(SystemTickCounterRead() - send_interval_ms) >= getInterval()) {
- if (appstate != APPSTATE_Error) {
- if (dropped_frames > 0) {
- Serial.printf("%d dropped frames\n", dropped_frames);
- dropped_frames = 0;
- }
-
- featurizer_input_buffer_read = next(featurizer_input_buffer_read);
- decibels = get_prediction(featurizer_input_buffers[featurizer_input_buffer_read]);
- }
-
- char messagePayload[MESSAGE_MAX_LEN];
- float * newValues;
- newValues = setMessage(messageCount++, messagePayload, decibels);
- if (!messageReceived) {
-
- char buff[128];
- sprintf(buff, "ID: %s \r\n Temp:%s°C \r\n Humidity:%s%% \r\n Pres:%smb \r\n", device_id, f2s( * (newValues), 1), f2s( * (newValues + 1), 1), f2s( * (newValues + 2), 1));
- Screen.print(buff);
- }
- EVENT_INSTANCE * message = DevKitMQTTClient_Event_Generate(messagePayload, MESSAGE);
- DevKitMQTTClient_SendEventInstance(message);
- send_interval_ms = SystemTickCounterRead();
- } else {
- DevKitMQTTClient_Check();
- }
- }
- delay(10);
- }
utility.cpp
As you can see, once we get the values from the get_prediction () function, we are passing the decibel value to our setMessage() function, which we have defined in the utility.cpp file. Inside the setMessage () function, we will add the decibel value to JSON object using the function json_object_set_number ().
-
-
- #include "HTS221Sensor.h"
- #include "AzureIotHub.h"
- #include "Arduino.h"
- #include "parson.h"
- #include <assert.h>
- #include "config.h"
- #include "RGB_LED.h"
- #include "Sensor.h"
- #include "LIS2MDLSensor.h"
- #define RGB_LED_BRIGHTNESS 32
- DevI2C * i2c;
- HTS221Sensor * ht_sensor;
- LPS22HBSensor * pressureSensor;
- LSM6DSLSensor * acc_gyro;
- LIS2MDLSensor * lis2mdl;
- int gAxes[3];
- int mAxes[3];
- static RGB_LED rgbLed;
- static int interval = INTERVAL;
- 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);
- ht_sensor = new HTS221Sensor( * i2c);
- ht_sensor - > init(NULL);
- pressureSensor = new LPS22HBSensor( * i2c);
- pressureSensor - > init(NULL);
- acc_gyro = new LSM6DSLSensor( * i2c, D4, D5);
- acc_gyro - > init(NULL);
- acc_gyro - > enableAccelerator();
- lis2mdl = new LIS2MDLSensor( * i2c);
- lis2mdl - > init(NULL);
- }
- float readTemperature() {
- ht_sensor - > reset();
- float temperature = 0;
- ht_sensor - > getTemperature( & temperature);
- return temperature;
- }
- float readHumidity() {
- ht_sensor - > reset();
- float humidity = 0;
- ht_sensor - > getHumidity( & humidity);
- return humidity;
- }
- float readPressure() {
- float pressure = 0;
- pressureSensor - > getPressure( & pressure);
- return pressure;
- }
- void setAccelAxes() {
- acc_gyro - > getXAxes(gAxes);
- }
- void setMagAxes() {
- lis2mdl - > getMAxes(mAxes);
- }
- float * setMessage(int messageId, char * payload, int decibels) {
- static float newValues[3];
- 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);
- json_object_set_number(root_object, "decibels", decibels);
-
- float temperature = readTemperature();
- float humidity = readHumidity();
- float pressure = readPressure();
- setAccelAxes();
- setMagAxes();
-
- newValues[0] = temperature;
- newValues[1] = humidity;
- newValues[2] = pressure;
- bool temperatureAlert = false;
-
- json_object_set_number(root_object, "temperature", temperature);
-
- json_object_set_number(root_object, "humidity", humidity);
-
- json_object_set_number(root_object, "pressure", pressure);
-
- json_object_set_number(root_object, "accelX", gAxes[0]);
- json_object_set_number(root_object, "accelY", gAxes[1]);
- json_object_set_number(root_object, "accelZ", gAxes[2]);
-
- json_object_set_number(root_object, "magX", mAxes[0]);
- json_object_set_number(root_object, "magY", mAxes[1]);
- json_object_set_number(root_object, "magZ", mAxes[2]);
- 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 newValues;
- }
You should also add the files featurizer.h and featurizer.s to get it working. You can get these files from the source code repository mentioned above.
Compile and Upload to the Device
As we have already made the needed changes, it is time to compile the device solution and upload the same to our device. Press F1 and select Azure IoT Device Workbench: Compile Device Code. If you ever get an error as “error: utility.h: No such file or directory”, please compile the device code again. If you are facing any unexpected errors, please delete the “.build” folder and compile it again.
Once you get a message as ” [Done] Finished verify sketch – Main.ino ” in your output window, you can upload the solution to your device. To do so, press F1 again, and select ” Azure IoT Device Workbench: Upload Device Code”. Just make sure that the device is connected to your machine. If everything goes well, you will be getting a message as ” [Done] Uploaded the sketch: Main.ino”.
Don't forget to see the GitHub repository for the full code.
Device to Cloud Messages
Now, your device will be sending the Decibels data to Azure IoT Hub. Let’s see that in the D2C Monitoring window.
Send MXChip Data to Cloud
- {
- "messageId": 119,
- "decibels": 94,
- "temperature": 26.4,
- "humidity": 34.400002,
- "pressure": 994.585693,
- "accelX": -9,
- "accelY": -12,
- "accelZ": 993,
- "magX": -156,
- "magY": -5,
- "magZ": -253
- }
Wow!.So, we have learned,
- How to detect the noise level in MXChip
- How to use AudioClassV2
- How to send device data to the Azure IoT Hub
Please consider reading my IoT articles
here for more.
Your turn. What do you think?
Thanks a lot for reading. Did I miss anything that you think may be needed in this article? Did you find this post useful? Do share your feedback.