moisesstevend
Published © MIT

Showing numbers based on movement patterns with TinyML and m

Use TinyML on the M5ATOM to detect motion patterns with the MPU sensor and display numbers on the LED matrix.

IntermediateFull instructions provided2 hours35
Showing numbers based on movement patterns with TinyML and m

Things used in this project

Hardware components

ATOM Matrix ESP32 Development Kit
M5Stack ATOM Matrix ESP32 Development Kit
×1

Software apps and online services

Arduino IDE
Arduino IDE
Edge Impulse Studio
Edge Impulse Studio

Story

Read more

Schematics

Principal functions in deployment EI

Code

imu_m5stack2_infering.ino

C/C++
main code
#include "M5Atom.h"
#include <faces_m5stack_inferencing.h>
#include "numbers.h"

#define CONVERT_G_TO_MS2    9.80665f
#define FREQUENCY_HZ        100
#define INTERVAL_MS         (1000 / (FREQUENCY_HZ + 1))

static unsigned long last_interval_ms = 0;

float gyroX = 0.0F;
float gyroY = 0.0F;
float gyroZ = 0.0F;

float accX = 0.0F;
float accY = 0.0F;
float accZ = 0.0F;

float features[EI_CLASSIFIER_DSP_INPUT_FRAME_SIZE];

//std::map<String, float> inferencia;
struct Inferencia {
  String label;
  float accuracy;
};

Inferencia resp;
// keep track of where we are in the feature array
size_t feature_ix = 0;
float maximo=0;

void setup(){
  M5.begin(true, false, true);
  M5.IMU.Init();

  //Serial.printf("accX accY accZ gyroX gyroY gyroZ\n");
}

void loop(){
  //M5.update(); //Add M5.update() to read the button state, details please see System
  //if (M5.Btn.isPressed()) { //If the button is pressed
  if (millis() > last_interval_ms + INTERVAL_MS) {
    last_interval_ms = millis();

    M5.IMU.getAccelData(&accX,&accY,&accZ);
    M5.IMU.getGyroData(&gyroX,&gyroY,&gyroZ);

    // fill the features buffer
    features[feature_ix++] = accX;
    features[feature_ix++] = accY;
    features[feature_ix++] = accZ;
    features[feature_ix++] = gyroX;
    features[feature_ix++] = gyroY;
    features[feature_ix++] = gyroZ;    

    // features buffer full? then classify!
        if (feature_ix == EI_CLASSIFIER_DSP_INPUT_FRAME_SIZE) {
            ei_impulse_result_t result;

            // create signal from features frame
            signal_t signal;
            numpy::signal_from_buffer(features, EI_CLASSIFIER_DSP_INPUT_FRAME_SIZE, &signal);

            // run classifier
            EI_IMPULSE_ERROR res = run_classifier(&signal, &result, false);
            ei_printf("run_classifier returned: %d\n", res);
            if (res != 0) return;

            // print predictions
            ei_printf("Predictions (DSP: %d ms., Classification: %d ms., Anomaly: %d ms.): \n",
                result.timing.dsp, result.timing.classification, result.timing.anomaly);

            maximo=0;
            // print the predictions
            for (size_t ix = 0; ix < EI_CLASSIFIER_LABEL_COUNT; ix++) {
                ei_printf("%s:\t%.5f\n", result.classification[ix].label, result.classification[ix].value); 
                
                if(result.classification[ix].value>maximo){
                    maximo = result.classification[ix].value;
                    resp.label = result.classification[ix].label;
                    resp.accuracy = result.classification[ix].value;
                    //inferencia[result.classification[ix].label] =  result.classification[ix].value;
                }
                      
            }

            if(resp.accuracy>=0.8){
              ei_printf("Prediccion Final: %s:\t%.5f\n", resp.label, resp.accuracy); 
              if(resp.label=="1"){
                show_face(NUMBER_ONE);
              }else if(resp.label=="2"){
                show_face(NUMBER_TWO);
              }else if(resp.label=="idle"){
                show_face(FACE_SAD);
              }
            }else{
              show_face(FACE_SAD);
            }
        #if EI_CLASSIFIER_HAS_ANOMALY == 1
            ei_printf("anomaly:\t%.3f\n", result.anomaly);
        #endif

            // reset features frame
            feature_ix = 0;
        }
    //Serial.printf("%5.2f  %5.2f  %5.2f %6.2f %6.2f %6.2f\n", accX, accY, accZ, gyroX, gyroY, gyroZ);
    //delay(10);
  }
  //}
}

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

numbers.h

C/C++
additional library
#include "Arduino.h"

int NUMBER_ONE[5][5]={
  {0,0,0,0,0},
  {0,0,0,0,0},
  {0,1,0,0,0},
  {1,1,1,1,1},
  {0,0,0,0,0}
};
/*
int NUMBER_TWO[5][5]={
  {0,0,0,0,0},
  {1,0,1,1,0},
  {1,0,1,0,1},
  {1,0,0,0,1},
  {0,0,0,0,0}
};
*/
int NUMBER_TWO[5][5]={
  {0,0,0,0,0},
  {1,0,0,1,1},
  {1,0,1,0,1},
  {0,1,0,0,1},
  {0,0,0,0,0}
};

int FACE_SAD[5][5]={
  {0,0,0,0,1},
  {0,1,0,1,0},
  {0,0,0,1,0},
  {0,1,0,1,0},
  {0,0,0,0,1}
};

void show_face(int _face[5][5]){
  for(int i=0;i<5;i++){
    for(int j=0;j<5;j++){
      if(_face[i][j]==1){
        M5.dis.drawpix(i,j, 0xFFFFFF);
      }else{
        M5.dis.drawpix(i,j, 0x000000);
      }    
    }
  }
}

Credits

moisesstevend

moisesstevend

4 projects • 1 follower
I am Moises, I'm a Peruvian engineer with a strong interest about machine learning, IoT for healthcare and public health.

Comments