Medi-Sense
Medi-Sense is a state-of-the-art remote healthcare monitoring device that employs the use of various sensors in order to monitor various healthcare parameters of the user. Medi-Sense is more than just a technological endeavor and is about making healthcare more accessible, convenient, and responsive to the evolving needs of society. This project combines the power of the Max30100 pulse oximeter sensor, AD8232 ECG sensor, MLX90614 temperature sensor, Raspberry Pi, and the Arduino Nano 33 IoT.
Supplies
MAX30100 Pulse Oximeter Sensor :- https://robu.in/product/max30100-pulse-oximeter-heart-rate-sensor-module/
MLX90614 ESF Non-Contact Human Body Infrared Temperature Measurement Module :- https://robu.in/product/mlx90614-esf-non-contact-human-body-infrared-temperature-measurement-module/
AD8232 ECG Sensor module :- https://robu.in/product/ecg-module-ad8232-ecg-measurement-pulse-heart-ecg-monitoring-sensor-module-kit/
Tynor Elbow support band :- https://www.1mg.com/otc/tynor-e-10-tennis-elbow-support-medium-otc345659?wpsrc=Google+Organic+Search
Finger Bands :- https://www.amazon.in/LP-Long-Finger-Band-Free/dp/B01BDLYZ6
Jumper Wires :- https://www.amazon.in/ApTechDeals-Jumper-Female-breadboard-jumper/dp/B074J9CPV3
Make the Required Connections
- MAX30100 SENSOR :
- VCC - 3.3 V
- GND - GND of Arduino Nano 33 IOT board
- SCL - A5
- SDA - A4
- MLX90614 TEMPERATURE SENSOR :
- VCC - 3.3V
- GND - GND
- SCL - SCL
- SDA - SDA
- AD8232 ECG SENSOR MODULE :
- 3.3V - 3.3V of Arduino Nano 33 IOT
- GND - GND
- LO(-) - D11
- LO(+) - D10
- OUTPUT - A0
You can refer to the circuit diagram for the connections. Make sure the connections are tight. Consider making a PCB for same.
Setting Up Firebase
You can refer to this YouTube video link for setting up a project with Firebase:-
https://www.youtube.com/watch?v=4B85lFGTL-U
After setting up the project you would need your database credentials in the Arduino Ide code.
Follow the below mentioned steps for finding the credentials:-
- Go to firebase console and open the project that you have just created.
- Click on the Project Overview option present on the left side of the dashboard and then click on project settings. Scroll down and you will get the database URL. For the database secret key go onto the service account tab present under the project settings option and click on database secrets. You will get your Firebase Secret key from there.
Setting Up an IFTTT Trigger
You need to create a trigger on IFTTT for storing the data on the Excel sheets. You can refer to the below-mentioned Youtube video to learn how to create a trigger for storing sensor data in Excel sheets.
https://www.youtube.com/watch?v=w-iBewJGY2s
You would need your IFTTT event name and key in the Arduino IDE code.
For finding the key click on the applet that you just created. After the applet details open up, click on the webhook icon present on the left side. After the webhook page opens up click on the documentation button and your key will be displayed to you.
The Code : Arduino Nano 33 IOT
In the Arduino Ide write the code for saving the data in the Excel sheet and updating the values in the real-time database of Firebase. The IFTTT webhook service has been used for saving the data on Excel sheets. You can consider the below-mentioned link for more knowledge about this webhook :-
https://randomnerdtutorials.com/esp32-esp8266-publish-sensor-readings-to-google-sheets/
You can refer to the Basic example of Firebase_Arduino_WiFiNINA.h library to get more knowledge about updating the Firebase Real-time data values using Arduino IDE.
Here is the Arduino Ide Code:-
Make sure you add your Wifi and Firebase credentials
/*
Description: The code for storing sensor data in excel sheets and updating
the firebase reatime database.
Hardware: Arduino Nano 33 IOT, Max30100 sensor, MLX90614 Temperature sensor, AD8232 ECG sensor
Note:
=> Libraries required
- WifiNINA by Arduino
- Wire.h
- MAX30100_PulseOximeter.h
- Adafruit_MLX90614.h
- Firebase_Arduino_WiFiNINA.h
Circuit Connections:
MAX30100 SENSOR :
VCC - 3.3 V
GND - GND of Arduino Nano 33 IOT board
SCL - A5
SDA - A4
MLX90614 TEMPERATURE SENSOR :
VCC - 3.3V
GND - GND
SCL - SCL
SDA - SDA
AD8232 ECG SENSOR MODULE :
3.3V - 3.3V of Arduino Nano 33 IOT
GND - GND
LO(-) - D11
LO(+) - D10
OUTPUT - A0
Note:
Make sure the connections are tight. Consider making a PCB for same.
*/
//Including the required libraries
#include <WiFiNINA.h>
#include <Wire.h>
#include "MAX30100_PulseOximeter.h"
#include <Adafruit_MLX90614.h>
#include "Firebase_Arduino_WiFiNINA.h"
//Add your database credentials
#define DATABASE_URL "" //<databaseName>.firebaseio.com or <databaseName>.<region>.firebasedatabase.app
#define DATABASE_SECRET ""
//Add your Wifi Credentials
#define WIFI_SSID ""
#define WIFI_PASSWORD ""
// Define Firebase data object
FirebaseData fbdo;
#define REPORTING_PERIOD_MS 1000
Adafruit_MLX90614 mlx = Adafruit_MLX90614();
PulseOximeter pox;
uint32_t tsLastReport = 0;
void onBeatDetected() {
Serial.println("Beat!");
}
// Ifttt settings
const char* server = "maker.ifttt.com";
String eventName = "";//Write your event name here
String IFTTT_Key = "";//Write your IFTTT key here
float value1 = 0.0;
float value2 = 0.0;
float value3 = 0.0;
String path = "/ ";//Write the name of the Realtime datbase field after /
void setup() {
Serial.begin(9600);
Serial.print("Connecting to Wi-Fi");
int status = WL_IDLE_STATUS;
while (status != WL_CONNECTED) {
status = WiFi.begin(WIFI_SSID, WIFI_PASSWORD);
Serial.print(".");
delay(100);
}
Serial.println();
Serial.print("Connected with IP: ");
Serial.println(WiFi.localIP());
Serial.println();
// Provide the authentication data
Firebase.begin(DATABASE_URL, DATABASE_SECRET, WIFI_SSID, WIFI_PASSWORD);
Firebase.reconnectWiFi(true);
Serial.println("Setting Sensor Data");
// Initializing Pulse Oximeter sensor
Serial.println();
Serial.print("Initializing pulse oximeter..");
// Initialize the PulseOximeter instance
// Failures are generally due to an improper I2C wiring, missing power supply
// or wrong target chip
if (!pox.begin()) {
Serial.println("FAILED");
for (;;) {
}
} else {
Serial.println("SUCCESS");
}
pox.setIRLedCurrent(MAX30100_LED_CURR_7_6MA);
// Initializing the MLX90614 temperature sensor
Wire.begin();
mlx.begin();
pinMode(10, INPUT); // Setup for leads off detection LO +
pinMode(11, INPUT); // Setup for leads off detection LO -
}
void sendDataToIFTTT() {
// Prepare the URL with event name and values
String url = "/trigger/" + eventName + "/with/key/" + IFTTT_Key +
"?value1=" + String(value1) + "&value2=" + String(value2) +
"&value3=" + String(value3);
Serial.println("Sending data to IFTTT: " + url);
// Create a WiFiClient object
WiFiClient client;
// Connect to the server
if (client.connect(server, 80)) {
// Send an HTTP request
client.print("GET " + url + " HTTP/1.1\r\n");
client.print("Host: " + String(server) + "\r\n");
client.print("Connection: close\r\n\r\n");
// Read and print the response
while (client.connected()) {
if (client.available()) {
String line = client.readStringUntil('\n');
Serial.println(line);
}
}
// Disconnect from the server
client.stop();
} else {
Serial.println("Failed to connect to the server");
}
}
void loop() {
// Read sensor values
// Make sure to call update as fast as possible
pox.update();
if (millis() - tsLastReport > REPORTING_PERIOD_MS) {
// Read heart rate and SpO2 values
float heartRate = pox.getHeartRate();
float SpO2Value = pox.getSpO2();
float ecg=analogRead(A0);
if (heartRate > 0 && SpO2Value > 0) {
Serial.print("Heart rate: ");
Serial.print(heartRate);
Serial.print(" bpm / SpO2: ");
Serial.print(SpO2Value);
Serial.println("%");
value2 = static_cast<float>(heartRate);
value3 = static_cast<float>(SpO2Value);
//Measuring Temp
value1=static_cast<double>(mlx.readAmbientTempC());
// Send data to IFTTT
sendDataToIFTTT();
// Sending data to Firebase
if (Firebase.setFloat(fbdo, path + "/Heart", value2)) {
Serial.println("Heart rate uploaded to Firebase");
} else {
Serial.println("Error uploading Heart rate to Firebase: " + fbdo.errorReason());
}
if (Firebase.setFloat(fbdo, path + "/Spo2", value3)) {
Serial.println("SpO2 uploaded to Firebase");
} else {
Serial.println("Error uploading SpO2 to Firebase: " + fbdo.errorReason());
}
if (Firebase.setDouble(fbdo, path + "/Temp", value1)) {
Serial.println("Temp uploaded to Firebase");
} else {
Serial.println("Error uploading SpO2 to Firebase: " + fbdo.errorReason());
}
if (Firebase.setDouble(fbdo, path + "/ECG", ecg)) {
Serial.println("ECG uploaded to Firebase");
} else {
Serial.println("Error uploading ecg to Firebase: " + fbdo.errorReason());
}
} else {
Serial.println("Invalid Heart rate or SpO2 values");
}
tsLastReport = millis();
}
// Continue with other code...
}
Downloads
Appscript Server Code
After getting sensor data to google sheets we need to setup our cloud Firestore. For this, open google firebase. Sign in using the same google account in which you created the sheets. If you are already signed in then your google profile pic will be visible at the top right corner. After signing in click on ‘Go to console’.
Open your project and click on cloud firestore. After that click on create database button.
Choose the database creation mode - go with 'test' for DIY projects. You can change it later by modifying Firebase rules. Then, click 'Next'.
Choose an appropriate location and your firestore database is created.
For finding your project credentials follow the steps given in the below mentioned link.
Consider starting from step 5 as all the previous steps have been already performed.
For writing the app script code open up your Excel sheet and click on the Appscript option present under the extensions option.
Write this code in the code.gs file:-
function doGet(e) {
Logger.log( JSON.stringify(e) ); // view parameters
var result = 'Ok'; // assume success
if (e.parameter == 'undefined') {
result = 'No Parameters';
}
else {
var sheet_id = ' '; // Spreadsheet ID
var sheet = SpreadsheetApp.openById(sheet_id).getActiveSheet(); // get Active sheet
var newRow = sheet.getLastRow() + 1;
var rowData = [];
for (var param in e.parameter) {
Logger.log('In for loop, param=' + param);
var value = stripQuotes(e.parameter[param]);
Logger.log(param + ':' + e.parameter[param]);
switch (param) {
case 'Temperature': //Parameter
rowData[0] = value; //Value in column A
result = 'Written on column A';
break;
case 'Humidity': //Parameter
rowData[1] = value; //Value in column B
result += ' ,Written on column B';
break;
default:
result = "unsupported parameter";
}
}
Logger.log(JSON.stringify(rowData));
// Write new row below
var newRange = sheet.getRange(newRow, 1, 1, rowData.length);
newRange.setValues([rowData]);
}
// Return result of operation
firestore();
return ContentService.createTextOutput(result);
}
/**
* Remove leading and trailing single or double quotes
*/
function stripQuotes( value ) {
return value.replace(/^["']|['"]$/g, "");
}
function firestore(){
//add firebase library to script before running the code
//get firebase project credentials from GCP and add data from the created json key below
const email = "firebase-adminsdk-4978y@sensordata-5b22b.iam.gserviceaccount.com";
const key = " ";
var firestore = FirestoreApp.getFirestore (email, key, projectId);
// get document data from ther spreadsheet
var ss = SpreadsheetApp.getActiveSpreadsheet();
//Give the sheetname of your spreadsheet
var sheetname = "IFTTT_Maker_Webhooks_Events";
var sheet = ss.getSheetByName(sheetname);
// get the last row and column in order to define range
var sheetLR = sheet.getLastRow(); // get the last row
var sheetLC = sheet.getLastColumn(); // get the last column
var dataSR = 2; // the first row of data
// define the data range
var sourceRange = sheet.getRange(2,1,sheetLR-dataSR+1,sheetLC);
// get the data
var sourceData = sourceRange.getValues();
// get the number of length of the object in order to establish a loop value
var sourceLen = sourceData.length;
// Loop through the rows
for (var i= sourceLen-1;i<sourceLen;i++){
if (sourceData[i][1] !=='') {
var data = {};
data.timestamp = sourceData[i][0];
data.temperature=sourceData[i][1];
data.spo2 = sourceData[i][2];
data.heartrate=sourceData[i][3];
firestore.createDocument("patientData",data);
}
}
}
You need to write your sheet ID in this code. You can get this link from the excel sheet. The sheet Id is present between the d/ and /edit words in the link visible in the browser.
Create a new file and name it as firebase.html and write the below mentioned code in that file.
<script src="https://www.gstatic.com/firebasejs/10.4.0/firebase-app.js"></script>
<script src="https://www.gstatic.com/firebasejs/10.4.0/firebase-firestore.js"></script>
<script src="https://www.gstatic.com/firebasejs/10.4.0/firebase-SERVICE.js"></script>
Rasberry Pi Code
Rasberry Pi was used for creating a GUI for displaying the data to the user. Open the thony editor on Rasberry Pi OS and crete a new file and write the following code into that file :-
import tkinter as tk
from tkinter import *
from tkinter import ttk
from tkcalendar import Calendar
import firebase_admin
from firebase_admin import credentials, firestore
from datetime import datetime
import pyrebase
import os
import requests
# Initialize Firebase Admin SDK with your JSON key file
cred = credentials.Certificate(" ")
firebase_admin.initialize_app(cred)
# Create a Firestore client
db = firestore.client()
def retrieve_historical_data():
selected_date_str = date_picker.get_date()
selected_time = time_entry.get()
# Parse the selected date string to a datetime object
selected_datetime_str = f"{selected_date_str} {selected_time}"
# Parse the combined date and time string to a datetime object
selected_datetime = datetime.strptime(selected_datetime_str, "%m/%d/%Y %I:%M%p")
# Format the selected date to match the Firestore timestamp format
formatted_date = selected_datetime.strftime("%B %d, %Y at %I:%M%p")
# Access a Firestore collection and retrieve data from documents with a specific timestamp
collection_ref = db.collection("patientData")
# Define a query to filter documents by the timestamp field
query = collection_ref.where("timestamp", "==", formatted_date)
# Retrieve matching documents
docs = query.stream()
data_str = ""
for doc in docs:
data = doc.to_dict()
data_str += f"Data: {data}\n"
if data_str:
result_label.config(text=data_str)
else:
result_label.config(text=f"No documents found for {formatted_date}")
def SubmitData():
config = {
"apiKey": "AIzaSyBeA6bASR9lDbAMQxyWMS6RpIOTjp2Rzjc",
"authDomain": "sensordata-5b22b.firebaseapp.com",
"databaseURL": "https://sensordata-5b22b-default-rtdb.asia-southeast1.firebasedatabase.app",
"storageBucket": "sensordata-5b22b.appspot.com"
}
firebase = pyrebase.initialize_app(config)
db_realtime = firebase.database()
data = db_realtime.child("Patient Data").get().val()
if data:
HeartRate = data.get("Heart")
SPO2 = data.get("Spo2")
Temperature = data.get("Temp")
def retrieve_realtime_data():
def update_realtime_data():
config = {
#Add your firebase credentials here
}
firebase = pyrebase.initialize_app(config)
db_realtime = firebase.database()
data = db_realtime.child("Patient Data").get().val()
if data is not None:
ECG = data.get("ECG")
HeartRate = data.get("Heart")
SPO2 = data.get("Spo2")
Temperature = data.get("Temp")
# Update your Tkinter UI with the new data
data_str = f"Real-time Data:\nHeart Rate: {HeartRate}\nSPO2: {SPO2}\nTemperature: {Temperature}\nECG: {ECG}"
realtime_label.config(text=data_str, font=("Helvetica", 30), bg="black", fg="white")
# Schedule the next update in milliseconds (e.g., every 1000ms for 1 second)
root.after(1000, update_realtime_data)
realtime_window = tk.Toplevel()
realtime_window.title("Real-Time Data")
window_width = 800
window_height = 400
screen_width = realtime_window.winfo_screenwidth()
screen_height = realtime_window.winfo_screenheight()
x = (screen_width - window_width) // 2
y = (screen_height - window_height) // 2
realtime_window.geometry(f"{window_width}x{window_height}+{x}+{y}")
realtime_label = tk.Label(realtime_window, text="", font=("Helvetica", 30), bg="black", fg="white")
#realtime_label.pack()
realtime_label.grid(row=0, column=0, sticky="nsew")
# Use grid layout and sticky to fill the window
submit_button = tk.Button(realtime_window, text="Submit Data", command=SubmitData, font=("Helvetica", 16))
submit_button.grid(row=1, column=0, padx=20, pady=10)
# Configure the grid to expand and fill the available space
realtime_window.grid_rowconfigure(0, weight=1)
realtime_window.grid_columnconfigure(0, weight=1)
update_realtime_data()
root = tk.Tk()
root.title("Firestore Data Retrieval")
#root.attributes('-fullscreen', True)
# Create a date picker widget
style = ttk.Style()
style.configure("TButton", background="black", foreground="white")
style.configure("TLabel", background="black", foreground="white")
style.layout("TButton", [("Button.focus", {"children": [("Button.padding", {"children": [("Button.label", {"side": "left", "expand": 1})]})]})])
# Configure the Calendar widget
date_picker = Calendar(root, date_pattern="mm/dd/yyyy", background="black", foreground="white", headersbackground="black", headersforeground="white", bordercolor="black", normalbackground="black", normalforeground="white", othermonthbackground="black", othermonthforeground="grey")
date_picker.pack(fill='both', expand=True)
date_picker.config(font=('Helvetica', 20))
# Create a time entry field
time_label = tk.Label(root, text="Select a Time (e.g., 12:00AM):", font=('Helvetica', 16))
time_label.pack()
time_entry = tk.Entry(root, font=('Helvetica', 16))
time_entry.pack()
# Create buttons for switching between historical and real-time data
historical_button = tk.Button(root, text="Historical Data", command=retrieve_historical_data, font=('Helvetica', 16))
historical_button.pack()
realtime_button = tk.Button(root, text="Real-Time Data", command=retrieve_realtime_data , font=('Helvetica', 16))
realtime_button.pack()
# Create buttons to show login and signup windows
result_label = tk.Label(root, text="", font=('Helvetica', 16))
result_label.pack()
root.mainloop()
Using this code a GUI display would be created in which the user will have the option to view his real-time as well as his historical data. The historical data will be fetched from the Firestore database and the real-time data will be fetched from the Firebase real time database.
Note :-
In the config section you need to add the firebase credentials. You can get these credentials by clicking on the project settings option present under the project overview section.
Final Designing
To make the project easy to use we need to consider the comfortability factor of the user. A wearable can be made and all the sensors can be integrated into the wearable. For this project, I made use of an elbow band. You will get the link for the elbow band in the supplies step. I fixed the PCB inside the elbow band. Integrating the MLX90614 and MAX30100 sensors into the finger bands makes temperature and pulse oximeter measurements easier and more accurate for the user. The ECG sensor can be fixed at the bottom of the band so that the probes can easily be connected to it.
And the project is ready..