Pianificare eventi ad una specifica dataora con arduino

Salve a tutti.

Volevo chiedervi come poter realizzare una sorta di scheduler con arduino. Mi spiego meglio.

Vorrei fare in modo di fare eseguire alcuni comandi ad arduino impostando un orario inizio, orario di fine.

Per la prima parte ci sono, nel senso, ho per il momento delle variabili statiche che mi rappresentano ora e minuti di inizio , ora e minuti di fine.

Arduino quindi dovrà all'ora di inizio eccitare un rele a stato solido, all'ora di fine dovrà spegnere il rele a stato solido. Ovviamente questo moltiplicato per 4 rele che sono il numero di rele che vorrei comandare.

Detto questo sicuramente per sapere da Arduino quale è l'orario ho bisogno di un modulo DS3231, però, per far si che Arduino scateni la sua azione allo scoccare dell'orario, c'è qualche libreria che potrebbe aiutarmi ? Magari è in grado di impostare l'orario di inizio ed ora di fine e questa allo scadere del tempo manda in qualche modo un messaggio ad arduino il quale compierà la sua azione, oppure devo nel LOOP, scrivere il codice e verificare ogni x secondi se sono arrivato o meno all'orario ?

spero di non essere stato confusionario.

La cosa di per se non è difficile, basta leggere l'orario una volta al minuto e prendere le docute decisioni. Quello che vorrei sapere è se Arduino dovrà andare in modalità basso consumo o meno, perché in base a questo il modo di operare è profondamente differente. A mio modo di vedere comunque non ti serve nessuna libreria, sono quattro if se arduno non si spegne e i soliti quattro if più una manciata d'istruzioni se si deve spegnere

Ah gli orari saranno fissi o dovrai riceverli chessò da un display+pulsanti / internet /ecc?

bircastri:
Detto questo sicuramente per sapere da Arduino quale è l'orario ho bisogno di un modulo DS3231, però, per far si che Arduino scateni la sua azione allo scoccare dell'orario, c'è qualche libreria che potrebbe aiutarmi ? Magari è in grado di impostare l'orario di inizio ed ora di fine e questa allo scadere del tempo manda in qualche modo un messaggio ad arduino il quale compierà la sua azione, oppure devo nel LOOP, scrivere il codice e verificare ogni x secondi se sono arrivato o meno all'orario ?

Il DS3231 avrebbe un allarme hardware (o due non ricordo, non li ho mai usati) ma secondo me è una complicazione in più. Nel loop basta continuare a leggere l'RTC, controllare quando cambia il minuto, e in quel momento verificare se è uno degli orari previsti:

Leggi RTC
Se minuto_attuale != minuto_rtc:
    minuto_attuale = minuto_rtc
    Se orario1: azioni1
    Se orario2: azioni2
    Se orario3: azioni3
    Se orario4: azioni4
    ....

Claudio_FF:
Il DS3231 avrebbe un allarme hardware (o due non ricordo, non li ho mai usati)…

Corretto, ne ha due, io li ho usati e non ho avuto problemi.
In realtà ne basta uno; ogni volta che scatta l’allarme corrente, si imposta il successivo, e cosi via…
Naturalmente servirebbe un qualcosa che mantenga associato orario/operazione, magari un array di struttura.

Federico

grazie a tutti per le risposte. Attualmente i relay che vorrei controllare sono 4, non escludo che in un futuro ne siano N.

Attualmente ho creato una funzione per cui se richiamato l'ip a cui è collegato arduino /releTimer, Arduino ti mostra una paginetta web con alcuni campi, qui ci imposti per ogni rele, ora inizio ed ora di fine.
Questo tempo, viene salvato nella eeprom. (Ho omesso di dire che ho con arduino la shied ESP8266).

Detto questo in fase di setup mi andrei a popolare un array di un oggetto strutturato (avevo pensato ad una struttura simile)

class releTimer
{
  int startM;     
  int startH;    
  int endM;     
  int endH;    
   int idRele;
};

A questo punto le alternative che suggerite sono 2.

  1. nel LOOP ciclerei questo array di releTimer, ed andrei a verificare se startM e startH sono maggiori dell ora e minuto corrente. Se vero, faccio partire il rele.

  2. potrei utilizzare gli allarmi della shied DS3231, però mi avete detto essere soltanto 2. Io ho 4 relè i quali potrebbero avere tutti e 4 orari uguali, oppure 4 ore differenti. Se il modulo ha soltanto 2 allarmi non so come poter risolvere.

Sicuramente la 2 mi sembra una soluzione più pulita a livello di codice. Però attualmente non mi viene in mente come poter fare per adattare questa soluzione al mio caso.
La soluzione 1, un pò più complicata da implementare però mi sembra fattibile.

Ad ogni modo mi era stato chiesto se Arduino doveva essere dormiente o meno, rispondo NO, una volta collegato eseguirà il loop infinite volte fino a quando la shied avrà voglia di funzionare. :smiley:

c’è qualche libreria che potrebbe aiutarmi

Non esistono librerie per le cose semplici, una libreria viene creata per “rendere più facile” le cose difficili :slight_smile:

Hai una libreria per leggere ora e minuti dal RTC, perché sarebbe difficilissimo farlo senza, ma poi per il tuo problema ti basta confrontare due numeri che è una cosa elementare :wink:

if(ore==oraInizio && minuti==minutiInizio){

      digitalWrite(rele,HIGH);

}else if(ore==oraFine && minuti==minutiFine){

      digitalWrite(rele,LOW);
}

bircastri:

  1. nel LOOP ciclerei questo array di releTimer, ed andrei a verificare se startM e startH sono maggiori dell ora e minuto corrente. Se vero, faccio partire il rele.

Questa condizione non mi convince molto: se scheduli un'attività alle 10:00 questa ti parte appena scatta la mezzanotte.. Devi, secondo me, invertire la condizione: ore e minuti correnti >= ore e minuti schedulati, ma ancora non basta, perchè anche il questo caso una volta superato l'orario di schedulazione esso ti partirebbe ad ogni minuto fino allo scattare della mezzanotte.

A parte qualche trucco con flag che eviti che lo stesso task venga eseguito due volte nello stesso giorno: potresti pensare di aggiungere anche l'informazione del giorno in cui un certo task deve essere eseguito e poi di volta in volta impostare la schedulazione successiva.

Ciao

if(ore==oraInizio && minuti==minutiInizio){

      digitalWrite(rele,HIGH);

}else if(ore==oraFine && minuti==minutiFine){

      digitalWrite(rele,LOW);
}

Le condizioni "strettamente uguali" applicate a variabili che contengono un dato relativo al tempo sono un rischio: se Arduino è impegnato a fare qualcosa che gli impedisce di leggere il minuto esatto, la condizione non scatta e potrebbe essere un problema.

Vedi elettrovalvola che non chiude e ti si allaga il balcone.. :slight_smile:

ho avuto lo stesso tipo di problema e la soluzione l’ho trovata in questo vecchio post:
operazioni con l’orario

in pratica si tratta di convertire le 24 ore del giorno (e dei relativi eventi,ovvio) in secondi o, nel tuo caso, minuti e confrontarli nel loop…

per la serie:

ora = (rtc.ora*60)+(rtc.min*60)+(rtc.sec)

evento = (ora.evento*60)+(min.evento*60)+(sec.evento)

if(ora == evento){ azione}

if(ora == evento1){ azione1}
if(ora == evento2){ azione2}

e cosi via......

n.b. in questo codice la risoluzione del timer è in secondi, ma puo essere aumentata o diminuita a seconda delle necessità(e ovvio, delle risorse disponibili)

bircastri:
Ad ogni modo mi era stato chiesto se Arduino doveva essere dormiente o meno, rispondo NO, una volta collegato eseguirà il loop infinite volte fino a quando la shied avrà voglia di funzionare. :smiley:

E allora le soluzioni fornite sono più che valide e sufficienti :slight_smile:

paolo311 non penso sia un problema le condizioni perché non andiamo a confrontare i secondi, quindi se confronto ora e minuti, ho tempo sessanta secondi per verificarla, penso che non ci sia rischio che le condizioni non vengono verificate :slight_smile:

bircastri:

Sicuramente la 2 mi sembra una soluzione più pulita a livello di codice. Però attualmente non mi viene in mente come poter fare per adattare questa soluzione al mio caso.
La soluzione 1, un pò più complicata da implementare però mi sembra fattibile.

Non ci sono soluzioni “più pulite” o “più semplici”, tutto dipende dalle capacità e dalle conoscenze di chi scrive (sviluppa).
In questo caso, la soluzione 1 è più semplice, nel senso che è indipendente dall’hardware, al contrario, la soluzione 2 dipende (e sfrutta) le caratteristiche dello specifico RTC.

Personalmente, tendo a sfruttare quello che l’hardware mi mette a disposizione, ma questo implica uno studio approfondito dello stesso per sfruttarne appieno le funzionalità.

In definitiva, scegliere una strada o l’altra, è una tua scelta.

Non so che libreria stai usando per RTC, io mi sono trovato molto bene con questa, ti consiglio comunque di darci un’occhiata (documentazione ed esempi).

Avere solo due allarmi, non è limitativo, anche perché te ne serve uno solo.

La mia proposta a grandi linee è questa:

struct timer_t {
  int startH;   
  int startM;     
  int duration; //in minuti     
  int idRele;
};

byte numTimers = 4;

/lista dei timer/allarmi
struct timer_t timerList[num];

//indice del timer attivo
byte timerCurIndex = 0;

//true: un rele è attivo
//false: nessun rele è attivo
bool releStarted = false;


void setup() {
  \\...
  \\imposto il primo allarme per avvio rele
  RTC.setAlarm(ALM1_MATCH_HOURS, 0, timerList[0].startM, timerList[0].startH, 0);
  RTC.alarm(ALARM_1);
  \\...
}


void loop() {
  \\...
  \\verifico se è scattato un allarme
  if (RTC.alarm(ALARM_1)) {
    if (releStarted) {
      //il rele è attivo, lo spengo
      //STOP timerList[timerCurIndex].idRele
      //e imposto alarme per avvio prossimo rele
      if (timerCurIndex < numTimers - 1) { 
        timerCurIndex++;
      } else {
        timerCurIndex = 0;  
      }
      RTC.setAlarm(ALM1_MATCH_HOURS, 0, timerList[timerCurIndex].startM, timerList[timerCurIndex].startH, 0);
      RTC.alarm(ALARM_1);    
      releStarted = false;
    } else {
      //non ci sono rele attivi, attivo quello corrente
      //START timerList[timerCurIndex].idRele
      //e imposto allarme per spegnimento rele corrente
      RTC.setAlarm(ALM1_MATCH_HOURS, 0, timerList[timerCurIndex].startM + timerList[timerCurIndex].duration, timerList[timerCurIndex].startH, 0);
      RTC.alarm(ALARM_1);
      releStarted = true;
    }
  }  
  \\...
}

E’ solo un’idea e non ho testato se funziona :slight_smile:
Naturalmente devi studiarti la documentazione della libreria di Christensen, che ti ho segnalato.

Federico

[UPDATE]
Scusa, mi sono accorto solo grazie al post di @fabpolli, che se si usa la durata, va sempre calcolata ora e min fine; cosi come l’ho scritta, funziona solo se sommando la durata non si supera l’ora!!
Quindi è più semplice usare ora e min fine.
Scusa ancora.

torn24:
paolo311 non penso sia un problema le condizioni perché non andiamo a confrontare i secondi, quindi se confronto ora e minuti, ho tempo sessanta secondi per verificarla, penso che non ci sia rischio che le condizioni non vengono verificate :slight_smile:

Diciamo che con millis() hai una ragionevole sicurezza di chiedere l'ora corrente all'RTC una volta ogni minuto, ma non hai nessuna percezione su quale secondo stai facendo l'operazione e nemmeno se il minuto calcolato da Arduino corrisponde esattamente al minuto calcolato dall'RTC, se tra i due "minuti" c'è un delta, c'è la possibilità che due rilevazioni consecutive diano lo stesso risultato o che ci sia un salto di 1 minuto tra la prima e la seconda.

Valutare remota la possibilità che queste condizioni si verifichino contemporaneamente, non azzera il rischio, quindi, secondo me, vale la pena architettare la soluzione in modo che sia più flessibile e sicura.

Ciao

paolo311, se lo dovessi fare io un codice simile controllerei l'ora più volte in un minuto, per cui non avrei questo problema :). Ma non ho neanche problema a cambiare la condizione con un >= :slight_smile: :slight_smile:

Io comunque opterei per l'allarme sull'RTC impostato con il valore dell'orario più prossimo e gestirei il tutto tramite interrupt.
nella struttura che gestire orario di inizio e fine, id del relé aggiungerei il suo stato a questo punto nel loop se non è stato impostato l'allarme cerco nella struttura quel'è l'orario più prossimo, lo imposto e memorizzo l'indice della struttura in una variabile, fatto questo aspetto senza far nulla.
Quando scatta l'allarme sull'RTC e invia il segnale al pin d'interrupt nella funzione che lo gestisce setto una variabile per capire che devo eseguire l'azione, il loop leggendo quella variabile sa che dovrà gestire il cambio di stato, sa l'id della struttura su cui deve agire, grazie allo stato gestito in questa attiva o disattiva il relé, aggiorna lo stato e setta una variabile per far scattare la sezione che andrà a ricercare di nuovo l'orario più prossimo e l'indice della struttura ad essso collegato e il tutto ricomincia

fabpolli:
Io comunque opterei per l'allarme sull'RTC impostato con il valore dell'orario più prossimo e gestirei il tutto tramite interrupt.

simile alla mia proposta ::slight_smile:

Federico

Federico66:
simile alla mia proposta ::slight_smile:

Federico

Assolutamente si, con alcune piccole differenze (due orari, tu suggerivi di inserire la durata al posto dell'orario di fine) giusto per fornire un punto di vista differente ma aderente a quanto proponevi. Reputo la tua proposta validissima e più interessante di un polling continuo dell'RTC nel loop è anche per questo che ho deciso di indicare una mia possibile soluzione al problema seppur simile alla tua.

fabpolli:
Assolutamente si, con alcune piccole differenze (due orari, tu suggerivi di inserire la durata al posto dell'orario di fine) ...

Ho consigliato la durata per semplificare, ma in realtà, adesso che mi ci fai pensare non va bene quello che ho scritto! Va comunque sempre calcolata l'ora fine!, quindi meglio avere ora fine, è più semplice! che fare calcoli con l'ora :frowning:

Grazie
Federico

Federico66:
simile alla mia proposta ::slight_smile:

fabpolli:
Assolutamente si, ...

Rileggendo mi sono accorto che il mio post poteva essere interpretato come polemico!
Assolutamente no, la faccina era ironica!!
Scusami se lo sono sembrato.

Federico