- LinkedIn profiles: Leonardo Salvucci, Antonino Di Maggio and Mattia Nicolella
- GitHub repository
- SlideShare first presentation
- SlideShare final presentation
1.Overview
The primary purpose of a systematic air quality monitoring network is to distinguish between areas where pollutant levels violate an ambient air quality standard and areas where they do not. As health-based ambient air quality standards are set at levels of pollutant concentrations that result in adverse impacts on human health, evidence of levels exceeding an ambient air quality standard in an area requires a public air quality agency to mitigate the corresponding pollutant. In other words, strategies, technologies, and regulations need to be developed to achieve the necessary reduction in pollution.
The secondary purpose of a systematic monitoring network is to document the success of this sophisticated endeavor, either to record the rate of progress towards attaining the ambient air quality standard or to show that the standard has been achieved.
We propose a simple and economic solution. Our device has small dimensions and good precision, able to warn when the pollution level exceeds a certain threshold. It send data in real time to the cloud to be analyzed and monitored even by inexperienced staff thanks to the ease of use of the software porposed.
2.Workflow
Step 1: Take humidity, temperature and CO values from the sensor on the board.
Step 2: If the specific values exceed a certain threshold a timer starts that processes all the data in that range, if the average of these values still exceeds the expected threshold, the data are send to the neural network, otherwise the detection is reset.
Step 3: Before sending them to the cloud, these values are analyzed by the neural network to predict the value of Benzene in the air (without the installation of an additional sensor).
Step 4: After the prediction of neural network, all data (Humidity, Temperature, CO and Benzene) are sent to the server via LoRa.
Step 5: Once the data has arrived at the server, the network server sends a request to the influxDB database, saving the data permanently using the timestamp.
Step 6: Through the web data connector the data stored in InfluxDB is analyzed and managed on Tableau.
2.1 STEVAL-MKI141V2 SensorAn humidity and temperature sensor adapter board for standard DIL24 socket(full pinout). For more technical clarifications about the sensor you can visit this page. SDA and SCL are the pins used by the hardware implementation of the i2c protocol.
We feed the sensor with 3.3V connected directly with the board, we get the digital data (Humidity and Temperature) by the SDA pin and we use SCL pin for the clock. Using the HTS221 libraries we can get easily the relative value about each survey. Of course, we have positioned the sensor away from the board so as not to alter the temperature perceived by the sensor, due to the overheating of the board.
#include "mbed.h"
#include "HTS221Sensor.h"
static DevI2C devI2c(D14,D15);
static HTS221Sensor hum_temp(&devI2c);
int main() {
uint8_t id;
float value1, value2;
hum_temp.init(NULL);
hum_temp.enable();
hum_temp.read_id(&id);
printf("HTS221 humidity & temperature = 0x%X\r\n", id);
printf("\n\r--- Reading sensor values ---\n\r"); ;
while(1) {
printf("\r\n");
hum_temp.get_temperature(&value1);
hum_temp.get_humidity(&value2);
printf("HTS221: [temp] %.2f C, [hum] %.2f%%\r\n", value1, value2);
wait(2);
}
}
2.2 P-NUCLEO-IKA02A1 SensorThis sensor comes with an expansion board and is compatible with several NUCLEO boards (F401RE, L053R8 and L476RG), it's equipped with a carbon monoxide sensor and a temperature sensor, which are used to read carbon monoxide values and compensating them with the actual temperature values.
It's full specification are available on this page, while the SDK with some examples for each compatible microcontroller is available here.
The SDK of the sensor is not compatible by default with the mbed framework, since it uses the Arduino pin names and some libraries that have the same function of some parts of the mbed framework. For this reason we had to do a bit of patchwork to make the sensor library compatible with the mbed framework, our work is available here. Further patchwork might be needed because the pin names for the used pin could change based on the used board, so there could be the need to substitute the stm* files in the library with the one of the used board (included in the other_boards folder) and change the pin names in gas.h according to the PinNames.h file of the used board.
After coupling the board with the sensor you could simply include the gas.h header and use the three provided functions to read CO values and compensate them with a temperature value, here is an example:
#include "mbed.h"
#include <string.h>
#include <stdio.h>
#include "gas.h"
float gas_value_ppm = 0;
float gas_value_ppm_comp = 0;
float temperature=0;
int main(void){
Serial pc(USBTX, USBRX);
while(1){
gas_value_ppm = Get_Gas_value();
gas_value_ppm_comp = Get_Gas_comp();
temperature=Get_Temp();
pc.printf("gas_value_ppm: %f, gas_value_ppm_comp: %f,temperature: %f\n",gas_value_ppm,gas_value_ppm_comp,temperature);
wait(1);
}
}
Here are the functions defined in gas.h:
// needed libraries
#include "gas.h"
extern "C"{
#include "sensor_calibration_TGS5141.h"
#include "x_nucleo_ika02a1.h"
}
#include <math.h>
float Get_Gas_value(){
int32_t* pSens = (int32_t*)FLASH_SENS_ADDR;
int32_t* pGain = (int32_t*)FLASH_GAIN_ADDR;
return Get_Gas_concentration((float)*pGain,((float)*pSens)/1000);
}
float Get_Gas_comp(){
float temperature = Get_Temperature();
float gas=Get_Gas_value();
return Get_comp_gas_concentration(gas,(int8_t)round(temperature));
}
float Get_Temp(){
return Get_Temperature();
}
This sensor is not compatible with boards that do not have Arduino's analog pins ( such as the DISCO-L072CZ-LRWAN1 board), since it uses A2, A3 and A5 pins to read the sensor values. Furthermore we discovered that even with the provided code there are some unidentified pin conflicts when using the sensor with the Mbed framework, since the sensor stops working when we initialize any other digital pin, such as D14 and D15 to use the I2C protocol.
2.3 UTensorUTensor (read as micro-Tensor) is a young project which provides a light-weight inference framework to use machine learning models for predictions with the Mbed compatible boards.
It works by converting a Tensorflow graph into a couple of C++ classes, namely a Context and a Tensor, which are used to make predictions by using the previously trained Tensorflow model.
We worked with uTensor by using this dataset, which contains air pollution measurements taken in an Italian city.
We wanted to emulate a benzene detector without using the actual sensor, so we build a simple linear regression model with Tensorflow, without the use of Keras APIs ( a little bit of insight on how to do this can be found here) and we converted the trained module in the uTensor C++ classes by using uTensor-cli (a you can do it by following this example).
For our emulation we considered the relative humidity, the temperature and the CO level, which has been converted from ppm (as read from the P-NUCLEO-IKA02A1) to mg/m^3 (as the value is represented in the dataset).
Having converted the model with the uTensor-cli tool we obtain 3 files:
- <model_name>.cpp: which contains the model implementation for our board
- <model_name>_weight.hpp: which contains the weights of the model
- <model_name>.hpp which is the header file to include to use the model
Now we only need to execute the prediction by crating a Context and a Tensor objects as in the following example:
#include "model.hpp" //gernerated model file
#include "tensor.hpp" //useful tensor classes
float predict(float* values){
Context ctx; //creating the context class, the stage where inferences take place
//wrapping the input data in a tensor class
Tensor* input_x = new WrappedRamTensor<float>({1, 3}, values);
get_predictor_ctx(ctx, input_x); // pass the tensor to the context
S_TENSOR pred_tensor = ctx.get("y_pred:0"); // getting a reference to the output tensor
ctx.eval(); //trigger the inference
float pred_label = *(pred_tensor->read<float>(0, 0)); //getting the result back
return pred_label;
}
By using uTensor we realized that even with a simple model, such as our Perceptron based predictor, the dimension of compiled binary file was significant (about 200KB) which makes this library incompatible with some boards (such as DISCO-L072CZ-LRWAN1, but we managed to use it on the NUCLEO_F401RE). Moreover this library requires special compilation (contained in the build_profile folder of the official repository) flags that could create problems in certain environments (eg. PlatformIO), it has been compiled and tested with the mbed-cli with gcc-arm v8 and it requires Mbed OS <= v5.11.5, since it raises some compilation errors with Mbed OS v5.12.1.
2.4 B-L072Z-LRWAN1 LoRa®LoRaWAN is a media access control (MAC) protocol for wide area networks. It is designed to allow low-powered devices to communicate with Internet-connected applications over long range wireless connections.
LoRaWAN can be mapped to the second and third layer of the OSI model. It is implemented on top of LoRa or FSK modulation in industrial, scientific and medical (ISM) radio bands. The LoRaWAN protocols are defined by the LoRa Alliance and formalized in the LoRaWAN Specification which can be downloaded on the LoRa Alliance website.
The B-L072Z-LRWAN1 LoRa/Sigfox™ Discovery kit is a development tool to learn and develop solutions based on LoRa, Sigfox™, and FSK/OOK technologies. This Discovery kit features an all-in-one open module CMWX1ZZABZ-091 (by Murata). The module is powered by an STM32L072CZ and an SX1276 transceiver. The transceiver features the LoRalong-range modem, providing ultra-long-range spread spectrum communication and high interference immunity, minimizing current consumption. Since CMWX1ZZABZ-091 is an open module, user has access to all STM32L072 peripherals such as ADC, 16-bit timer, LP-UART, I2C, SPI and USB 2.0 FS (supporting BCD and LPM).
We used the B-L072Z-LRWAN1 to receive the CO data from the Nucleo board. The board is also connected with the humidity and temperature sensor.
In this way we collect all the data about CO, temperature and humidity, that we will send thought The Things Network.
2.5 The things networkThe Things Network is a global, crowdsourced, open, free and decentralized internet of things network. The network allows for things to connect to the internet using little power and little data. Opening use cases never possible before. It makes use of the LoRaWAN technology.
The network is build by the users, indeed anyone can contribute adding a gateway in order to expand it. Actually there are 7097 gateways connected to The Things Network, and this number is constantly increasing.
The Things Network provides a console in which everyone can simply setup and register his own gateway or create an application specifying:
- A name of the application
- A brief description
- An handler (for the EU-zone it is ttn-handler-eu)
- One or more devices
After that some EUIs will be automatically generated, for the devices and the application, and even an application key. This information will be used to connect the device to the TTN's specific application.
The EUIs generated by The Things Network have to be insert in the "mbed_app.json", in this way, when a device starts, it will know to which application send the data and the application know which device is sending.
As you can see looking at the above image, the parameters we have to modify (for OTAA activation method) are:
- Device EUI
- Application EUI
- Application key
We a device is connected to the application it will start to send data. The data can be seen and manipulated trough a specific section of the console.
To send the data from the LoRa antenna to out TTN Application we created this sending function:
static void send_message()
{
uint16_t packet_len;
int16_t retcode;
float sensor_value;
if (ds1820.begin()) {
ds1820.startConversion();
sensor_value = ds1820.read();
printf("\r\n Dummy Sensor Value = %3.1f \r\n", sensor_value);
ds1820.startConversion();
} else {
printf("\r\n No sensor found \r\n");
return;
}
packet_len = sprintf((char *) tx_buffer, "Dummy Sensor Value is %3.1f",
sensor_value);
retcode = lorawan.send(MBED_CONF_LORA_APP_PORT, tx_buffer, packet_len,
MSG_UNCONFIRMED_FLAG);
if (retcode < 0) {
retcode == LORAWAN_STATUS_WOULD_BLOCK ? printf("send - WOULD BLOCK\r\n")
: printf("\r\n send() - Error code %d \r\n", retcode);
if (retcode == LORAWAN_STATUS_WOULD_BLOCK) {
//retry in 3 seconds
if (MBED_CONF_LORA_DUTY_CYCLE_ON) {
ev_queue.call_in(3000, send_message);
}
}
return;
}
printf("\r\n %d bytes scheduled for transmission \r\n", retcode);
memset(tx_buffer, 0, sizeof(tx_buffer));
}
2.6 InfluxDBInfluxDB is an open-source time series database optimized for fast, high-availability storage and retrieval of time series data. It is written in Go and it doesn't use an external dependecies. It is very simple thanks to its language very similar to SQL and it allows an easy integration with any platforms through its APIs that permit HTTP requests by executing queries. InfluxDB offers an very intuitive user interface to interact directly with the dabatabase called Chronograf.
It support many programming languages, for example, PHP, Java, Python and many others. We use this technologie to store data received by the sensor through The Things Network in real time, according with its limitations. With a simple POST request we update the database with the timestamp as a primary key for an easy filtering of data.
An example of the POST request is the follow:
curl -i -XPOST "https://marty-c9aef689.influxcloud.net:8086/write?db=sensor_surveys&precision=m&u=xxx&p=xxx" --data-binary "survey co=2.9,humidity=81.1,temperature=8,benzene=11.5"
The request specifies the endpoint url listening on port 8086 (default port of InfluxDB), the name of database used in write mode, the precision of timestamp (minutes), the username and password for the Oauth authentication and finally, it is specified the data about the survey (Co, Humidity, Temperature and Benzene) in a binary coding.
2.7 TableauTableau is a powerful and fastest growing data visualization tool used in the Business Intelligence Industry. It helps in simplifying raw data into the very easily understandable format. It is a multiplatform system avaible on Windows, Linux, Mac and on the public cloud. Tableau fits into your existing people and process workflows, with minimal retraining and onboarding required.
We have create a Web Data Connector (WDC) to connect InfluxDB and Tableau. It is a web page written in HTML and Javascript that allows a bidirectional connection between the two platforms, with a very intuitive interface. It execute an ajax call to influxDB to receive the list of existing tables and loads them into the selection.
var queryString_DBs = protocol + server + ':' + port + '/query?q=SHOW+DATABASES';
if (useAuth) {
setAuth();
queryString_DBs += '&u=' + username + '&p=' + password;
}
$.ajax({
url: queryString_DBs,
dataType: 'json',
timeout: 3000,
success: function (resp) {
if (debug) console.log(resp.results[0].series[0].values);
$('.selectpicker').html('');
$.each(resp.results[0].series[0].values, function (index, value) {
$('<option style="font-size:20px;">' + value + '</option>')
.appendTo('.selectpicker');
});
$('#getSchemaButton').prop('disabled', false);
})
});
The user can select the interest database and upload the data on InfluxDB, viewing updates without restarting the connector.
function loadSchemaIntoTableau() {
var json = {
db: db,
server: server,
protocol: protocol,
port: port,
useAuth: useAuth,
query: query,
schema: schema,
};
if (useAuth) {
tableau.username = username;
tableau.password = password;
}
tableau.connectionData = JSON.stringify(json);
tableau.submit();
}
As you can see on the video, we can manage the data with simple drag and drop displaying the graphs of interest in real time. We can easily compare different data, based on the detection time, displaying the change flows of our sensors. You can follow a simple guide here, where you can learn to create your dashboard step by step.
Comments