Skip to content

Latest commit

 

History

History
181 lines (143 loc) · 6.06 KB

File metadata and controls

181 lines (143 loc) · 6.06 KB

ESP32-C3 Non-Invasive Glucose Monitor Prototype

PlatformIO CI

⚠️ DISCLAIMER: This is an experimental prototype for educational/research purposes ONLY. It is NOT a medical device, NOT clinically validated, and should NEVER be used for actual health decisions. Glucose estimation accuracy is unverified and likely poor without proper calibration. Always use certified glucometers.

Overview

This project implements a non-invasive blood glucose monitor using:

  • ESP32-C3 microcontroller (WeAct Studio devkit recommended)
  • AS7263 6-channel spectral sensor (NIR/Visible: R=610nm, S=680nm, T=730nm, U=810nm, V=860nm, W=940nm)
  • Dual OLED displays (built-in 72x40 + optional external 128x64)
  • Custom algorithm: NIR-dominant spectral ratio scaled to mg/dL

How it works:

  1. Standby → Detects finger via intensity/ratio/stability checks.
  2. Takes 5 spectral measurements (bulb-stabilized).
  3. Computes glucose from NIR/vis ratio using weighted sum + linear scaling.
  4. Displays result + status for 10s.

Hardware Requirements

Component Details
MCU ESP32-C3-DevKitM-1 (WeAct)
Sensor AS726x (SparkFun breakout, I2C addr 0x49)
Wires SDA=GPIO8, SCL=GPIO9
Display Built-in SSD1306 72x40 (auto) + optional external 128x64 (GPIO3/4)
Bulb Sensor's built-in LED (enabled during reads)

Pinout (main.cpp):

I2C: SDA=8, SCL=9
OLED Builtin: SCL=6, SDA=5
OLED Ext: SCL=3, SDA=4
LED: BUILTIN

Installation & Build

  1. Install PlatformIO (VSCode extension or CLI).
  2. Clone/Open this project.
  3. Build/Upload:
    pio run -t upload -e esp32-c3-mini
    pio device monitor  # Serial @115200
    
  4. Libraries auto-installed: U8g2, SparkFun AS726X.

Usage

  1. Flash & open Serial Monitor (115200 baud).
  2. Power on → I2C scan → Standby screen.
  3. Place CLEAN finger flat on sensor (steady pressure, ~5-10s).
  4. LED blinks → "Measuring..." → Result (e.g., "102 OK").
  5. Wait 10s or re-place finger for new measurement.

🔧 Calibration Guide

This prototype uses FIXED parameters. Accurate glucose requires USER calibration!

Step 1: Sensor Tuning (Runtime via Serial)

Watch [FINGER] diagnostics. Adjust if false positives/negatives:

[FINGER] Total=XXXX  NIR/Vis=X.XX  Stability=XXX
[FINGER] Pass: intensity=Y ratio=Y stability=Y => FINGER DETECTED/no finger

Tune in src/SensorManager.h & reflash:

// Finger detection thresholds
static constexpr float FINGER_MIN_SIGNAL = 500.0f;     // Too low → ambient false pos
static constexpr float FINGER_MAX_SIGNAL = 50000.0f;   // Too high → no finger/sat
static constexpr float FINGER_MAX_NIR_RATIO = 0.25f;   // Finger: low ratio (~0.07)
static constexpr float FINGER_MAX_STABILITY = 500.0f;  // Steady finger vs noise
  • Ambient light: Raise MIN_SIGNAL.
  • Saturation: Lower MAX_SIGNAL or integrationTime.
  • Shaky finger fails: Raise MAX_STABILITY.
  • No detect: Lower MAX_NIR_RATIO.

Sensor config (main.cpp):

sensorConfig.gain = 3;           // 0-16, higher=amplification
sensorConfig.integrationTime = 100;  // 1-256ms, longer=more signal
sensorConfig.bulbStabilizeMs = 50;   // LED settle time

Example Serial (good finger):

[FINGER] Total=12450.2  NIR/Vis=0.07  Stability=120.5
[FINGER] Pass: intensity=Y ratio=Y stability=Y => FINGER DETECTED

Step 2: Algorithm Calibration (Empirical Dataset)

Goal: Fit GLUCOSE_SCALE/OFFSET to your sensor + users.

In src/GlucoseAlgorithm.h:

static constexpr float GLUCOSE_SCALE = 100.0f;  // Slope: pixels -> mg/dL
static constexpr float GLUCOSE_OFFSET = 0.0f;   // Intercept
// Weights (NIR heavy for glucose correlation)
static constexpr float WEIGHT_V = 0.30f;  // 860nm
static constexpr float WEIGHT_W = 0.30f;  // 940nm

Calibration Process:

  1. Collect Dataset (N=50+ readings):

    Protocol Device Spectral (Serial [MEAS]) Reference (lab glucometer)
    Fasting (morning) Note R,S,T,U,V,W Measure same finger immediately
    Post-meal Wait 2h after eating etc.
    Variety: skin tones, hydration, times of day

    Log format ([MEAS] Serial):

    [MEAS] Sample 1: R=1200 S=1100 T=950 U=800 V=6500 W=6800 total=17150
    
  2. Compute Index: For each reading:

    nir = WEIGHT_V*V + WEIGHT_W*W
    vis = WEIGHT_R*R + ... + WEIGHT_U*U
    index = nir / vis
    
  3. Fit Linear Model (Excel/Python):

    glucose_ref = SCALE * index + OFFSET
    
    • Use least-squares regression.
    • Target: MARD < 15% (clinical threshold).
  4. Validate: New dataset, compute % error vs reference.

Example Python Fit (save CSV, run):

import pandas as pd
from sklearn.linear_model import LinearRegression
df = pd.read_csv('calibration_data.csv')  # cols: index, glucose_ref
model = LinearRegression().fit(df[['index']], df['glucose_ref'])
print(f'SCALE={model.coef_[0]:.1f}, OFFSET={model.intercept_:.1f}')

Pro Tip: Calibrate per-user/sensor (skin, optics vary).

Serial Output Reference

[STATE] -> FINGER_DETECTED
[FINGER] Total=12450 NIR/Vis=0.07 Stability=120 => DETECTED
[MEAS] Sample 1: R=1200 ... W=6800 total=17150
New reading: 102.3 mg/dL
[STATE] Measurement done: level=102.1 valid=YES status=OK

Troubleshooting

Issue Fix
No sensor Check I2C scan (0x49?), wiring
No finger detect Clean sensor, steady pressure, tune thresholds
Crazy values Check saturation (65k?), bulbStabilizeMs
Build fail pio lib install
OLED blank Check pins 5/6 (builtin)

Limitations & Next Steps

  • Accuracy: Prototype; NIR correlation weak without ML/multivariate cal.
  • No trend: Add history buffer.
  • Battery: Add LiPo charging.
  • Bluetooth: App sync.
  • Clinical: Impossible without FDA path, but fun hack!

Contribute: PRs welcome (calibration data, UI, ML models).


Built with ❤️ using PlatformIO & SparkFun AS726x