Le'Knox Ayenie
Published © GPL3+

NIGHTINGALE ( Remote Patient Monitoring System)

A Remote Patient Monitoring System that gives health workers remote access to patient's vitals without physical contact with patients.

BeginnerFull instructions provided10 hours1,779
NIGHTINGALE ( Remote Patient Monitoring System)

Things used in this project

Hardware components

MAX30102 High-Sensitivity Pulse Oximeter and Heart-Rate Sensor for Wearable Health
Maxim Integrated MAX30102 High-Sensitivity Pulse Oximeter and Heart-Rate Sensor for Wearable Health
×1
Arduino MKR1000
Arduino MKR1000
×1
oled i2c 128x64
×1
Battery, 3.7 V
Battery, 3.7 V
×1

Software apps and online services

Ubidots
Ubidots
Arduino IDE
Arduino IDE
Fritzing

Hand tools and fabrication machines

Soldering iron (generic)
Soldering iron (generic)
Solder Wire, Lead Free
Solder Wire, Lead Free

Story

Read more

Schematics

NIGHTINGALE schematic

This is a schematic diagram. And can be accessed using the Fritzing software

Code

NIGHTINGALE

Arduino
This is the Arduino code for the project. Open with Arduino IDE
/*
THIS PROJECT LOOKS FORWARD TO CAPTURE VITALS OF PATIENT AND UPLOAD TO UBIDOTS PLATFORM.
THE FOLLOWING VITALS ARE FIRST CAPTURED BEFORE LOGGED TO THE CLOUD;

TEMPERATURE
HEARTRATE
SP02
*/






/********************************
 * Libraries included
 *******************************/

#include <SPI.h>
#include <WiFiNINA.h>
#include <avr/dtostrf.h>
#include <Wire.h>
#include "MAX30105.h"             
#include "spo2_algorithm.h"
#include "heartRate.h"
#include <Adafruit_GFX.h>        
#include <Adafruit_SSD1306.h>

#define OLED_RESET    -1 // Reset pin # (or -1 if sharing Arduino reset pin)
#define MAX_BRIGHTNESS 255


/********************************
 * Constants and objects
 *******************************/
Adafruit_SSD1306 display(OLED_RESET); //Declaring the display name (display)

MAX30105 particleSensor;              //Declaring the Pulse sensor ( particleSensor)



uint32_t irBuffer[100]; //infrared LED sensor data
uint32_t redBuffer[100];  //red LED sensor data


int32_t bufferLength; //data length
int32_t spo2; //SPO2 value
int8_t validSPO2; //indicator to show if the SPO2 calculation is valid
int32_t heartRate; //heart rate value
int8_t validHeartRate; //indicator to show if the heart rate calculation is valid


float temperature;

//COMMENT BOTH LINES IF YOU DON'T WANT TO ADD LEDS TO INDICATE HEARTBEATS BY BLINKING
byte pulseLED = 11; //Must be on PWM pin
byte readLED = 13; //Blinks with each data read


#define DEVICE_LABEL "patient1"                     //Device Label should match that of Ubidots (CASE SENSITIVE: small letters recommended with no white space)
#define TOKEN "BBFF-fqHRKYVPndnLKJtta6PtBUkNyH0NkR"  //Token can be accessed from API documentation on Ubidots
char const * VARIABLE_LABEL_1 = "spO2";             //Variable to store first sensor reading (SpO2) "O" is a letter not a number of
char const * VARIABLE_LABEL_2 = "heartrate";             //Variable to store second sensor reading (Heart Rate)
char const * VARIABLE_LABEL_3 = "temperature";             //Variable to store third sensor reading (Temperature)


  char payload[700];      //combines all sensor readings to be sent to Ubidots platform
  char str_val_1[30];     
  char str_val_2[30];
  char str_val_3[30];
  





//char const *SERVER="industrial.api.ubidots.com";
//Replace the above line if you are an Educational user 
char const *SERVER="things.ubidots.com";

const int HTTPPORT= 443;
char const *AGENT="Arduino MKR Wifi 1010"; 
char const *HTTP_VERSION = " HTTP/1.1\r\n";
char const *VERSION ="1.0";
char const *PATH= "/api/v1.6/devices/";

char const * SSID_NAME = "Turbo"; // Put here your SSID name
char const * SSID_PASS = "HONEYHIVE"; // Put here your password

int status = WL_IDLE_STATUS;

WiFiSSLClient client;

/********************************
 * Auxiliar Functions
 *******************************/

void printWiFiStatus() {
  // print the SSID of the network you're attached to:
  Serial.print("SSID: ");
  Serial.println(WiFi.SSID());
  // print your board's IP address:
  IPAddress ip = WiFi.localIP();
  Serial.print("IP Address: ");
  Serial.println(ip);
  // print the received signal strength:
  long rssi = WiFi.RSSI();
  Serial.print("signal strength (RSSI):");
  Serial.print(rssi);
  Serial.println(" dBm");
}

void getResponseServer() { 
  Serial.println(F("\nUbidots' Server response:\n"));
  while (client.available()) {
    char c = client.read();
    Serial.print(c); // Uncomment this line to visualize the response on the Serial Monitor
  }
}

void waitServer() {
  int timeout = 0;
  while (!client.available() && timeout < 5000) {
    timeout++;
    delay(1);
    if (timeout >= 5000) {
      Serial.println(F("Error, max timeout reached"));
      break;
    }
  }
}

//FUNCTION TO UPLOAD DATA TO CLOUD
void sendData(char* payload) {
  int contentLength = strlen(payload);

  /* Connecting the client */
  if (client.connect(SERVER, HTTPPORT)) {
    Serial.println("connected to server");
    
    client.print(F("POST "));
    client.print(PATH);    
    client.print(DEVICE_LABEL); 
    client.print(F("/"));
    client.print(HTTP_VERSION);
    client.print(F("Host: "));
    client.print(SERVER);
    client.print(F("\r\n"));   
    client.print(F("User-Agent: "));
    client.print(AGENT);
    client.print(F("\r\n"));
    client.print(F("X-Auth-Token: "));
    client.print(TOKEN);
    client.print(F("\r\n"));
    client.print(F("Connection: close\r\n"));
    client.print(F("Content-Type: application/json\r\n"));
    client.print(F("Content-Length: "));
    client.print(contentLength);
    client.print(F("\r\n\r\n"));
    client.print(payload);
    client.print(F("\r\n"));
    
    Serial.print(F("POST "));
    Serial.print(PATH);    
    Serial.print(DEVICE_LABEL); 
    Serial.print(F("/"));
    Serial.print(HTTP_VERSION);
    Serial.print(F("Host: "));
    Serial.print(SERVER);
    Serial.print(F("\r\n"));
    Serial.print(F("User-Agent: "));
    Serial.print(AGENT);
    Serial.print(F("\r\n"));
    Serial.print(F("X-Auth-Token: "));
    Serial.print(TOKEN);
    Serial.print(F("\r\n"));
    Serial.print(F("Connection: close\r\n"));
    Serial.print(F("Content-Type: application/json\r\n"));
    Serial.print(F("Content-Length: "));
    Serial.print(contentLength);
    Serial.print(F("\r\n\r\n"));
    Serial.print(payload);
    Serial.print(F("\r\n"));
    
    waitServer();
    getResponseServer();
  }

    /* Disconnecting the client */
  client.stop();
}

/********************************
 * Main Functions
 *******************************/

void setup() {
  //Initialize serial and wait for port to open:
  Serial.begin(115200);

  pinMode(pulseLED, OUTPUT);
  pinMode(readLED, OUTPUT);

  

  

  byte ledBrightness = 60; //Options: 0=Off to 255=50mA
  byte sampleAverage = 4; //Options: 1, 2, 4, 8, 16, 32
  byte ledMode = 2; //Options: 1 = Red only, 2 = Red + IR, 3 = Red + IR + Green
  byte sampleRate = 100; //Options: 50, 100, 200, 400, 800, 1000, 1600, 3200
  int pulseWidth = 411; //Options: 69, 118, 215, 411
  int adcRange = 4096; //Options: 2048, 4096, 8192, 16384

display.begin(SSD1306_SWITCHCAPVCC, 0x3C); //Start the OLED display
  display.clearDisplay();                                   //Clear the display
  display.display();
  delay(3000);


// Initialize sensor
  particleSensor.begin(Wire, I2C_SPEED_FAST);
  particleSensor.setup(ledBrightness, sampleAverage, ledMode, sampleRate, pulseWidth, adcRange); //Configure sensor with these settings
  particleSensor.setPulseAmplitudeRed(0x0A); //Turn Red LED to low to indicate sensor is running
 particleSensor.enableDIETEMPRDY(); //Enable the temp ready interrupt. This is required.

Serial.println("Please wear the device.");

 
  
 
  
  // check for the WiFi module:
  if (WiFi.status() == WL_NO_MODULE) {
    Serial.println("Communication with WiFi module failed!");
    // don't continue
    display.clearDisplay();
     display.setTextSize(1);                    
     display.setTextColor(WHITE);             
     display.setCursor(10,0);                
     display.println("Failed to connect"); 
     display.setCursor(10,10);
     display.println("to WiFi");
     display.display();
    while (true);
  }
  
  // attempt to connect to WiFi network:
  while (status != WL_CONNECTED) {
    Serial.print("Attempting to connect to SSID: ");
    Serial.println(SSID_NAME);
     display.clearDisplay();
     display.setTextSize(1);                    
     display.setTextColor(WHITE);             
     display.setCursor(10,0);                
     display.println("Connecting to"); 
     display.setCursor(10,10);
     display.println(SSID_NAME);
     display.display();
    
    // Connect to WPA/WPA2 network. Change this line if using open or WEP network:
    status = WiFi.begin(SSID_NAME, SSID_PASS);
    // wait 10 seconds for connection:
    delay(10000);
  }
  Serial.println("Connected to wifi");
  printWiFiStatus();
     display.clearDisplay();
     display.setTextSize(1);                    
     display.setTextColor(WHITE);             
     display.setCursor(10,0);                
     display.println("Connected to"); 
     display.setCursor(10,10);
     display.println(SSID_NAME);
     display.display();
}

void loop(){

long irValue = particleSensor.getIR();

if(irValue > 7000){ //IF DEVICE IS ATTACHED TO WEARER


//SPO2 and HEART RATE 
bufferLength = 100; //buffer length of 100 stores 4 seconds of samples running at 25sps

  //read the first 100 samples, and determine the signal range
  for (byte i = 0 ; i < bufferLength ; i++)
  {
    while (particleSensor.available() == false) //do we have new data?
      particleSensor.check(); //Check the sensor for new data

    redBuffer[i] = particleSensor.getRed();
    irBuffer[i] = particleSensor.getIR();
    particleSensor.nextSample(); //We're finished with this sample so move to next sample

    Serial.print(F("red="));
    Serial.print(redBuffer[i], DEC);
    Serial.print(F(", ir="));
    Serial.println(irBuffer[i], DEC);
    
  }
  

  //calculate heart rate and SpO2 after first 100 samples (first 4 seconds of samples)
  maxim_heart_rate_and_oxygen_saturation(irBuffer, bufferLength, redBuffer, &spo2, &validSPO2, &heartRate, &validHeartRate);

 
  
  //Continuously taking samples from MAX30102.  Heart rate and SpO2 are calculated every 1 second
  
    //dumping the first 25 sets of samples in the memory and shift the last 75 sets of samples to the top
    for (byte i = 25; i < 100; i++)
    {
      redBuffer[i - 25] = redBuffer[i];
      irBuffer[i - 25] = irBuffer[i];
    }
    
  
    //take 25 sets of samples before calculating the heart rate.
    for (byte i = 75; i < 100; i++)
    {
      while (particleSensor.available() == false) //do we have new data?
        particleSensor.check(); //Check the sensor for new data

      digitalWrite(readLED, !digitalRead(readLED)); //Blink onboard LED with every data read

      redBuffer[i] = particleSensor.getRed();
      irBuffer[i] = particleSensor.getIR();
      particleSensor.nextSample(); //We're finished with this sample so move to next sample

      //send samples and calculation result to terminal program through UART
      Serial.print(F("red="));
      Serial.print(redBuffer[i], DEC);
      Serial.print(F(", ir="));
      Serial.print(irBuffer[i], DEC);

      Serial.print(F(", HR="));
      Serial.print(heartRate, DEC);

      Serial.print(F(", HRvalid="));
      Serial.print(validHeartRate, DEC);

      Serial.print(F(", SPO2="));
      Serial.print(spo2, DEC);

      Serial.print(F(", SPO2Valid="));
      Serial.println(validSPO2, DEC);
      


     
    

    //After gathering 25 new samples recalculate HR and SP02
    maxim_heart_rate_and_oxygen_saturation(irBuffer, bufferLength, redBuffer, &spo2, &validSPO2, &heartRate, &validHeartRate);
  }


//TEMPERATURE
  float temperature = particleSensor.readTemperature();

  Serial.print("temperatureC=");
  Serial.print(temperature, 4);

float   temperatureF = particleSensor.readTemperatureF(); //Because I am a bad global citizen

  Serial.print(" temperatureF=");
  Serial.print(temperatureF, 4);

  Serial.println();




 

 


//DISPLAY THE VITALS ON THE OLED SCREEN
     display.clearDisplay();
     display.setTextSize(1);                    
     display.setTextColor(WHITE);             
     display.setCursor(0,0);                
     display.println("Sp02 :"); 
     display.setCursor(40,0);
     display.println(spo2);
     display.setCursor(73,0);                
     display.println("HR :"); 
     display.setCursor(99,0);
     display.println(heartRate);    
     display.setCursor(0,20);                
     display.println("Temp :"); 
     display.setCursor(40,20);
     display.println(temperature);  
     display.display();

  
  
  /*4 is the total length of number,maximum number accepted is 99.99*/
  
  dtostrf(spo2, 4, 2, str_val_1);
  dtostrf(heartRate, 4, 2, str_val_2);
  dtostrf(temperature, 4, 2, str_val_3);
  
  
  //The payload which contains all the sensor readings will be sent to the Ubidots platform in a JSON format, all at once
  sprintf(payload, "%s","");
  sprintf(payload, "{\"");
  sprintf(payload, "%s%s\":%s", payload, VARIABLE_LABEL_1, str_val_1);
  sprintf(payload, "%s,\"%s\":%s", payload, VARIABLE_LABEL_2, str_val_2);
  sprintf(payload, "%s,\"%s\":%s", payload, VARIABLE_LABEL_3, str_val_3);
  sprintf(payload, "%s}", payload);

  //Send the payload to Ubidots
  sendData(payload);


  
  //delay(5000);

  
  
 }
 
if (irValue < 7000){       //If device is removed it informs the user to put on the device
     
     display.clearDisplay();
     display.setTextSize(1);                    
     display.setTextColor(WHITE);             
     display.setCursor(30,5);                
     display.println("Please wear "); 
     display.setCursor(30,15);
     display.println("the device ");  
     display.display();
     
 }
}

Credits

Le'Knox Ayenie

Le'Knox Ayenie

2 projects • 0 followers

Comments