/*
* Sensor de Temperatura que registra el valor en Internet llamando a la
* aplicación con la URL
* http://php-domos.rhcloud.com/insert.php?sensor=1236&valor=xx
*/
#include "DHT.h"
#include "SPI.h"
#include "Ethernet.h"
#include <EEPROM.h>
#include <avr/wdt.h>
/* =============================================================================================
CONFIGURACION PARTICULAR
============================================================================================= */
//#define DEBUG_ON
#ifdef DEBUG_ON
#define debug(x) Serial.println(x)
#else
#define debug(x)
#endif
const long PERIODO_ACTUADOR = 10L * 60L * 1000L; // Cada cuanto pregunta por el estado del actuador.
const long PERIODO_TEMPERATURA = 20L * 60L * 1000L; // Cada cuanto informa sobre la temperatura.
const long PERIODO_HUMEDAD = 2L * 60L * 60L * 1000L; // Cada cuanto informa sobre la humedad
const long PERIODO_RESET = 4L * 60L * 60L * 1000L; // Hace un reset cada 4 horas
#define SW_TEMPERATURA 1 // Indica si queremos leer temperatura y enviarla al servidor
#define USAR_RELE // Indica si queremos que el actuador funcione con un Relé
#define USAR_IR // Indica si queremos usar códigos IR para
const int PIN_DISPOSITIVO = 8;
const int PIN_ERROR = 7;
const int PIN_EN_EJECUCION = 6;
//const int PIN_RESET = 5;
const int ID_SENSOR_TEMPERATURA=nnnn; // Temperatura interior de Lamuño
const int ID_SENSOR_HUMEDAD=nnnn; // Sensor de la humedad interior de Lamuño
const int ID_ACTUADOR=nnnn;
#define PIN_TERMOMETRO 2 // EL PIN DEL SENSOR DE TEMPERATURA
const int REINTENTOS = 4;
//const long CODIGO_IR_OFF = 0x807FC837; // El código para encender el aparato a controlar
//const long CODIGO_IR_ON = 0x807F58A7; // El código para apagar el aparato a controlar
const long CODIGO_IR_ON = 0x807F48B7; // El código para encender VERDADERO
const long CODIGO_IR_OFF = CODIGO_IR_ON;
#define PIN_IR 3
int ledEnEjecucion=0; // Simplemente el indicador que parpadeará si se está ejecuando el bucle.
const int ESTADO_ON = 1;
const int ESTADO_OFF = 0;
const int ESTADO_INDETERMINADO = -1;
int estadoDispositivo = ESTADO_OFF;
/* ============================================================================================= */
#ifdef USAR_IR
#include <IRremote.h>
IRsend irsend; // Parece ser que no se puede definir el PIN sino que es siempre el 5.
#endif
long milisActuador= -PERIODO_ACTUADOR;
long milisTemperatura= -PERIODO_TEMPERATURA;
long milisReset=0;
long milisHumedad= -PERIODO_HUMEDAD;
char server[]= "xxxxx.rhcloud.com";
char query[]="GET /xxxxx.php?sensor=%d&valor=%d HTTP/1.1";
char actuador[]="GET /xxxxxx.php?id=%d HTTP/1.1";
char actuador_off[]="GET /xxx.php?actuador=%d&estado=OFF HTTP/1.1";
char host[]="Host: xxxxxx.rhcloud.com";
#define DHTTYPE DHT11
DHT dht(PIN_TERMOMETRO, DHTTYPE);
byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };
IPAddress ip(192,168,0,2);
EthernetClient cliente;
void setup() {
wdt_disable();
delay(50); // Segun dice la documentación http://www.freetronics.com.au/pages/usb-power-and-reset#.VN_itPmG_gc
int isReset=checkReset(); // Hay que hacer esto lo antes posible para limpiar la EEPROM rapidamente.
// HACK Para poder hacer reset
//digitalWrite(PIN_RESET, HIGH);
//pinMode(PIN_RESET, OUTPUT);
pinMode(PIN_DISPOSITIVO, OUTPUT);
pinMode(PIN_ERROR,OUTPUT);
pinMode(PIN_EN_EJECUCION, OUTPUT);
Serial.begin(9600);
debug("Setup");
// Parpareamos un segundo el PIN de error para mostrar que funciona.
esperar(2000, 50, PIN_ERROR); // Espera a ver si se inicializa el modem...
esperar(2000, 50, PIN_DISPOSITIVO);
esperar(2000, 50, PIN_EN_EJECUCION);
if(SW_TEMPERATURA) {
dht.begin();
}
// De momento el wdt no funciona. Aunque haga reset la placa Ethernet no acaba funcionando hata que haga reset
// fisicamente en la placa.
// Además, 8 segundos es poco tiempo para esperar ya que el método connect podría tardar más incluso.
// wdt_enable(WDTO_8S);
debug("Inicializando Ethernet.");
//if(Ethernet.begin(mac)==0) {
// mostrarError("Error obteniendo la IP. La ponemos fija");
Ethernet.begin(mac, ip);
//}
debug(Ethernet.localIP());
wdt_reset();
esperar(5000, 100);
if(isReset) {
estadoDispositivo=EEPROM.read(1);
digitalWrite(PIN_DISPOSITIVO, estadoDispositivo);
} else {
int estadoRecibido=ESTADO_INDETERMINADO;
for(int vez=0; vez<3 && estadoRecibido==ESTADO_INDETERMINADO; vez++) {
estadoRecibido=consultarActuador(cliente,0);
if(estadoRecibido==ESTADO_INDETERMINADO) {
esperar(5000, 2000, PIN_ERROR);
}
}
if(estadoRecibido==ESTADO_INDETERMINADO) {
mostrarError("Imposible conocer el estado");
for(;;); // NO puedo continuar si no conozco el estado.
}
estadoDispositivo=estadoRecibido;
}
wdt_reset();
}
// Devuelve cierto si el Setup se está ejecutando tras un Reset.
int checkReset() {
int result=EEPROM.read(2);
EEPROM.write(2, 0); // Limpiamos
return result==1? 1:0; // Hacemos asi para garantixar 1 o cero.
}
// Marcamos para que se sepa al arrancar que viene de un reset.
void setReset() {
EEPROM.write(2, 1);
}
void reset() {
EEPROM.write(1, (byte)estadoDispositivo); // Guardamos el estado del dispositivo para cargarlo en el siguiente setup;
setReset();
wdt_enable(WDTO_8S); // Fuerzo un reset con el WatchDog a base de esperar unos segundos.
esperar(20000, 100, PIN_ERROR);
digitalWrite(PIN_ERROR, HIGH); // Marcamos que hay reset por si no funcionara.
// digitalWrite(PIN_RESET, LOW); // Sistema de reset con cable al RESET (NO lo uso);
for(;;); // Tras el Reset no puede haber nada.
}
void esperar(long total, long parpadeo, int pin) {
int valor=0;
for(long i=0; i<total; i+=parpadeo) {
wdt_reset();
delay(parpadeo);
digitalWrite(pin, valor);
valor=valor?0:1;
}
digitalWrite(pin, 0);
}
void esperar(long total, long parpadeo) {
esperar(total, parpadeo, PIN_EN_EJECUCION);
}
void mostrarError(char *Mensaje) {
debug(Mensaje);
digitalWrite(PIN_ERROR, 1);
}
void limpiarError() {
digitalWrite(PIN_ERROR, 0);
}
void enviarConsulta(EthernetClient &cliente, char *consulta) {
cliente.println(consulta);
cliente.println(host);
cliente.println("Connection: close\r");
cliente.println();
}
void enviarTemperatura(EthernetClient &cliente) {
if(!SW_TEMPERATURA) {
return;
}
wdt_reset();
digitalWrite(PIN_EN_EJECUCION, 1);
debug("Conectado para enviar temperatura");
int t = (int)dht.readTemperature();
if(t<=0) {
esperar(5000, 300, PIN_ERROR);
digitalWrite(PIN_ERROR, 1);
return;
}
if(conectar(cliente)!=1) {
mostrarError("Error conectando a temperatura");
return;
}
enviarQuery(ID_SENSOR_TEMPERATURA, t);
milisTemperatura=millis();
wdt_reset();
}
void enviarHumedad(EthernetClient &cliente) {
if(!SW_TEMPERATURA) {
return;
}
wdt_reset();
digitalWrite(PIN_EN_EJECUCION, 1);
debug("Conectado para enviar humedad");
int h = (int)dht.readHumidity();
if(h<=0) {
esperar(5000, 300, PIN_ERROR);
digitalWrite(PIN_ERROR, 1);
return;
}
if(conectar(cliente)!=1) {
mostrarError("Error conectando a humedad");
return;
}
enviarQuery(ID_SENSOR_HUMEDAD, h);
milisHumedad=millis();
wdt_reset();
}
void enviarQuery(int idSensor, int t) {
char aux[256];
sprintf(aux, query, idSensor, t);
enviarConsulta(cliente,aux);
esperar(3000,50);
cliente.stop();
cliente.flush();
esperar(2000,100);
digitalWrite(PIN_EN_EJECUCION, 0);
}
int conectar(EthernetClient &cliente) {
int ret;
for(int i=0; i<REINTENTOS; i++) {
wdt_reset();
ret=cliente.connect(server, 80);
if(ret==1) {
return ret;
}
}
return ret;
}
void ponerActuadorOff(EthernetClient &cliente) {
debug("Poniendo el actuador en OFF");
int ret=conectar(cliente);
if(ret != 1) {
char aux[256];
sprintf(aux, "Error %d connect", ret);
mostrarError(aux);
return;
}
char aux[256];
sprintf(aux, actuador_off, ID_ACTUADOR);
enviarConsulta(cliente, aux);
esperar(2000,100);
cliente.stop(); // Me da lo mismo lo que responda...
cliente.flush();
esperar(2000,100);
}
int consultarActuador(EthernetClient &cliente, int actuar) {
debug("Consultar actuador");
int ret=conectar(cliente);
if(ret != 1) {
char aux[256];
sprintf(aux, "Error %d connect", ret);
mostrarError(aux);
return ESTADO_INDETERMINADO;
}
char aux[256];
//debug("Conectado para leer actuador");
sprintf(aux, actuador, ID_ACTUADOR);
enviarConsulta(cliente, aux);
esperar(100,50); // Esperamos un poco a ver si hay respuesta.
int estadoRecibido=leerRespuesta(cliente);
cliente.stop();
cliente.flush();
if(actuar) {
if(estadoRecibido==ESTADO_ON) {
encenderDispositivo();
} else if(estadoRecibido==ESTADO_OFF) {
apagarDispositivo();
} else {
mostrarError("Estado recibido indeterminado");
}
} else {
if(estadoRecibido==ESTADO_INDETERMINADO) {
mostrarError("Estado recibido indeterminado");
}
}
return estadoRecibido;
}
void apagarDispositivo() {
#ifdef USAR_RELE
digitalWrite(PIN_DISPOSITIVO, 0);
#endif
#ifdef USAR_IR
if(estadoDispositivo==ESTADO_ON) {
irsend.sendNEC(CODIGO_IR_OFF, 32);
estadoDispositivo=ESTADO_OFF;
}
#endif
}
void encenderDispositivo() {
#ifdef USAR_RELE
digitalWrite(PIN_DISPOSITIVO, 1);
#endif
#ifdef USAR_IR
if(estadoDispositivo==ESTADO_OFF) {
irsend.sendNEC(CODIGO_IR_ON, 32);
estadoDispositivo=ESTADO_ON;
}
#endif
}
void leerLinea(EthernetClient &client, char aux[], int max) {
int i=0;
while(client.available() && i<max) {
char c=client.read();
if(c=='\n') {
break;
} else {
aux[i++]=c;
}
}
aux[i]=0;
}
int leerRespuesta(EthernetClient &cliente) {
int retorno=ESTADO_INDETERMINADO;
int intentos=0;
// Esperando a datos disponibles, un maximo de 5 segundos
for(intentos=0; intentos<500 && cliente.connected(); intentos++) {
if(cliente.available()) {
break;
}
delay(50);
}
// Leemos los datos disponibles...
for(intentos=0; intentos<100 && cliente.connected(); intentos++) {
if(cliente.available()) {
char linea[256];
leerLinea(cliente, linea,100);
if(!strcmp(linea,"ON")) {
retorno=ESTADO_ON;
break;
}
if(!strcmp(linea,"OFF")) {
retorno=ESTADO_OFF;
break;
}
}
delay(50);
}
return retorno;
}
void procesarActuador(EthernetClient &cliente) {
int estadoRecibido=consultarActuador(cliente, 1);
if(estadoRecibido!=ESTADO_INDETERMINADO) {
limpiarError();
}
}
void loop() {
wdt_reset();
long time=millis();
if(time > PERIODO_RESET) {
// Antes de hacer reset hay que ver si el actuador quiere que encienda o apague.
procesarActuador(cliente);
reset();
}
if(time > milisTemperatura + PERIODO_TEMPERATURA) {
enviarTemperatura(cliente);
milisTemperatura=time;
}
if(time > milisHumedad + PERIODO_HUMEDAD) {
enviarHumedad(cliente);
milisHumedad=time;
}
if(time > milisActuador + PERIODO_ACTUADOR) {
procesarActuador(cliente);
milisActuador=time;
}
delay(1000);
ledEnEjecucion = ledEnEjecucion? 0:1;
digitalWrite(PIN_EN_EJECUCION, ledEnEjecucion);
}
domingo, 15 de marzo de 2015
Domos - El código fuente definitivo
Bueno,... , tras un mes utilizando ya el sistema domótico de encendido de la estufa, el código fuente ya está bastante estabilizado.
Suscribirse a:
Enviar comentarios (Atom)
No hay comentarios:
Publicar un comentario