Загрузка данных


#include <Wire.h>
#include "SSD1306Ascii.h"
#include "SSD1306AsciiWire.h"
#include "MAX30105.h"
#include "heartRate.h"
#include "spo2_algorithm.h"

// Инициализация лёгкого текстового дисплея SSD1306 (занимает всего ~50 байт ОЗУ!)
SSD1306AsciiWire oled;
MAX30105 particleSensor;

// Назначение пинов согласно схеме
const int PIN_INT  = 2;  // D2 — прерывание MAX30102
const int PIN_BTN  = 3;  // D3 — кнопка SW2 (к GND)
const int PIN_VBAT = A0; // A0 — делитель R1/R2 (100k / 100k)

// Переменные для расчёта пульса (BPM)
const byte RATE_SIZE = 4;
byte rates[RATE_SIZE];
byte rateSpot = 0;
long lastBeat = 0;
float beatsPerMinute = 0;
int beatAvg = 0;

// Буферы для алгоритма SpO2
const byte BUFFER_SIZE = 50;
uint16_t irBuffer[BUFFER_SIZE];
uint16_t redBuffer[BUFFER_SIZE];
int32_t spo2 = 0;
int8_t validSPO2 = 0;
int32_t heartRateAlgo = 0;
int8_t validHeartRate = 0;
byte sampleIndex = 0;

// Оценка АД
int sysBP = 120;
int diaBP = 80;

// Напряжение аккумулятора
float vbat = 0.0;
int batPercent = 0;

// Управление экранами
byte screenMode = 0; // 0: HR + SpO2, 1: BP (АД), 2: Battery
byte lastScreenMode = 255;
unsigned long lastBtnPress = 0;
unsigned long lastDisplayUpdate = 0;

void setup() {
  Serial.begin(115200);
  
  pinMode(PIN_INT, INPUT_PULLUP);
  pinMode(PIN_BTN, INPUT_PULLUP);

  Wire.begin();
  Wire.setClock(400000L);
  
  // Отключаем внутренние подтяжки 5В на A4/A5 (согласно п. 5 примечаний)
  digitalWrite(SDA, LOW);
  digitalWrite(SCL, LOW);

  // Инициализация OLED по адресу 0x3C
  oled.begin(&Adafruit128x64, 0x3C);
  oled.setFont(System5x7);
  oled.clear();

  // Инициализация MAX30102 (адрес 0x57)
  if (!particleSensor.begin(Wire, I2C_SPEED_FAST)) {
    oled.println("ERR: MAX30102");
    oled.println("Check I2C wiring!");
    while (1);
  }

  particleSensor.setup(60, 4, 2, 100, 411, 4096);
}

void loop() {
  // 1. Опрос кнопки SW2 (D3)
  if (digitalRead(PIN_BTN) == LOW) {
    if (millis() - lastBtnPress > 250) {
      screenMode = (screenMode + 1) % 3;
      lastBtnPress = millis();
      oled.clear();
      updateDisplay(particleSensor.getIR() > 50000);
    }
  }

  // 2. Чтение датчика MAX30102
  particleSensor.check();
  while (particleSensor.available()) {
    uint32_t irValue = particleSensor.getFIFOIR(); // Исправлено!
    uint32_t redValue = particleSensor.getFIFORed();
    particleSensor.nextSample();

    if (irValue > 50000) {
      if (checkForBeat(irValue) == true) {
        long delta = millis() - lastBeat;
        lastBeat = millis();
        beatsPerMinute = 60 / (delta / 1000.0);

        if (beatsPerMinute < 220 && beatsPerMinute > 40) {
          rates[rateSpot++] = (byte)beatsPerMinute;
          rateSpot %= RATE_SIZE;

          beatAvg = 0;
          for (byte x = 0; x < RATE_SIZE; x++) beatAvg += rates[x];
          beatAvg /= RATE_SIZE;
        }
      }

      irBuffer[sampleIndex] = (uint16_t)(irValue >> 2);
      redBuffer[sampleIndex] = (uint16_t)(redValue >> 2);
      sampleIndex++;

      if (sampleIndex >= BUFFER_SIZE) {
        sampleIndex = 0;
        maxim_heart_rate_and_oxygen_saturation(
          irBuffer, BUFFER_SIZE, redBuffer, 
          &spo2, &validSPO2, &heartRateAlgo, &validHeartRate
        );

        if (beatAvg > 45) {
          uint16_t minIR = 65535, maxIR = 0;
          for (byte i = 0; i < BUFFER_SIZE; i++) {
            if (irBuffer[i] < minIR) minIR = irBuffer[i];
            if (irBuffer[i] > maxIR) maxIR = irBuffer[i];
          }
          uint16_t amplitude = maxIR - minIR;
          
          sysBP = 115 + (beatAvg - 70) * 0.4 + (amplitude / 150.0);
          diaBP = 75  + (beatAvg - 70) * 0.25 + (amplitude / 300.0);
          
          sysBP = constrain(sysBP, 85, 190);
          diaBP = constrain(diaBP, 55, 120);
        }
      }
    } else {
      beatAvg = 0;
      validSPO2 = 0;
      sampleIndex = 0;
    }
  }

  // 3. Обновление экрана каждые 500 мс
  if (millis() - lastDisplayUpdate > 500) {
    lastDisplayUpdate = millis();
    readBattery();
    updateDisplay(particleSensor.getIR() > 50000);
  }
}

void readBattery() {
  int raw = analogRead(PIN_VBAT);
  vbat = (raw * 5.0 / 1023.0) * 2.0;
  batPercent = map((long)(vbat * 100), 330, 420, 0, 100);
  batPercent = constrain(batPercent, 0, 100);
}

void updateDisplay(bool fingerDetected) {
  if (screenMode != lastScreenMode) {
    oled.clear();
    lastScreenMode = screenMode;
  }

  // Верхняя строка: номер экрана и заряд батареи
  oled.set1X();
  oled.setCursor(0, 0);
  oled.print("MODE ");
  oled.print(screenMode + 1);
  oled.print("/3     BAT:");
  if (batPercent < 100) oled.print(" ");
  oled.print(batPercent);
  oled.println("%");
  oled.println("---------------------");

  if (screenMode == 0) {
    oled.println("PULSE & SpO2:");
    if (!fingerDetected) {
      oled.println();
      oled.println("  Place finger!      ");
      oled.println("                     ");
    } else {
      oled.println();
      oled.print("HR:   ");
      if (beatAvg > 0) { oled.print(beatAvg); oled.println(" bpm    "); }
      else oled.println("---       ");

      oled.print("SpO2: ");
      if (validSPO2 && spo2 > 70 && spo2 <= 100) { oled.print(spo2); oled.println(" %      "); }
      else oled.println("calc...   ");
    }
  } 
  else if (screenMode == 1) {
    oled.println("BLOOD PRESSURE (EST):");
    if (!fingerDetected || beatAvg == 0) {
      oled.println();
      oled.println("  Waiting pulse...   ");
      oled.println("                     ");
    } else {
      oled.set2X();
      oled.setCursor(10, 4);
      oled.print(sysBP);
      oled.print("/");
      oled.print(diaBP);
      oled.println("   ");
      oled.set1X();
      oled.setCursor(35, 7);
      oled.print("mmHg");
    }
  } 
  else if (screenMode == 2) {
    oled.println("BATTERY STATUS:");
    oled.println();
    oled.print("Voltage: ");
    oled.print(vbat, 2);
    oled.println(" V   ");
    oled.print("Charge:  ");
    oled.print(batPercent);
    oled.println(" %   ");
  }
}