Misurazione della saturazione di ossigeno e della frequenza cardiaca con l’ESP8266 e il sensore MAX30101: guida completa

Introduzione

Nell’ambito del controllo continuo della nostra salute, la misurazione della saturazione di ossigeno e della frequenza cardiaca sono sicuramente due parametri di primaria importanza. Nel contesto dell’evoluzione tecnologica, l’integrazione di dispositivi indossabili per il monitoraggio della salute sta trasformando la nostra capacità di prenderci cura di noi stessi in modo più avanzato che mai. In questo scenario, il sensore MAX30101 emerge come un punto di riferimento per la misurazione simultanea della saturazione di ossigeno (SpO2) e dei battiti cardiaci. Questo piccolo, ma potente, dispositivo offre non solo un’opportunità straordinaria per il monitoraggio in tempo reale, ma è anche adatto a comunicare a distanza tramite un dispositivo come l’ESP8266 grazie alla tecnologia IoT (Internet of Things).

Questo articolo è una guida completa all’uso del sensore MAX30101, che ti insegnerà come ottenere misurazioni di SpO2 e frequenza cardiaca. Ma c’è di più: scoprirai come visualizzare questi dati in tempo reale attraverso un display TFT e connetterli all’ecosistema di Arduino Cloud per la visualizzazione remota. Questo approccio apre nuove prospettive per il monitoraggio della salute, consentendo un accesso istantaneo ai tuoi dati ovunque ti trovi.

Sia che tu sia uno sperimentatore tech-savvy o un individuo desideroso di controllare la propria salute, imparerai come sfruttare appieno il potenziale del sensore MAX30101 per il monitoraggio continuo e la condivisione dei dati in remoto. Esploreremo le fasi di installazione, configurazione e trasmissione dei dati e ti guideremo nella comprensione di come questo dispositivo stia cambiando il modo in cui affrontiamo il monitoraggio della salute.

Anche in questo progetto utilizzeremo l’ottimo PlatformIO come IDE.

Cosa è e come funziona il sensore MAX30101

Il MAX30101 è un sensore di rilevamento ottico che è stato progettato principalmente per misurare la frequenza cardiaca e la saturazione di ossigeno (SpO2). È ampiamente utilizzato in applicazioni di monitoraggio della salute, dispositivi indossabili, elettronica medica e progetti di fai-da-te per il monitoraggio delle condizioni fisiche. Esso comunica tramite il bus I2C. Ecco una spiegazione dettagliata di cosa è e come funziona il MAX30101:

Descrizione del sensore:

  • Il MAX30101 è un modulo che combina una sorgente di luce LED rossa, una sorgente di luce LED infrarossa (IR) e un fotodiodo per il rilevamento delle onde di ritorno di queste luci.
  • Utilizza una tecnica di riflessione ottica, dove le luci LED emettono radiazioni nella pelle e il fotodiodo rileva le variazioni di assorbimento di queste luci a causa del flusso sanguigno.
  • Il sensore è in grado di misurare sia la luce riflessa dalla pelle (usata per rilevare i battiti cardiaci) che la luce assorbita (usata per calcolare la SpO2).

Funzionamento del Sensore:

  1. Emissione di luce LED: il sensore emette luce LED rossa e infrarossa nella pelle del soggetto. Questa luce penetra nelle cellule del sangue.
  2. Assorbimento della luce: le cellule del sangue contenenti emoglobina assorbono queste lunghezze d’onda di luce in modo diverso a seconda del fatto che siano ossigenate o meno. Le onde di ritorno della luce LED vengono rilevate dal fotodiodo.
  3. Elaborazione dei dati: il segnale rilevato viene quindi elaborato da un microcontroller (come ESP8266) utilizzando algoritmi sofisticati. I picchi nel segnale riflettente vengono identificati per calcolare la frequenza cardiaca.
  4. Calcolo della SpO2: per calcolare la SpO2, vengono effettuate misurazioni di ambedue le luci LED. La differenza tra l’assorbimento della luce rossa e infrarossa viene utilizzata per determinare la saturazione di ossigeno.
  5. Visualizzazione dei dati: i risultati vengono quindi visualizzati (per esempio su un display oppure trasmessi a un’applicazione mobile o caricati su un servizio cloud per il monitoraggio a distanza).

Applicazioni Comuni:

  • Monitoraggio della frequenza cardiaca in tempo reale durante l’attività fisica o il sonno.
  • Misurazione della SpO2 in condizioni di riposo o durante attività fisiche.
  • Utilizzo in dispositivi medici per monitorare i pazienti.
  • Monitoraggio della salute in dispositivi indossabili come smartwatch e braccialetti fitness.
  • Progetti di fai-da-te per il monitoraggio della salute.

Il MAX30101 è un sensore estremamente versatile e offre la possibilità di ottenere misurazioni mediche importanti in un formato compatto. La sua applicabilità in una vasta gamma di dispositivi lo rende un componente chiave nella tecnologia per il monitoraggio della salute.

Avviso Importante

È fondamentale sottolineare che il progetto che stai per intraprendere, basato sull’utilizzo del sensore MAX30101 per la misurazione della frequenza cardiaca e della saturazione di ossigeno (SpO2), è stato realizzato principalmente a scopo didattico e di sperimentazione. I risultati ottenuti tramite questo progetto potrebbero non essere paragonabili o sostituire in alcun modo le misurazioni mediche ufficiali.

L’uso del sensore MAX30101 e di dispositivi correlati può offrire una visione approssimativa della tua frequenza cardiaca e dei livelli di ossigeno, ma è importante comprendere che questi dati non dovrebbero mai sostituire le consultazioni mediche professionali o le attrezzature mediche certificate!!!

Qualsiasi progetto di monitoraggio della salute dovrebbe essere affrontato con cautela e responsabilità. Assicurati sempre di consultare un professionista medico per valutare adeguatamente il tuo stato di salute e affidarti a dispositivi medici certificati per misurazioni precise e affidabili.

Ricorda che questo progetto è pensato per scopi educativi e di esplorazione tecnologica. È un’opportunità per imparare e sperimentare con nuove tecnologie, ma non dovrebbe mai sostituire la consulenza medica qualificata. La tua salute è una priorità, e l’uso di dispositivi di monitoraggio deve essere affiancato da una supervisione medica competente.

Il bus I2C

Il bus I2C (Inter-Integrated Circuit) è il bus tramite il quale il sensore MAX30101 comunica con l’ESP8266. Esso è un popolare protocollo di comunicazione seriale utilizzato per collegare vari dispositivi e sensori a microcontroller, microprocessori e altri dispositivi embedded. Ecco alcuni punti chiave da considerare:

  1. Comunicazione a due fili: il bus I2C utilizza solo due fili per la comunicazione: uno per la trasmissione dei dati (SDA, Serial Data) e uno per la sincronizzazione del clock (SCL, Serial Clock). Questa semplicità lo rende ideale per collegare più dispositivi su un unico bus.
  2. Master e Slave: nella comunicazione I2C, c’è un dispositivo principale chiamato “master” e uno o più dispositivi secondari chiamati “slave”. Il master inizia e controlla la comunicazione, mentre gli slave rispondono alle richieste del master.
  3. Indirizzamento: ogni dispositivo slave ha un indirizzo univoco sul bus I2C. Questo consente al master di selezionare il dispositivo con cui desidera comunicare. Non possono coesistere più dispositivi con lo stesso indirizzo sullo stesso bus.
  4. Half-Duplex: il bus I2C supporta la comunicazione half-duplex, il che significa che i dati possono fluire solo in una direzione alla volta. Tuttavia, è possibile invertire la direzione della comunicazione durante la trasmissione.
  5. Velocità variabile: il bus I2C supporta una varietà di velocità di comunicazione, consentendo di adattare la velocità in base alle esigenze specifiche dell’applicazione.
  6. Ampiamente utilizzato: il bus I2C è ampiamente utilizzato in una vasta gamma di dispositivi, tra cui sensori, dispositivi di memoria, schermi, microcontroller e molti altri. È una scelta comune nei dispositivi IoT e nei progetti embedded.
  7. Pull-Up resistori: per garantire una comunicazione affidabile, il bus I2C richiede l’uso di resistori di pull-up sugli sbocchi SDA e SCL. Questi resistori contribuiscono a stabilizzare il segnale e prevengono il rumore.
  8. Protocollo di alto livello: il bus I2C è un protocollo di alto livello che semplifica la comunicazione tra i dispositivi, consentendo agli sviluppatori di concentrarsi sulla logica dell’applicazione piuttosto che sulla gestione della comunicazione.

In sintesi, il bus I2C è un protocollo di comunicazione affidabile e flessibile che offre un modo conveniente per collegare dispositivi e sensori a un microcontroller o un microprocessore. La sua ampiezza di utilizzo e la sua facilità di implementazione lo rendono una scelta popolare nell’ambito dell’elettronica embedded e dell’IoT.

I display TFT

I display TFT (Thin-Film Transistor) rappresentano una categoria di schermi a cristalli liquidi (LCD) avanzati che si sono affermati come una delle tecnologie di visualizzazione più diffuse e versatili. Ecco alcuni punti chiave da considerare:

Qualità delle immagini: i display TFT sono noti per offrire immagini di alta qualità con colori brillanti e dettagli nitidi. Questo li rende ideali per una vasta gamma di applicazioni, dalla visualizzazione di foto e video all’interfaccia utente delle applicazioni.

Retroilluminazione: la maggior parte dei display TFT utilizza retroilluminazione a LED, che fornisce una fonte luminosa uniforme e consente di visualizzare le immagini anche in ambienti luminosi. Questa retroilluminazione è regolabile in base alle esigenze, il che contribuisce a risparmiare energia.

Risoluzione: i display TFT sono disponibili in varie risoluzioni, dalle piccole schermate da 1.8 pollici con risoluzione QVGA alle schermate più grandi con risoluzioni Full HD o superiori. La scelta della risoluzione dipende dalle esigenze specifiche dell’applicazione.

Touchscreen: molti schermi TFT sono dotati di tecnologia touchscreen capacitivo o resistivo, che consente l’interazione diretta dell’utente con il display. Questo è ampiamente utilizzato in dispositivi mobili, tablet, ATM e altri dispositivi interattivi.

Ampia applicabilità: i display TFT sono utilizzati in una vasta gamma di dispositivi, tra cui smartphone, tablet, monitor, televisori, orologi intelligenti, strumenti di diagnostica medica, GPS, dispositivi di automazione industriale e molto altro.

Connessioni e controlli: i display TFT sono progettati per l’integrazione con vari tipi di controller e microcontroller. Ciò li rende ideali per l’implementazione in progetti di elettronica fai da te e applicazioni personalizzate.

Consumo energetico: la tecnologia TFT è progettata per mantenere un basso consumo energetico quando possibile, il che la rende adatta per dispositivi alimentati a batteria.

Scelta del tipo: oltre ai display TFT a colori, esistono anche display TFT in scala di grigi, monocromatici e ad alta luminosità progettati per applicazioni specifiche.

In conclusione, i display TFT sono una scelta popolare e affidabile per molte applicazioni, grazie alla loro qualità dell’immagine, versatilità e facilità di integrazione. Sono diventati parte integrante della nostra vita quotidiana, contribuendo all’esperienza visiva in dispositivi di ogni genere.

Arduino Cloud

Arduino Cloud è una piattaforma basata su cloud sviluppata da Arduino che offre una soluzione completa per la gestione e la connettività dei dispositivi basati su Arduino, come Arduino MKR, ESP8266 e ESP32. Questa piattaforma è progettata per semplificare la creazione, la configurazione e il monitoraggio di dispositivi connessi, consentendo agli sviluppatori di concentrarsi sullo sviluppo delle proprie applicazioni senza doversi preoccupare dell’infrastruttura sottostante.

Una delle caratteristiche chiave di Arduino Cloud è la sua facilità d’uso. Gli sviluppatori possono registrare i propri dispositivi Arduino nella piattaforma e configurarli rapidamente attraverso un’interfaccia utente intuitiva. Una volta che i dispositivi sono online, è possibile monitorarli in tempo reale, raccogliere dati, avviare azioni remote e aggiornare il firmware in modo centralizzato.

Arduino Cloud fornisce anche funzionalità avanzate per la sicurezza e l’autenticazione dei dispositivi, garantendo che le comunicazioni tra i dispositivi e il cloud siano protette e private. Inoltre, offre un’ampia gamma di servizi e integrazioni che consentono agli sviluppatori di collegare i propri dispositivi Arduino a servizi di terze parti, come AWS, Google Cloud, IFTTT e molti altri.

La piattaforma è particolarmente adatta per progetti di IoT (Internet of Things) in cui è necessario monitorare, controllare e gestire dispositivi remoti in modo efficiente. Con Arduino Cloud, gli sviluppatori possono accelerare lo sviluppo di applicazioni IoT e concentrarsi sull’innovazione piuttosto che sulla complessità dell’infrastruttura.

Di che componenti abbiamo bisogno?

La lista dei componenti non è particolarmente lunga:

Vediamo nella foto seguente il tipo di display TFT utilizzato in questo progetto:

Il display TFT da 1.8"
Il display TFT da 1.8″

Realizzazione del progetto

Lo schema elettrico

Prima di realizzare il circuito vero e proprio diamo un’occhiata al pinout della board:

Pinout del NodeMCU ESP8266
Pinout del NodeMCU ESP8266

Vediamo il pinout del sensore MAX30101:

Pinout del sensore MAX30101
Pinout del sensore MAX30101

Il sensore MAX30101 potrebbe non avere il connettore saldato quindi, se avessi necessità di saldarlo, ti consiglio di dare prima un’occhiata all’articolo Un altro tutorial su come saldare.

Vediamo anche il pinout del display:

Pinout del display TFT da 1.8"
Pinout del display TFT da 1.8″

Per il nostro progetto prenderemo in considerazione solo i pin sul lato sinistro (con connettore giallo su questa foto).

In genere questo display viene venduto già col connettore montato ma se il tuo non lo dovesse avere e avessi necessità di saldarlo, ti consiglio di dare prima un’occhiata all’articolo Un altro tutorial su come saldare per imparare a fare una perfetta saldatura.

Prima di passare allo schema elettrico dovrai effettuare una piccola operazione. Il display può funzionare sia a 5V che a 3.3V ma siccome i pin digitali dell’ESP8266 non tollerano tensioni più alte di 3.3V dovremo predisporre il display per funzionare con tale tensione.

Per fare ciò dovremo semplicemente cortocircuitare il jumper J1 cioè quello mostrato nella foto seguente:

Jumper J1 da cortocircuitare per far funzionare il display TFT a 3.3V
Jumper J1 da cortocircuitare per far funzionare il display TFT a 3.3V

Come vedi è un’operazione abbastanza semplice: è sufficiente una piccola goccia di stagno per mettere in cortocircuito le due piazzole indicate in rosso.

Vediamo ora lo schema elettrico del progetto, realizzato come al solito con Fritzing:

Lo schema elettrico del sispositivo per la misurazione della saturazione di ossigeno e della frequenza cardiaca
Lo schema elettrico del sispositivo per la misurazione della saturazione di ossigeno e della frequenza cardiaca

Per maggiore comodità riporterò di seguito la tabella dei collegamenti tra display e ESP8266:

Display TFTESP8266
1D4
2D3
3D0
4D7
5D5
63V3
73V3
8GND
Collegamenti del display TFT

Creiamo gli oggetti e la dashboard sul cloud

Per prima cosa, se non lo hai già fatto, devi crearti un account su Arduino Cloud.

Una volta che hai fatto il login ti troverai di fronte a questa pagina:

Pagina iniziale di Arduino Cloud
Pagina iniziale di Arduino Cloud

Clicca sul pulsante GET STARTED e verrai mandato alla pagina:

Seconda pagina di Arduino Cloud
Seconda pagina di Arduino Cloud

Clicca sul pulsante IoT Cloud. Si aprirà la pagina Things.

A questo punto procederai a:

  • creare il device che mapperà la board
  • creare l’oggetto (thing) con le variabili associate al device creato al passo precedente
  • creare la dashboard da associare alle variabili

Vedremo la procedura appena elencata con questo video:

In questo video abbiamo creato:

  • l’oggetto (thing) chiamato health_thing con le sue variabili heartRateCloud e SPO2Cloud (sia come Name che come Declaration). Entrambe di tipo intero, read only e con periodicità di 1 secondo;
  • il device di nome healt_device e di tipo ESP8266 NodeMCU 1.0 (ESP-12E Module)
  • la dashboard di nome healt_dashboard con due diagrammi di tipo chart, uno di nome Heart Rate che visualizza la variabile heartRateCloud e l’altro di nome SPO2 che visualizza la variabile SPO2Cloud (entrambe le variabili sono appartenenti all’health_thing).

NOTA BENE: è importante notare che nel video, al minuto3:18, ho scaricato un pdf che contiene i parametri di collegamento al device. Essi sono chiamati Device ID e Secret Key e ci serviranno in seguito. Quindi scarica anche tu questo pdf e conservalo da qualche parte.

Ovviamente i parametri Device ID e Secret Key sono propri di ciascun device, quindi cambiano al variare del device.

Lo sketch

Creiamo il progetto PlatformIO

Abbiamo già visto la procedura di creazione di un progetto PlatformIO nell’articolo Come creare un progetto per NodeMCU ESP8266 con PlatformIO.

Non installare le librerie indicate in quell’articolo ma installa , sempre seguendo la solita procedura, la libreria Arduino_ConnectionHandler:

Installazione della libreria Arduino_ConnectionHandler
Installazione della libreria Arduino_ConnectionHandler

e la libreria ArduinoIoTCloud:

Installazione della libreria ArduinoIoTCloud
Installazione della libreria ArduinoIoTCloud

Installa poi la libreria Adafruit GFX Library by Adafruit:

Installa la libreria Adafruit GFX Library
Installa la libreria Adafruit GFX Library

e la libreria Adafruit ST7735 and ST7789 Library by Adafruit:

Installa la libreria Adafruit ST7735 and ST7789 Library
Installa la libreria Adafruit ST7735 and ST7789 Library

Infine devi installare la libreria SparkFun MAX3010x Pulse and Proximity Sensor Library by SparkFun Electronics:

Installa la libreria SparkFun MAX3010x Pulse and Proximity Sensor Library by SparkFun Electronics
Installa la libreria SparkFun MAX3010x Pulse and Proximity Sensor Library by SparkFun Electronics

Ora modifica il file platformio.ini per aggiungere queste due righe:

monitor_speed = 115200
upload_speed = 921600

in modo che abbia un aspetto del genere:

[env:nodemcuv2]
platform = espressif8266
board = nodemcuv2
framework = arduino
monitor_speed = 115200
upload_speed = 921600
lib_deps = sparkfun/SparkFun MAX3010x Pulse and Proximity Sensor Library@^1.1.2
           adafruit/Adafruit GFX Library@^1.11.9
	       adafruit/Adafruit ST7735 and ST7789 Library@^1.10.3
	       arduino-libraries/Arduino_ConnectionHandler@^0.7.6
	       arduino-libraries/ArduinoIoTCloud@^1.12.0

Ora scarica il progetto dal link qui sotto:

Decomprimi il progetto appena scaricato e sovrascrivi il file main.cpp nella cartella src del progetto creato prima.

Poi, dalla cartella include del progetto appena scaricato e decompresso, prendi il file thingProperties.h e copialo nella cartella include del progetto creato prima.

A questo punto dovrai editare questo file in modo da collegarlo al cloud:

const char DEVICE_LOGIN_NAME[]  = "YYYYYYYYYYYYYYYYYYYYYYYYYYYY";

const char SSID[]               = SECRET_SSID;    // Network SSID (name)
const char PASS[]               = SECRET_OPTIONAL_PASS;    // Network password (use for WPA, or use as key for WEP)
const char DEVICE_KEY[]  = SECRET_DEVICE_KEY;    // Secret device password

Come vedi, in questa sezione ci sono alcuni parametri da impostare.

Il primo parametro (DEVICE_LOGIN_NAME) lo trovi nel pdf che hai scaricato quando hai creato l’oggetto (trovi il riferimento nel video precedente). Nel pdf il parametro è chiamato Device ID.

I parametri SECRET_SSID e SECRET_OPTIONAL_PASS sono, rispettivamente, il nome e la password della tua rete WiFi. Quindi, al posto di SECRET_SSID metterai il nome della tua rete tra doppi apici (“) e al posto di SECRET_OPTIONAL_PASS metterai la password della tua rete (sempre fra doppi apici).

L’ultimo parametro, SECRET_DEVICE_KEY, lo trovi sempre nel pdf che hai scaricato dalla piattaforma col nome Secret Key.

Quindi la sezione in oggetto dovrebbe avere un aspetto del genere:

const char DEVICE_LOGIN_NAME[]  = "Device ID from the downloaded pdf";
const char SSID[]               = "my_wifi_ssid";    // Network SSID (name)
const char PASS[]               = "my_wifi_password";    // Network password (use for WPA, or use as key for WEP)
const char DEVICE_KEY[]  = "Secret Key from the downloaded pdf";    // Secret device password

Come puoi vedere, ho messo nel campo DEVICE_LOGIN_NAME il valore preso dal pdf alla voce Device ID , nel campo SSID l’SSID della mia rete WiFi (in questo caso my_wifi_ssid), nel campo PASS la password della mia rete WiFi (in questo caso my_wifi_password) e nel campo DEVICE_KEY il valore preso dal pdf alla voce Secret Key.

Questo sketch è principalmente ispirato ad un esempio presente nella libreria Sparkfun. Ad esso ho aggiunto la parte di gestione del display TFT e quella di collegamento con Arduino Cloud.

Vediamolo più in dettaglio.

Inizialmente vengono inclusi il file thingProperties.h, la libreria Wire necessaria per la comunicazione I2C del sensore, la libreria di gestione del sensore (anche se il sensore si chiama MAX30101 viene gestito dalla libreria MAX30105), il file spo2_algorithm che contiene l’algoritmo di calcolo della SPO2 e le librerie Adafruit per la gestione del display :

#include "thingProperties.h"

#include <Wire.h>
#include "MAX30105.h"
#include "spo2_algorithm.h"

#include <Adafruit_GFX.h>      // include Adafruit graphics library
#include <Adafruit_ST7735.h>   // include Adafruit ST7735 TFT library

Di seguito vengono definiti i collegamenti col display, l’oggetto tft che gestisce il display, l’oggetto particleSensor che gestisce il sensore MAX30101 e una serie di buffer e variabili utilizzati dallo sketch:

// ST7735 TFT module connections
#define TFT_RST   D4     // TFT RST pin is connected to NodeMCU pin D4 (GPIO2)
#define TFT_CS    D3     // TFT CS  pin is connected to NodeMCU pin D3 (GPIO0)
#define TFT_DC    D0     // TFT DC  pin is connected to NodeMCU pin D0 (GPIO16)

Adafruit_ST7735 tft = Adafruit_ST7735(TFT_CS, TFT_DC, TFT_RST);

MAX30105 particleSensor;

#define MAX_BRIGHTNESS 255

#if defined(__AVR_ATmega328P__) || defined(__AVR_ATmega168__)
//Arduino Uno doesn't have enough SRAM to store 100 samples of IR led data and red led data in 32-bit format
//To solve this problem, 16-bit MSB of the sampled data will be truncated. Samples become 16-bit data.
uint16_t irBuffer[100]; //infrared LED sensor data
uint16_t redBuffer[100];  //red LED sensor data
#else
uint32_t irBuffer[100]; //infrared LED sensor data
uint32_t redBuffer[100];  //red LED sensor data
#endif

int32_t bufferLength; //data length
int8_t validSPO2; //indicator to show if the SPO2 calculation is valid
int8_t validHeartRate; //indicator to show if the heart rate calculation is valid

String heartRateToShow = "";
String SPO2ToShow = "";

Vengono poi definite le variabili che impostano il tempo di lettura dei valori di interesse (battito cardiaco e ossigeno nel sangue) per il display e per Arduino Cloud (facciamo una lettura al secondo):

unsigned long lastTimeRanCycle;
unsigned long measureDelayCycle = 1000;  // 1s

Di seguito troviamo la funzione setup.

Inizialmente viene inizializzata la porta seriale:

Serial.begin(115200); // initialize serial communication at 115200 bits per second:
delay(2000);
Serial.println("Serial port ready!");

Viene poi definita la parte di inizializzazione del collegmento con Arduino Cloud:

  // Defined in thingProperties.h
  initProperties();

  // Connect to Arduino IoT Cloud
  ArduinoCloud.begin(ArduinoIoTPreferredConnection);
  
  /*
     The following function allows you to obtain more information
     related to the state of network and IoT Cloud connection and errors
     the higher number the more granular information you’ll get.
     The default is 0 (only errors).
     Maximum is 4
 */
  setDebugMessageLevel(2);
  ArduinoCloud.printDebugInfo();

e viene inizializzato il display:

  tft.initR(INITR_BLACKTAB);
  tft.fillScreen(ST7735_BLACK);

Segue l’inizializzazione del nostro sensore:

  // Initialize sensor
  if (!particleSensor.begin(Wire, I2C_SPEED_FAST)) //Use default I2C port, 400kHz speed
  {
    Serial.println(F("MAX30101 was not found. Please check wiring/power."));
    while (1);
  }

Adesso viene una parte molto importante. La parte dedicata ai parametri dell’algoritmo:

byte ledBrightness = 60; //Options: 0=Off to 255=50mA
byte sampleAverage = 1; //Options: 1, 2, 4, 8, 16, 32
byte ledMode = 2; //Options: 1 = Red only, 2 = Red + IR, 3 = Red + IR + Green
byte sampleRate = 400; //Options: 50, 100, 200, 400, 800, 1000, 1600, 3200
int pulseWidth = 411; //Options: 69, 118, 215, 411
int adcRange = 16384; //Options: 2048, 4096, 8192, 16384

particleSensor.setup(ledBrightness, sampleAverage, ledMode, sampleRate, pulseWidth, adcRange); //Configure sensor with these settings

I valori ottimali per ciascuno di questi parametri variano per ogni diversa applicazione del sensore. Sparkfun dà dei valori che io ho provato ad adattare al mio caso per far funzionare meglio l’algoritmo. Puoi iniziare a ottimizzarli comprendendo meglio il loro significato:

  • adcRange: il sensore trasmette luce rossa e infrarossa (IR) e misura la luce riflessa utilizzando un ADC. Questo parametro imposta la risoluzione dell’ ADC. Prova ad iniziare con 2048 durante la calibrazione e spostati più in alto a seconda della necessità. È possibile visualizzare i valori IR grezzi e luce ROSSA (nello sketch le stampe sul Serial Monitor sono commentate per comodità, per vedere tali valori le devi decommentare. Sono sotto il nome PRINTS BLOCK). I segnali non dovrebbero essere saturati.
  • ledBrightness determina l’intensità della luce rossa e IR. Puoi abbassare la luminosità per risparmiare energia nelle applicazioni a batteria (ma avrai un pessimo valore di SNR). Oppure puoi aumentare la luminosità per ottenere un buon SNR.
  • pulseWidth imposta il tempo di attivazione della luce ROSSA e IR. Sono preferibili larghezze di impulso inferiori con una frequenza di campionamento bassa, ma il rumore sarà elevato.
  • sampleAverage imposta la frequenza di campionamento per l’ADC interno. Dovrai modificarlo per tentativi in modo da ottenere letture il più possibile stabili dall’ADC
  • ledMode MAX30101 supporta il Rosso, Rosso e IR, Rosso + IR + Verde
  • sampleRate frequenza di campionamento dell’ADC. Aumentalo/diminuiscilo in base alla luminosità del LED, al sampleAverage e alla larghezza dell’impulso.

Insomma, dovrai fare un bel po’ di tentativi per trovare un insieme di valori che ti diano delle letture più accurate e stabili possibili.

Io ho tarato questi parametri confrontando il battito cardiaco e l’ossigeno nel sangue con i valori rilevati da apparecchi commerciali. Ho notato che, per quanto ci si impegni nell’effettuare la taratura, il valore del battito cardiaco non rimane abbastanza stabile (quello dell’ossigeno sì). Quando misuri la frequenza cardiaca con il sensore MAX30101, otterrai letture molto più affidabili e stabili se assicuri il sensore al polpastrello con un pezzo di nastro adesivo o un elastico in modo che il contatto fra il dito e il sensore sia più stabile possibile.

Vediamo ora la funzione loop.

Inizialmente viene aggiornato il collegamento al Cloud di Arduino. Poi viene definito un buffer per contenere i campioni rilevati dal sensore, infine viene stampato un messaggio sul display che invita l’utente ad attendere la conclusione delle misurazioni preliminari:

ArduinoCloud.update();

bufferLength = 100; //buffer length of 100 stores 4 seconds of samples running at 25sps


tft.fillScreen(ST7735_BLACK);
tft.setCursor(0, 0);
tft.setRotation(3);
tft.setTextColor(ST7735_RED);
tft.setTextSize(2);
tft.println("Measurements in progress: wait a moment");

Segue poi un blocco che raccoglie i primi 100 campioni del segnale ricevuto dal sensore in modo da determinarne l’escursione:

//read the first 100 samples, and determine the signal range
for (byte i = 0 ; i < bufferLength ; i++)
{
while (particleSensor.available() == false) //do we have new data?
    particleSensor.check(); //Check the sensor for new data

redBuffer[i] = particleSensor.getRed();
irBuffer[i] = particleSensor.getIR();
particleSensor.nextSample(); //We're finished with this sample so move to next sample

/*  PRINTS BLOCK
    Serial.print(F("red="));
    Serial.print(redBuffer[i], DEC);
    Serial.print(F(", ir="));
    Serial.println(irBuffer[i], DEC);
      */
}

Subito dopo viene stampato sul display un messaggio che avverte l’utente che questa fase preliminare è stata completata. Dopo di che viene lanciato l’algoritmo di calcolo sui 100 campioni raccolti per determinare il battito cardiaco e la saturazione:

tft.fillScreen(ST7735_BLACK);
tft.setCursor(0, 0);
tft.setRotation(3);
tft.setTextColor(ST7735_GREEN);
tft.setTextSize(2);
tft.println("Measurements completed");


//calculate heart rate and SpO2 after first 100 samples (first 4 seconds of samples)
maxim_heart_rate_and_oxygen_saturation(irBuffer, bufferLength, redBuffer, &spo2, &validSPO2, &heartRate, &validHeartRate);

Successivamente inizia un ciclo infinito (while(1)) che svolge svariate operazioni ciclicamente. In questo primo blocco viene aggiornato il collegamento al Cloud di Arduino e stampato un messaggio sul display in caso di insuccesso:

ArduinoCloud.update();
if(!ArduinoCloud.connected()) {      
    tft.fillScreen(ST7735_BLACK);
    tft.setCursor(0, 0);
    tft.setRotation(3);
    tft.setTextColor(ST7735_RED);
    tft.setTextSize(2);
    tft.println("Trying to connect to Arduino Cloud");
} else {
    Serial.println("Connected to the cloud");

Il codice seguente continua a calcolare i valori di battito cardiaco e saturazione:

//dumping the first 25 sets of samples in the memory and shift the last 75 sets of samples to the top
for (byte i = 25; i < 100; i++)
{
    redBuffer[i - 25] = redBuffer[i];
    irBuffer[i - 25] = irBuffer[i];
}

//take 25 sets of samples before calculating the heart rate.
for (byte i = 75; i < 100; i++)
{
    while (particleSensor.available() == false) //do we have new data?
    particleSensor.check(); //Check the sensor for new data

    // digitalWrite(readLED, !digitalRead(readLED)); //Blink onboard LED with every data read

    redBuffer[i] = particleSensor.getRed();
    irBuffer[i] = particleSensor.getIR();
    particleSensor.nextSample(); //We're finished with this sample so move to next sample
/* 
    //send samples and calculation result to terminal program through UART
    Serial.print(F("red="));
    Serial.print(redBuffer[i], DEC);
    Serial.print(F(", ir="));
    Serial.print(irBuffer[i], DEC);

    Serial.print(F(", HR="));
    Serial.print(heartRate, DEC);

    Serial.print(F(", HRvalid="));
    Serial.print(validHeartRate, DEC);

    Serial.print(F(", SPO2="));
    Serial.print(spo2, DEC);

    Serial.print(F(", SPO2Valid="));
    Serial.println(validSPO2, DEC);
    */
}

//After gathering 25 new samples recalculate HR and SP02
maxim_heart_rate_and_oxygen_saturation(irBuffer, bufferLength, redBuffer, &spo2, &validSPO2, &heartRate, &validHeartRate);

In particolare, quelli che ci interessano maggiormente sono spo2, validSPO2, heartRate, validHeartRate.

I valori validSPO2 e validHeartRate ci dicono se i valori contenuti rispettivamente in spo2 e heartRate sono validi e quindi da prendere in considerazione oppure da scartare. Se spo2 e heartRate sono da prendere in considerazione avremo validSPO2 e validHeartRate uguali a 1.

Il blocco seguente è il solito blocco if che temporizza le operazioni svolte al suo interno (in questo caso il periodo è pari ad 1 secondo).

if (millis() > lastTimeRanCycle + measureDelayCycle)  
{
if(validHeartRate == 1) {
    heartRateToShow =  String(heartRate);
    heartRateCloud = heartRate;
} else {
    heartRateToShow = "0";
    heartRateCloud = 0;
}

if (validSPO2 == 1) {
    SPO2ToShow =  String(spo2);
    SPO2Cloud = spo2;
} else {
    SPO2ToShow = "0"; 
    SPO2Cloud = 0;
}

/*       Serial.print(F("HR="));
    Serial.print(heartRate, DEC); */
    tft.fillScreen(ST7735_BLACK);
    tft.setCursor(0, 0);
    tft.setRotation(3);
    tft.setTextColor(ST7735_GREEN);
    tft.setTextSize(2);
    tft.println("HR: " + heartRateToShow);


/*       Serial.print(F(", SPO2="));
    Serial.print(spo2, DEC); */
    tft.setTextSize(2);
    tft.println("\n\n\nSPO2: " + SPO2ToShow);

    Serial.println(heartRateCloud);
    Serial.println(SPO2Cloud);


lastTimeRanCycle = millis();
}

Solo se validHeartRate è pari a 1 aggiorna la variabile heartRateToShow (il cui valore verrà mostrato sul display) e la variabile heartRateCloud (il cui valore verrà mostrato sul cloud). Nello stesso modo solo se validSPO2 è pari a 1 aggiorna la variabile SPO2ToShow (il cui valore verrà mostrato sul display) e la variabile SPO2Cloud (il cui valore verrà mostrato sul cloud).

Nel caso i valori non siano validi, sia nel display che sulla dashboard del Cloud verranno convenzionalmente indicati col valore 0.

La parte finale del blocco si occupa di aggiornare il display TFT mentre il Cloud di Arduino verrà aggiornato automaticamente dalla sua libreria ogni volta che legge le variabili heartRateCloud e SPO2Cloud (circa ogni secondo).

Come usare il dispositivo

Col dispositivo collegato al pc tramite USB appoggia il polpastrello del dito indice sul sensore. Carica lo sketch e aspetta che vengano fatte le misurazioni preliminari. Verrai avvisato da un messaggio sul display. A questo punto lo sketch applica l’algoritmo sui valori provenienti dal sensore ed inizia a mostrare le misurazioni sul display e sulla dashboard di Arduino Cloud. Se provi ad allontanare il dito dal sensore, dopo qualche secondo la misurazione dovrebbe mostrare il valore 0 per entrambi i parametri (indicandone la non validità).

Per tentativi, prova a modificare i valori dei parametri dell’algoritmo come spiegato più sopra in modo da avere valori il più possibile attendibili e stabili (ti occorreranno degli strumenti di misura di riferimento come un cardiofrequenzimetro e un saturimetro).

Video del funzionamento del sistema di misurazione della saturazione di ossigeno e della frequenza cardiaca

Per avere misure stabili, il polpastrello dovrebbe esercitare una pressione costante sul sensore e rimanere ben fermo. Sarebbe consigliabile anche assumere una posizione stabile (per esempio stare seduti).

Quindi cosa stai ancora aspettando? Iscriviti gratuitamente su https://cloud.arduino.cc/

Newsletter

Se vuoi essere aggiornato sui nuovi articoli, iscriviti alla newsletter. Prima dell’iscrizione alla newsletter leggi la pagina Privacy Policy (UE)

Se ti vuoi disiscrivere dalla newsletter clicca sul link che troverai nella mail della newsletter.

Inserisci il tuo nome
Inserisci la tua email
Torna in alto