Running Movement in Arduino Nicla Sense ME error

Question/Issue: I’m having problems running the static buffer file after exporting from Edge Impulse

Project ID: Movement model Nicla Sense Me

Context/Use case: I’m using this guide to try running a movement model in my Arduino locally, but I’m having this error after uploading to my board:

Edge Impulse standalone inferencing (Arduino)
The size of your 'features' array is not correct. Expected 1300 items, but had 0

So I’m not sure what should I change in the code, what I’ve seen doing my research was that I should change this codeline but I tried and when I change it, it doesn’t even compiled:

static const float features[] = {
    // copy raw features here (for example from the 'Live classification' page)
    // see https://docs.edgeimpulse.com/docs/running-your-impulse-arduino
};

Help please!

Exactly. static const float features[] = {} is indeed zero length and has zero items.

You need to copy raw features like this.

1 Like

Hi actually I did it before but it’s uses it as a static value and I want to read the sensor values there here is what appears all the time if I use the raw features:

Edge Impulse standalone inferencing (Arduino)
run_classifier returned: 0
Predictions (DSP: 201 ms., Classification: 0 ms., Anomaly: 0 ms.): 
[0.00000, 0.99609]
    up: 0.00000
    wave: 0.99609

Hello @je.padron,

Once you’ve imported your library in Arduino IDE, we provide example for all our officially supported arduino boards. For example:

Best,

Louis

1 Like

I just tried on the Nicla Sense ME with the motion recognition tutorial and I confirm that it’s working on my side:

Starting inferencing in 2 seconds...
Sampling...
Predictions (DSP: 15 ms., Classification: 0 ms., Anomaly: 1 ms.):
idle: 0.99609
snake: 0.00391
updown: 0.00000
wave: 0.00000
    anomaly score: 0.073

Starting inferencing in 2 seconds...
Sampling...
Predictions (DSP: 15 ms., Classification: 0 ms., Anomaly: 1 ms.):
idle: 0.00000
snake: 0.00000
updown: 0.99609
wave: 0.00000
    anomaly score: 0.227

Best,

Louis

1 Like

Hi @louis when I used the nicla_sense_fusion in the serial monitor appears this:

Starting inferencing in 2 seconds...
ERR: Nicla sensors don't match the sensors required in the model
Following sensors are required: accel.x + accel.y + accel.z + gyro.x + gyro.y + gyro.z + ori.heading + ori.pitch + ori.roll + rotation.x + rotation.y + rotation.z + rotation.w

So the code in my example looks like this…

/* Includes ---------------------------------------------------------------- */
#include <sense-me-je_inferencing.h>
#include "Arduino_BHY2.h" //Click here to get the library: http://librarymanager/All#Arduino_BHY2

/** Struct to link sensor axis name to sensor value function */
typedef struct{
    const char *name;
    float (*get_value)(void);

}eiSensors;

/* Constant defines -------------------------------------------------------- */
#define CONVERT_G_TO_MS2    9.80665f

/** Number sensor axes used */
#define NICLA_N_SENSORS     17


/* Private variables ------------------------------------------------------- */
static const bool debug_nn = false; // Set this to true to see e.g. features generated from the raw signal

SensorXYZ accel(SENSOR_ID_ACC);
SensorXYZ gyro(SENSOR_ID_GYRO);
SensorOrientation ori(SENSOR_ID_ORI);
SensorQuaternion rotation(SENSOR_ID_RV);
Sensor temp(SENSOR_ID_TEMP);
Sensor baro(SENSOR_ID_BARO);
Sensor hum(SENSOR_ID_HUM);
Sensor gas(SENSOR_ID_GAS);

static bool ei_connect_fusion_list(const char *input_list);
static float get_accX(void){return (accel.x() * 8.0 / 32768.0) * CONVERT_G_TO_MS2;}
static float get_accY(void){return (accel.y() * 8.0 / 32768.0) * CONVERT_G_TO_MS2;}
static float get_accZ(void){return (accel.z() * 8.0 / 32768.0) * CONVERT_G_TO_MS2;}
static float get_gyrX(void){return (gyro.x() * 8.0 / 32768.0) * CONVERT_G_TO_MS2;}
static float get_gyrY(void){return (gyro.y() * 8.0 / 32768.0) * CONVERT_G_TO_MS2;}
static float get_gyrZ(void){return (gyro.z() * 8.0 / 32768.0) * CONVERT_G_TO_MS2;}
static float get_oriHeading(void){return ori.heading();}
static float get_oriPitch(void){return ori.pitch();}
static float get_oriRoll(void){return ori.roll();}
static float get_rotX(void){return rotation.x();}
static float get_rotY(void){return rotation.y();}
static float get_rotZ(void){return rotation.z();}
static float get_rotW(void){return rotation.w();}
static float get_temperature(void){return temp.value();}
static float get_barrometric_pressure(void){return baro.value();}
static float get_humidity(void){return hum.value();}
static float get_gas(void){return gas.value();}

static int8_t fusion_sensors[NICLA_N_SENSORS];
static int fusion_ix = 0;

/** Used sensors value function connected to label name */
eiSensors nicla_sensors[] =
{
    "accX", &get_accX,
    "accY", &get_accY,
    "accZ", &get_accZ,
    "gyrX", &get_gyrX,
    "gyrY", &get_gyrY,
    "gyrZ", &get_gyrZ,
    "heading", &get_oriHeading,
    "pitch", &get_oriPitch,
    "roll", &get_oriRoll,
    "rotX", &get_rotX,
    "rotY", &get_rotY,
    "rotZ", &get_rotZ,
    "rotW", &get_rotW,
    "temperature", &get_temperature,
    "barometer", &get_barrometric_pressure,
    "humidity", &get_humidity,
    "gas", &get_gas,
};

/**
* @brief      Arduino setup function
*/
void setup()
{
    /* Init serial */
    Serial.begin(115200);
    // comment out the below line to cancel the wait for USB connection (needed for native USB)
    while (!Serial);
    Serial.println("Edge Impulse Sensor Fusion Inference\r\n");

    /* Connect used sensors */
    if(ei_connect_fusion_list(EI_CLASSIFIER_FUSION_AXES_STRING) == false) {
        ei_printf("ERR: Errors in sensor list detected\r\n");
        return;
    }

    /* Init & start sensors */
    BHY2.begin(NICLA_I2C);
    accel.begin();
    gyro.begin();
    ori.begin();
    rotation.begin();
    temp.begin();
    baro.begin();
    hum.begin();
    gas.begin();
}

/**
* @brief      Get data and run inferencing
*/
void loop()
{
    ei_printf("\nStarting inferencing in 2 seconds...\r\n");

    delay(2000);

    if (EI_CLASSIFIER_RAW_SAMPLES_PER_FRAME != fusion_ix) {
        ei_printf("ERR: Nicla sensors don't match the sensors required in the model\r\n"
        "Following sensors are required: %s\r\n", EI_CLASSIFIER_FUSION_AXES_STRING);
        return;
    }

    ei_printf("Sampling...\r\n");

    // Allocate a buffer here for the values we'll read from the IMU
    float buffer[EI_CLASSIFIER_DSP_INPUT_FRAME_SIZE] = { 0 };

    for (size_t ix = 0; ix < EI_CLASSIFIER_DSP_INPUT_FRAME_SIZE; ix += EI_CLASSIFIER_RAW_SAMPLES_PER_FRAME) {
        // Determine the next tick (and then sleep later)
        int64_t next_tick = (int64_t)micros() + ((int64_t)EI_CLASSIFIER_INTERVAL_MS * 1000);

        // Update function should be continuously polled
        BHY2.update();

        for(int i = 0; i < fusion_ix; i++) {
            buffer[ix + i] = nicla_sensors[fusion_sensors[i]].get_value();
        }

        int64_t wait_time = next_tick - (int64_t)micros();

        if(wait_time > 0) {
            delayMicroseconds(wait_time);
        }
    }

    // Turn the raw buffer in a signal which we can the classify
    signal_t signal;
    int err = numpy::signal_from_buffer(buffer, EI_CLASSIFIER_DSP_INPUT_FRAME_SIZE, &signal);
    if (err != 0) {
        ei_printf("ERR:(%d)\r\n", err);
        return;
    }

    // Run the classifier
    ei_impulse_result_t result = { 0 };

    err = run_classifier(&signal, &result, debug_nn);
    if (err != EI_IMPULSE_OK) {
        ei_printf("ERR:(%d)\r\n", err);
        return;
    }

    // print the predictions
    ei_printf("Predictions (DSP: %d ms., Classification: %d ms., Anomaly: %d ms.):\r\n",
        result.timing.dsp, result.timing.classification, result.timing.anomaly);
    for (size_t ix = 0; ix < EI_CLASSIFIER_LABEL_COUNT; ix++) {
        ei_printf("%s: %.5f\r\n", result.classification[ix].label, result.classification[ix].value);
    }
#if EI_CLASSIFIER_HAS_ANOMALY == 1
    ei_printf("    anomaly score: %.3f\r\n", result.anomaly);
#endif
}

#if !defined(EI_CLASSIFIER_SENSOR) || (EI_CLASSIFIER_SENSOR != EI_CLASSIFIER_SENSOR_FUSION && EI_CLASSIFIER_SENSOR != EI_CLASSIFIER_SENSOR_ACCELEROMETER)
#error "Invalid model for current sensor"
#endif


/**
 * @brief Go through nicla sensor list to find matching axis name
 *
 * @param axis_name
 * @return int8_t index in nicla sensor list, -1 if axis name is not found
 */
static int8_t ei_find_axis(char *axis_name)
{
    int ix;
    for(ix = 0; ix < NICLA_N_SENSORS; ix++) {
        if(strstr(axis_name, nicla_sensors[ix].name)) {
            return ix;
        }
    }
    return -1;
}

/**
 * @brief Check if requested input list is valid sensor fusion, create sensor buffer
 *
 * @param[in]  input_list      Axes list to sample (ie. "accX + gyrY + magZ")
 * @retval  false if invalid sensor_list
 */
static bool ei_connect_fusion_list(const char *input_list)
{
    char *buff;
    bool is_fusion = false;

    /* Copy const string in heap mem */
    char *input_string = (char *)ei_malloc(strlen(input_list) + 1);
    if (input_string == NULL) {
        return false;
    }
    memset(input_string, 0, strlen(input_list) + 1);
    strncpy(input_string, input_list, strlen(input_list));

    /* Clear fusion sensor list */
    memset(fusion_sensors, 0, NICLA_N_SENSORS);
    fusion_ix = 0;

    buff = strtok(input_string, "+");

    while (buff != NULL) { /* Run through buffer */
        int8_t found_axis = 0;

        is_fusion = false;
        found_axis = ei_find_axis(buff);

        if(found_axis >= 0) {
            if(fusion_ix < NICLA_N_SENSORS) {
                fusion_sensors[fusion_ix++] = found_axis;
            }
            is_fusion = true;
        }

        buff = strtok(NULL, "+ ");
    }

    ei_free(input_string);

    return is_fusion;
}

Best Eduardo

This is a guess but try deleting the following lines in setup() since it appears you did not train your Model with these sensors.
temp.begin();
baro.begin();
hum.begin();
gas.begin();

1 Like

hi @MMarcial
I tried but it stop and gives me this:

Basically I think maybe I should reboot my laptop or something, uninstall the Arduino or what recomendation you give me I’m having a lot of problems

st detected

Starting inferencing in 2 seconds...
ERR: Nicla sensors don't match the sensors required in the model
Following sensors are required: accel.x + accel.y + accel.z + gyro.x + gyro.y + gyro.z + ori.heading + ori.pitch + ori.roll + rotation.x Sampling...

++ MbedOS Fault Handler ++

FaultType: HardFault

Context:
R   0: 00000000
R   1: 20001124
R   2: 0005DE1C
R   3: 20000150
R   4: 20004498
R   5: 20001138
R   6: 003AA9F7
R   7: 00000000
R   8: 000186A0
R   9: 00000000
R  10: 200048A8
R  11: 00000000
R  12: 20004236
SP   : 20004378
LR   : 00012A49
PC   : 0005DE1C
xPSR : 200F0000
PSP  : 20004310
MSP  : 2000FFC0
CPUID: 410FC241
HFSR : 40000000
MMFSR: 00000000
BFSR : 00000000
UFSR : 00000002
DFSR : 00000000
AFSR : 00000000
Mode : Thread
Priv : Privileged
Stack: PSP

-- MbedOS Fault Handler --



++ MbedOS Error Info ++
Error Status: 0x80FF013D Code: 317 Module: 255
Error Message: Fault exception
Location: 0x5DE1C
Error Value: 0x20009C54
Current Thread: main Id: 0x200048FC Entry: 0x20603 StackSize: 0xC00 StackMem: 0x20003CD8 SP: 0x20004378 
For more info, visit: https://mbed.com/s/error?error=0x80FF013D&tgt=NICLA
-- MbedOS Error Info --

I need access to your ZIP file for your Edge Impulse deployed library for sense-me-je_inferencing.h. Either:

  • Make me a Collaborator on your EI project or
  • Make your EI project public, or
  • Post all Arduino code to GitHub.

Until then find function ei_connect_fusion_list() and see how EI_CLASSIFIER_FUSION_AXES_STRING is defined.

1 Like

Hi @MMarcial,
I think I make it correctly, I’m really new with Edge Impulse.

Let me know if that’s okay

@je.padron I found your public EI project. Thankyou.

It looks like function ei_find_axis() is comparing values from

#define EI_CLASSIFIER_FUSION_AXES_STRING "accel.x + accel.y + accel.z + gyro.x + gyro.y + gyro.z + ori.heading + ori.pitch + ori.roll + rotation.x + rotation.y + rotation.z + rotation.w"

to

the names found in array, eiSensors nicla_sensors[].

Try modifying eiSensors nicla_sensors[] as:

eiSensors nicla_sensors[] =
{
“accel.x”, &get_accX,
“accel.y”, &get_accY,
“accel.z”, &get_accZ,
“gyro.x”, &get_gyrX,
“gyro.y”, &get_gyrY,
“gyro.z”, &get_gyrZ,
“ori.heading”, &get_oriHeading,
“ori.pitch”, &get_oriPitch,
“ori.roll”, &get_oriRoll,
“rotation.x”, &get_rotX,
“rotation.y”, &get_rotY,
“rotation.z”, &get_rotZ,
“rotation.w”, &get_rotW,
“temperature”, &get_temperature,
“barometer”, &get_barrometric_pressure,
“humidity”, &get_humidity,
“gas”, &get_gas,
};

I know this is contrary to the official EI docs that state “These exact axis names (accX, accY, etc.) are required for the Edge Impulse Arduino library deployment example applications for the Nicla Sense.”

1 Like

@MMarcial,

Indeed, another user had the same issue: My edge impulse code for Nicla Sense Me doesn't work - #2 by louis

I will update the documentation to mention that we can also change the axis names in the eiSensors nicla_sensors[] object.

Best,

Louis

1 Like

FYI, Just updated the documentation, it should be merged in a couple of minutes.

Best,

Louis

1 Like

@louis This is the process I follow:

I started with creating a project and selecting accelerometer data then I upload to the nicla sense the ingestion code in arduino, and then I use the edge-impulse-data-forwarder --clean in my comand line in windows and the appears this:

So I used the names accel.x, accel.y, accel.z, gyro.x, gyro.y, gyro.z, ori.heading, ori.pitch, ori.roll, rotation.x, rotation.y, rotation.z, rotation.w and then I started using the data collector tool from the platform that’s the right wey to do it? or the names are wrong?

Check my last answer

The names you give the sensors must match what is in your INO file. For example, if your INO file has:

/** Used sensors value function connected to label name */
eiSensors nicla_sensors[] =
{
    "accX", &get_accX,
    "accY", &get_accY,
    "accZ", &get_accZ,
    "gyrX", &get_gyrX,
    "gyrY", &get_gyrY,
    "gyrZ", &get_gyrZ,
    "heading", &get_oriHeading,
    "pitch", &get_oriPitch,
    "roll", &get_oriRoll,
    "rotX", &get_rotX,
    "rotY", &get_rotY,
    "rotZ", &get_rotZ,
    "rotW", &get_rotW,
    "temperature", &get_temperature,
    "barometer", &get_barrometric_pressure,
    "humidity", &get_humidity,
    "gas", &get_gas,
};

Then name your sensors: accX, accY, accZ, gyrX, gyrY, gyrZ, heading, pitch, roll, rotX, rotY, rotZ, and rotW.

1 Like

That was the problem, thanks for your help @MMarcial @louis

@louis I need some extra help with the tutorial for Nicla with Edge to the gathering I’m not sure about the steps to conect bluetooth the Nicla and the cloud could you help me?