Blog de Acuariofilia, Biología y Medioambiente (Antonio Castro)

Blog sobre Acuariofilia, Biología, Medioambiente, Mundo animal, Maltrato Animal, Medioambiente, Biodiversidad, Cambio Climátido…

CAO-05: Prototipo 01 con Arduino

El objetivo de esta primera práctica que vamos a realizar con Arduino, será la construcción de un prototipo a modo de toma de contacto. Esto nos permitirá ilustrar la resolución de una serie de obstáculos que han de considerarse en la consecución de nuestro objetivo de desarrollar un Controlador de Acuarios por Ordenador.

En el vídeo, de cinco minutos y medio, las explicaciones son demasiado concisas porque no interesaba otra cosa que mostrar el prototipo y el principal requisito era hacer un vídeo muy cortito que sirva para mostrar el prototipo funcionando.

En este artículo vamos a explicarlo todo con mayor detalle. Quizás sea recomendable volver a ver el vídeo una vez que se termine de asimilar el presente artículo para poder interpretar correctamente todo lo que aparece en él.

Este es un prototipo sencillo pero completito. No es lo más indicado tomarlo como primer ejercicio con Arduino. Por ello, y a pesar de que iremos avanzando poco a poco, lo más adecuado para los que no tienen experiencia con a Arduino, sería practicar con los ejemplos más básicos,  tales como hacer parpadear un LED. Véase Tutorial: Comenzando con Arduino

 

El esquema de la electrónica que hemos conectado al Arduino podéis apreciarlo en la imagen siguiente:

CAO_Prototipo 01

CAO_Prototipo 01

1) Iluminación:

Al implementar un rudimentario sistema de control horario pretendemos superar con las limitaciones de los temporizadores tradicionales de acuarios que controlan un solo circuito eléctrico para  la iluminación. Algunos temporizadores programables permiten un tipo de flexibilidad que permiten definir comportamientos diferentes en función de los días de la semana y cosas así, pero los peces, no entiende de días de la semana. A lo que estos sí son sensibles la mayoría de los seres vivos es al fotoperiodo (relación noche/día que tienen una variación estacional). Los seres vivos, incluidos los peces, sincronizan su ciclo reproductivo para aprovechar los patrones estacionales usando el fotoperiodo como indicador para desencadenar sus comportamientos estacionales.  También son sensibles a la forma gradual en que se pasa del día a la noche y viceversa. Por ejemplo: algunos cíclidos que cuidan su prole necesitan acomodar a los alevines recogiéndoles en su nido, y un apagado brusco de la luz les puede sorprender en mal momento con parte de los alevines dispersados de forma nada conveniente para pasar la noche.

Estos dos factores, fotoperiodo y las transiciones crepusculares factores son especialmente importantes para la cría de determinadas especies y no se tienen en cuenta en la mayoría de los acuarios domésticos.

Para esta primera prueba estableceremos un programa para controlar tres tipos de iluminación diferentes que llamaremos iluminación nocturna (luz azul), iluminación crepuscular (blanco cálido) e iluminación diurna (luz blanca intensa + blanco cálido). Para esta primera prueba se han establecido unos horarios fijos:

  • Luz azul : de 22:00 a 8:00
  • Luz crepuscular: de 8:00 a 22:00
  • Luz diurna : de 9:30 a 20:30

Como la verificación de todo ello en un periodo de 24 horas resultaría poco cómoda, para esta demo, el ciclo horario de iluminación se ejecutará 5000 veces más rápido de lo normal. Es decir, el día de 24 horas en esta demo durará poco más de 17 segundos.

2) Calibración de sensores:

También vamos a probar las capacidades de Arduino para la lectura de sensores. Vamos a hacer que un sensor de luz con una simple fotoresitencia LDR, se pueda auto ajustar a partir de lecturas de referencia mínima y máxima, para así provocar una respuesta de iluminación variable en un LED dentro de los rangos de iluminación deseados.

3) Concurrencia:

Programaremos todo ello de forma que las tareas automatizadas por Arduino se gestionen de forma simultánea sin introducir retrasos las unas en las otras. Todas las funciones que se activan desde la función loop(), estarán diseñadas para ser muy breves con objeto de no ir acumulando los posibles retrasos en las diferentes tareas. Para que esto quede claro, haremos que una de estas tareas concurrentes haga parpadear un LED a una velocidad constante de un ciclo por segundo, con independencia de las restantes tareas.

4) Recepción de comandos por puerto serie:

Por último, implementaremos la posibilidad de aceptar comandos muy sencillos a través del puerto serie.

Nuestra base de tiempos funcionará usando el contador de milisegundos de Arduino. Este se va incrementando desde cero a partir del momento en el que arranca. Para esta demo eso nos basta, pero para un caso real hay que considerar que antes de que pasen 50 días este contador se desbordaría y recomenzaría en cero. Esto es un problema que tendremos que tratar en futuras versiones. Iremos resolviendo uno a uno los diferentes problemas hasta alcanzar una versión plenamente funcional para automatizar acuarios reales.

Código fuente del programa:

El código fuente que para finalizar se muestra a continuación, incluye abundantes comentarios:

/*********************************************************
(C) Antonio Castro Snurmacher 2013

Bajo licencia Creative Commons (BY-NC-SA)
Reconocimiento - NoComercial - CompartirIgual (by-nc-sa)

CAO_Proto01
-----------
1) Un led parpadeara de forma continua indicando con ello
que el programa está funcionando y que otras tareas pueden 
realizarse de forma concurrente sin afectarse sensible-
mente unas a otras.

2) Existira un sensor de luz y un LED asociado al mismo.
Al inicio parpadeara indicando que necesita ser calibrado.
Mientras esto ocurra hay que someter al sensor a su valor 
minimo de luz y a su valor maximo para que estos sean 
registrados como tales. Transcurrido el tiempo de calibra-
ción, el LED dejará de parpadear y lucira variando en in-
tensidad dependiendo de la luz recibida en el sensor 
dentro del rango maximo y mínimo registrado.
Un pulsador permitira recalibrar el sensor a voluntad.

3) Al inicio las trazas estan activas y la inicializacion
de los horarios se envia al puerto serie.
Este prototipo define tres horarios de iluminación para:
Luz_noctura, luz_crespuscular y luz_diurna.

4) El programa escucha por la entrada del puerto serie,
para identificar tres comandos diferentes: "INISENS",
"TRAZAON", y "TRAZAOFF".

NOTA:
Algunas funciones han tomado la idea en fragmentos de 
codigo fuente de dominio publico para Arduino. Vease:

http://arduino.cc/es/Tutorial/Calibration 

*********************************************************/

const int PinBlink_1Hz=13;   // salida digital
const int PinLuzNocturna=12; // salida digilal
const int PinLuzCrepuscu=11; // salida digital 
const int PinLuzDiurna=10;   // salida digital 
const int PinLedSensor = 5;  // salida digital PWM
const int PinPulsador =2;    // entrada digital para 
                             // el pulsador.
// Entrada analogica para el sensor
const int sensorPin = 3;                          

// Variables
boolean H_LuzNocturna[24][60], H_LuzCrepuscu[24][60], 
        H_LuzDiurna[24][60];

int sensorValue = 0;        // the sensor value
int sensorMin = 1023;       // minimum sensor value
int sensorMax = 0;          // maximum sensor value
boolean SensorPreparado = false;

// 5 segundos para calibrar el Sensor
const unsigned long TimeOutSetingSens1= 5000L; 

// Si vale menos de TimeOutSetingSens1 se activara la 
// calibración del sensor y se desactivara la lectura del 
// mismo.
unsigned long IniSensorAt = 0L; 
char CadAux[333];
boolean DebugOn=false;

/*********************************************************
Identifica una cadena de caracteres.
(Optimizable)
*********************************************************/
boolean Tok(char *buff, char *tok){
    int i=0;

    sprintf(CadAux,"buff=(%s), tok=(%s)\n", buff, tok); 
    Debug(CadAux, 0);
    while (tok[i]!='\0'){
        if (tok[i]!=buff[i])
            return false;
        i++;
    }
    return true;
}

/*********************************************************
  No lee hasta que se hace return (la consola no envia 
  los datos hasta entonces) y lee todos los caracteres 
  disponibles en el buffer de entrada. 
  La funcion leera todos los caracteres disponibles de 
  un tiron, o en su caso, si llegan mas de,  MaxBuffIn 
  caracteres, vaciara el buffer de entrada y retornara 
  error. (Hay que dejar pausa entre comandos. 
  SerialGetCommand no recibira comandos si vienen 
  apelotonados). Despues de leer el comando lo procesara
  o devolverá error.
 ********************************************************/
void SerialGetCommand() {
   // Maxima longitud del buffer de recepcion.
   int MaxBuffIn=128; 
   unsigned i=0;
   char sIn;
   char BUFF[MaxBuffIn];

   // Si no hay nada por leer retornar sin mas dilación
   if (Serial.available()==0){  
       return;
   }
   // Ya hay algo pero puede que aun no esten disponibles
   // todos los caracteres enviados. Por eso forzaremos 
   // una espera de 50 milisegundos. (Optimizable)
   delay(50);
   if (Serial.available()>=MaxBuffIn){ 
      // Vaciar Buffer de entrada (demasiados caracteres).
       while (Serial.available()){
          Serial.read(); 
       }
       Serial.print("ERROR: Comando muy largo\n");
       return;
   }

   // Queremos que la cadena se rellene hasta 
   // MaxBuffIn-2 para que en el carácter MaxBuffIn-1 
   // podamos meter el terminador \0
   --MaxBuffIn;        
   while (Serial.available() && i=TimeOutSetingSens1)
      return; // sobrepasado el tiempo del setup
  Blink_KPeriod(PinLedSensor, 1000/8); // Parpadeo a 8Hz
  sensorValue = analogRead(sensorPin);
  if (sensorValue > sensorMax) {
      sensorMax = sensorValue;
  }
  if (sensorValue < sensorMin) {       sensorMin = sensorValue;   }   sprintf(CadAux, "** CalibraSensor() min=%d, max=%d\n",           sensorMin, sensorMax);  Debug(CadAux, 200);  } /*********************************************************  Lee el valor del sensor y provoca la intensidad de brillo  en el LED que corresponda. *********************************************************/  void TaskSensor() {       // Para sacar una traza la primera vez que entre        // una vez calibrado el sensor.       if (!SensorPreparado){            sprintf(CadAux,          "\n** OK *** sensorMin=%d sensorMax=%d *****\n",          sensorMin, sensorMax);  Debug(CadAux, 0);       }       sensorValue = analogRead(sensorPin);       sensorValue = map(sensorValue, sensorMin,                          sensorMax, 0, 255);       sensorValue = constrain(sensorValue, 0, 255);       analogWrite(PinLedSensor, sensorValue);       SensorPreparado= true;  } /*********************************************************   Comprueba el pulsador y en su caso resetea el sensor    para forzar el recalibrado. Si es el turno de recalibrar   proceder a ello y sino proceder con la tarea asociada    al sensor. (LED de intensidad variable)  ********************************************************/ void Sensor() {   int p;   p=digitalRead(PinPulsador);   if (p==HIGH){ // Si pulsador pulsado--> recalibrar
    SetupSensor();
    delay(500);
  }
  // Hasta que no pasen "TimeOutSetingSens1" milisegs no termina la autocalibración 
  if ((millis()-IniSensorAt)=FromM)
				HR[h][m]=Val;
			if (h>FromH & h overflow 
 un poco antes de los 50 días.
 ********************************************************/
void ProcFotoPeriodo(boolean HR[24][60], int Pin){ 
    unsigned long s, h, m, d;

    // Velocidad real del reloj (dias de 24 horas)
    // s = millis() / 1000; 

    // Multiplicamos la velocidad del reloj * 5000 
    // (dias de 17 segundos con 280 milis)
    s = millis() * 5; 
    m = s / 60;
    s %= 60;
    h = m / 60;
    m %= 60;
    d = h / 24;
    h %= 24;

    if (HR[h][m]){
       digitalWrite(Pin, HIGH);  
    }
    else{
       digitalWrite(Pin, LOW);  
    }
}

/*********************************************************
  Hace parpadear un led con un periodo (=1/frec.) 
  determinado.
 ********************************************************/
void Blink_KPeriod(int Pin, int KPeriod){
  if ((millis()/KPeriod) % 2){
    digitalWrite(Pin, HIGH);  
  }
  else{
      digitalWrite(Pin, LOW);
  }
}

/****************************************************/
/**** INICIALIZACIÓN DE LAS TAREAS AL ARRANCAR ******/
/****************************************************/
void setup(){

  DebugOn=true; // Activar trazas.
  if (DebugOn){
      Serial.begin(9600);
      Debug("\n\n\nEstamos haciendo setup()\n\n", 2000);
  }
  pinMode(PinBlink_1Hz, OUTPUT);
  pinMode(PinLuzNocturna, OUTPUT);
  pinMode(PinLuzCrepuscu, OUTPUT);
  pinMode(PinLuzDiurna, OUTPUT);

  // EStablecer horarios iniciando matrices.
  SetHorario(H_LuzNocturna,  0, 0,23,59, true);
  SetHorario(H_LuzNocturna,  8, 0,22, 0, false);
  SetHorario(H_LuzCrepuscu,  0, 0,23,59, false);
  SetHorario(H_LuzCrepuscu,  8, 0,22, 0, true);
  SetHorario(H_LuzDiurna,    0, 0,23,59, false);
  SetHorario(H_LuzDiurna,    9,30,20,30, true);
  /************* TRAZAS HORARIAS  *************/
  if (DebugOn){
      Debug("\n\nHorario de luz nocturna\n", 0);
      MostrarHorario(H_LuzNocturna);
      Debug("\n\nHorario de luz crepuscular\n", 0);
      MostrarHorario(H_LuzCrepuscu);
      Debug("\n\nHorario de luz diurna\n", 0);
      MostrarHorario(H_LuzDiurna);
  }
  /***********************************************/
  SetupSensor();
  CalibraSensor();
}

/****************************************************/
/******* BUCLE PRINCIPAL DE CONTROL DE TAREAS *******/
/****************************************************/
void loop(){
	ProcFotoPeriodo(H_LuzNocturna, PinLuzNocturna); 
	ProcFotoPeriodo(H_LuzCrepuscu, PinLuzCrepuscu); 
	ProcFotoPeriodo(H_LuzDiurna,   PinLuzDiurna);
        // Parpadeo a 1Hz
        Blink_KPeriod(PinBlink_1Hz, 1000); 
        Sensor(); 
        SerialGetCommand();
}

Anterior

La aventura de la acuariofilia (reproducción de Apistogramma ramirezi)

Siguiente

La venganza del Cuco.

3 comentarios

  1. Manuel

    Hola Antonio, acabo de empezar con mis primeros diseños sencillos en Arduino y me interesa el CAO que has desarrollado. Entiendo bastantes cosas del código escrito pero veo que aún estoy un poco verde.

    He cogido tú código y al compilarlo me da error en: while (tok[i]!=»){

    Me podrías decir como solucionarlo?

    Saludos,
    Manuel

  2. Antonio Castro

    Mi código no contiene ese error porque también a mí me daría error. No me comentas que programa has bajado, ni en que linea da el error ni que dice el error aunque esto último no es necesario porque eso tiene que dar error de sintaxis.
    Este tipo de asistencia la ofrezco desde
    Alula Arduino para principiantes (subforo de acuariofiliamadrid.org)

  3. Andrés

    Hola, Antonio. Me encanta el proyecto y te doy la enhorabuena por ello.

    No sé si desde que lo publicaste has llegado a hacer alguna mejora o nueva revisión. El caso es que no sé si el esquema esta o no publicado para poderlo hacer. Sólo quiero que cuando el ph baje de un nivel ajustable, un relé corte la alimentación de la electroválvula de 220 v. del sistema de CO2. A un determinado ciclo de histéresis el CO2 volvería a saltar.

    Muy agradecido de antemano por la respuesta y un cordial saludo.

    Los parámetros que se visualizan en pantalla, en la línea que hace scroll entiendo no sean del acuario exactamente, ¿no?

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

Funciona con WordPress & Tema de Anders Norén