#include <Wire.h>
#include <LiquidCrystal_I2C.h>
#include <WiFi.h>
#include <HTTPClient.h>
// WiFi credentials (Wokwi simulation)
const char* SSID = "Wokwi-GUEST";
const char* password = "";
// ThingSpeak channel details
const char* thingSpeakApiKey = "2IQEBXCQYKBQHDXP";
const char* thingSpeakServer = "api.thingspeak.com";
const int thingSpeakChannelId = 2826596;
const int SPO2_PIN = 35; // SpO2 sensor on pin 35 (adjust as needed)
const int HR_PIN = 0; // Heart rate sensor on pin 0 (adjust as needed)
const int LED_PIN = 13; // LED on pin 13 (adjust as needed)
// Initialize the LCD. The I2C address is usually 0x27 or 0x3F for many LCDs.
LiquidCrystal_I2C lcd(0x27, 16, 2); // 16x2 LCD
int heartRate;
int spo2;
struct LCDMessage {
String line1;
String line2;
// Custom equality operator for easy comparison
bool operator!=(const LCDMessage &other) const {
return line1 != other.line1 || line2 != other.line2;
}
};
LCDMessage currentMessage;
LCDMessage previousMessage;
void setup() {
Serial.begin(115200);
pinMode(SPO2_PIN, INPUT); // Set the SpO2 pin as input
pinMode(HR_PIN, INPUT); // Set the HR pin as input
pinMode(LED_PIN, OUTPUT); // Set the LED pin as output
// Initialize I2C communication
Wire.begin(); // Start I2C
lcd.begin(16,2); // Initialize the LCD with 16 columns and 2 rows
lcd.backlight(); // Turn on the backlight
lcd.setCursor(0, 0); // Set the cursor to the top-left corner
//Connect to WIfi
WiFi.begin(SSID,password,6);
Serial.print("Connecting to WiFi...");
delay(1000); // Add a 1-second delay here
while(WiFi.status()!= WL_CONNECTED)
{
delay(500);
Serial.print(".");
}
Serial.println("\nConnected to WiFi");
}
void loop() {
// Call the custom function to get data from both sensors
readSensorsData();
// Check critical conditions and trigger LED if needed
checkVitalsStatus(heartRate, spo2);
UpdateLcdMessage();
// Send data to ThingSpeak
sendDataToThingSpeak(heartRate, spo2);
delay(500);
}
// Custom function to read data from pulse oximeter (MAX30102) and DHT22 sensor
void readSensorsData() {
// Read heart rate sensor (HR) and SpO2 sensor values
int spo2_raw = analogRead(SPO2_PIN);
int hr_raw = analogRead(HR_PIN);
// Convert analog values to voltage
float voltageHR = hr_raw * (5.0 / 4095.0); // For 5V system, scale to 0-5V
float voltageSPO2 = spo2_raw * (5.0 / 4095.0); // For 5V system, scale to 0-5V
// Calculate heart rate and SpO2 values
heartRate = (voltageHR / 3.3) * (200 - 40) + 40;
spo2 = (voltageSPO2 / 3.3) * (100 - 80) + 80;
// Print the results to the serial monitor
Serial.print("Heart Rate: ");
Serial.print(heartRate);
Serial.print(" bpm, ");
Serial.print("SpO2: ");
Serial.print(spo2);
Serial.print(" %.");
Serial.println("----------------------");
}
void checkVitalsStatus(int heartRate, int spo2) {
String hrStatus = "";
String spo2Status = "";
bool critical = false;
// Prepare line1: HR and SpO2 values
currentMessage.line1 = "HR:" + String(heartRate) + " SpO2:" + String(spo2);
// --- Heart Rate Conditions ---
if (heartRate < 40) {
hrStatus = "Flatline HR";
critical = true;
}
else if (heartRate < 60) {
hrStatus = "Bradycardia";
critical = true;
}
else if (heartRate <= 100) {
hrStatus = "HR Normal";
}
else if (heartRate <= 140) {
hrStatus = "HR Elevated";
}
else {
hrStatus = "Tachycardia!";
critical = true;
}
// --- SpO2 Conditions ---
if (spo2 < 70) {
spo2Status = "SpO2 Error";
critical = true;
}
else if (spo2 < 90) {
spo2Status = "Hypoxemia!";
critical = true;
}
else if (spo2 <= 94) {
spo2Status = "SpO2 Low";
}
else {
spo2Status = "SpO2 Normal";
}
// Determine priority message for line2
if (critical) {
currentMessage.line2 = hrStatus.length() > spo2Status.length() ? hrStatus : spo2Status;
} else {
currentMessage.line2 = "All Normal";
}
// Output to Serial Monitor (for debugging)
Serial.println(currentMessage.line1);
Serial.println(currentMessage.line2);
Serial.println("-----------------------------");
// Trigger the LED if any critical condition is met
if (critical) {
digitalWrite(LED_PIN, HIGH); // Turn on LED
} else {
digitalWrite(LED_PIN, LOW); // Turn off LED
Serial.println("Vitals Status: ✅ All Systems Normal");
}
}
void UpdateLcdMessage() {
if (currentMessage != previousMessage) {
// --- Line 1 ---
lcd.setCursor(0, 0);
lcd.print(currentMessage.line1);
clearLinePadding(currentMessage.line1.length(), previousMessage.line1.length());
// --- Line 2 ---
lcd.setCursor(0, 1);
lcd.print(currentMessage.line2);
clearLinePadding(currentMessage.line2.length(), previousMessage.line2.length());
previousMessage = currentMessage;
}
}
// Helper to pad shorter messages with blanks
void clearLinePadding(int currentLen, int previousLen) {
for (int i = currentLen; i < previousLen; i++) {
lcd.print(" ");
}
}
void sendDataToThingSpeak(int heartRate, int spo2)
{
if(WiFi.status() == WL_CONNECTED)
{
HTTPClient http;
WiFiClient client; // Create a WiFiClient object
char serverPath[150]; // Increase buffer size for multiple fields
// Build the GET request with all 4 fields
sprintf(serverPath, "/update?api_key=%s&field1=%.2f&field2=%.2f&field3=%d&field4=%d",
thingSpeakApiKey, 0.0, 0.0, heartRate, spo2);
http.begin(client, thingSpeakServer,80,serverPath);
int httpResponseCode = http.GET();
if(httpResponseCode==200)
{
Serial.println("ThingSpeak Update Successful");
}
else
{
Serial.print("ThingSpeak Update Failed. Error Code: ");
Serial.println(httpResponseCode);
}
http.end();
client.stop();
}
else
{
Serial.println("Wifi Disconnected!");
}
}