Wio Terminal + Edge Impulse: Distinción de Bebidas con Sensor de Gas Multicanal

En este proyecto aprenderás a utilizar el Wio Terminal junto con Edge Impulse para implementar un modelo de Machine Learning que distingue entre distintos tipos de bebidas usando el sensor Grove - Multichannel Gas Sensor v2.
Inspirado en el proyecto "Artificial nose" de Benjamin Cabé, este ejemplo muestra el flujo de trabajo completo desde la recopilación de datos hasta el despliegue en el dispositivo.
Hardware necesario
- Wio Terminal
- Grove - Multichannel Gas Sensor v2
- Muestras de bebidas: alcohol, café, refresco (cola)
Paso 1: Cargar firmware Edge Impulse
- Conecta el Wio Terminal a tu computadora.
- Entra en modo "bootloader" deslizando el interruptor de encendido dos veces rápido.
- Se montará una unidad externa llamada
Arduino
. - Arrastra el archivo
edge_impulse_firmware.uf2
a la unidad.
Puedes compilar tu propio firmware desde el repositorio de Edge Impulse para Wio Terminal.
Paso 2: Conexión con Edge Impulse Studio
- Abre tu proyecto en Edge Impulse y ve a Data acquisition.
- Haz clic en
Connect using WebUSB
, selecciona el puerto serie y acepta la conexión. - Verifica que aparezca "connected" en la parte superior.
Paso 3: Recolección de datos
- Conecta el sensor de gas Grove v2 al puerto Grove I2C del Wio Terminal.

-
Coloca el sensor sobre una bebida y cúbrelo para evitar interferencias ambientales.
-
En el dashboard de Edge Impulse:
- En la pestaña Data acquisition, elige el dispositivo.
- Define el Label (ej: "cola", "cafe", "alcohol", "aire").
- Sample length: 10000 ms
- Sensor:
External multichannel gas
- Frequency:
10Hz
-
Presiona Start Sampling y repite el proceso varias veces por cada etiqueta.
Recolecta datos consistentes. Evita ruido ambiental o ventilación cercana.
Paso 4: Diseñar el Impulse
-
Ve a Impulse Design > Create Impulse:
- Processing block:
Raw data
- Learning block:
Neural Network (Keras)
- Processing block:
-
Guarda y genera features desde la pestaña Raw data.
Paso 5: Entrenar el modelo
-
En Impulse Design > NN Classifier, ajusta:
- Número de épocas, tasa de aprendizaje, tamaño de batch, arquitectura de la red, etc.
-
Presiona Start Training.
-
Verifica la tabla de performance (accuracy y confusion matrix).
Paso 6: Clasificación en vivo
- Ve a Live classification.
- Coloca el sensor sobre una nueva muestra.
- Observa las predicciones del modelo entrenado.
Paso 7: Despliegue al dispositivo
- Ve a la pestaña Deployment y selecciona
Arduino Library
. - Descarga y extrae el archivo en tu carpeta de bibliotecas de Arduino.
- Abre el ejemplo:
File -> Examples -> <tu proyecto> -> static_buffer
Modifica la función raw_feature_get_data()
así:
int raw_feature_get_data(size_t offset, size_t length, float *out_ptr) {
float features[4];
features[0] = gas.getGM102B();
features[1] = gas.getGM302B();
features[2] = gas.getGM502B();
features[3] = gas.getGM702B();
memcpy(out_ptr, features + offset, length * sizeof(float));
return 0;
}
Código completo
#include <coffee_cola_alcohol_big_inferencing.h>
#include <Multichannel_Gas_GMXXX.h>
#include <Wire.h>
GAS_GMXXX<TwoWire> gas;
int raw_feature_get_data(size_t offset, size_t length, float *out_ptr) {
float features[4];
features[0]=gas.getGM102B();
features[1] = gas.getGM302B();
features[2]=gas.getGM502B();
features[3]=gas.getGM702B();
memcpy(out_ptr, features + offset, length * sizeof(float));
return 0;
}
void setup() {
Serial.begin(115200);
gas.begin(Wire, 0x08); // I2C
Serial.println("Edge Impulse Inferencing Demo");
}
void loop() {
ei_printf("Edge Impulse standalone inferencing (Arduino)\n");
ei_impulse_result_t result = { 0 };
signal_t features_signal;
features_signal.total_length = sizeof(features) / sizeof(features[0]);
features_signal.get_data = &raw_feature_get_data;
EI_IMPULSE_ERROR res = run_classifier(&features_signal, &result, false);
ei_printf("run_classifier returned: %d\n", res);
if (res != 0) return;
ei_printf("Predictions (DSP: %d ms., Classification: %d ms., Anomaly: %d ms.):\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\n", result.classification[ix].label, result.classification[ix].value);
}
#if EI_CLASSIFIER_HAS_ANOMALY == 1
ei_printf(" anomaly score: %.3f\n", result.anomaly);
#endif
delay(1000);
}
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);
}
}