You are on page 1of 23

Appendices 41

B. Arduino Mega Program

#include <LayadCircuits_SalengGSM.h>

#include <TinyGPS++.h>

#include <SoftwareSerial.h>

#include <LiquidCrystal_I2C.h>

TinyGPSPlus gps;

LiquidCrystal_I2C lcd(0x3F,16,2);

//SoftwareSerial gsmSerial(2,3);

#define gsmSerial Serial1

LayadCircuits_SalengGSM salengGSM =
LayadCircuits_SalengGSM(&gsmSerial);

TinyGPSPlus tinygps;

char PHONE[12] = "09164428565";

const int MAX_NORMAL_BPM = 100;

const int MIN_NORMAL_BPM = 50;

bool DEBUG = true;

//Pulse Variables

const byte PULSEPIN = A0;

// Pulse Sensor purple wire connected to analog pin 0

const byte SMS_DISABLE = A1;

// Volatile Variables, used in the interrupt service


routine!
Appendices 42

volatile int BPM;

// int that holds raw Analog in 0. updated every 2mS

volatile int Signal;

// holds the incoming raw data

volatile int IBI = 600;

// int that holds the time interval between beats! Must be


seeded!

volatile boolean Pulse = false;

// "True" when User's live heartbeat is detected. "False"


when not a "live beat".

volatile boolean QS = false;

// becomes true when Arduoino finds a beat.

// interrupt for Pulse Sensor

volatile int rate[10];

// array to hold last ten IBI values

volatile unsigned long sampleCounter = 0;

// used to determine pulse timing

volatile unsigned long lastBeatTime = 0;

// used to find IBI

volatile int P =512;

// used to find peak in pulse wave, seeded

volatile int T = 512;

// used to find trough in pulse wave, seeded

volatile int thresh = 515;

// used to find instant moment of heart beat, seeded


Appendices 43

volatile int amp = 100;

// used to hold amplitude of pulse waveform, seeded

volatile boolean firstBeat = true;

// used to seed rate array so we startup with reasonable


BPM

volatile boolean secondBeat = false;

// used to seed rate array so we startup with reasonable


BPM

float latitude;

float longitude;

char sms_buff[160]="";

char buf[17]="";

bool flag_abnormal_bpm;

bool flag_gps; // true when gps signal is seen

void gps_routine()

unsigned long tim;

if(Serial2.available())

tim = millis();

while(millis() - tim < 100)


Appendices 44

if(Serial2.available())

char c;

c = Serial2.read();

tinygps.encode(c);

if (!tinygps.location.isValid()) Serial.write(c); //
test only

static unsigned long t;

if(millis() - t < 1000) return;

t=millis();

if (tinygps.location.isValid())

digitalWrite(LED_BUILTIN,HIGH);

latitude = tinygps.location.lat();

longitude = tinygps.location.lng();

Serial.print(F("-----------------latitude="));
Serial.println(latitude);

Serial.print(F("-----------------longitude="));
Serial.println(longitude);
Appendices 45

else digitalWrite(LED_BUILTIN,LOW);

void interruptSetup()

// Initializes Timer2 to throw an interrupt every 2mS.

TCCR2A = 0x02;

// DISABLE PWM ON DIGITAL PINS 3 AND 11, AND GO INTO CTC


MODE

TCCR2B = 0x06;

// DON'T FORCE COMPARE, 256 PRESCALER

OCR2A = 0X7C;

// SET THE TOP OF THE COUNT TO 124 FOR 500Hz SAMPLE RATE

TIMSK2 = 0x02;

// ENABLE INTERRUPT ON MATCH BETWEEN TIMER2 AND OCR2A

sei();

// MAKE SURE GLOBAL INTERRUPTS ARE ENABLED

// THIS IS THE TIMER 2 INTERRUPT SERVICE ROUTINE.


Appendices 46

// Timer 2 makes sure that we take a reading every 2


miliseconds

ISR(TIMER2_COMPA_vect)

// triggered when Timer2 counts to 124

cli();

// disable interrupts while we do this

Signal = analogRead(PULSEPIN);

// read the Pulse Sensor

sampleCounter += 2;

// keep track of the time in mS with this variable

int N = sampleCounter - lastBeatTime;

// monitor the time since the last beat to avoid noise

// find the peak and trough of the pulse wave

if(Signal < thresh && N > (IBI/5)*3){

// avoid dichrotic noise by waiting 3/5 of last IBI

if (Signal < T){

// T is the trough

T = Signal;

// keep track of lowest point in pulse wave

if(Signal > thresh && Signal > P){

// thresh condition helps avoid noise


Appendices 47

P = Signal;

// P is the peak

// keep track of highest point in pulse wave

// NOW IT'S TIME TO LOOK FOR THE HEART BEAT

// signal surges up in value every time there is a pulse

if (N > 250){

// avoid high frequency noise

if ( (Signal > thresh) && (Pulse == false) && (N >


(IBI/5)*3) ){

Pulse = true;

// set the Pulse flag when we think there is a pulse

IBI = sampleCounter - lastBeatTime;

// measure time between beats in mS

lastBeatTime = sampleCounter;

// keep track of time for next pulse

if(secondBeat){

// if this is the second beat, if secondBeat == TRUE

secondBeat = false;

// clear secondBeat flag

for(int i=0; i<=9; i++){

// seed the running total to get a realisitic BPM at


startup

rate[i] = IBI;
Appendices 48

if(firstBeat){

// if it's the first time we found a beat, if firstBeat ==


TRUE

firstBeat = false;

// clear firstBeat flag

secondBeat = true;

// set the second beat flag

sei();

// enable interrupts again

return;

// IBI value is unreliable so discard it

// keep a running total of the last 10 IBI values

word runningTotal = 0;

// clear the runningTotal variable

for(int i=0; i<=8; i++){

// shift data in the rate array

rate[i] = rate[i+1];

// and drop the oldest IBI value

runningTotal += rate[i];

// add up the 9 oldest IBI values


Appendices 49

rate[9] = IBI;

// add the latest IBI to the rate array

runningTotal += rate[9];

// add the latest IBI to runningTotal

runningTotal /= 10;

// average the last 10 IBI values

BPM = 60000/runningTotal;

// how many beats can fit into a minute? that's BPM!

QS = true;

// set Quantified Self flag

// QS FLAG IS NOT CLEARED INSIDE THIS ISR

if (Signal < thresh && Pulse == true){

// when the values are going down, the beat is over

Pulse = false;

// reset the Pulse flag so we can do it again

amp = P - T;

// get amplitude of the pulse wave

thresh = amp/2 + T;

// set thresh at 50% of the amplitude

P = thresh;
Appendices 50

// reset these for next time

T = thresh;

if (N > 2500){

// if 2.5 seconds go by without a beat

thresh = 512;

// set thresh default

P = 512;

// set P default

T = 512;

// set T default

lastBeatTime = sampleCounter;

// bring the lastBeatTime up to date

firstBeat = true;

// set these to avoid noise

secondBeat = false;

// when we get the heartbeat back

sei();

// enable interrupts when youre done!

void pulse_sensor()
Appendices 51

static unsigned long actual_temp;

if (millis() - actual_temp < 20) return;

actual_temp = millis();

if (QS == true)// A Heartbeat Was Found

QS = false; // reset the Quantified


Self flag for next time

void sensor_check()

static bool flagp; // tells us when the transition from


normal to abnormal occurs

static byte ctrp; // counts how many times we detected an


abnormal sensor value

static unsigned long timer;

if(millis() - timer < 100) return; // we only check every


1 second. prevent rapid checking

timer = millis();
Appendices 52

if(BPM > MAX_NORMAL_BPM || BPM < MIN_NORMAL_BPM && BPM !=


0 )

if(flagp == false)

ctrp++;

if(ctrp >= 10)

flag_abnormal_bpm = true; // flag for second phone


number

flagp = true;

else

flagp = false;

ctrp = 0;

Serial.print(F("BPM="));

Serial.println(BPM);

void sms_manager()
Appendices 53

if(digitalRead(SMS_DISABLE) == LOW)

flag_abnormal_bpm = false;

return;

if(salengGSM.isFreeToSend() == false) return; // if we are


not yet free to send then exit immediately.

static unsigned long t;

if(millis() - t < 1000) return;

t=millis();

if(flag_abnormal_bpm == true) // check if we detected an


abnormal sensor value

flag_abnormal_bpm = false;

assemble_sms(sms_buff);

salengGSM.sendSMS(PHONE,sms_buff); // intiiate the


SMS.

Serial.print(F("Sending to:"));

Serial.println(PHONE);

Serial.println(sms_buff);
Appendices 54

// if(DEBUG)Serial.println(F("ABNORMAL PULSERATE"));

void assemble_sms(char * pmsg)

char temp[32];

byte p;

p=0;

memset(pmsg,' ',160);

pmsg[159] = 0;

sprintf(pmsg,"BPM=%03d",BPM); p = 7;

memcpy(&pmsg[p],"\nLOCATION\nLATITUDE:",19); p = p +
19

memset(temp,0,32);

// dtostrf(latitude,3,6,temp);

// strncpy(&pmsg[p], temp,9);

// p = p + 9;

dtostrf(latitude,3,6,temp);

strncpy(&pmsg[p], temp,8);

p = p + 8;
Appendices 55

pmsg[p] = '\n';

p++;

memcpy(&pmsg[p],"LONGITUDE:",10); p = p + 10;

memset(temp,0,32);

dtostrf(longitude,3,6,temp);

strncpy(&pmsg[p], temp,9);

p = p + 9;

pmsg[p] = '\n';

p++;

strncpy(&pmsg[p],"\r\n",2); p = p + 2;

if(latitude!=0)

// http://maps.google.com/?q=lll.llllll,ooo.oooooo

// p=0;

strncpy(&pmsg[p], "http://maps.google.com/?q=",26);

p = p+ 26;

memset(temp,0,32);

dtostrf(latitude,3,6,temp);

// strncpy(&pmsg[p], temp,9);
Appendices 56

// p = p + 9;

strncpy(&pmsg[p], temp,8);

p = p + 8;

pmsg[p] = ',';

p++;

memset(temp,0,32);

dtostrf(longitude,3,6,temp);

strncpy(&pmsg[p], temp,9);

p = p + 9;

else

sprintf(pmsg,"BPM=
%03d\nLOCATION\nLATITUDE:0\nLONGITUDE:0\n\r\nNo GPS
Signal",BPM);

memcpy(&pmsg[p], "\r\n",2);

p = p+2;

pmsg[p] = 0;
Appendices 57

void lcd_refresh()

static unsigned long t;

if(millis() - t < 500) return;

t=millis();

char g,s;

if (tinygps.location.isValid()) g='G';

else g=' ';

if(digitalRead(SMS_DISABLE) == LOW) s=' ';

else s = 'S';

if(salengGSM.isFreeToSend() == false)

lcd.setCursor(0,0); lcd.print(F("SENDING SMS... "));

lcd.setCursor(0,1);

sprintf(buf,"%03d bpm %c %c",BPM,g,s);

lcd.print(buf);

else
Appendices 58

lcd.setCursor(0,0); lcd.print(F(" HEART RATE "));

lcd.setCursor(0,1);

sprintf(buf,"%03d bpm %c %c",BPM,g,s);

lcd.print(buf);

void setup() {

pinMode(LED_BUILTIN,OUTPUT);

pinMode(SMS_DISABLE,INPUT_PULLUP);

salengGSM.begin(9600);

// this is the default baud rate

Serial.begin(9600);

Serial2.begin(9600);

Serial.println(F("AT"));

lcd.init();

lcd.backlight();

lcd.setCursor(0,0); lcd.print(F(" PLEASE "));

lcd.setCursor(0,1); lcd.print(F(" WAIT "));

//Pulse Sensor

interruptSetup();

Serial.print(F("Preparing Saleng GSM Shield.Pls wait for


10 seconds..."));
Appendices 59

unsigned long t;

// delay(10000); // allow 10 seconds for modem to boot up


and register

salengGSM.initSalengGSM();

Serial.println(F("Start"));

//salengGSM.sendSMS("09164428565","Hi, this is a test SMS


from the Layad Circuits' Saleng GSM Shield. Have a nice
day!");

void loop() {

salengGSM.smsMachine();

pulse_sensor()a;

sms_manager();

sensor_check();

gps_routine();

lcd_refresh();

}
Appendices 60

C. Documentation

Figure 3: Connecting components to Arduino Mega

Figure 4: Assembly of prototype to be placed in the chassis


Appendices 61

Figure 5: Attaching power source to the chassis

Figure 6: Components are being secured and taped on the chassis


Appendices 62

Figure 7: Fixing wires to avoid soldering of components

Figure 8: Activating and testing the prototype


Appendices 63

Figure 9: Final Prototype

You might also like