Maintenance: Project Hub will be unavailable on Wednesday 25 (9AM to 6PM CET) while we deploy critical improvements
Smelling Fresh, Feeling Fresh!
Use an Arduino Nicla Sense ME to see if you need to freshen up after a workout
Components and supplies
1
Nicla Sense ME
Apps and platforms
1
Edge Impulse Studio
Project description
Code
Smelly Coat
cpp
Signals a Red light or Green light if you smell
1/* Edge Impulse ingestion SDK 2 * Copyright (c) 2022 EdgeImpulse Inc. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * http://www.apache.org/licenses/LICENSE-2.0 8 * 9 * Unless required by applicable law or agreed to in writing, software 10 * distributed under the License is distributed on an "AS IS" BASIS, 11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 * See the License for the specific language governing permissions and 13 * limitations under the License. 14 * 15 */ 16 17/* Includes ---------------------------------------------------------------- */ 18#include <smelly-coat_inferencing.h> 19 #include "Nicla_System.h" 20#include "Arduino_BHY2.h" //Click here to get the library: http://librarymanager/All#Arduino_BHY2 21 22/** Struct to link sensor axis name to sensor value function */ 23typedef struct{ 24 const char *name; 25 float (*get_value)(void); 26 27}eiSensors; 28 29 30/** Number sensor axes used */ 31#define NICLA_N_SENSORS 1 32 33 34/* Private variables ------------------------------------------------------- */ 35static const bool debug_nn = false; // Set this to true to see e.g. features generated from the raw signal 36 37 38Sensor gas(SENSOR_ID_GAS); 39 40static bool ei_connect_fusion_list(const char *input_list); 41static float get_gas(void){return gas.value();} 42 43static int8_t fusion_sensors[NICLA_N_SENSORS]; 44static int fusion_ix = 0; 45 46/** Used sensors value function connected to label name */ 47eiSensors nicla_sensors[] = 48{ 49 "gas", &get_gas, 50}; 51 52/** 53* @brief Arduino setup function 54*/ 55void setup() 56{ 57 /* Init serial */ 58 Serial.begin(115200); 59 // comment out the below line to cancel the wait for USB connection (needed for native USB) 60 //while (!Serial); 61 Serial.println("Edge Impulse Sensor Fusion Inference\r\n"); 62 63 /* Connect used sensors */ 64 if(ei_connect_fusion_list(EI_CLASSIFIER_FUSION_AXES_STRING) == false) { 65 ei_printf("ERR: Errors in sensor list detected\r\n"); 66 return; 67 } 68 69 /* Init & start sensors */ 70 BHY2.begin(NICLA_I2C); 71 gas.begin(); 72 73 nicla::begin(); 74 nicla::enableCharge(100); 75 nicla::leds.begin(); 76 nicla::leds.setColor(yellow); 77} 78 79/** 80* @brief Get data and run inferencing 81*/ 82void loop() 83{ 84 ei_printf("\nStarting inferencing in 2 seconds...\r\n"); 85 86 delay(2000); 87 88 if (EI_CLASSIFIER_RAW_SAMPLES_PER_FRAME != fusion_ix) { 89 ei_printf("ERR: Nicla sensors don't match the sensors required in the model\r\n" 90 "Following sensors are required: %s\r\n", EI_CLASSIFIER_FUSION_AXES_STRING); 91 return; 92 } 93 94 ei_printf("Sampling...\r\n"); 95 96 // Allocate a buffer here for the values we'll read from the IMU 97 float buffer[EI_CLASSIFIER_DSP_INPUT_FRAME_SIZE] = { 0 }; 98 99 for (size_t ix = 0; ix < EI_CLASSIFIER_DSP_INPUT_FRAME_SIZE; ix += EI_CLASSIFIER_RAW_SAMPLES_PER_FRAME) { 100 // Determine the next tick (and then sleep later) 101 int64_t next_tick = (int64_t)micros() + ((int64_t)EI_CLASSIFIER_INTERVAL_MS * 1000); 102 103 // Update function should be continuously polled 104 BHY2.update(); 105 106 for(int i = 0; i < fusion_ix; i++) { 107 buffer[ix + i] = nicla_sensors[fusion_sensors[i]].get_value(); 108 ei_printf("Gas Sensor: %.2f\n",buffer[ix + i]); 109 } 110 111 int64_t wait_time = next_tick - (int64_t)micros(); 112 113 if(wait_time > 0) { 114 delayMicroseconds(wait_time); 115 } 116 } 117 118 // Turn the raw buffer in a signal which we can the classify 119 signal_t signal; 120 int err = numpy::signal_from_buffer(buffer, EI_CLASSIFIER_DSP_INPUT_FRAME_SIZE, &signal); 121 if (err != 0) { 122 ei_printf("ERR:(%d)\r\n", err); 123 return; 124 } 125 126 // Run the classifier 127 ei_impulse_result_t result = { 0 }; 128 129 err = run_classifier(&signal, &result, debug_nn); 130 if (err != EI_IMPULSE_OK) { 131 ei_printf("ERR:(%d)\r\n", err); 132 return; 133 } 134 135 // print the predictions 136 BHY2.update(); 137 float current = gas.value(); 138 ei_printf("Gas Sensor: %.2f\n",current); 139 ei_printf("Predictions (DSP: %d ms., Classification: %d ms., Anomaly: %d ms.):\r\n", 140 result.timing.dsp, result.timing.classification, result.timing.anomaly); 141 for (size_t ix = 0; ix < EI_CLASSIFIER_LABEL_COUNT; ix++) { 142 ei_printf("%s: %.5f\r\n", result.classification[ix].label, result.classification[ix].value); 143 } 144 if (result.classification[0].value > 0.80) { 145 nicla::leds.setColor(green); 146 } else if (result.classification[1].value > 0.80) { 147 nicla::leds.setColor(red); 148 } else { 149 nicla::leds.setColor(yellow); 150 } 151#if EI_CLASSIFIER_HAS_ANOMALY == 1 152 ei_printf(" anomaly score: %.3f\r\n", result.anomaly); 153#endif 154} 155 156#if !defined(EI_CLASSIFIER_SENSOR) || (EI_CLASSIFIER_SENSOR != EI_CLASSIFIER_SENSOR_FUSION && EI_CLASSIFIER_SENSOR != EI_CLASSIFIER_SENSOR_ACCELEROMETER) 157#error "Invalid model for current sensor" 158#endif 159 160 161/** 162 * @brief Go through nicla sensor list to find matching axis name 163 * 164 * @param axis_name 165 * @return int8_t index in nicla sensor list, -1 if axis name is not found 166 */ 167static int8_t ei_find_axis(char *axis_name) 168{ 169 int ix; 170 for(ix = 0; ix < NICLA_N_SENSORS; ix++) { 171 if(strstr(axis_name, nicla_sensors[ix].name)) { 172 return ix; 173 } 174 } 175 return -1; 176} 177 178/** 179 * @brief Check if requested input list is valid sensor fusion, create sensor buffer 180 * 181 * @param[in] input_list Axes list to sample (ie. "accX + gyrY + magZ") 182 * @retval false if invalid sensor_list 183 */ 184static bool ei_connect_fusion_list(const char *input_list) 185{ 186 char *buff; 187 bool is_fusion = false; 188 189 /* Copy const string in heap mem */ 190 char *input_string = (char *)ei_malloc(strlen(input_list) + 1); 191 if (input_string == NULL) { 192 return false; 193 } 194 memset(input_string, 0, strlen(input_list) + 1); 195 strncpy(input_string, input_list, strlen(input_list)); 196 197 /* Clear fusion sensor list */ 198 memset(fusion_sensors, 0, NICLA_N_SENSORS); 199 fusion_ix = 0; 200 201 buff = strtok(input_string, "+"); 202 203 while (buff != NULL) { /* Run through buffer */ 204 int8_t found_axis = 0; 205 206 is_fusion = false; 207 found_axis = ei_find_axis(buff); 208 209 if(found_axis >= 0) { 210 if(fusion_ix < NICLA_N_SENSORS) { 211 fusion_sensors[fusion_ix++] = found_axis; 212 } 213 is_fusion = true; 214 } 215 216 buff = strtok(NULL, "+ "); 217 } 218 219 ei_free(input_string); 220 221 return is_fusion; 222}
Comments
Only logged in users can leave comments