This shows you the differences between two versions of the page.
iothings:proiecte:2021:airqualityedgeimpulse [2022/01/22 19:11] alexandra.covor [Introduction] |
iothings:proiecte:2021:airqualityedgeimpulse [2022/01/25 22:11] (current) alexandra.covor [Hardware Description] |
||
---|---|---|---|
Line 1: | Line 1: | ||
====== Air Quality Monitoring Using Edge Impulse ====== | ====== Air Quality Monitoring Using Edge Impulse ====== | ||
+ | |||
+ | Author: Covor Alexandra, ACES | ||
Project repository: [[https://github.com/Alexandra182/AirQuality-EdgeImpulse-ESP32]] | Project repository: [[https://github.com/Alexandra182/AirQuality-EdgeImpulse-ESP32]] | ||
+ | |||
+ | Demo video: [[https://www.youtube.com/watch?v=48sH_nrRz8E]] | ||
===== Introduction ===== | ===== Introduction ===== | ||
- | The purpose of this project is to identify anomalies in air quality data through embedded Machine Learning, using the Edge Impulse platform to build and train a model to be deployed on an ESP32 development board. Firstly, the training data will be collected through the ESP32 and sent to the Edge Impulse platform. The ML model will be created and trained online, after which it will be deployed on the ESP32. The program will enable the user to set up the WiFi connection using Bluetooth. The data from the air quality sensor, as well as the output of the inference, will be displayed on a webpage, through a web server running on the ESP32 development board. | + | The purpose of this project is to identify anomalies in air quality data through embedded Machine Learning, using the Edge Impulse platform to build and train a model to be deployed on an ESP32 development board. Firstly, the training data will be collected through the ESP32 and sent to the Edge Impulse platform. The ML model will be created and trained online, after which it will be deployed on the ESP32. The program will enable the user to set up the WiFi connection through a web page. The data from the air quality sensor, as well as the output of the inference, will be displayed on a webpage, through a web server running on the ESP32 development board. |
A potential future application for this project is an artificial nose, such as the one implemented in this project: | A potential future application for this project is an artificial nose, such as the one implemented in this project: | ||
Line 18: | Line 22: | ||
* LED - connected to a PWM pin of the ESP32. | * LED - connected to a PWM pin of the ESP32. | ||
+ | {{:iothings:proiecte:2021:alexandra_c:circuit.jpeg?400|}} | ||
==== Wiring ==== | ==== Wiring ==== | ||
Line 27: | Line 32: | ||
The ESP32 development board is not officially supported by Edge Impulse, but their [[https://docs.edgeimpulse.com/docs/porting-guide|Porting guide]] provides instructions on how to connect it to the platform. The first option to do this, and also the fastest and easiest to implement, is the Data Forwarder, which allows collecting data from the development board over a serial connection. The second option, and the one I chose to implement in this project, is sending data remotely, directly from the device, using the [[https://docs.edgeimpulse.com/reference/ingestion-api|Ingestion API]] and (optionally) the [[https://docs.edgeimpulse.com/reference/remote-management|Remote management protocol]]. | The ESP32 development board is not officially supported by Edge Impulse, but their [[https://docs.edgeimpulse.com/docs/porting-guide|Porting guide]] provides instructions on how to connect it to the platform. The first option to do this, and also the fastest and easiest to implement, is the Data Forwarder, which allows collecting data from the development board over a serial connection. The second option, and the one I chose to implement in this project, is sending data remotely, directly from the device, using the [[https://docs.edgeimpulse.com/reference/ingestion-api|Ingestion API]] and (optionally) the [[https://docs.edgeimpulse.com/reference/remote-management|Remote management protocol]]. | ||
- | Using the Remote management protocol, I first opened a WebSocket connection from the ESP32 to **[[ws://remote-mgmt.edgeimpulse.com]]** and I sent a JSON-encoded Hello request: | + | After creating a project on Edge Impulse, using the Remote management protocol, I first opened a WebSocket connection from the ESP32 to **[[ws://remote-mgmt.edgeimpulse.com]]** and I sent a JSON-encoded Hello request: |
<code> | <code> | ||
Line 101: | Line 106: | ||
</code> | </code> | ||
- | After all the samples are collected, the data is encoded and signed, then sent to Edge Impulse through an HTTP POST request to: [[http://ingestion.edgeimpulse.com/api/training/data]] | + | After all the samples are collected, the data is encoded and signed, then sent to Edge Impulse through an HTTP POST request to: |
+ | |||
+ | [[http://ingestion.edgeimpulse.com/api/training/data]] | ||
+ | |||
+ | The logic diagram for this program is presented below. | ||
+ | |||
+ | {{:iothings:proiecte:2021:alexandra_c:logic_diagram_2.png?|}} | ||
The collected data can be visualized on Edge Impulse in the Data Acquisition menu: | The collected data can be visualized on Edge Impulse in the Data Acquisition menu: | ||
Line 111: | Line 122: | ||
After collecting enough samples from the air quality sensor, I labelled all the samples with the //normal// or //anomaly// labels and then I performed a train/test split to balance the data. By default, the program sent all the samples to the Training data category. | After collecting enough samples from the air quality sensor, I labelled all the samples with the //normal// or //anomaly// labels and then I performed a train/test split to balance the data. By default, the program sent all the samples to the Training data category. | ||
- | {{:iothings:proiecte:2021:alexandra_c:3.png?750|}} | + | {{:iothings:proiecte:2021:alexandra_c:3.png?|}} |
Sample labelled as anomaly: | Sample labelled as anomaly: | ||
Line 124: | Line 135: | ||
{{:iothings:proiecte:2021:alexandra_c:6.png?750|}} | {{:iothings:proiecte:2021:alexandra_c:6.png?750|}} | ||
- | {{:iothings:proiecte:2021:alexandra_c:7.png?750|}} | + | {{:iothings:proiecte:2021:alexandra_c:7.png?|}} |
The following step was to train the NN Classifier. I created a model with 5 layers: the input layer, a dense layer with 10 neurons, a dense layer with 20 neurons, a dense layer with 30 neurons, and the output layer having 2 neurons, corresponding to the two classes I wanted to identify (normal and anomaly). | The following step was to train the NN Classifier. I created a model with 5 layers: the input layer, a dense layer with 10 neurons, a dense layer with 20 neurons, a dense layer with 30 neurons, and the output layer having 2 neurons, corresponding to the two classes I wanted to identify (normal and anomaly). | ||
Line 134: | Line 145: | ||
The last step was to deploy the trained model to the ESP32 development board. From the Deployment menu on Edge Impulse, I downloaded the Arduino library with the trained model and added it to my project. | The last step was to deploy the trained model to the ESP32 development board. From the Deployment menu on Edge Impulse, I downloaded the Arduino library with the trained model and added it to my project. | ||
- | I used the instructions and the code sample provided [[https://docs.edgeimpulse.com/docs/running-your-impulse-arduino|here]] and [[https://docs.edgeimpulse.com/docs/cli-data-forwarder#classifying-data-arduino|here]] to feed data from the air quality sensor to the classifier network. Also, in order to visualise the sensor readings and the inference output, I used [[https://randomnerdtutorials.com/esp32-plot-readings-charts-multiple/|this]] tutorial to implement a webpage running on the ESP32. | + | I used the instructions and the code sample provided [[https://docs.edgeimpulse.com/docs/running-your-impulse-arduino|here]] and [[https://docs.edgeimpulse.com/docs/cli-data-forwarder#classifying-data-arduino|here]] to feed data from the air quality sensor to the classifier network. Also, in order to visualise the sensor readings and the inference output, I used [[https://randomnerdtutorials.com/esp32-plot-readings-charts-multiple/|this]] tutorial to implement a webpage running on the ESP32. For implementing the webpage used for configuring the WiFi connection, I used [[https://randomnerdtutorials.com/esp32-wi-fi-manager-asyncwebserver/|this tutorial]]. |
The full code can be found in the [[https://github.com/Alexandra182/AirQuality-EdgeImpulse-ESP32/blob/main/inference_web_server/inference_web_server.ino|inference_web_server.ino]] file. | The full code can be found in the [[https://github.com/Alexandra182/AirQuality-EdgeImpulse-ESP32/blob/main/inference_web_server/inference_web_server.ino|inference_web_server.ino]] file. | ||
- | {{:iothings:proiecte:2021:alexandra_c:10.png?750|}} | + | The user has to connect to the Access Point named ESP-WIFI-MANAGER, access the following IP from a web browser: 192.168.4.1, and fill in the SSID and the password of the network: |
+ | |||
+ | {{:iothings:proiecte:2021:alexandra_c:11.png?750|}} | ||
+ | |||
+ | The next step is connecting to the WiFi network and accessing the previously assigned IP from a web browser, which will display the Air Quality dashboard: | ||
+ | |||
+ | {{:iothings:proiecte:2021:alexandra_c:12.png?750|}} | ||
+ | |||
+ | Below is the logic diagram of the Arduino program: | ||
+ | |||
+ | {{:iothings:proiecte:2021:alexandra_c:logic_diagram_3.png?|}} | ||
+ | |||
+ | The **inference_web_server.ino** file contains the **getSensorReadings()** function, which constructs a JSON object with the sensor readings and the inference result: | ||
+ | |||
+ | <code c> | ||
+ | String getSensorReadings() { | ||
+ | airQualitySensor.readAlgorithmResults(); | ||
+ | readings["co2"] = String(airQualitySensor.getCO2()); | ||
+ | readings["tvoc"] = String(airQualitySensor.getTVOC()); | ||
+ | readings["result"] = String(anomalyScore); | ||
+ | |||
+ | String jsonString = JSON.stringify(readings); | ||
+ | Serial.println(jsonString); | ||
+ | return jsonString; | ||
+ | } | ||
+ | </code> | ||
- | The program running on the ESP32 reads the CO2 and TVOC values from the Air Quality sensor and displays them on the webpage. The chart displays 100 data points at once and it updates automatically using Sever-Sent Events (SSE). | + | The program reads the CO2 and TVOC values from the Air Quality sensor and displays them on the webpage. When 100 samples are collected, it runs the ML model on them and outputs the prediction scores for each label (anomaly, normal). The anomaly score is also updated in the JSON String. |
- | {{:iothings:proiecte:2021:alexandra_c:inference-web.drawio.png?|}} | + | The chart displays 100 data points at once and it updates automatically every second using Sever-Sent Events (SSE). |
The HTML, CSS, and JavaScript files necessary for the web page are stored on the board’s filesystem (SPIFFS) and can be found in the **data** folder of the **inference_web_server** Arduino sketch. | The HTML, CSS, and JavaScript files necessary for the web page are stored on the board’s filesystem (SPIFFS) and can be found in the **data** folder of the **inference_web_server** Arduino sketch. | ||
Line 164: | Line 201: | ||
</code> | </code> | ||
- | The **index.html** file calls **script.js** which performs a GET request to the server on the ///readings// URL: | + | The **index.html** file calls **script.js**, where the inference and the chart data points are read: |
+ | |||
+ | <code js> | ||
+ | const inferenceElement = document.getElementById('inference'); | ||
+ | |||
+ | var chartT = new Highcharts.Chart({ | ||
+ | chart:{ | ||
+ | renderTo:'chart-aq' | ||
+ | }, | ||
+ | series: [ | ||
+ | { | ||
+ | name: 'CO2', | ||
+ | type: 'line', | ||
+ | color: '#101D42', | ||
+ | marker: { | ||
+ | symbol: 'circle', | ||
+ | radius: 3, | ||
+ | fillColor: '#101D42', | ||
+ | } | ||
+ | }, | ||
+ | { | ||
+ | name: 'TVOC', | ||
+ | type: 'line', | ||
+ | color: '#00A6A6', | ||
+ | marker: { | ||
+ | symbol: 'square', | ||
+ | radius: 3, | ||
+ | fillColor: '#00A6A6', | ||
+ | } | ||
+ | }, | ||
+ | ], | ||
+ | title: { | ||
+ | text: undefined | ||
+ | }, | ||
+ | xAxis: { | ||
+ | type: 'datetime', | ||
+ | dateTimeLabelFormats: { second: '%H:%M:%S' } | ||
+ | }, | ||
+ | yAxis: { | ||
+ | title: { | ||
+ | text: 'ppm/ppb' | ||
+ | } | ||
+ | }, | ||
+ | credits: { | ||
+ | enabled: false | ||
+ | } | ||
+ | }); | ||
+ | </code> | ||
+ | |||
+ | The //getReadings()// function performs a GET request to the server on the ///readings// URL and receives the JSON string from which it parses the values: | ||
<code js> | <code js> | ||
Line 181: | Line 267: | ||
} | } | ||
</code> | </code> | ||
- | + | ===== Conclusions ===== | |
- | The **inference_web_server.ino** file contains the **getSensorReadings()** function, which constructs a JSON file with the sensor readings and the inference result: | + | |
- | + | ||
- | <code c> | + | |
- | String getSensorReadings() { | + | |
- | airQualitySensor.readAlgorithmResults(); | + | |
- | readings["co2"] = String(airQualitySensor.getCO2()); | + | |
- | readings["tvoc"] = String(airQualitySensor.getTVOC()); | + | |
- | readings["result"] = String(anomalyScore); | + | |
- | + | ||
- | String jsonString = JSON.stringify(readings); | + | |
- | Serial.println(jsonString); | + | |
- | return jsonString; | + | |
- | } | + | |
- | </code> | + | |
- | + | ||
- | ==== Conclusions ==== | + | |
Future improvements to the project include gathering more data in order to properly train the model, as the current implementation is more of a proof of concept, and implementing alerts when anomalies in the data are detected. | Future improvements to the project include gathering more data in order to properly train the model, as the current implementation is more of a proof of concept, and implementing alerts when anomalies in the data are detected. | ||
Line 218: | Line 288: | ||
- [[https://github.com/edgeimpulse/ingestion-sdk-c|Edge Impulse C Ingestion SDK]] | - [[https://github.com/edgeimpulse/ingestion-sdk-c|Edge Impulse C Ingestion SDK]] | ||
- [[https://github.com/rafaelbidese/ei-projects|Edge Impulse C Ingestion SDK Samples]] | - [[https://github.com/rafaelbidese/ei-projects|Edge Impulse C Ingestion SDK Samples]] | ||
+ | - [[https://github.com/me-no-dev/ESPAsyncWebServer|ESPAsyncWebServer]] | ||
+ | - [[https://github.com/me-no-dev/AsyncTCP|AsyncTCP]] | ||
==== Others ==== | ==== Others ==== | ||
- [[https://randomnerdtutorials.com/esp32-plot-readings-charts-multiple/|ESP32 Plot Sensor Readings in Charts (Multiple Series)]] | - [[https://randomnerdtutorials.com/esp32-plot-readings-charts-multiple/|ESP32 Plot Sensor Readings in Charts (Multiple Series)]] | ||
+ | - [[https://randomnerdtutorials.com/esp32-wi-fi-manager-asyncwebserver/|ESP32: Create a Wi-Fi Manager (AsyncWebServer library)]] | ||