Proyecto 34.2 - Reloj-Termómetro-Calendario-Higrómetro

19 de junio 2016

En el Proyecto 34.1  vimos una introducción a los displays VFD. Con un módulo RTC DS3231 hicimos un sencillo Reloj-Termómetro aprovechando la temperatura proporcionada por el sensor interno del RTC.

Para completar las funciones del display, vamos a añadir un sensor de humedad DHT11. Este sensor también proporciona la temperatura, pero al igual que el RTC DS3231 tiene una tolerancia de +- 2ºC, por lo que únicamente lo utilizaremos para medir la humedad relativa del aire.

Para medir la temperatura con mayor exactitud, voy a emplear un sensor LM35 de National Semiconductor.

El LM35 es un sensor de temperatura de precisión, con una exactitud de 0.5ºC, es lineal en la respuesta y tiene la ventaja de estar directamente calibrado en grados centígrados.

Antes de continuar, una observación: el LM35 se conecta inversamente, es decir, la patilla GND se conecta a +5V y la patilla +VS se conecta a GND. Esto es porque a grandes rasgos el sensor es un diodo zener y si lo conectamos atendiendo al nombre de las patillas lo que estamos haciendo en polarizar directamente el diodo y provocando un cortocircuito en la alimentación, lo que puede dañar la fuente o el puerto USB de nuestro ordenador.

Vista superior y frontal del display montado sobre un soporte.

Esquema

Como veis en el esquema, el LM35 se conecta inversamente. La salida se conecta directamente a la entrada analógica A1 del Arduino.

El sensor DHT11 se conecta de igual forma que en ejemplos anteriores.

El trimmer de 100K es para ajustar la tensión de referencia del canal analógico a 1.0 voltios. Esto es porque el sensor LM35 proporciona una salida máxima de 1.0 voltios, por lo que si dejamos que aRef sea 5V estamos perdiendo exactitud en la medida.

Recordad que Arduino tiene un conversor A/D en las entradas analógicas. Por defecto convierte los 0-5 voltios de variación a la entrada analógica en un valor digital 0-1023. Como el LM35 varía entre 0 y 1V, estamos perdiendo mucha información, por eso modificamos el valor del pin Analog Reference a 1.0 voltios.

Puedes consultar una explicación más detallada aquí.

Para alimentar el circuito he utilizado una fuente externa de 5V. Se puede utilizar un cargador de móvil. Cuanto más próxima sea la tensión a 5V, más exactas serán las medidas.

Sketch

He programado el sketch de forma que la información se muestra en tres pantallas. La primera muestra el día de la semana y la fecha, tras 5 segundos se muestra la hora y la temperatura y a continuación la hora y la humedad relativa.

/*

Precision Thermometer, Digital Clock-Day-Date-Humidity sensor 

with Arduino, RTC DS3231SN, DHT11 Humidity sensor, LM35 Temp. sensor

& VFD Module ISE ELECTRONICS CU169SCPB-T21A

Angel M. https://sites.google.com/site/angmuz

  Domingo   19 Jun

  13:14   T:24.3ºC

  13:14   H:32%

 

 

*/

#include "DHT.h"

#include <Wire.h>

#include "ds3231.h"

#define DHTPIN 3     // DHT11 connected to pin 3

#define DHTTYPE DHT11  

DHT dht(DHTPIN, DHTTYPE);

#include <SoftwareSerial.h>

SoftwareSerial VFD(6,7); // RX, TX. Pin 6 not connected

#define BUFF_MAX 128

uint8_t time[8];

char recv[BUFF_MAX];

unsigned int recv_size = 0;

unsigned long prev, interval = 1000;

// parametros para LM35

float tempC;

int reading;

int tempPin = 1;

int media;

int cnt;

void setup()

{

    analogReference(EXTERNAL); // set aRef to 1.0V

    Serial.begin(9600);

    VFD.begin(19200);

    resetVFD();  

    VFDclearsceen();

    VFD.write(0x16);  // underline cursor off

    Wire.begin();

    DS3231_init(DS3231_INTCN);

    memset(recv, 0, BUFF_MAX);

    dht.begin();

  //parse_cmd("TssmmhhDddmmaaaa",16);

  //parse_cmd("T001523715052016",16); // RTC setup, 

}

void resetVFD()

// performs a software reset on the VFD controller

{

  VFD.write(0x1B); // ESC

  VFD.write(0x49); // software reset

}

void VFDclearsceen()

// moves cursor to top-left and clears display

{

  VFD.write(0x0E); // clear display 

  VFD.write(0x0C); // form feed - cursor to top-left

}

void moveCursor(byte position)

// moves the cursor - top row is 0~15

// vertical scroll mode must be turned off if used

{

    VFD.write(0x1B); // ESC

    VFD.write(0x48); // move cursor 

    VFD.write(position); // location

}

void loop()

{  

    int humedad = dht.readHumidity();

    char in;

    char buff[BUFF_MAX];

    unsigned long now = millis();

    struct ts t;

     

  

    // show time once in a while

    if ((now - prev > interval) && (Serial.available() <= 0)) {

        DS3231_get(&t); //Get time

        parse_cmd("C",1);

        

        // dia de la semana

        VFDclearsceen();

        moveCursor(0);

        printDay(t.wday);

        

        // dia del mes, mes

        VFD.print(t.mday);

        printMonth(t.mon);

        delay(5000);

        

    // check if returns are valid, if they are NaN (not a number) then something went wrong!

    if (isnan(humedad)) {

      VFDclearsceen();

      moveCursor(0);

      VFD.print("Failer read DHT");

    } else {

     

      // calculo de la temperatura. con LM35

      

      media=0;  // inicializamos el valor de la media

      

      // medimos la temperatura 10 veces y la almacenamos en tmedia

      for ( cnt=0; cnt<10; cnt++)

      {

        reading = analogRead(tempPin);

        media=media+reading;

        delay(100);

      }

    

      // calculamos la media de las medidas

      media=media/cnt; 

      tempC = media / 10.24;

      

      // hora y temperatura

      VFDclearsceen();

      if(t.hour<10)

      {

        moveCursor(1);

        VFD.print(t.hour);

        VFD.print(":");

      }

      else

      {

        moveCursor(0);

        VFD.print(t.hour);

        VFD.print(":");

      }

      if(t.min<10)

        {

          VFD.print("0");

        }

      VFD.print(t.min);

      

      // temperatura 

      moveCursor(7);

      VFD.print(" T:");

      VFD.print(tempC,1);

      VFD.print((char)149);

      VFD.print("C");

      delay(500);

      

      // the two dots of the hour blink each 500ms

      for (int z=0; z<6; z++){

        moveCursor(2);

        VFD.print(" ");

        delay(500);

        moveCursor(2);

        VFD.print(":");        

        delay(500);

        }

      

      // hora y humedad

      VFDclearsceen();

      if(t.hour<10)

      {

        moveCursor(1);

        VFD.print(t.hour);

        VFD.print(" ");

      }

      else

      {

        moveCursor(0);

        VFD.print(t.hour);

        VFD.print(" ");

      }

      if(t.min<10)

        {

          VFD.print("0");

        }

      VFD.print(t.min);

      

      // Humedad

      moveCursor(7);

      VFD.print(" H:");

      VFD.print(humedad);

      VFD.print("%");

      delay(500);

      

      // the two dots of the hour blink each 500ms

      for (int z=0; z<6; z++){

        moveCursor(2);

        VFD.print(":");

        delay(500);

        moveCursor(2);

        VFD.print(" ");        

        delay(500);

        }

    } 

        prev = now;

        

  }

    

    if (Serial.available() > 0) {

        in = Serial.read();

        if ((in == 10 || in == 13) && (recv_size > 0)) {

            parse_cmd(recv, recv_size);

            recv_size = 0;

            recv[0] = 0;

        } else if (in < 48 || in > 122) {;       // ignore ~[0-9A-Za-z]

        } else if (recv_size > BUFF_MAX - 2) {   // drop lines that are too long

            // drop

            recv_size = 0;

            recv[0] = 0;

        } else if (recv_size < BUFF_MAX - 2) {

            recv[recv_size] = in;

            recv[recv_size + 1] = 0;

            recv_size += 1;

        }

    }

}

void parse_cmd(char *cmd, int cmdsize)

{

    uint8_t i;

    uint8_t reg_val;

    char buff[BUFF_MAX];

    struct ts t;

    //snprintf(buff, BUFF_MAX, "cmd was '%s' %d\n", cmd, cmdsize);

    //Serial.print(buff);

    // TssmmhhWDDMMYYYY aka set time

    if (cmd[0] == 84 && cmdsize == 16) {

        //T355720619112011

        t.sec = inp2toi(cmd, 1);

        t.min = inp2toi(cmd, 3);

        t.hour = inp2toi(cmd, 5);

        t.wday = inp2toi(cmd, 6);

        t.mday = inp2toi(cmd, 8);

        t.mon = inp2toi(cmd, 10);

        t.year = inp2toi(cmd, 12) * 100 + inp2toi(cmd, 14);

        DS3231_set(t);

        Serial.println("OK");

    } else if (cmd[0] == 49 && cmdsize == 1) {  // "1" get alarm 1

        DS3231_get_a1(&buff[0], 59);

        Serial.println(buff);

    } else if (cmd[0] == 50 && cmdsize == 1) {  // "2" get alarm 1

        DS3231_get_a2(&buff[0], 59);

        Serial.println(buff);

    } else if (cmd[0] == 51 && cmdsize == 1) {  // "3" get aging register

        Serial.print("aging reg is ");

        Serial.println(DS3231_get_aging(), DEC);

    } else if (cmd[0] == 65 && cmdsize == 9) {  // "A" set alarm 1

        DS3231_set_creg(DS3231_INTCN | DS3231_A1IE);

        //ASSMMHHDD

        for (i = 0; i < 4; i++) {

            time[i] = (cmd[2 * i + 1] - 48) * 10 + cmd[2 * i + 2] - 48; // ss, mm, hh, dd

        }

        byte flags[5] = { 0, 0, 0, 0, 0 };

        DS3231_set_a1(time[0], time[1], time[2], time[3], flags);

        DS3231_get_a1(&buff[0], 59);

        Serial.println(buff);

    } else if (cmd[0] == 66 && cmdsize == 7) {  // "B" Set Alarm 2

        DS3231_set_creg(DS3231_INTCN | DS3231_A2IE);

        //BMMHHDD

        for (i = 0; i < 4; i++) {

            time[i] = (cmd[2 * i + 1] - 48) * 10 + cmd[2 * i + 2] - 48; // mm, hh, dd

        }

        byte flags[5] = { 0, 0, 0, 0 };

        DS3231_set_a2(time[0], time[1], time[2], flags);

        DS3231_get_a2(&buff[0], 59);

        Serial.println(buff);

    } else if (cmd[0] == 67 && cmdsize == 1) {  // "C" - get temperature register

        Serial.print("temperature reg is ");

        Serial.println(DS3231_get_treg(), DEC);

    } else if (cmd[0] == 68 && cmdsize == 1) {  // "D" - reset status register alarm flags

        reg_val = DS3231_get_sreg();

        reg_val &= B11111100;

        DS3231_set_sreg(reg_val);

    } else if (cmd[0] == 70 && cmdsize == 1) {  // "F" - custom fct

        reg_val = DS3231_get_addr(0x5);

        Serial.print("orig ");

        Serial.print(reg_val,DEC);

        Serial.print("month is ");

        Serial.println(bcdtodec(reg_val & 0x1F),DEC);

    } else if (cmd[0] == 71 && cmdsize == 1) {  // "G" - set aging status register

        DS3231_set_aging(0);

    } else if (cmd[0] == 83 && cmdsize == 1) {  // "S" - get status register

        Serial.print("status reg is ");

        Serial.println(DS3231_get_sreg(), DEC);

    } else {

        Serial.print("unknown command prefix ");

        Serial.println(cmd[0]);

        Serial.println(cmd[0], DEC);

    }

}

void printMonth(int month)

{

  switch(month)

  {

    case 1: VFD.print(" Ene");break;

    case 2: VFD.print(" Feb");break;

    case 3: VFD.print(" Mar");break;

    case 4: VFD.print(" Abr");break;

    case 5: VFD.print(" May");break;

    case 6: VFD.print(" Jun");break;

    case 7: VFD.print(" Jul");break;

    case 8: VFD.print(" Ago");break;

    case 9: VFD.print(" Sep");break;

    case 10: VFD.print(" Oct");break;

    case 11: VFD.print(" Nov");break;

    case 12: VFD.print(" Dic");break;

    default: VFD.print(" Error");break;

  } 

}

void printDay(int wday)

{

  switch(wday)

  {

    case 1: VFD.print("Lunes     ");break;

    case 2: VFD.print("Martes    ");break;

    case 3: VFD.print("Miercoles ");break;

    case 4: VFD.print("Jueves    ");break;

    case 5: VFD.print("Viernes   ");break;

    case 6: VFD.print("Sabado    ");break;

    case 7: VFD.print("Domingo   ");break;

    default: VFD.print("Error    ");break;

  } 

}


Una imagen del sketch en funcionamiento.

Links:

Tutorial about the VFD Modules, by John Boxall

Arduino library for DS3231 RTC, by Petre Rodan 

LM35 Higher Resolution, Arduino Playground

Descarga el proyecto en un .zip aquí abajo. Incluye el esquema, sketch y el datasheet LM35.