ESP32 Anomaly detection using Edge Impulse and machine learning

This tutorial covers how to implement an ESP32 anomaly detection system using Edge Impulse and machine learning. In more detail, we will detect when there is an anomaly in the CO2 concentration and volatile organic compounds. Therefore, we will implement a machine learning model that is capable of identifying if there are some values outside the normal range. To measure the concentration of CO2 and volatile organic compounds we will use ESP32 and CCS811.

What is anomaly detection?

To define anomaly detection we can use this:

In data analysis, anomaly detection (also outlier detection) is the identification of rare items, events or observations which raise suspicions by differing significantly from the majority of the data

https://en.wikipedia.org/wiki/Anomaly_detection

In other words, anomaly detection is the activiy that identifies those values and observations that do not adhere to a pattern that is considered a normal pattern.

To achieve this goal, we will use machine learning and ESP32 in order to identify those values, retrieved from the sensor, that do not belong to the normal pattern. There are several ways to implement anomaly detection with ESP32, in this tutorial, we will use the edge impulse that makes it easy to build the model.

How to implement anomaly detection system with ESP32

To implement an ESP32 anomaly detection system it is necessary to divide it in two different steps:

  • acquire CO2 and tVoC values to define the normal pattern
  • build the machine learning model and use it with ESP32 to detect anomalies

Acquire data using ESP32, CCS811 and Edge Impulse

The first step is acquiring data using ESP32 and CCS811: we will acquire CO2 concentration and tVoC (volatile organic compounds). This data will be forwarded to Edge Impulse. Before building this project is necessary to create an Edge Impulse account for free and a new project.

Upload this code to the ESP32:

#include <Arduino.h>
#include <Wire.h>    // I2C library
#include "ccs811.h"  // CCS811 library
#define FREQUENCY_HZ        1
#define INTERVAL_MS         (1000 / (FREQUENCY_HZ + 1))
// Initialize CCS811
CCS811 ccs811(-1); 
static unsigned long last_interval_ms = 0;
  
void setup() {
  Serial.begin(115200);
  Serial.println("Air Quality monitoring with ML");
   // Enable I2C
  Wire.begin(); 
  
  // Enable CCS811
   bool ok= ccs811.begin();
  if( !ok ) Serial.println("setup: CCS811 begin FAILED");
    
  // Start measuring
  ok= ccs811.start(CCS811_MODE_1SEC);
  if( !ok ) Serial.println("setup: CCS811 start FAILED");
}
void loop() {
  uint16_t eco2, etvoc, errstat, raw;
  if (millis() > last_interval_ms + INTERVAL_MS) {
    last_interval_ms = millis();
    ccs811.read(&eco2,&etvoc,&errstat,&raw); 
  
    Serial.print(eco2);
    Serial.print(",");
    Serial.println(etvoc);
  }
  
}Code language: C++ (cpp)

This code acquires data every 1 second and write CO2 concentration and tVoC to the serial output. Next, run the edge impulse forward:

Now go to Data acquisition and you should see it:

Acquire data samples to detect anomaly using ESP32

Now start sampling data using sample length long enough. At the end, you have the raw data samples that we will use to detect anomaly using ESP32 and machine learning:

This is the normal pattern therefore each value measured outside of this pattern will be recognized as anomaly.

Other useful content:

Building the machine learning model to detect the anomaly with ESP32

Next, let us create the machine learning model that will use on the ESP32 to detect anomalies:

ESP32 machine learning model to detect anomalies

Then, let us extract features from the raw data and under anomaly detection select the following axes:

Finally, you can train the model and download it using the Deploymnent item.

Anomaly detection using ESP32

In this last step, we will use the machine learning model created before to detect anomaly with ESP32 and integrate this model with CCS811 to detect anomalies in the CO2 and tVoC:

#include <Arduino.h>
#include <air_quality_inference.h>
#include <Wire.h>    // I2C library
#include "ccs811.h"  // CCS811 library
#define FREQUENCY_HZ        1
#define INTERVAL_MS         (1000 / (FREQUENCY_HZ + 1))
float features[EI_CLASSIFIER_DSP_INPUT_FRAME_SIZE];
size_t feature_ix = 0;
// Initialize CCS811
CCS811 ccs811(-1); 
  
void setup() {
  Serial.begin(115200);
  Serial.println("Air Quality monitoring with ML");
   // Enable I2C
  Wire.begin(); 
  
  // Enable CCS811
   bool ok= ccs811.begin();
  if( !ok ) Serial.println("setup: CCS811 begin FAILED");
    
  // Start measuring
  ok= ccs811.start(CCS811_MODE_1SEC);
  if( !ok ) Serial.println("setup: CCS811 start FAILED");
  Serial.print("Features: ");
  Serial.println(EI_CLASSIFIER_DSP_INPUT_FRAME_SIZE);
  Serial.print("Label count: ");
  Serial.println(EI_CLASSIFIER_LABEL_COUNT);
   
}
void loop() {
  uint16_t eco2, etvoc, errstat, raw;
  static unsigned long last_interval_ms = 0;
 
  if (millis() > last_interval_ms + INTERVAL_MS) {
    last_interval_ms = millis();
    ccs811.read(&eco2,&etvoc,&errstat,&raw); 
    
   
    Serial.print(eco2);
    Serial.print(",");
    Serial.println(etvoc);
    // fill the features buffer
    features[feature_ix++] = eco2;
    features[feature_ix++] = etvoc;
    // Check if we are ready to reun the inference
    if (feature_ix == EI_CLASSIFIER_DSP_INPUT_FRAME_SIZE) {
      Serial.println("Running the inference...");
      signal_t signal;
      ei_impulse_result_t result;
      int err = numpy::signal_from_buffer(features, EI_CLASSIFIER_DSP_INPUT_FRAME_SIZE, &signal);
       if (err != 0) {
        ei_printf("Failed to create signal from buffer (%d)\n", err);
        return;
      }
      
      EI_IMPULSE_ERROR res = run_classifier(&signal, &result, true);
      if (res != 0) return;
      ei_printf("Predictions ");
      ei_printf("(DSP: %d ms., Classification: %d ms., Anomaly: %d ms.)",
        result.timing.dsp, result.timing.classification, result.timing.anomaly);
      ei_printf(": \n");
      for (size_t ix = 0; ix < EI_CLASSIFIER_LABEL_COUNT; ix++) {
        ei_printf("    %s: %.5f\n", result.classification[ix].label, result.classification[ix].value);
      }
      #if EI_CLASSIFIER_HAS_ANOMALY == 1
        ei_printf("    anomaly score: %.3f\n", result.anomaly);
      
        Serial.println("Anomaly Score");
        Serial.println(result.anomaly);
        delay(1000);
    #endif
      feature_ix = 0;
      Serial.println("==========");
    }
  }
  
  
}
/**
 * @brief      Printf function uses vsnprintf and output using Arduino Serial
 *
 * @param[in]  format     Variable argument list
 */
void ei_printf(const char *format, ...) {
    static char print_buf[1024] = { 0 };
    va_list args;
    va_start(args, format);
    int r = vsnprintf(print_buf, sizeof(print_buf), format, args);
    va_end(args);
    if (r > 0) {
        Serial.write(print_buf);
    }
}Code language: C++ (cpp)

Run the code on the ESP32.

Remember that the CCS811 sensor needs some time before reading correct values

How to detect anomaly with ESP32

Now it is time to detect anomaly with ESP32. Try to run the code above and when the values read adhere to the normal pattern you will have:

Now try to blow on the sensor and you will notice that the machine learning model will detect the anomaly.

Wrapping up

At the end of this post, we have implemented an ESP32 anomaly detection system that capable of identify anomalies in the CO2 e tVoC concentration. First we have detected the normal pattern acquiring data in a controlled environment and later using this pattern to detect the anomalies.

    1. Martin May 31, 2021
      • Francesco Azzola June 1, 2021
    2. archana August 4, 2022

    Add Your Comment