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