Buffer (EEPROM) per ESP8266 e Thingspeak

Buondi a tutti cari appassionati. Il mio progettino per la gestione autonoma delle piante con i relativi sensori è a buon punto. Terminata la sistemazione dell'hardware, ho buttato giù un codice, che credo sia valido, e credo testerò oggi. In sostanza ho una serie di sensori (analogici e digitali) e una scheda wifi ESP8266. Ho completato tutta la parte di codifica classica, e rivisto più volte il codice scritto.

Dato che come al solito bisogna prevedere ogni possibile situazione che può capitare e quindi istruire arduino sul da farsi, mi chiedevo come comportarmi nella situazione che descrivo ora. Di solito a casa abbiamo il wifi acceso di giorno, ma lo spegniamo tramite apposito bottoncino sul router la sera prima di andare a dormire, visto che le radiazioni del wifi bene proprio non fanno e di notte comunque non lo usa nessuno. Ora, il mio progettino deve svegliarsi alle 7 (o alle 8 poi deciderò) di mattina, fare un check dei sensori e loggare tutti i dati su Thingspeak, nota piattaforma per l'IoT. Il problema è....mettiamo il caso che una mattina mi sveglio tardi e accendo il router dopo le 7 (o 8 che siano) ??? Ovviamente in tal caso il modulo non riuscirà a connettersi a nessuna rete visto che se il router è spento la rete non è proprio accessibile.

E' possibile "tenere in memoria" la stringa di dati che dovevo inviare, e poi ritentare chessò dopo 2 ore l'invio tramite wifi? In tal caso mando a dormire temporaneamente arduino e ritento l'invio ogni 2 ore. Avevo pensato alla memoria EEPROM per conservare la stringa, ma non so se è sufficientemente capiente. La stringa se non erro è composta da 50 caratteri (5x10). Secondo voi come posso aggirare il problema?

Sulla UNO la EEPROM è da 1K cioè 1024 byte, mentre sulla MEGA è da 4K cioè 4096 byte: c'è spazio da vendere!

cyberhs:
Sulla UNO la EEPROM è da 1K cioè 1024 byte, mentre sulla MEGA è da 4K cioè 4096 byte: c’è spazio da vendere!

Bene, il mio dubbio era appunto se nel mio caso ci stesse tutto. Ricordavo che un char vale un byte, ma non so se nella eeprom servono altri byte in più per indirizzi o altro.

Appurato quindi che la cosa è fattibile, il dubbio, enorme lo ammetto, è il come.
Non ho mai salvato nulla sulla EEPROM, ora devo addirittura salvarci una stringa intera.
Visto che una volta che la stringa è stata loggata non mi serve più avevo pensato di mettere anche il comando di pulizia della eeprom in modo da non riempirla troppo…
Avete delle librerie da consigliare, o dei topic dove potrei trovare una mano? Cercando sul forum ne ho trovati alcuni, ora vado a leggermeli con calma…

Una domanda però al contempo mi sorge…
Io a thingspeak invio i dati puliti…cioè ho 5 dati e gli invio solo quelli, non includo nella stringa anche ora e data in quanto mi è sembrato di vedere che li ricava in automatico da ora e data di invio…ma se mi succedesse che devo mandargli dati “vecchi” dalla EEPROM, come devo fare perchè venga “capito” da thingspeak che non sono dati live?

Se ti interessa avere una sorta di data logger, ti conviene salvare anche data e ora.

Suggerirei di salvare i dati grezzi che occupano meno spazio.

Per ogni mancato collegamento salva una stringa e quando viene ripristinato invii tutte le stringhe.

Per ottimizzare la vita della EEPROM (ovvero il numero di cicli di scrittura) potresti lasciare i vecchi dati creando una struttura circolare: quando lo spazio è esaurito, resetti il contatore e ricominci dall'inizio della EEPROM.

Una buona libreria è la EEPROMEx, una evoluzione rispetto alla EEPROM standard.

Anche se pochi lo sanno o se lo ricordano, l’IDE di Arduino automaticamente include la AVRLibc e quindi, senza cercare cose strane, ti consiglio di guardare la <avr/eeprom.h> dove troverai tutte le funzioni che ti possono servire per legge/scrivere nella EEPROM.

Guglielmo

cyberhs:
Se ti interessa avere una sorta di data logger, ti conviene salvare anche data e ora.

Suggerirei di salvare i dati grezzi che occupano meno spazio.

Per ogni mancato collegamento salva una stringa e quando viene ripristinato invii tutte le stringhe.

Per ottimizzare la vita della EEPROM (ovvero il numero di cicli di scrittura) potresti lasciare i vecchi dati creando una struttura circolare: quando lo spazio è esaurito, resetti il contatore e ricominci dall’inizio della EEPROM.

Una buona libreria è la EEPROMEx, una evoluzione rispetto alla EEPROM standard.

Grazie mille, un paio di domande per capire meglio…
io si, vorrei creare una sorta di data logger, ma invece che su SD sul web in modo che posso consultarlo anche da altrove o quando sono fuori.
Per l’invio anche di ora e data ho avuto problemi…nel senso che in qualunque guida o tutorial io abbia letto, quando faceva l’invio a thingspeak inviava solo il set di dati che poi confluivano nei vari field (le varie tabelle dei dati).
Ho provato anche a fare un invio con una stringa di questo tipo 00:00:00, 00/00/0000, 00.00, 00.00, 00.00, 00.00, 00.00 ma ha interpretato i primi due (ora e data) come dei set di dati e ha creato due tabelle con quei numeri, ovviamente senza senso.
Ho sbagliato io in qualche modo o passaggio? mi sembra comunque strano che non permettano l’invio anche di data e ora…ecco perchè non sapevo spiegarmi dove era l’inghippo.

Per l’idea di salvare i float grezzi è meglio, in effetti.
Poi se il collegamento wifi è ripristinato, li leggo al momento e li riconverto nelle stringhe.

Questo della scrittura circolare mi pare una cosa molto saggia…sai se questa funzione è possibile con la libreria che suggerivi?
Come libreria ho trovato anche la EEPROMAnything.h, mi sembrava molto intuitiva…vado a studiarmi la eepromex…:smiley:

gpb01:
Anche se pochi lo sanno o se lo ricordano, l’IDE di Arduino automaticamente include la AVRLibc e quindi, senza cercare cose strane, ti consiglio di guardare la <avr/eeprom.h> dove troverai tutte le funzioni che ti possono servire per legge/scrivere nella EEPROM.

Guglielmo

Ciao Guglielmo, si la eeprom “standard” l’avevo trovata, ma in effetti spulciando parecchi topic leggevo che le altre librerie come la EEPROMAnything.h o la eepromex sono più semplici da usare e hanno maggiori funzionalità…andrò a leggermi anche quella cosi imparo cose nuove…

L'ESP8266 può gestire direttamente il datalogger è interfacciarsi con memorie esterne. Esempio una SD. Purtroppo ad oggi è un prodotto molto giovane e si trovano pochi esempi di codice. Oltre a non essere programmabile con l'IDE di Arduino. Ovviamente.

PaoloP: L'ESP8266 può gestire direttamente il datalogger è interfacciarsi con memorie esterne. Esempio una SD. Purtroppo ad oggi è un prodotto molto giovane e si trovano pochi esempi di codice. Oltre a non essere programmabile con l'IDE di Arduino. Ovviamente.

Ciao Paolo, nel mio caso non ho necessità di metterci anche una SD, perchè lo "scatolotto" sarà chiuso e non reso accessibile. Inoltre se riesco a loggare i dati in rete, l'utilità di una SD fisica decade visto che lo storico lo potrei anche poi ricavare da thingspeak stessa... il mio problema piuttosto è sul come inviare data e ora a thingspeak nello stream dei dati...per i dati live non c'è problema perchè lo ricava automaticamente, ma mi sembra poco credibile che una piattaforma del genere non accetti anche tra i dati in stream l'ora e data effettiva del log...sono sicuramente io che non ho capito come fare...

giorgio90: Ciao Guglielmo, si la eeprom "standard" l'avevo trovata ...

La eeprom che tu definisci "standard" immagino che sia invece quella di Arduino, quella che ti ho linkato io invece è quella che fa parte di AVRLibc ... ovvero la libreria C dedicata alle MCU AVR di Atmel.

Guglielmo

gpb01: La eeprom che tu definisci "standard" immagino che sia invece quella di Arduino, quella che ti ho linkato io invece è quella che fa parte di AVRLibc ... ovvero la libreria C dedicata alle MCU AVR di Atmel.

Guglielmo

Ecco, si, mea culpa, ho fatto confusione tra le due...in effetti non avevo notato appunto quel AVRLibc e credevo ti riferissi alla normale eeprom.h Vado a dare un'occhiata senz'altro...grazie :D

Allora, piccolo aggiornamento…alcune questioni sollevate in precedenza sono state risolte studiando per bene sia i documenti che thingspeak fornisce, sia altri milioni di tutorial :smiley:
La questione della data mi pareva veramente strana, infatti era impossibile che un sito che fa quasi esclusivamente datalogging non avesse l’invio della data del log quando diverso da quella di upload.
Infatti c’è, ma è opzionale, è non è stato facilissimo trovarlo perchè in nessun tutorial online è mai messo negli esempi o negli sketch…Ecco qui come viene descritto nei documenti di thingspeak:

created_at (datetime) - Date when this feed entry was created, in the format
YYYY-MM-DD%20HH:NN:SS (optional)

https://thingspeak.com/docs/channels

Risolta la questione ora (e ti credo che aveva preso la mia data e ora per campi di dati)…resta la questione eeprom.
L’idea di far ritentare la connessione ogni 2 ore se fallisce la prima era buona, ma bisognava andare a trovare un sistema alternativo per far dormire nel frattempo arduino, l’unica soluzione era il watchdogtimer o reimpostare la sveglia dell’RTC.
Non volendo fare troppi casini ho pensato che semplicemente posso rimandare tutto alla sveglia della mattina dopo, quando perciò verranno loggati i dati “live” e i dati vecchi in eeprom.

Passando quindi alla questione eeprom ho studiato le varie librerie e sono tutte valide e anche non troppo incasinate, ho tuttavia preferito la strada indicata da gpb01, quella della <avr/eeprom.h>.
Volevo domandare quindi un dettaglio. Ho testato un codicino scritto al volo per vedere se avevo capito il funzionamento della eeprom con dei float e tutto funziona.
Volevo capire semplicemente come fare a far ciò che cyberths chiamava prima “scrittura circolare”, ovvero ottimizzare al massimo le scritture.
Il mio codicino di prova è

#include <avr/eeprom.h>
#include "DHT.h"
#define DHTPIN 4 
#define DHTTYPE DHT11
DHT dht(DHTPIN, DHTTYPE);

byte flag = 0;
long count=0;
struct settings_t
{
  float temp;
  float humi;
  
} settings;

void setup()
{
  Serial.begin(9600); 
    dht.begin();
  
}
void loop()
{  delay(2000);
  float h = dht.readHumidity();
  // Read temperature as Celsius
  float t = dht.readTemperature();
  Serial.println("Valori originali letti; ");
    Serial.println(t);
    Serial.println(h);

  
  // Set Struct variable 
   settings.temp= t;
   settings.humi= h;
  
  if( flag == 0 ){
     // Write  EEPROM
     eeprom_write_block((void*)&settings, (void*)0, sizeof(settings));
     flag = 1;
     Serial.println("scrittura eeprom fatta");
     Serial.print("temp: ");
     Serial.println(settings.temp);
     Serial.print("humi: ");
     Serial.println(settings.humi);
     }
   
   delay(3000);
   if(flag == 1){
     eeprom_read_block((void*)&settings, (void*)0, sizeof(settings));
   
     Serial.println("Lettura con successo:");
     Serial.print("temp: ");
     Serial.println(settings.temp);
     Serial.print("humi: ");
     Serial.println(settings.humi);
     flag = 2;
     count = millis();
     }
    
    delay(100);
    if ((millis()-count) > 60000){
      flag = 0;
      }
}

Volevo capire…in questo caso a quanto mi sembra di leggere l’allocazione comincia dalla cella 0 giusto?
Ma se volessi scrivere in seguito di nuovo, non sovrascrivendo questa, come dovrei scrivere il codice per partire diciamo dalla prima cella libera?
Per realizzare una “scrittura circolare” devo procedere a mano considerando quante volte posso scrivere la totalità dei float (se sono 5 float lo spazio sarà 4*5=20 byte, giusto? Quindi in 1024byte posso scrivere max 51 volte) prima di azzerare tutto o c’è un modo che usa qualche funzione?

Mmm ... magari comincia a studiare QUESTO ... e, se la cosa è troppo complicata, lascia stare ...

Ricorda che comunque ogni cella della EEPROM ha una vita di circa 100'000 scritture. Fatti un conto di quante ne fai un un giorno al massimo e capisci se è veramente necessario introdurre la complicazione di un buffer circolare o ... non ti serve :roll_eyes:

Per il resto ... è tutto compito tuo sapere esattamente da dove parti e quanto scrivi per poter gestire più scritture in punti differenti ...

Guglielmo

Con le versioni più recenti della toolchain Avr puoi utilizzare una comodissima funzione, eeprom_update_xxx. Cosa fa? Semplicemente legge prima la cella e controlla se il valore che vuoi scrivere è identico a quello presente, in caso affermativo salta la scrittura! In questo modo salvi un ciclo di riscrittura della cella. Questa soluzione non funziona con l'IDE 1.0.6 a meno che tu non aggiorni la toolchain. Funziona con l'IDE 1.5.8.

Alternativamente puoi farti una semplice routine tu dove leggi i dati, se sono uguali non scrivi.

leo72: Con le versioni più recenti della toolchain Avr puoi utilizzare una comodissima funzione, eeprom_update_xxx. Cosa fa? Semplicemente legge prima la cella e controlla se il valore che vuoi scrivere è identico a quello presente, in caso affermativo salta la scrittura! In questo modo salvi un ciclo di riscrittura della cella. Questa soluzione non funziona con l'IDE 1.0.6 a meno che tu non aggiorni la toolchain. Funziona con l'IDE 1.5.8.

Alternativamente puoi farti una semplice routine tu dove leggi i dati, se sono uguali non scrivi.

Si, ieri sera mi sono letto il manuale sulla libreria AVRlibC e ho imparato un sacco di cose interessanti, tra cui la funzione update. il mio "problema" però non è quello. Il mio caso prevede la scrittura nella eeprom come un caso eccezionale, non la regola. Perciò in effetti andarmi a impelagare con il buffer circolare ha poca utilità, bastano e avanzano 51 "block" scrivibili (considerando che ogni block è sempre di 20byte nel mio caso, perchè ho 5 float). Ora, la mia necessità non è "updatare" ciò che è scritto in eeprom, ma semplicemente salvare i vari blocchi in modo che non si sovrappongano, e quindi diminuiscono la vita di quelle celle che vengono a essere scritte più volte. Ovviamente poi la necessità è andare a leggere dal punto giusto.

Una cosa che insomma funzioni cosi...: oggi non funziona il wifi, salvo in eeprom il block di float partendo da indirizzo 0, domani mattina il wifi funziona e mi carica i dati di domani e quelli di oggi che sono in eeprom, partendo da 0. Tra tre giorni il wifi non funziona di nuovo, i dati non devono essere scritti in eeprom ripartendo da 0, ma continuando a riempire celle, quindi presumibilmente dalla cella 20 (il primo blocco di 20 byte riempiva 0-19). Ovviamente la lettura non dovra leggere tutto da 0, ma da 20 dove parte il block che mi serve. Considerando che in 1024 posso scrivere circa 51 volte 20byte, con un counter (i=0) una volta arrivato alla 51esima lettura faccio ripartire la scrittura in eeprom dalla cella 0 e riparto. Secondo voi è un buon metodo?

Così facendo però hai bisogno di una cella in cui salvare anche il puntatore all'ultima scrittura, e questa cella verrebbe scritta tutte le volte andando a consumare il numero di riscritture ben prima di tutte le altre.

... Leo ...da quello che dice, stiamo parlano ... se va male ... di UNA o DUE scritture al giorno ... ma mettiamo anche DIECI scritture al giorno ... sono 10'000 giorni minimo ... 27 ANNI ... :stuck_out_tongue_closed_eyes: :stuck_out_tongue_closed_eyes: :stuck_out_tongue_closed_eyes:

Guglielmo

Allora non ho contato bene il numero di scritture che vuol fare :sweat_smile:

gpb01: ... Leo ...da quello che dice, stiamo parlano ... se va male ... di UNA o DUE scritture al giorno ... ma mettiamo anche DIECI scritture al giorno ... sono 10'000 giorni minimo ... 27 ANNI ... :stuck_out_tongue_closed_eyes: :stuck_out_tongue_closed_eyes: :stuck_out_tongue_closed_eyes:

Guglielmo

Esattamente...il logger farà una (o due) letture al giorno. La scrittura in eeprom avviene solo se fallisce il log sul sito internet. Quindi è possibile che il numero di scritture vari da un massimo di 2 al giorno a un minimo di 0 anche per settimane o mesi. Arrivare a consumare la eeprom mi sembra inverosimile in queste condizioni.... :D Fallendo il log tutti i giorni, per sempre, la eeprom mi durerebbe comunque minimo 137 anni... :o Visto che 1024 /20 non fa 51 esatti, ergo le ultime 4 celle non vengono mai scritte, avevo intenzione di salvare in una di quelle il contatore, cosi non disturba la scrittura dei block che si susseguono, che ne dite?

Mi pare che nelle ultime 2 celle Avrdude (o l'IDE) scriva il numero di volte che è stato programmato l'Arduino. Però è un dato che puoi ignorare.

Sicuramente ti serve una cella per il contatore che non sia tra quelle usate dai blocchi altrimenti addio contatore ;) Ti basta una sola cella dato che essa può contenere un byte, numeri da 0 a 255 mentre il max numero che puoi salvarci è 51, l'indice dell'ultimo blocco utile.