Che ore sono? è l'ora della centralina!

Salve a tutti.
Mi sono presentato qualche giorno fa e non vi annoio presentandomi anche qui ma vi ricordo l'essenziale: non sono un eletronico ne un programmatore, solo una persona molto curiosa e che sa fare tante cose.
Tra le cose che non so fare, che sono molte anche quelle, sicuramente in questi ultimi giorni risalta il fatto che io non riesca a impostare come si deve l'ora del mio rtc1307( ho una datalogger della az delivery). Mi spiego meglio: sto progettando una centralina per la coltivazione ( se volete maggiori info guardate la mia presentazione) e sono gia riuscito a gestire temperatura e umidita tramite un dht11, ma con l'ora e la data proprio non ci riesco. L'idea e quella di ipostare l'ora e la data tramite un display i2c di quelli cinesi e una pulsantiera resistiva(qui non so se è il termine è giusto). Ho gia fatto uno sketch incompeto dove ho trovato una soluzione che pero non è bella per niente perche ogni volta che finisco di impostare i parametri devo salvarli su EEPROM.
Quindi l'obbiettivo che mi piacerebbe raggiungere è quello di riuscire a impostare l'ora e la data tramite la pulsantiera e salvare quest'ultima sull rtc ma non riesco a trovare delle funzioni o librerie che mi permettano di fare ciò.
Vi allego tutto lo sketch e vi chiedo umilmente di non screditarmi se vedete delle "maialate" visto che un mese fa non sapevo nemmeno accendere un led a livello hardware. Vi ringrazio in anticipo per la comprensione.

Spippolo_RTC_case.ino (9.99 KB)

Ho provato a postare tutto il codice ma non me lo fa mettere perche è troppo lungo cmq per chi ha dubbi ho allegato tutto lo sketch al primo post. Di seguito metto solo quello che può interessare per la risoluzione del problema:

DateTime Regular=rtc.now();
DateTime setting (Regular.unixtime() + (d * 86400L) + (c * 3600) + (b * 60) + a);

io sul disply stampo setting che non e altro che Regular più giorni, ore, minuti e secondi. le variabili vengono incrementate grazie ad un interfaccia del display, nel seguente modo:

//qui utilizzo la variabile n che a seconda del suo valore, modificabile con i tasti left e right
//sposta il cursore per modificare lora, i minuti o i secondi. il cursore sarebbe un banale lampeggio
   case(Sub1_ImpostaOra):
     String Space = "  ";
     if (n==0){ 
       if ((setblink==1)and (millis()-tempoTD>=500)){
         lcd.clear();
         lcd.print("Modifica orario");
         lcd.setCursor(0,1);
         lcd.print((pad(setting.hour()))+(":")+(pad(setting.minute()))+(":")+(pad(setting.second())));
         tempoTD=millis();
         setblink=!setblink;
       }
       if ((setblink==0)and (millis()-tempoTD>=500)){
         lcd.clear();
         lcd.print("Modifica orario");
         lcd.setCursor(0,1);
         lcd.print((Space)+(":")+(pad(setting.minute()))+(":")+(pad(setting.second())));
         tempoTD=millis();
         setblink=!setblink;
       }
     }
     if (n==1){ 
       if ((setblink==1)and (millis()-tempoTD>=500)){
         lcd.clear();
         lcd.print("Modifica orario");
         lcd.setCursor(0,1);
         lcd.print((pad(setting.hour()))+(":")+(pad(setting.minute()))+(":")+(pad(setting.second())));
         tempoTD=millis();
         setblink=!setblink;
       }
       if ((setblink==0)and (millis()-tempoTD>=500)){
         lcd.clear();
         lcd.print("Modifica orario");
         lcd.setCursor(0,1);
         lcd.print((pad(setting.hour()))+(":")+(Space)+(":")+(pad(setting.second())));
         tempoTD=millis();
         setblink=!setblink;
       }
     }
     if (n==2){ 
       if ((setblink==1)and (millis()-tempoTD>=500)){
         lcd.clear();
         lcd.print("Modifica orario");
         lcd.setCursor(0,1);
         lcd.print((pad(setting.hour()))+(":")+(pad(setting.minute()))+(":")+(pad(setting.second())));
         tempoTD=millis();
         setblink=!setblink;
       }
       if ((setblink==0)and (millis()-tempoTD>=500)){
         lcd.clear();
         lcd.print("Modifica orario");
         lcd.setCursor(0,1);
         lcd.print((pad(setting.hour()))+(":")+(pad(setting.minute()))+(":")+(Space));
         tempoTD=millis();
         setblink=!setblink;
       }
     }
     switch(nkey){
       case KY_DOWN:
         if (n==0){
           c--;
         }
         if (n==1){
           b--;
         }
         if (n==2){
           a--;
         }
         break;
       case KY_UP:
         if (n==0){
           c++;
         }
         if (n==1){
           b++;
         }
         if (n==2){
           a++;
         }
         break;
       case KY_RIGHT:
        if(n!=2){
           n++;
         } else if(n==2){
           n=0;}
         break;
       case KY_LEFT:
         if(n!=0){
           n--;
         } else if(n==0){
           n=2;}
         break;
       case KY_SEL:
         setStato(ImpostaOra);
         n=0;
         break;
     }
   break;

>pronoob94: ti ricordo che in conformità al regolamento, punto 7, devi editare il tuo post (quindi NON scrivendo un nuovo post, ma utilizzando il bottone More -> Modify che si trova in basso a destra del tuo post) e racchiudere il codice all'interno dei tag CODE (... sono quelli che in edit inserisce il bottone con icona fatta così: </>, tutto a sinistra).

In pratica, tutto il tuo codice dovrà trovarsi racchiuso tra due tag: [code] _il _tuo_ codice_ [/code] così da non venire interpretato e non dare adito alla formazione di caratteri indesiderati o cattiva formattazione del testo. Grazie.

Guglielmo

P.S.: Ti ricordo che, purtroppo, fino a quando non avrai sistemato il codice come richiesto, nessuno ti potrà rispondere, quindi ti consiglio di farlo al più presto. :wink:

Grazie per la dritta Guglielmo. Penso di aver descritto l'hardware bene, ma ho omesso che sto lavorando con un arduino UNO. Il display è collegato ai pin SDA ed SCL presenti sulla "shield datalogger della az delivery" mentre la pulsantiera ha la trasmissione dati collegata sul pin A0 Tutto è collegato ai 5v e al GND di arduino, l'hardware dovrebbe essere collegato bene visto che funziona. Se non mi sono spiegato bene ribadisco il problema. In internet non sono riuscito a trovare un modo per impostare l'ora manualmente grazie all'interfaccia offerta da pulsantiera e display. Quello che cerco sono delle funzioni che mi permettono di impostare un orario e poi tramite i bottoni incrementarlo o decrementarlo, ho gia strutturato la macchina a stati e quelli riportati nel post precedente sono dei segmenti rigardanti i punti salienti dove lavorare. Tutti i consigli sono ben accettti e aprezzo anche il minimo sforzo di tutti coloro che dedicano tempo per aiutare me e le altre persone di questo forum.

Le funzioni che cerchi sono solo il risultato di una certa impostazione logica, che in gran parte è corretta (gestione a stati), forse un po' troppo prolissa, probabilmente si può ridurre tutto alla metà. Piuttosto sono da convertire tutte quelle String in stringhe standard C (array di char), altrimenti il sistema si pianta dopo poco tempo causa intasamento della memoria.

Giusto usare una variabile 'stato' per le varie fasi, e una "sottostato" all'interno dei case tipo la 'n' per indicare se stiamo regolando ore minuti o secondi. OK anche la 'first', il cui uso si potrebbe semplificare:

first = (stato != statoPrec);
statoPrec = stato;

switch (stato)
{
    case GestisciOraData:
      if (first) { ... }
      ...      
      ... stato = ...;
      ...
    break;
}

Lampeggio, dieci righe invece di quasi cinquanta....

if (millis()-tempoTD >= 500)
{
    tempoTD = millis();
    setblink = !setblink;
    lcd.setCursor(0,1);
    if (setblink)  { lcd.print(pad("%02d:%02d:%02d", setting.hour(),   setting.minute(), setting.second())); }
    else if (0==n) { lcd.print(pad("  :%02d:%02d",   setting.minute(), setting.second()));  }
    else if (1==n) { lcd.print(pad("%02d:  :%02d",   setting.hour(),   setting.second()));  }
    else if (2==n) { lcd.print(pad("%02d:%02d:  ",   setting.hour(),   setting.minute()));  }
}
char res[9];

char* pad(char* fmt, byte a, byte b, byte c)
{
    sprintf(res, fmt, a, b, c);
    return res;
}
  
char* pad(char* fmt, byte a, byte b)
{
    sprintf(res, fmt, a, b);
    return res;
}

Grazie mille, non pensavo che in un colpo solo si potesse ottenere questi risultati a livello di miglioramento del codice, sono una persona precisa e scrupolosa ma a causa delle mie scarse conoscenze mi tocca arrangiarmi alla meno peggio. Riguardo alla gestione del lampeggio ci sono e a breve studiero la funzione sprintf!(mancavano i % per identificare i formati ma ho risolto, non so come!!).

Riguardo al primo miglioramento mi stai dicendo di rimuovere la funzione setStato? effetivamente la usavo con un delay(300) fino all'altro ieri per gestire la pulsantiera siccome non avevo fatto ancora la funzione che mi rilevava il fronte di salita del pulsante.Ho scelto di rimuovere il delay perche siccome si tratta di una centralina voglio categoricamente evitare le funzioni bloccanti in modo da far essere il tutto più reattivo.

Vedo che non hai fatto commenti riguardo a come io ho imostato il tutto, nel senso che per gestire l'ora non vado a modificare l'ora attuale (perche non mi riesce) ma creo un altro orario (chiamato setting) nella quale incremento e decremento i valori delle costanti legate alle ore, minuti e secondi, per poi in fine salvarle su EEPROM non appena esco dal sub1_ImpostaOra (devo ancora scrivere questo codice ma non ci sono problemi).
è un po bruttina come cosa ma funziona, forse dovrei tenerlo così, che ne dite?

Grazie mille ancora Caludio_FF!!!

Non ho capito esattamente che libreria stai usando, ho fatto solo una rapida ricerca, sorry, ma se non sbaglio dovrebbe avere il metodo adjust che accetta come parametro un DateTime, quindi per aggiornare l'RTC dovrebbe bastare fare:

rtc.adjust(setting);

dopo ogni modifica da pulsante.

Io leggerei il DateTime dall'rtc, farei la modifica di una sola unità dell'elemento corrente, quindi +1 o -1, ad es. ore, e salverei subito sull'rtc.

Maurizio

pronoob94:
Vedo che non hai fatto commenti riguardo a come io ho imostato il tutto, nel senso che per gestire l'ora non vado a modificare l'ora attuale (perche non mi riesce) ma creo un altro orario (chiamato setting) nella quale incremento e decremento i valori delle costanti legate alle ore, minuti e secondi

Il primo motivo del non commento era l'ora/stanchezza :smiley: Poi anche io nella mia sveglia comodino per la regolazione ho usato variabili di appoggio, da scrivere nell'RTC solo in caso di effettiva modifica e conferma. Il codice che ho usato (solo il case della regolazione) è questo, ci sono un po' di assunzioni da fare, la prima è che i valori letti dall'RTC in un altro punto sono conservati in una struttura 'now', che solo alla fine viene aggiornata con i valori modificati e trascritta sull'RTC con la funzione 'setHour'. Ce ne sarebbero anche tante altre ('gmillis' che contiene il valore di 'millis' al ciclo di programma attuale, 't' che è una variabile tempo a 16 bit in grado di conteggiare fino a 65 secondi, 'onUp' 'onDn' 'onClic' 'onLpress' che valgono 1 per un ciclo in seguito alle azioni su un encoder, corrispondenti alle pressioni di equivalenti pulsanti ecc).

uint8_t primo = (fase != fasePrec);
if (primo) { fasePrec = fase;  t = gmillis; }
uint16_t elapsed = (uint16_t)gmillis - t;
switch (fase)
{
    case REGORA:  // REGOLAZIONE ORA-MINUTI
        if (primo)
        {
            s = 0;                   // 0 regola ora, 1 regola minuti
            modify = 0;              // 1 se modificato un valore
            modHour = now.hour;      // variabili modifica temporanee
            modMinute = now.minute;
            displayModHour(s, modHour, modMinute);
        }


        if (elapsed > 60000) { fase = PRINCIPALE;  break; }  // timeout 60s
        else if (onClic)     { beepClic();  t = gmillis;  }
        else if (onUp)       { beepUp();    t = gmillis;  }
        else if (onDn)       { beepDn();    t = gmillis;  }
        else if (onLpress)   { beepLpress();  fase = PRINCIPALE;  break; }


        if (onUp)
        {
            if      (0 == s) { modHour   = (modHour + 1)   % 24;  }
            else if (1 == s) { modMinute = (modMinute + 1) % 60;  }
        }
        else if (onDn)
        {
            if      (0 == s) { modHour   = (modHour > 0)   ? modHour-1   : 23; }
            else if (1 == s) { modMinute = (modMinute > 0) ? modMinute-1 : 59; }
        }


        if (onUp  ||  onDn) { modify = 1;  displayModHour(s, modHour, modMinute); }


        if (onClic  &&  0 == s)        // passa a regolazione minuti
        { 
            s = 1;
            displayModHour(s, modHour, modMinute); 
        }
        else if (onClic  &&  1 == s)   // fine regolazione
        {
            fase = PRINCIPALE; 
            if (modify) { now.hour = modHour;  now.minute = modMinute;  setHour(); }
        }
    break;
}

La struttura now:

struct {
        uint8_t hour = 0;
        uint8_t minute = 0;
        uint8_t second = 0;
        uint8_t w_day = 1;    // 1 = lunedi`
        uint8_t m_day = 1;
        uint8_t month = 1;
        uint8_t year = 0;
} now;

Le funzioni per i beep:

void beepClic(void)   { tone(SPEAKER, 500,  60); }

void beepLpress(void) { tone(SPEAKER, 1000, 60); }

void beepUp(void)     { tone(SPEAKER, 3000, 10); }

void beepDn(void)     { tone(SPEAKER, 2000, 10); }

La 'displayModHour' che non lampeggia ma "sottolinea" il campo in modifica con un carattere disegnato ad hoc:

void displayModHour(uint8_t s, uint8_t hour, uint8_t minute)
{
    lcd.clear();
    lcd.setCursor(0, 0);
    char z[9];
    sprintf(z, "%2d:%02d", hour, minute);
    lcd.print(z);
    if      (0 == s) { lcd.setCursor(0, 1);  lcdindicat(2); }
    else if (1 == s) { lcd.setCursor(3, 1);  lcdindicat(2); }
}

void lcdindicat(uint8_t n)
{
    for (uint8_t i = 0 ; i < n ; i++) { lcd.write((uint8_t)2); }
}

La 'setHour' che non usa librerie RTC ma accede direttamente:

void setHour(void) 
{
    Wire.beginTransmission(0x68);   // indirizzo RTC su bus i2c
    Wire.write(0);                  // punta al registro 0
    Wire.write(0);                  // azzera secondi RTC
    Wire.write(binBCD(now.minute)); // imposta minuti RTC
    Wire.write(binBCD(now.hour));   // imposta ore RTC
    Wire.endTransmission();
}

uint8_t binBCD(uint8_t n) { return ((n / 10) << 4) | (n % 10); }

Grazie maubarzi e Caudio_FF per le risposte!
Mi studio i codici e li provo, appena ho notizie (porbabilemnte positive visto come Claudio_FF mi ha scritto tutto il codice!) vi riagiorno.
Ancora grazie mille, purtroppo in rete non ho trovato niente di tutto ciò o forse non sono stato capacie di scomporre gli esempi per trarne le giuste conoscenze.
Grazie ancora!!!

Ringrazio ancora maubarzi e Caudio_FF per le risposte!
È passato più di qualche giorno ma sono riuscito a far funzionare il tutto, anche se il sub menù per impostare la data è molto brutale (mi accontento, poi ci metterò le mani).
Sorge un nuovo problema, non ho più pin da utilizzare e mi serve collegare una sonda DHT11. Avevo già preparato un codice e dopo averlo riadattato e unito i due codici mi sono accorto che nel pin alla quale lo collegavo di solito non funziona (pin 8 ). Non sono riuscito a capire quali pin sono impegnati dalla datalogger e pensavo di montare tutto su un mega. Ma Arduino mega ha problemi di compatibilità con la shield. Quindi bisogna inventarsi un modo o per rendere compatibile la shield con Arduino mega (preferibile visto che in futuro vorrei aggiungere altre funzionalità). Cmq se si sceglie questa strada farò un post nella sezione apposita.
L’altra strada potrebbe essere quella di dichiarare meglio l’oggetto lcd, mi spiego meglio:
in rete per far funzionare il mio lcd ho trovato questo modo:

#define I2C_ADDR 0x3F
#define BACKLIGHT_PIN 3
#define En_pin 2
#define Rw_pin 1
#define Rs_pin 0
#define D4_pin 4
#define D5_pin 5
#define D6_pin 6
#define D7_pin 7

LiquidCrystal_I2C lcd(I2C_ADDR,En_pin,Rw_pin,Rs_pin,D4_pin,D5_pin,D6_pin,D7_pin);

Però i pin dall’1 al 7 sono liberi perché comando il display tramite i2c. mi chiedevo se era possibile dichiarare l’oggetto in un altro modo visto che questi pin sono totalmente sprecati. Se è possibile liberare questi pin risolverei molti problemi. Altrimenti sono costretto a passare ad Arduino mega.
I pin dall’8 al 13 non mi funzionano. Penso che vengano usati dalla shield ma non lo so, in internet non ho trovato neanche un datasheet da leggere o allegare perché della datalogger che ho esistono solo datasheet cartacei e anche se lo ordinato e me lo spediscono gratis ancora non mi arriva! ( è molto probabile che ho detto assurdità clamorose visto la mia bassa esperienza).

Se usi I2C direi proprio che quei pin li puoi riutilizzare, basta solo trovare il costruttore giusto per inizializzare l'oggetto correttamente.
Ora non ho guardato nello specifico, ma da una rapida ricerca potrebbe essere qualcosa del genere:

LiquidCrystal (lcd_addr,lcd_cols, lcd_rows, charsize)

non ho chiaro cosa sia il charsize se si lavora con i display a caratteri fissi, tipo il 20x4 o il 16x2 non penso serva.
Il primo è l'indirizzo I2C, poi colonne e righe, quindi 16 e 2 ad esempio.

Se ho beccato una libreria sbagliata o di versione diversa dalla tua, sicuramente cambierà qualcosa ma di poco.

Maurizio

Se il display usa l'I2C quei pin non sono quelli dell'Arduino, ma quelli dell'expander (l'IC sulla schedina collegata al display, quindi sull'Arduio sei libero di usarli come vuoi, anzi qualcuno lo stai già usano (lo 0 e l'1) visto che usi la seriale.

Ciao, Ale.