10-12-2012
http://www.youtube.com/watch?v=xSuP_aj86Bk
He modificado la cerradura electronica, que se puede ver en el video, para que funione con Arduino.
He usado:
Un Arduino Nano. 12€
Un teclado keypad de ebay. 3€
Un lcd 1602 que vale unos 5€
Un modulo de dos relés de ebay. 3€
Un sensor magnetico de puerta abierta. 4€
Un zumbador. 3€
El Motor con mando a distancia de ABUs Hometec. 70€ (En Alemania cuesta unos 40€, en ferreteria Española unos 150€... sin comentarios)
Estoy esperando de china:
-RCT por I2C. Mi arduino atrasa 10 minutos al día¡¡ Se puede paliar por soft, pero el RTC es lo suyo y vale menos de 3€
-Un LCD con I2C para liberar pines y así poder poner sensor temperatura 18B20, grabador de tarjetas SD, sensor llamadas al timbre (poniendole un relé de 220 en paralelo) y cuarta columna keypad.
He conectado los dos relés a los botones del motor que abren y cierran la puerta manualmente. He tenido que usar el motor alrevés (que cierre cuando abre y viceversa).
El codigo fuente es el siguiente (compilado con Arduino 1.0.2):
/*******************************************************************************
* CERRADURA ELECTRONICA de Antonio EB4CAK
* Pongo este programa bajo licencia GNU GPL.
*
* 22-5-2011 INICIO
* 20-6-11 Version 1.0
* 12-7-11 V 1.1 Puerta se cierra ante clave erronea - corregir bug que no cierra la puerta -
* 10-9-11 V 1.2 Avisa de la ultima clave erronea
* 08-10-12 v 1.3 Modificacion para Mari Carmen y añadida clave 7 generica
* 1-12-2012 v1.4 Adaptacion de STM8s a Arduino. Quitada clave7. Espera incremental si error.
* 7-12-2012 Lo instalo
******************************************************************************
Cosas Pendientes:
-RTC (pines A4 y A5)
-SD para grabar entradas y salidas
-Pin de entrada para avisar si han llamado al timbre
PINES:
2 Teclado Col 1
3 Teclado Col 2
4 ABRIRCERRADURA Rele cerradura ON
5 CERRARCERRADURA Rele cerradura OFF
6 Teclado Col 3
7 Zumbador Teclado Col 4 (Opcional)
8 Teclado Fila 1
9 Teclado Fila 2
10Teclado Fila 3
11Teclado Fila 4
12 LCD RS
13* LCD E
A0 LCD D4
A1 LCD D5
A2 LCD D6
A3 LCD D7
A4* RTC
A5* RTC
A6 Sensor timbre? 18b20?
A7 Sensor puerta abierta (Poner una R de 5v al pin y luego el pin a Masa pasando por el interruptor)
*/
#include <LiquidCrystal.h>
#include <Keypad.h>
#include <Time.h> // Para la hora
#include <Wire.h>
#include <DS1307RTC.h> // a basic DS1307 library that returns time as a time_t
#include <EEPROM.h>
// initialize the library with the numbers of the interface pins
LiquidCrystal lcd(12, 13, 17, 16, 15, 14);
//////////// Config teclado
const byte ROWS = 4; //Filas
const byte COLS = 3; //Columnas
char keys[ROWS][COLS] = {
{'1','2','3'},
{'4','5','6'},
{'7','8','9'},
{'*','0','#'}
};
byte rowPins[ROWS] = {8,9,10,11}; //connect to the row pinouts of the keypad
byte colPins[COLS] = {2,3,6}; //connect to the column pinouts of the keypad
Keypad keypad = Keypad( makeKeymap(keys), rowPins, colPins, ROWS, COLS );
//////////// Fin Config Teclado
////// Claves
char Clave1[15]={"1234"}; // Pueden ser 15 caracteres o menos.
char Clave2[15]={"00000000"};
char Clave3[15]={"1111111111111"}; // Para Mari Carmen
char Clave4[15]={"32165498732165"}; // Clave de un solo uso
char Clave5[15]={"32112346549878"}; // Clave de un solo uso
char Clave6[15]={"55555556546546"}; // Clave de un solo uso
// Nota: Hay que cambiar estas claves. Solo son de prueba para la difusion del codigo.
////// Fin Claves
#define LUNES 2
#define MARTES 3
#define MIERCOLES 4
#define JUEVES 5
#define VIERNES 6
#define SABADO 7
#define DOMINGO 1
#define SI 0xFF
#define NO 0
#define COMPROBAR 1 // Usado en exceso_de_tiempo
#define INICIAR 2 // Usado en exceso_de_tiempo
#define ANULAR 0 // Usado en exceso_de_tiempo
#define SensorPuerta A7
#define BUZZ 7 //Definimos el pin del zumbador
#define ABRIRCERRADURA 4
#define CERRARCERRADURA 5
#define INACTIVO HIGH // Para no liarse con ABRIRCERRADURA
#define ACTIVO LOW
/********* PARA TEMPORIZACIONES Y RELES ***********/
unsigned int error_acumulativo=1; // Para que a cada codigo erroneo la espera sea mayor
/********* FIN PARA TEMPORIZACIONES Y RELES *************/
time_t pctime = 1354786049; // Tiempo inicial de prueba hasta poner el RTC.
time_t TiempoError = 0; // Para pintar hora de ultima entrada erronea
void setup() {
delay(50); // Por si acaso, para estabilizar.
Serial.begin(9600); // Para depuracion solo.
lcd.begin(16, 2); // 16 Columnas y 2 filas
lcd.clear(); //Para evitar que queden cosas mal.
pinMode(BUZZ, OUTPUT); // Esto se puede quitar.
pinMode(ABRIRCERRADURA, OUTPUT);
pinMode(CERRARCERRADURA, OUTPUT);
digitalWrite(ABRIRCERRADURA, INACTIVO); // Reles inactivos
digitalWrite(CERRARCERRADURA, INACTIVO); // Reles inactivos
/* Aqui PONEMOS RTC Revisarlo para varios intentos
setSyncProvider(RTC.get); // the function to get the time from the RTC
if(timeStatus()!= timeSet)
Serial.println("Unable to sync with the RTC");
else
Serial.println("RTC has set the system time");
*/
setTime(pctime);
melodia(2); // Musiquilla que indica reinico
}// Del setup
void loop() {
pinta_reloj();
delay(5);
char key = keypad.getKey(); // Capturamos tecla
clave_tecla(key); // Comprobamos si es la clave OK
// Pintamos errores
if(TiempoError)pinta_reloj_eventos();
// Si PUERTA ABIERTA pasamos a modo configuracion
if(analogRead(SensorPuerta) > 500){ // if(analogRead(SensorPuerta) > 500){
luz_puerta(SI); // Encendemos la luz de la entrada
modo_configuracion();
luz_puerta(SI); // Volvemos para iniciar contador otra vez
}else luz_puerta(NO); // Con la puerta cerrada pensamos en apagar la luz de la entrada y en cerrar la cerradura a los x seg.
}// Del loop
/************************************************************************/
/************ Pintamos la Hora ***********************/
/************************************************************************/
void pinta_reloj(){
int dia;
dia=weekday();
lcd.setCursor(0,0); // Situamos el cursor
if(hour() < 10) lcd.print('0');
lcd.print(hour());
printDigits(minute());
// printDigits_segundos(second());
lcd.print(" ");
/* lcd.print(day(),DEC);
lcd.print("/");
lcd.print(month(),DEC);
lcd.print("/");
lcd.print(year(),DEC); */
switch(dia){
case LUNES:
lcd.print("L");
break;
case MARTES:
lcd.print("M");
break;
case MIERCOLES:
lcd.print("X");
break;
case JUEVES:
lcd.print("J");
break;
case VIERNES:
lcd.print("V");
break;
case SABADO:
lcd.print("S");
break;
case DOMINGO:
lcd.print("D");
break;
}
}//Fin pinta_reloj()
void printDigits(int digits){
// utility function for digital clock display: prints preceding colon and leading 0
lcd.print(":");
if(digits < 10)
lcd.print('0');
lcd.print(digits);
}
void printDigits_segundos(int digits){
// utility function for digital clock display: prints preceding colon and leading 0
lcd.print(".");
if(digits < 10)
lcd.print('0');
lcd.print(digits);
}
/************************************************************************/
/************ Centralizacion de Musiquillas ***********************/
/* Tuve problemas hasta que descubrà que sin el delay() no funciona */
/************************************************************************/
void melodia(int tipo){ // Musiquillas 1->Error 2->Inicio 3->Correcto 4->Pulsar tecla 5->puerta abierta 6->Puerta cerrada
switch(tipo){
case 1:
cerrar_puerta(); // En caso de clave mal, echamos cerrojo
tone(BUZZ,2000);
TiempoError=now(); // Memorizamos hora para recordar el ultimo error
delay(2000*error_acumulativo++); // Mas retraso y acumulativo (puede llegar a un pitido de 35 horas por cada error)
noTone(BUZZ);
break;
case 2:
tone(BUZZ,1000); delay(50);
tone(BUZZ,4000); delay(50);
tone(BUZZ,1000); delay(50);
noTone(BUZZ);
break;
case 3:
tone(BUZZ,1000); delay(20);
tone(BUZZ,4000); delay(40);
tone(BUZZ,2000); delay(20);
tone(BUZZ,1000); delay(40);
tone(BUZZ,4000); delay(40);
tone(BUZZ,2000); delay(20);
tone(BUZZ,1000); delay(40);
tone(BUZZ,4000); delay(60);
tone(BUZZ,2000); delay(40);
tone(BUZZ,1000); delay(40);
tone(BUZZ,4000); delay(40);
tone(BUZZ,1000); delay(60);
noTone(BUZZ);
error_acumulativo=1; // Reiniciamos la espera si claves errorneas.
break;
case 4:
tone(BUZZ,1000); delay(80);
noTone(BUZZ);
break;
case 5:
tone(BUZZ,2000); delay(80);
tone(BUZZ,4000); delay(50);
tone(BUZZ,1000); delay(80);
tone(BUZZ,4000); delay(30);
noTone(BUZZ);
break;
case 6:
tone(BUZZ,4000); delay(40);
tone(BUZZ,2000); delay(30);
tone(BUZZ,4000); delay(30);
tone(BUZZ,1000); delay(100);
noTone(BUZZ);
break;
}//Del switch
noTone(BUZZ); // Por si acaso.
}//del melodia()
/************************************************************************/
/* Entramos en modo configuracion al abrir la puerta */
/* Solo saldremos al cerrar la puerta */
/* Mantenemos la luz de la entrada encendida */
/************************************************************************/
void modo_configuracion(void){
char key=0; char estado=0;
melodia(5); // Avisamos de puerta abierta
lcd.setCursor(0,1); // Situamos el cursor
lcd.print(" CONFIG PULSE 0 ");
pctime=now(); // Igualamos pctime al tiempo del sistema.
// Encender luz entrada (y mantenerla despues, quizas usando exceso_de_tiempo())
while(analogRead(SensorPuerta) > 500){ // Mientras la puerta este abierta:
pinta_reloj();
key = keypad.getKey(); //Capturamos tecla
if (key) //new key has been pressed
{
melodia(4); // Pitidito de pulsacion
if(key=='0') estado++; // Cambiamos de menu con el CERO
if(estado>4)estado=1; // Lo hacemos ciclico
switch(estado)
{
case 1: // BORRAR ENTRADAS ERRONEAS
lcd.setCursor(0,1);
lcd.print(" BORRAR ENTRADA");
if(key=='*'){TiempoError=0;error_acumulativo=1;}
if(key=='#'){TiempoError=0;error_acumulativo=1;}
break;
case 2: // AJUSTAR HORA
lcd.setCursor(0,1);
lcd.print(" AJUSTE HORA ");
if(key=='*'){pctime-=3600;}
if(key=='#'){pctime+=3600;}
setTime(pctime); // igualamos el tiempo de sistema al de pctime
break;
case 3: // AJUSTAR MINUTOS
lcd.setCursor(0,1);
lcd.print(" AJUSTE MINUTOS");
if(key=='*'){pctime-=60;}
if(key=='#'){pctime+=60;}
setTime(pctime);
break;
case 4: // AJUSTAR DIA
lcd.setCursor(0,1);
lcd.print(" AJUSTE DIA ");
if(key=='*'){pctime-=86400;}
if(key=='#'){pctime+=86400;}
setTime(pctime);
break;
}
} // Del si key
} // Del while
lcd.clear(); // Al salir hay que limpiar el display
melodia(6); // Cantamos q la puerta se cierra
}
/************************************************************************/
/* COMPROBAMOS LAS TECLA PULSADAS en busca de claves */
/************************************************************************/
void clave_tecla(char Tecla){
static char oldkey, posicion_clave=0;
static char Clave[15];
if(exceso_de_tiempo(10, COMPROBAR)){posicion_clave=0; melodia(1); return;} // Damos señal de error
if (Tecla != oldkey) //new key has been pressed
{
if (Tecla)
{
melodia(4); // Pitidito de pulsacion
exceso_de_tiempo(10, INICIAR);
Clave[posicion_clave]=Tecla;
Clave[posicion_clave+1]=0; // Finalizamos la cadena para evitar errores
if(posicion_clave < 14){ posicion_clave++; }
else{ // Si metemos 15 num y no hay clave hay que esperar
posicion_clave=0;
melodia(1);
exceso_de_tiempo(10, ANULAR);
// Implantar esperas si se mete codigo erroneo X veces

}
if(!strcmp(Clave, Clave1)){ // CLAVE CORRECTA
clave_correcta(); // abrir puerta
posicion_clave=0; // Reiniciamos contador
}
if(!strcmp(Clave, Clave2)){ // CLAVE CORRECTA
clave_correcta(); // abrir puerta
posicion_clave=0; // Reiniciamos contador
}
/* La clave3 funcionará los dias de curro de 2 a 9 (OJO Hasta el RTC pongo todos los dias de 9 a 23) */
if(hour()>9 && hour()<23 /* && day()!=DOMINGO && day()!=SABADO*/)
if(!strcmp(Clave, Clave3)){ // CLAVE CORRECTA
clave_correcta(); // abrir puerta
posicion_clave=0; // Reiniciamos contador
}
/* CLAVES DE UN SOLO USO */
if(!EEPROM.read(100))
if(!strcmp(Clave, Clave4)){ // CLAVE CORRECTA
clave_correcta(); // abrir puerta
posicion_clave=0; // Reiniciamos contador
EEPROM.write(100, NO); // La clave queda invalidada // OJO poner temporizacion para que se pueda usar durante ~10 min.
}
if(!EEPROM.read(101))
if(!strcmp(Clave, Clave5)){ // CLAVE CORRECTA
clave_correcta(); // abrir puerta
posicion_clave=0; // Reiniciamos contador
EEPROM.write(101, NO); // La clave queda invalidada
}
if(!EEPROM.read(102))
if(!strcmp(Clave, Clave6)){ // CLAVE CORRECTA
clave_correcta(); // abrir puerta
posicion_clave=0; // Reiniciamos contador
EEPROM.write(102, NO); // La clave queda invalidada
}
oldkey = Tecla; // Antirebotes
}
else // Si no hay tecla pulsada o tecla no correcta
{
oldkey = 255;
}
} // Del si tecla != oldkey
}
/*****************************************************/
/* Abrir puerta, cerrarla luego, memorizar evento y */
/*****************************************************/
void clave_correcta(){
digitalWrite(ABRIRCERRADURA, ACTIVO); // abrimos puerta
melodia(3); // Usamos melodia de timer
digitalWrite(ABRIRCERRADURA, INACTIVO); // Dejamos de abrir puerta
exceso_de_tiempo(10, ANULAR);
}
/*****************************************************/
/* Cerrar Puerta solo si esta cerrada, memorizar evento y */
/*****************************************************/
void cerrar_puerta(){
if(analogRead(SensorPuerta) < 500){ //Si la puerta está cerrada
digitalWrite(CERRARCERRADURA, ACTIVO); // Echamos el cerrojo
melodia(6); // Avisamos del cierre y lo usamos de timer
digitalWrite(CERRARCERRADURA, INACTIVO); // Dejamos de cerrar
}
}
/************************************************************************/
/* Para comprobar si ha pasado mucho tiempo (en segundos) sin respuesta */
/************************************************************************/
char exceso_de_tiempo(int tiempo_espera, char tipo_operacion)
{
static time_t hora2=0;
pctime=now();
if(tipo_operacion == INICIAR) hora2=pctime;
if(tipo_operacion == ANULAR) hora2=0;
if(tipo_operacion == COMPROBAR){
if(hora2){ // Para que no salte sin iniciar
if((hora2+tiempo_espera) < pctime){
hora2=0; // Para no comprobar de nuevo sin iniciar
return SI; // TIEMPO DE ESPERA EXCEDIDO
}
}
}
return NO; // Tiempo de espera no excedido
}
/************************************************************************/
/* Pintamos horas de eventos especificos */
/************************************************************************/
void pinta_reloj_eventos(){
int dia;
dia=weekday(TiempoError);
lcd.setCursor(0,1); // Situamos el cursor
if(hour() < 10) lcd.print('0');
lcd.print(hour(TiempoError));
printDigits(minute(TiempoError));
lcd.print(" ");
/* lcd.print(day(),DEC);
lcd.print("/");
lcd.print(month(),DEC);
lcd.print("/");
lcd.print(year(),DEC); */
switch(dia){
case LUNES:
lcd.print("L");
break;
case MARTES:
lcd.print("M");
break;
case MIERCOLES:
lcd.print("X");
break;
case JUEVES:
lcd.print("J");
break;
case VIERNES:
lcd.print("V");
break;
case SABADO:
lcd.print("S");
break;
case DOMINGO:
lcd.print("D");
break;
}
} // Fin del void pinta_reloj_eventos()
/******************************************************************/
/** Encendemos y apagamos la luz de la entrada **/
/** La luz se apaga con 20seg de reatraso **/
/** También cerramos la cerradura a los 20 seg de cerrar puerta **/
/******************************************************************/
void luz_puerta(char estado)
{
static time_t horaX=0;
if(estado){
//GPIO_WriteHigh(GPIOA, GPIO_PIN_3); // Encendemos luz
horaX=now(); // Empezamos a contar (cada vez que pasemos por aqui, empezamos a contar)
}else{ // Si el estado es NO, nos preparamos para apagar y cerrar
if((horaX) && (now() > (horaX+20))){
//GPIO_WriteLow(GPIOA, GPIO_PIN_3); //Apagar luz
cerrar_puerta();
horaX=0; // Reiniciamos el contador
}
} // Del else
}