Proyecto 22 - Alarmas con RTC´s

17 de mayo de 2015

En este tutorial vamos a programar alarmas con el módulo RTC DS3231.

Este RTC tiene dos alarmas programables que se pueden configurar para que se activen cada segundo, minuto, hora, día de la semana o cada día del mes.

En el primer ejemplo veremos una alarma sencilla que se activa a una hora exacta -hora, minutos y segundos- todos los días.

En el segundo ejemplo la alarma se puede configurar para que se dispare un día concreto de la semana, o varios días, por ejemplo de lunes a viernes.

En ambos ejemplos la alarma activa la salida 13 donde hay conectado un zumbador piezo eléctrico por simplificar.

Pero quien activa un zumbador puede activar un relé, un mosfet, un módulo bluetooth o por infrarrojos...cada uno encontrará la aplicación que necesite al proyecto aquí descrito.

Proyecto 22a - Alarma diaria con RTC

Este ejemplo esta sacado del la librería de Petre Rodan -ver Proyecto 21 para más info- al que he añadido un zumbador piezo eléctrico para ver el funcionamiento de la alarma.

Esquema

Sketch

// Programar una alarma a una hora determinada

//

// during an alarm the INT pin of the RTC is pulled low

//

// this is handy for minimizing power consumption for sensor-like devices, 

// since they can be started up by this pin on given time intervals.

int piezo = 13; // conect a piezo buzzer on pin 13

#include <Wire.h>

#include "ds3231.h"

#define BUFF_MAX 256

// time when to wake up

uint8_t wake_HOUR = 7;

uint8_t wake_MINUTE = 00;

uint8_t wake_SECOND = 0;

// how often to refresh the info on stdout (ms)

unsigned long prev = 5000, interval = 5000;

void set_alarm(void)

{

    // flags define what calendar component to be checked against the current time in order

    // to trigger the alarm - see datasheet

    // A1M1 (seconds) (0 to enable, 1 to disable)

    // A1M2 (minutes) (0 to enable, 1 to disable)

    // A1M3 (hour)    (0 to enable, 1 to disable) 

    // A1M4 (day)     (0 to enable, 1 to disable)

    // DY/DT          (dayofweek == 1/dayofmonth == 0)

    uint8_t flags[5] = { 0, 0, 0, 1, 1 };

    // set Alarm1

    DS3231_set_a1(wake_SECOND, wake_MINUTE, wake_HOUR, 0, flags);

    // activate Alarm1

    DS3231_set_creg(DS3231_INTCN | DS3231_A1IE);

}

void setup()

{

    Serial.begin(9600);

    Wire.begin();

    DS3231_init(DS3231_INTCN);

    DS3231_clear_a1f();

    set_alarm();

    pinMode(piezo, OUTPUT);

}

void loop()

{

    char buff[BUFF_MAX];

    unsigned long now = millis();

    struct ts t;

    // once a while show what is going on

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

        DS3231_get(&t);

        // display current time

        snprintf(buff, BUFF_MAX, "%d.%02d.%02d %02d:%02d:%02d", t.year,

             t.mon, t.mday, t.hour, t.min, t.sec);

        Serial.println(buff);

        // display a1 debug info

        DS3231_get_a1(&buff[0], 59);

        Serial.println(buff);

        if (DS3231_triggered_a1()) {

            // INT has been pulled low

            Serial.println(" -> alarm1 has been triggered");

            // clear a1 alarm flag and let INT go into hi-z

            DS3231_clear_a1f();

            digitalWrite(piezo, HIGH);

        }

        prev = now;

    }

}


Proyecto 22b - Alarma semanal con RTC

El código de este ejemplo es un poco más complejo.

Partiendo del Proyecto 21 he añadido la función alarma del ejemplo anterior. Es decir ahora tenemos el módulo RTC, el display de 16x2 y la alarma todo junto en el mismo código, de esa forma podemos visualizar la hora en curso y podemos verificar como se dispara la alarma a la hora seleccionada.

En el ejemplo la alarma de activa el día en curso a la hora exacta, minutos y segundos. Con ligeras modificaciones se puede programar una alarma semanal de lunes a viernes, en días alternativos, o un día del mes en concreto. Las posibilidades son ilimitadas.

Esquema

Sketch

/*

Test 08. Alarma wday (día de la semana) con display.

El formato es:

              SAB 13 May 2015

             2:05:15  26.8ºC

*/

#include <Wire.h>

#include "ds3231.h"

#include <LiquidCrystal.h>

int piezo = 13; //conect a piezzo buzzer on pin 13

int offSwitch = 0;

LiquidCrystal lcd(4, 5, 6, 7, 8, 9); // pins for RS, E, DB4, DB5, DB6, DB7

#define BUFF_MAX 128

// alarm time

uint8_t wake_HOUR = 23;

uint8_t wake_MINUTE = 22;

uint8_t wake_SECOND = 0;

uint8_t wake_DAY = 1;

//int wday;

boolean flagAlarm = 0;

uint8_t time[8];

char recv[BUFF_MAX];

unsigned int recv_size = 0;

// how often to refresh the info on stdout (ms)

unsigned long prev, interval = 1000;

void set_alarm(int wday)

{

    

    // flags define what calendar component to be checked against the current time in order

    // to trigger the alarm - see datasheet

    // A1M1 (seconds) (0 to enable, 1 to disable)

    // A1M2 (minutes) (0 to enable, 1 to disable)

    // A1M3 (hour)    (0 to enable, 1 to disable) 

    // A1M4 (day)     (0 to enable, 1 to disable)

    // DY/DT          (dayofweek == 1/dayofmonth == 0)

    

    uint8_t flags[5] = { 0, 0, 0, 0, 1 };

    

    // set Alarm1

    wake_DAY = wday;  //para fijar la alarma el dia en curso

    DS3231_set_a1(wake_SECOND, wake_MINUTE, wake_HOUR, wake_DAY, flags);

    // activate Alarm1

    DS3231_set_creg(DS3231_INTCN | DS3231_A1IE);

    flagAlarm = 1; // para que la alarma se active una sola vez

    //mostramos por pantalla día y hora de la alarma

    

    lcd.clear();

    lcd.setCursor(0,0);

    lcd.print("Alarm set to:");

    lcd.setCursor(0,1);

    printDay(wday);

    lcd.print(" ");

    lcd.print(wake_HOUR);

    lcd.print(":");

    lcd.print(wake_MINUTE);

    lcd.print(":");

    if(wake_SECOND<10)

        {

          lcd.print("0");

        }

    lcd.print(wake_SECOND);

    delay(5000);

    

    /*

    otra forma de programamar la alarma 

    para que se active de lunes a viernes

    

    if  (wday <=5){

    wake_DAY = wday;

    DS3231_set_a1(wake_SECOND, wake_MINUTE, wake_HOUR, wake_DAY, flags);

    // activate Alarm1

    DS3231_set_creg(DS3231_INTCN | DS3231_A1IE);

    flagAlarm = 1;

    }

    */

    

}

void setup()

{

    Serial.begin(9600);

    Wire.begin();

    DS3231_init(DS3231_INTCN);

    memset(recv, 0, BUFF_MAX);

    Serial.println("GET time");

    lcd.begin(16, 2);

    lcd.clear();

    DS3231_clear_a1f();

    //set_alarm(wday);

    pinMode(piezo, OUTPUT);

    pinMode(11, INPUT);  // conect the off Switch on pin 11

        

    // Serial.println("Setting time");

    // (seconds,minutes,hour,week day,date, month,year)

    // the first day of the week is monday=1

    // parse_cmd("T004600501052015",16);

}

void loop()

{

    char in;

    char tempF[6]; 

    float temperature;

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

        temperature = DS3231_get_treg(); //Get temperature

        dtostrf(temperature, 5, 1, tempF);

        lcd.clear();

        lcd.setCursor(0,0);

        

        printDay(t.wday);

        

        lcd.print(t.mday);

        

        printMonth(t.mon);

        

        lcd.print(t.year);

        

        lcd.setCursor(0,1); //Go to second line of the LCD Screen

        lcd.print(t.hour);

        lcd.print(":");

        if(t.min<10)

        {

          lcd.print("0");

        }

        lcd.print(t.min);

        lcd.print(":");

        if(t.sec<10)

        {

          lcd.print("0");

        }

        lcd.print(t.sec);

        

        lcd.print(' ');

        lcd.print(tempF);

        lcd.print((char)223);

        lcd.print("C ");

        prev = now;

        

        if (flagAlarm == 0 ){

        set_alarm(t.wday);

        }

    

    }

    

    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;

        }

    }

    

    if (DS3231_triggered_a1()) {

            // INT has been pulled low

            Serial.println(" -> alarm1 has been triggered");

            // clear a1 alarm flag and let INT go into hi-z

            DS3231_clear_a1f();

            digitalWrite(piezo, HIGH);

            

        }

    //press the off switch to turn off the alarm

    offSwitch = digitalRead(11);

    if (offSwitch == HIGH) {

          digitalWrite(piezo, LOW);

          flagAlarm = 0;

          }

}

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: lcd.print(" Ene ");break;

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

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

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

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

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

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

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

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

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

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

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

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

  } 

}

void printDay(int wday)

{

      

  switch(wday)

  {

    case 1: lcd.print(" Lun ");break;

    case 2: lcd.print(" Mar ");break;

    case 3: lcd.print(" Mie ");break;

    case 4: lcd.print(" Jue ");break;

    case 5: lcd.print(" Vie ");break;

    case 6: lcd.print(" Sab ");break;

    case 7: lcd.print(" Dom ");break;

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

  } 

}