#include <WiFi.h>
#include <PubSubClient.h>
#include <HTTPClient.h>
#include <Wire.h>
#include <Adafruit_SSD1306.h>
#include <OneWire.h>
#include <DallasTemperature.h>
#include <HX711.h>
#include <SD.h>
#include <SPI.h>
#include <Adafruit_ADXL345_U.h>
// OLED
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, -1);
// Sensor pins
#define ECG_PIN 36
#define ONE_WIRE_BUS 4
#define DOUT 25
#define CLK 26
#define HR_PIN 35
#define SPO2_PIN 32
#define SYS_PIN 33
#define DIA_PIN 34
#define MOTION_PIN 14
// Buzzer & LED
#define BUZZER_PIN 13
#define LED_PIN 12
// SD card
#define SD_CS 5
// WiFi / MQTT / ThingSpeak
const char* ssid = "Wokwi-GUEST";
const char* password = "";
const char* mqttServer = "broker.hivemq.com";
const int mqttPort = 1883;
const char* patientID = "PT-001";
const char* thingspeakServer = "http://api.thingspeak.com/update";
String apiKey = "J3WIA5NRCOU54Q9I";
// Objects
WiFiClient espClient;
PubSubClient client(espClient);
OneWire oneWire(ONE_WIRE_BUS);
DallasTemperature sensors(&oneWire);
HX711 scale;
Adafruit_ADXL345_Unified accel = Adafruit_ADXL345_Unified(12345);
// ECG + HR variables
float ecgVoltage;
int ecgRaw;
unsigned long lastBeatTime = 0;
int beatCount = 0;
unsigned long startTime;
// RR variables
float prevZ = 0;
unsigned long lastBreathTime = 0;
int breathCount = 0;
int RR = 0;
// Helper functions
String getTimestamp() {
char buf[20];
sprintf(buf, "%04d-%02d-%02d %02d:%02d:%02d", 2025,8,20,22,15,0);
return String(buf);
}
// Log to SD
void logToSD(String line) {
File f = SD.open("/data.csv", FILE_APPEND);
if (f) { f.println(line); f.close(); Serial.println("Logged to SD"); }
else Serial.println("SD write error");
}
// MQTT Reconnect
void reconnectMQTT() {
while (!client.connected()) {
Serial.print("Connecting to MQTT...");
if (client.connect(patientID)) Serial.println("Connected");
else { Serial.print("Failed, rc="); Serial.print(client.state()); Serial.println(" try again in 2s"); delay(2000); }
}
}
// WiFi Init
void initWiFi() {
WiFi.begin(ssid, password);
Serial.print("Connecting to WiFi");
while (WiFi.status() != WL_CONNECTED) {
Serial.print(".");
delay(500);
}
Serial.println("\nWiFi connected");
}
// ThingSpeak
void sendToThingSpeak(int hr, int spo2, int sys, int dia, float temp, float weight, int motion, int rr, String condition) {
if (WiFi.status() == WL_CONNECTED) {
HTTPClient http;
http.begin(thingspeakServer);
http.addHeader("Content-Type", "application/x-www-form-urlencoded");
String post = "api_key=" + apiKey +
"&field1=" + String(hr) +
"&field2=" + String(spo2) +
"&field3=" + String(sys) +
"&field4=" + String(dia) +
"&field5=" + String(temp, 2) +
"&field6=" + String(weight, 2) +
"&field7=" + String(motion) +
"&field8=" + String(rr) +
"&field9=" + condition;
int code = http.POST(post);
if (code > 0) Serial.println("Data sent to ThingSpeak successfully");
else Serial.println("Error sending data to ThingSpeak");
http.end();
} else Serial.println("WiFi not connected");
}
void setup() {
Serial.begin(115200);
// OLED
display.begin(SSD1306_SWITCHCAPVCC, 0x3C);
display.clearDisplay(); display.display();
// Temperature
sensors.begin();
// Scale
scale.begin(DOUT, CLK);
scale.set_scale(2280.f);
scale.tare();
// Pins
pinMode(BUZZER_PIN, OUTPUT);
pinMode(LED_PIN, OUTPUT);
pinMode(MOTION_PIN, INPUT);
// SD card
SPI.begin();
if (!SD.begin(SD_CS)) Serial.println("SD init failed!");
else if (!SD.exists("/data.csv")) {
File f = SD.open("/data.csv", FILE_WRITE);
if (f) f.println("Timestamp,PatientID,HR,SpO2,Sys,Dia,Temp,Weight,Motions,RR,Status");
f.close();
}
// WiFi + MQTT
initWiFi();
client.setServer(mqttServer, mqttPort);
// Accelerometer
if(!accel.begin()) {
Serial.println("ADXL345 not found!");
while(1);
}
accel.setRange(ADXL345_RANGE_2_G);
startTime = millis();
}
// Loop
void loop() {
if (!client.connected()) reconnectMQTT();
client.loop();
unsigned long currentTime = millis();
// Simulated HR/SpO2/BP for now (replace HR with real ECG HR later)
int spo2 = map(analogRead(SPO2_PIN), 0, 4095, 80, 100);
int sys = map(analogRead(SYS_PIN), 0, 4095, 70, 190);
int dia = map(analogRead(DIA_PIN), 0, 4095, 40, 120);
float temp = sensors.getTempCByIndex(0);
float weight = scale.get_units(5); if(weight<0) weight=0;
bool motion = digitalRead(MOTION_PIN);
// --- ECG + HR calculation ---
ecgRaw = analogRead(ECG_PIN);
ecgVoltage = ecgRaw * (3.3 / 4095.0);
const float THRESHOLD = 1.5; // adjust based on sensor
if(ecgVoltage > THRESHOLD && (currentTime - lastBeatTime) > 300) {
lastBeatTime = currentTime;
beatCount++;
}
int hr = beatCount * 60000 / (currentTime - startTime);
// --- RR calculation from ADXL345 ---
sensors_event_t event;
accel.getEvent(&event);
float z = event.acceleration.z;
const float RR_THRESHOLD = 0.3;
if((z - prevZ) > RR_THRESHOLD && (currentTime - lastBreathTime) > 1000) {
lastBreathTime = currentTime;
breathCount++;
}
prevZ = z;
RR = breathCount * 60000 / (currentTime - startTime);
// --- Condition check ---
String condition = "Stable";
if(hr < 40 || hr > 150 || spo2 < 75 || sys < 60 || dia < 40) condition = "CRITICAL";
else if(hr < 50 || hr > 130 || spo2 < 85 || sys > 180 || dia > 110) condition = "EMERGENCY";
else if(hr < 60 || hr > 100 || spo2 < 92 || sys > 140 || dia > 90 || temp > 37.5) condition = "WARNING";
// --- Alarm ---
if(condition == "CRITICAL") { tone(BUZZER_PIN, 1500); digitalWrite(LED_PIN, HIGH); }
else { noTone(BUZZER_PIN); digitalWrite(LED_PIN, LOW); }
// --- OLED display ---
display.clearDisplay();
display.setCursor(0,0);
display.printf("HR:%d bpm SpO2:%d\nBP:%d/%d T:%.1f\nW:%.1f RR:%d\nStatus:%s", hr, spo2, sys, dia, temp, weight, RR, condition.c_str());
display.display();
// --- MQTT / SD / ThingSpeak ---
String payload = "{\"PatientID\":\"" + String(patientID) + "\"," +
"\"HR\":" + String(hr) + "," +
"\"SpO2\":" + String(spo2) + "," +
"\"Sys\":" + String(sys) + "," +
"\"Dia\":" + String(dia) + "," +
"\"ECG\":" + String(ecgRaw) + "," +
"\"Temp\":" + String(temp,2) + "," +
"\"Weight\":" + String(weight,2) + "," +
"\"Motion\":" + String(motion) + "," +
"\"RR\":" + String(RR) + "," +
"\"Status\":\"" + condition + "\"}";
client.publish(("ICU/patient/" + String(patientID)).c_str(), payload.c_str());
logToSD(getTimestamp() + "," + String(patientID) + "," +
String(hr) + "," + String(spo2) + "," + String(sys) + "," +
String(dia) + "," + String(temp,2) + "," + String(weight,2) + "," +
String(motion) + "," + String(RR) + "," + condition);
sendToThingSpeak(hr, spo2, sys, dia, temp, weight, motion, RR, condition);
delay(2000);
}