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

*  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);

}

No hay comentarios:

Publicar un comentario