#include <EEPROM.h>
#include <Adafruit_Sensor.h>
#include <RTClib.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#define SENSOR_PIN A0
#define SPEAKER_PIN 10
#define SOUND_CTRL_BUTTON_PIN 9
#define RECORD_CTRL_BUTTON_PIN 8
#define MENU_CTRL_BUTTON_PIN 7
#define POT1_PIN A1
#define POT2_PIN A2
#define RED_LED 13
#define YELLOW_LED 12
#define GREEN_LED 11
#define SENSOR_READINGS 10
#define READINGS_DELAY 100
#define GRAPH_HEIGHT 32
#define GRAPH_WIDTH 128
enum Language { ENGLISH, FRENCH };
Language currentLanguage = ENGLISH;
// English and French text
const String englishText[] = {"Time: ", "BPM: ", "Menu Option: ", "Menu Value: "};
const String frenchText[] = {"Temps: ", "BPM: ", "Option de menu: ", "Valeur de menu: "};
RTC_DS3231 rtc;
Adafruit_SSD1306 display1(128, 32, &Wire, -1);
Adafruit_SSD1306 display2(128, 32, &Wire, -1);
bool speakerOn = true;
int graphTimeScale = 1;
int heartRate = 0;
DateTime now;
int heartRateBuffer[10];
int bufferIndex = 0;
int eepromAddress = 0;
struct Record {
time_t timestamp;
int heartRate;
};
void setup() {
Serial.begin(9600);
pinMode(RED_LED, OUTPUT);
pinMode(YELLOW_LED, OUTPUT);
pinMode(GREEN_LED, OUTPUT);
pinMode(SPEAKER_PIN, OUTPUT);
pinMode(SOUND_CTRL_BUTTON_PIN, INPUT_PULLUP);
pinMode(RECORD_CTRL_BUTTON_PIN, INPUT_PULLUP);
pinMode(MENU_CTRL_BUTTON_PIN, INPUT_PULLUP);
pinMode(POT1_PIN, INPUT);
pinMode(POT2_PIN, INPUT);
if(!rtc.begin()) {
Serial.println("Couldn't find RTC");
while (1);
}
if(rtc.lostPower()) {
Serial.println("RTC lost power, let's set the time!");
rtc.adjust(DateTime(F(__DATE__), F(__TIME__)));
}
if(!display1.begin(SSD1306_SWITCHCAPVCC, 0x3C)) {
Serial.println(F("SSD1306 allocation failed!"));
for(;;);
}
if(!display2.begin(SSD1306_SWITCHCAPVCC, 0x3D)) {
Serial.println(F("SSD1306 allocation failed"));
for(;;);
}
display1.clearDisplay();
display2.clearDisplay();
}
void loop() {
// Heart rate capturing
heartRate = readHeartRate();
if(bufferIndex < 10) {
heartRateBuffer[bufferIndex] = heartRate;
bufferIndex++;
}
if(bufferIndex == 10 && digitalRead(RECORD_CTRL_BUTTON_PIN) == LOW) {
Record record;
record.timestamp = now.unixtime();
record.heartRate = average(heartRateBuffer, 10);
EEPROM.put(eepromAddress, record);
eepromAddress += sizeof(Record);
bufferIndex = 0;
// Check if EEPROM is full
if (eepromAddress >= EEPROM.length()) {
eepromAddress = 0; // Start overwriting old records
}
}
// Time Counting
now = rtc.now();
// Display patient's status
displayPatientStatus(heartRate);
// Sound signal
if(digitalRead(SOUND_CTRL_BUTTON_PIN) == LOW) {
speakerOn = !speakerOn;
}
beepHeartRate(heartRate);
// Display on OLEDs
displayTimeAndBPM(now, heartRate, &display1);
displayPPG(&display2);
// Menu system
if(digitalRead(MENU_CTRL_BUTTON_PIN) == LOW) {
handleMenu();
}
delay(1000);
}
int readHeartRate() {
unsigned long lastPeakTime = 0;
int beatsPerMinute = 0;
int sensorValue = analogRead(SENSOR_PIN);
static int lastSensorValue = 0;
if (lastSensorValue < sensorValue) {
lastPeakTime = millis();
} else if (lastSensorValue > sensorValue) {
unsigned long peakInterval = millis() - lastPeakTime;
beatsPerMinute = 60000 / peakInterval;
}
lastSensorValue = sensorValue;
return beatsPerMinute;
}
void displayPatientStatus(int heartRate) {
digitalWrite(RED_LED, LOW);
digitalWrite(YELLOW_LED, LOW);
digitalWrite(GREEN_LED, LOW);
if (heartRate < 50 || heartRate > 100) {
digitalWrite(RED_LED, HIGH);
}
else if (heartRate < 60) {
digitalWrite(YELLOW_LED, HIGH);
}
else {
digitalWrite(GREEN_LED, HIGH);
}
}
void beepHeartRate(int heartRate) {
int frequency;
if (heartRate < 60) {
frequency = 200;
}
else if (heartRate > 100) {
frequency = 800;
}
else {
frequency = 500;
}
if (speakerOn) {
tone(SPEAKER_PIN, frequency, 500);
}
}
void displayTimeAndBPM(DateTime now, int heartRate, Adafruit_SSD1306 *display) {
display->clearDisplay();
display->setTextSize(1);
display->setTextColor(WHITE);
display->setCursor(0,0);
String timeText = (currentLanguage == ENGLISH) ? englishText[0] : frenchText[0];
String bpmText = (currentLanguage == ENGLISH) ? englishText[1] : frenchText[1];
display->println(timeText + String(now.hour()) + ":" + String(now.minute()) + ":" + String(now.second()));
display->println(bpmText + String(heartRate));
display->display();
}
void displayMenu(int menuOption, int menuValue) {
// Clear display
display1.clearDisplay();
// Set text properties
display1.setTextSize(1);
display1.setTextColor(WHITE);
// Display menu options and values
display1.setCursor(0, 0);
String menuOptionText = (currentLanguage == ENGLISH) ? englishText[2] : frenchText[2];
String menuValueText = (currentLanguage == ENGLISH) ? englishText[3] : frenchText[3];
display1.print(menuOptionText);
display1.println(menuOption);
display1.setCursor(0, 10);
display1.print(menuValueText);
display1.println(menuValue);
// Update display
display1.display();
}
void displayPPG(Adafruit_SSD1306 *display) {
static int x = 0;
static int old_y = display->height() / 2;
int sensorValue = analogRead(SENSOR_PIN);
int y = map(sensorValue, 0, 1023, display->height(), 0);
display->clearDisplay();
display->drawLine(x - 1, old_y, x, y, WHITE);
old_y = y;
x++;
if(x >= display->width()) {
x = 0;
}
display->display();
}
void handleMenu() {
// Menu variables
int menuOption = 0; // Current selected menu option
int numMenuOptions = 3; // Total number of menu options
int menuValue = 0; // Current value of the selected menu option
// Menu control variables
int potValue = 0; // Potentiometer value
int lastPotValue = 0; // Previous potentiometer value
int menuButtonState = HIGH; // State of the menu button
int lastMenuButtonState = HIGH; // Previous state of the menu button
int menuButtonPress = 0; // Flag to detect menu button press
// Menu loop
while (true) {
// Read potentiometer value
potValue = analogRead(POT1_PIN);
// Update menu option based on potentiometer value
menuOption = map(potValue, 0, 1023, 0, numMenuOptions - 1);
// Check for menu button press
menuButtonState = digitalRead(MENU_CTRL_BUTTON_PIN);
if (menuButtonState == LOW && lastMenuButtonState == HIGH) {
menuButtonPress = 1;
}
lastMenuButtonState = menuButtonState;
// Update menu value based on button press
if (menuButtonPress) {
menuValue++;
if (menuValue > 10) {
menuValue = 0;
}
menuButtonPress = 0;
}
// Display menu options and values
displayMenu(menuOption, menuValue);
// Delay to debounce inputs
delay(100);
}
}
void displayMenu(int menuOption, int menuValue) {
// Clear display
display1.clearDisplay();
// Set text properties
display1.setTextSize(1);
display1.setTextColor(WHITE);
// Display menu options and values
display1.setCursor(0, 0);
display1.print("Menu Option: ");
display1.println(menuOption);
display1.setCursor(0, 10);
display1.print("Menu Value: ");
display1.println(menuValue);
// Update display
display1.display();
}
int average(int* buffer, int size) {
int sum = 0;
for(int i = 0; i < size; i++) {
sum += buffer[i];
}
return sum / size;
}