viernes, 2 de septiembre de 2016

Bloqueador de llamadas usando un modulo Caller ID

Para mejorar el bloqueador de llamadas y hacerlo más compacto, a la vez que más sencillo de construir, me he decidido a comprar un módulo identificador de llamadas llamado Ferduino Caller ID, que puedes encontrar en Ebay. Se vende como un shield para aarduino con los componentes para montar, pero yo lo he montado para poderlo conectar con algunas líneas a mi shield bloqueador de llamadas con pantalla LCD que ya tenía diseñado. El Ferduino Caller ID lo teneis descrito aqui:

http://www.ferduino.es/callerid/#funcionamiento

En resumen puede proporcionar a arduino a través de una señal serie la información CallerID y a través de un pin la señal de RING.
La señal de CallerID de 1200 baud es procesada como en los demás proyectos del bloqueador de llamadas que he hecho, con ligeras diferencias para el formateo de los datos recibidos.

El código final 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. Graba automáticamente los números que han llamado
en un fichero en la tarjeta SD, que posteriormente pueden seleccionarse con la botonadura para ser bloqueados.
Se necesitan dos RING para poder leer el CALLERID. Lleva un buzzer incorporado que suena a partir del tercer RING,
para evitar que suenen los telefonos siempre un par de veces (hacer mute en los telefonos para evitar el ring).
LLeva un rele que activa o desactiva la linea telefonica saliente a solicitud del programa (para poder asi evitar
los 2 RING necesarios antes de la deteccion de callerID y bloquear la llamada entrante). Pero no es conveniente
usar este metodo de bloqueo, ya que no detecta el estado HOLD o llamada atendida, o en curso, con lo cual corta
la linea tras un periodo corto de tiempo y corta la llamada en curso o la atendida. Para evitar esto hay
que implementar un detector de HOLD y conectarlo a un PIN de arduino y generar el codigo de monitorización del
estado de dicho pin.
El hardware necesario es un Arduino Uno, un escudo Ferduino CaallerID, una tarjeta SD, un modulo LCD de 16x2,
una botonadura tipo Keypad DIY de resistencias (una botonadura simple con tres resistencias de 3K en serie
conectando GND a Pin A0, p.e.) y un rele. Todo el conjunto va metido en una caja.

El proyecto esta en : http://http://aestradadiy.blogspot.com.es/
Agradecimientos a callerid@ferduino.es por el hardware CallerID y el software de lectura serie.

La cadena del Caller ID detectada por el modem es siempre del tipo siguiente:
DATE = 0422 //MMDD
TIME = 1945 //HHMM
NMBR = 659278358 //Numero llamante

Conexion modulo Ferduino CallerID a Arduino es :
Pin 1 Ferduino RX a RX Arduino
Alimentación es por 5V. desde Arduino.
Pin 3 Ferduino a pin A3 de Arduino
http://www.ferduino.es/callerid/#funcionamiento

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

La botonadura es del tipo resistencias en serie unidas a pin A0 y GND, y boton uniendo PIN RESET a GND.

El buzzer está conectado a: Pin A1 - GND - 5v
El rele esta conectado a pin A2.
El pin A3 RING del ferduino callerID esta conectado a pin A3.


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

#include <SdFat.h>//CS en pin 10
SdFat sd;
SdFile myFile;

// 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

String Version = "V.1.12.04";//Version actual del sketch

String CadenaSerial = "";//Crea la cadena de caracteres vacia que va a ser leida por puerto serie
String reportString = ""; //Crea la cadena de caracteres vacia comparadora
unsigned long SerialMillis = millis();
unsigned long RingMillis = millis();
unsigned long CounterMillis = millis();
boolean SerialFin = false; //bandera de control de lectura del puerto serie
boolean SerialOn = false; //Bandera de activado del puerto serie
boolean ReleOn = false;//byte de control del rele (por defecto apagado)
boolean BloqueoOn = false;//byte condicion de bloqueo de llamadas

String NumeroTelefono;
String stringOne;
int ringer = 0;
long ringerfin = 1;
int counter = 0;


// Guardamos en que entrada de arduino esta conectado el pin CS del modulo.
const int chipSelect = 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 i = 0;
int i2 = 0;
int llamadas = 0;// Contador de llamadas, debe ser 0. Otro valor es para pruebas.
int llamadasb = 0;// acumulador temporal de llamadas
int pin = A1;//pin conexion buzzer
int rele = A2;// pin conexion rele
int pulsado =0;//valor control pulsado boton para evitar sucesos
int comparador = 0; //Bandera de cumplimiento de ser un valor numerico
int a = 0;// Contador de digitos de un numero normal (9)
String stringThree;

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

//Sonido del buzzer
void playNote(int noteInt, long length, long breath = 20) {
  length = length - breath;
  buzz(pin, 2000 , length); // Valor 2000 es agudo y fuerte, 50 es grave y flojo
  if(breath > 0) { //take a short pause or 'breath' if specified
    delay(breath);
  }
}



void setup()
{
 lcd.begin(16, 2);              // start the library
 pinMode(A0, INPUT_PULLUP); // sets analog pin for input
 pinMode(rele, OUTPUT); // set analog pin for output
 digitalWrite(rele, HIGH);// Activa rele para que NO suene llamada
 //digitalWrite(pin, HIGH);//Produce ruido de fondo en waiting call
 //pinMode(pin, OUTPUT); // set a pin for buzzer output

 lcd.setCursor(0,0);
 lcd.print("LCD Activado"); // print a simple messag
 lcd.setCursor(0,1);
 lcd.print(Version);
 delay (2000);

  // Configuramos el puerto serie (esta version NO usa Serie Virtual)
  Serial.begin(1200);// Se configura a 1200 baud
  while (!Serial) {
; // Espera para conexion puerto serie, solo necesario para leonardo
}
   
  lcd.clear();
  lcd.setCursor(0,0);
  lcd.print("Setup is Ready");
  delay(2000);
 
  //Iniciamos la SD
  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(chipSelect, SPI_HALF_SPEED))
{
lcd.setCursor(0,1);
lcd.print("Error SD...");
sd.initErrorHalt();
 
  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

if (!myFile.open("RECIB.TXT", O_RDWR)) {
    lcd.setCursor(0,1);
    lcd.print("error SD write");
    delay(2000);
    }
    myFile.println("111111111");
    delay(200);
    myFile.close();// close the file:
   
lcd.clear();
lcd.setCursor(0,0);
lcd.print("RECIB.TXT ini");
delay(2000);
lcd.setCursor(0,1);
lcd.print("OK");
delay(2000);

// Beep de OK de fin de Setup
  digitalWrite(pin, HIGH);
  pinMode(pin, OUTPUT); // set a pin for buzzer output
  delay(2000);
  playNote(1,100);
  digitalWrite(pin, HIGH);
  //digitalWrite(pin, LOW);
}
// Fin Setup

void loop()
{
 
  buttonselect();// Selector botones
 
  a = 0; //Restablece contador de caracteres del numero llamante
  comparador = 0; // Restablece a 0 el comparador
  reportString = "";// Restablece a 0 el numero comparado
   
  // Inicio lectura puerto serie y deteccion numero llamante
  char ch;
 
  if(SerialFin == true)
 {
    SerialFin = false;
    SerialOn = false;
                   
    String NumeroTelefono = CadenaSerial.substring(14,23);//Lee el número llamante de la cadena leida
   
    CadenaSerial = ""; //Restablecemos a 0 la cadena creada al leer el puerto serie
 
    reportString = NumeroTelefono;
     
    //Compara cada uno de los 9 digitos (un numero normal)
    while (a <= 9){
    a++;//Añade otro caracter al comparador
    if (isDigit(reportString.charAt(a))){ //detecta si cada caracter del numero es un valor numerico
    comparador++;
    }
    else{
        comparador = 0;//Caracter no numerico encontrado
        break;
        }
    if (comparador = 9){//se han leido los 9 caracteres y eran numeros
      break;
       }
    } //Fin del while
   
    if (ringer >> 0 && comparador >> 0)//Se activa si se recibe una llamada y es un numero valido
    {
    lcd.clear();
    lcd.setCursor(0,0);
    lcd.print("Num.llamante:");
       
    stringOne = NumeroTelefono;//convierte numero char a string
    stringOne.toCharArray(charBuf, 10);//convierte el numero string a char para strcmp
   
     lcd.setCursor(0,1);
     lcd.print(charBuf);// Presenta en pantalla el numero llamante
   
   
     //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.
 
   // if the file opened okay, write to it:
  if (!myFile.open("RECIB.TXT", O_RDWR | O_CREAT | O_AT_END)) {
    lcd.setCursor(0,1);
    lcd.print("Error SD write");
   }
   
    myFile.println(stringOne);// escribe el numero en el fichero
    delay (200);
    myFile.close();// close the file:
   
    lcd.setCursor(12,1);
    lcd.print("WCOK");// Indica en el display que la llamada ha sido grabada en el fichero de Recibidas
   
    BloqueoOn = false;//Byte Activando el desbloqueo de la llamada grabada
       
    //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;
   
    // Calcula el acumulado de numero de llamadas
    llamadasb = llamadas;
    llamadas++;
    lcd.setCursor(0,1);
    //lcd.print("Llamadas.....");
    //lcd.setCursor(13,1);
    //lcd.print(llamadas);
   
   
 
  // 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 BLOCK:");
      lcd.setCursor(0,1);
      lcd.print(charBuf); // Muestra el numero bloqueado
     
      // Desativador del Rele
      //digitalWrite(rele, LOW);
      ReleOn = false;// BYTE DE DESACTIVACION DE RELE
      BloqueoOn = true;// BYTE DE ACTIVACION DE BLOQUEO  
    }// Fin deteccion numero bloqueado
 
   }//Fin bucle lectura numeros bloqueados SD
 
    //ReleOn = true;// BYTE DE ACTIVACION DE RELE
     
   }//Fin lectura buffer serie
 
 
     
  } // Fin deteccion numero llamante valido
 
         
  //  lectura puerto serie
  if(Serial.available())
  {
    SerialOn = true;
    ch = Serial.read();
    //Serial.print(ch);//comprobador datos serie    
    CadenaSerial += ch;//crea la cadena de caracteres leidos por el puerto serie
    SerialMillis = millis();
  }
 
  if(SerialOn == true && (millis() - SerialMillis) > 10)//Temporizador para recoger el numero llamante
    {
      SerialFin = true;
      SerialOn = false;
     }
   
   
   
  // Fin lectura y deteccion de numeros
 
  // Sistema de deteccion y conteo de rings por sensor de voltaje en pin ferduino
 
 /* testeo de rings por lcd
    lcd.setCursor(9,0);
    lcd.print(ringer);
    lcd.setCursor(10,0);
    lcd.print(counter);
  */
 
    int voltaje = analogRead(A3);//Pin lectura de voltaje para deteccion de rings
    //int ring = voltaje * (5.0 / 1023.0);//Conversion de medicion de voltaje a valor comparable
    int ring = voltaje;
   
    /* //Muestra el valor de voltaje
    lcd.setCursor(0,0);
    lcd.print("Volt");
    lcd.setCursor(6,0);
    lcd.print(ring);
    */
   
   //contador de rings detectados, 1 ring cada 2 segundos
    if ( ring == 0 && (millis() - RingMillis) > 2000 )
     {
     //delay(3000);
     ringer++;
     RingMillis = millis();
     CounterMillis = millis();
     //lcd.setCursor(12,0);
     //lcd.print(ringer);
   
     if (BloqueoOn == false){
      //Buzzer hace sonar un ring
       digitalWrite(pin, HIGH);
        playNote(1,100);
        playNote(1,200);
        playNote(1,100);
        playNote(1,200);
        playNote(1,100);
        playNote(1,200);
        playNote(1,100);
        playNote(1,200);
        playNote(1,100);
        playNote(1,200);
        playNote(1,100);
        playNote(1,200);
        digitalWrite(pin, HIGH);
        //digitalWrite(pin, LOW);
      }
    }
   
   
     //contador medidor de cadencia teorica de  1 rings cada 2.2 segundos para detectar fin de rings
    if (ringer > 0 && ( millis() - CounterMillis) > 2250 ){
      counter++;
      CounterMillis = millis();
      ringerfin = 0;
      //lcd.setCursor(15,0);
      //lcd.print(counter);
    }
           
    //Comprobador de activado de rings    
     if (ringer > 1 && BloqueoOn == false)
     {
      ReleOn = true;//Activa el rele y permite que suene la llamada
     }
                 
     // comprobador de final de rings (teoricos menos recibidos)
     if(counter > 0 && (counter - ringer) > 1){
     ringer = 0;
     counter = 0;
     BloqueoOn = true;
     RingMillis = millis();
     CounterMillis = millis();
     ringerfin = 1;
     }
       
    //comprobador para desactivado de rings  
    if (BloqueoOn == true)
    {
      ReleOn = false;//Mantiene el rele desactivado por defecto
    }
 
   if(ReleOn == true)//Activador del rele
    {
      digitalWrite(rele, LOW);// Activa rele y permite suene llamada
    }
  if(ReleOn == false)//Desactivador del rele
    {
    digitalWrite(rele, HIGH);// Desactiva rele e impide suene llamada
    }

   
}// Fin LOOP



// Codigo de lectura de los ficheros de telefonos de la SD
String ReadFile(int Linea,char Ruta[]){
int Lin=0;
String Resultado;
byte Bin;
if (!myFile.open(Ruta, O_RDWR)) {
    lcd.setCursor(0,1);
    lcd.print("Read Error");
    delay (2000);
    myFile.close();return Resultado;
    }
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;
}

 
/// MENU 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,0);  
     lcd.print("Numero");
     lcd.setCursor(0,1);  
     lcd.print("Bloqueado");
     bloquear();
     pulsado = 1;
     delay(500);
     break;
     }
   case btn2:
     {
     //lcd.print(adc_key_in);
     lcd.clear();
     lcd.setCursor(0,0);  
     lcd.print("Bloquear Num?");
     lcd.setCursor(0,1);
     lcd.print("NO");
     lcd.setCursor(7,1);
     lcd.print("SI");
     pulsado = 1;
     delay(500);
     break;
     }
   case btn1:
     {
   
     leer();
     pulsado = 1;
     break;
     }
     case btnNONE:
     {
      long time_now = millis();
   
    if ((ringerfin == 1 && (long)(time_now - tAntes) > 10000)) // regulador de tiempo 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;
     pulsado = 0;
     }
   
    if ((pulsado == 0 && ringerfin == 1 && (long)(time_now - tAntes2) > 3000))//Blink de * para indicar funcionamiento normal
    {
    lcd.setCursor(13,1);
    lcd.print(llamadas);
    tAntes2 = time_now;
    }
    if ((pulsado == 0 && ringerfin == 1 && (long)(time_now - tAntes3) > 3500))
    {
    lcd.setCursor(13,1);
    lcd.print("  ");
    tAntes3 = time_now;
    }
   
    //Reponedor a 0 de lectura llamadas si inactividad en botones
    if ((long)((time_now - tAntes4) > 10000))
    {
      i2 = 0;
      tAntes4 = time_now;
    }
 
      break;
     }
 }

}

// Funcion de lectura de numeros en fichero de llamadas recibidas
void leer()
{  
    char Ruta[10] = {'R', 'E', 'C', 'I', 'B', '.', 'T', 'X', 'T', '\0'};//establece nombre fichero num tlfs recibidos
   
    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(14,0);
    lcd.print((llamadas + 5)- i2);//Se ponen 5 números mas a leer para evitar no poder leer tras cortes de luz
   
    lcd.setCursor(0,1);
    lcd.print(charThree); // Muestra el numero leido
    //lcd.setCursor(12,1);
    //lcd.print("..");
    //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))
    {
      lcd.setCursor(0,1);
      lcd.print("Borra Recib.txt"); // Advierte borrado Recib.txt
    }
   
    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...");
   
    if (!myFile.open("RECIB.TXT", O_RDWR | O_CREAT)) {
    lcd.setCursor(0,1);
    lcd.print("error SD write");
    delay(2000);
    lcd.clear();
    lcd.setCursor(0,1);
    lcd.print("PULSE RESET");
    delay(2000);
    }
   
    //Borrando fichero RECIB.TXT
    myFile.remove();//borra fichero
    delay(2000);
   
    myFile.close();
   
    //REcreando fichero RECIB.TXT

if (!myFile.open("RECIB.TXT", O_RDWR | O_CREAT)) {
    lcd.setCursor(0,1);
    lcd.print("error SD write");
    delay(2000);
    lcd.clear();
    lcd.setCursor(0,1);
    lcd.print("PULSE RESET");
    delay(2000);
    }
   
    myFile.println("111111111");
    delay(200);  
    myFile.close();// close the file:
   
    lcd.clear();
    lcd.setCursor(0,0);
    lcd.print("RECIB.TXT ini");
    delay(2000);
    lcd.setCursor(0,1);
    lcd.print("OK");
    delay(2000);
   }
}

//funcion para escribir el número leido de RECIB.TXT y grabarlo en
//el fichero de numeros bloqueados BLOQ.TXT
void bloquear()
{
  if (!myFile.open("BLOQ.TXT", O_RDWR | O_CREAT | O_AT_END)){
  lcd.setCursor(0,1);
  lcd.print("Error SD write");
 
  sd.initErrorHalt();
 
  delay(2000);
  lcd.clear();
  lcd.setCursor(0,0);
  lcd.print("PULSE RESET");
  pinMode(10, INPUT);
  delay(1000);
  }
  myFile.println(charThree);
  delay(200);
  myFile.close();
 
  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");
  delay(2000);
}

// 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 < 175 && adc_key_in > 150)  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

// sonido del buzzer

void buzz(int targetPin, long frequency, long length) {
  long delayValue = 1000000/frequency/2; // calculate the delay value between transitions
  //// 1 second's worth of microseconds, divided by the frequency, then split in half since
  //// there are two phases to each cycle
  long numCycles = frequency * length/ 1000; // calculate the number of cycles for proper timing
  //// multiply frequency, which is really cycles per second, by the number of seconds to
  //// get the total number of cycles to produce
  for (long i=0; i < numCycles; i++){ // for the calculated length of time...
    digitalWrite(targetPin,HIGH); // write the buzzer pin high to push out the diaphram
    delayMicroseconds(delayValue); // wait for the calculated delay value
    digitalWrite(targetPin,LOW); // write the buzzer pin low to pull back the diaphram
    delayMicroseconds(delayValue); // wait againf or the calculated delay value
  }
}