Si trabajas frente a un computador, sabes cómo pasa: te sientas a las nueve, levantas la vista y ya es la una, con la espalda tiesa y sin haberte movido de la silla. Las pausas activas existen justamente para eso, pero nadie se acuerda de hacerlas. Este tutorial parte de un sensor que puede recordártelo sin que tengas que ponerte nada encima: una cámara con inteligencia artificial que reconoce el cuerpo y las manos, y que entrega coordenadas en vez de video.

El sensor es el DFRobot Gravity AI Vision Posture and Gesture Sensor (SEN0670), conectado a una Seeed Studio XIAO ESP32 S3 por I2C. Al final vas a saber leer los 17 puntos del esqueleto y los 21 de la mano, entrenar gestos propios, controlar la música del computador con la mano y entender cómo se arma un monitor de actividad que te avisa cuando llevas demasiado rato quieto.

Por qué un sensor de pose y no una cámara común

Con una cámara normal, el microcontrolador recibe imágenes y tiene que hacer él mismo el reconocimiento, algo que un ESP32 apenas puede hacer y con modelos muy recortados. La SEN0670 invierte el trabajo: trae un procesador de IA Himax HX6538 que corre los modelos de reconocimiento dentro del módulo y le entrega al ESP32 solo el resultado, unos pocos números por persona. Eso tiene tres ventajas concretas:

  • Privacidad. Las imágenes nunca salen del sensor. Para algo que vive en tu escritorio mirándote todo el día, es lo que hace la diferencia entre algo razonable y algo incómodo.
  • Sin red. Todo es local (edge AI): no depende de un servicio en la nube ni de WiFi.
  • Un microcontrolador modesto alcanza. El ESP32 solo lee coordenadas por I2C y decide qué hacer con ellas.

Frente y reverso del sensor Gravity AI Vision Posture and Gesture SEN0670

Qué ve y qué entrega

La cámara es VGA (640 × 480) con 89° de campo de visión diagonal. Funciona mejor a unos 0,5 m para manos y a unos 2 m para cuerpo completo. El modo mano sigue una sola mano; el modo postura puede seguir a varias personas.

Según el modelo que elijas con la librería DFRobot_HumanPose, recibes cosas distintas:

  • Modelo de pose: 17 puntos del cuerpo (nariz, ojos, orejas, hombros, codos, muñecas, caderas, rodillas y tobillos), cada uno con x e y en píxeles de la imagen. Además, un ID de seguimiento, un puntaje de confianza, una etiqueta y un rectángulo (izquierda, arriba, ancho y alto) que encierra a la persona.
  • Modelo de mano: 21 puntos (la muñeca más cuatro articulaciones por dedo, de la base a la punta) con la misma estructura.
  • Gestos aprendidos: un ID, un nombre y un puntaje para gestos que tú entrenaste. Caben hasta 8 y se pueden borrar y reentrenar cuando quieras.

El origen de coordenadas está arriba a la izquierda de la imagen: x crece hacia la derecha y y crece hacia abajo. Recuérdalo cuando compares alturas: una muñeca "más arriba" que el hombro tiene una y menor, no mayor.

Especificaciones que importan

Característica Valor
Procesador HX6538, IA local
Cámara 640 × 480 (VGA), 89° diagonal
Distancia para gestos cerca de 0,5 m
Distancia para postura cerca de 2 m
Salida de pose 17 puntos del cuerpo
Salida de mano 21 puntos
Gestos aprendidos hasta 8
Alimentación 3,3 a 5,5 V
Nivel lógico 3,3 V
Interfaces I2C o UART
Dirección I2C 0x3A

El módulo trae además un puerto USB-C (para el software de configuración de DFRobot, para programarlo o para alimentarlo) y un pin WAKEUP para dormirlo cuando no se usa. Un interruptor pequeño elige entre I2C y UART: déjalo en I2C, que usa solo dos líneas de datos y permite compartir el bus con una pantalla.

La luz manda. El puntaje de confianza baja con contraluz fuerte (una ventana detrás de ti), sombras marcadas o movimiento rápido. Si lo vas a dejar fijo en el escritorio, ponlo de modo que la luz principal te ilumine de frente o de costado, nunca desde atrás.

Conexiones

El sensor tiene cuatro pines en un header de 2,54 mm: VCC, GND, C/R y D/T. En modo I2C, C/R es SCL y D/T es SDA.

Pin de la SEN0670 Pin de la XIAO ESP32 S3
VCC 3V3
GND GND
C/R (SCL) GPIO6 (D5)
D/T (SDA) GPIO5 (D4)

La XIAO trabaja a 3,3 V, igual que la lógica del sensor. Aunque el sensor acepta hasta 5,5 V, alimentarlo con 3,3 V mantiene alimentación y señales al mismo nivel. La XIAO se alimenta por USB-C y le pasa energía al sensor por VCC.

Sensor conectado a la XIAO ESP32-S3

Para el proyecto con pantalla se agrega una OLED de 128 × 64 en el mismo bus, en paralelo con el sensor (VCC, GND, SCL y SDA). No hay choque porque el sensor está en 0x3A y la OLED en 0x3C.

Sensor y pantalla OLED conectados a la XIAO ESP32-S3

Preparar el IDE de Arduino

  1. En Archivo > Preferencias, agrega esta URL al gestor de placas: https://raw.githubusercontent.com/espressif/arduino-esp32/gh-pages/package_esp32_index.json.

Campo de URLs adicionales del gestor de placas en las preferencias del IDE de Arduino

  1. En Herramientas > Placa > Gestor de placas, busca "esp32" de Espressif Systems e instálalo. Es un paquete grande: dale un minuto.
  2. Conecta la XIAO, elige la placa XIAO_ESP32S3 (identificador completo esp32:esp32:XIAO_ESP32S3) y el puerto en Herramientas > Puerto.
  3. En el gestor de librerías instala DFRobot_HumanPose. Para el proyecto con pantalla, agrega Adafruit GFX Library y Adafruit SSD1306.

Leer el esqueleto: los 17 puntos del cuerpo

Este primer sketch pone el sensor en modo pose e imprime, por cada persona detectada, su rectángulo y sus 17 puntos con nombre.

C++
#include <DFRobot_HumanPose.h>

const uint8_t I2C_SDA_PIN = 5;
const uint8_t I2C_SCL_PIN = 6;

static const uint8_t SENSOR_I2C_ADDRESS = 0x3A;
DFRobot_HumanPose_I2C humanPose(&Wire, SENSOR_I2C_ADDRESS);

struct NamedPoint {
  const char *name;
  const PointU16 *point;
};

static void printPoint(const NamedPoint &keypoint) {
  Serial.printf("    %-10s: (%u, %u)\n",
                keypoint.name,
                keypoint.point->x,
                keypoint.point->y);
}

static void printPoseKeypoints(const PoseResult *pose) {
  const NamedPoint keypoints[] = {
    {"nose", &pose->nose},
    {"leye", &pose->leye},
    {"reye", &pose->reye},
    {"lear", &pose->lear},
    {"rear", &pose->rear},
    {"lshoulder", &pose->lshoulder},
    {"rshoulder", &pose->rshoulder},
    {"lelbow", &pose->lelbow},
    {"relbow", &pose->relbow},
    {"lwrist", &pose->lwrist},
    {"rwrist", &pose->rwrist},
    {"lhip", &pose->lhip},
    {"rhip", &pose->rhip},
    {"lknee", &pose->lknee},
    {"rknee", &pose->rknee},
    {"lankle", &pose->lankle},
    {"rankle", &pose->rankle}
  };

  for (const NamedPoint &keypoint : keypoints) {
    printPoint(keypoint);
  }
}

void setup() {
  Serial.begin(115200);
  Wire.begin(I2C_SDA_PIN, I2C_SCL_PIN);

  while (!humanPose.begin()) {
    Serial.printf("Sensor init failed; retrying...\n");
    delay(1000);
  }

  humanPose.setModelType(DFRobot_HumanPose::ePose);
}

void loop() {
  if (humanPose.getResult() != DFRobot_HumanPose::eOK) {
    return;
  }

  while (humanPose.availableResult()) {
    Result *result = humanPose.popResult();
    if (!result) {
      continue;
    }

    PoseResult *pose = static_cast<PoseResult *>(result);

    Serial.printf("Body: id=%u, score=%u, name=%s\n",
                  pose->id,
                  pose->score,
                  pose->name.c_str());
    Serial.printf("  Bounding box: left=%u, top=%u, width=%u, height=%u\n",
                  pose->xLeft,
                  pose->yTop,
                  pose->width,
                  pose->height);
    printPoseKeypoints(pose);
    Serial.printf("\n");
  }

  delay(100);
}

Cómo leerlo

  • NamedPoint junta un nombre legible con un puntero al punto del resultado. Así printPoseKeypoints recorre un arreglo en vez de repetir 17 printf.
  • setup() arranca el serial y el I2C en GPIO5 y GPIO6, reintenta humanPose.begin() hasta que el sensor responde y elige el modelo ePose.
  • loop() pregunta con getResult() si hay un cuadro nuevo; si no, sale sin imprimir nada. Si hay, vacía la cola con availableResult() y popResult(), convierte cada resultado a PoseResult e imprime sus datos.

En el Monitor Serie a 115200 verás bloques como este por cada persona:

Text
Body: id=0, score=85, name=unknown
  Bounding box: left=105, top=2, width=135, height=239
    nose      : (203, 43)
    leye      : (214, 36)
    reye      : (195, 32)
    lear      : (217, 43)
    rear      : (170, 39)
    lshoulder : (232, 105)
    rshoulder : (144, 102)
    lelbow    : (239, 175)
    relbow    : (140, 175)
    lwrist    : (232, 219)
    rwrist    : (170, 219)
    lhip      : (214, 226)
    rhip      : (151, 226)
    lknee     : (206, 263)
    rknee     : (144, 244)
    lankle    : (210, 274)
    rankle    : (170, 244)

El puntaje va de 0 a 100: más cerca de 100, más seguro está el modelo. Un punto que el sensor no logra ver (una rodilla tapada por el escritorio, por ejemplo) suele venir como (0, 0); tenlo en cuenta antes de usarlo en un cálculo.

Leer la mano: 21 puntos

Cambiando el modelo a eHand recibes la mano completa: muñeca y, por cada dedo, la articulación de la base (MCP), las dos intermedias (PIP y DIP) y la punta. El pulgar usa CMC, MCP, IP y punta.

C++
#include <DFRobot_HumanPose.h>

const uint8_t I2C_SDA_PIN = 5;
const uint8_t I2C_SCL_PIN = 6;

static const uint8_t SENSOR_I2C_ADDRESS = 0x3A;
DFRobot_HumanPose_I2C humanPose(&Wire, SENSOR_I2C_ADDRESS);

struct NamedPoint {
  const char *name;
  const PointU16 *point;
};

static void printPoint(const NamedPoint &keypoint) {
  Serial.printf("    %-16s: (%u, %u)\n",
                keypoint.name,
                keypoint.point->x,
                keypoint.point->y);
}

static void printHandKeypoints(const HandResult *hand) {
  const NamedPoint keypoints[] = {
      {"wrist", &hand->wrist},
      {"thumbCmc", &hand->thumbCmc},
      {"thumbMcp", &hand->thumbMcp},
      {"thumbIp", &hand->thumbIp},
      {"thumbTip", &hand->thumbTip},
      {"indexFingerMcp", &hand->indexFingerMcp},
      {"indexFingerPip", &hand->indexFingerPip},
      {"indexFingerDip", &hand->indexFingerDip},
      {"indexFingerTip", &hand->indexFingerTip},
      {"middleFingerMcp", &hand->middleFingerMcp},
      {"middleFingerPip", &hand->middleFingerPip},
      {"middleFingerDip", &hand->middleFingerDip},
      {"middleFingerTip", &hand->middleFingerTip},
      {"ringFingerMcp", &hand->ringFingerMcp},
      {"ringFingerPip", &hand->ringFingerPip},
      {"ringFingerDip", &hand->ringFingerDip},
      {"ringFingerTip", &hand->ringFingerTip},
      {"pinkyFingerMcp", &hand->pinkyFingerMcp},
      {"pinkyFingerPip", &hand->pinkyFingerPip},
      {"pinkyFingerDip", &hand->pinkyFingerDip},
      {"pinkyFingerTip", &hand->pinkyFingerTip}};

  for (const NamedPoint &keypoint : keypoints) {
    printPoint(keypoint);
  }
}

void setup() {
  Serial.begin(115200);
  Wire.begin(I2C_SDA_PIN, I2C_SCL_PIN);

  while (!humanPose.begin()) {
    Serial.printf("Sensor init failed; retrying...\n");
    delay(1000);
  }

  humanPose.setModelType(DFRobot_HumanPose::eHand);
}

void loop() {
  if (humanPose.getResult() != DFRobot_HumanPose::eOK) {
    return;
  }

  while (humanPose.availableResult()) {
    Result *result = humanPose.popResult();
    if (!result) {
      continue;
    }

    HandResult *hand = static_cast<HandResult *>(result);

    Serial.printf("Hand: id=%u, score=%u, name=%s\n",
                  hand->id,
                  hand->score,
                  hand->name.c_str());
    Serial.printf("  Bounding box: left=%u, top=%u, width=%u, height=%u\n",
                  hand->xLeft,
                  hand->yTop,
                  hand->width,
                  hand->height);
    printHandKeypoints(hand);
    Serial.printf("\n");
  }

  delay(100);
}

La estructura es la misma que la del cuerpo, con HandResult en vez de PoseResult. Con los 21 puntos puedes calcular cosas tú mismo: por ejemplo, un dedo está estirado si su punta queda más lejos de la muñeca que su articulación PIP. Contando dedos estirados ya tienes un contador del 0 al 5 sin entrenar nada.

Enseñarle gestos propios

Para que el sensor reconozca un gesto con nombre, se entrena una vez desde el computador con el software de configuración de DFRobot, que viene en un ZIP con el programa "SEN0670-AI Posture and Gesture Recognition Sensor.exe" (solo para Windows).

Software de configuración del sensor SEN0670

El proceso:

  1. Conecta el sensor por USB-C y elige su puerto COM en el menú Port del programa.
  2. Elige el modo "Gesture".
  3. Haz el gesto frente a la cámara (por ejemplo, pulgar arriba) y aprieta "Learn". Se agrega a la lista, y ahí puedes ponerle nombre, cambiarlo o borrarlo.

Lista de gestos aprendidos en el software de configuración

En el tutorial original se entrenaron cuatro: start (mano abierta), stop (puño), vol_inc (pulgar arriba) y vol_dec (pulgar abajo). Los nombres quedan guardados en el sensor.

Hay también un modo de gestos fijos con 13 gestos predefinidos que no requieren entrenamiento. La documentación de DFRobot advierte que es nuevo y puede reconocer con baja precisión, así que para algo que vas a usar todos los días conviene entrenar los tuyos.

Gestos predefinidos del modo de gestos fijos

Para entrenar bien: buena luz, fondo despejado, la mano a unos 0,5 m y cada gesto sostenido cerca de un segundo. Elige gestos que se distingan mucho entre sí: dos gestos parecidos (un dedo arriba y dos dedos arriba) se confunden más que un puño y una mano abierta. Si el puntaje sale bajo, reentrena o mejora la luz.

Este sketch imprime los gestos reconocidos:

C++
#include <DFRobot_HumanPose.h>

const uint8_t I2C_SDA_PIN = 5;
const uint8_t I2C_SCL_PIN = 6;

static const uint8_t SENSOR_I2C_ADDRESS = 0x3A;
DFRobot_HumanPose_I2C humanPose(&Wire, SENSOR_I2C_ADDRESS);

void setup() {
  Serial.begin(115200);
  Wire.begin(I2C_SDA_PIN, I2C_SCL_PIN);

  while (!humanPose.begin()) {
    Serial.printf("Sensor init failed; retrying...\n");
    delay(1000);
  }

  humanPose.setModelType(DFRobot_HumanPose::eHand);
}

void loop() {
  if (humanPose.getResult() != DFRobot_HumanPose::eOK) {
    return;
  }

  while (humanPose.availableResult()) {
    Result *result = humanPose.popResult();
    if (!result || !result->id) {
      continue;
    }

    Serial.printf("Gesture: ID=%u, name=%s, score=%u\n",
                  result->id,
                  result->name.c_str(),
                  result->score);
  }

  delay(100);
}

El modelo sigue siendo eHand: con él llegan también los gestos aprendidos. Un ID 0 significa "mano sin gesto reconocido", por eso se descarta. Usa result->name en vez de una tabla propia de nombres: así tu código queda sincronizado con lo que configuraste en el software y no repites la asignación en dos lugares. La salida se ve así:

Text
Gesture: ID=1, name=start, score=85
Gesture: ID=2, name=stop, score=87
Gesture: ID=4, name=vol_dec, score=86
Gesture: ID=3, name=vol_inc, score=88

Proyecto: controlar la música con la mano

La XIAO ESP32 S3 tiene USB nativo y puede presentarse al computador como un teclado multimedia (un dispositivo HID de "consumer control"). Eso permite que los cuatro gestos entrenados manejen la música sin ningún otro hardware: mano abierta para reproducir o pausar, puño para detener y pulgar arriba o abajo para el volumen.

C++
#include "USB.h"
#include "USBHIDConsumerControl.h"
#include <DFRobot_HumanPose.h>

const uint8_t I2C_SDA_PIN = 5;
const uint8_t I2C_SCL_PIN = 6;

static const uint8_t SENSOR_I2C_ADDRESS = 0x3A;
DFRobot_HumanPose_I2C humanPose(&Wire, SENSOR_I2C_ADDRESS);
USBHIDConsumerControl consumerControl;

// Send a gesture only once during this cooldown period.
static const uint32_t GESTURE_COOLDOWN_MS = 1500;
uint8_t lastGestureId = 0;
uint32_t lastGestureTime = 0;

static bool sendMediaCommand(uint8_t id) {
  uint16_t mediaKey;

  switch (id) {
  case 1:
    // Start: play/pause the current Windows media player.
    mediaKey = CONSUMER_CONTROL_PLAY_PAUSE;
    break;
  case 2:
    // Stop: stop the current Windows media player.
    mediaKey = CONSUMER_CONTROL_STOP;
    break;
  case 3:
    // Increase volume.
    mediaKey = CONSUMER_CONTROL_VOLUME_INCREMENT;
    break;
  case 4:
    // Decrease volume.
    mediaKey = CONSUMER_CONTROL_VOLUME_DECREMENT;
    break;
  default:
    return false;
  }

  consumerControl.press(mediaKey);
  consumerControl.release();
  return true;
}

static void handleGesture(const Result *result) {
  if (!result || result->id == 0) {
    return;
  }

  const uint32_t now = millis();
  const bool cooldownExpired = (now - lastGestureTime) >= GESTURE_COOLDOWN_MS;
  const bool newGesture = result->id != lastGestureId;

  if (!newGesture && !cooldownExpired) {
    return;
  }

  Serial.printf("Gesture: ID=%u, name=%s, score=%u\n",
                result->id,
                result->name.c_str(),
                result->score);

  if (sendMediaCommand(result->id)) {
    Serial.printf("Windows media command sent for ID %u\n", result->id);
  } else {
    Serial.printf("No media command mapped for ID %u\n", result->id);
  }

  lastGestureId = result->id;
  lastGestureTime = now;
}

void setup() {
  Serial.begin(115200);
  Wire.begin(I2C_SDA_PIN, I2C_SCL_PIN);

  USB.productName("Gesture Media Controller");
  USB.manufacturerName("Makerguides");
  USB.begin();
  consumerControl.begin();

  while (!humanPose.begin()) {
    delay(1000);
  }

  humanPose.setModelType(DFRobot_HumanPose::eHand);
}

void loop() {
  if (humanPose.getResult() != DFRobot_HumanPose::eOK) {
    return;
  }

  while (humanPose.availableResult()) {
    Result *result = humanPose.popResult();
    handleGesture(result);
  }

  delay(100);
}

Lo que hay que entender de este código

  • sendMediaCommand() traduce los IDs 1 a 4 a las teclas multimedia estándar (reproducir/pausar, detener, subir y bajar volumen). Son las mismas que mandan las teclas de función de un teclado, así que funcionan con cualquier reproductor que las respete. El original lo probó en Windows; macOS y la mayoría de los escritorios Linux también reconocen estas teclas.
  • El antirrebote. El sensor reporta el gesto en cada cuadro, varias veces por segundo. Sin filtro, sostener el pulgar arriba un segundo subiría el volumen diez veces. handleGesture() solo actúa si el gesto es distinto del anterior o si ya pasaron 1500 ms desde el último. Un gesto nuevo se acepta de inmediato; uno repetido espera. Si quieres que el volumen suba más rápido al sostener el gesto, baja GESTURE_COOLDOWN_MS.
  • USB nativo. USB.productName() y USB.manufacturerName() fijan el nombre con que aparece el dispositivo; USB.begin() y consumerControl.begin() lo arrancan. Para que funcione, en el menú Herramientas el modo USB tiene que ser el nativo (USB OTG / TinyUSB), no el puerto serie JTAG. Si el computador no reconoce el dispositivo, revisa eso primero.

Proyecto: el monitor de pausas activas

Este es el proyecto que le da sentido al sensor en un escritorio. La idea es simple: medir cuánto se mueve tu cuerpo y encender un LED cuando llevas demasiado tiempo casi inmóvil. El LED se apaga cuando te levantas o te mueves de verdad.

Cómo medir "movimiento" con puntos

Con el modelo de pose activo, en cada lectura tienes los 17 puntos de tu cuerpo. El movimiento se puede estimar comparando la posición de cada punto con la de la lectura anterior y sumando las distancias. Algunas decisiones que hacen que funcione bien:

  • Usa puntos estables. Hombros, codos y muñecas dicen mucho; los ojos y las orejas varían con cada gesto mínimo de la cara y meten ruido.
  • Ignora puntos perdidos. Un punto que pasa a (0, 0) porque quedó tapado no es movimiento; si lo cuentas, un brazo detrás del monitor parecerá un salto de cientos de píxeles.
  • Promedia en una ventana. Teclear mueve las muñecas todo el tiempo, pero no es una pausa activa. Por eso conviene promediar la actividad durante varios segundos y fijar un umbral por encima del movimiento normal de trabajo.
  • Ausencia no es inactividad. Si el sensor deja de ver a la persona, lo más probable es que te paraste. Eso debería reiniciar el contador, no sumar minutos.

La lógica completa

  1. Cada lectura, calcula la actividad (suma de desplazamientos de los puntos válidos) y acumúlala en una media móvil.
  2. Si la media queda bajo el umbral, suma ese tiempo a un contador de "quieto".
  3. Si el contador pasa del límite (por ejemplo, 45 o 50 minutos), enciende el LED.
  4. Si la actividad sube claramente sobre el umbral durante unos segundos, o si no hay persona en cuadro, reinicia el contador y apaga el LED.

El sketch completo de este proyecto está en el tutorial original (enlace en Recursos). Para ajustarlo a tu escritorio, lo que más vas a tocar es el umbral de actividad (depende de la distancia a la cámara: a 2 m los desplazamientos en píxeles son menores que a 1 m) y el tiempo límite.

Por qué un LED y no un pitido. Un aviso visual suave se tolera todo el día; uno sonoro lo vas a desconectar al segundo día. Si quieres algo más insistente, combínalo: LED primero y, si en cinco minutos no te moviste, un buzzer corto.

Errores comunes

  • "Sensor init failed; retrying..." sin parar. Revisa el interruptor I2C/UART, que SDA y SCL no estén cruzados y que el sensor reciba 3,3 V.
  • Puntajes bajos. Casi siempre es la luz: contraluz, poca iluminación o la cámara muy cerca o muy lejos. Para manos, 0,5 m; para cuerpo, 2 m.
  • El gesto se dispara muchas veces. Falta antirrebote o el tiempo de espera es muy corto.
  • El computador no reconoce el control multimedia. El modo USB de la placa no es el nativo, o el cable es solo de carga.
  • La pantalla no enciende. Comprueba que esté en 0x3C y comparta el bus en paralelo con el sensor.

Variantes y mejoras

  • El esqueleto en una pantalla. El original incluye un tercer proyecto que dibuja tu figura de palitos en una OLED de 128 × 64 colocada en vertical. El truco es calcular el rectángulo que encierra los puntos válidos y escalarlo para que la figura llene la pantalla sin deformarse, y después unir los puntos con líneas: hombros con codos, codos con muñecas, caderas con rodillas. Las librerías Adafruit GFX y SSD1306 hacen el dibujo.
  • Detector de mala postura. Con los puntos de nariz y hombros puedes estimar si te estás encorvando: cuando la nariz baja demasiado respecto de la línea de los hombros (o se adelanta mucho si pones el sensor de costado), mandas un aviso. Calibra la posición "derecha" al encender, porque cada persona y cada silla son distintas.
  • Registro en el celular. La XIAO tiene WiFi: manda por MQTT o a una planilla cuántas pausas hiciste por día y cuántos minutos seguidos pasaste sentado. Ver la curva de una semana convence más que cualquier recordatorio.
  • Ahorro de energía. Usa el pin WAKEUP para dormir el sensor fuera del horario de trabajo y despertarlo al llegar.

Personalización para Chile

En Chile consigues en MechatronicStore lo necesario para el montaje:

  • Seeed Studio XIAO ESP32 S3 (SKU GS1-3): la misma placa del original, con USB nativo para el control multimedia y WiFi para la variante con registro.
  • Cable USB Tipo C a USB Tipo A 1mt (SKU B-101): para programar la XIAO y, en el proyecto multimedia, para que el computador la vea como teclado. Tiene que transmitir datos.
  • Pantalla Oled 128x64 I2C 0.96" SSD1315 (SKU D-111): para el proyecto del esqueleto en pantalla. El SSD1315 es compatible con la librería Adafruit SSD1306 y usa la dirección 0x3C.
  • Breadboard / Protoboard 830 puntos MB102 (SKU C-302): para montar sensor y pantalla en paralelo sobre el mismo bus.
  • Cables macho macho 40 piezas 20cm (SKU C-411): para el cableado en la protoboard.

El sensor DFRobot SEN0670 no está en el catálogo. La alternativa más cercana es la Grove Vision AI V2 HX6538 ARM (SKU GV2-1), que usa el mismo procesador de IA HX6538. No es intercambiable directo: trae otro firmware y se programa con los modelos de SenseCraft AI en vez de la librería DFRobot_HumanPose, así que los sketches de arriba no corren tal cual, pero la idea de leer resultados por I2C y decidir en la XIAO es la misma.

Recursos

Versión chilena basada en el tutorial de Makerguides, con componentes en stock local.