Las palomas si bien son aves simpáticas pueden dejar de serlo cuando deciden de manera insistente construir su nido en el compresor del sistema de aire acondicionado.
Esto ademas de ser molesto por la cantidad de basura que generan al construir su nido resulta peligroso para las propias palomas y para el equipo de aire acondicionado .
Como la idea es tratar de convencer a las palomas de que se busquen otro lugar sin hacerles daño se me ocurrió construir este pequeño dispositivo usando el sensor ultrasónico HC-SR04 un Arduino Nano y algunos componentes extras como un potenciómetro de 10K, dos display cátodo común y dos transistores BC337.
El objetivo es usar el sensor para detectar palomas en donde no deben estar y activar una señal sonora que las espante cada vez que se acerquen a la zona de cobertura. El sistema cuenta con un potenciómetro que ajusta el rango de alcance donde se debe activar el sistema "espanta palomas", este rango se visualiza en dos dígitos.
El rango se puede cambiar actuando sobre el botón de programación y moviendo el potenciómetro, el nuevo valor se almacena en memoria EEPROM por lo tanto el sistema siempre "recuerda" el valor fijado aunque su alimentación se pierda.
El siguiente es el código completo del trabajo propuesto.
/****************************************************************************** ** Detector de proximidad por ulatrasonido con Arduino y sensor SRF-04. ** El ejemplo incluye un ajuste de rango que va desde 1 cmts. a 50 cmts. ** mediante un potenciómetro y la posibilidad de ver el rango en dos dígitos ** con multiplexor de dos transistores BC337 y un decodificador BCD CD4511. ** ** Target: Arduino nano ** Ide para Arduino v.1.8.19 con Linux Debian. ** ********************************************************************************/ /* NOTA: Para la conexion de los segmentos del display (cátodo común) al CD4511 * ver la hoja de datos del BCD. ********************************************************************************/ #include <EEPROM.h> #define prog 4 // Boton para programar nuevo rango #define led 15 // Pin para controlar la sirena, led o un relevador. #define A 8 // Pin A entrada BCD del CD4511 (pin 7) #define B 9 // Pin B entrada BCD del CD4511 (PIN 1) #define C 10 // Pin C entrada BCD del CD4511 (Pin 2) #define D 11 // Pin D entrada BCD del CD4511 (Pin 6) #define T1 2 // Pin control de la base del transistor BC337 (Unidad) #define T2 3 // Pin control de la base del transistor BC337 (Decena) const unsigned char EchoPin = 5; // Pin para el ultrasonido const unsigned char TriggerPin = 6; // Pin para el ultrasonido unsigned int conversion =0; unsigned char muestras =0, rango = 0, unidad = 0, decena = 0, marca = 1; unsigned int M0=0; void setup() { pinMode(led, OUTPUT); // Para controlar un led, sirena o un relay pinMode(A, OUTPUT); // Pines para pasar al BCD los valores binarios pinMode(B, OUTPUT); pinMode(C, OUTPUT); pinMode(D, OUTPUT); pinMode(T1, OUTPUT); // Pin para la base de transistor unidad pinMode(T2, OUTPUT); // pin para la base del transistor decena pinMode(prog,INPUT_PULLUP); // Botón para programar el rango digitalWrite(led,LOW ); Serial.begin(9600); pinMode(TriggerPin, OUTPUT); pinMode(EchoPin, INPUT); if(EEPROM.read(0) > 50){ // Recupera de memoria el último rango válido rango = 30; // Si no es válido asigna uno por defecto. } else rango = EEPROM.read(0); // Recupera el rango guardado en memoria decena = (rango / 10) % 10; // y ajusta la unidad y decena para ver unidad = rango % 10; // el dato del rango en el display. //---------- Congigura el Timer 1 ---------------- noInterrupts(); // No interupciones TCNT1 = 65000; // Ajusta modulo a ~6 Milisegundos TCCR1B |= (1 << CS10); // Sin prescalador TIMSK1 |= (1 << TOIE1); // Habilita la mascara de interrupción interrupts(); // Habilita las interrupciones generales } void loop() { rango = EEPROM.read(0); if (digitalRead(prog) == LOW){ // Boton de programa activado? Leer_Conversor(); // OK, entonces leer el conversor. EEPROM.write(0, conversion); // Almacena el nuevo rango delay(500); } int dist = ping(TriggerPin, EchoPin); if(dist <= rango) // Distancia es menor al rango?? digitalWrite(led,HIGH ); // Sirena o led activado if(dist >= rango || dist == 0) // Distancia > 31 o 0 ?? digitalWrite(led,LOW ); // Entonces apagar sirena!!! Serial.println(dist); delay(500); } /**************************************************************** * Función para medir la distancia a que se encuentra el intruso *****************************************************************/ int ping(int TriggerPin, int EchoPin) { long tiempo, distancia; digitalWrite(TriggerPin, LOW); // Genera un pulso limpio de 4us delayMicroseconds(4); digitalWrite(TriggerPin, HIGH); // Genera disparo de 10us delayMicroseconds(10); digitalWrite(TriggerPin, LOW); tiempo = pulseIn(EchoPin, HIGH); // Mide el tiempo entre pulsos distancia = tiempo * 10 / 292/ 2; // Convierte la distancia, en cm return distancia; } /**************************************************************** * Lee el canal A0 donde se ha colocado un potenciómetro de 10K * para leer un voltaje entre 0 y 5V que se escalara a un valor * entre 0 y 50 para ajustar nuevos rangos. * *****************************************************************/ void Leer_Conversor(void){ do{ M0 += analogRead(A0); // Lee el A/D y acumula el dato en M0 muestras++; // Incrementa el contador de muestras }while(muestras <=49); // Se tomaron muestras ?? conversion = M0/50; // Se busca el promedio de las 50 muestras conversion = map(conversion, 0, 1023, 0, 50); decena = (conversion / 10) % 10; unidad = conversion % 10; M0 =0; muestras =0; } /************************************************************************ Muestra el rango ajustado o extraído de la memoria **************************************************************************/ void Multiplexador(){ switch(marca){ // El valor de marca determina que transistor se activa. case 1:{ PORTB = unidad; // Pasa al BCD CD4511 el valor de la Unidad digitalWrite(T1, HIGH); // Activar el display de la unidad break; } case 2:{ digitalWrite(T1, LOW); // Apagar el display de la unidad PORTB = decena; // Pasa al BCD CD4511 el valor de la Decena if(decena > 0) // Si la decena es 0 apagar el dígito digitalWrite(T2, HIGH); // Activar el display de la decena break; } case 3:{ digitalWrite(T2, LOW); // Apagar el display de la decena marca =0; break; } } } /* ******** ISR del Timer 1 por desborde ************** * Determina la velocidad a la cual se mostrará el * valor de la unidad y la decena. ******************************************************/ ISR(TIMER1_OVF_vect){ TCNT1 = 65000; // 5,7 Milisegundos marca++; // Apuntador para activar los transistores Multiplexador(); // LLama a la función para mostrar los datos } /************************************************************************ Escala el valor del conversor a un valor entre 0 y 50 **************************************************************************/ long map(long x, long in_min, long in_max, long out_min, long out_max){ return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min; }
También podemos eliminar el CD4511 y manejar todo el display directamente con Arduino, para esto tendríamos que modificar el circuito electrónico y adecuarlo a este nuevo código que es el que actualmente esta funcionando en terreno.
/*********************************************************************************
* Los segmentos están conectados de la siguiente forma:
* Pin 2 segmento a
* Pin 3 segmento b
* Pin 4 segmento c
* Pin 5 segmento d
* Pin 6 segmento e
* Pin 7 segmento f
* Pin 8 segmento g
***********************************************************************************/
#include <EEPROM.h>
#define prog 15 // Botón para programar nuevo rango
#define led 16 // Pin para controlar la sirena, led o un relevador.
#define T1 17 // Pin control de la base del transistor BC337 (Unidad)
#define T2 18 // Pin control de la base del transistor BC337 (Decena)
//----- Codifica los números y segmentos ---------------
const unsigned char num_array[10][7]PROGMEM =
{{ 1,1,1,1,1,1,0 }, // 0
{ 0,1,1,0,0,0,0 }, // 1
{ 1,1,0,1,1,0,1 }, // 2
{ 1,1,1,1,0,0,1 }, // 3
{ 0,1,1,0,0,1,1 }, // 4
{ 1,0,1,1,0,1,1 }, // 5
{ 1,0,1,1,1,1,1 }, // 6
{ 1,1,1,0,0,0,0 }, // 7
{ 1,1,1,1,1,1,1 }, // 8
{ 1,1,1,0,0,1,1 }}; // 9
const unsigned char EchoPin = 11; // Pin para el ultrasonido
const unsigned char TriggerPin = 12; // Pin para el ultrasonido
unsigned int conversion =0;
unsigned char muestras =0, rango = 0, unidad = 0, decena = 0, marca = 1;
unsigned int M0=0;
boolean debug = false; // falso o true ahorra memoria y código mas ágil
void setup() {
pinMode(led, OUTPUT); // Para controlar un led, sirena o un relay
pinMode(T1, OUTPUT); // Pin para la base de transistor unidad
pinMode(T2, OUTPUT); // pin para la base del transistor decena
pinMode(prog,INPUT_PULLUP); // Botón para programar el rango
// -- Configuración de los pines de los segmentos
pinMode(2, OUTPUT);
pinMode(3, OUTPUT);
pinMode(4, OUTPUT);
pinMode(5, OUTPUT);
pinMode(6, OUTPUT);
pinMode(7, OUTPUT);
pinMode(8, OUTPUT);
digitalWrite(led,LOW );
if (debug)
Serial.begin(9600);
pinMode(TriggerPin, OUTPUT);
pinMode(EchoPin, INPUT);
if(EEPROM.read(0) > 50){ // Recupera de memoria el último rango válido
rango = 30; // Si no es válido asigna uno por defecto.
}
else
rango = EEPROM.read(0); // Recupera el rango guardado en memoria
decena = (rango / 10) % 10; // y ajusta la unidad y decena para ver
unidad = rango % 10; // el dato del rango en el display.
//---------- Congigura el Timer 1 ----------------
noInterrupts(); // No interrupciones
TCNT1 = 65000; // Ajusta modulo a ~6 Milisegundos
TCCR1B |= (1 << CS10); // Sin prescalador
TIMSK1 |= (1 << TOIE1); // Habilita la mascara de interrupción
interrupts(); // Habilita las interrupciones generales
}
void loop() {
rango = EEPROM.read(0);
if (digitalRead(prog) == LOW){ // Boton de programa activado?
Leer_Conversor(); // OK, entonces leer el conversor.
EEPROM.write(0, conversion); // Almacena el nuevo rango
delay(500);
}
// NOTA:
// La sirena suena mientras el intruso este dentro del rango.
int dist = ping(TriggerPin, EchoPin);
if(dist <= rango -1) // Distancia es menor al rango offset??
digitalWrite(led,HIGH ); // Sirena o led activado
if(dist >= rango +1 || dist == 0) // Distancia > al offset o 0 ??
digitalWrite(led,LOW ); // Entonces apagar sirena (o lo que sea)!!!
if (debug) {
Serial.println(dist);
}
delay(500);
}
/****************************************************************
* Función para medir la distancia a que se encuentra el intruso
*****************************************************************/
int ping(int TriggerPin, int EchoPin) {
long tiempo, distancia;
digitalWrite(TriggerPin, LOW); // Genera un pulso de 4us
delayMicroseconds(4);
digitalWrite(TriggerPin, HIGH); // Genera disparo de 10us
delayMicroseconds(10);
digitalWrite(TriggerPin, LOW);
tiempo = pulseIn(EchoPin, HIGH); // Mide el tiempo entre pulsos y
distancia = tiempo * 10 / 292/ 2; // lo convierte a distancia en cm
return distancia;
}
/****************************************************************
* Lee el canal A0 donde se ha colocado un potenciometro de 10K
* para leer un voltaje entre 0 y 5V que se escalara a un valor
* entre 0 y 50 para ajustar nuevos rangos.
*
*****************************************************************/
void Leer_Conversor(void){
do{
M0 += analogRead(A0); // Lee el A/D y acumula el dato en M0
muestras++; // Incrementa el contador de muestras
}while(muestras <=29); // Se tomaron muestras ??
conversion = M0/30; // Se busca el promedio de las 30 muestras
conversion = map(conversion, 0, 1023, 0, 50);
decena = (conversion / 10) % 10;
unidad = conversion % 10;
M0 =0;
muestras =0;
}
/************************************************************************
Muestra el rango ajustado o extraído de la memoria
**************************************************************************/
void Multiplexador(){
switch(marca){ // El valor de marca determina que transistor se activa.
case 1:{
Muestra_Dato(unidad); // Muestra el valor de la unidad
digitalWrite(T1, HIGH); // Activar el display de la unidad
break;
}
case 2:{
digitalWrite(T1, LOW); // Apagar el display de la unidad
Muestra_Dato(decena); // Muestra el valor de la decena
if(decena > 0) // Si la decena es 0 apagar el dígito
digitalWrite(T2, HIGH); // Activar el display de la decena
break;
}
case 3:{
digitalWrite(T2, LOW); // Apagar el display de la decena
marca =0;
break;
}
}
}
/************************************************************************
Matriz bidimensional para activar los segmentos del display
**************************************************************************/
void Muestra_Dato(unsigned char number){
unsigned char pin = 2;
for (unsigned char j=0; j < 7; j++) {
digitalWrite(pin, pgm_read_byte(&num_array[number][j]));
pin++;
}
}
/* ******** ISR del Timer 1 por desborde **************
* Determina la velocidad a la cual se mostrará el
* valor de la unidad y la decena.
******************************************************/
ISR(TIMER1_OVF_vect){
TCNT1 = 65000; // 5,7 Milisegundos
marca++; // Apuntador para activar los transistores
Multiplexador(); // LLama a la funcion para mostrar los datos
}
/************************************************************************
Escala el valor del conversor a un valor entre 0 y 50
**************************************************************************/
long map(long x, long in_min, long in_max, long out_min, long out_max){
return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
}
En los meses que el sistema lleva funcionando los resultados han sido impecables y las palomas finalmente decidieron buscar un lugar mas tranquilo donde construir su nido :)