ramialcala
Published © GPL3+

Geriatrino

GERIATRINO is a device designed to be an economic and efficient monitoring and alert system for vulnerable people living alone.

IntermediateFull instructions provided2,264
Geriatrino

Things used in this project

Hardware components

Arduino MKR GSM 1400
Arduino MKR GSM 1400
×1
PIR Motion Sensor (generic)
PIR Motion Sensor (generic)
×2
Door opening sensor
×1
LiPo battery 3,7 v, 1200 mAh
×1
Buzzer
Buzzer
×1
GSM Antenna
×1
Mini breadboard
×1
HC-06 Bluetooth Module
×1
Female 3,5 jack DC connector
×1
Male-female Arduino jumper wires
×1
5v, 2 A, (100-240 AC adapter) power supply
×1
Hologram Global IoT SIM Card
Hologram Global IoT SIM Card
×1

Software apps and online services

FreeCAD 3D moduler
MIT App Inventor
MIT App Inventor
Cura Ultimaker 3D printing software
Arduino IDE
Arduino IDE
www.programarfacil.com

Hand tools and fabrication machines

3D Printer (generic)
3D Printer (generic)
Plier, Needle Nose
Plier, Needle Nose
Soldering iron (generic)
Soldering iron (generic)
Solder Wire, Lead Free
Solder Wire, Lead Free

Story

Read more

Custom parts and enclosures

Geriatrino Case

Geriatrino Cover

Geriatrino leg

Schematics

Geriatrino basic schematics

GERIATRINO_CONF_ENGLISH

This is the . apk file you have to download and install in your smartphone or tablet to get App to configure your Geriatrino. You can also get it visiting the App Inventor Gallery.

Code

GERIATRINO_COMPLETO_APP_DEPURADO_Operativo_P3_English.ino

Arduino
This is the Arduino sketch developped for Geriatrino project. In this version you configure the device using an app.
//Include the necessary libraries
#include <MKRGSM.h>   
GSM gsm;  
GSM_SMS sms;
#include <RTCZero.h>  
RTCZero rtc;

//VARIABLES for SMS management
char numeroCliente[20];  //In this array is saved the phone number from which an SMS is sent to the SIM card inserted in the MKRGSM
String numeroClienteString;
String mensaje; // This variable will be updated with the right message depending on the information we need to transmit
char contenidoMensaje; //This variable wiil keep the content of the incoming SMS (initially the incoming messages have been thought to be single characters)
boolean noConectado = true; // Variable to know whether the SIM card is connected to the telephone network or not.
//The following variables will keep the hour and minute values of the last correct sensor activations (pressence and door opening)
long int horasUltimaActivacionCorrecta, minutosUltimaActivacionCorrecta,horasUltimaAperturaNevera, minutosUltimaAperturaNevera;

// VARIABLES used during device configuration 
//NOTE.- I am aware that some of the following type convertions could look strange. I was implementing them on the run, as I was programming, and as they finally worked
//I dont dare to change a code line. Anybody familiar with the different type of variables can easily improve the code. 


String telefonos[3];//This array will store the phone numbers loaded from the app. Initially I decided a maximum number of 3 contact numbers, but you can customize it
char telefonoAviso[20]; //This variable is used to convert the contact numbers from String to array char type, for the funcion sms.begin needs that type to work
String periodo;
char PIN []="1234"; //Array char type variable to store the PIN of our SIM card.
String numeroPIN; //String type variable to store the PIN number
int longitudPIN;
//Variable to keep the number of contacts entered with the app. At least one phone number will have to be entered so that Geriatrino can work
byte contactos =0;
//This variable will keep the period of time to trigger alarms if no activity is detected. The app will admit a value between a minimum of 3 hours and a maximum of ten hours
//But you can change these limits. The default value is 5 hours  
int intervaloAvisos =5;  
int  tiempoSinActividad;
//These are control variables that will be used while settings are performed from the app
char anadirOtroTelefono ='S';
char primerCaracter; 
boolean realizarTestPresencia = true, testPresenciaOK=false, realizarTestPuertaCerrada = true, testPuertaCerradaOK = false;
boolean realizarTestPuertaAbierta =true,testPuertaAbiertaOK = false, pendienteEnviarSMSConfiguracion = false, peticionConfiguracion = false;
String mensajeRecibido;
byte intentosTestPresencia =0,intentosTestPuertaCerrada =0, intentosTestPuertaAbierta =0,intentosConexionRedGSM= 0;
String hora, minuto, inicioPeriodoSinActividadString, duracionPeriodoSinActividadString;
int horaActual, minutoActual,segundoActual, inicioPeriodoSinActividad, duracionPeriodoSinActividad;
boolean horarioNocturno = false;
long int tiempoDeNoche;

//MKR 1400 pin allocation
# define PinLed 6 //In this  case we use the  built-in led of Arduino MKR1400
# define PinPIR1 5  // Digital pin assigned to PIR 1 
# define PinPIR2 9 // Digital pin assigned to PIR2
# define PinDetectorPuerta  7 //Digital pin assigned to door sensor
# define PinSirena 8 //If we decide to add a buzzer this will be the pin. The code is prepared to make it sound under some conditions
//This pin will be used if a switcher or press button is implemented. It can be useful if you decide to use to arm or disarm the alarm
//But you could do it sending an SMS or via bluetooth connection.
# define PinInterruptor 3  
//Digital pins 13 and 14 are reserved to connect the mkr1400 with TX an Rx pins of HC-06 Bluetooth module

//Variables to control the way that Geriatrino will work. If modoAlarma is true it will work in "Alarm mode", otherwise it will work in "Geriatrino mode"
boolean modoAlarma;
String modoTrabajo;

//Variables used for the control of the PIR sensors
boolean presenciaPIR1 = false;
boolean presenciaPIR2 = false;
boolean deteccionPresencia = false;

//The following variables will keep the millis() values in the moment that a certain event occurs.
long int inicioPresenciaPIR1, inicioPresenciaPIR2, inicioDeteccionPresencia; 
long int primeraFalsaAlarmaPIR1, primeraFalsaAlarmaPIR2;
long int tiempoUltimaActivacionCorrecta, tiempoDesdePrimeraActivacionSolitarioPIR1, tiempoDesdePrimeraActivacionSolitarioPIR2;
long int tiempoSinAperturaPuerta,tiempoSinPresenciaDetectada;
long int ultimoChequeoRedGSM = millis();

//Variables to manage false alarms or single activation of one PIR
int activacionSolitarioPIR1=0,activacionSolitarioPIR2=0, numeroSensoresActivos=0, mensajesAvisoSinPresenciaDetectada=0;

//This variable will count the number of right activations detected during one period to measure the activity level
//I will not use in this prototype but it could be interesting to know it on demand, so I declare it to use it in future versions.
int numeroActivacionesCorrectas =0; 
int diferenciaActivaciones =0;  
int horasSinPresencia;

//VARIABLES used to manage the information provided by the door sensor
boolean puertaAbierta = false;
long int inicioPuertaAbierta,tiempoUltimaAperturaNevera; 
boolean mensajePuertaAbiertaNoEnviado = true;
byte HorasConPuertaAbierta =0, mensajesAvisoNeveraSinAbrir =0;
int numeroAperturasPuerta = 0, horasSinApertura =0;

//This variable is used to control the  activation of the buzzer if it is finally incorporated
boolean sonarSirena = false;
byte ciclosSirena=0;

//VARIABLES to manage the way the device works in Alarm Mode
boolean alarmaArmada=false,estadoPrevioAlarma=false,deteccionIntruso=false, alarmaActiva = false;
#define PinAlarmaArmada 4  
long int inicioDeteccionIntruso, contadorParaNuevoEnvioSMS;
byte avisoIntrusoEnviado=0, envioMensajeActivacionPuertaEnSolitario=0;


//IMPORTANT REMARKS IF YOU USE ARDUINO IDE TO CONFIGURE YOUR GERIATRINO
//To do the settings without the app the  following  variables will have declared with the values we want them to work:
/*This is equivalent to the call the function  anadirOtroTelefono(). Remember to write the phone number in the  international format) */
//String telefonos[3]={"phone number 1", "phone number 2", "phone number 3"} 
/*You set the operating mode, Alarm mode (true), Geriatrino mode (false) */
//boolean alarmMode = true or false;
/*If you choose the Geriatrino mode you set  the  time parameters needed to manage the alarm messages */
// int inicioPeriodoSinActividad = X; This is equivalent to call the function
// int duracionPeriodoSinActividad = y;
// int intervaloAvisos = z;
/*You set the PIN number of the SIM card to connect to the phone network */
// char PIN []="1234";
//If you use the IDE you do not need the HC-06 bluetooth module and you have to use Serial.begin(9600) instead of Serial1.begin(9600) to stablish communication 
//The current hour and minute could be get directly sending it through the IDE serial port during installation process.
//In the setup() function you have to subtitute the call to the  functions that communicate with the app for the next lines to get the current hour and minute
//rtc.begin(); // initialize RTC
//Serial.println("To set the current hour type the value and then press Enter key");
/*Waiting to read data from the IDE serial port*/
//while (Serial.available()==0){  delay(100);  }
//Valor_hora = Serial.readStringUntil('\n');  
//hora = String(Valor_hora).toInt(); 
//rtc.setHours(hora); 
//Serial.print("The value of the current hour is :  ");Serial.print(Valor_hora);Serial.println(" Now you have to type the value for the current minute and the press Enter key ");
/*Waiting for the value of the current minute  */
//while (Serial.available ()==0){    delay(100);  }
//Valor_minuto = Serial.readStringUntil('\n');
//minuto = String(Valor_minuto).toInt(); 
//rtc.setMinutes(minuto); 
//Serial.print("The value of the current minute is :  ");Serial.println(Valor_minuto);
//Serial.println("Type the value for the current second");
/*Waiting for the value of current second...*/
//while (Serial.available ()==0){    delay(100);  }  
//Valor_segundo = Serial.readStringUntil('\n');
//segundo = String(Valor_segundo).toInt();  
//Serial.print("The value of current second is :  ");Serial.println(Valor_segundo);
//rtc.setSeconds(segundo);
//horaActual = rtc.getHours();
//minutoActual =rtc.getMinutes();
//segundoActual = rtc.getSeconds();
//Serial.print("La current time is  ");Serial.print(horaActual);Serial.print(":");Serial.print(minutoActual);Serial.print(":");Serial.print(segundoActual);Serial.println("...");
/*I set the Interrupt time routine that determines the start of the rest period with the entered values */
//rtc.setAlarmTime(inicioPeriodoSinActividad,0,0); 
//rtc.enableAlarm(rtc.MATCH_HHMMSS);
//rtc.attachInterrupt(empiezaNoche); 
/*All the calls to the functions related to the app must be supressed and you have  to omit the sensor tests too*/

void setup() {
  //IMPORTANT.-To work with bluetooth and the serial port in MKR boards you have to use  "Serial1" instead of "Serial"  
  Serial1.begin(9600);
  //Pins associated to PIR and door sensors are declared as inputs
  pinMode(PinPIR1, INPUT);
  pinMode(PinPIR2, INPUT);  
  //If the fridge door is closed the contact of the door sensor is closed too, and a LOW will be read as this pin will be connected to ground
  //I activate the internal pull-up resistor of this  pin to read a HIGH level when the door is opened
  pinMode(PinDetectorPuerta, INPUT_PULLUP);
  //PinLed is declared as an Output
  pinMode(PinLed, OUTPUT);  
  digitalWrite(PinLed, LOW);  
  rtc.begin(); // initialize RTC
  
  //During setup I the program will remain in this loop until at least a phone number is entered, and the button "CONTINUE" is tapped in the  app.
  //When this happens the app sends an ASCII character "N" and it is storedd in the variable anadirOtroTelefono. If 3 phone numbers have been saved,
  //then the program automatically leaves the loop
  while (((contactos==0) || (anadirOtroTelefono == 'S'))&&(contactos <3)) { 
    anadirTelefono();  //This function is called to add a phone number
  }
  //Next lines may not seem necessary, but I need them to coordinate the app whith the Geriatrino Sketch when the last phone number is finally added.
  //In the event that 3 phone numbers have been typed in the app, then  the sketch has to wait until the button "CONTINUE" is tapped to go on with the settings
  if (contactos == 3){
    //I will allways empty the Serial buffer when I am expecting a data to come from the app. I know this is not necessary but I prefere to do it this way
    while (Serial1.available()!=0){Serial1.read();} 
    //Waiting to the next character from the app
    while (Serial1.available() == 0){    
    delay(100);
   }  //close  while
    anadirOtroTelefono = Serial1.read();  //
  }  //close if

  //After the phone numbers, the next data sent from the app will be the PIN number of the SIM card whe have inserted in the MKR1400. 
  //It is essential to enter it properly, otherwise the PIN will be accepted but the MKR1400 will not connect to the  network.
  while (Serial1.available()!=0){Serial1.read();}  
  while (Serial1.available() == 0){    
    delay(100);
   }  //close while
  
  
  //The PIN number is stored in the String type variable "numeroPIN"
  numeroPIN= Serial1.readStringUntil('\n');
  longitudPIN = numeroPIN.length(); //get the length of the string 
  //I convert the variable numeroPIN to the type "array char" because the function  gsm.begin() requires this type of data,
  //I apply (longitudPIN+1) to get the last blank space needed in the array structure
  numeroPIN.toCharArray(PIN,longitudPIN +1); 
   
  Serial1.print("1");  //I send a "1" to the app to confirm I haver received the PIN number, so that it continues showing the  setting options 
 
  //In the app, after entering the PIN, two buttons are showed to choose the mode of work: Alarm Mode or Geriatrino Mode. The sketch is now waiting 
  //until the choice is done. Then, if a character "G" is received trough the serial por it will work in Geriatrino MOde, and more time settings will be
  //required. If a character "A" is received instead, it will work in Alarm Mode. In this case no more settings are required and the sketch will proceed 
  //to perform the sensor tests.
     
  while (Serial1.available()!=0){Serial1.read();} 
  //Waiting for the next character from the app
  while (Serial1.available() == 0){    
    delay(100);
   }  //cierro while

 
  modoTrabajo= Serial1.readStringUntil('\n');
  if (modoTrabajo == "A"){
      modoAlarma = true;
      }
  else{
      modoAlarma = false;  //when modoAlarma = false that means we are working on Geriatrino Mode
      }
      
  while (Serial1.available()!=0){Serial1.read();}
  
  //If  Geriatrino mode is selected,(modoAlarma = False) some aditional time settings are required and the sketch calls one by one  the functions 
  //to configure these parameters. 
  if (modoAlarma==false){
    //Function to set the lapse  of time to trigger alerts if no activity  is detected 
    configurarTiempoSinActividad();
    //Functions to set the current hour and minute. Another option to get the current time could be using AT commands to get it from the phone network
    //I have not tried it but I think it could be possible.
    configurarHoraActual();
    rtc.setHours(horaActual);
    configurarMinutoActual();    
    rtc.setMinutes(minutoActual);    
    rtc.setSeconds(0);

    //Functions to set the start and the duration of the rest period. During this period, alarms will not be triggered, but the information of the sensors 
    //will be taken into  account and registered. It is important to know the habits of the monitored person to get an efective monitoring work.
    configurarInicioPeriodoSinActividad();
    configurarDuracionPeriodoSinActividad();
    //After setting the rest period, I configure the interrupt routine associated to the timer, to stablish the beginning of the rest time
    rtc.setAlarmTime(inicioPeriodoSinActividad,0,0); 
    rtc.enableAlarm(rtc.MATCH_HHMMSS);
    rtc.attachInterrupt(empiezaNoche); 
    
  //Next  block is been added to deal with the case in which the Geriatrino  is installed and configured during the rest period. With these lines we get that Geriatrino
  //starts working from the first moment after it is installed. Without them, the device will not be working properly until the Interrupt "empiezaNoche" is called. 
  if ( ((horaActual < inicioPeriodoSinActividad)&&(horaActual >=(inicioPeriodoSinActividad + duracionPeriodoSinActividad -24))) || (horaActual >= (inicioPeriodoSinActividad + duracionPeriodoSinActividad)) ) {  
    horarioNocturno = false;    
  }
  else{
    horarioNocturno = true;
    if  ((horaActual < inicioPeriodoSinActividad)&& (horarioNocturno==true)){
      tiempoDeNoche = millis()-((((duracionPeriodoSinActividad - horaActual) *60) + minutoActual) *60000) ;
    }
    if ((horaActual >= inicioPeriodoSinActividad)&& (horarioNocturno==true)){
      tiempoDeNoche = millis()-((((horaActual-inicioPeriodoSinActividad)*60) + minutoActual) * 60000);
    }    
   }    
  } //end of    if modoAlarma == false
  

  //After finishing the settings we start performing the sensor test to make sure they  are working properly. If the  test result is not Ok it
  //will be repeated one more time.
  while ((realizarTestPresencia == true)&&(intentosTestPresencia <2)){
    //This function checks that the  two PIR sensors are working  ok.
    testPresencia();
  }

  while ((realizarTestPuertaCerrada == true)&&(intentosTestPuertaCerrada <2)){
    //This function checks the signal of the door sensor when the fridge door is closed
    testPuertaCerrada();
  }
  
  while ((realizarTestPuertaAbierta == true)&&(intentosTestPuertaAbierta <2)){
    //This function checks the signal of the door sensor when the fridge door is open
    testPuertaAbierta();
  }

  //Finally  I  call the function to connect to the mobile phone network. It will be trying to connect for some time, and if connection is not stablished, 
  //the sketch  will  go on and loop function will start to run, but the function conectarRedGSM will be periodically called until it gets to connect. 
  conectarRedGSM();
  //If we have got a failure in the connection to the network or in any of the sensor tests, we call a function that will send a message informing about it
  if ((noConectado==true)||(testPresenciaOK==false)||(testPuertaCerradaOK==false)||(testPuertaAbiertaOK==false)){
    avisoFallosInstalacion();    
  }
  
  //At the end of setup() the time values to these to variables are updated with millis()
  tiempoUltimaActivacionCorrecta = millis();  //time for the last simoultaneous activation of both PIR
  tiempoUltimaAperturaNevera = millis();      //time for the last door opening.
  
}  //END SETUP
  


//This function is coordinated with the app to configure de contact phone numbers
void anadirTelefono(){
  
  while (Serial1.available()!=0){Serial1.read();} 
  //Waiting for a data coming from the  app through the serial  port 
  while (Serial1.available() == 0){    
    delay(100);
  }
  //To read the first byte of the buffer withbut deleting it I use Serial1.peek(), and then I check whether it is a number or a letter. If button "CONTINUE" is tapped on the app
  //then it sends a "N" trough the bluetooth and the serial port, and I know that the data is not a phone number
  primerCaracter= Serial1.peek(); 
  delay(100);
  //Calling function isDigit I check if the first character is a number, and if so, I consider the  data is a phone number and I store it.
  if (isDigit(primerCaracter)){
    telefonos[contactos] = Serial1.readStringUntil('\n');
    
    // IMPORTANT.- On the app, international prefix number is not requested. After getting the phone number I convert it to international format by adding a prefix code.
    //This is needed because when receiving SMS messages from the configured contacts, the number will be in international format. 
    //To make the device compatible with any country code number I think that the best way  is to request to enter the whole number in the app, but I did it this
    //way to make it easier to the user, and because when I started the project my intention was to use it only with Spanish phone numbers. Prefix "+34" correspond to Spain country code. 
    //YOU HAVE TO TAKE THIS INTO ACCOUNT IF YOU USE THE DEVICE IN A DIFFERENT COUNTRY AND CHANGE THE CODE
    
    telefonos[contactos] = "+34" + telefonos[contactos];
    contactos = contactos +1;
  } //Close  if isDigit
  
  //If the first byte is not a digit, then it is either 'N' (no more phone numbers) or 'S' (a new phone number will be send from the app)
  //Go to the  setup function to understand how the variable primerCaracter is used
  if ((primerCaracter == 'N') || (primerCaracter =='S')){
   anadirOtroTelefono = primerCaracter; 
   }   
     
}  //end of aadirTelefono()


//Function coordinated with the app to set the lapse time with no activity detected that will trigger the alerts.
void configurarTiempoSinActividad(){
  while(Serial1.available()!=0){Serial1.read();} //  Vaco Buffer de entrada
  
  //Waiting for the data from the app 
  while(Serial1.available()==0){
     delay(100);
   }
  periodo = Serial1.readStringUntil('\n');
  //Convert the String type  to  Int
  intervaloAvisos = String(periodo).toInt();
  while(Serial1.available()!=0){Serial1.read();} // I empty again the serial buffer
}// end of configurarTiempoSinActividad()



///Set the current hour. The  value received from the app is within the range 0-23
void configurarHoraActual(){
  while(Serial1.available()!=0){Serial1.read();} //  Vaco Buffer de entrada  
  while(Serial1.available()==0){
     delay(100);
   }
  hora = Serial1.readStringUntil('\n');
  //Convert the type of variable hour from string to int, because I will need to operate with it
  horaActual = String(hora).toInt();
  while(Serial1.available()!=0){Serial1.read();} //Empty serial Buffer
}// End of configurarHoraActual()


//Set the current minute. The value received from the app is within the range 0-59.
void configurarMinutoActual(){
  while(Serial1.available()!=0){Serial1.read();} 
  while(Serial1.available()==0){
     delay(100);
   }
  minuto = Serial1.readStringUntil('\n');
  //Convert from string to int type
  minutoActual = String(minuto).toInt();
  while(Serial1.available()!=0){Serial1.read();} 
}//End of configurarMinutoActual()

//This function collects from the app the star time of the rest period (value in the range 0-23)
void configurarInicioPeriodoSinActividad(){

  while(Serial1.available()!=0){Serial1.read();} 
  while(Serial1.available()==0){
     delay(100);
   }
  inicioPeriodoSinActividadString = Serial1.readStringUntil('\n');  
  //Convert from String to int type
  inicioPeriodoSinActividad = String(inicioPeriodoSinActividadString).toInt();
  while(Serial1.available()!=0){Serial1.read();} 
}// End of configurarinicioPeriodoSinActividad()


//In this function  is set the duration of the rest period. The app sends a value in the range 6 to 10 hours
//Once more I remind that is important to know how long the person usually sleeps to make the devices more effective
void configurarDuracionPeriodoSinActividad(){
  while(Serial1.available()!=0){Serial1.read();}   
  while(Serial1.available()==0){
     delay(100);
   }
  duracionPeriodoSinActividadString = Serial1.readStringUntil('\n');  
  //Convert the String type to int
  duracionPeriodoSinActividad = String(duracionPeriodoSinActividadString).toInt();
  while(Serial1.available()!=0){Serial1.read();} 
}// end of configurarDuracionPeriodoSinActividad()



//Interrupt routine that will be called every day at the time in which the rest period begins
 void empiezaNoche(){  
  horarioNocturno = true;  //nighttime is on now
  tiempoDeNoche = millis(); //Update the value of this  variable in the moment that rest period begins
  }
  




//Function coordinated with the app that performs a working test of the PIR sensors after all the  settings have been saved
void testPresencia(){
   /*
   *In the app, instruccions are showed to leave the room  (everybody: humans and pets),where the device has been installed , then wait for 20 seconds 
   *and come in again placing near the Geriatrino. Then the button PRESENCE TEST has to be tapped in the app, and the text "TPr" is sent via bluetooh 
   *When this message  is receive in the serial buffer the sketch start to read the signals of the two PIR sensors to check if they have detected the  person. 
   *If everythin is correct Geriatrino sends to the app the character '1', otherwise he sends the character '0' 
   */
  while (Serial1.available()!=0){Serial1.read();} 
  while (Serial1.available() == 0){    
    delay(100);
  }
  mensajeRecibido = Serial1.readStringUntil('\n');
  if (mensajeRecibido == "TPr"){
    //I read the two PIR sensors to check if they are both activated. If the user has followed the instructions of the app he should  be in front of the
    //Geriatrino and they should activate.
    for (int z=0;z<=5;z++){
      delay(1000);
      if ((digitalRead(PinPIR1) == HIGH) && (digitalRead(PinPIR2) == HIGH)) {
        //If the two sensors are active then the led is on continously for 5 seconds and character '1' is send to the  app        
        digitalWrite(PinLed, HIGH);
        delay(5000);
        digitalWrite(PinLed, LOW);
        realizarTestPresencia = false;
        testPresenciaOK = true;
        Serial1.write("1");
        break;  // I leave the "for" loop
      }
     } //close for
   intentosTestPresencia +=1;
   //If the result of the first attempt is a failure, the led blinks for five seconds, and the app shows messages to repeat the  test and to tap the button again
   if ((intentosTestPresencia <= 2) && (realizarTestPresencia == true)){
     for (int p=0; p < 10 ; p++){
      digitalWrite(PinLed, HIGH);
      delay(500);
      digitalWrite(PinLed, LOW);
      delay(500);       
     }//cierro for
     testPresenciaOK = false; //In this variable is kept the result of the presence test
     Serial1.write("0"); //envo mensaje error test  
   }//cierro if intentosTestPresencia
   
   //Whether the result of the second attempt is correct or not, the app automatically passes to the next step and a warning  will be sent at the end of the process
   //y continuar con la  llamada a la siguiente funcin testPuertaAbierta()
  }  // close if (mensajeRecibido == "TPr")
  while(Serial1.available()!=0){Serial1.read();} //  Vaco Buffer de entrada
  
}  //End of testPresencia()


//Functions that checks the work of the door opening sensor and if it has been placed correctly on the door
//This function is called first to check that the door is closed. The app shows messages to close de door and then tap the CLOSED DOOR TEST button
void testPuertaCerrada(){
   
  //Waiting for a data from the app that is send when the TEST button is tapped.
  while (Serial1.available() == 0){    
    delay(100);
  }
  mensajeRecibido = Serial1.readStringUntil('\n');
  if (mensajeRecibido == "TPC"){  //This is what the app sends when the button is pressed  
    for (int z=0;z<=5;z++){
      delay(1000);
      //If a LOW level is read in PinDetectorPuerta then the sensor has  been installed correctly.
      if (digitalRead(PinDetectorPuerta) == LOW)  {
        //The led lights continously  for 5 seconds an '1' is sent to the  app        
        digitalWrite(PinLed, HIGH);
        delay(5000);
        digitalWrite(PinLed, LOW);
        realizarTestPuertaCerrada = false;
        testPuertaCerradaOK = true;
        Serial1.print("1");
        break;  
      }
     } //close loop for
   intentosTestPuertaCerrada +=1;
   //We have a second attempt if the result of the test is a fail, 
   if ((intentosTestPuertaCerrada <= 2) && (realizarTestPuertaCerrada == true)){
     for (int p=0; p < 10 ; p++){
      digitalWrite(PinLed, HIGH);
      delay(500);
      digitalWrite(PinLed, LOW);
      delay(500);       
     }//cierro for
     testPuertaCerradaOK = false; //This variable keeps the result of the CLOSED DOOR TEST
     Serial1.print("0"); //If there is a fail '0' is sent to the app
   }//end of if intentosTestPuertaCerrada()
   //If the second  attempt is also a fail the sketch jumps anyway to the next test and a warning message will be send later.
 }  // close  if (mensajeRecibido == "TPC"){
  while (Serial1.available()!=0){Serial1.read();} 
}  //End of testPuertaCerrada()


//This function is similar to the previous one, but it checks if the door is open
void testPuertaAbierta(){
   
  while (Serial1.available() == 0){    
    delay(100);
  }
  mensajeRecibido = Serial1.readStringUntil('\n');
  if (mensajeRecibido == "TPA"){      
    for (int z=0;z<=5;z++){
      delay(1000);
      if (digitalRead(PinDetectorPuerta) == HIGH)  {
              
        digitalWrite(PinLed, HIGH);
        delay(5000);
        digitalWrite(PinLed, LOW);
        realizarTestPuertaAbierta = false;
        testPuertaAbiertaOK = true;
        Serial1.print("1");
        break;  
      }
     } //cierro for
   intentosTestPuertaAbierta +=1;
   
   if ((intentosTestPuertaAbierta <= 2) && (realizarTestPuertaAbierta == true)){
     for (int p=0; p < 10 ; p++){
      digitalWrite(PinLed, HIGH);
      delay(500);
      digitalWrite(PinLed, LOW);
      delay(500);       
     }//cierro for
     testPuertaAbiertaOK = false; 
     Serial1.print("0"); 
   } 
  }//close  if (mensajeRecibido == "TPA"){
  while (Serial1.available()!=0){Serial1.read();} 
} //close testPuertaAbierta(){ 


//Function that try to connect to the GSM network
void conectarRedGSM(){

  //After the sensor tests, the app shows a new button RECEIVE MESSAGE. When we tap on it a character 'R' is sent to the Geriatrino
  while (Serial1.available() == 0){    
    delay(100);
  }
  mensajeRecibido = Serial1.readStringUntil('\n');
  if (mensajeRecibido == "R"){
    // Initializing GSM module
      
    intentosConexionRedGSM =0;
    while ((noConectado)&& (intentosConexionRedGSM < 6)){ 
     if(gsm.begin(PIN)==GSM_READY) {      
        noConectado = false;
        Serial1.print("1");  //character '1' is sent to the app when it connects to the network     
        enviarSMSConfiguracion();
        pendienteEnviarSMSConfiguracion = false;
      }
      
      else{
        intentosConexionRedGSM +=1;
      }
    } //cierro while
    
    if (noConectado == true) {
     Serial1.print("0");  //if connection fails, character '0' is sent to the app. The loop() function will start running and will try to connect again in one hour   
     pendienteEnviarSMSConfiguracion = true; 
    }
  }  //Close if mensajeRecibido =="R"

  //When the device connects to the network, SMS with all the information of the settings and the result of the tests are send to every phone number previously 
  //saved during the congiration process. But if after 6 attempts, connection is not established, The app finishes anyway, and information is showed  that 
  //the device will peridically try to connect to the network later. The SMS with the settings information are pending to be send until connection is achieved.
  
}  //End of ConectarRedGSM()

//This function works to send an SMS to all the contact phone numbers entered during the configuration process, with all the information about it.
void  enviarSMSConfiguracion(){
  mensaje = "CONTACTS: ";
  for (int f =0; f < contactos; f ++){
    mensaje.concat(telefonos[f]); 
    mensaje.concat(", ");
  }
  mensaje.concat('\n');
  mensaje.concat("PIN : ");
  mensaje.concat(numeroPIN);
  mensaje.concat('\n');
  mensaje.concat("Mode ");
  if (modoAlarma ==false){
    mensaje.concat("GERIATRINO");
  }
  else{
    mensaje.concat("ALARM");
  }
  mensaje.concat('\n');
  if (modoAlarma==false){   
    mensaje.concat("Warning period ");
    mensaje.concat(periodo);
    mensaje.concat(" hours");
    mensaje.concat('\n'); 
    mensaje.concat("Start rest period ");
    mensaje.concat(inicioPeriodoSinActividadString);
    mensaje.concat('\n');
    mensaje.concat("Duration rest period ");
    mensaje.concat(duracionPeriodoSinActividadString);
    
  }
  
  //After constructing the message it is sent to the every contact.
  for(int v=0; v<contactos; v ++){    
    telefonos[v].toCharArray(telefonoAviso,20); //convert the telephone number from string to array char type before calling sms.beginSMS
    sms.beginSMS(telefonoAviso);
    sms.print(mensaje);
    sms.endSMS(); 
  }        
}//cierro enviarSMSConfiguracion()


//If some failure is detected during the sensor tests, it is reported by sending SMS and asks to check the frequent failures guide.
void avisoFallosInstalacion(){
  mensaje = "FAULTY INSTALATION";
  mensaje.concat("\n");
  if (testPresenciaOK == false) {
    mensaje.concat("Fail Pres. test");
    mensaje.concat("\n");
   }
  if (testPuertaCerradaOK==false) {
      mensaje.concat("Fail Closed D. Test");
      mensaje.concat("\n");    
  }
  if (testPuertaAbiertaOK==false){
      mensaje.concat("Fail Open D. Test");
      mensaje.concat("\n");    
  }  
  mensaje.concat("SEE FREQUENT FAILURES");

  for(int v=0; v<contactos; v ++){    
    telefonos[v].toCharArray(telefonoAviso,20); 
    sms.beginSMS(telefonoAviso);
    sms.print(mensaje);
    sms.endSMS(); 
  }    
}

//This function send the alert messages to the contacs. The right message content has previously been saved in the variable "mensaje"
void enviarAvisoSMS(){
  for(int x=0; x<contactos; x ++){    
    telefonos[x].toCharArray(telefonoAviso,20); //Conver "telefonoAviso" from String to array char type before calling sms.beginSMS
    sms.beginSMS(telefonoAviso);
    sms.print(mensaje);
    sms.endSMS();        
  }  
}

//This funcion sends reply messages when The device receives an SMS asking for some information. Depending on the working mode (Geriatrino or Alarm mode)
//different actions will be taken. Everyone can customize this function to adapt it to his needs. The code only reads the first character of the  incoming
//SMS, and the  options are treated in a 'case' structure.
void enviarRespuestaSMS(){
   sms.remoteNumber(numeroCliente, 20); //The phone number from which the SMS has been sent is kept in "numeroCliente" 
   //Convert the array char to string type to compare the number whith the phoned numbers configured during the settings, which are the only numbers that
   //can communicate whith  Geriatrino
   numeroClienteString = String(numeroCliente);      
   if ((numeroClienteString == telefonos[0]) || (numeroClienteString == telefonos[1]) || (numeroClienteString == telefonos[2])){
     //The firs character of the incoming SMS is saved in "contenidoMensaje"
     contenidoMensaje = sms.peek();
     //After we pick the first character, we empty the incoming SMS buffer
     sms.flush();

     //Everybody can customize the actions he wants to perform according to his needs              
     switch(contenidoMensaje){
       //If the message is a '1' I set this variable to true to make the buzzer sound. Depending of the  working mode, the action and the answer of the  device  
       //are different.
       case '1':  
       //Geriatrino mode     
       if (modoAlarma == false){
        sonarSirena = true;  
        ciclosSirena =0; 
        mensaje = "The buzzer has been activated on request";
       }
       //Alarm mode
       else{
        alarmaActiva = true;  //When working in alarm mode, the  alarm will be armed after receiving a '1'    
        mensaje = "The alarm has been armed"; 
       }
       break;
       
       case '2':
       //In Geriatrino mode when a '2' is received and SMS is sent to the phone sender with info about the time in which last activity was detected
       if (modoAlarma == false){
        //First I get the hour and minute in which the last correct activation of both PIRs occured.
        horasUltimaActivacionCorrecta =(millis()-tiempoUltimaActivacionCorrecta)/3600000;
        minutosUltimaActivacionCorrecta = ((millis()-tiempoUltimaActivacionCorrecta) % 3600000)/60000;
        //Then I do the same to get the time of the last door opening.
        horasUltimaAperturaNevera = (millis()- tiempoUltimaAperturaNevera)/3600000;
        minutosUltimaAperturaNevera = ((millis() - tiempoUltimaAperturaNevera)%3600000)/60000;
        mensaje ="Last presence detected occured ";
        mensaje.concat(String(horasUltimaActivacionCorrecta)); mensaje.concat(" hours and ");
        mensaje.concat(String(minutosUltimaActivacionCorrecta)); mensaje.concat(" minutes");
        mensaje.concat(" ago.");
        mensaje.concat('\n');
        mensaje.concat("Last door opening occured ");
        mensaje.concat(String(horasUltimaAperturaNevera)); mensaje.concat(" hours and ");
        mensaje.concat(String(minutosUltimaAperturaNevera)); mensaje.concat(" minutes"); 
        mensaje.concat(" ago.");       
       }
       //On Alarm mode when a '2' is received the alarm is disarmed.
       else{
        alarmaActiva = false;  
        mensaje = "The alarm has been disarmed";
       }
       break;      

      case '3':
       //On Geriatrino mode if a '3' is received an SMS with the current time of the device is sent, and it also informs whether it is in rest period or not.
       if (modoAlarma == false){
        horaActual = rtc.getHours();
        minutoActual =rtc.getMinutes();
        segundoActual = rtc.getSeconds();
        mensaje = "La hora actual es  ";
        mensaje.concat(String(horaActual));
        mensaje.concat(" : ");
        mensaje.concat(String(minutoActual));
        mensaje.concat(" : ");
        mensaje.concat(String(segundoActual)); 
        mensaje.concat('\n');
        if (horarioNocturno == true) {
          mensaje.concat("Working in rest period");      
        }
        else{
          mensaje.concat("Working in active period");
        }
       }
       //On the Alarm mode I reserve this option to make the buzzer sound.
       else{
        sonarSirena = true;  
        ciclosSirena =0;  
        mensaje = "The buzzer has been activated and it will sound for 30 seconds";      
       }
       break; 
       
      case '4':       
       //Regardless of the operating mode, when a '4' is received and SMS is sent with a complete information about the settings
       mensaje = "CONTACTS: ";
      for (int f =0; f < contactos; f ++){
        mensaje.concat(telefonos[f]); 
        mensaje.concat(", ");
      }
      mensaje.concat('\n');
      mensaje.concat("PIN : ");
      mensaje.concat(numeroPIN);
      mensaje.concat('\n');
      mensaje.concat("Mode ");
      if (modoAlarma ==false){
       mensaje.concat("GERIATRINO");
      }
      else{
       mensaje.concat("ALARM");
      }
      mensaje.concat('\n');
      //When operating in Geriatrino Mode, the following information will be added to the SMS
      if (modoAlarma==false){   
       mensaje.concat("Warning period ");
       mensaje.concat(periodo);
       mensaje.concat(" hours");
       mensaje.concat('\n'); 
       mensaje.concat("Start rest period ");
       mensaje.concat(inicioPeriodoSinActividadString);
       mensaje.concat('\n');
       mensaje.concat("Duration rest period ");
       mensaje.concat(duracionPeriodoSinActividadString);           
      }  
      break; 
       
     default:
       break; 
           
   } //cierro switch
  //The answer is sent only to the sender phone number
  sms.beginSMS(numeroCliente);
  sms.print(mensaje);
  sms.endSMS();
  }   
} //End of EnviarRespuestaSMS()


//Function that analize the number of single activations for every PIR sensor and take actions to inform about it. The function is the same for both operating modes
void chequeoFuncionamientoPIR(){
  if(activacionSolitarioPIR1>activacionSolitarioPIR2){
        diferenciaActivaciones = activacionSolitarioPIR1-activacionSolitarioPIR2;
        // PIR's are read only every 60 seconds. I have decided to set a number of at least 20 single activations of a sensor within a period of 24 hours
        //to send the message reporting the misfunction. Everyone can adjust this threshold number and will depend on the quality of the sensors and on
        //the existing conditions in the places where the devices has been installed (insects, air streams, etc).
        //To reduce this number I HAVE PREVIOUSLY SET TO THE MINIMUM, THE SENSITIVITY CONTROL OF BOTH SENSORS
        if(diferenciaActivaciones > 20){  
          mensaje = "PIR1 has activated ";
          mensaje.concat(String(diferenciaActivaciones));          
          mensaje.concat(" more times than PIR2");
          
          enviarAvisoSMS();  
          }
      }
      else{
        diferenciaActivaciones =activacionSolitarioPIR2-activacionSolitarioPIR1;
        if(diferenciaActivaciones > 20){    
          mensaje = "PIR2 has activated ";
          mensaje.concat(String(diferenciaActivaciones));          
          mensaje.concat(" more times than PIR1");
          enviarAvisoSMS();           
        }
      }
      //After managing the different number of activations within 24 hours all the  counters and control variables are reset   
      activacionSolitarioPIR1=0;
      activacionSolitarioPIR2=0;
      tiempoDesdePrimeraActivacionSolitarioPIR1 = millis();
      tiempoDesdePrimeraActivacionSolitarioPIR2 = millis();
  }


//This function is called from loop() every hour to check the status of the network connection, if no connection detected it will be called every 15 minutes 
//until it is restored
void chequeoRedGSM(){
      noConectado = true;
      for(int n=0; n < 3; n++) {
        delay(1000);
        if(gsm.begin(PIN)==GSM_READY)
        {
         noConectado = false;     
         break;
        }  //cierro if
      }  //cierro for 
      ultimoChequeoRedGSM = millis();  
}


//Function that activates the  siren or buzzer in a different way depending on the operating mode 
void sirenaActiva(){
  //Geriatrino mode (this has been set to meet my particular needs)
  if (modoAlarma == false){
        
        digitalWrite(PinSirena, HIGH);
        digitalWrite(PinLed, HIGH);   //debug
        delay(2000);
        digitalWrite(PinSirena, LOW);
        digitalWrite(PinLed, LOW);   //debug
        delay(2000);
        ciclosSirena+=1;
        if (ciclosSirena ==8){  
          sonarSirena=false;
          ciclosSirena =0;
        }
  }
   //Alarm mode. (it sounds for 30 seconds) 
   else{
        digitalWrite(PinSirena, HIGH);
        digitalWrite(PinLed, HIGH);
        delay(1000);
        ciclosSirena +=1;
        if (ciclosSirena >30){
          sonarSirena = false;
          ciclosSirena=0;
        }
    }  
}  //End of sirenaActiva()




void loop() {

// G E R I A T R I N O    M O D E
while (modoAlarma == false) {
    //If the rest period is on (horarioNocturno == true), the program checks the time passed since it started, and checks when it finishes according to the duration 
    //of the rest period that was configured during the settings (3600000 * duracionPeriodoSinActividad)
    
    if ((horarioNocturno == true) && ((millis()- tiempoDeNoche) > (3600000 * duracionPeriodoSinActividad))){
      horarioNocturno = false;
      //When the rest period ends, I reset these variables to start counting the time with no activity detected.
      tiempoSinAperturaPuerta = millis(); 
      tiempoSinPresenciaDetectada = millis();
     }
 
    if ((digitalRead(PinPIR1) == HIGH) && (presenciaPIR1 == false)) {
      presenciaPIR1 = true;
      inicioPresenciaPIR1 = millis(); 
      delay(200);     
    }
    
    if ((digitalRead(PinPIR2) == HIGH) && (presenciaPIR2 == false)) {
      presenciaPIR2 = true;
      inicioPresenciaPIR2 = millis();
      delay(200);      
    }   

    //IMPORTANT.- To understand the following lines of code, I must say that I have set the control of the PIRs that determines the duration of the activation signal to the minimum.
    //After this is done, in all my test with different PIR sensors of the model HC-SR501, the output signal is not active for more than 8 seconds (normally on for 4 or 5 seconds)     
    //According to this, I read if  the PIR have been activated every 60 seconds
    if ((presenciaPIR1== true) &&((millis()- inicioPresenciaPIR1) >= 60000)){    
      //If both sensors were activated, it is considered a valid detection and I reset the flags
      if (presenciaPIR2 == true) {
        presenciaPIR1 = false;
        presenciaPIR2 = false;
        //This variable keeps the time since the last valid detection occured. The code uses it to trigger alarms, but only if we are not in the  rest period
        tiempoUltimaActivacionCorrecta = millis();
        //This variable is similar. It is not used to trigger alerts, but it is updated when a valid detection occurs even if it was during the rest period. 
        tiempoSinPresenciaDetectada = millis();
        horasSinPresencia =0;
        mensajesAvisoSinPresenciaDetectada =0;  
        //This variable will keep the number of valid activations. I do not use it in this sketch but it could be used in a later version to  implement a mechanism
        //that sends us on request what has been the level of  activity during a specified period of time. This way we can monitor the activity level of the person
        numeroActivacionesCorrectas = numeroActivacionesCorrectas +1; 
      }
      //If 60 seconds have passed since the PIR1 was activated and the other PIR2 was not, then  I consider a false  activation and I reset the flags.
      else{
        presenciaPIR1= false;
        activacionSolitarioPIR1 = activacionSolitarioPIR1 + 1;
        //If it is  the first single activation of PIR1 I reset the timer to start counting 24 hours since this event.
        if (activacionSolitarioPIR1 ==1){
          tiempoDesdePrimeraActivacionSolitarioPIR1= millis();
        } 
      }//close else          
    }// close   if (presenciaPIR1 == true)......
    
    
    //CONTROL PIR 2    This block is similar to the previous one, but now we check PIR2
    if ((presenciaPIR2== true) &&((millis()- inicioPresenciaPIR2) >= 60000)){  
      if (presenciaPIR1 == true) {
        presenciaPIR1 = false;
        presenciaPIR2 = false;
        tiempoUltimaActivacionCorrecta = millis();
        tiempoSinPresenciaDetectada =millis();
        mensajesAvisoSinPresenciaDetectada = 0;
        horasSinPresencia =0;
        numeroActivacionesCorrectas = numeroActivacionesCorrectas +1;
      }
      else{
        presenciaPIR2= false;
        activacionSolitarioPIR2 = activacionSolitarioPIR2 + 1;
        if (activacionSolitarioPIR2 ==1){
          tiempoDesdePrimeraActivacionSolitarioPIR2 = millis();
        } 
      }//close else      
    }// close    if (presenciaPIR2 == true)
    

    //After reading the signals of the PIR sensors, the sketch manages the information regarding the last valid activation, and the number of false activations. 
    //If active period is  on, and no activity  has been detected during the number of hours we configured during the  settings, then an alert is triggered 
    if  (((millis() - tiempoSinPresenciaDetectada) > (intervaloAvisos * 3600000)) && (horarioNocturno == false)){ 
        mensajesAvisoSinPresenciaDetectada = mensajesAvisoSinPresenciaDetectada +1;
        //This timer is reseted here, to start counting the  time for a new period before the  next alert message is sent
        tiempoSinPresenciaDetectada = millis();
        horasSinPresencia = mensajesAvisoSinPresenciaDetectada * intervaloAvisos;
        mensaje = "No presence detected during the last  ";
        mensaje.concat(String(horasSinPresencia));
        mensaje.concat(" hours");
        enviarAvisoSMS();  //We call the function that sends the SMS with the rigth message      
     }

  //The following block manages the number of false activations (activation of a single PIR sensor) within a period of 24 hours.
  if ((((millis()- tiempoDesdePrimeraActivacionSolitarioPIR1) > (86400000)) || ((millis() - tiempoDesdePrimeraActivacionSolitarioPIR2) > (86400000)))&& ((activacionSolitarioPIR1 != 0)||( activacionSolitarioPIR2 !=0))){
   chequeoFuncionamientoPIR();  //If 24 hours have passed since the first false activation was detected, then I  call this  function    
  } 

  //CONTROL OF DOOR SENSOR 
  if ((digitalRead(PinDetectorPuerta) == HIGH) && (puertaAbierta == false)) {
      //When the  door is opened we reset all the timer and flags related to this sensor
      puertaAbierta = true;
      inicioPuertaAbierta = millis();      
      tiempoUltimaAperturaNevera = millis();
      tiempoSinAperturaPuerta = millis();
      mensajesAvisoNeveraSinAbrir =0; 
      //This variable wont be used in this sketch, but we can use it in a later version to get the degree of activitiy  of the  monitored person
      numeroAperturasPuerta = numeroAperturasPuerta +1;
  }
    
    
  //After 10 minutos since the door opening was detected, (I consider 10 minutes are enough time to take out an put food in the fridge), I check if has been closed,
  //and if so, variable PuertaAbierta is set to  "false" and the code will start to check  when the door is  open again.
  if (((millis()-inicioPuertaAbierta) > (600000))  && (digitalRead(PinDetectorPuerta) == LOW)&& (puertaAbierta == true)){     
      puertaAbierta = false;
      HorasConPuertaAbierta =0;
      mensajesAvisoNeveraSinAbrir =0;
      }
   //But if after one hour since the door was opened, it is no closed, then I start to consider that the door has been left open.
   if (((millis()-inicioPuertaAbierta) > (3600000)) && (puertaAbierta == true)){
      //this counter controls the  number of hours that the  door remains open
      HorasConPuertaAbierta = HorasConPuertaAbierta +1;
      puertaAbierta = false;
      inicioPuertaAbierta = millis();
    }
    //If the fridge door remains opened for three hours in a row, then I consider it enough time to send and alert message
    if (HorasConPuertaAbierta >= 3){           
      mensaje = "The fridge door has been open for more than three hours";
      enviarAvisoSMS();
      HorasConPuertaAbierta =0;
      //sonarSirena = true; //We could make the buzzer sound to advise the person that the door is been open to much time     
    }

    //Now I deal with the case in which the door is properly closed,  but the sensor does not detect it is open again in the time we specified
    //to trigger alerts
    if (((millis() - tiempoSinAperturaPuerta) > (3600000 * intervaloAvisos)) && (horarioNocturno == false)){
         tiempoSinAperturaPuerta = millis();         
         mensajesAvisoNeveraSinAbrir = mensajesAvisoNeveraSinAbrir + 1;
         horasSinApertura = mensajesAvisoNeveraSinAbrir * intervaloAvisos;
         mensaje = "The fridge has not been open during the last  ";
         mensaje.concat(String(horasSinApertura));
         mensaje.concat(" hours");
         Serial.println(mensaje);      
         enviarAvisoSMS();    
      }      
     
   /* These lines should be uncommented if we want the buzzer to make sound
   if (sonarSirena == true){   
      sirenaActiva();
    }  */


  //Check the connection status every hour. If after an attempt to connect we have no connection, the device  will try  to  connect again in fifteen minutes
  if ( (((millis() - ultimoChequeoRedGSM) > 3600000)  || ((millis() - ultimoChequeoRedGSM) > 900000)) && (noConectado == true) )    {                
        chequeoRedGSM();
    } 

  //Now the program checks if an SMS has been received  
  if (sms.available()){
    enviarRespuestaSMS(); 
  } 

}//Close " while (modoAlarma == false)"  End of the code executed when operating in Geriatrino Mode 




//A L A R M        M O D E

while (modoAlarma== true){
  estadoPrevioAlarma = false;
  //I first check if an SMS has been received to arm the alarm. 
  if (sms.available()){
    //When operating in alarm mode, if a '1' is received then following function sets the variable "alarmaActiva"  to true 
    //If a '2' is received it il be set to false.
    enviarRespuestaSMS();
  } 
    
  //NOTE.-We could use another way to arm/disarm the alarm. A switcher could be used for  this purpose and then we will use the following line to control
  //the loop     while(digitalRead(PinAlarm) == true)
  
  //When the alarm is armed the next block will be always executed
  while(alarmaActiva == true) {  
    //After the alarm has been armed, in the first cicle of the loop all the timers and flags used to manage the information are reset
    //The program waits for 60 seconds to let people  leave the house before it starts reading the sensors
    if(estadoPrevioAlarma == false)
    {
      estadoPrevioAlarma = true;
      presenciaPIR1 = false;
      presenciaPIR2 = false;
      puertaAbierta = false;
      deteccionIntruso = false;
      avisoIntrusoEnviado = 0;
      envioMensajeActivacionPuertaEnSolitario = false; 
      ultimoChequeoRedGSM = millis();
      activacionSolitarioPIR1=0;
      activacionSolitarioPIR2=0;
      numeroSensoresActivos=0;
      delay(60000); 
    }
    
    //I check  the incoming SMS inside the 'while' loop too, to check if a message has been received to disarm the alarm.
    if (sms.available()){      
       enviarRespuestaSMS();  
    } 

    //When operating in Alarm mode the information provided by the sensors is  treated in  a different  way. I consider that an intruder has been
    //detected when at least two of the three sensors provide an active signal.
    if ((digitalRead(PinPIR1) == HIGH) && (presenciaPIR1 == false)) {
      presenciaPIR1 = true;
      numeroSensoresActivos = numeroSensoresActivos +1;
      inicioPresenciaPIR1 = millis();      
      delay(100);
    }
    
    if ((digitalRead(PinPIR2) == HIGH) && (presenciaPIR2 == false)) {
      presenciaPIR2 = true;
      numeroSensoresActivos = numeroSensoresActivos +1;
      inicioPresenciaPIR2 = millis();
      delay(100);      
     }

    if ((digitalRead(PinDetectorPuerta) == HIGH) && (puertaAbierta == false)) {
      puertaAbierta = true;
      numeroSensoresActivos = numeroSensoresActivos +1;
      inicioPuertaAbierta = millis();      
      delay(100);
    }
    
    //Regardless of the sensor type , if more than one sensor is active, an alarm is triggered.
    if ((numeroSensoresActivos >=2) && (deteccionIntruso ==false))  
    {                                                                                                       
      inicioDeteccionIntruso = millis();                                      
      deteccionIntruso = true;
      delay(100);
    }
    
    //If after 30 seconds since intrusion was detected, the alarm has not been disarmed, an alert message will be sent and the 
    //buzzer will sound for 30 seconds.
    //If nobody disarms the alarm, the intruder conditions will persist and the message will be sent again to a maximum of three times
    
    
    if (((millis() - inicioDeteccionIntruso) > 30000)&& (deteccionIntruso == true) && (avisoIntrusoEnviado <3)){  
      mensaje = "INTRUDER DETECTED, more than one sensor is active... ";
      mensaje.concat('\n');
      if (presenciaPIR1) {
        mensaje.concat("PIR 1 ACTIVE");
      }
      mensaje.concat('\n');
      if (presenciaPIR2) {
        mensaje.concat("PIR 2 ACTIVE");
      }
      mensaje.concat('\n');
      if (puertaAbierta) {
        mensaje.concat("DOOR OPEN");
      }
      else{
        mensaje.concat("DOOR HAS NOT BEEN OPEN");        
      }

      //Once the message has been mounted, it is sent to all the interest phone numbers an the flags are reset.
      enviarAvisoSMS();  
      deteccionIntruso = false;
      presenciaPIR1 = false;
      presenciaPIR2 = false;
      puertaAbierta = false;
      numeroSensoresActivos=0;
      avisoIntrusoEnviado +=1 ; //This variable controls that the alarm message is sent a maximum of three times
      sonarSirena = true;     
    }  // close    if ((millis() - inicioDeteccionIntruso) > 30000)&& ......


  //In the same way that it was programmed in the Geriatrino mode, the following block checks if both PIR sensors work properly, or one of them
  //gets active much  more often than  the  other one.  
  if ((presenciaPIR1== true) &&((millis()- inicioPresenciaPIR1) >= 50000)){  
      if (presenciaPIR2 == true) {
        presenciaPIR1 = false;
        presenciaPIR2 = false;        
      }
      //If only PIR1 is active then I consider a false activation
      else{
        presenciaPIR1= false;
        activacionSolitarioPIR1 = activacionSolitarioPIR1 + 1;
        if (activacionSolitarioPIR1 ==1){
          tiempoDesdePrimeraActivacionSolitarioPIR1= millis();
        } 
      }   
   }    
    
  //CONTROL PIR 2
  if ((presenciaPIR2== true) &&((millis()- inicioPresenciaPIR2) >= 50000)){  
     if (presenciaPIR1 == true) {
        presenciaPIR1 = false;
        presenciaPIR2 = false;
     }
     else{
        presenciaPIR2= false;
        activacionSolitarioPIR2 = activacionSolitarioPIR2 + 1;
        if (activacionSolitarioPIR2 ==1){
          tiempoDesdePrimeraActivacionSolitarioPIR2 = millis();
        } 
     }      
  }
    
 //24 hours after the first false activation occured, I call the function that checks how many false activations were detected and send a warning message if a limit is exceeded  
 if ((((millis()- tiempoDesdePrimeraActivacionSolitarioPIR1) > (86400000)) || ((millis() - tiempoDesdePrimeraActivacionSolitarioPIR2) > (86400000)))&& ((activacionSolitarioPIR1 != 0)||( activacionSolitarioPIR2 !=0))){
   if (activacionSolitarioPIR1 != activacionSolitarioPIR2){
     chequeoFuncionamientoPIR(); 
   }     
 }    
 
 
  //This blocks deals with the strange case in which only the door sensor is active. If after 50 seconds the door has been
  //open but no PIR is active then I consider a false activation. This could happen if one of the components of the 
  //door sensor is moved or it falls down to the floor  
  if(((millis() - inicioPuertaAbierta) > 50000)&& (puertaAbierta == true)){
    numeroSensoresActivos =0;
    puertaAbierta=false;
    envioMensajeActivacionPuertaEnSolitario +=1;
    if (envioMensajeActivacionPuertaEnSolitario < 3){
      mensaje ="El sensor puerta se ha activado en solitario";
      enviarAvisoSMS();      
    }
  }
    
  //The connection to the net will be checked every hour, but if an intruder detection is on the run, it will wait until this situation is managed
  //before calling the function chequeoRedGSM() 
  if ((((millis() - ultimoChequeoRedGSM) > 3600000) && (deteccionIntruso == false)) || (((millis() - ultimoChequeoRedGSM) > 900000)&&(deteccionIntruso == false) && (noConectado == true)))
    {                
     chequeoRedGSM();
    }
     
  //This function makes the siren sound if the variable sonarSirena is true
  if (sonarSirena) {
      sirenaActiva();  
    }
    
  }//close  while (alarmaActiva == true)
}//close while modoALARMA == true

}  // End of loop()

Credits

ramialcala

ramialcala

0 projects • 0 followers

Comments