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


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

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 SAMPLE_WINDOW = 50;
byte sampleCount = 0;
uint32_t minIR = 0xFFFFFFFF, maxIR = 0;
uint32_t minRed = 0xFFFFFFFF, maxRed = 0;
uint32_t sumIR = 0, sumRed = 0;

int spo2 = 0;
bool validSPO2 = false;
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() {
  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.begin(&Adafruit128x64, 0x3C);
  oled.setFont(System5x7);
  oled.clear();

  if (!particleSensor.begin(Wire, I2C_SPEED_FAST)) {
    oled.println(F("ERR: MAX30102"));
    oled.println(F("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;
        }
      }

      // Накопление минимумов/максимумов на лету (экономит 1.5 КБ ОЗУ!)
      if (irValue < minIR) minIR = irValue;
      if (irValue > maxIR) maxIR = irValue;
      if (redValue < minRed) minRed = redValue;
      if (redValue > maxRed) maxRed = redValue;
      
      sumIR += (irValue >> 4);
      sumRed += (redValue >> 4);
      sampleCount++;

      // Каждые 50 выборок вычисляем SpO2 и оценку АД
      if (sampleCount >= SAMPLE_WINDOW) {
        float acIR = (float)(maxIR - minIR);
        float acRed = (float)(maxRed - minRed);
        float dcIR = (float)(sumIR / SAMPLE_WINDOW);
        float dcRed = (float)(sumRed / SAMPLE_WINDOW);

        if (dcIR > 0 && dcRed > 0 && acIR > 20 && acRed > 20) {
          // Классическая формула оксиметрии через отношение R
          float R = (acRed / dcRed) / (acIR / dcIR);
          int calcSpo2 = (int)(110.0 - 25.0 * R);
          
          if (calcSpo2 >= 70 && calcSpo2 <= 100) {
            spo2 = constrain(calcSpo2, 80, 99);
            validSPO2 = true;
          }
        }

        // Оценка АД по ЧСС и амплитуде пульсовой волны (п. 4 примечаний)
        if (beatAvg > 45) {
          sysBP = 115 + (beatAvg - 70) * 0.4 + (acIR / 600.0);
          diaBP = 75  + (beatAvg - 70) * 0.25 + (acIR / 1200.0);
          sysBP = constrain(sysBP, 85, 190);
          diaBP = constrain(diaBP, 55, 120);
        }

        // Сброс для следующего окна измерений
        sampleCount = 0;
        minIR = 0xFFFFFFFF; maxIR = 0;
        minRed = 0xFFFFFFFF; maxRed = 0;
        sumIR = 0; sumRed = 0;
      }
    } else {
      beatAvg = 0;
      validSPO2 = false;
      sampleCount = 0;
      minIR = 0xFFFFFFFF; maxIR = 0;
      minRed = 0xFFFFFFFF; maxRed = 0;
      sumIR = 0; sumRed = 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(F("MODE "));
  oled.print(screenMode + 1);
  oled.print(F("/3     BAT:"));
  if (batPercent < 100) oled.print(F(" "));
  oled.print(batPercent);
  oled.println(F("%"));
  oled.println(F("---------------------"));

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

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