#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;
}
mega:SCL
mega:SDA
mega:AREF
mega:GND.1
mega:13
mega:12
mega:11
mega:10
mega:9
mega:8
mega:7
mega:6
mega:5
mega:4
mega:3
mega:2
mega:1
mega:0
mega:14
mega:15
mega:16
mega:17
mega:18
mega:19
mega:20
mega:21
mega:5V.1
mega:5V.2
mega:22
mega:23
mega:24
mega:25
mega:26
mega:27
mega:28
mega:29
mega:30
mega:31
mega:32
mega:33
mega:34
mega:35
mega:36
mega:37
mega:38
mega:39
mega:40
mega:41
mega:42
mega:43
mega:44
mega:45
mega:46
mega:47
mega:48
mega:49
mega:50
mega:51
mega:52
mega:53
mega:GND.4
mega:GND.5
mega:IOREF
mega:RESET
mega:3.3V
mega:5V
mega:GND.2
mega:GND.3
mega:VIN
mega:A0
mega:A1
mega:A2
mega:A3
mega:A4
mega:A5
mega:A6
mega:A7
mega:A8
mega:A9
mega:A10
mega:A11
mega:A12
mega:A13
mega:A14
mega:A15
pot1:GND
pot1:SIG
pot1:VCC
bz1:1
bz1:2
btn1:1.l
btn1:2.l
btn1:1.r
btn1:2.r
btn2:1.l
btn2:2.l
btn2:1.r
btn2:2.r
btn3:1.l
btn3:2.l
btn3:1.r
btn3:2.r
pot2:GND
pot2:SIG
pot2:VCC
pot3:GND
pot3:SIG
pot3:VCC
led1:A
led1:C
led2:A
led2:C
led3:A
led3:C
r1:1
r1:2
r2:1
r2:2
r3:1
r3:2