IoT IV Bag Monitoring & Alert System for Healthcare Facilities
Prevent critical medication errors with real-time IV fluid level monitoring using load cells and ESP32, sending nurse station alerts via WiFi.
Required Hardware & Component Checklist
Curated components verified for this project. Check the items you need, adjust quantities, and add straight to cart:
IoT IV Bag Monitoring & Alert System for Healthcare Facilities
Educational Notes
This project is designed to be accessible to students from Class 4 to Masters level, with complexity scalable to match different age groups and skill levels.
Learning Objectives:
- Understand basic electronics and circuitry principles
- Learn sensor applications and data collection techniques
- Develop problem-solving skills through hands-on building and troubleshooting
- Apply programming concepts to control hardware and process data
- Connect projects to real-world Nepalese contexts and challenges
Adaptability:
- For younger students (Class 4-8): Focus on assembling pre-built circuits, observing results, and understanding basic concepts
- For intermediate students (Class 9-12): Modify code, experiment with parameters, and explore underlying principles
- For advanced students (Undergraduate/Masters): Optimize designs, add features, conduct research extensions, and analyze performance
Safety Note: Always supervise younger students when working with electricity, heat, or moving parts. Medical devices require professional validation before clinical use.
Eliminate the risk of air embolism and medication interruption with continuous IV bag weight monitoring. The system uses a precision HX711 24-bit ADC with a 5kg load cell to track fluid levels in real-time, alerting nursing staff via WiFi when bags reach critical thresholds.
Hardware Bill of Materials (In Stock at Ghumti Pasal):
- Controller: ESP32 Dev Module (WiFi + Bluetooth)
- Weight Sensor: Load Cell 5kg + HX711 24-Bit ADC Module
- Display: 0.96" OLED I2C 128x64 (Bedside Status)
- Alert: Active Buzzer + RGB LED (Visual/Audible)
- Mounting: IV Pole Clamp + 3D Printed Load Cell Housing
- Power: 5V 2A USB Adapter + LiPo Backup (Power Fail)
- Enclosure: Waterproof Project Box IP65
Circuit Pinout & Wiring Connections:
| Component / Sensor Pin | ESP32 GPIO Pin | Function / Description |
|---|---|---|
| HX711 VCC / GND | 3.3V / GND | ADC Power |
| HX711 DT (Data) | GPIO 21 | Serial Data Out |
| HX711 SCK (Clock) | GPIO 22 | Serial Clock In |
| OLED SDA / SCL | GPIO 4 / 5 (I2C) | Display Communication |
| Buzzer (+) | GPIO 18 | Audible Alert |
| RGB LED R/G/B | GPIO 19/23/13 | Status: Green/Amber/Red |
| LiPo Battery | 3.7V -> 5V Boost | Backup Power |
Firmware Source Code (Arduino/ESP32 C++)
#include <HX711.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include <WiFi.h>
#include <HTTPClient.h>
#include <ArduinoJson.h>
// WiFi & Server Config
const char* ssid = "HOSPITAL_WIFI";
const char* password = "WIFI_PASSWORD";
const char* serverUrl = "http://nurse-station.local/api/iv-alert";
// Hardware Pins
#define HX711_DT 21
#define HX711_SCK 22
#define OLED_SDA 4
#define OLED_SCL 5
#define BUZZER 18
#define LED_R 19
#define LED_G 23
#define LED_B 13
// IV Bag Parameters
const float BAG_CAPACITY_ML = 500.0; // Standard IV bag
const float EMPTY_BAG_WEIGHT_G = 50.0; // Empty bag + tubing
const float FLUID_DENSITY = 1.0; // g/ml (saline)
const float CRITICAL_THRESHOLD_ML = 50.0; // Alert at 50ml remaining
const float WARNING_THRESHOLD_ML = 100.0; // Warning at 100ml
HX711 scale;
Adafruit_SSD1306 display(128, 64, &Wire, -1);
float calibration_factor = -420000; // Calibrate with known weight
unsigned long lastAlert = 0;
const unsigned long ALERT_INTERVAL = 60000; // 1 min between alerts
bool alertSent = false;
void setup() {
Serial.begin(115200);
// GPIO Setup
pinMode(BUZZER, OUTPUT);
pinMode(LED_R, OUTPUT);
pinMode(LED_G, OUTPUT);
pinMode(LED_B, OUTPUT);
setLED(0, 1, 0); // Green = Initializing
// Display
Wire.begin(OLED_SDA, OLED_SCL);
if (!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) {
Serial.println(F("OLED failed"));
}
display.clearDisplay();
display.setTextSize(1);
display.setTextColor(WHITE);
// Load Cell
scale.begin(HX711_DT, HX711_SCK);
scale.set_scale(calibration_factor);
scale.tare(); // Zero with empty hook
// WiFi
WiFi.begin(ssid, password);
displayPrint("Connecting WiFi...");
while (WiFi.status() != WL_CONNECTED) {
delay(500);
}
displayPrint("WiFi Connected\nCalibrating...");
delay(2000);
setLED(0, 1, 0); // Green = Ready
}
void loop() {
if (scale.is_ready()) {
float weight_g = scale.get_units(10); // Average 10 readings
float fluid_ml = (weight_g - EMPTY_BAG_WEIGHT_G) / FLUID_DENSITY;
float remaining_pct = (fluid_ml / BAG_CAPACITY_ML) * 100;
// Clamp values
fluid_ml = max(0.0, min(BAG_CAPACITY_ML, fluid_ml));
remaining_pct = max(0.0, min(100.0, remaining_pct));
updateDisplay(fluid_ml, remaining_pct);
checkAlerts(fluid_ml, remaining_pct);
sendTelemetry(fluid_ml, remaining_pct);
}
delay(1000);
}
void updateDisplay(float ml, float pct) {
display.clearDisplay();
display.setCursor(0, 0);
display.print("IV MONITOR");
display.setCursor(0, 16);
display.printf("Remaining: %.0f ml (%.0f%%)", ml, pct);
display.setCursor(0, 32);
if (ml <= CRITICAL_THRESHOLD_ML) {
display.print("*** CRITICAL ***");
setLED(1, 0, 0);
} else if (ml <= WARNING_THRESHOLD_ML) {
display.print("!! WARNING !!");
setLED(1, 1, 0);
} else {
display.print("Status: Normal");
setLED(0, 1, 0);
}
display.setCursor(0, 48);
display.printf("Rate: %.1f ml/hr", calculateFlowRate());
display.display();
}
void checkAlerts(float ml, float pct) {
unsigned long now = millis();
if (ml <= CRITICAL_THRESHOLD_ML && !alertSent && (now - lastAlert > ALERT_INTERVAL)) {
triggerAlert("CRITICAL", ml, "IV bag nearly empty - IMMEDIATE ACTION REQUIRED");
alertSent = true;
lastAlert = now;
} else if (ml <= WARNING_THRESHOLD_ML && !alertSent && (now - lastAlert > ALERT_INTERVAL)) {
triggerAlert("WARNING", ml, "IV bag low - Prepare replacement");
alertSent = true;
lastAlert = now;
} else if (ml > WARNING_THRESHOLD_ML) {
alertSent = false; // Reset when bag replaced
}
}
void triggerAlert(String level, float ml, String message) {
// Local alert
for (int i = 0; i < 5; i++) {
digitalWrite(BUZZER, HIGH);
delay(100);
digitalWrite(BUZZER, LOW);
delay(100);
}
// Network alert
if (WiFi.status() == WL_CONNECTED) {
HTTPClient http;
http.begin(serverUrl);
http.addHeader("Content-Type", "application/json");
StaticJsonDocument<200> doc;
doc["bed_id"] = "BED_001";
doc["level"] = level;
doc["remaining_ml"] = ml;
doc["message"] = message;
String json;
serializeJson(doc, json);
int httpCode = http.POST(json);
http.end();
}
}
float calculateFlowRate() {
static float last_ml = -1;
static unsigned long last_time = 0;
float current_ml = scale.get_units();
if (last_ml < 0) { last_ml = current_ml; last_time = millis(); return 0; }
float delta_ml = last_ml - current_ml;
unsigned long delta_ms = millis() - last_time;
float rate = (delta_ml / delta_ms) * 3600000.0; // ml/hr
last_ml = current_ml;
last_time = millis();
return max(0, rate);
}
void setLED(bool r, bool g, bool b) {
digitalWrite(LED_R, r);
digitalWrite(LED_G, g);
digitalWrite(LED_B, b);
}
void displayPrint(String text) {
display.clearDisplay();
display.setCursor(0, 0);
display.print(text);
display.display();
}
Calibration Procedure
- Hang empty IV bag hook on load cell
- Power on system - it auto-tares
- Hang known weight (500ml water bottle = 500g)
- Adjust
calibration_factoruntil display reads 500ml - Save factor to EEPROM for persistence
Alert Escalation Levels
| Level | Fluid Remaining | Local Alert | Network Alert | Escalation |
|---|---|---|---|---|
| Info | >100ml | Green LED | Periodic telemetry | None |
| Warning | 50-100ml | Amber LED + Slow beep | Nurse station notification | 5 min repeat |
| Critical | <50ml | Red LED + Fast beep | Nurse station + SMS + Call | 1 min repeat |
Nepal Healthcare Context
- Government Hospitals: High patient-to-nurse ratio makes automation critical
- Remote Health Posts: Solar-powered with LoRa backup for connectivity
- Home Care: Family alerts for home-based IV therapy patients
- Emergency Response: Ambulance integration for transport monitoring
Regulatory Compliance (Nepal)
- MoHP Guidelines: Non-invasive monitoring approved for pilot programs
- Data Privacy: Patient data encrypted, local storage only
- Calibration: Monthly verification with certified weights required
- Backup Power: Minimum 4-hour battery backup mandatory
Cost Breakdown (NPR)
| Component | Est. Price | Source |
|---|---|---|
| ESP32 Dev Module | 1,200 | Ghumti Pasal |
| 5kg Load Cell + HX711 | 1,800 | Ghumti Pasal |
| 0.96" OLED | 650 | Ghumti Pasal |
| Buzzer + RGB LED | 200 | Ghumti Pasal |
| IV Pole Mount (3D Print) | 500 | Local maker |
| IP65 Enclosure | 800 | Ghumti Pasal |
| LiPo + Boost Module | 1,200 | Ghumti Pasal |
| Total | ~6,350 | Per bed |
Future Enhancements
- Multi-bag Support: Monitor primary + secondary bags simultaneously
- Drug Library: Integrate with hospital formulary for dose calculations
- EHR Integration: HL7/FHIR compatibility for electronic health records
- Predictive Analytics: ML-based flow rate prediction and anomaly detection
- Nurse Call Integration: Direct integration with existing nurse call systems
