RTC DS3231 impostare allarmi

Salve,
ho bisogno di programmare alcuni relè ad orari diversi ed ho pensato di utilizzare un RTC DS3231.

So che il DS3231 gestisce solo due allarmi ed ho provato diverse librerie ma non ancora ne vengo a capo.
Ho bisogno che i relè si accendano ad una determinata ora del giorno spegnendosi ad una certa ora della notte.
In realtà i relè sono quattro ma hanno più o meno le stesse funzioni ed uno in particolare funziona al contrario dell'altro; Qui non è un problema perchè una volta eccitato l'uno metto su LOW l'altro e vice versa.
Due devono accendersi di mattina e due di notte.
Chi mi aiuta con un consiglio su dove indirizzarmi per gestire al meglio l'RTC?
Grazie.

Per ora sono riuscito ad arrivare a questo punto:

/*
   Esempio per accensione ed spegnimento relè temporizzati
*/
#include <TimeLib.h>
#include <TimeAlarms.h>
#include <LiquidCrystal_I2C.h>
#include <DS3232RTC.h>
#include <Wire.h>

AlarmId id;

// lcd object
LiquidCrystal_I2C lcd(0x27, 16, 2); // set the LCD address to 0x27 for a 16 chars and 2 line display

//================================= DEFINISCO LE VARIABILI DEI RELE' ====================================
const int releCO2 = 8;         //relè CO2                   R1 NC
const int releAereatore = 9; //relè Aereatore           R2 NA
const int releTermo = 10;    //relè Temoriscaldatore R3 NA
const int relePlafo = 12;     //relè Plafoniera            R4 NA


void setup() {
  Serial.begin(57600);
  while (!Serial) ; // wait for Arduino Serial Monitor


  setSyncProvider(RTC.get);  // the function to get the time from the RTC
  if (timeStatus() != timeSet)
    Serial.println("Unable to sync with the RTC");
  else
    Serial.println("RTC has set the system time");

  //setTime(8, 29, 40, 1, 1, 11); // set time to Saturday 8:29:00am Jan 1 2011

  lcd.init();
  lcd.clear();
  lcd.backlight();

  pinMode(releCO2 , OUTPUT);
  digitalWrite(releCO2 , HIGH);
  pinMode(releAereatore , OUTPUT);
  digitalWrite(releAereatore , HIGH);
  pinMode(releTermo , OUTPUT);
  digitalWrite(releTermo , HIGH);
  pinMode(relePlafo , OUTPUT);
  digitalWrite(relePlafo , HIGH);

  // create the alarms, to trigger at specific times
  Alarm.alarmRepeat(17, 00, 00, CO2); // 17:00:00 every day
  Alarm.alarmRepeat(18, 00, 00, Aereatore); // 18:00:00 every day

  digitalClockDisplay();
}

void loop() {
  printTime();
  Alarm.delay(1000); // wait one second between clock display
}

// functions to be called when an alarm triggers:
void CO2() {
  Serial.println("Alarm: accendo CO2");
  lcd.setCursor(0, 0);
  lcd.print("CO2:ON Aereatore:OFF");
  digitalWrite(releCO2 , LOW);
}

void Aereatore() {
  lcd.setCursor(0, 0);
  lcd.print("CO2:OFF Aereatore:OFF");
  Serial.println("Alarm: spengo CO2");
  digitalWrite(releCO2 , HIGH);
  delay(2000);

  lcd.setCursor(0, 0);
  lcd.print("CO2:OFF Aereatore:ON");
  Serial.println("Alarm: accedo aereatore");
  digitalWrite(releAereatore , LOW);
}

void p2dig(uint8_t v)
// Print 2 digits with leading zero
{
  if (v < 10) lcd.print("0");
  lcd.print(v);
}

void printDigits(int digits) {
  Serial.print(":");
  if (digits < 10)
    Serial.print('0');
  Serial.print(digits);
}

const char *dow2String(uint8_t code)
// Day of week to string. DOW 1=Sunday, 0 is undefined
{
  static const char *str[] = {"---", "Dom", "Lun", "Mar", "Mer", "Gio", "Ven", "Sab"};

  if (code > 7) code = 0;
  return (str[code]);
}

void printTime(void)
// Display the current time on the display
{
  // display the current time
  lcd.setCursor(0, 1);
  lcd.print (hour());
  lcd.print(":");
  p2dig(minute());
  lcd.print(":");
  p2dig(second());
}

void digitalClockDisplay() {
  // digital clock display of the time
  Serial.print(hour());
  printDigits(minute());
  printDigits(second());
  Serial.print(' ');
  Serial.print(day());
  Serial.print('/');
  Serial.print(month());
  Serial.print('/');
  Serial.print(year());
  Serial.println();
  Alarm.delay(1000); // wait one second between clock display
}

I problema però è che se va via la luce o carico lo sketch dopo gli allarmi questi, non si attivano.
Mi spiego meglio; Se carico lo sketch alle 16:00 tutto funziona correttamente; Se invece lo carico alle 17:30 il relè CO2 non si eccita per (alle 18:00 tutto funzionerà correttamente); Se lo carico alle 18:01 non accadrà nulla.
La domanda è: come faccio a far “leggere” l’allarme in qualunque momento?[/code]

semplice, non fai
non è che "legge" l'allarme, esegue un comando (OK OK una funzione passata come indirizzo)
dicevo esegue una funzione a quell'ora, e non ad altre
se vuoi che un certo coso stia acceso tra un'ora e un'altra "anche" se all'inizio del periodo arduino è spento devi fare differente
Consiglio
se ora è piu' di inizio e meno di fine
traducilo prima in inglese e poi in 'C' vedrai che è meno complicato di quanto si puo' pensare

Standardoil:
semplice, non fai
non è che "legge" l'allarme, esegue un comando (OK OK una funzione passata come indirizzo)
dicevo esegue una funzione a quell'ora, e non ad altre
se vuoi che un certo coso stia acceso tra un'ora e un'altra "anche" se all'inizio del periodo arduino è spento devi fare differente
Consiglio
se ora è piu' di inizio e meno di fine
traducilo prima in inglese e poi in 'C' vedrai che è meno complicato di quanto si puo' pensare

Così facendo ovviamente credo sia inutile utilizzare la funzione Alarm.alarmRepeat della libreria TimeAlarms.
Inizia però il problema di come memorizzare inizio e fine, entrando in una bolgia di clcoli con i secondi o con Unix Time.
In realtà cercavo un modo meno complicato per non uscire matto.

Provando così funziona:

/*
   Esempio per accensione ed spegnimento relè temporizzati
*/
#include <TimeLib.h>
#include <TimeAlarms.h>
#include <LiquidCrystal_I2C.h>
#include <DS3232RTC.h>
#include <Wire.h>

AlarmId id;

// lcd object
LiquidCrystal_I2C lcd(0x27, 16, 2); // set the LCD address to 0x27 for a 16 chars and 2 line display

//================================= DEFINISCO LE VARIABILI DEI RELE' ====================================
const int releCO2 = 8;       //relè CO2              R1 NC
const int releAereatore = 9; //relè Aereatore        R2 NA
const int releTermo = 10;    //relè Temoriscaldatore R3 NA
const int relePlafo = 12;    //relè Plafoniera       R4 NA




void setup() {
  Serial.begin(57600);
  while (!Serial) ; // wait for Arduino Serial Monitor


  setSyncProvider(RTC.get);  // the function to get the time from the RTC
  if (timeStatus() != timeSet)
    Serial.println("Unable to sync with the RTC");
  else
    Serial.println("RTC has set the system time");

  //setTime(8, 29, 40, 1, 1, 11); // set time to Saturday 8:29:00am Jan 1 2011

  lcd.init();
  lcd.clear();
  lcd.backlight();

  pinMode(releCO2 , OUTPUT);
  digitalWrite(releCO2 , HIGH);
  pinMode(releAereatore , OUTPUT);
  digitalWrite(releAereatore , HIGH);
  pinMode(releTermo , OUTPUT);
  digitalWrite(releTermo , HIGH);
  pinMode(relePlafo , OUTPUT);
  digitalWrite(relePlafo , HIGH);

  // create the alarms, to trigger at specific times
  Alarm.alarmRepeat(18, 43, 0, CO2); // 18:430 every day
  Alarm.alarmRepeat(21, 43, 30, Aereatore); // 21:43:00 every day

  digitalClockDisplay();
}

void loop() {
  printTime();
  Alarm.delay(0); // wait one second between clock display
  CO2();
}

// functions to be called when an alarm triggers:
void CO2() {
    if ( (hour() >= 20) || (hour() <= 2) ) {
    Serial.println("Alarm: accendo CO2");
    lcd.setCursor(0, 0);
    lcd.print("CO2:ON AIR:OFF");
    digitalWrite(releCO2 , LOW);
  }
  else {
    Serial.println("Alarm: spengo CO2");
    lcd.setCursor(0, 0);
    lcd.print("CO2:OF AIR:OFF");
    digitalWrite(releCO2 , HIGH);
  }
  /*
    Serial.println("Alarm: accendo CO2");
    lcd.setCursor(0, 0);
    lcd.print("CO2:ON AIR:OFF");
    digitalWrite(releCO2 , LOW);*/
}

void Aereatore() {
  lcd.setCursor(0, 0);
  lcd.print("CO2:OFF AIR:OFF");
  Serial.println("Alarm: spengo CO2");
  digitalWrite(releCO2 , HIGH);
  delay(2000);

  lcd.setCursor(0, 0);
  lcd.print("CO2:OFF AIR:ON ");
  Serial.println("Alarm: accedo aereatore");
  digitalWrite(releAereatore , LOW);
}

void p2dig(uint8_t v)
// Print 2 digits with leading zero
{
  if (v < 10) lcd.print("0");
  lcd.print(v);
}

void printDigits(int digits) {
  Serial.print(":");
  if (digits < 10)
    Serial.print('0');
  Serial.print(digits);
}

const char *dow2String(uint8_t code)
// Day of week to string. DOW 1=Sunday, 0 is undefined
{
  static const char *str[] = {"---", "Dom", "Lun", "Mar", "Mer", "Gio", "Ven", "Sab"};

  if (code > 7) code = 0;
  return (str[code]);
}

void printTime(void)
// Display the current time on the display
{
  // display the current time
  lcd.setCursor(0, 1);
  lcd.print (hour());
  lcd.print(":");
  p2dig(minute());
  lcd.print(":");
  p2dig(second());
}

void digitalClockDisplay() {
  // digital clock display of the time
  Serial.print(hour());
  printDigits(minute());
  printDigits(second());
  Serial.print(' ');
  Serial.print(day());
  Serial.print('/');
  Serial.print(month());
  Serial.print('/');
  Serial.print(year());
  Serial.println();
  Alarm.delay(1000); // wait one second between clock display
}

però non gestisce i minuti :frowning:

Scriviti in italiano la condizione con ore e minuti
Oppure ragiona in minuti moltiplicando per 60 le ore e sommando i minuti
Oppure ragiona in ore e frazioni digitando per 60 i minuti e sommando le ore
Insomma si può fare....

Standardoil:
Scriviti in italiano la condizione con ore e minuti
Oppure ragiona in minuti moltiplicando per 60 le ore e sommando i minuti
Oppure ragiona in ore e frazioni digitando per 60 i minuti e sommando le ore
Insomma si può fare…

Qualcosa non mi torna però con il tuo ragionamento, e quel qualcosa è se imposto la fine dell’allarme ad un orario dopo la mezza notte.
Seguendo il tuo consiglio tutto fila fino alle 23.59 poi tutto sballa.
Faccio un esempio:

T1 - Inizio allarme 14:30 = 860 m
T2 - Fine allarme 21:00 = 1.260 m.
Il controllo da fare sarebbe: IF NOW => T1 AND NOW <= T2 ELSE relè ON

Invece se si imposta la fine dell’allarme dopo la mezza notte ad esempio alle 3:00 = 180m la formula non funziona più perchè se NOW = 23:00 =1.380 m in questo caso NOW non è più piccolo di T2.

Purtroppo è vero
Soluzioni semplici non ne ho

Sarebbe da convertire tutto in Unix Time Stamp però al contrario di NOW non saprei come fare per convertire T1 e T2

invece di consicerare solo ora
T1 - Inizio allarme 14:30 = 860 m
T2 - Fine allarme 21:00 = 1.260 m.
considerare anche data...
T1 - Inizio allarme 2018-05-10-14:30 = ...
T2 - Fine allarme 2018-05-11-03:00 = ...

Patrick_M:
invece di consicerare solo ora
T1 - Inizio allarme 14:30 = 860 m
T2 - Fine allarme 21:00 = 1.260 m.
considerare anche data...
T1 - Inizio allarme 2018-05-10-14:30 = ...
T2 - Fine allarme 2018-05-11-03:00 = ...

In che modo? T1 e T2 vengono impostai una tantum e modificati con un encoder e display molto di rado. Come faccio a capire la data dei due T?

probabilmente bisognerebbe stravolgere un po il codice....

e se allo start dell'allarme imposti una variabile con i minuti mancanti per la fine del periodo? ovvio che se l'orario finale è minore bisogna aggiungere i minuti mancanti alle 00:00 e il tutto vale solo per intervalli inferiori alle 24 ore

Patrick_M:
probabilmente bisognerebbe stravolgere un po il codice....

e se allo start dell'allarme imposti una variabile con i minuti mancanti per la fine del periodo? ovvio che se l'orario finale è minore bisogna aggiungere i minuti mancanti alle 00:00 e il tutto vale solo per intervalli inferiori alle 24 ore

Grazie per la risposta.
In realtà quello scritto è una "bozza" che poi andrà inclusa in un codice più complesso che prevede l'utilizzo di più relè, sonda ds18b20, ed un menù su lcd i2c comandato da encoder rotativo.
Sto scrivendo piccoli pezzi di codice per semplificare il tutto per poi riassemblarli insieme alla fine.
Se ne hai voglia puoi tranquillamente provare la tua teoria e postarmi le modifiche, ne sarei più che onorato. :wink:
P.S: in giornata vedo di condividere anche tutta la parte hardware che, senza presunzione, penso sia venuta molto bella.

Ecco le foto del porgetto.
Per il display volevo utilizzare le custom char per inserire simboli in modo di dare più spazio alle informazioni utili variabili (tipo ora, allarmi e temperature) ma purtoppo non so perchè ma utilizzando la libreria LCDMenuLib.h di Nils Feldkämper non risco a visualizzarli; Questo però è ripagato dal fatto che è una liberia davvero completa che permette di gestire tantissimi display e tante possibilità per comandarli tramite tasti, pulsanti, encoder ed altro che semplifica non poco la vita.

ho dato un’occhiata al readme.txt della libreria degli allarmi e ho trovato questo:

Alarm.timerOnce(Hour, Minute, Second, TimerFunction);
Description: As timerOnce above, but period is the number of seconds in the given Hour, Minute and Second parameters

quindi puoi impostare l’allarme all’orario che vuoi come gia facevi e impostare la durata per l’intervento con la timerOnce che accetta ore, minuti e secondi
quindi seguendo l’esempio

#include <Time.h>
#include <TimeAlarms.h>

void setup()
{
  Serial.begin(9600);
  setTime(8,29,0,1,1,11); // set time to Saturday 8:29:00am Jan 1 2011
  // create the alarms 
  Alarm.alarmRepeat(8,30,0, MorningAlarm);  // 8:30am every day
  Alarm.alarmRepeat(17,45,0,EveningAlarm);  // 5:45pm every day 
  
// 
// nel caso di mancanza corrente
// qui ci va il controllo se siamo in un orario successivo al previsto per l'allarme 
//per l'attivazione/disattivazione dei relè 
// qualche cosa tipo
// if (oraAdesso > oraPrimoAllarme && oraAdesso < oraFineAllarme)
//    attiva/disattiva relè;


}
void  loop(){  
  digitalClockDisplay();    //fai quello che serve
  Alarm.delay(1000);     // wait one second between clock display
}

// functions to be called when an alarm triggers:
void MorningAlarm(){
  Serial.println("Alarm: - accendi relè 1");
// esegui le varie istruzioni
Alarm.timerOnce(Hour, Minute, Second, EndFunction); // tra quanto tempo in ore, minuti, secondi intervenire
}

void EndFunction() {
   serial.println("Spengo relè 1");
// esegui le varie istruzioni

}

Quel parametro mi era sfuggito, grazie!
Devo però studiarmela attentamente e con la mente più lucida perchè in questo momento sono fuso :confused:

Della parte hardware cosa ne pensi? :slight_smile:

Intervengo un po' tardi ma.... il fatto che il DS3231 gestisce solo 2 allarmi non è mica un problema.
Ti fai una lista degli orari e imposti il primo elemento come allarme.
Quando scatta l'allarme esegui ciò che devi fare, quindi prendi il secondo elemento e lo imposti come allarme.
E così via.

Patrick_M:
ho dato un’occhiata al readme.txt della libreria degli allarmi e ho trovato questo:

Alarm.timerOnce(Hour, Minute, Second, TimerFunction);
Description: As timerOnce above, but period is the number of seconds in the given Hour, Minute and Second parameters

quindi puoi impostare l’allarme all’orario che vuoi come gia facevi e impostare la durata per l’intervento con la timerOnce che accetta ore, minuti e secondi
quindi seguendo l’esempio

#include <Time.h>

#include <TimeAlarms.h>

void setup()
{
 Serial.begin(9600);
 setTime(8,29,0,1,1,11); // set time to Saturday 8:29:00am Jan 1 2011
 // create the alarms
 Alarm.alarmRepeat(8,30,0, MorningAlarm);  // 8:30am every day
 Alarm.alarmRepeat(17,45,0,EveningAlarm);  // 5:45pm every day
 
//
// nel caso di mancanza corrente
// qui ci va il controllo se siamo in un orario successivo al previsto per l’allarme
//per l’attivazione/disattivazione dei relè
// qualche cosa tipo
// if (oraAdesso > oraPrimoAllarme && oraAdesso < oraFineAllarme)
//    attiva/disattiva relè;

}
void  loop(){  
 digitalClockDisplay();    //fai quello che serve
 Alarm.delay(1000);     // wait one second between clock display
}

// functions to be called when an alarm triggers:
void MorningAlarm(){
 Serial.println(“Alarm: - accendi relè 1”);
// esegui le varie istruzioni
Alarm.timerOnce(Hour, Minute, Second, EndFunction); // tra quanto tempo in ore, minuti, secondi intervenire
}

void EndFunction() {
  serial.println(“Spengo relè 1”);
// esegui le varie istruzioni

}

Grazie mille per avermi illuminato! Lo sketch inizia a prendere la strada giusta. Peccato però che se va via la corrente non considera l’allarme. Faccio un esempio allegando il codice aggiornato secondo le tue indcazioni:

/*
   Esempio per accensione ed spegnimento relè temporizzati
*/
#include <TimeLib.h>
#include <TimeAlarms.h>
#include <LiquidCrystal_I2C.h>
#include <DS3232RTC.h>
#include <Wire.h>

AlarmId id;

// lcd object
LiquidCrystal_I2C lcd(0x27, 16, 2); // set the LCD address to 0x27 for a 16 chars and 2 line display

//================================= DEFINISCO LE VARIABILI DEI RELE' ====================================
const int releCO2 = 8;       //relè CO2             R1 NC
const int relePlafo = 9;    //relè Plafoniera       R4 NA
const int releTermo = 10;   //relè Temoriscaldatore R3 NA
const int releAereatore = 12; //relè Aereatore      R2 NA


#define RelayOn LOW
#define RelayOff HIGH


void setup() {
  Serial.begin(57600);
  while (!Serial) ; // wait for Arduino Serial Monitor


  setSyncProvider(RTC.get);  // the function to get the time from the RTC
  if (timeStatus() != timeSet)
    Serial.println("Unable to sync with the RTC");
  else
    Serial.println("RTC has set the system time");

  //setTime(01, 59, 30, 3, 24, 18); // set time to Saturday 8:29:00am Jan 1 2011

  lcd.init();
  lcd.clear();
  lcd.backlight();

  digitalWrite(releCO2 , RelayOff);
  pinMode(releCO2 , OUTPUT);
  digitalWrite(releAereatore , RelayOff);
  pinMode(releAereatore , OUTPUT);
  digitalWrite(releTermo , RelayOff);
  pinMode(releTermo , OUTPUT);
  digitalWrite(relePlafo , RelayOff);
  pinMode(relePlafo , OUTPUT);

  // create the alarms, to trigger at specific times
  Alarm.alarmRepeat(23, 1, 0, CO2); // 18:430 every day
  Alarm.alarmRepeat(23, 3, 30, Plafo); // 21:43:00 every day

  digitalClockDisplay();
}

void loop() {
  printTime();
  Alarm.delay(0); // wait one second between clock display
  //CO2();
}

// functions to be called when an alarm triggers:
void CO2() {
  Serial.println("Alarm: accendo CO2");
  lcd.setCursor(0, 0);
  lcd.print("CO2:ON AIR:OFF");
  digitalWrite(releCO2 , RelayOn); // Accendo CO2
  digitalWrite(releAereatore , RelayOff); // Spengo Aereatore
  Alarm.timerOnce(1, 0, 00, EndCO2); // tra quanto tempo in ore, minuti, secondi intervenire
}

void EndCO2() {
  Serial.println("Spengo CO2");
  lcd.setCursor(0, 0);
  lcd.print("CO2:OFF AIR:OFF");
  digitalWrite(releCO2 , RelayOff); // Spengo CO2
  digitalWrite(releAereatore , RelayOn); //Accendo Aereatore
}


void Plafo() {
  lcd.setCursor(0, 0);
  lcd.print("PLAFO:ON                 ");
  Serial.println("Alarm: Accendo LUCI");
  digitalWrite(relePlafo , RelayOn); // Accendo luci
  Alarm.timerOnce(3, 0, 0, EndPlafo); // tra quanto tempo in ore, minuti, secondi intervenire
}

void EndPlafo() {
  Serial.println("Spengo LUCI");
  lcd.setCursor(0, 0);
  lcd.print("PLAFO:OFF                 ");
  digitalWrite(relePlafo , RelayOff); //Spengo luci
  // esegui le varie istruzioni
}

void p2dig(uint8_t v)
// Print 2 digits with leading zero
{
  if (v < 10) lcd.print("0");
  lcd.print(v);
}

void printDigits(int digits) {
  Serial.print(":");
  if (digits < 10)
    Serial.print('0');
  Serial.print(digits);
}

const char *dow2String(uint8_t code)
// Day of week to string. DOW 1=Sunday, 0 is undefined
{
  static const char *str[] = {"---", "Dom", "Lun", "Mar", "Mer", "Gio", "Ven", "Sab"};

  if (code > 7) code = 0;
  return (str[code]);
}

void printTime(void)
// Display the current time on the display
{
  // display the current time
  lcd.setCursor(0, 1);
  p2dig (hour());
  lcd.print(":");
  p2dig(minute());
  lcd.print(":");
  p2dig(second());
}

void digitalClockDisplay() {
  // digital clock display of the time
  Serial.print(hour());
  printDigits(minute());
  printDigits(second());
  Serial.print(' ');
  Serial.print(day());
  Serial.print('/');
  Serial.print(month());
  Serial.print('/');
  Serial.print(year());
  Serial.println();
  Alarm.delay(1000); // wait one second between clock display
}

Secondo questo esempio se carico lo sketch alle ore 23:02:00 il codice non mi tiene conto dell’allarme CO2 impostato alle ore 23:01:00, considerando invece correttamente quello Plafo delle 23:3:30.

Inoltre mi assale anche un altro dubbio. La libreria da me usata gestisce automaticamente il passaggio all’ora solare/legale? Considera automaticamente anche gli anni bisestili?
Grazie.

Secondo questo esempio se carico lo sketch alle ore 23:02:00 il codice non mi tiene conto dell’allarme CO2 impostato alle ore 23:01:00, considerando invece correttamente quello Plafo delle 23:3:30.

infatti non hai tenuto conto di quanto commentato nel setup

void setup() {
   ...
   ...
   // nel caso di mancanza corrente
   // qui ci va il controllo se siamo in un orario successivo al previsto per l'allarme
   //per l'attivazione/disattivazione dei relè
   // qualche cosa tipo
   // if (oraAdesso > oraPrimoAllarme && oraAdesso < oraFineAllarme)
   //    attiva/disattiva relè;
   ...
}

Inoltre mi assale anche un altro dubbio. La libreria da me usata gestisce automaticamente il passaggio all’ora solare/legale? Considera automaticamente anche gli anni bisestili?

non lo so, non ho controllato… ma non mi pare

Patrick_M:
infatti non hai tenuto conto di quanto commentato nel setup

void setup() {


  …
  // nel caso di mancanza corrente
  // qui ci va il controllo se siamo in un orario successivo al previsto per l’allarme
  //per l’attivazione/disattivazione dei relè
  // qualche cosa tipo
  // if (oraAdesso > oraPrimoAllarme && oraAdesso < oraFineAllarme)
  //    attiva/disattiva relè;
  …
}



non lo so, non ho controllato... ma non mi pare

Hai ragione scusa.
Alla tarda ora che l’ho testato ieri sera, preso dal l’euforia che funzionava non ho proprio letto la prima parte!
Chiedo venia.
Per gli anni bisestili ho letto che li gestisce automaticamente ma sarei curioso di verificarlo.