Power outages anticipation device ready for Maker Faire Rome

0 22517 Medium

Edenoff, a TinyML Arduino device was modified for secure and interactive demos at Maker Faire Rome 2022

Power outages anticipation device ready for Maker Faire Rome

Things used in this project

 

Hardware components

HARDWARE LIST
1 Arduino Nano 33 BLE Sense
1 DFRobot Digital Buzzer
1 DFRobot FF Jumper wires
1 DFRobot Gravity:Analog Rotation Potentiometer Sensor V1 For Arduino
1 DFRobot Gravity I2C OLED-2864 Display
1 DFRobot button light buttons

Software apps and online services

 

Arduino IDE

 

Autodesk Fusion 360: https://www.hackster.io/autodesk/products/fusion-360?ref=project-277e05

 

Edge Impulse Studio

Story

 

Just shut off the light, Ooh, let the good times roll, child (Shut off the light, Betty Davis, 1975)

 

Simulated AC voltage, external temp sensor and Oled info screen

 

Simulated AC voltage, external temp sensor and Oled info screen

 

Power outages are not unlikely in Argentina and other regions of Latin America. On one hand, the gove rnment says it is due to lack of private infrastructure investment. On the other, electricity distributors argues non discriminated subsidized rates and regulations. One case or another, private companies' production and even equipment – not so easy to replace and import in Argentina - suffers.

 

This scenario was the starting point for a project named EdenOff (after Edenor, one of two power distributor in Buenos Aires)

 

 

Original version

 

Device fits into a power outlet

 

Device fits into a power outlet

 

The original version was designed to fit a power outlet with a Zmpt101b voltage sensor, but that doesn't sound safe enough for a crowded Maker Faire.

 

On the other hand, Italy is not Argentina and the Faire will be over way before sensing significant AC variations.

 

Wall mount

 

Wall mount

 

So, I've decided to replace the Zmpt101b by a potentiometer and show the "AC intensity" with a Led.

CODE
int potValue = analogRead(A1);
  
// map pot to voltage
voltage=map(potValue, 0, 1024, 260, 180);

// map led intensity
int ledValue=map(voltage, 199, 260, 0, 255);
  
// write to led
analogWrite(pinLed,ledValue);

The internal temperature sensor was replaced by an external one, so a heat source will modify the data provided to the model.

External Temp Sensor

External Temp Sensor

CODE
#include "DHT.h"
# define DHTTYPE DHT11
DHT dht(DHTPIN, DHTTYPE);

// setup
dht.begin();

// loop
temperature=dht.readTemperature();

To make things more interactive, I've also added an Oled screen, so every AC reading along with temperature is displayed in X/Y chart

CODE
void printLines(){
  
  // horizontal line
  display.drawLine(1, 20, 128, 20,SSD1306_WHITE);
  
  // vertical line
  display.drawLine(1, 0, 1, 32,SSD1306_WHITE);
  
  // horizontal scales
  for (int i=1; i <= 128; i=i+25){
    display.drawLine(i, 17, i, 20,SSD1306_WHITE);
   }
  
   // vertical scales
  for (int i=1; i <= 20; i=i+5){
    display.drawLine(1, i, 3, i,SSD1306_WHITE);
   }
}
// map voltage 1 to 32
        myY=map(voltage, 180, 260, 10, 32);
        if (myY<1) {myY=1;}        
        myY=32-myY; // inverse scale


        // print voltage circle
        display.fillCircle(myX, myY, 2,SSD1306_WHITE);
        
        // print temp
        display.setCursor(myX, 24);
        int intTemp=int(temperature);
        display.println(intTemp);
          
        display.display();

        // increase x
        myX=myX+20;
        
        // check limit
        if (myX>120) {
          Serial.print("myX>120 clearing display, reseting counter");
          myX=20;
          myCounter=0;
          display.clearDisplay();
        }

Everything seems ready. Dots are AC readings, numbers are temperature in Celsius

 

 

 

 

Visiting Maker Faire Rome this week?

 

Come play with Edenoff at Stand C.38 (pav. C). L 1.19 I will have ready other interesting devices as well. Electronic parts provided by DFRobot.

 

Edenoff Maker Faire Rome 2022 Flyer

 

Edenoff Maker Faire Rome 2022 Flyer

 

Demo video (english CC caption available)

Contact

 

Instagram

 

Twitter

Code

 

Edenoff demo with potentiometer and Oled screen

 

C/C++

Edenoff demo version

CODE
/* EdenOff 
 * Power outages prediction with Machine Learning
 * Using Edge Impulse Platform
 * Maker Faire Rome 2022 Demo version 
 * Roni Bandini, Argentina, April 2022, update September 2022
 * @RoniBandini
 */

#include <VoltageVariation_inferencing.h>
#include <SPI.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include "DHT.h"

#define SCREEN_WIDTH 128  
#define SCREEN_HEIGHT 32  
#define OLED_RESET     4  
#define SCREEN_ADDRESS 0x3C  
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);


#define imageWidthLogo 128
#define imageHeightLogo 32

# define pinBuzzer 9
# define pinLed 10
# define DHTPIN 11
# define pinButton 12 

# define DHTTYPE DHT11    

const unsigned char logo [] PROGMEM = {
  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 
  0xff, 0xff, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0xff, 0xff, 
  0xff, 0xff, 0xe0, 0x00, 0x00, 0x40, 0x00, 0x04, 0x00, 0x08, 0x20, 0x00, 0x00, 0x07, 0xff, 0xff, 
  0xff, 0xff, 0xe0, 0x44, 0x08, 0x00, 0x10, 0x02, 0x40, 0x00, 0x00, 0x08, 0x88, 0x07, 0xff, 0xff, 
  0xff, 0xff, 0xe8, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x07, 0xff, 0xff, 
  0xff, 0xff, 0xf7, 0xbf, 0xef, 0xee, 0xdf, 0xee, 0xff, 0xff, 0xfb, 0xee, 0xff, 0xff, 0xff, 0xff, 
  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 
  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf3, 0x7f, 0xfd, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 
  0xff, 0xff, 0xff, 0x01, 0x00, 0x7f, 0xc0, 0x00, 0x0f, 0xe0, 0x7f, 0x80, 0xf0, 0x0f, 0xff, 0xff, 
  0xff, 0xff, 0xfc, 0x01, 0x00, 0x3f, 0x00, 0x40, 0x07, 0x80, 0x1e, 0x00, 0xe0, 0x0f, 0xff, 0xff, 
  0xff, 0xff, 0xf8, 0x00, 0x00, 0x1f, 0x00, 0x40, 0x03, 0xc0, 0x1e, 0x00, 0xe0, 0x0f, 0xff, 0xff, 
  0xff, 0xff, 0xf8, 0x4b, 0x12, 0x0c, 0x15, 0x47, 0x43, 0x02, 0x0c, 0x14, 0x82, 0xaf, 0xff, 0xff, 
  0xff, 0xff, 0xf0, 0x7e, 0x1f, 0x0c, 0x1f, 0xc7, 0xe1, 0x0f, 0x00, 0x7f, 0x83, 0xff, 0xff, 0xff, 
  0xff, 0xff, 0xf0, 0xcb, 0x1f, 0x8c, 0x6a, 0x87, 0xe3, 0x1f, 0x80, 0x95, 0x8d, 0x5f, 0xff, 0xff, 
  0xff, 0xff, 0xf1, 0x01, 0x1f, 0x84, 0x40, 0x47, 0xe1, 0x1f, 0xc0, 0xc1, 0x88, 0x1f, 0xff, 0xff, 
  0xff, 0xff, 0xf1, 0x01, 0x1f, 0x84, 0x40, 0xc7, 0xe1, 0x1f, 0x80, 0x81, 0x88, 0x0f, 0xff, 0xff, 
  0xff, 0xff, 0xf1, 0x81, 0x1f, 0x84, 0x40, 0x47, 0xe1, 0x1f, 0xc0, 0x81, 0x08, 0x1f, 0xff, 0xff, 
  0xff, 0xff, 0xf0, 0x5b, 0x1f, 0x8c, 0x35, 0x47, 0xe1, 0x0f, 0x80, 0xe9, 0x8d, 0xbf, 0xff, 0xff, 
  0xff, 0xff, 0xf0, 0xfe, 0x1f, 0x0c, 0x1f, 0x87, 0xf1, 0x0f, 0x88, 0xff, 0x8f, 0xff, 0xff, 0xff, 
  0xff, 0xff, 0xf8, 0x15, 0x14, 0x0e, 0x15, 0xc7, 0xe3, 0x80, 0x08, 0xff, 0x8f, 0xff, 0xff, 0xff, 
  0xff, 0xff, 0xf8, 0x01, 0x10, 0x1f, 0x00, 0x47, 0xf1, 0xc0, 0x08, 0xff, 0x8f, 0xff, 0xff, 0xff, 
  0xff, 0xff, 0xfe, 0x01, 0x10, 0x3f, 0x00, 0x07, 0xe1, 0xc4, 0x38, 0xff, 0x8f, 0xff, 0xff, 0xff, 
  0xff, 0xff, 0xff, 0x81, 0x10, 0xff, 0xc0, 0x47, 0xe1, 0xf0, 0x70, 0xff, 0x0f, 0xff, 0xff, 0xff, 
  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 
  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 
  0xff, 0xff, 0xe8, 0xa9, 0x2a, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0x95, 0x52, 0x57, 0xff, 0xff, 
  0xff, 0xff, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xff, 0xff, 
  0xff, 0xff, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xff, 0xff, 
  0xff, 0xff, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0xff, 0xff, 
  0xff, 0xff, 0xf2, 0x48, 0x40, 0x41, 0x12, 0x48, 0x40, 0x10, 0x84, 0x92, 0x44, 0xa7, 0xff, 0xff, 
  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 
  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
};


// Temp
DHT dht(DHTPIN, DHTTYPE);

float old_temp = 0;
float old_hum = 0;

int failFlag=0;
float failureInference=0;

// Axis array
float features[3];

// Display
String line1="";
String line2="";
String line3="";
String line4="";
int myX=20;
int myY=0;

int raw_feature_get_data(size_t offset, size_t length, float *out_ptr) {
    memcpy(out_ptr, features + offset, length * sizeof(float));
    return 0;
}


float voltage=0;
float temperature=0;
float avglatest5=0;
float sumVoltage=0;
int   myCounter=0;
int inferenceCounter=0;
float threesold=0.85;
int testFail=0;
int iterationsForAvg=5;
int delayIteration=3000;

// buzzer beep
void myBeep(){     
     tone(pinBuzzer, 349, 500);
     delay(150);                        
     tone(pinBuzzer, 200, 500);
     delay(150); 
     tone(pinBuzzer, 150, 500);
     delay(500);   
     noTone(pinBuzzer);
}

void printLines(){
  
  // horizontal line
  display.drawLine(1, 20, 128, 20,SSD1306_WHITE);
  
  // vertical line
  display.drawLine(1, 0, 1, 32,SSD1306_WHITE);
  
  // horizontal scales
  for (int i=1; i <= 128; i=i+25){
    display.drawLine(i, 17, i, 20,SSD1306_WHITE);
   }
  
   // vertical scales
  for (int i=1; i <= 20; i=i+5){
    display.drawLine(1, i, 3, i,SSD1306_WHITE);
   }
}
  
void updateScreen(){

      display.clearDisplay();
      display.setTextSize(1);    
      display.setCursor(0, 0);        
      display.setTextColor(SSD1306_WHITE); 
      display.println(line1);
      display.println(line2); 
      display.println(line3); 
      display.println(line4); 
      display.display();
}
  
void setup()
{

    pinMode(pinBuzzer, OUTPUT);  
    pinMode(pinLed, OUTPUT);  
    pinMode(pinButton, INPUT); 
    
    dht.begin();
    
    Serial.begin(115200);
    Serial.println("EdenOff v 2.0");
    Serial.println("TinyML via Edge Impulse");
    Serial.println("Roni Bandini, September 2022");
    Serial.println("@RoniBandini");
    Serial.println("------------------------------------");

     if(!display.begin(SSD1306_SWITCHCAPVCC, SCREEN_ADDRESS)) {
        Serial.println(F("SSD1306 allocation failed"));
        for(;;); // Don't proceed, loop forever
    }

      display.display();
      delay(2000); 
      display.clearDisplay();   
      display.drawBitmap(0, 0,  logo, imageWidthLogo, imageHeightLogo, 1); 
      display.display();   
      delay(4000);
    
      display.clearDisplay();
      display.setTextSize(1);            
      display.setTextColor(SSD1306_WHITE); 
      display.println("Edenoff 2.0");
      display.println("Arduino & TinyML"); 
      display.println("@RoniBandini 9/22"); 
      display.println("Maker Faire Rome");
      display.display();
      delay(3000); 

      display.clearDisplay();
  
}


void loop()

{

  int butValue = digitalRead(pinButton);

  // read temp
  temperature=dht.readTemperature();  
  
  // read AC
    
  int potValue = analogRead(A1);
  
  // map pot to voltage
  voltage=map(potValue, 0, 1024, 260, 180);

    //Serial.println("Pot voltage: ");
    //Serial.println(voltage);

  // map led intensity
  int ledValue=map(voltage, 199, 260, 0, 255);

  //Serial.println("Led value: ");
  //Serial.println(ledValue);
    
  // write to led
  analogWrite(pinLed,ledValue);
         
  if (myCounter==iterationsForAvg+1){                                   

        inferenceCounter++;
        
        if (sumVoltage>0) {
          avglatest5=(sumVoltage)/iterationsForAvg;
        }

        ei_printf("Inference #: ");
        Serial.println(inferenceCounter);
        
        ei_printf("Latest AC: ");
        Serial.println(voltage);

        ei_printf("AVG 5 AC: ");
        Serial.println(avglatest5);

        ei_printf("Temperature: ");
        Serial.println(temperature);

        features[0] = voltage;
        features[1] = temperature;
        features[2] = avglatest5;

        if (sizeof(features) / sizeof(float) != EI_CLASSIFIER_DSP_INPUT_FRAME_SIZE) {
            ei_printf("The size of your 'features' array is not correct. Expected %lu items, but had %lu\n",
                EI_CLASSIFIER_DSP_INPUT_FRAME_SIZE, sizeof(features) / sizeof(float));
            delay(1000);
            return;
        }
    
        ei_impulse_result_t result = { 0 };
    
        // the features are stored into flash, and we don't want to load everything into RAM
        signal_t features_signal;
        features_signal.total_length = sizeof(features) / sizeof(features[0]);
        features_signal.get_data = &raw_feature_get_data;
    
        // invoke the impulse
        EI_IMPULSE_ERROR res = run_classifier(&features_signal, &result, false /* debug */);
        ei_printf("run_classifier returned: %d\n", res);
    
        if (res != 0) return;
    
        // print predictions
        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");
        ei_printf("[");
        for (size_t ix = 0; ix < EI_CLASSIFIER_LABEL_COUNT; ix++) {
            ei_printf("%.5f", result.classification[ix].value);
          #if EI_CLASSIFIER_HAS_ANOMALY == 1
                  ei_printf(", ");
          #else
                  if (ix != EI_CLASSIFIER_LABEL_COUNT - 1) {
                      ei_printf(", ");
                  }
          #endif
              }
          #if EI_CLASSIFIER_HAS_ANOMALY == 1
              ei_printf("%.3f", result.anomaly);
          #endif
              ei_printf("]\n");
    
          // human-readable predictions
          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 (result.classification[ix].label=="failure"){
              failureInference=result.classification[ix].value;  
             }            


            if (float(result.classification[ix].value)>threesold and result.classification[ix].label=="failure" and butValue==1)
            {              
              // power outage detected
              myBeep(); 
              display.clearDisplay();
              line1="Outage coming";
              line2="Inference "+String(result.classification[ix].value)+"%";
              line3=String(voltage)+" V";
              line4=String(temperature)+" Celsius";              
              updateScreen();
              delay(5000);
              failFlag=1;                                          
            }
                       
          }
          
          #if EI_CLASSIFIER_HAS_ANOMALY == 1
              ei_printf("    anomaly score: %.3f\n", result.anomaly);
          #endif


        if (failFlag==0 and butValue==1){

          // no outage, just print avg readings  
          display.clearDisplay();      
          line1="Regular service";
          line2="Inference "+String(failureInference)+" %";  
          line3=String(voltage)+" AC "+ String(avglatest5)+" Avg";
          line4=String (temperature)+" Celsius";              
          updateScreen();      
          delay(5000);
                                 
        
        }
       
          // reset calculations
          avglatest5=0;
          sumVoltage=0;
          myCounter=0;
          failFlag=0; 
          myX=10;   
          display.clearDisplay();   
        
     }
     else
     {

        ei_printf("Counter: ");
        Serial.println(myCounter);
  
        // iterations did not reach limit
        
        //Serial.print("Voltage: ");
        //Serial.println(voltage);   
        
        sumVoltage=sumVoltage+voltage;               
        
        printLines();
        
        // map voltage 1 to 32
        myY=map(voltage, 180, 260, 10, 32);
        if (myY<1) {myY=1;}        
        myY=32-myY; // inverse scale
        //Serial.print("Mapped Voltage: ");
        //Serial.println(myY); 

        // print voltage circle
        display.fillCircle(myX, myY, 2,SSD1306_WHITE);
        
        // print temp
        display.setCursor(myX, 24);
        int intTemp=int(temperature);
        display.println(intTemp);
          
        display.display();

        // increase x
        myX=myX+20;
        
        // check limit
        if (myX>120) {
          Serial.print("myX>120 clearing display, reseting counter");
          myX=20;
          myCounter=0;
          display.clearDisplay();
        }

        
      }
           
    delay(delayIteration);
    
    myCounter=myCounter+1;
    
}

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);
    }
}

The article was first published in hackster, April 9, 2022

cr: https://www.hackster.io/roni-bandini/power-outages-anticipation-device-ready-for-maker-faire-rome-277e05

author: Roni Bandini

License
All Rights
Reserved
licensBg
0