/* 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);
}
}
Comments