martes, 23 de septiembre de 2014

Discriminador de llamadas telefónicas a líneas fijas

Hola, en esta nueva entrada voy a describir como crear un discriminador de llamadas telefónicas usando la capacidad de ciertos modems serie de hacer Caller ID o lo que es lo mismo, identificación de llamadas. Para ello usaremos un Arduino UNO R3 Atmega32, un modem serie U.S. Robotics 56K, un modulo serie, un cable serie modem nulo, un modulo SD YL-30 y un keypad shield LCD 16x2 (en la última versión del proyecto se usa un LCD 16x2 y una botonadura simple de 4 botones DIY). Como siempre el costo de los materiales es ridículo, si exceptuamos el módem, que es un cacharro que muchos tenemos en casa de la época del internet sin ADSL.

El sistema funciona del siguiente modo:

Al recibirse una llamada, el modem realiza un Caller ID y detecta el número llamante, para que se reciba esta información al menos deben recibirse dos RING, este es enviado al Arduino a través del cable modem nulo y el módulo serie. Arduino lee el número y lo compara con los números almacenados en un fichero de teléfonos bloqueados ya existente, si no lo encuentra entre los números del fichero, lo graba en un fichero de llamadas recibidas, si lo encuentra, le indica al modem que coja la llamada, evitando que nos fría a timbrazos. Luego también podremos leer el fichero de llamadas recibidas y bloquear los números que nos interesen, desde el propio Arduino, usando la botonadura del keypad y los números presentados en el LCD.

La conexiones a realizar entre los diferentes elementos y Arduino, son las siguientes:

- Modulo SD YL-30:

MOSI............11
MISO............12
SCK..............13
CS.................10
3,3v................3.3v
5v...................5v
GND..............GND

- Modulo Serie:
Es un módulo serie sencillo con entradas TX-RX y de voltaje, y una salida DB9 para poder conectarla al modem a través del cable serie.

Modulo:                     Arduino:
RX............................8
TX............................9


- Modulo Keypad shield LCD de 5 botones:
Conectaremos los pines del Keypad shield del modo siguiente:

Keypad:                               Arduino:
5v..........................................5v.
GND.....................................GND
4............................................4
5............................................5
6............................................6
7............................................7
8............................................2
9............................................3
A0.........................................A0

En el caso de usar un LCD 1602 bruto, hay que hacer una pequeña modificación para que sea usable en Arduino. Hay que soldarle una tira de 16 pines macho, y hay que soldar entre sí los pines 1 - 4 y 16 (GND) y entre sí los pines 2 y 15 (+5v.), así como unir los pines 1 y 3 con una resistencia de 3 K Ohm.
La correspondencia con Arduino es la siguiente:
LCD:            Arduino:
 1 ...................GND
 2 ...................5v.
 3.....................Unido a GND por resistencia 3 KOhm
 4....................2
 5....................GND
 6.....................3
 ....                            
11.....................4
12.....................5
13.....................6
14.....................7
15.....................5v.
16 .....................GND

La botonadura DIY se hace de forma sencilla con cuatro botones y tres resistencias de 3 K Ohm. Como en la figura adjunta, pero uniendo un extremo al Pin analógico A0 y el otro a cualquier GND.



El cable serie de conexión entre el modulo serie y el modem se construye de la siguiente manera:

Se necesita un cable con tres polos para las líneas RX y TX, que deben cruzarse entre los dos conectores, y la linea de GND. En cada conector se deben unir entre si las patillas RTS-CTS (7 y 8) y las DTR-DSR (4 y 6). De esta manera el módem creerá que la negociación de datos que realiza es correcta y nos permitirá comunicarnos con el Arduino.



El sketch para hacer funcionar todo es el siguiente:

/* Proyecto de discriminador de llamadas telefonicas no deseadas, tras la llamada se identifica el
numero llamante y se compara con los que están anotados en un fichero almacenado en una tarjeta SD. Si
coincide con alguno, la llamada es bloqueada. Ademas graba automáticamente los números que han llamado
 y que posteriormente pueden seleccionarse con la botonadura para ser bloqueados.
El hardware necesario es un Arduino Uno, un modem con CallerID (en mi caso US Robotics 56k), un modulo
SD YL-30, una tarjeta SD, un modulo Serie RS232, un cable serie modem nulo, un modulo LCD de 16x2 y
una botonadura tipo Keypad de resistencias (una botonadura simple con tres resistencias de 3K en serie
conectando GND a Pin A0, p.e.).
El cable serie modem nulo permite obviar el comportamiento del modem en respuesta a la lineas RTS-CTS,
DTR-DSR, las cuales se unen en los extremos de cada conector, cruzándose las lineas TX-RX y
uniendo las GND entre si.

Conexion pines modulo SD a Arduino:
modulo: GND 3.3v 5v. CS MOSI SCK MISO GND
Arduino:GND 3.3V 5V. 10 11   13  12   GND

Conexion pines modulo LCD 1602 a Arduino:
(X)En modulo unir 1 y 3 con Resitencia 3K.
modulo:  1  2  3  4  5   6 ....11 12 13 14 15 16
Arduino:GND 5v(X) 2 GND  3 ....4  5  6  7  5v GND

*/
//Sample using LiquidCrystal library
#include <LiquidCrystal.h>

#include <SD.h> //CS en pin 10

// select the pins used on the LCD panel
 LiquidCrystal lcd(2, 3, 4, 5, 6, 7);

// define some values used by the panel and buttons
int lcd_key     = 0;
int adc_key_in  = 0;
#define btn3      0
#define btnUP     1
#define btn2      2
#define btnLEFT   3
#define btn1      4
#define btnNONE   5

// Guardamos en que entrada de arduino esta conectado el pin CS del modulo.
//const int chipSelect = 10;
char nmbrstring[ ] = "NMBR";
char numerodete[10];
char numerotst[10];
char numllam[10];

char charBuf[10];
char charBuf2[10];
char charThree[10];

int numblock = 50;//cantidad de lineas de numeros del fichero
int i2 = 0;
int llamadas = 0;// Contador de llamadas, debe ser 0. Otro valor es para pruebas.
File myFile; //fichero llamadas recibidas
String stringThree;

unsigned long tAntes = 0;
unsigned long tAntes2 = 0;
unsigned long tAntes3 = 0;
unsigned long tAntes4 = 0;
unsigned long tAntes5 = 0;
long time_now = 0;

void setup()
{
 lcd.begin(16, 2);              // start LCD library
 pinMode(A0, INPUT_PULLUP); // sets analog pin for input
 lcd.setCursor(0,0);
 lcd.print("LCD Activado"); // print a simple messag
 delay (2000);

  // Configuramos el puerto serie (esta version NO usa Serie Virtual)
  Serial.begin(9600);
  while (!Serial) {
; // Espera para conexion puerto serie, solo necesario para leonardo
}
 

  lcd.clear();
  lcd.setCursor(0,0);
  lcd.print("Setup is Ready");
   
  //Iniciamos la SD
  //Serial.print("Iniciando SD...");
  //Serial.print("\n");
  lcd.clear();
  lcd.setCursor(0,0);
  lcd.print("Iniciando SD...");
  // El pin CS por defecto de la placa arduino debe ser configurado como salida
  // aunque no se use (10 en la mayoria de las placas, 53 en Arduino Mega).
  pinMode(10, OUTPUT);
  // Si ha habido error al leer la tarjeta informamos por el puerto serie.
  if (!SD.begin(10)) {
  //Serial.println("Error inicializar SD!");
  lcd.setCursor(0,1);
  lcd.print("Error ini.SD!");
  delay(2000);

  lcd.clear();
  lcd.setCursor(0,0);
  lcd.print("PULSE RESET");
  pinMode(10, INPUT);
  delay(1000);

  return;
 }


 lcd.setCursor(0,1);
 lcd.print("SD ini...OK");
 delay(2000);

//REcreando fichero RECIB.TXT

File Archivo1;
Archivo1 = SD.open("RECIB.TXT", FILE_WRITE);

if (Archivo1) {
Archivo1.println("111111111");

Archivo1.close();
lcd.clear();
lcd.setCursor(0,0);
lcd.print("RECIB.TXT ini");
delay(2000);
lcd.setCursor(0,1);
lcd.print("OK");
delay(2000);
} else {
lcd.setCursor(0,1);
lcd.print("Error SD write");
delay(2000);
}

 
  lcd.clear();
  lcd.setCursor(0,0);
  lcd.print("Start Modem....");

  // Iniciamos los comandos Hayes para configurar el modem
  Serial.print("ATZ\r\n");
  delay(4000);
  lcd.setCursor(0,1);
  lcd.print("4.");
  //mySerial.write("AT+VCID=1\r\n");//Conceptronics CID ON
  Serial.print("AT#CID=1\r\n");//USR Robotics CID ON
  delay(2000);
  lcd.setCursor(2,1);
  lcd.print("3.");
  Serial.print("ATS7=5\r\n");//USR Robotics Answer Wait 7 seg
  delay(2000);
  lcd.setCursor(4,1);
  lcd.print("2.");
  Serial.print("ATS0=0\r\n");//USR Robotics Auto Answer OFF
  delay(2000);
  lcd.setCursor(6,1);
  lcd.print("OK");

  delay(2000);
 }



void loop()
{
  static char buffer[20];

    buttonselect();// Selector botones

  if (readline(Serial.read(), buffer, 80) > 0)
  {
    char* numero1 = strchr(buffer, 'NMBR =');
    char* prefinmbr = strtok(buffer, " =");
    char* numero2 = strchr(numero1, ' ');
 
     
    if(strcmp(prefinmbr, nmbrstring) == 0)//Detectamos la cadena NMBR que contiene el numero llamante
    {
     //Lector de numero
     char* numerodete = numero2;//detecta numero con 1 espacio al inicio
     String stringOne = numerodete;//convierte numero char a string
     stringOne.trim();//elimina el espacio del inicio del número
   
       
     lcd.clear();
     lcd.setCursor(0,0);
     lcd.print("Num.llamante:");
     lcd.setCursor(0,1);
     lcd.print(stringOne);
   
   
     stringOne.toCharArray(charBuf, 10);//convierte el numero string a char para strcmp
   
     lcd.setCursor(0,1);
     lcd.print(charBuf);
   
     //Inicio codigo escritura llamadas recibidas  
     // open the file. note that only one file can be open at a time,
  // so you have to close this one before opening another.

  myFile = SD.open("RECIB.TXT", FILE_WRITE);
  // if the file opened okay, write to it:
  if (myFile) {
 
    myFile.println(stringOne);
    myFile.close();// close the file:
 
    llamadas++;// acumulado de numero de llamadas
    lcd.setCursor(0,0);
    lcd.print("Llamadas.....");
    lcd.setCursor(13,0);
    lcd.print(llamadas);
 
    lcd.setCursor(12,1);
    lcd.print("WCOK");
 
    //Mantiene el tiempo a 0 mientras se pulsa boton lectura
    long time_now = millis();
    tAntes = time_now;
    tAntes2 = time_now;
    tAntes3 = time_now;
    tAntes4 = time_now;
 
 
  } else {
    // if the file didn't open, print an error:
    //Serial.println("error opening file");
  lcd.setCursor(0,1);
  lcd.print("error SD write");
}

  // fin codigo escritura llamadas recibidas
   
    //Inicio seccion lectura fichero numeros bloqueados.
 
    char Ruta[9] = {'B', 'L', 'O', 'Q', '.', 'T', 'X', 'T', '\0'};//establece nombre fichero tlfs bloqueados
   
    for(int i = 0; i < numblock; i++)// secuencialmente se lee cada una de los numeros del fichero
    {
    //Leemos linea de archivo en la SD
    String numerotst = ReadFile(i,Ruta);
    String stringTwo = numerotst;
    stringTwo.toCharArray(charBuf2, 10);// convertimos string to char para strcmp
 
    if(strcmp(charBuf, charBuf2) == 0)// comparamos el numero detectado con el leido en el fichero
    {
   
      lcd.clear();
      lcd.setCursor(0,0);
      lcd.print("Numero detec.:");
      lcd.setCursor(0,1);
      lcd.print(charBuf); // Muestra el numero bloqueado
   
     //Codigo contestar y colgar
     Serial.print("ATA\r\n");//Coge el tlf
     delay(2000);
     Serial.print("ATH1\r\n");//Cuelga el tlf
   
       delay (4000);
     
     }// Fin deteccion numero bloqueado
   }//Fin bucle lectura numeros bloqueados SD
  }//Fin deteccion de llamada NMBR
 }//Fin lectura buffer serie
}



// Codigo de lectura de los telefonos bloqueados en la SD
String ReadFile(int Linea,char Ruta[]){
int Lin=0;
String Resultado;
File myFile;
byte Bin;
myFile = SD.open(Ruta);;
if (myFile) {
while (myFile.available()) {
Bin=myFile.read();
if (Bin==13){Lin++;myFile.read();}
else
{
if (Lin==Linea){Resultado=Resultado+(char(Bin));}
if (Lin>Linea){myFile.close();return Resultado;}
}
}
myFile.close();return Resultado;
}
}

 
/// AÑADIDO CONTROL BOTONADURA Y GRABADO NUMEROS BLOQUEADOS SD

void buttonselect()
{
 lcd.setCursor(0,1);            // move to the begining of the second line
 lcd_key = read_LCD_buttons();  // read the buttons

 switch (lcd_key)               // depending on which button was pushed, we perform an action
 {
   case btn3:
     {
     lcd.clear();
     lcd.setCursor(0,1);
     //lcd.print(adc_key_in);
     lcd.print("RIGHT ");
     delay(500);
     break;
     }
   case btn2:
     {
     /*lcd.clear();
     lcd.setCursor(0,1);
     lcd.print(adc_key_in);
     lcd.print("DOWN ");*/
     leer();
     break;
     }
   case btn1:
     {
     //lcd.setCursor(7,0);
     //lcd.print("Bloquear");
     bloquear();
     /*lcd.clear();
     lcd.setCursor(0,1);
     lcd.print(adc_key_in);
     lcd.print("SELECT");*/
     break;
     }
     case btnNONE:
     {
      long time_now = millis();
   
    if ((long)(time_now - tAntes) > 10000) // regulador de tiempo medida datos y presentacion lcd
    {
     lcd.clear();
     lcd.setCursor(0,0);
     lcd.print("Waiting call...");
     lcd.setCursor(0,1);
     lcd.print("Llamadas.....");
     lcd.setCursor(13,1);
     lcd.print(llamadas);
     tAntes = time_now;
    }
     if ((long)(time_now - tAntes2) > 1000)
    {
    lcd.setCursor(15,0);
    lcd.print("*");
    tAntes2 = time_now;
    }
    if ((long)(time_now - tAntes3) > 1500)
    {
    lcd.setCursor(15,0);
    lcd.print(" ");
    tAntes3 = time_now;
    }
    //Reponedor a 0 de lectura llamadas si inactividad en botones
    if ((long)(time_now - tAntes4) > 20000)
    {
      i2 = 0;
      tAntes4 = time_now;
    }
    if ((long)(time_now - tAntes5) > 300000) //RESETEO MODEM CADA 5 MINUTOS
    {
      modemreset();
      tAntes5 = time_now;
    }
 
      break;
     }
 }

}


void leer()
{
    char Ruta[10] = {'R', 'E', 'C', 'I', 'B', '.', 'T', 'X', 'T', '\0'};//establece nombre fichero tlfs bloqueados
     
    String numllam = ReadFile(i2,Ruta);//Leemos linea de archivo como char en la SD
 
    //Proceso conversion de string to char para poder grabar los datos leidos
    String stringThree = numllam; //convertir char to string
    stringThree.toCharArray(charThree, 10);// convertimos string to char
 
    delay(500);
    lcd.clear();
    lcd.setCursor(0,0);
    lcd.print("Numero Leido.....");
    lcd.setCursor(15,0);
    lcd.print((llamadas + 5)- i2);
    //lcd.setCursor(0,1);
    //lcd.print("         "); // Muestra el numero leido
    lcd.setCursor(0,1);
    lcd.print(charThree); // Muestra el numero leido
    lcd.setCursor(12,1);
    lcd.print(".."); // Muestra el numero leido
    delay (500);
 
    i2++;
 
    //Mantiene el tiempo a 0 mientras se pulsa boton lectura
    long time_now = millis();
    tAntes = time_now;
    tAntes2 = time_now;
    tAntes3 = time_now;
    tAntes4 = time_now;
 
    if (i2 > (llamadas + 5))
    {
    i2 = 0;
    llamadas = 0;
    delay (1000);
    lcd.clear();
    lcd.setCursor(0,0);
    lcd.print("Borrando las");
    lcd.setCursor(0,1);
    lcd.print("LLamadas...");
 
    myFile.close();
    delay(2000);
 
    //Borrando fichero RECIB.TXT
    SD.remove("RECIB.TXT");
    delay(2000);
 
    myFile.close();
 
    //REcreando fichero RECIB.TXT

File Archivo1;
Archivo1 = SD.open("RECIB.TXT", FILE_WRITE);

if (Archivo1) {
Archivo1.println("111111111");
delay(200);
Archivo1.close();
lcd.clear();
lcd.setCursor(0,0);
lcd.print("RECIB.TXT ini");
delay(2000);
lcd.setCursor(0,1);
lcd.print("OK");
delay(2000);
} else {
lcd.setCursor(0,1);
lcd.print("Error SD write");
delay(2000);
lcd.clear();
lcd.setCursor(0,1);
lcd.print("PULSE RESET");
delay(2000);
}
    // fin recreo

    }
 }
//funcion para escribir el número leido de RECIB.TXT y grabarlo en
//el fichero de numeros bloqueados BLOQ.TXT
void bloquear()
{
  myFile = SD.open("BLOQ.TXT", FILE_WRITE);
  if (myFile){
  myFile.println(charThree);
  delay(200);
  myFile.close();
  }  else  {
  lcd.setCursor(0,1);
  lcd.print("error SD write");
  }
  lcd.clear();
  lcd.setCursor(0,0);
  lcd.print("Numero bloqueado");
  lcd.setCursor(0,1);
  lcd.print("         "); // Limpia espacio numero
  lcd.setCursor(0,1);
  lcd.print(charThree); // Muestra el numero bloqueado
  lcd.setCursor(12,1);
  lcd.print("BQ");
  //Mantiene el tiempo a 0 mientras se pulsa boton lectura
    long time_now = millis();
    tAntes = time_now;
    tAntes2 = time_now;
    tAntes3 = time_now;
    tAntes4 = time_now;
    delay(2000);
}

//Sistema de lectura del puerto serie
int readline(int readch, char *buffer, int len)
{
  static int pos = 0;
  int rpos;

  if (readch > 0) {
    switch (readch) {
      case '\n': // Ignore new-lines
        break;
      case '\r': // Return on CR
        rpos = pos;
        pos = 0;  // Reset position index ready for next time
        return rpos;
      default:
        if (pos < len-1) {
          buffer[pos++] = readch;
          buffer[pos] = 0;
        }
    }
  }
  // No end of line has been found, so return -1.
  return -1;
}
// fin programa lectura puerto serie

// read the buttons
int read_LCD_buttons()
{
 adc_key_in = analogRead(0);      // read the value from the sensor

//Valores para botonadura simple de 3 resistencias de 3k Ohm en serie

 if (adc_key_in > 1000) return btnNONE; // Ningun boton pulsado

 if (adc_key_in < 95 && adc_key_in > 90)   return btn3;
 //if (adc_key_in < 250)  return btnUP;
 if (adc_key_in < 200 && adc_key_in > 100)  return btn2;
 //if (adc_key_in < 650)  return btnLEFT;
 if (adc_key_in > 200 && adc_key_in < 300)  return btn1;

 return btnNONE;  // when all others fail, return this...
}
// FIN READ BUTTONS

// Reseteo modem
void modemreset()
{
  lcd.clear();
  lcd.setCursor(0,0);
  lcd.print("Reset Modem....");

  // Iniciamos los comandos Hayes para configurar el modem
  Serial.print("ATZ\r\n");
  delay(4000);
  lcd.setCursor(0,1);
  lcd.print("4.");
  //mySerial.write("AT+VCID=1\r\n");//Conceptronics CID ON
  Serial.print("AT#CID=1\r\n");//USR Robotics CID ON
  delay(2000);
  lcd.setCursor(2,1);
  lcd.print("3.");
  Serial.print("ATS7=5\r\n");//USR Robotics Answer Wait 7 seg
  delay(2000);
  lcd.setCursor(4,1);
  lcd.print("2.");
  Serial.print("ATS0=0\r\n");//USR Robotics Auto Answer OFF
  delay(2000);
  lcd.setCursor(6,1);
  lcd.print("OK");

  delay(2000);
}