Table of Contents

Dispozitiv de monitorizare a sanatatii si alcoolemiei

Autor: Atomei Alexandru-Constantin

Grupa: 334CD

Introducere

Proiectul consta intr-un dispozitiv portabil care integreaza un senzor de pulsoximetrie și un senzor de alcool. Cu ajutorul pulsoximetrului se masoara în timp real ritmul cardiac (BPM) și nivelul de saturație a oxigenului din sange (SpO₂), iar cu ajutorul senzorului de alcool se determina concentrația de alcool din aerul expirat. Datele sunt afișate pe un ecran LCD TFT, iar utilizatorul poate schimba modul de functionare al dispozitivului printr-un buton dedicat. În cazul depășirii unui prag de alcoolemie prestabilit, un buzzer emite un semnal de avertizare.

Scopul proiectului este de a oferi o soluție practică de auto-monitorizare a starii de sanatate și a nivelului de alcool, ajutand astfel la prevenirea incidentelor cauzate de lipsa de informare. Dispozitivul poate fi util atat pentru persoanele preocupate de sanatatea lor, cat si pentru șoferi care vor să evite sancțiunile legale.

Inspirat de dorinta de a avea control total asupra propriei stari, mi-am imaginat un dispozitiv care să imbine două functii: monitorizarea continua a semnelor vitale şi testarea alcoolemiei, pentru evitarea deciziilor riscante. Am realizat ca, de multe ori, nu avem la indemana un instrument rapid care sa ne spuna ca avem o problema – fie că e vorba de un atac de panica/efort mult prea ridicat, fie ca ne intoarcem acasa dupa o seara în oras in care am baut.

Astfel, am decis sa proiectez un sistem compact, user-friendly, care sa puna la dispozitie date importante despre corpul nostru, combinand utilitatea medicala cu responsabilitatea sociala.

Descriere generală

Proiectul are la baza un microcontroller ESP32, care face legatura intre tot hardware-ul si gestioneaza atat achizitia de date, cat si interactiunea cu utilizatorul.

Conectate la el sunt urmatoarele componente:

Hardware Design

Schema electrica

pm_schematic_atomei_alexandru.pdf

Bill of Materials

Nume componenta Model Protocol Link achizitie Datasheet
Microcontroller ESP32-WROOM-32 - Optimus Digital Datasheet ESP32
Senzor pulsoximetrie MAX30102 I2C eMAG Datasheet MAX30102
Senzor gaz MQ-3 ADC Optimus Digital Datasheet MQ-3
Display LCD TFT 128x128px 1.44 inch ST7735S SPI eMAG -
Buzzer Buzzer pasiv 5V PWM - -
Buton Push Button - - -
Rezistori - - - -

Conexiuni componente

Din punct de vedere al legaturilor, componentele sunt conectate astfel:

Poze

Software Design

Descrierea codului aplicaţiei (firmware):

Intrerupere + timer
void IRAM_ATTR isr_button(void* arg) {
  button_time = esp_timer_get_time() / 1000;
  if (button_time - last_button_time > 250) {
    buttonPressed = true;
    last_button_time = button_time;
  }
}

Intreruperea e folosita pentru a detecta corect apasarea butonului, care determina schimbarea starii device-ului intre pulsoximetru si alcooltest, iar interogarea timer-ului intern al ESP32-ului pentru a scapa de debouncing.

GPIO + PWM
// Setup buzzer PWM
ledc_timer_config_t ledc_timer = {
    .speed_mode = LEDC_LOW_SPEED_MODE,
    .duty_resolution = LEDC_TIMER_8_BIT,
    .timer_num = LEDC_TIMER_0,
    .freq_hz = 2000,
    .clk_cfg = LEDC_AUTO_CLK
};
ledc_timer_config(&ledc_timer);
 
ledc_channel_config_t ledc_channel = {
    .gpio_num = buzzerPin,
    .speed_mode = LEDC_LOW_SPEED_MODE,
    .channel = buzzerChannel,
    .timer_sel = LEDC_TIMER_0,
    .duty = 0,
    .hpoint = 0
};
ledc_channel_config(&ledc_channel);
 
// Set GPIO19 as input
GPIO.enable_w1tc = (1 << 19); // Clear output enable (input mode)
 
// Enable pull-up
GPIO.pin[19].pad_driver = 0; // Normal output (not open-drain)
REG_SET_BIT(GPIO_PIN_MUX_REG[19], FUN_PU); // Enable pull-up (register macro)
 
// Set interrupt type: negative edge
GPIO.pin[19].int_type = 1;
 
// Enable GPIO interrupt for GPIO19
GPIO.pin[19].int_ena = 1;     // Enable interrupt for CPU 0
 
// Register the ISR
esp_intr_alloc(ETS_GPIO_INTR_SOURCE, 0, isr_button, NULL, NULL);
 
// Enable global GPIO interrupts
ets_intr_unlock();

Initializarea butonului si al PWM-ului pentru buzzer sunt realizate utilizand ESP-IDF + lucru direct pe registrii ESP-ului(lucru care mi-a dat batai de cap foarte mari, pentru restul implementarilor am decis sa raman la ESP-IDF si framework-ul de Arduino). Pinul la care e legat butonul este initializat ca input, intreruperea e setata pe falling edge si e activata rezistenta de pull-up interna corespunzatoare. Pentru PWM, este folosit timer-ul LEDC pe 8 biti.

void buzzer_task(void *param) {
    int freq;
 
    while (1) {
        if (xQueueReceive(buzzerQueue, &freq, portMAX_DELAY)) {
            ledc_set_freq(LEDC_LOW_SPEED_MODE, LEDC_TIMER_0, freq);
            ledc_set_duty(LEDC_LOW_SPEED_MODE, buzzerChannel, 128);
            ledc_update_duty(LEDC_LOW_SPEED_MODE, buzzerChannel);
 
            vTaskDelay(pdMS_TO_TICKS(100));
 
            ledc_set_duty(LEDC_LOW_SPEED_MODE, buzzerChannel, 0);
            ledc_update_duty(LEDC_LOW_SPEED_MODE, buzzerChannel);
        }
    }
}
if (buttonPressed) {
    int note = NOTE_D4;
    xQueueSend(buzzerQueue, &note, 0);
 
    change_state();
}

La fiecare apasare a butonului este actionat si buzzer-ul prin modificarea duty cycle-ului pe o perioada scurta. Am decis sa creez un task separat pentru buzzer, deoarece aparea un delay sesizabil la schimbarea afisajului din cauza delay-ului.

SPI

SPI-ul e initializat prin libraria Adafruit_ST7735 a display-ului. La schimbarea valorilor de pe ecran, acesta nu se redeseneaza in totalitate, ci doar partea care necesita modificari.

I2C

I2C-ul e initializat prin libraria Wire.h din Arduino. Transmiterea si primirea de informatii este realizata prin libraria MAX3010x a senzorului de pulsoximetrie. Codul pentru prelucrarea datelor este preluat dintr-un exemplu pus la dispozitie in aceasta librarie.Referinta este la finalul paginii.

ADC
double measure_alcohol() {
    const int measurementDuration = 500;
    const int sampleInterval = 50;
    const int numSamples = measurementDuration / sampleInterval;
    int samples[numSamples];
 
    unsigned long startTime = millis();
    int sampleCount = 0;
 
    while (millis() - startTime < measurementDuration && sampleCount < numSamples) {
        samples[sampleCount++] = analogRead(MQ3_PIN);
        delay(sampleInterval);
    }
 
    float mean = 0.0;
    for (int i = 0; i < sampleCount; ++i) {
        mean += samples[i];
    }
    mean /= sampleCount;
 
    double Vout = static_cast<double>(mean) * (5 / 4096.0);
    double Rs = RL * (5 - Vout) / Vout;
 
    double ratio = Rs / R0;
 
    // Apply the log-log relationship to get ppm
    double ppm = pow(10, -1.76 * log10(ratio) + 2.3);
 
    // Convert to mg/L (ethanol molecular weight = 46.07 g/mol)
    double mgL = (ppm * 46.07 / 24.45) / 1000.0;
 
    return mgL;
}

Pentru citirea senzorului de alcool prin ADC m-am folosit de functia de analogRead() din Arduino(din nou, am incercat sa folosesc varianta din ESP-IDF, dar obtineam rezultate diferite si nu mai erau calibrate..). Calculele pentru transformari au fost gasite prin diferite articole pe care le voi referentia la finalul paginii.

Rezultate Obţinute

Aici gasiti demo-ul proiectului: https://www.youtube.com/watch?v=yMKbu_0QTNY

Download

Repository Github

Jurnal

Bibliografie/Resurse

Resurse Software

Resurse Hardware