Semplice telesoccorso con Arduino

Tutti quanti molte volte abbiamo sentito parlare di quei dispositivi chiamati “telesoccorso” oppure “tele salvavita” i quali, nella loro forma più semplice, permettono di effettuare una chiamata verso un numero telefonico preimpostato dando così la possibilità di poter intervenire tempestivamente in tutte quelle situazioni di pericolo. Si pensi ad esempio ai numerosi casi in cui, per un motivo o per l’altro, molte persone anziane devono rimanere sole in casa e quanto sia importante per loro poter chiedere aiuto in caso di emergenza.

Il semplice telesoccorso descritto in questo articolo di certo non ha nessuna pretesa di competere con i numerosi tipi commercializzati, specialmente per le numerose opzioni aggiuntive che si possono trovare in svariati modelli, ma nella sua funzione di base svolge perfettamente il suo compito e partendo da questo potremo decidere di arricchirlo in futuro di ulteriori funzionalità.

 

Schema elettrico

Il principio di funzionamento di tutti i tipi di telesoccorso è molto semplice : la persona interessata porta con se un telecomando con un solo tasto, di solito appeso al collo, premendo il quale invia un segnale radio ad una centralina in grado di riconoscerlo e di effettuare, tramite la linea fissa, una chiamata verso il numero di telefono memorizzato. Quindi il sistema è formato da una coppia formata da trasmettitore e ricevitore che comunicano fra loro via radio. Detto questo partiamo dalla descrizione del ricevitore raffigurato nello schema elettrico qui in basso e in particolare dall’entrata della linea telefonica in alto a destra.

In condizioni normali il relè a doppio scambio risulta disattivato lasciando libera la linea in modo da permettere la ricezione di eventuali chiamate su telefoni collegati su altre prese. Premendo il pulsante sul trasmettitore si invia un segnale radio che verrà captato dal modulo ricevente a 433 Mhz, il quale sarà trasferito al pin 11 dell’Arduino UNO che lo elaborerà e commuterà l’uscita 3 a livello logico 1. In questo modo si provvederà a mandare in conduzione il transistor 2N3904 e a far chiudere i contatti del relè collegato sul suo collettore.

A questo punto la resistenza da 470 Ohm  1/2 W verrà a trovarsi in parallelo sulla linea telefonica e la tensione su di essa, che in condizioni normali è di circa 48 V, scenderà fino ad un valore intorno agli 8-10 V. Questa resistenza in pratica non fa altro che simulare lo sgancio della cornetta in modo da poter iniziare la procedura di composizione del numero. Ricevuto il segnale di allarme il buzzer collegato sul pin A1 suonerà per tre volte, contemporaneamente il led sul pin A5 si accenderà e dopo 3 secondi verrà effettuata la chiamata verso il numero telefonico memorizzato. Trascorso 1 minuto dall’invio della chiamata il circuito verrà disattivato in automatico e la linea telefonica tornerà libera ( questo lasso di tempo possiamo regolarlo cambiando la variabile corrispondente nello sketch ).

 

 

La composizione del numero avviene tramite la generazione di toni DTMF in uscita dai pin A3-A4 e immessi sulla linea telefonica attraverso il condensatore da 47 uF. Terminata questa fase, dopo circa 1 secondo, sulla linea verrà immesso un segnale di bassa frequenza che sarà udito dalla persona che riceverà la chiamata. In pratica, la persona che risponderà al telefono sentirà questo tono caratteristico e saprà che da quel numero è partita una richiesta di aiuto. Come accennato sopra, trascorso un minuto il relè verrà disattivato e il tutto si ripeterà ad ogni pressione del tasto posto sul trasmettitore o premendo il pulsante “test” collegato al pin 2.

Telesoccorso Arduino schema

 

Telefono Arduino
Vista frontale di un connettore femmina RJ11. Il positivo della linea ( filo rosso ) si trova a destra

 

Stessa foto di prima ma con vista dall’alto. Fare attenzione a non invertire le polarità dei terminali nella fase di montaggio

Come memorizzare un numero telefonico

La registrazione di un numero telefonico viene effettua attraverso i quattro pulsanti collegati alle entrate 10-9-8-A0 le cui funzioni sono riepilogate nella seguente tabella

 

PulsanteDescizioneNote
SetEntra o esce dalla fase di memorizzazione del numero telefonicoOgni volta che si preme questo pulsante il numero precedentemente memorizzato verrà cancellato
NumIncrementa da 0 a 9 le singole cifre
SuccAggiunge una cifra al numero
MemoEffettua la registrazione del numero sulla EEPROM di ArduinoDurante il normale funzionamento, premendo questo pulsante si visualizza sul display il numero telefonico memorizzato. Se la memoria è vuota sul display verrà visualizzato "Nessun numero"

 

In pratica si preme il pulsante “Set” e dopo aver visualizzato sul display “Imp. numero” si procede con i pulsanti “Num” e “Succ” che servono rispettivamente uno ad incrementare e l’altro ad aggiungere le cifre che andranno a formare il numero che abbiamo intenzione di memorizzare. Infine, basta premere il pulsante “Memo” e il numero sarà registrato sulla EEPROM dell’ATmega di Arduino. Il video seguente dovrebbe illustrare chiaramente la procedura appena descritta.

 

 

Nota : quando si entra nella fase di memorizzazione del numero, può succedere di sbagliare durante la composizione delle singole cifre e poiché non è possibile tornare indietro l’unica cosa che si può fare è uscire ed entrare dalla procedura premendo nuovamente il pulsante “Set”.

 

Quello seguente invece è lo schema del trasmettitore nel quale premendo il pulsante collegato al pin 7 invieremo un segnale codificato attraverso il modulo radio a 433 Mhz che raggiungerà lo stadio ricevente e farà partire la chiamata di soccorso. Anche se in questo schema viene raffigurata una scheda Arduino, nella realizzazione pratica l’intento è quello di poter rendere il telecomando il più compatto possibile e  quindi si può pensare di realizzare un sistema stand-alone con un ATmega 328. Anche per quanto riguarda l’antenna, indispensabile per una corretta trasmissione, si potrebbe pensare di nasconderla nel contenitore che decideremo di usare.

Arduino Trasmettitore 433 Mhz telesoccorso

Il video seguente mostra invece il funzionamento pratico del telesoccorso. Per inviare la chiamata viene usato il pulsante di Test.

 

 

Uno sguardo agli sketch

Qui in basso sono riportati gli sketch usati nel progetto. Partendo da quello più semplice del trasmettitore possiamo vedere come il programma non fa altro che trasmettere via radio una stringa che dovrà essere riconosciuta dal ricevitore attraverso la pressione del pulsante collegato al pin 7. Nello sketch la stringa in questione è “#SOS” e viene inviata per mezzo della funzione “Trasmetti()” che accetta come argomento una variabile di tipo *char. L’unica nota da aggiungere è che il codice fa uso della libreria “<RH_ASK.h> ( scaricabile a fondo pagina ) e che per il pulsante si possono usare tutti i pin  tranne il numero 12 in quanto esso viene utilizzato per default dalla libreria che codifica il segnale.

 

/*
 * Programma radiocomando a 433 Mhz  : TRASMETTITORE
   by Mario de Nichilo
   13/10/2018
	
 */

#include <RH_ASK.h>
#include <SPI.h>
int pulsante = 7;



RH_ASK askTx;  // Crea un oggetto ASK che permette la trasmissione di dati via radio sul pin 12

void setup()
{
  pinMode(pulsante, INPUT);
  askTx.init();   // inizializza l'oggetto ASK

}

// Trasmette la stringa che dovrà essere riconosciuta dal ricevitore
//
void trasmetti(char *msg)
{
  askTx.send((uint8_t *)msg, strlen(msg));
  askTx.waitPacketSent();
  delay(200);
}


void loop()
{
  if (digitalRead(pulsante) == LOW ) {
   
    trasmetti("#SOS");
   
  }
}

 

Di seguito invece viene riportato lo sketch usato dallo stadio ricevitore

 

/*
   Programma Telesoccorso Arduino
   Stadio ricevente
   by Mario de Nichilo
   18/10/2018

*/

#include <avr/eeprom.h>
#include <LiquidCrystal.h>
#include <RH_ASK.h>
#include <SPI.h>

LiquidCrystal lcd(12, 13, 5, 4, 7, 6); // RS-E-D4-D5-D6-D7

#define hp A4               // Uscita segnale BF sulla linea e Tono DTMF1
#define hp2 A3              // Uscita Tono DTMF2
#define pulsante 2          // Entrata segnale allarme
#define ledChiamata A5      // Indicazione allarme avvenuto
#define rele 3              // pin uscita del relè commutatore
#define set 10              // pulsante entra o esce dalla modalità imposta numero
#define num 9               // pin pulsante selezione numero
#define cifraSucc 8         // pin pulsante sposta cursore sul display
#define memo A0             // pin pulsante memorizza
#define dimMax 20           // numero massimo di cifre
#define bz A1               // pin buzzer
String EPt = "";            // Stringa usata per l'impostazione del numero telefonico
char ArrayTemp[dimMax];             // Array usato per la scrittura e lettura dati dalla EEPROM
char EEMEM ArrayEEPROM[dimMax];    // Array nella EEPROM
int tOff = 60;
int tOn = 30;
boolean chiamata = false;
int attesa = 0;
boolean setNum;
int n = 0;
short pos = 0;
boolean invioBF = false;
RH_ASK ask;               // Crea un oggetto ask per la ricezione dati


int DTMF[13][2] = {
  {941, 1336}, // tono 0
  {697, 1209}, // tono 1
  {697, 1336}, // tono 2
  {697, 1477}, // tono 3
  {770, 1209}, // tono 4
  {770, 1336}, // tono 5
  {770, 1477}, // tono 6
  {852, 1209}, // tono 7
  {852, 1336}, // tono 8
  {852, 1477}, // tono 9
  {941, 1209}, // tono *
  {941, 1477}, // tono #
  {0, 0}       // pausa
};

void playDTMF(byte digit, byte duration) {
  boolean tone1state = false;
  boolean tone2state = false;
  int tone1delay = (500000 / DTMF[digit][0]) - 10;
  int tone2delay = (500000 / DTMF[digit][1]) - 10;
  unsigned long tone1timer = micros();
  unsigned long tone2timer = micros();
  unsigned long timer = millis();
  if (digit == 12) {
    delay(1000);
  } else {
    while (millis() - timer < duration) {
      if (micros() - tone1timer > tone1delay) {
        tone1timer = micros();
        tone1state = !tone1state;
        digitalWrite(hp, tone1state);
      }
      if (micros() - tone2timer > tone2delay) {
        tone2timer = micros();
        tone2state = !tone2state;
        digitalWrite(hp2, tone2state);
      }
    }
    digitalWrite(hp, LOW);
    digitalWrite(hp2, LOW);
  }
}

void dialNumber(int number[], int len) {
  for (int i = 0; i < len; i++) {
    playDTMF(number[i], 100);
    delay(100);
  }
}


//
// funzione che converte una stringa in *char
//
char* stringTochar(String stringa) {
  if (stringa.length() != 0) {
    char *c = const_cast<char*>(stringa.c_str());
    return c;
  }
}



void displayDefault()
{
  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print("Telesoccorso");
  lcd.setCursor(0, 1);
  lcd.print("in funzione...");
}

void memorizza()
{
  /*
     La variabile String EPt che contiene il numero telefonico viene convertita in Array char
     dalla funzione stringTochar e copiata in ArrayEEPROM, cioè nella EEPROM, attraverso il
     metodo eeprom_write_block.
  */
  eeprom_write_block(stringTochar(EPt), ArrayEEPROM, dimMax); // Scrive nella EEPROM il numero di telefono
  delay(300);
  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print("Registr. OK");
  delay(1000);
  displayDefault();
}

void componiNumero()
{
  /*
     L'arrayEEPROM che contiene il numero telefonico viene copiato in ArrayTemp
     attraverso il metodo eeprom_read_block
  */
  eeprom_read_block(ArrayTemp, ArrayEEPROM, dimMax); // legge dalla EEPROM il numero di telefono
  delay(100);
  if (strlen(ArrayTemp) > 0 ) {   // controlla se ArrayTemp non sia vuoto

    int numeroTelefonico[dimMax]; // Array usato per memorizzare il numero telefonico in formato integer
    int lung = strlen(ArrayTemp); // numero di cifre che compongono il numero
    int cifra;                    // variabile usata per la conversione delle cifre da char a int

    lcd.clear();
    lcd.setCursor(0, 0);
    lcd.print("Sto chiamando:");
    lcd.setCursor(0, 1);
    lcd.print(ArrayTemp);

    Serial.println(ArrayTemp); // scrive sulla seriale il numero recuperato sulla EEPROM
    delay(1000);               // attende 1 sec.

    /*
       Ogni cifra del numero telefonico contenuta in ArrayTemp viene scansionata
       e convertita in formato intero e successivamente memorizzata nell'array numeroTelefonico
    */
    for ( int i = 0; i < strlen(ArrayTemp); i++)
    {
      cifra = (int)ArrayTemp[i] - '0';

      numeroTelefonico[i] = cifra;
      Serial.println(numeroTelefonico[i]);
    }

    digitalWrite(rele, HIGH);   // Aziona il relè per stabilire la connessione con la linea telefonica
    delay(3000);
    dialNumber(numeroTelefonico, lung);
    delay(1000);
    invioBF = true;
  } else {

    /*
       Se la EEPROM non contiene nessun numero visualizza
       sul display la scritta "EEPRON vuota!" per tre secondi

    */
    lcd.clear();
    lcd.setCursor(0, 0);
    lcd.print("Errore:");
    lcd.setCursor(0, 1);
    lcd.print("EEPROM vuota!");
    delay(3000);
    displayDefault();
    return;
  }
  while (chiamata == true )
  {
    digitalWrite(ledChiamata, HIGH); // Visualizzazione chiamata in corso

    if (invioBF == true)
    {
      tone( hp, 754, 200 ); // Tono allarme immesso sulla linea telefonica
      delay(200);
      tone( hp, 390, 200 );
      delay(500);
    }


    if (attesa > 60) // +- 1 min
    {
      chiamata = false;
      attesa = 0;
      digitalWrite(rele, LOW);
      digitalWrite(ledChiamata, LOW);
      delay(100);
      displayDefault();
      invioBF = false;

    }
    attesa++;
  }
}



void setup() {

  ask.init();
  Serial.begin(9600);
  pinMode(hp, OUTPUT);
  pinMode(hp2, OUTPUT);
  pinMode(rele, OUTPUT);
  pinMode(ledChiamata, OUTPUT);
  pinMode(pulsante, INPUT);
  pinMode(set, INPUT);
  pinMode(num, INPUT);
  pinMode(cifraSucc, INPUT);
  pinMode(memo, INPUT);
  pinMode(bz, OUTPUT);

  digitalWrite(ledChiamata, LOW);
  digitalWrite(rele, LOW);
  lcd.begin(16, 2);
  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print("Telesoccorso");
  lcd.setCursor(0, 1);
  lcd.print("in funzione...");
  setNum = false;
}

void loop() {


  /*
      Premendo il pulsante "set" inverto la variabile "setNum",
      in questo ad ogni pressione del pulsante si entra ed esce
      dalla funzione di impostazione del numero telefonico

  */
  if (digitalRead(set) == LOW)
  {
    setNum = !setNum;
    delay(300);
    if ( setNum == true )
    {
      // Cancello prima la EEPROM
      EPt = "";
      eeprom_write_block(stringTochar(EPt), ArrayEEPROM, dimMax);
      delay(300);
      lcd.clear();
      lcd.setCursor(0, 0);
      lcd.print("Imp. numero");
    }
    if ( setNum == false ) {
      n = 0;
      displayDefault();

    }
  }

  switch (setNum)
  {
    /*
       Se "setNum" è true,attraverso la pressione del pulsante "num"
       incremento le cifre che dovranno formare il numero telefonico
       da memorizzare.

    */
    case true:
      lcd.setCursor(pos, 1);
      lcd.print(n);
      if (digitalRead(num) == LOW)
      {
        n++;                  // parto da zero e incremento n
        if (n > 9)
        {
          n = 0;              //azzero n se supera 9
        }
        lcd.setCursor(pos, 1);
        lcd.print(n);         // scrivo sul display il numero
        delay(300);
      }

      if ( digitalRead(cifraSucc) == LOW ) // passo alla cifra successiva con il pulsante "succ"
      {
        EPt.concat(String(n));  // memorizzo nella stringa EP l'ultima cifra scelta
        pos++;
        n = 0;
        if (pos > 15)
        {
          pos = 0;
        }
        lcd.setCursor(pos, 1); // faccio attenzione a considerare la lunghezza della riga del display
        lcd.print(n);
        delay(300);
      }
      if (digitalRead(memo) == LOW) // pulsante di memorizzazione del numero
      {
        EPt.concat(String(n));
        memorizza();                // richiamo il codice che effettua la scrittura in memoria
        setNum = false;             // esco dalla fase di settaggio del numero
      }
      break;

    case false:

      // visualizza,se c'è, il numero memorizzato nella EEPROM
      pos = 0;


      /*
         Se premo il pulsante "memo" al di fuori della procedura di memorizzazione,
         visualizza sul display il numero telefonico registrato in memoria oppure stampa
         "nessun numero" se quest'ultima è vuota.
      */

      if (digitalRead(memo) == LOW)
      {
        delay(300);
        eeprom_read_block(ArrayTemp, ArrayEEPROM, dimMax); // legge dalla EEPROM il numero di telefono
        delay(100);
        if (strlen(ArrayTemp) > 0) { // verifica se l'array del numero è vuoto
          lcd.clear();
          lcd.setCursor(0, 0);
          lcd.print(ArrayTemp);
          delay(2000);
        } else {
          lcd.clear();
          lcd.setCursor(0, 0);
          lcd.print("nessun numero");
          delay(2000);
        }
        displayDefault();

      }
      break;
  }

  /*
      Ricezione del segnale radio
  */

  uint8_t buf[RH_ASK_MAX_MESSAGE_LEN];  // Array in cui viene memorizzato il dato ricevuto
  uint8_t buflen = sizeof(buf);
  byte lung_datoRicevuto = 4;           // Lunghezza dell stringa che ci si aspetta di avere
  char dato[lung_datoRicevuto];

  if (ask.recv(buf, &buflen))
  {
    // Prendo i primi quattro caratteri della variabile buf e li memorizzo nella variabile 'dato'
    //
    for (int j = 0; j < lung_datoRicevuto; j++)
    {
      dato[j] = buf[j];
    }

    // converto la variabile char 'dato' in stringa

    if (String(dato).substring(0, 4) == "#SOS")
    {
      delay(500);
      for (int j = 0; j < 3; j++)
      {
        tone( bz, 390, 300 ); // Tono allarme immesso sulla linea telefonica
        delay(500);
      }
      chiamata = true;
      componiNumero();
    }
  }


  /*
     alla pressione del pulsante "test" si avvia la chiamata al
     numero memorizzato un memoria
  */


  if (digitalRead(pulsante) == LOW) {
    delay(500);
    for (int j = 0; j < 3; j++)
    {
      tone( bz, 390, 300 ); // Tono immesso sulla linea telefonica
      delay(500);
    }

    chiamata = true;
    componiNumero(); // esegue la chiamata

  }
}

 

Logicamente esso risulta notevolmente più complesso rispetto a quello del trasmettitore, in quanto deve gestire diversi aspetti come ad esempio la fase di formazione del numero attraverso i pulsanti visti sopra e la memorizzazione dello stesso sulla EEPROM. Per tale scopo viene usata dapprima una variabile tipo String denominata “EPt” la quale, alla pressione del pulsante “Memo” e attraverso il richiamo della funzione “memorizza()“, viene convertita  in char* e fisicamente memorizzata  all’interno dell’array ArrayEEPROM grazie al metodo eeprom_write_block() della libreria <avr/eeprom.h>.

Nel momento in cui si va ad effettuare la chiamata viene eseguita la funzione componiNumero(), nella quale viene evocato il metodo eeprom_read_block() che copia il contenuto di ArrayEEPROM in ArrayTemp[].  Dopo aver controllato che questo array non sia vuoto, si effettua una conversione dei suoi elementi da char a Int e lo si copia in un altro array chiamato numeroTelefonico[], quest’ultimo a sua volta sarà passato come argomento in dialNumber() che infine, attraverso playDTMF(), genererà i toni DTMF corrispondenti al numero letto dalla memoria. Per quanto riguarda la ricezione del segnale radio, vediamo che esso è affidato alla libreria già menzionata e non fa altro che confrontare il dato in arrivo con quello che deve far scattare le operazioni appena viste. Per il resto penso che il codice sia stato sufficientemente commentato e quindi non dovrebbe risultare difficile.

 

Elenco componenti del ricevitore

TipoQuantità
Scheda Arduino UNO1
Display LCD 16 x 21
Modulo radio 433 Mhz XD-RF-5V1
Amplificatore 1 W basato su
LM 386
1
Transistor NPN tipo 2N3904 oppure BC5471
Relè 5V doppio scambio1
Connettore femmina RJ111
Diodo led2
Diodo 1N40071
Trimmer da 5K1
Condensatore elettrolitico da 47uF 400V1
Condensatore elettrolitico da 4,7uF 25V2
Buzzer preamplificato1
Resistenza da 470 Ohm 1/2 Watt1
Resistenza da 240 Ohm1
Resistenza da 270 Ohm1
Resistenza da 470 Ohm 1/4 W1
Resistenza d 1K 1/4 W1
Resistenza da 10K 1/4 W6
Altoparlante 8 Ohm1
Pulsante N.A.5

 

Elenco componenti del telecomando

TipoQuantità
Scheda Arduino UNO1
Modulo radio 433 Mhz tipo XD-SFT1
Resistenza da 10K 1/4 W1
Pulsante N.A.1

 

File del progetto

Sketch Telesoccorso

Schemi TX-RX

RadioHead-1.86

 

Considerazioni finali

Come già detto all’inizio, benché il circuito nella sua funzione di base svolge perfettamente il suo compito esso potrebbe essere migliorato in alcuni aspetti. Ad esempio, lo si potrebbe dotare della capacità di poter memorizzare più numeri di telefono anziché uno solo e far in modo che questi vengano chiamati ciclicamente e a distanza di tempo prestabiliti. Inoltre, lo si potrebbe dotare di un sistema di alimentazione autonomo in caso di mancanza di corrente elettrica, ecc. ma tutto questo logicamente avrebbe richiesto un codice e uno schema più complessi di quelli visti. Ciò nonostante esso potrebbe essere preso come base per poter realizzare progetti di altro tipo e più rispondenti alle nostre esigenze.