Components and supplies
Arduino MKR GSM 1400
HC-06 Bluetooth Module
Door opening sensor
GSM Antenna
5v, 2 A, (100-240 AC adapter) power supply
PIR Motion Sensor (generic)
LiPo battery 3,7 v, 1200 mAh
Mini breadboard
Male-female Arduino jumper wires
Buzzer
Female 3,5 jack DC connector
Hologram Global IoT SIM Card
Tools and machines
Plier, Needle Nose
Soldering iron (generic)
3D Printer (generic)
Solder Wire, Lead Free
Apps and platforms
Arduino IDE
www.programarfacil.com
FreeCAD 3D moduler
Cura Ultimaker 3D printing software
MIT App Inventor
Project description
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.
1//Include the necessary libraries 2#include <MKRGSM.h> 3GSM gsm; 4GSM_SMS sms; 5#include <RTCZero.h> 6RTCZero rtc; 7 8//VARIABLES for SMS management 9char numeroCliente[20]; //In this array is saved the phone number from which an SMS is sent to the SIM card inserted in the MKRGSM 10String numeroClienteString; 11String mensaje; // This variable will be updated with the right message depending on the information we need to transmit 12char contenidoMensaje; //This variable wiil keep the content of the incoming SMS (initially the incoming messages have been thought to be single characters) 13boolean noConectado = true; // Variable to know whether the SIM card is connected to the telephone network or not. 14//The following variables will keep the hour and minute values of the last correct sensor activations (pressence and door opening) 15long int horasUltimaActivacionCorrecta, minutosUltimaActivacionCorrecta,horasUltimaAperturaNevera, minutosUltimaAperturaNevera; 16 17// VARIABLES used during device configuration 18//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 19//I dont dare to change a code line. Anybody familiar with the different type of variables can easily improve the code. 20 21 22String 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 23char 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 24String periodo; 25char PIN []="1234"; //Array char type variable to store the PIN of our SIM card. 26String numeroPIN; //String type variable to store the PIN number 27int longitudPIN; 28//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 29byte contactos =0; 30//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 31//But you can change these limits. The default value is 5 hours 32int intervaloAvisos =5; 33int tiempoSinActividad; 34//These are control variables that will be used while settings are performed from the app 35char anadirOtroTelefono ='S'; 36char primerCaracter; 37boolean realizarTestPresencia = true, testPresenciaOK=false, realizarTestPuertaCerrada = true, testPuertaCerradaOK = false; 38boolean realizarTestPuertaAbierta =true,testPuertaAbiertaOK = false, pendienteEnviarSMSConfiguracion = false, peticionConfiguracion = false; 39String mensajeRecibido; 40byte intentosTestPresencia =0,intentosTestPuertaCerrada =0, intentosTestPuertaAbierta =0,intentosConexionRedGSM= 0; 41String hora, minuto, inicioPeriodoSinActividadString, duracionPeriodoSinActividadString; 42int horaActual, minutoActual,segundoActual, inicioPeriodoSinActividad, duracionPeriodoSinActividad; 43boolean horarioNocturno = false; 44long int tiempoDeNoche; 45 46//MKR 1400 pin allocation 47# define PinLed 6 //In this case we use the built-in led of Arduino MKR1400 48# define PinPIR1 5 // Digital pin assigned to PIR 1 49# define PinPIR2 9 // Digital pin assigned to PIR2 50# define PinDetectorPuerta 7 //Digital pin assigned to door sensor 51# 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 52//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 53//But you could do it sending an SMS or via bluetooth connection. 54# define PinInterruptor 3 55//Digital pins 13 and 14 are reserved to connect the mkr1400 with TX an Rx pins of HC-06 Bluetooth module 56 57//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" 58boolean modoAlarma; 59String modoTrabajo; 60 61//Variables used for the control of the PIR sensors 62boolean presenciaPIR1 = false; 63boolean presenciaPIR2 = false; 64boolean deteccionPresencia = false; 65 66//The following variables will keep the millis() values in the moment that a certain event occurs. 67long int inicioPresenciaPIR1, inicioPresenciaPIR2, inicioDeteccionPresencia; 68long int primeraFalsaAlarmaPIR1, primeraFalsaAlarmaPIR2; 69long int tiempoUltimaActivacionCorrecta, tiempoDesdePrimeraActivacionSolitarioPIR1, tiempoDesdePrimeraActivacionSolitarioPIR2; 70long int tiempoSinAperturaPuerta,tiempoSinPresenciaDetectada; 71long int ultimoChequeoRedGSM = millis(); 72 73//Variables to manage false alarms or single activation of one PIR 74int activacionSolitarioPIR1=0,activacionSolitarioPIR2=0, numeroSensoresActivos=0, mensajesAvisoSinPresenciaDetectada=0; 75 76//This variable will count the number of right activations detected during one period to measure the activity level 77//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. 78int numeroActivacionesCorrectas =0; 79int diferenciaActivaciones =0; 80int horasSinPresencia; 81 82//VARIABLES used to manage the information provided by the door sensor 83boolean puertaAbierta = false; 84long int inicioPuertaAbierta,tiempoUltimaAperturaNevera; 85boolean mensajePuertaAbiertaNoEnviado = true; 86byte HorasConPuertaAbierta =0, mensajesAvisoNeveraSinAbrir =0; 87int numeroAperturasPuerta = 0, horasSinApertura =0; 88 89//This variable is used to control the activation of the buzzer if it is finally incorporated 90boolean sonarSirena = false; 91byte ciclosSirena=0; 92 93//VARIABLES to manage the way the device works in Alarm Mode 94boolean alarmaArmada=false,estadoPrevioAlarma=false,deteccionIntruso=false, alarmaActiva = false; 95#define PinAlarmaArmada 4 96long int inicioDeteccionIntruso, contadorParaNuevoEnvioSMS; 97byte avisoIntrusoEnviado=0, envioMensajeActivacionPuertaEnSolitario=0; 98 99 100//IMPORTANT REMARKS IF YOU USE ARDUINO IDE TO CONFIGURE YOUR GERIATRINO 101//To do the settings without the app the following variables will have declared with the values we want them to work: 102/*This is equivalent to the call the function anadirOtroTelefono(). Remember to write the phone number in the international format) */ 103//String telefonos[3]={"phone number 1", "phone number 2", "phone number 3"} 104/*You set the operating mode, Alarm mode (true), Geriatrino mode (false) */ 105//boolean alarmMode = true or false; 106/*If you choose the Geriatrino mode you set the time parameters needed to manage the alarm messages */ 107// int inicioPeriodoSinActividad = X; This is equivalent to call the function 108// int duracionPeriodoSinActividad = y; 109// int intervaloAvisos = z; 110/*You set the PIN number of the SIM card to connect to the phone network */ 111// char PIN []="1234"; 112//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 113//The current hour and minute could be get directly sending it through the IDE serial port during installation process. 114//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 115//rtc.begin(); // initialize RTC 116//Serial.println("To set the current hour type the value and then press Enter key"); 117/*Waiting to read data from the IDE serial port*/ 118//while (Serial.available()==0){ delay(100); } 119//Valor_hora = Serial.readStringUntil('\n'); 120//hora = String(Valor_hora).toInt(); 121//rtc.setHours(hora); 122//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 "); 123/*Waiting for the value of the current minute */ 124//while (Serial.available ()==0){ delay(100); } 125//Valor_minuto = Serial.readStringUntil('\n'); 126//minuto = String(Valor_minuto).toInt(); 127//rtc.setMinutes(minuto); 128//Serial.print("The value of the current minute is : ");Serial.println(Valor_minuto); 129//Serial.println("Type the value for the current second"); 130/*Waiting for the value of current second...*/ 131//while (Serial.available ()==0){ delay(100); } 132//Valor_segundo = Serial.readStringUntil('\n'); 133//segundo = String(Valor_segundo).toInt(); 134//Serial.print("The value of current second is : ");Serial.println(Valor_segundo); 135//rtc.setSeconds(segundo); 136//horaActual = rtc.getHours(); 137//minutoActual =rtc.getMinutes(); 138//segundoActual = rtc.getSeconds(); 139//Serial.print("La current time is ");Serial.print(horaActual);Serial.print(":");Serial.print(minutoActual);Serial.print(":");Serial.print(segundoActual);Serial.println("..."); 140/*I set the Interrupt time routine that determines the start of the rest period with the entered values */ 141//rtc.setAlarmTime(inicioPeriodoSinActividad,0,0); 142//rtc.enableAlarm(rtc.MATCH_HHMMSS); 143//rtc.attachInterrupt(empiezaNoche); 144/*All the calls to the functions related to the app must be supressed and you have to omit the sensor tests too*/ 145 146void setup() { 147 //IMPORTANT.-To work with bluetooth and the serial port in MKR boards you have to use "Serial1" instead of "Serial" 148 Serial1.begin(9600); 149 //Pins associated to PIR and door sensors are declared as inputs 150 pinMode(PinPIR1, INPUT); 151 pinMode(PinPIR2, INPUT); 152 //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 153 //I activate the internal pull-up resistor of this pin to read a HIGH level when the door is opened 154 pinMode(PinDetectorPuerta, INPUT_PULLUP); 155 //PinLed is declared as an Output 156 pinMode(PinLed, OUTPUT); 157 digitalWrite(PinLed, LOW); 158 rtc.begin(); // initialize RTC 159 160 //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. 161 //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, 162 //then the program automatically leaves the loop 163 while (((contactos==0) || (anadirOtroTelefono == 'S'))&&(contactos <3)) { 164 anadirTelefono(); //This function is called to add a phone number 165 } 166 //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. 167 //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 168 if (contactos == 3){ 169 //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 170 while (Serial1.available()!=0){Serial1.read();} 171 //Waiting to the next character from the app 172 while (Serial1.available() == 0){ 173 delay(100); 174 } //close while 175 anadirOtroTelefono = Serial1.read(); // 176 } //close if 177 178 //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. 179 //It is essential to enter it properly, otherwise the PIN will be accepted but the MKR1400 will not connect to the network. 180 while (Serial1.available()!=0){Serial1.read();} 181 while (Serial1.available() == 0){ 182 delay(100); 183 } //close while 184 185 186 //The PIN number is stored in the String type variable "numeroPIN" 187 numeroPIN= Serial1.readStringUntil('\n'); 188 longitudPIN = numeroPIN.length(); //get the length of the string 189 //I convert the variable numeroPIN to the type "array char" because the function gsm.begin() requires this type of data, 190 //I apply (longitudPIN+1) to get the last blank space needed in the array structure 191 numeroPIN.toCharArray(PIN,longitudPIN +1); 192 193 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 194 195 //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 196 //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 197 //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 198 //to perform the sensor tests. 199 200 while (Serial1.available()!=0){Serial1.read();} 201 //Waiting for the next character from the app 202 while (Serial1.available() == 0){ 203 delay(100); 204 } //cierro while 205 206 207 modoTrabajo= Serial1.readStringUntil('\n'); 208 if (modoTrabajo == "A"){ 209 modoAlarma = true; 210 } 211 else{ 212 modoAlarma = false; //when modoAlarma = false that means we are working on Geriatrino Mode 213 } 214 215 while (Serial1.available()!=0){Serial1.read();} 216 217 //If Geriatrino mode is selected,(modoAlarma = False) some aditional time settings are required and the sketch calls one by one the functions 218 //to configure these parameters. 219 if (modoAlarma==false){ 220 //Function to set the lapse of time to trigger alerts if no activity is detected 221 configurarTiempoSinActividad(); 222 //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 223 //I have not tried it but I think it could be possible. 224 configurarHoraActual(); 225 rtc.setHours(horaActual); 226 configurarMinutoActual(); 227 rtc.setMinutes(minutoActual); 228 rtc.setSeconds(0); 229 230 //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 231 //will be taken into account and registered. It is important to know the habits of the monitored person to get an efective monitoring work. 232 configurarInicioPeriodoSinActividad(); 233 configurarDuracionPeriodoSinActividad(); 234 //After setting the rest period, I configure the interrupt routine associated to the timer, to stablish the beginning of the rest time 235 rtc.setAlarmTime(inicioPeriodoSinActividad,0,0); 236 rtc.enableAlarm(rtc.MATCH_HHMMSS); 237 rtc.attachInterrupt(empiezaNoche); 238 239 //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 240 //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. 241 if ( ((horaActual < inicioPeriodoSinActividad)&&(horaActual >=(inicioPeriodoSinActividad + duracionPeriodoSinActividad -24))) || (horaActual >= (inicioPeriodoSinActividad + duracionPeriodoSinActividad)) ) { 242 horarioNocturno = false; 243 } 244 else{ 245 horarioNocturno = true; 246 if ((horaActual < inicioPeriodoSinActividad)&& (horarioNocturno==true)){ 247 tiempoDeNoche = millis()-((((duracionPeriodoSinActividad - horaActual) *60) + minutoActual) *60000) ; 248 } 249 if ((horaActual >= inicioPeriodoSinActividad)&& (horarioNocturno==true)){ 250 tiempoDeNoche = millis()-((((horaActual-inicioPeriodoSinActividad)*60) + minutoActual) * 60000); 251 } 252 } 253 } //end of if modoAlarma == false 254 255 256 //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 257 //will be repeated one more time. 258 while ((realizarTestPresencia == true)&&(intentosTestPresencia <2)){ 259 //This function checks that the two PIR sensors are working ok. 260 testPresencia(); 261 } 262 263 while ((realizarTestPuertaCerrada == true)&&(intentosTestPuertaCerrada <2)){ 264 //This function checks the signal of the door sensor when the fridge door is closed 265 testPuertaCerrada(); 266 } 267 268 while ((realizarTestPuertaAbierta == true)&&(intentosTestPuertaAbierta <2)){ 269 //This function checks the signal of the door sensor when the fridge door is open 270 testPuertaAbierta(); 271 } 272 273 //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, 274 //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. 275 conectarRedGSM(); 276 //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 277 if ((noConectado==true)||(testPresenciaOK==false)||(testPuertaCerradaOK==false)||(testPuertaAbiertaOK==false)){ 278 avisoFallosInstalacion(); 279 } 280 281 //At the end of setup() the time values to these to variables are updated with millis() 282 tiempoUltimaActivacionCorrecta = millis(); //time for the last simoultaneous activation of both PIR 283 tiempoUltimaAperturaNevera = millis(); //time for the last door opening. 284 285} //END SETUP 286 287 288 289//This function is coordinated with the app to configure de contact phone numbers 290void anadirTelefono(){ 291 292 while (Serial1.available()!=0){Serial1.read();} 293 //Waiting for a data coming from the app through the serial port 294 while (Serial1.available() == 0){ 295 delay(100); 296 } 297 //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 298 //then it sends a "N" trough the bluetooth and the serial port, and I know that the data is not a phone number 299 primerCaracter= Serial1.peek(); 300 delay(100); 301 //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. 302 if (isDigit(primerCaracter)){ 303 telefonos[contactos] = Serial1.readStringUntil('\n'); 304 305 // 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. 306 //This is needed because when receiving SMS messages from the configured contacts, the number will be in international format. 307 //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 308 //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. 309 //YOU HAVE TO TAKE THIS INTO ACCOUNT IF YOU USE THE DEVICE IN A DIFFERENT COUNTRY AND CHANGE THE CODE 310 311 telefonos[contactos] = "+34" + telefonos[contactos]; 312 contactos = contactos +1; 313 } //Close if isDigit 314 315 //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) 316 //Go to the setup function to understand how the variable primerCaracter is used 317 if ((primerCaracter == 'N') || (primerCaracter =='S')){ 318 anadirOtroTelefono = primerCaracter; 319 } 320 321} //end of aadirTelefono() 322 323 324//Function coordinated with the app to set the lapse time with no activity detected that will trigger the alerts. 325void configurarTiempoSinActividad(){ 326 while(Serial1.available()!=0){Serial1.read();} // Vaco Buffer de entrada 327 328 //Waiting for the data from the app 329 while(Serial1.available()==0){ 330 delay(100); 331 } 332 periodo = Serial1.readStringUntil('\n'); 333 //Convert the String type to Int 334 intervaloAvisos = String(periodo).toInt(); 335 while(Serial1.available()!=0){Serial1.read();} // I empty again the serial buffer 336}// end of configurarTiempoSinActividad() 337 338 339 340///Set the current hour. The value received from the app is within the range 0-23 341void configurarHoraActual(){ 342 while(Serial1.available()!=0){Serial1.read();} // Vaco Buffer de entrada 343 while(Serial1.available()==0){ 344 delay(100); 345 } 346 hora = Serial1.readStringUntil('\n'); 347 //Convert the type of variable hour from string to int, because I will need to operate with it 348 horaActual = String(hora).toInt(); 349 while(Serial1.available()!=0){Serial1.read();} //Empty serial Buffer 350}// End of configurarHoraActual() 351 352 353//Set the current minute. The value received from the app is within the range 0-59. 354void configurarMinutoActual(){ 355 while(Serial1.available()!=0){Serial1.read();} 356 while(Serial1.available()==0){ 357 delay(100); 358 } 359 minuto = Serial1.readStringUntil('\n'); 360 //Convert from string to int type 361 minutoActual = String(minuto).toInt(); 362 while(Serial1.available()!=0){Serial1.read();} 363}//End of configurarMinutoActual() 364 365//This function collects from the app the star time of the rest period (value in the range 0-23) 366void configurarInicioPeriodoSinActividad(){ 367 368 while(Serial1.available()!=0){Serial1.read();} 369 while(Serial1.available()==0){ 370 delay(100); 371 } 372 inicioPeriodoSinActividadString = Serial1.readStringUntil('\n'); 373 //Convert from String to int type 374 inicioPeriodoSinActividad = String(inicioPeriodoSinActividadString).toInt(); 375 while(Serial1.available()!=0){Serial1.read();} 376}// End of configurarinicioPeriodoSinActividad() 377 378 379//In this function is set the duration of the rest period. The app sends a value in the range 6 to 10 hours 380//Once more I remind that is important to know how long the person usually sleeps to make the devices more effective 381void configurarDuracionPeriodoSinActividad(){ 382 while(Serial1.available()!=0){Serial1.read();} 383 while(Serial1.available()==0){ 384 delay(100); 385 } 386 duracionPeriodoSinActividadString = Serial1.readStringUntil('\n'); 387 //Convert the String type to int 388 duracionPeriodoSinActividad = String(duracionPeriodoSinActividadString).toInt(); 389 while(Serial1.available()!=0){Serial1.read();} 390}// end of configurarDuracionPeriodoSinActividad() 391 392 393 394//Interrupt routine that will be called every day at the time in which the rest period begins 395 void empiezaNoche(){ 396 horarioNocturno = true; //nighttime is on now 397 tiempoDeNoche = millis(); //Update the value of this variable in the moment that rest period begins 398 } 399 400 401 402 403 404//Function coordinated with the app that performs a working test of the PIR sensors after all the settings have been saved 405void testPresencia(){ 406 /* 407 *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 408 *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 409 *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. 410 *If everythin is correct Geriatrino sends to the app the character '1', otherwise he sends the character '0' 411 */ 412 while (Serial1.available()!=0){Serial1.read();} 413 while (Serial1.available() == 0){ 414 delay(100); 415 } 416 mensajeRecibido = Serial1.readStringUntil('\n'); 417 if (mensajeRecibido == "TPr"){ 418 //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 419 //Geriatrino and they should activate. 420 for (int z=0;z<=5;z++){ 421 delay(1000); 422 if ((digitalRead(PinPIR1) == HIGH) && (digitalRead(PinPIR2) == HIGH)) { 423 //If the two sensors are active then the led is on continously for 5 seconds and character '1' is send to the app 424 digitalWrite(PinLed, HIGH); 425 delay(5000); 426 digitalWrite(PinLed, LOW); 427 realizarTestPresencia = false; 428 testPresenciaOK = true; 429 Serial1.write("1"); 430 break; // I leave the "for" loop 431 } 432 } //close for 433 intentosTestPresencia +=1; 434 //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 435 if ((intentosTestPresencia <= 2) && (realizarTestPresencia == true)){ 436 for (int p=0; p < 10 ; p++){ 437 digitalWrite(PinLed, HIGH); 438 delay(500); 439 digitalWrite(PinLed, LOW); 440 delay(500); 441 }//cierro for 442 testPresenciaOK = false; //In this variable is kept the result of the presence test 443 Serial1.write("0"); //envo mensaje error test 444 }//cierro if intentosTestPresencia 445 446 //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 447 //y continuar con la llamada a la siguiente funcin testPuertaAbierta() 448 } // close if (mensajeRecibido == "TPr") 449 while(Serial1.available()!=0){Serial1.read();} // Vaco Buffer de entrada 450 451} //End of testPresencia() 452 453 454//Functions that checks the work of the door opening sensor and if it has been placed correctly on the door 455//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 456void testPuertaCerrada(){ 457 458 //Waiting for a data from the app that is send when the TEST button is tapped. 459 while (Serial1.available() == 0){ 460 delay(100); 461 } 462 mensajeRecibido = Serial1.readStringUntil('\n'); 463 if (mensajeRecibido == "TPC"){ //This is what the app sends when the button is pressed 464 for (int z=0;z<=5;z++){ 465 delay(1000); 466 //If a LOW level is read in PinDetectorPuerta then the sensor has been installed correctly. 467 if (digitalRead(PinDetectorPuerta) == LOW) { 468 //The led lights continously for 5 seconds an '1' is sent to the app 469 digitalWrite(PinLed, HIGH); 470 delay(5000); 471 digitalWrite(PinLed, LOW); 472 realizarTestPuertaCerrada = false; 473 testPuertaCerradaOK = true; 474 Serial1.print("1"); 475 break; 476 } 477 } //close loop for 478 intentosTestPuertaCerrada +=1; 479 //We have a second attempt if the result of the test is a fail, 480 if ((intentosTestPuertaCerrada <= 2) && (realizarTestPuertaCerrada == true)){ 481 for (int p=0; p < 10 ; p++){ 482 digitalWrite(PinLed, HIGH); 483 delay(500); 484 digitalWrite(PinLed, LOW); 485 delay(500); 486 }//cierro for 487 testPuertaCerradaOK = false; //This variable keeps the result of the CLOSED DOOR TEST 488 Serial1.print("0"); //If there is a fail '0' is sent to the app 489 }//end of if intentosTestPuertaCerrada() 490 //If the second attempt is also a fail the sketch jumps anyway to the next test and a warning message will be send later. 491 } // close if (mensajeRecibido == "TPC"){ 492 while (Serial1.available()!=0){Serial1.read();} 493} //End of testPuertaCerrada() 494 495 496//This function is similar to the previous one, but it checks if the door is open 497void testPuertaAbierta(){ 498 499 while (Serial1.available() == 0){ 500 delay(100); 501 } 502 mensajeRecibido = Serial1.readStringUntil('\n'); 503 if (mensajeRecibido == "TPA"){ 504 for (int z=0;z<=5;z++){ 505 delay(1000); 506 if (digitalRead(PinDetectorPuerta) == HIGH) { 507 508 digitalWrite(PinLed, HIGH); 509 delay(5000); 510 digitalWrite(PinLed, LOW); 511 realizarTestPuertaAbierta = false; 512 testPuertaAbiertaOK = true; 513 Serial1.print("1"); 514 break; 515 } 516 } //cierro for 517 intentosTestPuertaAbierta +=1; 518 519 if ((intentosTestPuertaAbierta <= 2) && (realizarTestPuertaAbierta == true)){ 520 for (int p=0; p < 10 ; p++){ 521 digitalWrite(PinLed, HIGH); 522 delay(500); 523 digitalWrite(PinLed, LOW); 524 delay(500); 525 }//cierro for 526 testPuertaAbiertaOK = false; 527 Serial1.print("0"); 528 } 529 }//close if (mensajeRecibido == "TPA"){ 530 while (Serial1.available()!=0){Serial1.read();} 531} //close testPuertaAbierta(){ 532 533 534//Function that try to connect to the GSM network 535void conectarRedGSM(){ 536 537 //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 538 while (Serial1.available() == 0){ 539 delay(100); 540 } 541 mensajeRecibido = Serial1.readStringUntil('\n'); 542 if (mensajeRecibido == "R"){ 543 // Initializing GSM module 544 545 intentosConexionRedGSM =0; 546 while ((noConectado)&& (intentosConexionRedGSM < 6)){ 547 if(gsm.begin(PIN)==GSM_READY) { 548 noConectado = false; 549 Serial1.print("1"); //character '1' is sent to the app when it connects to the network 550 enviarSMSConfiguracion(); 551 pendienteEnviarSMSConfiguracion = false; 552 } 553 554 else{ 555 intentosConexionRedGSM +=1; 556 } 557 } //cierro while 558 559 if (noConectado == true) { 560 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 561 pendienteEnviarSMSConfiguracion = true; 562 } 563 } //Close if mensajeRecibido =="R" 564 565 //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 566 //saved during the congiration process. But if after 6 attempts, connection is not established, The app finishes anyway, and information is showed that 567 //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. 568 569} //End of ConectarRedGSM() 570 571//This function works to send an SMS to all the contact phone numbers entered during the configuration process, with all the information about it. 572void enviarSMSConfiguracion(){ 573 mensaje = "CONTACTS: "; 574 for (int f =0; f < contactos; f ++){ 575 mensaje.concat(telefonos[f]); 576 mensaje.concat(", "); 577 } 578 mensaje.concat('\n'); 579 mensaje.concat("PIN : "); 580 mensaje.concat(numeroPIN); 581 mensaje.concat('\n'); 582 mensaje.concat("Mode "); 583 if (modoAlarma ==false){ 584 mensaje.concat("GERIATRINO"); 585 } 586 else{ 587 mensaje.concat("ALARM"); 588 } 589 mensaje.concat('\n'); 590 if (modoAlarma==false){ 591 mensaje.concat("Warning period "); 592 mensaje.concat(periodo); 593 mensaje.concat(" hours"); 594 mensaje.concat('\n'); 595 mensaje.concat("Start rest period "); 596 mensaje.concat(inicioPeriodoSinActividadString); 597 mensaje.concat('\n'); 598 mensaje.concat("Duration rest period "); 599 mensaje.concat(duracionPeriodoSinActividadString); 600 601 } 602 603 //After constructing the message it is sent to the every contact. 604 for(int v=0; v<contactos; v ++){ 605 telefonos[v].toCharArray(telefonoAviso,20); //convert the telephone number from string to array char type before calling sms.beginSMS 606 sms.beginSMS(telefonoAviso); 607 sms.print(mensaje); 608 sms.endSMS(); 609 } 610}//cierro enviarSMSConfiguracion() 611 612 613//If some failure is detected during the sensor tests, it is reported by sending SMS and asks to check the frequent failures guide. 614void avisoFallosInstalacion(){ 615 mensaje = "FAULTY INSTALATION"; 616 mensaje.concat("\ 617"); 618 if (testPresenciaOK == false) { 619 mensaje.concat("Fail Pres. test"); 620 mensaje.concat("\ 621"); 622 } 623 if (testPuertaCerradaOK==false) { 624 mensaje.concat("Fail Closed D. Test"); 625 mensaje.concat("\ 626"); 627 } 628 if (testPuertaAbiertaOK==false){ 629 mensaje.concat("Fail Open D. Test"); 630 mensaje.concat("\ 631"); 632 } 633 mensaje.concat("SEE FREQUENT FAILURES"); 634 635 for(int v=0; v<contactos; v ++){ 636 telefonos[v].toCharArray(telefonoAviso,20); 637 sms.beginSMS(telefonoAviso); 638 sms.print(mensaje); 639 sms.endSMS(); 640 } 641} 642 643//This function send the alert messages to the contacs. The right message content has previously been saved in the variable "mensaje" 644void enviarAvisoSMS(){ 645 for(int x=0; x<contactos; x ++){ 646 telefonos[x].toCharArray(telefonoAviso,20); //Conver "telefonoAviso" from String to array char type before calling sms.beginSMS 647 sms.beginSMS(telefonoAviso); 648 sms.print(mensaje); 649 sms.endSMS(); 650 } 651} 652 653//This funcion sends reply messages when The device receives an SMS asking for some information. Depending on the working mode (Geriatrino or Alarm mode) 654//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 655//SMS, and the options are treated in a 'case' structure. 656void enviarRespuestaSMS(){ 657 sms.remoteNumber(numeroCliente, 20); //The phone number from which the SMS has been sent is kept in "numeroCliente" 658 //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 659 //can communicate whith Geriatrino 660 numeroClienteString = String(numeroCliente); 661 if ((numeroClienteString == telefonos[0]) || (numeroClienteString == telefonos[1]) || (numeroClienteString == telefonos[2])){ 662 //The firs character of the incoming SMS is saved in "contenidoMensaje" 663 contenidoMensaje = sms.peek(); 664 //After we pick the first character, we empty the incoming SMS buffer 665 sms.flush(); 666 667 //Everybody can customize the actions he wants to perform according to his needs 668 switch(contenidoMensaje){ 669 //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 670 //are different. 671 case '1': 672 //Geriatrino mode 673 if (modoAlarma == false){ 674 sonarSirena = true; 675 ciclosSirena =0; 676 mensaje = "The buzzer has been activated on request"; 677 } 678 //Alarm mode 679 else{ 680 alarmaActiva = true; //When working in alarm mode, the alarm will be armed after receiving a '1' 681 mensaje = "The alarm has been armed"; 682 } 683 break; 684 685 case '2': 686 //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 687 if (modoAlarma == false){ 688 //First I get the hour and minute in which the last correct activation of both PIRs occured. 689 horasUltimaActivacionCorrecta =(millis()-tiempoUltimaActivacionCorrecta)/3600000; 690 minutosUltimaActivacionCorrecta = ((millis()-tiempoUltimaActivacionCorrecta) % 3600000)/60000; 691 //Then I do the same to get the time of the last door opening. 692 horasUltimaAperturaNevera = (millis()- tiempoUltimaAperturaNevera)/3600000; 693 minutosUltimaAperturaNevera = ((millis() - tiempoUltimaAperturaNevera)%3600000)/60000; 694 mensaje ="Last presence detected occured "; 695 mensaje.concat(String(horasUltimaActivacionCorrecta)); mensaje.concat(" hours and "); 696 mensaje.concat(String(minutosUltimaActivacionCorrecta)); mensaje.concat(" minutes"); 697 mensaje.concat(" ago."); 698 mensaje.concat('\n'); 699 mensaje.concat("Last door opening occured "); 700 mensaje.concat(String(horasUltimaAperturaNevera)); mensaje.concat(" hours and "); 701 mensaje.concat(String(minutosUltimaAperturaNevera)); mensaje.concat(" minutes"); 702 mensaje.concat(" ago."); 703 } 704 //On Alarm mode when a '2' is received the alarm is disarmed. 705 else{ 706 alarmaActiva = false; 707 mensaje = "The alarm has been disarmed"; 708 } 709 break; 710 711 case '3': 712 //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. 713 if (modoAlarma == false){ 714 horaActual = rtc.getHours(); 715 minutoActual =rtc.getMinutes(); 716 segundoActual = rtc.getSeconds(); 717 mensaje = "La hora actual es "; 718 mensaje.concat(String(horaActual)); 719 mensaje.concat(" : "); 720 mensaje.concat(String(minutoActual)); 721 mensaje.concat(" : "); 722 mensaje.concat(String(segundoActual)); 723 mensaje.concat('\n'); 724 if (horarioNocturno == true) { 725 mensaje.concat("Working in rest period"); 726 } 727 else{ 728 mensaje.concat("Working in active period"); 729 } 730 } 731 //On the Alarm mode I reserve this option to make the buzzer sound. 732 else{ 733 sonarSirena = true; 734 ciclosSirena =0; 735 mensaje = "The buzzer has been activated and it will sound for 30 seconds"; 736 } 737 break; 738 739 case '4': 740 //Regardless of the operating mode, when a '4' is received and SMS is sent with a complete information about the settings 741 mensaje = "CONTACTS: "; 742 for (int f =0; f < contactos; f ++){ 743 mensaje.concat(telefonos[f]); 744 mensaje.concat(", "); 745 } 746 mensaje.concat('\n'); 747 mensaje.concat("PIN : "); 748 mensaje.concat(numeroPIN); 749 mensaje.concat('\n'); 750 mensaje.concat("Mode "); 751 if (modoAlarma ==false){ 752 mensaje.concat("GERIATRINO"); 753 } 754 else{ 755 mensaje.concat("ALARM"); 756 } 757 mensaje.concat('\n'); 758 //When operating in Geriatrino Mode, the following information will be added to the SMS 759 if (modoAlarma==false){ 760 mensaje.concat("Warning period "); 761 mensaje.concat(periodo); 762 mensaje.concat(" hours"); 763 mensaje.concat('\n'); 764 mensaje.concat("Start rest period "); 765 mensaje.concat(inicioPeriodoSinActividadString); 766 mensaje.concat('\n'); 767 mensaje.concat("Duration rest period "); 768 mensaje.concat(duracionPeriodoSinActividadString); 769 } 770 break; 771 772 default: 773 break; 774 775 } //cierro switch 776 //The answer is sent only to the sender phone number 777 sms.beginSMS(numeroCliente); 778 sms.print(mensaje); 779 sms.endSMS(); 780 } 781} //End of EnviarRespuestaSMS() 782 783 784//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 785void chequeoFuncionamientoPIR(){ 786 if(activacionSolitarioPIR1>activacionSolitarioPIR2){ 787 diferenciaActivaciones = activacionSolitarioPIR1-activacionSolitarioPIR2; 788 // 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 789 //to send the message reporting the misfunction. Everyone can adjust this threshold number and will depend on the quality of the sensors and on 790 //the existing conditions in the places where the devices has been installed (insects, air streams, etc). 791 //To reduce this number I HAVE PREVIOUSLY SET TO THE MINIMUM, THE SENSITIVITY CONTROL OF BOTH SENSORS 792 if(diferenciaActivaciones > 20){ 793 mensaje = "PIR1 has activated "; 794 mensaje.concat(String(diferenciaActivaciones)); 795 mensaje.concat(" more times than PIR2"); 796 797 enviarAvisoSMS(); 798 } 799 } 800 else{ 801 diferenciaActivaciones =activacionSolitarioPIR2-activacionSolitarioPIR1; 802 if(diferenciaActivaciones > 20){ 803 mensaje = "PIR2 has activated "; 804 mensaje.concat(String(diferenciaActivaciones)); 805 mensaje.concat(" more times than PIR1"); 806 enviarAvisoSMS(); 807 } 808 } 809 //After managing the different number of activations within 24 hours all the counters and control variables are reset 810 activacionSolitarioPIR1=0; 811 activacionSolitarioPIR2=0; 812 tiempoDesdePrimeraActivacionSolitarioPIR1 = millis(); 813 tiempoDesdePrimeraActivacionSolitarioPIR2 = millis(); 814 } 815 816 817//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 818//until it is restored 819void chequeoRedGSM(){ 820 noConectado = true; 821 for(int n=0; n < 3; n++) { 822 delay(1000); 823 if(gsm.begin(PIN)==GSM_READY) 824 { 825 noConectado = false; 826 break; 827 } //cierro if 828 } //cierro for 829 ultimoChequeoRedGSM = millis(); 830} 831 832 833//Function that activates the siren or buzzer in a different way depending on the operating mode 834void sirenaActiva(){ 835 //Geriatrino mode (this has been set to meet my particular needs) 836 if (modoAlarma == false){ 837 838 digitalWrite(PinSirena, HIGH); 839 digitalWrite(PinLed, HIGH); //debug 840 delay(2000); 841 digitalWrite(PinSirena, LOW); 842 digitalWrite(PinLed, LOW); //debug 843 delay(2000); 844 ciclosSirena+=1; 845 if (ciclosSirena ==8){ 846 sonarSirena=false; 847 ciclosSirena =0; 848 } 849 } 850 //Alarm mode. (it sounds for 30 seconds) 851 else{ 852 digitalWrite(PinSirena, HIGH); 853 digitalWrite(PinLed, HIGH); 854 delay(1000); 855 ciclosSirena +=1; 856 if (ciclosSirena >30){ 857 sonarSirena = false; 858 ciclosSirena=0; 859 } 860 } 861} //End of sirenaActiva() 862 863 864 865 866void loop() { 867 868// G E R I A T R I N O M O D E 869while (modoAlarma == false) { 870 //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 871 //of the rest period that was configured during the settings (3600000 * duracionPeriodoSinActividad) 872 873 if ((horarioNocturno == true) && ((millis()- tiempoDeNoche) > (3600000 * duracionPeriodoSinActividad))){ 874 horarioNocturno = false; 875 //When the rest period ends, I reset these variables to start counting the time with no activity detected. 876 tiempoSinAperturaPuerta = millis(); 877 tiempoSinPresenciaDetectada = millis(); 878 } 879 880 if ((digitalRead(PinPIR1) == HIGH) && (presenciaPIR1 == false)) { 881 presenciaPIR1 = true; 882 inicioPresenciaPIR1 = millis(); 883 delay(200); 884 } 885 886 if ((digitalRead(PinPIR2) == HIGH) && (presenciaPIR2 == false)) { 887 presenciaPIR2 = true; 888 inicioPresenciaPIR2 = millis(); 889 delay(200); 890 } 891 892 //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. 893 //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) 894 //According to this, I read if the PIR have been activated every 60 seconds 895 if ((presenciaPIR1== true) &&((millis()- inicioPresenciaPIR1) >= 60000)){ 896 //If both sensors were activated, it is considered a valid detection and I reset the flags 897 if (presenciaPIR2 == true) { 898 presenciaPIR1 = false; 899 presenciaPIR2 = false; 900 //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 901 tiempoUltimaActivacionCorrecta = millis(); 902 //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. 903 tiempoSinPresenciaDetectada = millis(); 904 horasSinPresencia =0; 905 mensajesAvisoSinPresenciaDetectada =0; 906 //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 907 //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 908 numeroActivacionesCorrectas = numeroActivacionesCorrectas +1; 909 } 910 //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. 911 else{ 912 presenciaPIR1= false; 913 activacionSolitarioPIR1 = activacionSolitarioPIR1 + 1; 914 //If it is the first single activation of PIR1 I reset the timer to start counting 24 hours since this event. 915 if (activacionSolitarioPIR1 ==1){ 916 tiempoDesdePrimeraActivacionSolitarioPIR1= millis(); 917 } 918 }//close else 919 }// close if (presenciaPIR1 == true)...... 920 921 922 //CONTROL PIR 2 This block is similar to the previous one, but now we check PIR2 923 if ((presenciaPIR2== true) &&((millis()- inicioPresenciaPIR2) >= 60000)){ 924 if (presenciaPIR1 == true) { 925 presenciaPIR1 = false; 926 presenciaPIR2 = false; 927 tiempoUltimaActivacionCorrecta = millis(); 928 tiempoSinPresenciaDetectada =millis(); 929 mensajesAvisoSinPresenciaDetectada = 0; 930 horasSinPresencia =0; 931 numeroActivacionesCorrectas = numeroActivacionesCorrectas +1; 932 } 933 else{ 934 presenciaPIR2= false; 935 activacionSolitarioPIR2 = activacionSolitarioPIR2 + 1; 936 if (activacionSolitarioPIR2 ==1){ 937 tiempoDesdePrimeraActivacionSolitarioPIR2 = millis(); 938 } 939 }//close else 940 }// close if (presenciaPIR2 == true) 941 942 943 //After reading the signals of the PIR sensors, the sketch manages the information regarding the last valid activation, and the number of false activations. 944 //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 945 if (((millis() - tiempoSinPresenciaDetectada) > (intervaloAvisos * 3600000)) && (horarioNocturno == false)){ 946 mensajesAvisoSinPresenciaDetectada = mensajesAvisoSinPresenciaDetectada +1; 947 //This timer is reseted here, to start counting the time for a new period before the next alert message is sent 948 tiempoSinPresenciaDetectada = millis(); 949 horasSinPresencia = mensajesAvisoSinPresenciaDetectada * intervaloAvisos; 950 mensaje = "No presence detected during the last "; 951 mensaje.concat(String(horasSinPresencia)); 952 mensaje.concat(" hours"); 953 enviarAvisoSMS(); //We call the function that sends the SMS with the rigth message 954 } 955 956 //The following block manages the number of false activations (activation of a single PIR sensor) within a period of 24 hours. 957 if ((((millis()- tiempoDesdePrimeraActivacionSolitarioPIR1) > (86400000)) || ((millis() - tiempoDesdePrimeraActivacionSolitarioPIR2) > (86400000)))&& ((activacionSolitarioPIR1 != 0)||( activacionSolitarioPIR2 !=0))){ 958 chequeoFuncionamientoPIR(); //If 24 hours have passed since the first false activation was detected, then I call this function 959 } 960 961 //CONTROL OF DOOR SENSOR 962 if ((digitalRead(PinDetectorPuerta) == HIGH) && (puertaAbierta == false)) { 963 //When the door is opened we reset all the timer and flags related to this sensor 964 puertaAbierta = true; 965 inicioPuertaAbierta = millis(); 966 tiempoUltimaAperturaNevera = millis(); 967 tiempoSinAperturaPuerta = millis(); 968 mensajesAvisoNeveraSinAbrir =0; 969 //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 970 numeroAperturasPuerta = numeroAperturasPuerta +1; 971 } 972 973 974 //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, 975 //and if so, variable PuertaAbierta is set to "false" and the code will start to check when the door is open again. 976 if (((millis()-inicioPuertaAbierta) > (600000)) && (digitalRead(PinDetectorPuerta) == LOW)&& (puertaAbierta == true)){ 977 puertaAbierta = false; 978 HorasConPuertaAbierta =0; 979 mensajesAvisoNeveraSinAbrir =0; 980 } 981 //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. 982 if (((millis()-inicioPuertaAbierta) > (3600000)) && (puertaAbierta == true)){ 983 //this counter controls the number of hours that the door remains open 984 HorasConPuertaAbierta = HorasConPuertaAbierta +1; 985 puertaAbierta = false; 986 inicioPuertaAbierta = millis(); 987 } 988 //If the fridge door remains opened for three hours in a row, then I consider it enough time to send and alert message 989 if (HorasConPuertaAbierta >= 3){ 990 mensaje = "The fridge door has been open for more than three hours"; 991 enviarAvisoSMS(); 992 HorasConPuertaAbierta =0; 993 //sonarSirena = true; //We could make the buzzer sound to advise the person that the door is been open to much time 994 } 995 996 //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 997 //to trigger alerts 998 if (((millis() - tiempoSinAperturaPuerta) > (3600000 * intervaloAvisos)) && (horarioNocturno == false)){ 999 tiempoSinAperturaPuerta = millis(); 1000 mensajesAvisoNeveraSinAbrir = mensajesAvisoNeveraSinAbrir + 1; 1001 horasSinApertura = mensajesAvisoNeveraSinAbrir * intervaloAvisos; 1002 mensaje = "The fridge has not been open during the last "; 1003 mensaje.concat(String(horasSinApertura)); 1004 mensaje.concat(" hours"); 1005 Serial.println(mensaje); 1006 enviarAvisoSMS(); 1007 } 1008 1009 /* These lines should be uncommented if we want the buzzer to make sound 1010 if (sonarSirena == true){ 1011 sirenaActiva(); 1012 } */ 1013 1014 1015 //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 1016 if ( (((millis() - ultimoChequeoRedGSM) > 3600000) || ((millis() - ultimoChequeoRedGSM) > 900000)) && (noConectado == true) ) { 1017 chequeoRedGSM(); 1018 } 1019 1020 //Now the program checks if an SMS has been received 1021 if (sms.available()){ 1022 enviarRespuestaSMS(); 1023 } 1024 1025}//Close " while (modoAlarma == false)" End of the code executed when operating in Geriatrino Mode 1026 1027 1028 1029 1030//A L A R M M O D E 1031 1032while (modoAlarma== true){ 1033 estadoPrevioAlarma = false; 1034 //I first check if an SMS has been received to arm the alarm. 1035 if (sms.available()){ 1036 //When operating in alarm mode, if a '1' is received then following function sets the variable "alarmaActiva" to true 1037 //If a '2' is received it il be set to false. 1038 enviarRespuestaSMS(); 1039 } 1040 1041 //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 1042 //the loop while(digitalRead(PinAlarm) == true) 1043 1044 //When the alarm is armed the next block will be always executed 1045 while(alarmaActiva == true) { 1046 //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 1047 //The program waits for 60 seconds to let people leave the house before it starts reading the sensors 1048 if(estadoPrevioAlarma == false) 1049 { 1050 estadoPrevioAlarma = true; 1051 presenciaPIR1 = false; 1052 presenciaPIR2 = false; 1053 puertaAbierta = false; 1054 deteccionIntruso = false; 1055 avisoIntrusoEnviado = 0; 1056 envioMensajeActivacionPuertaEnSolitario = false; 1057 ultimoChequeoRedGSM = millis(); 1058 activacionSolitarioPIR1=0; 1059 activacionSolitarioPIR2=0; 1060 numeroSensoresActivos=0; 1061 delay(60000); 1062 } 1063 1064 //I check the incoming SMS inside the 'while' loop too, to check if a message has been received to disarm the alarm. 1065 if (sms.available()){ 1066 enviarRespuestaSMS(); 1067 } 1068 1069 //When operating in Alarm mode the information provided by the sensors is treated in a different way. I consider that an intruder has been 1070 //detected when at least two of the three sensors provide an active signal. 1071 if ((digitalRead(PinPIR1) == HIGH) && (presenciaPIR1 == false)) { 1072 presenciaPIR1 = true; 1073 numeroSensoresActivos = numeroSensoresActivos +1; 1074 inicioPresenciaPIR1 = millis(); 1075 delay(100); 1076 } 1077 1078 if ((digitalRead(PinPIR2) == HIGH) && (presenciaPIR2 == false)) { 1079 presenciaPIR2 = true; 1080 numeroSensoresActivos = numeroSensoresActivos +1; 1081 inicioPresenciaPIR2 = millis(); 1082 delay(100); 1083 } 1084 1085 if ((digitalRead(PinDetectorPuerta) == HIGH) && (puertaAbierta == false)) { 1086 puertaAbierta = true; 1087 numeroSensoresActivos = numeroSensoresActivos +1; 1088 inicioPuertaAbierta = millis(); 1089 delay(100); 1090 } 1091 1092 //Regardless of the sensor type , if more than one sensor is active, an alarm is triggered. 1093 if ((numeroSensoresActivos >=2) && (deteccionIntruso ==false)) 1094 { 1095 inicioDeteccionIntruso = millis(); 1096 deteccionIntruso = true; 1097 delay(100); 1098 } 1099 1100 //If after 30 seconds since intrusion was detected, the alarm has not been disarmed, an alert message will be sent and the 1101 //buzzer will sound for 30 seconds. 1102 //If nobody disarms the alarm, the intruder conditions will persist and the message will be sent again to a maximum of three times 1103 1104 1105 if (((millis() - inicioDeteccionIntruso) > 30000)&& (deteccionIntruso == true) && (avisoIntrusoEnviado <3)){ 1106 mensaje = "INTRUDER DETECTED, more than one sensor is active... "; 1107 mensaje.concat('\n'); 1108 if (presenciaPIR1) { 1109 mensaje.concat("PIR 1 ACTIVE"); 1110 } 1111 mensaje.concat('\n'); 1112 if (presenciaPIR2) { 1113 mensaje.concat("PIR 2 ACTIVE"); 1114 } 1115 mensaje.concat('\n'); 1116 if (puertaAbierta) { 1117 mensaje.concat("DOOR OPEN"); 1118 } 1119 else{ 1120 mensaje.concat("DOOR HAS NOT BEEN OPEN"); 1121 } 1122 1123 //Once the message has been mounted, it is sent to all the interest phone numbers an the flags are reset. 1124 enviarAvisoSMS(); 1125 deteccionIntruso = false; 1126 presenciaPIR1 = false; 1127 presenciaPIR2 = false; 1128 puertaAbierta = false; 1129 numeroSensoresActivos=0; 1130 avisoIntrusoEnviado +=1 ; //This variable controls that the alarm message is sent a maximum of three times 1131 sonarSirena = true; 1132 } // close if ((millis() - inicioDeteccionIntruso) > 30000)&& ...... 1133 1134 1135 //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 1136 //gets active much more often than the other one. 1137 if ((presenciaPIR1== true) &&((millis()- inicioPresenciaPIR1) >= 50000)){ 1138 if (presenciaPIR2 == true) { 1139 presenciaPIR1 = false; 1140 presenciaPIR2 = false; 1141 } 1142 //If only PIR1 is active then I consider a false activation 1143 else{ 1144 presenciaPIR1= false; 1145 activacionSolitarioPIR1 = activacionSolitarioPIR1 + 1; 1146 if (activacionSolitarioPIR1 ==1){ 1147 tiempoDesdePrimeraActivacionSolitarioPIR1= millis(); 1148 } 1149 } 1150 } 1151 1152 //CONTROL PIR 2 1153 if ((presenciaPIR2== true) &&((millis()- inicioPresenciaPIR2) >= 50000)){ 1154 if (presenciaPIR1 == true) { 1155 presenciaPIR1 = false; 1156 presenciaPIR2 = false; 1157 } 1158 else{ 1159 presenciaPIR2= false; 1160 activacionSolitarioPIR2 = activacionSolitarioPIR2 + 1; 1161 if (activacionSolitarioPIR2 ==1){ 1162 tiempoDesdePrimeraActivacionSolitarioPIR2 = millis(); 1163 } 1164 } 1165 } 1166 1167 //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 1168 if ((((millis()- tiempoDesdePrimeraActivacionSolitarioPIR1) > (86400000)) || ((millis() - tiempoDesdePrimeraActivacionSolitarioPIR2) > (86400000)))&& ((activacionSolitarioPIR1 != 0)||( activacionSolitarioPIR2 !=0))){ 1169 if (activacionSolitarioPIR1 != activacionSolitarioPIR2){ 1170 chequeoFuncionamientoPIR(); 1171 } 1172 } 1173 1174 1175 //This blocks deals with the strange case in which only the door sensor is active. If after 50 seconds the door has been 1176 //open but no PIR is active then I consider a false activation. This could happen if one of the components of the 1177 //door sensor is moved or it falls down to the floor 1178 if(((millis() - inicioPuertaAbierta) > 50000)&& (puertaAbierta == true)){ 1179 numeroSensoresActivos =0; 1180 puertaAbierta=false; 1181 envioMensajeActivacionPuertaEnSolitario +=1; 1182 if (envioMensajeActivacionPuertaEnSolitario < 3){ 1183 mensaje ="El sensor puerta se ha activado en solitario"; 1184 enviarAvisoSMS(); 1185 } 1186 } 1187 1188 //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 1189 //before calling the function chequeoRedGSM() 1190 if ((((millis() - ultimoChequeoRedGSM) > 3600000) && (deteccionIntruso == false)) || (((millis() - ultimoChequeoRedGSM) > 900000)&&(deteccionIntruso == false) && (noConectado == true))) 1191 { 1192 chequeoRedGSM(); 1193 } 1194 1195 //This function makes the siren sound if the variable sonarSirena is true 1196 if (sonarSirena) { 1197 sirenaActiva(); 1198 } 1199 1200 }//close while (alarmaActiva == true) 1201}//close while modoALARMA == true 1202 1203} // End of loop() 1204
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.
1//Include the necessary libraries 2#include <MKRGSM.h> 3GSM gsm; 4 5GSM_SMS sms; 6#include <RTCZero.h> 7RTCZero rtc; 8 9//VARIABLES 10 for SMS management 11char numeroCliente[20]; //In this array is saved the phone 12 number from which an SMS is sent to the SIM card inserted in the MKRGSM 13String 14 numeroClienteString; 15String mensaje; // This variable will be updated with the 16 right message depending on the information we need to transmit 17char contenidoMensaje; 18 //This variable wiil keep the content of the incoming SMS (initially the incoming 19 messages have been thought to be single characters) 20boolean noConectado = true; 21 // Variable to know whether the SIM card is connected to the telephone network or 22 not. 23//The following variables will keep the hour and minute values of the last 24 correct sensor activations (pressence and door opening) 25long int horasUltimaActivacionCorrecta, 26 minutosUltimaActivacionCorrecta,horasUltimaAperturaNevera, minutosUltimaAperturaNevera; 27 28// 29 VARIABLES used during device configuration 30//NOTE.- I am aware that some of 31 the following type convertions could look strange. I was implementing them on the 32 run, as I was programming, and as they finally worked 33//I dont dare to change 34 a code line. Anybody familiar with the different type of variables can easily improve 35 the code. 36 37 38String telefonos[3];//This array will store the phone numbers 39 loaded from the app. Initially I decided a maximum number of 3 contact numbers, 40 but you can customize it 41char telefonoAviso[20]; //This variable is used to convert 42 the contact numbers from String to array char type, for the funcion sms.begin needs 43 that type to work 44String periodo; 45char PIN []="1234"; //Array char type 46 variable to store the PIN of our SIM card. 47String numeroPIN; //String type variable 48 to store the PIN number 49int longitudPIN; 50//Variable to keep the number of 51 contacts entered with the app. At least one phone number will have to be entered 52 so that Geriatrino can work 53byte contactos =0; 54//This variable will keep the 55 period of time to trigger alarms if no activity is detected. The app will admit 56 a value between a minimum of 3 hours and a maximum of ten hours 57//But you can 58 change these limits. The default value is 5 hours 59int intervaloAvisos =5; 60int 61 tiempoSinActividad; 62//These are control variables that will be used while settings 63 are performed from the app 64char anadirOtroTelefono ='S'; 65char primerCaracter; 66 67boolean realizarTestPresencia = true, testPresenciaOK=false, realizarTestPuertaCerrada 68 = true, testPuertaCerradaOK = false; 69boolean realizarTestPuertaAbierta =true,testPuertaAbiertaOK 70 = false, pendienteEnviarSMSConfiguracion = false, peticionConfiguracion = false; 71String 72 mensajeRecibido; 73byte intentosTestPresencia =0,intentosTestPuertaCerrada =0, 74 intentosTestPuertaAbierta =0,intentosConexionRedGSM= 0; 75String hora, minuto, 76 inicioPeriodoSinActividadString, duracionPeriodoSinActividadString; 77int horaActual, 78 minutoActual,segundoActual, inicioPeriodoSinActividad, duracionPeriodoSinActividad; 79boolean 80 horarioNocturno = false; 81long int tiempoDeNoche; 82 83//MKR 1400 pin allocation 84# 85 define PinLed 6 //In this case we use the built-in led of Arduino MKR1400 86# 87 define PinPIR1 5 // Digital pin assigned to PIR 1 88# define PinPIR2 9 // Digital 89 pin assigned to PIR2 90# define PinDetectorPuerta 7 //Digital pin assigned to 91 door sensor 92# define PinSirena 8 //If we decide to add a buzzer this will be 93 the pin. The code is prepared to make it sound under some conditions 94//This pin 95 will be used if a switcher or press button is implemented. It can be useful if you 96 decide to use to arm or disarm the alarm 97//But you could do it sending an SMS 98 or via bluetooth connection. 99# define PinInterruptor 3 100//Digital pins 13 101 and 14 are reserved to connect the mkr1400 with TX an Rx pins of HC-06 Bluetooth 102 module 103 104//Variables to control the way that Geriatrino will work. If modoAlarma 105 is true it will work in "Alarm mode", otherwise it will work in "Geriatrino mode" 106boolean 107 modoAlarma; 108String modoTrabajo; 109 110//Variables used for the control of the 111 PIR sensors 112boolean presenciaPIR1 = false; 113boolean presenciaPIR2 = false; 114boolean 115 deteccionPresencia = false; 116 117//The following variables will keep the millis() 118 values in the moment that a certain event occurs. 119long int inicioPresenciaPIR1, 120 inicioPresenciaPIR2, inicioDeteccionPresencia; 121long int primeraFalsaAlarmaPIR1, 122 primeraFalsaAlarmaPIR2; 123long int tiempoUltimaActivacionCorrecta, tiempoDesdePrimeraActivacionSolitarioPIR1, 124 tiempoDesdePrimeraActivacionSolitarioPIR2; 125long int tiempoSinAperturaPuerta,tiempoSinPresenciaDetectada; 126long 127 int ultimoChequeoRedGSM = millis(); 128 129//Variables to manage false alarms or 130 single activation of one PIR 131int activacionSolitarioPIR1=0,activacionSolitarioPIR2=0, 132 numeroSensoresActivos=0, mensajesAvisoSinPresenciaDetectada=0; 133 134//This variable 135 will count the number of right activations detected during one period to measure 136 the activity level 137//I will not use in this prototype but it could be interesting 138 to know it on demand, so I declare it to use it in future versions. 139int numeroActivacionesCorrectas 140 =0; 141int diferenciaActivaciones =0; 142int horasSinPresencia; 143 144//VARIABLES 145 used to manage the information provided by the door sensor 146boolean puertaAbierta 147 = false; 148long int inicioPuertaAbierta,tiempoUltimaAperturaNevera; 149boolean 150 mensajePuertaAbiertaNoEnviado = true; 151byte HorasConPuertaAbierta =0, mensajesAvisoNeveraSinAbrir 152 =0; 153int numeroAperturasPuerta = 0, horasSinApertura =0; 154 155//This variable 156 is used to control the activation of the buzzer if it is finally incorporated 157boolean 158 sonarSirena = false; 159byte ciclosSirena=0; 160 161//VARIABLES to manage the way 162 the device works in Alarm Mode 163boolean alarmaArmada=false,estadoPrevioAlarma=false,deteccionIntruso=false, 164 alarmaActiva = false; 165#define PinAlarmaArmada 4 166long int inicioDeteccionIntruso, 167 contadorParaNuevoEnvioSMS; 168byte avisoIntrusoEnviado=0, envioMensajeActivacionPuertaEnSolitario=0; 169 170 171//IMPORTANT 172 REMARKS IF YOU USE ARDUINO IDE TO CONFIGURE YOUR GERIATRINO 173//To do the settings 174 without the app the following variables will have declared with the values we 175 want them to work: 176/*This is equivalent to the call the function anadirOtroTelefono(). 177 Remember to write the phone number in the international format) */ 178//String 179 telefonos[3]={"phone number 1", "phone number 2", "phone number 3"} 180/*You 181 set the operating mode, Alarm mode (true), Geriatrino mode (false) */ 182//boolean 183 alarmMode = true or false; 184/*If you choose the Geriatrino mode you set the time 185 parameters needed to manage the alarm messages */ 186// int inicioPeriodoSinActividad 187 = X; This is equivalent to call the function 188// int duracionPeriodoSinActividad 189 = y; 190// int intervaloAvisos = z; 191/*You set the PIN number of the SIM card 192 to connect to the phone network */ 193// char PIN []="1234"; 194//If you use the 195 IDE you do not need the HC-06 bluetooth module and you have to use Serial.begin(9600) 196 instead of Serial1.begin(9600) to stablish communication 197//The current hour 198 and minute could be get directly sending it through the IDE serial port during installation 199 process. 200//In the setup() function you have to subtitute the call to the functions 201 that communicate with the app for the next lines to get the current hour and minute 202//rtc.begin(); 203 // initialize RTC 204//Serial.println("To set the current hour type the value and 205 then press Enter key"); 206/*Waiting to read data from the IDE serial port*/ 207//while 208 (Serial.available()==0){ delay(100); } 209//Valor_hora = Serial.readStringUntil('\ 210'); 211 212//hora = String(Valor_hora).toInt(); 213//rtc.setHours(hora); 214//Serial.print("The 215 value of the current hour is : ");Serial.print(Valor_hora);Serial.println(" Now 216 you have to type the value for the current minute and the press Enter key "); 217/*Waiting 218 for the value of the current minute */ 219//while (Serial.available ()==0){ delay(100); 220 } 221//Valor_minuto = Serial.readStringUntil('\ 222'); 223//minuto = String(Valor_minuto).toInt(); 224 225//rtc.setMinutes(minuto); 226//Serial.print("The value of the current minute 227 is : ");Serial.println(Valor_minuto); 228//Serial.println("Type the value for 229 the current second"); 230/*Waiting for the value of current second...*/ 231//while 232 (Serial.available ()==0){ delay(100); } 233//Valor_segundo = Serial.readStringUntil('\ 234'); 235//segundo 236 = String(Valor_segundo).toInt(); 237//Serial.print("The value of current second 238 is : ");Serial.println(Valor_segundo); 239//rtc.setSeconds(segundo); 240//horaActual 241 = rtc.getHours(); 242//minutoActual =rtc.getMinutes(); 243//segundoActual = rtc.getSeconds(); 244//Serial.print("La 245 current time is ");Serial.print(horaActual);Serial.print(":");Serial.print(minutoActual);Serial.print(":");Serial.print(segundoActual);Serial.println("..."); 246/*I 247 set the Interrupt time routine that determines the start of the rest period with 248 the entered values */ 249//rtc.setAlarmTime(inicioPeriodoSinActividad,0,0); 250//rtc.enableAlarm(rtc.MATCH_HHMMSS); 251//rtc.attachInterrupt(empiezaNoche); 252 253/*All the calls to the functions related to the app must be supressed and you 254 have to omit the sensor tests too*/ 255 256void setup() { 257 //IMPORTANT.-To 258 work with bluetooth and the serial port in MKR boards you have to use "Serial1" 259 instead of "Serial" 260 Serial1.begin(9600); 261 //Pins associated to PIR 262 and door sensors are declared as inputs 263 pinMode(PinPIR1, INPUT); 264 pinMode(PinPIR2, 265 INPUT); 266 //If the fridge door is closed the contact of the door sensor is 267 closed too, and a LOW will be read as this pin will be connected to ground 268 //I 269 activate the internal pull-up resistor of this pin to read a HIGH level when the 270 door is opened 271 pinMode(PinDetectorPuerta, INPUT_PULLUP); 272 //PinLed is declared 273 as an Output 274 pinMode(PinLed, OUTPUT); 275 digitalWrite(PinLed, LOW); 276 277 rtc.begin(); // initialize RTC 278 279 //During setup I the program will remain 280 in this loop until at least a phone number is entered, and the button "CONTINUE" 281 is tapped in the app. 282 //When this happens the app sends an ASCII character 283 "N" and it is storedd in the variable anadirOtroTelefono. If 3 phone numbers have 284 been saved, 285 //then the program automatically leaves the loop 286 while (((contactos==0) 287 || (anadirOtroTelefono == 'S'))&&(contactos <3)) { 288 anadirTelefono(); //This 289 function is called to add a phone number 290 } 291 //Next lines may not seem necessary, 292 but I need them to coordinate the app whith the Geriatrino Sketch when the last 293 phone number is finally added. 294 //In the event that 3 phone numbers have been 295 typed in the app, then the sketch has to wait until the button "CONTINUE" is 296 tapped to go on with the settings 297 if (contactos == 3){ 298 //I will allways 299 empty the Serial buffer when I am expecting a data to come from the app. I know 300 this is not necessary but I prefere to do it this way 301 while (Serial1.available()!=0){Serial1.read();} 302 303 //Waiting to the next character from the app 304 while (Serial1.available() 305 == 0){ 306 delay(100); 307 } //close while 308 anadirOtroTelefono 309 = Serial1.read(); // 310 } //close if 311 312 //After the phone numbers, the 313 next data sent from the app will be the PIN number of the SIM card whe have inserted 314 in the MKR1400. 315 //It is essential to enter it properly, otherwise the PIN 316 will be accepted but the MKR1400 will not connect to the network. 317 while (Serial1.available()!=0){Serial1.read();} 318 319 while (Serial1.available() == 0){ 320 delay(100); 321 } //close 322 while 323 324 325 //The PIN number is stored in the String type variable "numeroPIN" 326 327 numeroPIN= Serial1.readStringUntil('\ 328'); 329 longitudPIN = numeroPIN.length(); 330 //get the length of the string 331 //I convert the variable numeroPIN to the type 332 "array char" because the function gsm.begin() requires this type of data, 333 334 //I apply (longitudPIN+1) to get the last blank space needed in the array structure 335 336 numeroPIN.toCharArray(PIN,longitudPIN +1); 337 338 Serial1.print("1"); 339 //I send a "1" to the app to confirm I haver received the PIN number, so that 340 it continues showing the setting options 341 342 //In the app, after entering 343 the PIN, two buttons are showed to choose the mode of work: Alarm Mode or Geriatrino 344 Mode. The sketch is now waiting 345 //until the choice is done. Then, if a character 346 "G" is received trough the serial por it will work in Geriatrino MOde, and more 347 time settings will be 348 //required. If a character "A" is received instead, 349 it will work in Alarm Mode. In this case no more settings are required and the sketch 350 will proceed 351 //to perform the sensor tests. 352 353 while (Serial1.available()!=0){Serial1.read();} 354 355 //Waiting for the next character from the app 356 while (Serial1.available() 357 == 0){ 358 delay(100); 359 } //cierro while 360 361 362 modoTrabajo= 363 Serial1.readStringUntil('\ 364'); 365 if (modoTrabajo == "A"){ 366 modoAlarma 367 = true; 368 } 369 else{ 370 modoAlarma = false; //when modoAlarma = 371 false that means we are working on Geriatrino Mode 372 } 373 374 while 375 (Serial1.available()!=0){Serial1.read();} 376 377 //If Geriatrino mode is selected,(modoAlarma 378 = False) some aditional time settings are required and the sketch calls one by one 379 the functions 380 //to configure these parameters. 381 if (modoAlarma==false){ 382 383 //Function to set the lapse of time to trigger alerts if no activity is detected 384 385 configurarTiempoSinActividad(); 386 //Functions to set the current hour 387 and minute. Another option to get the current time could be using AT commands to 388 get it from the phone network 389 //I have not tried it but I think it could 390 be possible. 391 configurarHoraActual(); 392 rtc.setHours(horaActual); 393 394 configurarMinutoActual(); 395 rtc.setMinutes(minutoActual); 396 rtc.setSeconds(0); 397 398 399 //Functions to set the start and the duration of the rest period. During this 400 period, alarms will not be triggered, but the information of the sensors 401 //will 402 be taken into account and registered. It is important to know the habits of the 403 monitored person to get an efective monitoring work. 404 configurarInicioPeriodoSinActividad(); 405 406 configurarDuracionPeriodoSinActividad(); 407 //After setting the rest period, 408 I configure the interrupt routine associated to the timer, to stablish the beginning 409 of the rest time 410 rtc.setAlarmTime(inicioPeriodoSinActividad,0,0); 411 rtc.enableAlarm(rtc.MATCH_HHMMSS); 412 413 rtc.attachInterrupt(empiezaNoche); 414 415 //Next block is been added 416 to deal with the case in which the Geriatrino is installed and configured during 417 the rest period. With these lines we get that Geriatrino 418 //starts working from 419 the first moment after it is installed. Without them, the device will not be working 420 properly until the Interrupt "empiezaNoche" is called. 421 if ( ((horaActual 422 < inicioPeriodoSinActividad)&&(horaActual >=(inicioPeriodoSinActividad + duracionPeriodoSinActividad 423 -24))) || (horaActual >= (inicioPeriodoSinActividad + duracionPeriodoSinActividad)) 424 ) { 425 horarioNocturno = false; 426 } 427 else{ 428 horarioNocturno 429 = true; 430 if ((horaActual < inicioPeriodoSinActividad)&& (horarioNocturno==true)){ 431 432 tiempoDeNoche = millis()-((((duracionPeriodoSinActividad - horaActual) *60) 433 + minutoActual) *60000) ; 434 } 435 if ((horaActual >= inicioPeriodoSinActividad)&& 436 (horarioNocturno==true)){ 437 tiempoDeNoche = millis()-((((horaActual-inicioPeriodoSinActividad)*60) 438 + minutoActual) * 60000); 439 } 440 } 441 } //end of if modoAlarma 442 == false 443 444 445 //After finishing the settings we start performing the sensor 446 test to make sure they are working properly. If the test result is not Ok it 447 448 //will be repeated one more time. 449 while ((realizarTestPresencia == true)&&(intentosTestPresencia 450 <2)){ 451 //This function checks that the two PIR sensors are working ok. 452 453 testPresencia(); 454 } 455 456 while ((realizarTestPuertaCerrada == true)&&(intentosTestPuertaCerrada 457 <2)){ 458 //This function checks the signal of the door sensor when the fridge 459 door is closed 460 testPuertaCerrada(); 461 } 462 463 while ((realizarTestPuertaAbierta 464 == true)&&(intentosTestPuertaAbierta <2)){ 465 //This function checks the signal 466 of the door sensor when the fridge door is open 467 testPuertaAbierta(); 468 469 } 470 471 //Finally I call the function to connect to the mobile phone network. 472 It will be trying to connect for some time, and if connection is not stablished, 473 474 //the sketch will go on and loop function will start to run, but the function 475 conectarRedGSM will be periodically called until it gets to connect. 476 conectarRedGSM(); 477 478 //If we have got a failure in the connection to the network or in any of the sensor 479 tests, we call a function that will send a message informing about it 480 if ((noConectado==true)||(testPresenciaOK==false)||(testPuertaCerradaOK==false)||(testPuertaAbiertaOK==false)){ 481 482 avisoFallosInstalacion(); 483 } 484 485 //At the end of setup() the 486 time values to these to variables are updated with millis() 487 tiempoUltimaActivacionCorrecta 488 = millis(); //time for the last simoultaneous activation of both PIR 489 tiempoUltimaAperturaNevera 490 = millis(); //time for the last door opening. 491 492} //END SETUP 493 494 495 496//This 497 function is coordinated with the app to configure de contact phone numbers 498void 499 anadirTelefono(){ 500 501 while (Serial1.available()!=0){Serial1.read();} 502 503 //Waiting for a data coming from the app through the serial port 504 while 505 (Serial1.available() == 0){ 506 delay(100); 507 } 508 //To read the first 509 byte of the buffer withbut deleting it I use Serial1.peek(), and then I check whether 510 it is a number or a letter. If button "CONTINUE" is tapped on the app 511 //then 512 it sends a "N" trough the bluetooth and the serial port, and I know that the data 513 is not a phone number 514 primerCaracter= Serial1.peek(); 515 delay(100); 516 517 //Calling function isDigit I check if the first character is a number, and if 518 so, I consider the data is a phone number and I store it. 519 if (isDigit(primerCaracter)){ 520 521 telefonos[contactos] = Serial1.readStringUntil('\ 522'); 523 524 // IMPORTANT.- 525 On the app, international prefix number is not requested. After getting the phone 526 number I convert it to international format by adding a prefix code. 527 //This 528 is needed because when receiving SMS messages from the configured contacts, the 529 number will be in international format. 530 //To make the device compatible 531 with any country code number I think that the best way is to request to enter the 532 whole number in the app, but I did it this 533 //way to make it easier to the 534 user, and because when I started the project my intention was to use it only with 535 Spanish phone numbers. Prefix "+34" correspond to Spain country code. 536 //YOU 537 HAVE TO TAKE THIS INTO ACCOUNT IF YOU USE THE DEVICE IN A DIFFERENT COUNTRY AND 538 CHANGE THE CODE 539 540 telefonos[contactos] = "+34" + telefonos[contactos]; 541 542 contactos = contactos +1; 543 } //Close if isDigit 544 545 //If the first 546 byte is not a digit, then it is either 'N' (no more phone numbers) or 'S' (a new 547 phone number will be send from the app) 548 //Go to the setup function to understand 549 how the variable primerCaracter is used 550 if ((primerCaracter == 'N') || (primerCaracter 551 =='S')){ 552 anadirOtroTelefono = primerCaracter; 553 } 554 555} //end 556 of aadirTelefono() 557 558 559//Function coordinated with the app to set the lapse 560 time with no activity detected that will trigger the alerts. 561void configurarTiempoSinActividad(){ 562 563 while(Serial1.available()!=0){Serial1.read();} // Vaco Buffer de entrada 564 565 566 //Waiting for the data from the app 567 while(Serial1.available()==0){ 568 569 delay(100); 570 } 571 periodo = Serial1.readStringUntil('\ 572'); 573 //Convert 574 the String type to Int 575 intervaloAvisos = String(periodo).toInt(); 576 while(Serial1.available()!=0){Serial1.read();} 577 // I empty again the serial buffer 578}// end of configurarTiempoSinActividad() 579 580 581 582///Set 583 the current hour. The value received from the app is within the range 0-23 584void 585 configurarHoraActual(){ 586 while(Serial1.available()!=0){Serial1.read();} // Vaco 587 Buffer de entrada 588 while(Serial1.available()==0){ 589 delay(100); 590 591 } 592 hora = Serial1.readStringUntil('\ 593'); 594 //Convert the type of variable 595 hour from string to int, because I will need to operate with it 596 horaActual 597 = String(hora).toInt(); 598 while(Serial1.available()!=0){Serial1.read();} //Empty 599 serial Buffer 600}// End of configurarHoraActual() 601 602 603//Set the current 604 minute. The value received from the app is within the range 0-59. 605void configurarMinutoActual(){ 606 607 while(Serial1.available()!=0){Serial1.read();} 608 while(Serial1.available()==0){ 609 610 delay(100); 611 } 612 minuto = Serial1.readStringUntil('\ 613'); 614 //Convert 615 from string to int type 616 minutoActual = String(minuto).toInt(); 617 while(Serial1.available()!=0){Serial1.read();} 618 619}//End of configurarMinutoActual() 620 621//This function collects from the 622 app the star time of the rest period (value in the range 0-23) 623void configurarInicioPeriodoSinActividad(){ 624 625 626 while(Serial1.available()!=0){Serial1.read();} 627 while(Serial1.available()==0){ 628 629 delay(100); 630 } 631 inicioPeriodoSinActividadString = Serial1.readStringUntil('\ 632'); 633 634 //Convert from String to int type 635 inicioPeriodoSinActividad = String(inicioPeriodoSinActividadString).toInt(); 636 637 while(Serial1.available()!=0){Serial1.read();} 638}// End of configurarinicioPeriodoSinActividad() 639 640 641//In 642 this function is set the duration of the rest period. The app sends a value in 643 the range 6 to 10 hours 644//Once more I remind that is important to know how long 645 the person usually sleeps to make the devices more effective 646void configurarDuracionPeriodoSinActividad(){ 647 648 while(Serial1.available()!=0){Serial1.read();} 649 while(Serial1.available()==0){ 650 651 delay(100); 652 } 653 duracionPeriodoSinActividadString = Serial1.readStringUntil('\ 654'); 655 656 //Convert the String type to int 657 duracionPeriodoSinActividad = String(duracionPeriodoSinActividadString).toInt(); 658 659 while(Serial1.available()!=0){Serial1.read();} 660}// end of configurarDuracionPeriodoSinActividad() 661 662 663 664//Interrupt 665 routine that will be called every day at the time in which the rest period begins 666 667 void empiezaNoche(){ 668 horarioNocturno = true; //nighttime is on now 669 tiempoDeNoche 670 = millis(); //Update the value of this variable in the moment that rest period 671 begins 672 } 673 674 675 676 677 678//Function coordinated with the app that 679 performs a working test of the PIR sensors after all the settings have been saved 680void 681 testPresencia(){ 682 /* 683 *In the app, instruccions are showed to leave the 684 room (everybody: humans and pets),where the device has been installed , then wait 685 for 20 seconds 686 *and come in again placing near the Geriatrino. Then the button 687 PRESENCE TEST has to be tapped in the app, and the text "TPr" is sent via bluetooh 688 689 *When this message is receive in the serial buffer the sketch start to read 690 the signals of the two PIR sensors to check if they have detected the person. 691 692 *If everythin is correct Geriatrino sends to the app the character '1', otherwise 693 he sends the character '0' 694 */ 695 while (Serial1.available()!=0){Serial1.read();} 696 697 while (Serial1.available() == 0){ 698 delay(100); 699 } 700 mensajeRecibido 701 = Serial1.readStringUntil('\ 702'); 703 if (mensajeRecibido == "TPr"){ 704 //I 705 read the two PIR sensors to check if they are both activated. If the user has followed 706 the instructions of the app he should be in front of the 707 //Geriatrino and 708 they should activate. 709 for (int z=0;z<=5;z++){ 710 delay(1000); 711 if 712 ((digitalRead(PinPIR1) == HIGH) && (digitalRead(PinPIR2) == HIGH)) { 713 //If 714 the two sensors are active then the led is on continously for 5 seconds and character 715 '1' is send to the app 716 digitalWrite(PinLed, HIGH); 717 delay(5000); 718 719 digitalWrite(PinLed, LOW); 720 realizarTestPresencia = false; 721 722 testPresenciaOK = true; 723 Serial1.write("1"); 724 break; 725 // I leave the "for" loop 726 } 727 } //close for 728 intentosTestPresencia 729 +=1; 730 //If the result of the first attempt is a failure, the led blinks for 731 five seconds, and the app shows messages to repeat the test and to tap the button 732 again 733 if ((intentosTestPresencia <= 2) && (realizarTestPresencia == true)){ 734 735 for (int p=0; p < 10 ; p++){ 736 digitalWrite(PinLed, HIGH); 737 delay(500); 738 739 digitalWrite(PinLed, LOW); 740 delay(500); 741 }//cierro for 742 743 testPresenciaOK = false; //In this variable is kept the result of the presence 744 test 745 Serial1.write("0"); //envo mensaje error test 746 }//cierro if 747 intentosTestPresencia 748 749 //Whether the result of the second attempt is 750 correct or not, the app automatically passes to the next step and a warning will 751 be sent at the end of the process 752 //y continuar con la llamada a la siguiente 753 funcin testPuertaAbierta() 754 } // close if (mensajeRecibido == "TPr") 755 756 while(Serial1.available()!=0){Serial1.read();} // Vaco Buffer de entrada 757 758 759} //End of testPresencia() 760 761 762//Functions that checks the work of 763 the door opening sensor and if it has been placed correctly on the door 764//This 765 function is called first to check that the door is closed. The app shows messages 766 to close de door and then tap the CLOSED DOOR TEST button 767void testPuertaCerrada(){ 768 769 770 //Waiting for a data from the app that is send when the TEST button is 771 tapped. 772 while (Serial1.available() == 0){ 773 delay(100); 774 } 775 776 mensajeRecibido = Serial1.readStringUntil('\ 777'); 778 if (mensajeRecibido == 779 "TPC"){ //This is what the app sends when the button is pressed 780 for 781 (int z=0;z<=5;z++){ 782 delay(1000); 783 //If a LOW level is read in PinDetectorPuerta 784 then the sensor has been installed correctly. 785 if (digitalRead(PinDetectorPuerta) 786 == LOW) { 787 //The led lights continously for 5 seconds an '1' is sent 788 to the app 789 digitalWrite(PinLed, HIGH); 790 delay(5000); 791 792 digitalWrite(PinLed, LOW); 793 realizarTestPuertaCerrada = false; 794 795 testPuertaCerradaOK = true; 796 Serial1.print("1"); 797 break; 798 799 } 800 } //close loop for 801 intentosTestPuertaCerrada +=1; 802 803 //We have a second attempt if the result of the test is a fail, 804 if ((intentosTestPuertaCerrada 805 <= 2) && (realizarTestPuertaCerrada == true)){ 806 for (int p=0; p < 10 ; p++){ 807 808 digitalWrite(PinLed, HIGH); 809 delay(500); 810 digitalWrite(PinLed, 811 LOW); 812 delay(500); 813 }//cierro for 814 testPuertaCerradaOK 815 = false; //This variable keeps the result of the CLOSED DOOR TEST 816 Serial1.print("0"); 817 //If there is a fail '0' is sent to the app 818 }//end of if intentosTestPuertaCerrada() 819 820 //If the second attempt is also a fail the sketch jumps anyway to the next test 821 and a warning message will be send later. 822 } // close if (mensajeRecibido == 823 "TPC"){ 824 while (Serial1.available()!=0){Serial1.read();} 825} //End of testPuertaCerrada() 826 827 828//This 829 function is similar to the previous one, but it checks if the door is open 830void 831 testPuertaAbierta(){ 832 833 while (Serial1.available() == 0){ 834 delay(100); 835 836 } 837 mensajeRecibido = Serial1.readStringUntil('\ 838'); 839 if (mensajeRecibido 840 == "TPA"){ 841 for (int z=0;z<=5;z++){ 842 delay(1000); 843 if 844 (digitalRead(PinDetectorPuerta) == HIGH) { 845 846 digitalWrite(PinLed, 847 HIGH); 848 delay(5000); 849 digitalWrite(PinLed, LOW); 850 realizarTestPuertaAbierta 851 = false; 852 testPuertaAbiertaOK = true; 853 Serial1.print("1"); 854 855 break; 856 } 857 } //cierro for 858 intentosTestPuertaAbierta 859 +=1; 860 861 if ((intentosTestPuertaAbierta <= 2) && (realizarTestPuertaAbierta 862 == true)){ 863 for (int p=0; p < 10 ; p++){ 864 digitalWrite(PinLed, HIGH); 865 866 delay(500); 867 digitalWrite(PinLed, LOW); 868 delay(500); 869 870 }//cierro for 871 testPuertaAbiertaOK = false; 872 Serial1.print("0"); 873 874 } 875 }//close if (mensajeRecibido == "TPA"){ 876 while (Serial1.available()!=0){Serial1.read();} 877 878} //close testPuertaAbierta(){ 879 880 881//Function that try to connect to 882 the GSM network 883void conectarRedGSM(){ 884 885 //After the sensor tests, the 886 app shows a new button RECEIVE MESSAGE. When we tap on it a character 'R' is sent 887 to the Geriatrino 888 while (Serial1.available() == 0){ 889 delay(100); 890 891 } 892 mensajeRecibido = Serial1.readStringUntil('\ 893'); 894 if (mensajeRecibido 895 == "R"){ 896 // Initializing GSM module 897 898 intentosConexionRedGSM 899 =0; 900 while ((noConectado)&& (intentosConexionRedGSM < 6)){ 901 if(gsm.begin(PIN)==GSM_READY) 902 { 903 noConectado = false; 904 Serial1.print("1"); //character 905 '1' is sent to the app when it connects to the network 906 enviarSMSConfiguracion(); 907 908 pendienteEnviarSMSConfiguracion = false; 909 } 910 911 else{ 912 913 intentosConexionRedGSM +=1; 914 } 915 } //cierro while 916 917 918 if (noConectado == true) { 919 Serial1.print("0"); //if connection fails, 920 character '0' is sent to the app. The loop() function will start running and will 921 try to connect again in one hour 922 pendienteEnviarSMSConfiguracion = true; 923 924 } 925 } //Close if mensajeRecibido =="R" 926 927 //When the device 928 connects to the network, SMS with all the information of the settings and the result 929 of the tests are send to every phone number previously 930 //saved during the 931 congiration process. But if after 6 attempts, connection is not established, The 932 app finishes anyway, and information is showed that 933 //the device will peridically 934 try to connect to the network later. The SMS with the settings information are pending 935 to be send until connection is achieved. 936 937} //End of ConectarRedGSM() 938 939//This 940 function works to send an SMS to all the contact phone numbers entered during the 941 configuration process, with all the information about it. 942void enviarSMSConfiguracion(){ 943 944 mensaje = "CONTACTS: "; 945 for (int f =0; f < contactos; f ++){ 946 mensaje.concat(telefonos[f]); 947 948 mensaje.concat(", "); 949 } 950 mensaje.concat('\ 951'); 952 mensaje.concat("PIN 953 : "); 954 mensaje.concat(numeroPIN); 955 mensaje.concat('\ 956'); 957 mensaje.concat("Mode 958 "); 959 if (modoAlarma ==false){ 960 mensaje.concat("GERIATRINO"); 961 } 962 963 else{ 964 mensaje.concat("ALARM"); 965 } 966 mensaje.concat('\ 967'); 968 969 if (modoAlarma==false){ 970 mensaje.concat("Warning period "); 971 mensaje.concat(periodo); 972 973 mensaje.concat(" hours"); 974 mensaje.concat('\ 975'); 976 mensaje.concat("Start 977 rest period "); 978 mensaje.concat(inicioPeriodoSinActividadString); 979 mensaje.concat('\ 980'); 981 982 mensaje.concat("Duration rest period "); 983 mensaje.concat(duracionPeriodoSinActividadString); 984 985 986 } 987 988 //After constructing the message it is sent to the every 989 contact. 990 for(int v=0; v<contactos; v ++){ 991 telefonos[v].toCharArray(telefonoAviso,20); 992 //convert the telephone number from string to array char type before calling sms.beginSMS 993 994 sms.beginSMS(telefonoAviso); 995 sms.print(mensaje); 996 sms.endSMS(); 997 998 } 999}//cierro enviarSMSConfiguracion() 1000 1001 1002//If some failure 1003 is detected during the sensor tests, it is reported by sending SMS and asks to check 1004 the frequent failures guide. 1005void avisoFallosInstalacion(){ 1006 mensaje = "FAULTY 1007 INSTALATION"; 1008 mensaje.concat("\ 1009"); 1010 if (testPresenciaOK == false) 1011 { 1012 mensaje.concat("Fail Pres. test"); 1013 mensaje.concat("\ 1014"); 1015 1016 } 1017 if (testPuertaCerradaOK==false) { 1018 mensaje.concat("Fail Closed 1019 D. Test"); 1020 mensaje.concat("\ 1021"); 1022 } 1023 if (testPuertaAbiertaOK==false){ 1024 1025 mensaje.concat("Fail Open D. Test"); 1026 mensaje.concat("\ 1027"); 1028 1029 } 1030 mensaje.concat("SEE FREQUENT FAILURES"); 1031 1032 for(int v=0; v<contactos; 1033 v ++){ 1034 telefonos[v].toCharArray(telefonoAviso,20); 1035 sms.beginSMS(telefonoAviso); 1036 1037 sms.print(mensaje); 1038 sms.endSMS(); 1039 } 1040} 1041 1042//This function 1043 send the alert messages to the contacs. The right message content has previously 1044 been saved in the variable "mensaje" 1045void enviarAvisoSMS(){ 1046 for(int x=0; 1047 x<contactos; x ++){ 1048 telefonos[x].toCharArray(telefonoAviso,20); //Conver 1049 "telefonoAviso" from String to array char type before calling sms.beginSMS 1050 1051 sms.beginSMS(telefonoAviso); 1052 sms.print(mensaje); 1053 sms.endSMS(); 1054 1055 } 1056} 1057 1058//This funcion sends reply messages when The device 1059 receives an SMS asking for some information. Depending on the working mode (Geriatrino 1060 or Alarm mode) 1061//different actions will be taken. Everyone can customize this 1062 function to adapt it to his needs. The code only reads the first character of the 1063 incoming 1064//SMS, and the options are treated in a 'case' structure. 1065void 1066 enviarRespuestaSMS(){ 1067 sms.remoteNumber(numeroCliente, 20); //The phone number 1068 from which the SMS has been sent is kept in "numeroCliente" 1069 //Convert the 1070 array char to string type to compare the number whith the phoned numbers configured 1071 during the settings, which are the only numbers that 1072 //can communicate whith 1073 Geriatrino 1074 numeroClienteString = String(numeroCliente); 1075 if ((numeroClienteString 1076 == telefonos[0]) || (numeroClienteString == telefonos[1]) || (numeroClienteString 1077 == telefonos[2])){ 1078 //The firs character of the incoming SMS is saved in 1079 "contenidoMensaje" 1080 contenidoMensaje = sms.peek(); 1081 //After we pick 1082 the first character, we empty the incoming SMS buffer 1083 sms.flush(); 1084 1085 1086 //Everybody can customize the actions he wants to perform according to his 1087 needs 1088 switch(contenidoMensaje){ 1089 //If the message 1090 is a '1' I set this variable to true to make the buzzer sound. Depending of the 1091 working mode, the action and the answer of the device 1092 //are different. 1093 1094 case '1': 1095 //Geriatrino mode 1096 if (modoAlarma == 1097 false){ 1098 sonarSirena = true; 1099 ciclosSirena =0; 1100 mensaje 1101 = "The buzzer has been activated on request"; 1102 } 1103 //Alarm mode 1104 1105 else{ 1106 alarmaActiva = true; //When working in alarm mode, the 1107 alarm will be armed after receiving a '1' 1108 mensaje = "The alarm 1109 has been armed"; 1110 } 1111 break; 1112 1113 case '2': 1114 1115 //In Geriatrino mode when a '2' is received and SMS is sent to the phone 1116 sender with info about the time in which last activity was detected 1117 if 1118 (modoAlarma == false){ 1119 //First I get the hour and minute in which the 1120 last correct activation of both PIRs occured. 1121 horasUltimaActivacionCorrecta 1122 =(millis()-tiempoUltimaActivacionCorrecta)/3600000; 1123 minutosUltimaActivacionCorrecta 1124 = ((millis()-tiempoUltimaActivacionCorrecta) % 3600000)/60000; 1125 //Then 1126 I do the same to get the time of the last door opening. 1127 horasUltimaAperturaNevera 1128 = (millis()- tiempoUltimaAperturaNevera)/3600000; 1129 minutosUltimaAperturaNevera 1130 = ((millis() - tiempoUltimaAperturaNevera)%3600000)/60000; 1131 mensaje ="Last 1132 presence detected occured "; 1133 mensaje.concat(String(horasUltimaActivacionCorrecta)); 1134 mensaje.concat(" hours and "); 1135 mensaje.concat(String(minutosUltimaActivacionCorrecta)); 1136 mensaje.concat(" minutes"); 1137 mensaje.concat(" ago."); 1138 mensaje.concat('\ 1139'); 1140 1141 mensaje.concat("Last door opening occured "); 1142 mensaje.concat(String(horasUltimaAperturaNevera)); 1143 mensaje.concat(" hours and "); 1144 mensaje.concat(String(minutosUltimaAperturaNevera)); 1145 mensaje.concat(" minutes"); 1146 mensaje.concat(" ago."); 1147 1148 } 1149 //On Alarm mode when a '2' is received the alarm is disarmed. 1150 1151 else{ 1152 alarmaActiva = false; 1153 mensaje = "The alarm 1154 has been disarmed"; 1155 } 1156 break; 1157 1158 case '3': 1159 1160 //On Geriatrino mode if a '3' is received an SMS with the current time of 1161 the device is sent, and it also informs whether it is in rest period or not. 1162 1163 if (modoAlarma == false){ 1164 horaActual = rtc.getHours(); 1165 minutoActual 1166 =rtc.getMinutes(); 1167 segundoActual = rtc.getSeconds(); 1168 mensaje 1169 = "La hora actual es "; 1170 mensaje.concat(String(horaActual)); 1171 mensaje.concat(" 1172 : "); 1173 mensaje.concat(String(minutoActual)); 1174 mensaje.concat(" 1175 : "); 1176 mensaje.concat(String(segundoActual)); 1177 mensaje.concat('\ 1178'); 1179 1180 if (horarioNocturno == true) { 1181 mensaje.concat("Working in 1182 rest period"); 1183 } 1184 else{ 1185 mensaje.concat("Working 1186 in active period"); 1187 } 1188 } 1189 //On the Alarm mode I reserve 1190 this option to make the buzzer sound. 1191 else{ 1192 sonarSirena = true; 1193 1194 ciclosSirena =0; 1195 mensaje = "The buzzer has been activated 1196 and it will sound for 30 seconds"; 1197 } 1198 break; 1199 1200 1201 case '4': 1202 //Regardless of the operating mode, when a '4' 1203 is received and SMS is sent with a complete information about the settings 1204 mensaje 1205 = "CONTACTS: "; 1206 for (int f =0; f < contactos; f ++){ 1207 mensaje.concat(telefonos[f]); 1208 1209 mensaje.concat(", "); 1210 } 1211 mensaje.concat('\ 1212'); 1213 1214 mensaje.concat("PIN : "); 1215 mensaje.concat(numeroPIN); 1216 mensaje.concat('\ 1217'); 1218 1219 mensaje.concat("Mode "); 1220 if (modoAlarma ==false){ 1221 mensaje.concat("GERIATRINO"); 1222 1223 } 1224 else{ 1225 mensaje.concat("ALARM"); 1226 } 1227 mensaje.concat('\ 1228'); 1229 1230 //When operating in Geriatrino Mode, the following information will be added 1231 to the SMS 1232 if (modoAlarma==false){ 1233 mensaje.concat("Warning 1234 period "); 1235 mensaje.concat(periodo); 1236 mensaje.concat(" hours"); 1237 1238 mensaje.concat('\ 1239'); 1240 mensaje.concat("Start rest period "); 1241 1242 mensaje.concat(inicioPeriodoSinActividadString); 1243 mensaje.concat('\ 1244'); 1245 1246 mensaje.concat("Duration rest period "); 1247 mensaje.concat(duracionPeriodoSinActividadString); 1248 1249 } 1250 break; 1251 1252 default: 1253 break; 1254 1255 1256 } //cierro switch 1257 //The answer is sent only to the sender 1258 phone number 1259 sms.beginSMS(numeroCliente); 1260 sms.print(mensaje); 1261 sms.endSMS(); 1262 1263 } 1264} //End of EnviarRespuestaSMS() 1265 1266 1267//Function that analize the 1268 number of single activations for every PIR sensor and take actions to inform about 1269 it. The function is the same for both operating modes 1270void chequeoFuncionamientoPIR(){ 1271 1272 if(activacionSolitarioPIR1>activacionSolitarioPIR2){ 1273 diferenciaActivaciones 1274 = activacionSolitarioPIR1-activacionSolitarioPIR2; 1275 // PIR's are read 1276 only every 60 seconds. I have decided to set a number of at least 20 single activations 1277 of a sensor within a period of 24 hours 1278 //to send the message reporting 1279 the misfunction. Everyone can adjust this threshold number and will depend on the 1280 quality of the sensors and on 1281 //the existing conditions in the places 1282 where the devices has been installed (insects, air streams, etc). 1283 //To 1284 reduce this number I HAVE PREVIOUSLY SET TO THE MINIMUM, THE SENSITIVITY CONTROL 1285 OF BOTH SENSORS 1286 if(diferenciaActivaciones > 20){ 1287 mensaje 1288 = "PIR1 has activated "; 1289 mensaje.concat(String(diferenciaActivaciones)); 1290 1291 mensaje.concat(" more times than PIR2"); 1292 1293 1294 enviarAvisoSMS(); 1295 } 1296 } 1297 else{ 1298 diferenciaActivaciones 1299 =activacionSolitarioPIR2-activacionSolitarioPIR1; 1300 if(diferenciaActivaciones 1301 > 20){ 1302 mensaje = "PIR2 has activated "; 1303 mensaje.concat(String(diferenciaActivaciones)); 1304 1305 mensaje.concat(" more times than PIR1"); 1306 enviarAvisoSMS(); 1307 1308 } 1309 } 1310 //After managing the different number 1311 of activations within 24 hours all the counters and control variables are reset 1312 1313 activacionSolitarioPIR1=0; 1314 activacionSolitarioPIR2=0; 1315 1316 tiempoDesdePrimeraActivacionSolitarioPIR1 = millis(); 1317 tiempoDesdePrimeraActivacionSolitarioPIR2 1318 = millis(); 1319 } 1320 1321 1322//This function is called from loop() every hour to 1323 check the status of the network connection, if no connection detected it will be 1324 called every 15 minutes 1325//until it is restored 1326void chequeoRedGSM(){ 1327 1328 noConectado = true; 1329 for(int n=0; n < 3; n++) { 1330 delay(1000); 1331 1332 if(gsm.begin(PIN)==GSM_READY) 1333 { 1334 noConectado = false; 1335 1336 break; 1337 } //cierro if 1338 } //cierro for 1339 1340 ultimoChequeoRedGSM = millis(); 1341} 1342 1343 1344//Function that activates 1345 the siren or buzzer in a different way depending on the operating mode 1346void 1347 sirenaActiva(){ 1348 //Geriatrino mode (this has been set to meet my particular 1349 needs) 1350 if (modoAlarma == false){ 1351 1352 digitalWrite(PinSirena, 1353 HIGH); 1354 digitalWrite(PinLed, HIGH); //debug 1355 delay(2000); 1356 1357 digitalWrite(PinSirena, LOW); 1358 digitalWrite(PinLed, LOW); //debug 1359 1360 delay(2000); 1361 ciclosSirena+=1; 1362 if (ciclosSirena ==8){ 1363 1364 sonarSirena=false; 1365 ciclosSirena =0; 1366 } 1367 1368 } 1369 //Alarm mode. (it sounds for 30 seconds) 1370 else{ 1371 digitalWrite(PinSirena, 1372 HIGH); 1373 digitalWrite(PinLed, HIGH); 1374 delay(1000); 1375 ciclosSirena 1376 +=1; 1377 if (ciclosSirena >30){ 1378 sonarSirena = false; 1379 ciclosSirena=0; 1380 1381 } 1382 } 1383} //End of sirenaActiva() 1384 1385 1386 1387 1388void loop() 1389 { 1390 1391// G E R I A T R I N O M O D E 1392while (modoAlarma == false) { 1393 1394 //If the rest period is on (horarioNocturno == true), the program checks the 1395 time passed since it started, and checks when it finishes according to the duration 1396 1397 //of the rest period that was configured during the settings (3600000 * 1398 duracionPeriodoSinActividad) 1399 1400 if ((horarioNocturno == true) && ((millis()- 1401 tiempoDeNoche) > (3600000 * duracionPeriodoSinActividad))){ 1402 horarioNocturno 1403 = false; 1404 //When the rest period ends, I reset these variables to start 1405 counting the time with no activity detected. 1406 tiempoSinAperturaPuerta = 1407 millis(); 1408 tiempoSinPresenciaDetectada = millis(); 1409 } 1410 1411 if 1412 ((digitalRead(PinPIR1) == HIGH) && (presenciaPIR1 == false)) { 1413 presenciaPIR1 1414 = true; 1415 inicioPresenciaPIR1 = millis(); 1416 delay(200); 1417 1418 } 1419 1420 if ((digitalRead(PinPIR2) == HIGH) && (presenciaPIR2 == false)) 1421 { 1422 presenciaPIR2 = true; 1423 inicioPresenciaPIR2 = millis(); 1424 delay(200); 1425 1426 } 1427 1428 //IMPORTANT.- To understand the following lines of 1429 code, I must say that I have set the control of the PIRs that determines the duration 1430 of the activation signal to the minimum. 1431 //After this is done, in all my 1432 test with different PIR sensors of the model HC-SR501, the output signal is not 1433 active for more than 8 seconds (normally on for 4 or 5 seconds) 1434 //According 1435 to this, I read if the PIR have been activated every 60 seconds 1436 if ((presenciaPIR1== 1437 true) &&((millis()- inicioPresenciaPIR1) >= 60000)){ 1438 //If both sensors 1439 were activated, it is considered a valid detection and I reset the flags 1440 if 1441 (presenciaPIR2 == true) { 1442 presenciaPIR1 = false; 1443 presenciaPIR2 1444 = false; 1445 //This variable keeps the time since the last valid detection 1446 occured. The code uses it to trigger alarms, but only if we are not in the rest 1447 period 1448 tiempoUltimaActivacionCorrecta = millis(); 1449 //This variable 1450 is similar. It is not used to trigger alerts, but it is updated when a valid detection 1451 occurs even if it was during the rest period. 1452 tiempoSinPresenciaDetectada 1453 = millis(); 1454 horasSinPresencia =0; 1455 mensajesAvisoSinPresenciaDetectada 1456 =0; 1457 //This variable will keep the number of valid activations. I do 1458 not use it in this sketch but it could be used in a later version to implement 1459 a mechanism 1460 //that sends us on request what has been the level of activity 1461 during a specified period of time. This way we can monitor the activity level of 1462 the person 1463 numeroActivacionesCorrectas = numeroActivacionesCorrectas 1464 +1; 1465 } 1466 //If 60 seconds have passed since the PIR1 was activated 1467 and the other PIR2 was not, then I consider a false activation and I reset the 1468 flags. 1469 else{ 1470 presenciaPIR1= false; 1471 activacionSolitarioPIR1 1472 = activacionSolitarioPIR1 + 1; 1473 //If it is the first single activation 1474 of PIR1 I reset the timer to start counting 24 hours since this event. 1475 if 1476 (activacionSolitarioPIR1 ==1){ 1477 tiempoDesdePrimeraActivacionSolitarioPIR1= 1478 millis(); 1479 } 1480 }//close else 1481 }// close if (presenciaPIR1 1482 == true)...... 1483 1484 1485 //CONTROL PIR 2 This block is similar to 1486 the previous one, but now we check PIR2 1487 if ((presenciaPIR2== true) &&((millis()- 1488 inicioPresenciaPIR2) >= 60000)){ 1489 if (presenciaPIR1 == true) { 1490 presenciaPIR1 1491 = false; 1492 presenciaPIR2 = false; 1493 tiempoUltimaActivacionCorrecta 1494 = millis(); 1495 tiempoSinPresenciaDetectada =millis(); 1496 mensajesAvisoSinPresenciaDetectada 1497 = 0; 1498 horasSinPresencia =0; 1499 numeroActivacionesCorrectas = numeroActivacionesCorrectas 1500 +1; 1501 } 1502 else{ 1503 presenciaPIR2= false; 1504 activacionSolitarioPIR2 1505 = activacionSolitarioPIR2 + 1; 1506 if (activacionSolitarioPIR2 ==1){ 1507 1508 tiempoDesdePrimeraActivacionSolitarioPIR2 = millis(); 1509 } 1510 1511 }//close else 1512 }// close if (presenciaPIR2 == true) 1513 1514 1515 1516 //After reading the signals of the PIR sensors, the sketch manages the information 1517 regarding the last valid activation, and the number of false activations. 1518 //If 1519 active period is on, and no activity has been detected during the number of hours 1520 we configured during the settings, then an alert is triggered 1521 if (((millis() 1522 - tiempoSinPresenciaDetectada) > (intervaloAvisos * 3600000)) && (horarioNocturno 1523 == false)){ 1524 mensajesAvisoSinPresenciaDetectada = mensajesAvisoSinPresenciaDetectada 1525 +1; 1526 //This timer is reseted here, to start counting the time for a new 1527 period before the next alert message is sent 1528 tiempoSinPresenciaDetectada 1529 = millis(); 1530 horasSinPresencia = mensajesAvisoSinPresenciaDetectada * 1531 intervaloAvisos; 1532 mensaje = "No presence detected during the last "; 1533 1534 mensaje.concat(String(horasSinPresencia)); 1535 mensaje.concat(" 1536 hours"); 1537 enviarAvisoSMS(); //We call the function that sends the SMS 1538 with the rigth message 1539 } 1540 1541 //The following block manages the 1542 number of false activations (activation of a single PIR sensor) within a period 1543 of 24 hours. 1544 if ((((millis()- tiempoDesdePrimeraActivacionSolitarioPIR1) > 1545 (86400000)) || ((millis() - tiempoDesdePrimeraActivacionSolitarioPIR2) > (86400000)))&& 1546 ((activacionSolitarioPIR1 != 0)||( activacionSolitarioPIR2 !=0))){ 1547 chequeoFuncionamientoPIR(); 1548 //If 24 hours have passed since the first false activation was detected, then 1549 I call this function 1550 } 1551 1552 //CONTROL OF DOOR SENSOR 1553 if ((digitalRead(PinDetectorPuerta) 1554 == HIGH) && (puertaAbierta == false)) { 1555 //When the door is opened we reset 1556 all the timer and flags related to this sensor 1557 puertaAbierta = true; 1558 1559 inicioPuertaAbierta = millis(); 1560 tiempoUltimaAperturaNevera 1561 = millis(); 1562 tiempoSinAperturaPuerta = millis(); 1563 mensajesAvisoNeveraSinAbrir 1564 =0; 1565 //This variable wont be used in this sketch, but we can use it in 1566 a later version to get the degree of activitiy of the monitored person 1567 numeroAperturasPuerta 1568 = numeroAperturasPuerta +1; 1569 } 1570 1571 1572 //After 10 minutos since 1573 the door opening was detected, (I consider 10 minutes are enough time to take out 1574 an put food in the fridge), I check if has been closed, 1575 //and if so, variable 1576 PuertaAbierta is set to "false" and the code will start to check when the door 1577 is open again. 1578 if (((millis()-inicioPuertaAbierta) > (600000)) && (digitalRead(PinDetectorPuerta) 1579 == LOW)&& (puertaAbierta == true)){ 1580 puertaAbierta = false; 1581 HorasConPuertaAbierta 1582 =0; 1583 mensajesAvisoNeveraSinAbrir =0; 1584 } 1585 //But if after one 1586 hour since the door was opened, it is no closed, then I start to consider that the 1587 door has been left open. 1588 if (((millis()-inicioPuertaAbierta) > (3600000)) 1589 && (puertaAbierta == true)){ 1590 //this counter controls the number of hours 1591 that the door remains open 1592 HorasConPuertaAbierta = HorasConPuertaAbierta 1593 +1; 1594 puertaAbierta = false; 1595 inicioPuertaAbierta = millis(); 1596 1597 } 1598 //If the fridge door remains opened for three hours in a row, then 1599 I consider it enough time to send and alert message 1600 if (HorasConPuertaAbierta 1601 >= 3){ 1602 mensaje = "The fridge door has been open for more than 1603 three hours"; 1604 enviarAvisoSMS(); 1605 HorasConPuertaAbierta =0; 1606 1607 //sonarSirena = true; //We could make the buzzer sound to advise the person 1608 that the door is been open to much time 1609 } 1610 1611 //Now I deal with 1612 the case in which the door is properly closed, but the sensor does not detect it 1613 is open again in the time we specified 1614 //to trigger alerts 1615 if (((millis() 1616 - tiempoSinAperturaPuerta) > (3600000 * intervaloAvisos)) && (horarioNocturno == 1617 false)){ 1618 tiempoSinAperturaPuerta = millis(); 1619 mensajesAvisoNeveraSinAbrir 1620 = mensajesAvisoNeveraSinAbrir + 1; 1621 horasSinApertura = mensajesAvisoNeveraSinAbrir 1622 * intervaloAvisos; 1623 mensaje = "The fridge has not been open during the 1624 last "; 1625 mensaje.concat(String(horasSinApertura)); 1626 mensaje.concat(" 1627 hours"); 1628 Serial.println(mensaje); 1629 enviarAvisoSMS(); 1630 1631 } 1632 1633 /* These lines should be uncommented if we 1634 want the buzzer to make sound 1635 if (sonarSirena == true){ 1636 sirenaActiva(); 1637 1638 } */ 1639 1640 1641 //Check the connection status every hour. If after an attempt 1642 to connect we have no connection, the device will try to connect again in fifteen 1643 minutes 1644 if ( (((millis() - ultimoChequeoRedGSM) > 3600000) || ((millis() - 1645 ultimoChequeoRedGSM) > 900000)) && (noConectado == true) ) { 1646 1647 chequeoRedGSM(); 1648 } 1649 1650 //Now the program checks if an SMS has 1651 been received 1652 if (sms.available()){ 1653 enviarRespuestaSMS(); 1654 } 1655 1656 1657}//Close " while (modoAlarma == false)" End of the code executed when 1658 operating in Geriatrino Mode 1659 1660 1661 1662 1663//A L A R M M O D E 1664 1665while 1666 (modoAlarma== true){ 1667 estadoPrevioAlarma = false; 1668 //I first check if an 1669 SMS has been received to arm the alarm. 1670 if (sms.available()){ 1671 //When 1672 operating in alarm mode, if a '1' is received then following function sets the variable 1673 "alarmaActiva" to true 1674 //If a '2' is received it il be set to false. 1675 1676 enviarRespuestaSMS(); 1677 } 1678 1679 //NOTE.-We could use another way 1680 to arm/disarm the alarm. A switcher could be used for this purpose and then we 1681 will use the following line to control 1682 //the loop while(digitalRead(PinAlarm) 1683 == true) 1684 1685 //When the alarm is armed the next block will be always executed 1686 1687 while(alarmaActiva == true) { 1688 //After the alarm has been armed, in the 1689 first cicle of the loop all the timers and flags used to manage the information 1690 are reset 1691 //The program waits for 60 seconds to let people leave the house 1692 before it starts reading the sensors 1693 if(estadoPrevioAlarma == false) 1694 1695 { 1696 estadoPrevioAlarma = true; 1697 presenciaPIR1 = false; 1698 presenciaPIR2 1699 = false; 1700 puertaAbierta = false; 1701 deteccionIntruso = false; 1702 1703 avisoIntrusoEnviado = 0; 1704 envioMensajeActivacionPuertaEnSolitario 1705 = false; 1706 ultimoChequeoRedGSM = millis(); 1707 activacionSolitarioPIR1=0; 1708 1709 activacionSolitarioPIR2=0; 1710 numeroSensoresActivos=0; 1711 delay(60000); 1712 1713 } 1714 1715 //I check the incoming SMS inside the 'while' loop too, 1716 to check if a message has been received to disarm the alarm. 1717 if (sms.available()){ 1718 1719 enviarRespuestaSMS(); 1720 } 1721 1722 //When operating in 1723 Alarm mode the information provided by the sensors is treated in a different way. 1724 I consider that an intruder has been 1725 //detected when at least two of the 1726 three sensors provide an active signal. 1727 if ((digitalRead(PinPIR1) == HIGH) 1728 && (presenciaPIR1 == false)) { 1729 presenciaPIR1 = true; 1730 numeroSensoresActivos 1731 = numeroSensoresActivos +1; 1732 inicioPresenciaPIR1 = millis(); 1733 delay(100); 1734 1735 } 1736 1737 if ((digitalRead(PinPIR2) == HIGH) && (presenciaPIR2 == false)) 1738 { 1739 presenciaPIR2 = true; 1740 numeroSensoresActivos = numeroSensoresActivos 1741 +1; 1742 inicioPresenciaPIR2 = millis(); 1743 delay(100); 1744 } 1745 1746 1747 if ((digitalRead(PinDetectorPuerta) == HIGH) && (puertaAbierta == false)) { 1748 1749 puertaAbierta = true; 1750 numeroSensoresActivos = numeroSensoresActivos 1751 +1; 1752 inicioPuertaAbierta = millis(); 1753 delay(100); 1754 } 1755 1756 1757 //Regardless of the sensor type , if more than one sensor is active, 1758 an alarm is triggered. 1759 if ((numeroSensoresActivos >=2) && (deteccionIntruso 1760 ==false)) 1761 { 1762 1763 inicioDeteccionIntruso = millis(); 1764 1765 deteccionIntruso = true; 1766 delay(100); 1767 } 1768 1769 //If 1770 after 30 seconds since intrusion was detected, the alarm has not been disarmed, 1771 an alert message will be sent and the 1772 //buzzer will sound for 30 seconds. 1773 1774 //If nobody disarms the alarm, the intruder conditions will persist and the 1775 message will be sent again to a maximum of three times 1776 1777 1778 if 1779 (((millis() - inicioDeteccionIntruso) > 30000)&& (deteccionIntruso == true) && (avisoIntrusoEnviado 1780 <3)){ 1781 mensaje = "INTRUDER DETECTED, more than one sensor is active... 1782 "; 1783 mensaje.concat('\ 1784'); 1785 if (presenciaPIR1) { 1786 mensaje.concat("PIR 1787 1 ACTIVE"); 1788 } 1789 mensaje.concat('\ 1790'); 1791 if (presenciaPIR2) 1792 { 1793 mensaje.concat("PIR 2 ACTIVE"); 1794 } 1795 mensaje.concat('\ 1796'); 1797 1798 if (puertaAbierta) { 1799 mensaje.concat("DOOR OPEN"); 1800 } 1801 1802 else{ 1803 mensaje.concat("DOOR HAS NOT BEEN OPEN"); 1804 } 1805 1806 1807 //Once the message has been mounted, it is sent to all the interest phone 1808 numbers an the flags are reset. 1809 enviarAvisoSMS(); 1810 deteccionIntruso 1811 = false; 1812 presenciaPIR1 = false; 1813 presenciaPIR2 = false; 1814 puertaAbierta 1815 = false; 1816 numeroSensoresActivos=0; 1817 avisoIntrusoEnviado +=1 ; //This 1818 variable controls that the alarm message is sent a maximum of three times 1819 sonarSirena 1820 = true; 1821 } // close if ((millis() - inicioDeteccionIntruso) > 30000)&& 1822 ...... 1823 1824 1825 //In the same way that it was programmed in the Geriatrino mode, 1826 the following block checks if both PIR sensors work properly, or one of them 1827 1828 //gets active much more often than the other one. 1829 if ((presenciaPIR1== 1830 true) &&((millis()- inicioPresenciaPIR1) >= 50000)){ 1831 if (presenciaPIR2 1832 == true) { 1833 presenciaPIR1 = false; 1834 presenciaPIR2 = false; 1835 1836 } 1837 //If only PIR1 is active then I consider a false activation 1838 1839 else{ 1840 presenciaPIR1= false; 1841 activacionSolitarioPIR1 1842 = activacionSolitarioPIR1 + 1; 1843 if (activacionSolitarioPIR1 ==1){ 1844 1845 tiempoDesdePrimeraActivacionSolitarioPIR1= millis(); 1846 } 1847 1848 } 1849 } 1850 1851 //CONTROL PIR 2 1852 if ((presenciaPIR2== true) 1853 &&((millis()- inicioPresenciaPIR2) >= 50000)){ 1854 if (presenciaPIR1 == true) 1855 { 1856 presenciaPIR1 = false; 1857 presenciaPIR2 = false; 1858 } 1859 1860 else{ 1861 presenciaPIR2= false; 1862 activacionSolitarioPIR2 = 1863 activacionSolitarioPIR2 + 1; 1864 if (activacionSolitarioPIR2 ==1){ 1865 tiempoDesdePrimeraActivacionSolitarioPIR2 1866 = millis(); 1867 } 1868 } 1869 } 1870 1871 //24 hours after the 1872 first false activation occured, I call the function that checks how many false activations 1873 were detected and send a warning message if a limit is exceeded 1874 if ((((millis()- 1875 tiempoDesdePrimeraActivacionSolitarioPIR1) > (86400000)) || ((millis() - tiempoDesdePrimeraActivacionSolitarioPIR2) 1876 > (86400000)))&& ((activacionSolitarioPIR1 != 0)||( activacionSolitarioPIR2 !=0))){ 1877 1878 if (activacionSolitarioPIR1 != activacionSolitarioPIR2){ 1879 chequeoFuncionamientoPIR(); 1880 1881 } 1882 } 1883 1884 1885 //This blocks deals with the strange case 1886 in which only the door sensor is active. If after 50 seconds the door has been 1887 1888 //open but no PIR is active then I consider a false activation. This could happen 1889 if one of the components of the 1890 //door sensor is moved or it falls down to 1891 the floor 1892 if(((millis() - inicioPuertaAbierta) > 50000)&& (puertaAbierta 1893 == true)){ 1894 numeroSensoresActivos =0; 1895 puertaAbierta=false; 1896 envioMensajeActivacionPuertaEnSolitario 1897 +=1; 1898 if (envioMensajeActivacionPuertaEnSolitario < 3){ 1899 mensaje ="El 1900 sensor puerta se ha activado en solitario"; 1901 enviarAvisoSMS(); 1902 1903 } 1904 } 1905 1906 //The connection to the net will be checked every hour, 1907 but if an intruder detection is on the run, it will wait until this situation is 1908 managed 1909 //before calling the function chequeoRedGSM() 1910 if ((((millis() 1911 - ultimoChequeoRedGSM) > 3600000) && (deteccionIntruso == false)) || (((millis() 1912 - ultimoChequeoRedGSM) > 900000)&&(deteccionIntruso == false) && (noConectado == 1913 true))) 1914 { 1915 chequeoRedGSM(); 1916 } 1917 1918 1919 //This function makes the siren sound if the variable sonarSirena is true 1920 1921 if (sonarSirena) { 1922 sirenaActiva(); 1923 } 1924 1925 }//close while 1926 (alarmaActiva == true) 1927}//close while modoALARMA == true 1928 1929} // End of 1930 loop() 1931
Downloadable files
Geriatrino basic 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.
GERIATRINO_CONF_ENGLISH
Geriatrino basic 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.
GERIATRINO_CONF_ENGLISH
Documentation
Geriatrino Cover
Geriatrino Cover
Geriatrino Case
Geriatrino Case
Geriatrino Case
Geriatrino Case
Geriatrino Cover
Geriatrino Cover
Geriatrino leg
Geriatrino leg
Comments
Only logged in users can leave comments
ramialcala
0 Followers
•0 Projects
Table of contents
Intro
3
0