#include "hardware/i2c.h"
#include "hardware/gpio.h"
#include "hardware/timer.h"
#include "tensorflow/lite/micro/micro_interpreter.h"
#include "tensorflow/lite/micro/micro_mutable_op_resolver.h"
#include "tensorflow/lite/schema/schema_generated.h"
#include "gesture_model.h"
/* I2C and sensor config */
#define MPU6050_ADDR 0x68
#define SAMPLE_RATE_HZ 100
#define INPUT_SIZE (WINDOW_SIZE * NUM_AXES)
/* LED pins (adjust for your wiring) */
#define LED_CIRCLE_PIN 19
/* Normalization constants (from training script) */
static const float NORM_MEANS[] = {0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f};
static const float NORM_STDS[] = {1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f};
/* Replace with actual values from your training output */
static const char *GESTURE_LABELS[] = {"shake", "tilt", "tap", "circle"};
static const uint LED_PINS[] = {
LED_SHAKE_PIN, LED_TILT_PIN, LED_TAP_PIN, LED_CIRCLE_PIN
#define TENSOR_ARENA_SIZE (24 * 1024)
static uint8_t tensor_arena[TENSOR_ARENA_SIZE] __attribute__((aligned(16)));
static float imu_window[WINDOW_SIZE][NUM_AXES];
/* ---- MPU6050 functions ---- */
static void mpu6050_init(void)
buf[0] = 0x6B; buf[1] = 0x00;
i2c_write_blocking(I2C_PORT, MPU6050_ADDR, buf, 2, false);
buf[0] = 0x1C; buf[1] = 0x00;
i2c_write_blocking(I2C_PORT, MPU6050_ADDR, buf, 2, false);
buf[0] = 0x1B; buf[1] = 0x00;
i2c_write_blocking(I2C_PORT, MPU6050_ADDR, buf, 2, false);
buf[0] = 0x19; buf[1] = 0x09;
i2c_write_blocking(I2C_PORT, MPU6050_ADDR, buf, 2, false);
static void mpu6050_read(float *ax, float *ay, float *az,
float *gx, float *gy, float *gz)
i2c_write_blocking(I2C_PORT, MPU6050_ADDR, ®, 1, true);
i2c_read_blocking(I2C_PORT, MPU6050_ADDR, raw, 14, false);
int16_t raw_ax = (int16_t)((raw[0] << 8) | raw[1]);
int16_t raw_ay = (int16_t)((raw[2] << 8) | raw[3]);
int16_t raw_az = (int16_t)((raw[4] << 8) | raw[5]);
int16_t raw_gx = (int16_t)((raw[8] << 8) | raw[9]);
int16_t raw_gy = (int16_t)((raw[10] << 8) | raw[11]);
int16_t raw_gz = (int16_t)((raw[12] << 8) | raw[13]);
/* ---- Capture and normalize ---- */
static void capture_window(void)
uint32_t interval_us = 1000000 / SAMPLE_RATE_HZ;
for (int i = 0; i < WINDOW_SIZE; i++) {
uint64_t start = time_us_64();
mpu6050_read(&imu_window[i][0], &imu_window[i][1],
&imu_window[i][2], &imu_window[i][3],
&imu_window[i][4], &imu_window[i][5]);
/* Wait for next sample period */
while ((time_us_64() - start) < interval_us) {
static void normalize_window(float *flat_output)
for (int i = 0; i < WINDOW_SIZE; i++) {
for (int j = 0; j < NUM_AXES; j++) {
flat_output[i * NUM_AXES + j] =
(imu_window[i][j] - NORM_MEANS[j]) / NORM_STDS[j];
i2c_init(I2C_PORT, 400 * 1000);
gpio_set_function(I2C_SDA_PIN, GPIO_FUNC_I2C);
gpio_set_function(I2C_SCL_PIN, GPIO_FUNC_I2C);
gpio_pull_up(I2C_SDA_PIN);
gpio_pull_up(I2C_SCL_PIN);
/* Initialize LED pins */
for (int i = 0; i < NUM_CLASSES; i++) {
gpio_set_dir(LED_PINS[i], GPIO_OUT);
gpio_put(LED_PINS[i], 0);
/* Initialize TFLite Micro */
const tflite::Model *model = tflite::GetModel(gesture_model_tflite);
static tflite::MicroMutableOpResolver<3> resolver;
resolver.AddFullyConnected();
static tflite::MicroInterpreter interpreter(
model, resolver, tensor_arena, TENSOR_ARENA_SIZE);
interpreter.AllocateTensors();
TfLiteTensor *input = interpreter.input(0);
TfLiteTensor *output = interpreter.output(0);
printf("Gesture classifier ready. Arena used: %zu bytes\n",
interpreter.arena_used_bytes());
float normalized[INPUT_SIZE];
/* Capture 1-second window */
normalize_window(normalized);
float input_scale = input->params.scale;
int input_zp = input->params.zero_point;
int8_t *input_data = input->data.int8;
for (int i = 0; i < INPUT_SIZE; i++) {
int32_t q = (int32_t)(normalized[i] / input_scale) + input_zp;
input_data[i] = (int8_t)q;
uint64_t t_start = time_us_64();
uint64_t t_end = time_us_64();
float output_scale = output->params.scale;
int output_zp = output->params.zero_point;
int8_t *output_data = output->data.int8;
float best_score = -100.0f;
for (int i = 0; i < NUM_CLASSES; i++) {
float score = (output_data[i] - output_zp) * output_scale;
if (score > best_score) {
printf("Gesture: %s (%.2f), Inference: %llu us\n",
GESTURE_LABELS[best], best_score,
(unsigned long long)(t_end - t_start));
/* LED feedback: light the corresponding LED for 500 ms */
gpio_put(LED_PINS[best], 1);
gpio_put(LED_PINS[best], 0);
Comments