jeffreythecoder
Published © MIT

Cura - A Health Emergency Alert for Seniors We Care

Cura is a wearable device that detects the wearer’s health emergency issue and sends an SMS alert to the wearer’s emergency contact.

IntermediateShowcase (no instructions)1,050
Cura - A Health Emergency Alert for Seniors We Care

Things used in this project

Story

Read more

Schematics

MPU6050 wire

Pulse sensor wire

ESP8266 12E wire

Code

Arduino Uno health emergency detection

Arduino
This code is uploaded to Arduino Uno to detect:
1. Fall measured by accelerometer
2. Unusual heart rate measured by pulse sensor
3. Emergency button pushed

Once one of three above is triggered, the code sends HIGH output to the WiFi module and the LED bulb.

I incorporated fall detection code from https://create.arduino.cc/projecthub/Technovation/health-band-a-smart-assistant-for-the-elderly-0fed12 since it gives accurate result by monitoring a 3-stage change in G.
#define USE_ARDUINO_INTERRUPTS true // Set-up low-level interrupts for most acurate BPM math
#include <PulseSensorPlayground.h>
#include <Wire.h>

const int PulseWire = 0; // 'S' Signal pin connected to A0
// const int LED13 = 13;          // The on-board Arduino LED
int Threshold = 550; // Determine which Signal to "count as a beat" and which to ignore
PulseSensorPlayground pulseSensor;

const int MPU_addr = 0x68; // I2C address of the MPU-6050
int16_t AcX, AcY, AcZ, Tmp, GyX, GyY, GyZ;
float ax = 0, ay = 0, az = 0, gx = 0, gy = 0, gz = 0;
const int buttonPin = 6;
const int ledPin = 3;

// int data[STORE_SIZE][5]; //array for saving past data
// byte currentIndex=0; //stores current data array index (0-255)
boolean fall = false;     // stores if a fall has occurred
boolean trigger1 = false; // stores if first trigger (lower threshold) has occurred
boolean trigger2 = false; // stores if second trigger (upper threshold) has occurred
boolean trigger3 = false; // stores if third trigger (orientation change) has occurred

byte trigger1count = 0; // stores the counts past since trigger 1 was set true
byte trigger2count = 0; // stores the counts past since trigger 2 was set true
byte trigger3count = 0; // stores the counts past since trigger 3 was set true
int angleChange = 0;

// variable for storing the pushbutton status
int buttonState = 0;
int ledState = 0;

int fall_message_ouput = 7;

void setup()
{
  Wire.begin();
  Wire.beginTransmission(MPU_addr);
  Wire.write(0x6B); // PWR_MGMT_1 register
  Wire.write(0);    // set to zero (wakes up the MPU-6050)
  Wire.endTransmission(true);
  Serial.begin(9600);
  
  // set output pins to send fall alert to ESP8266
  pinMode(fall_message_ouput, OUTPUT);
  pinMode(11, OUTPUT);
  pinMode(0, OUTPUT);
  digitalWrite(11, HIGH);
  
  // set input pin of button and output pin of led
  pinMode(buttonPin, INPUT);
  pinMode(ledPin, OUTPUT);

  // Configure the PulseSensor object, by assigning our variables to it
  pulseSensor.analogInput(PulseWire);
  // pulseSensor.blinkOnPulse(LED13);       // Blink on-board LED with heartbeat
  pulseSensor.setThreshold(Threshold);

  if (pulseSensor.begin())
  {
    Serial.println("PulseSensor object created!");
  }
}

void loop()
{
  // read the state of the pushbutton value
  buttonState = digitalRead(buttonPin);
  // check if the pushbutton is pressed.
  // if it is, the buttonState is HIGH
  if (buttonState == HIGH)
  {
    ledState = !ledState;
  }
  if (ledState == HIGH)
  {
    // turn LED on
    digitalWrite(ledPin, HIGH);
  }
  else
  {
    // turn LED off
    digitalWrite(ledPin, LOW);
  }

  // heartbeat sensor
  int myBPM = pulseSensor.getBeatsPerMinute(); // Calculates BPM

  if (pulseSensor.sawStartOfBeat())
  {                                             // Constantly test to see if a beat happened
    Serial.println("Heartbeat  BPM: " + myBPM); // If true, print a message                       // Print the BPM value
  }
  if (myBPM > 170)
  { // high heart rate detection
    Serial.println("High Heartrate Alert: Your heart rate is above 200!");
    digitalWrite(fall_message_ouput, HIGH);
    digitalWrite(ledPin, HIGH);
    delay(1000);
    digitalWrite(fall_message_ouput, LOW);
  }
  if (myBPM < 27)
  { // high heart rate detection
    Serial.println("Low Heartrate Alert: Your heart rate is below 27!");
    digitalWrite(fall_message_ouput, HIGH);
    digitalWrite(ledPin, HIGH);
    delay(1000);
    digitalWrite(fall_message_ouput, LOW);
  }

  // accelerator
  mpu_read();
  // 2050, 77, 1947 are values for calibration of accelerometer
  //  values may be different for you
  ax = (AcX - 2050) / 16384.00;
  ay = (AcY - 77) / 16384.00;
  az = (AcZ - 1947) / 16384.00;

  // 270, 351, 136 for gyroscope
  gx = (GyX + 270) / 131.07;
  gy = (GyY - 351) / 131.07;
  gz = (GyZ + 136) / 131.07;

  // calculating Amplitute vactor for 3 axis
  float Raw_AM = pow(pow(ax, 2) + pow(ay, 2) + pow(az, 2), 0.5);
  int AM = Raw_AM * 10; // as values are within 0 to 1, I multiplied
                        // it by for using if else conditions
                        // Serial.println(analogRead(A1));
  // if(digitalRead(A3) == HIGH)
  //{
  //   Serial.println("A3: SOS"+digitalRead(A3));
  // }

  Serial.println(AM);
  // Serial.println(PM);
  // delay(5);

  if (trigger3 == true)
  {
    trigger3count++;
    // Serial.println(trigger3count);
    if (trigger3count >= 10)
    {
      angleChange = pow(pow(gx, 2) + pow(gy, 2) + pow(gz, 2), 0.5);
      // delay(10);
      Serial.println(angleChange);
      if ((angleChange >= 0) && (angleChange <= 10))
      { // if orientation changes remains between 0-10 degrees
        fall = true;
        trigger3 = false;
        trigger3count = 0;
        Serial.println(angleChange);
      }
      else
      { // user regained normal orientation
        trigger3 = false;
        trigger3count = 0;
        Serial.println("TRIGGER 3 DEACTIVATED");
      }
    }
  }
  if (fall == true)
  { // in event of a fall detection
    Serial.println("FALL DETECTED");
    digitalWrite(fall_message_ouput, HIGH);
    digitalWrite(ledPin, HIGH);
    delay(1000);
    digitalWrite(fall_message_ouput, LOW);
    fall = false;
    // exit(1);
  }
  if (trigger2count >= 6)
  { // allow 0.5s for orientation change
    trigger2 = false;
    trigger2count = 0;
    Serial.println("TRIGGER 2 DECACTIVATED");
  }
  if (trigger1count >= 6)
  { // allow 0.5s for AM to break upper threshold
    trigger1 = false;
    trigger1count = 0;
    Serial.println("TRIGGER 1 DECACTIVATED");
  }
  if (trigger2 == true)
  {
    trigger2count++;
    // angleChange=acos(((double)x*(double)bx+(double)y*(double)by+(double)z*(double)bz)/(double)AM/(double)BM);
    angleChange = pow(pow(gx, 2) + pow(gy, 2) + pow(gz, 2), 0.5);
    Serial.println(angleChange);
    if (angleChange >= 30 && angleChange <= 400)
    { // if orientation changes by between 80-100 degrees
      trigger3 = true;
      trigger2 = false;
      trigger2count = 0;
      Serial.println(angleChange);
      Serial.println("TRIGGER 3 ACTIVATED");
    }
  }
  if (trigger1 == true)
  {
    trigger1count++;
    if (AM >= 12)
    { // if AM breaks upper threshold (3g)
      trigger2 = true;
      Serial.println("TRIGGER 2 ACTIVATED");
      trigger1 = false;
      trigger1count = 0;
    }
  }
  if (AM <= 2 && trigger2 == false)
  { // if AM breaks lower threshold (0.4g)
    trigger1 = true;
    Serial.println("TRIGGER 1 ACTIVATED");
  }
  // It appears that delay is needed in order not to clog the port
  delay(100);
}

void mpu_read()
{
  Wire.beginTransmission(MPU_addr);
  Wire.write(0x3B); // starting with register 0x3B (ACCEL_XOUT_H)
  Wire.endTransmission(false);
  Wire.requestFrom(MPU_addr, 14, true); // request a total of 14 registers
  AcX = Wire.read() << 8 | Wire.read(); // 0x3B (ACCEL_XOUT_H) & 0x3C (ACCEL_XOUT_L)
  AcY = Wire.read() << 8 | Wire.read(); // 0x3D (ACCEL_YOUT_H) & 0x3E (ACCEL_YOUT_L)
  AcZ = Wire.read() << 8 | Wire.read(); // 0x3F (ACCEL_ZOUT_H) & 0x40 (ACCEL_ZOUT_L)
  Tmp = Wire.read() << 8 | Wire.read(); // 0x41 (TEMP_OUT_H) & 0x42 (TEMP_OUT_L)
  GyX = Wire.read() << 8 | Wire.read(); // 0x43 (GYRO_XOUT_H) & 0x44 (GYRO_XOUT_L)
  GyY = Wire.read() << 8 | Wire.read(); // 0x45 (GYRO_YOUT_H) & 0x46 (GYRO_YOUT_L)
  GyZ = Wire.read() << 8 | Wire.read(); // 0x47 (GYRO_ZOUT_H) & 0x48 (GYRO_ZOUT_L)
}

WiFi module sends SMS with location

Arduino
This code is uploaded to ESP8266 12E to
1. Get the wearer's location triangulated by WiFi position system using UnwiredLab Geolocation API
2. Send SMS with the location to the wearer's emergency contact using Twilio SMS API

Both actions are triggered when ESP8266 receives pin input from Arduino Uno.
/*
 * Twilio SMS and MMS on ESP8266 Example.
 */

#include <Wire.h>
#include <ESP8266WiFi.h>
#include <WiFiClientSecure.h>
#include <ESP8266WebServer.h>

#include "twilio.hpp"

// Use software serial for debugging?
#define USE_SOFTWARE_SERIAL 0

// Print debug messages over serial?
#define USE_SERIAL 1

//
// UnwiredLab Geolocation API info
//
// your network SSID (name) & network password
char myssid[] = "UCLA_WEB";
char mypass[] = "";

// unwiredlabs Hostname & Geolocation Endpoint url
const char *host = "us1.unwiredlabs.com";
String endpoint = "/v2/process.php";
const int httpsPort = 443;

const char *fingerprint = "41 3F 95 84 0A E3 74 F6 2B C3 19 27 C9 67 F3 0C 38 D4 6F B6";

// UnwiredLabs API_Token. Signup here to get a free token https://unwiredlabs.com/trial
String geolocation_api_key = "pk.73d4083cea6ff7527b49262ee534f8f1";
String jsonString = "{\n";

// Variables to store unwiredlabs response
double latitude = 0.0;
double longitude = 0.0;
double accuracy = 0.0;

int GPIO = 5;

// Your network SSID and password
const char *ssid = "UCLA_WEB";
const char *password = "";

// Find the api.twilio.com SHA1 fingerprint, this one was valid as
// of July 2020. This will change, please see
// https://www.twilio.com/docs/sms/tutorials/how-to-send-sms-messages-esp8266-cpp
// to see how to update the fingerprint.
const char fingerprint[] = "72 1C 17 31 85 E2 7E 0D F9 D4 C2 5B A0 0E BD B7 E2 06 26 ED";

// Twilio account specific details, from https://twilio.com/console
// Please see the article:
// https://www.twilio.com/docs/guides/receive-and-reply-sms-and-mms-messages-esp8266-c-and-ngrok

// If this device is deployed in the field you should only deploy a revocable
// key. This code is only suitable for prototyping or if you retain physical
// control of the installation.
const char *account_sid = "ACb63f3d2be8ed9c87832c49302c30a636";
const char *auth_token = "63bee244f52b6f923f0b9b719598c530";

// Details for the SMS we'll send with Twilio.  Should be a number you own
// (check the console, link above).
String to_number = "+12134682703";
String from_number = "+15205025995";
String user_name = "Jeffrey";
String message_body = "Cura - " + user_name + " alerts a health emergency!!! He's at latitude: " + String(loc_longitude, 6) + ", longitude: " + String(loc_latitude, 6) + ". Call 911 if you need any emergent medical help.";

// The 'authorized number' to text the ESP8266 for our example
String master_number = "+12134682703";

// Optional - a url to an image.  See 'MediaUrl' here:
// https://www.twilio.com/docs/api/rest/sending-messages
String media_url = "";

// Global twilio objects
Twilio *twilio;
ESP8266WebServer twilio_server(8000);
twilio = new Twilio(account_sid, auth_token, fingerprint);

//  Optional software serial debugging
#if USE_SOFTWARE_SERIAL == 1
#include <SoftwareSerial.h>
// You'll need to set pin numbers to match your setup if you
// do use Software Serial
extern SoftwareSerial swSer(14, 4, false, 256);
#else
#define swSer Serial
#endif

/*
 * Callback function when we hit the /message route with a webhook.
 * Use the global 'twilio_server' object to respond.
 */
void handle_message()
{
#if USE_SERIAL == 1
  swSer.println("Incoming connection!  Printing body:");
#endif
  bool authorized = false;
  char command = '\0';

  // Parse Twilio's request to the ESP
  for (int i = 0; i < twilio_server.args(); ++i)
  {
#if USE_SERIAL == 1
    swSer.print(twilio_server.argName(i));
    swSer.print(": ");
    swSer.println(twilio_server.arg(i));
#endif

    if (twilio_server.argName(i) == "From" and
        twilio_server.arg(i) == master_number)
    {
      authorized = true;
    }
    else if (twilio_server.argName(i) == "Body")
    {
      if (twilio_server.arg(i) == "?" or
          twilio_server.arg(i) == "0" or
          twilio_server.arg(i) == "1")
      {
        command = twilio_server.arg(i)[0];
      }
    }
  } // end for loop parsing Twilio's request

  // Logic to handle the incoming SMS
  // (Some board are active low so the light will have inverse logic)
  String response = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>";
  if (command != '\0')
  {
    if (authorized)
    {
      switch (command)
      {
      case '0':
        digitalWrite(LED_BUILTIN, LOW);
        response += "<Response><Message>"
                    "Turning light off!"
                    "</Message></Response>";
        break;
      case '1':
        digitalWrite(LED_BUILTIN, HIGH);
        response += "<Response><Message>"
                    "Turning light on!"
                    "</Message></Response>";
        break;
      case '?':
      default:
        response += "<Response><Message>"
                    "0 - Light off, 1 - Light On, "
                    "? - Help\n"
                    "The light is currently: ";
        response += digitalRead(LED_BUILTIN);
        response += "</Message></Response>";
        break;
      }
    }
    else
    {
      response += "<Response><Message>"
                  "Unauthorized!"
                  "</Message></Response>";
    }
  }
  else
  {
    response += "<Response><Message>"
                "Look: a SMS response from an ESP8266!"
                "</Message></Response>";
  }

  twilio_server.send(200, "application/xml", response);
}

//
// Get current location from UnwiredLab Geolocation API
//
void getLocation()
{
  char bssid[6];
  DynamicJsonBuffer jsonBuffer;

  // WiFi.scanNetworks will return the number of networks found
  int n = WiFi.scanNetworks();
  Serial.println("scan done");

  if (n == 0)
  {
    Serial.println("No networks available");
  }
  else
  {
    Serial.print(n);
    Serial.println(" networks found");
  }

  // now build the jsonString...
  jsonString = "{\n";
  jsonString += "\"token\" : \"";
  jsonString += geolocation_api_key;
  jsonString += "\",\n";
  jsonString += "\"id\" : \"saikirandevice01\",\n";
  jsonString += "\"wifi\": [\n";
  for (int j = 0; j < n; ++j)
  {
    jsonString += "{\n";
    jsonString += "\"bssid\" : \"";
    jsonString += (WiFi.BSSIDstr(j));
    jsonString += "\",\n";
    jsonString += "\"signal\": ";
    jsonString += WiFi.RSSI(j);
    jsonString += "\n";
    if (j < n - 1)
    {
      jsonString += "},\n";
    }
    else
    {
      jsonString += "}\n";
    }
  }
  jsonString += ("]\n");
  jsonString += ("}\n");
  Serial.println(jsonString);

  WiFiClientSecure client;

  // Connect to the client and make the api call
  Serial.println("Requesting URL: https://" + (String)Host + endpoint);
  if (client.connect(Host, 443))
  {
    Serial.println("Connected");
    client.println("POST " + endpoint + " HTTP/1.1");
    client.println("Host: " + (String)Host);
    client.println("Connection: close");
    client.println("Content-Type: application/json");
    client.println("User-Agent: Arduino/1.0");
    client.print("Content-Length: ");
    client.println(jsonString.length());
    client.println();
    client.print(jsonString);
    delay(500);
  }

  // Read and parse all the lines of the reply from server
  while (client.available())
  {
    String line = client.readStringUntil('\r');
    JsonObject &root = jsonBuffer.parseObject(line);
    if (root.success())
    {
      latitude = root["lat"];
      longitude = root["lon"];
      accuracy = root["accuracy"];

      Serial.print("Latitude = ");
      Serial.println(latitude, 6);
      Serial.print("Longitude = ");
      Serial.println(longitude, 6);
      Serial.print("Accuracy = ");
      Serial.println(accuracy);

      loc_latitude = latitude;
      loc_longitude = longitude;
      loc_accuracy = accuracy;
    }
  }

  Serial.println("closing connection");
  Serial.println();
  client.stop();
}

/*
 * Setup function for ESP8266 Twilio Example.
 *
 * Here we connect to a friendly wireless network, set the time, instantiate
 * our twilio object, optionally set up software serial, then send a SMS
 * or MMS message.
 */
void setup()
{
  pinMode(GPIO, INPUT);
  Wire.begin(9); // 9 here is the address(Mentioned even in the master board code)

  WiFi.begin(ssid, password);
  //        twilio = new Twilio(account_sid, auth_token, fingerprint);

#if USE_SERIAL == 1
  swSer.begin(115200);
  while (WiFi.status() != WL_CONNECTED)
  {
    delay(1000);
    swSer.print(".");
  }
  swSer.println("");
  swSer.println("Connected to WiFi, IP address: ");
  swSer.println(WiFi.localIP());
#else
  while (WiFi.status() != WL_CONNECTED)
    delay(1000);
#endif
}

/*
 *  In our main loop, we listen for connections from Twilio in handleClient().
 */
void loop()
{
  twilio_server.handleClient();

  if (digitalRead(GPIO) == HIGH)
  {
    Serial.println("Health emergency detected!");
    getLocation();

    // Response will be filled with connection info and Twilio API responses
    // from this initial SMS send.
    String response;
    bool success = twilio->send_message(
        to_number,
        from_number,
        message_body,
        response,
        media_url);

    // Set up a route to /message which will be the webhook url
    twilio_server.on("/message", handle_message);
    twilio_server.begin();

    // Use LED_BUILTIN to find the LED pin and set the GPIO to output
    pinMode(LED_BUILTIN, OUTPUT);

#if USE_SERIAL == 1
    swSer.println(response);
#endif
  }
}

Credits

jeffreythecoder

jeffreythecoder

0 projects • 0 followers

Comments