[risolto] Modificare una variabile in modo permanente

Ciao, io tramite l'uso di due pulsanti vado a modificare il valore di una variabile, ma quando tolgo l'alimentazione ad Arduino, giustamente la variabile torna al valore di setup.
C'è un modo per modificarla anche nel campo setup così che rimanga uguale al prossimo avvio?
Grazie

I valori delle variabili sono memorizzate nella ram. La ram è una memoria volatile, quando manca l'alimentazione perde tutti i dati. Quindi il valore di una variabile si perde se manca l'alimentazione.

Per salvare i dati in modo permanente, bisogna pensare di utilizzare sistemi che salvino su altri tipi di memoria il valore della variabile prima dello spegnimento.

La memoria disponibile su arduino è la eeprom, ma ha un problema, ha un numero limitato di scritture
"mi pare 100000", quindi un programma che scriva sempre sulla eeprom non funzionerà per sempre, arrivati al limite di scrittura della eeprom si otterranno errori o risultati non voluti.

PERO, se tu salvi il valore della variabile solo una volta al giorno, la eeprom durerebbe teoricamente centinaia di anni. Quindi il problema non sussiste :slight_smile:

Puoi pensare ad un pulsante che se premuto prima di spegnere arduino, scrive il valore della variabile in eeprom, nella funzione setup leggi la eeprom e assegni il valore alla tua variabile.

Ogni quanto modifichi il settaggio con i pulsanti? Quando lo modifichi è frequente che alzi/abbassi di un solo valore (ovvero un solo click sul pulsante) o sali/scendi di molte unità?
Nel primo caso ti basta il solito debounce hardware e subito dopo aver aggiornato il contenuto della variabile utilizzare la libreria EEPROM metodi update o put.
Quando Arduino riparte nel setup userai invece il metodo get della solita librerie per assegnare alla variabile il valore letto dalla EEPROM.
Nel secondo caso il consiglio è quello di utilizzare uno stratagemma in modo da evitare innutili scritture sulla EEPROM, ad esempio alla pressione del pulsante setti un flag che ti indica che dovari salvare il dato e unitamente a questo metti il valore della funzione millis() in una variabile, dopo un certo lasso di tempo che non avvengono più pressioni salvi il valore definitivo nella EEPROM come indicato in precedenza in questo modo limiti al minimo le scritture, per le letture non c'è problema sono "infinite".
Se i cambi sono frequentissimi, un altro sistema un po' più complicato è quello di creare tutta l'infrastruttura per rilevare l'assenza di alimentazione, mantenere attivo Arduino con un supercondensatore il tempo necessario alla scrittura in EEPROM, in questo modo scrivi il valore sono quando strettamente necessario

Cavolo pensavo fosse una cosa più semplice!
Io Arduino lo spegnerò 2 volte all'anno, il problema principale è quando salta la corrente, che qui da me capita ogni tanto.
Quel valore non lo cambio molte volte, 1/2 volte al mese (nei mesi invernali)
Quindi potrebbe essere valida l'opzione da voi detta "scrivi dopo Xtempo che non cambia (così se devo cambiare di più unità non scrive ogni volta.

Anche 10 volte al mese fanno 10'000 mesi, ovvero più di 833 anni ... dubito che il tuo sistema avrà uan vita così lunga :smiley:

Usa tranquillamente la EEPROM per salvare le variabili che devi rileggere all'accensione ... c'è una libreria di base di Arduino per farlo.

Guglielmo

gpb01:
Anche 10 volte al mese fanno 10'000 mesi, ovvero più di 833 anni ... dubito che il tuo sistema avrà uan vita così lunga :smiley:

Usa tranquillamente la EEPROM per salvare le variabili che devi rileggere all'accensione ... c'è una librer
ia di base
di Arduino per farlo.

Guglielmo

Magari lui si, ma io sicuramente no!
Comincerò a studiarmi anche questo argomento... Se penso alla semplicità del progetto iniziale mi vien da ridere!

Solo a titolo di mera curiosità...non fatelo..
un PIC della microchip garantito
per 1 milione di scritture
dopo un mio test si è "rotto" dopo 29 milioni
di scritture e letture...

bye bye :slight_smile:

ps: ripeto non è un istigazione a de...fulminarli...

Stilita:
Solo a titolo di mera curiosità...non fatelo..
un PIC della microchip garantito
per 1 milione di scritture
dopo un mio test si è "rotto" dopo 29 milioni
di scritture e letture...

bye bye :slight_smile:

ps: ripeto non è un istigazione a de...fulminarli...

non riesco a capire se sei ironico o no, dici di non farlo e poi dici che con un tuo test ha durato 29milioni di volte rispetto all'1 dichiarato.

Vuole solo dire che, in realtà, le stime dei produttori sono molto prudenti e che, spesso, la vita delle EEPROM è molto più alta di quanto dichiarato. :slight_smile:

Guglielmo

gpb01:
Vuole solo dire che, in realtà, le stime dei produttori sono molto prudenti e che, spesso, la vita delle EEPROM è molto più alta di quanto dichiarato. :slight_smile:

Guglielmo

ah ok grazie!
mi sono letto un po di info e ho buttato giù il codice, sto provando un circuito semplificato per provare, poi andrò ad integrarlo nel codice comlpeto:

#include <EEPROM.h>

int temppiu=A0;
int tempmeno=A1;
int val=24;
int flag=0;
int flag2=0;
int attesamodval=0;
void setup()
{
  pinMode(temppiu,INPUT_PULLUP);
  pinMode(tempmeno,INPUT_PULLUP);
  val= (EEPROM.read(0) * 256) + EEPROM.read(1);
  Serial.begin(9600); // inizializzo la comunicazione seriale
}

void loop()
{
  if (digitalRead(temppiu)==0 and flag == 0) //funzione per effetto relè
  {
    val= (val+1);
     flag=1;
     attesamodval=millis();
    Serial.println( val);
   
 }

 if (digitalRead(temppiu)==1 and flag == 1) //funzione per effetto relè
  {
    flag=0;
 }

if (digitalRead(tempmeno)==0 and flag2 == 0) //funzione per effetto relè
  {
    val= (val-1);
     flag2=1;
     attesamodval=millis();
    Serial.println( val);
   
 }

 if (digitalRead(tempmeno)==1 and flag2 == 1) //funzione per effetto relè
  {
    flag2=0;
 }
 
 if (flag==0 and millis() >attesamodval+3000) //funzione per effetto relè
  {
    EEPROM.write(0, highByte(val));
    EEPROM.write(1, lowByte(val));
    attesamodval=millis();
 }
 delay (50);
    }

il sistema di lettura scrittura penso di averli capiti bene, io devo salvare il valore VAL che ha sempre un numero intero a 2 cifre.

il mio dubbio è sul tempo di attesa per la scrittura in eeprom, io voglio che me lo scriva dopo 30 sec dall'ultima modifica (pressione effettuata), ma come l'ho scritta penso la faccia ogni 30 secondi all'infinito, vero?

Non ho guardato come opera il tuo scotch, ma sto pensando direttamente a come integrarlo nel programma.
Tanto per cominciare dovendo muovere un numero a due cifre (Max 99) è sufficiente movimentare un byte, e la cosa rende facile scrivere/leggere su EEPROM.
Abbiamo quindi bisogno di aggiungere al programma principale la libreria EEPROM e dichiarare la variabile interessata come byte.
Nella setup () dobbiamo leggere il valore reale della variabile da EEPROM e assegnargli alla variabile. E questo credo tu lo sappia fare.
In loopO) è necessario innanzitutto separare chiaramente (richiedendo dentro in apposito blocco) la parte in cui modifichi la variabile (in flash). Questo soprattutto per poterlo identificare in sede di possibile correzione successiva.
Ora entriamo nella vera logica: se ho capito bene vuoi modificare la variabile (in EEPROM ( 30 secondi dopo la sua ultima modifica in flash, E tu puoi modificare la variabile SOLO con il pulsante (quindi no ci sono modifiche automatiche).
Di conseguenza la cosa si fa semplice:

  1. aggiungi una variabile unsigned long globale (o locale static alla loop ()) in cui poi vedremo che fare
    2)aggiungi una boolean con le stesse caratteristiche della variabile unsigned long
    3)aggiungi una #define 30000 (millisecondi dopo l'ultima modifica)

4)nella parte di modifica aggiungi queste due informazioni:
A)la flag diventa vera
B)la variabile unsigned long prende millis ()

5)fuori dalla id di modifica metti un timer che se la flag è alta E millis ()-la unsigned è >della de fine...
A)scrive la variabile su EEPROM
B)mette la flag falsa.

Ecco la descrizione. La sia tradurre in C?

Poi ti do un consiglio: guardati un Po gli altri tipi dati (ho visto che nel tuo codice sui solo int). Ricorda che, specie per codici complessi, la memoria variabili di Arduino è fattore limitante, quindi usa byte per tutti i valori con Max 255.
Inoltre è molto utile imparare l'uso di "const" o di "#define", che creano variabili costanti, il cui valore non può cambiare in nessun punto del programma (utile soprattutto per tempi fissi,p e pin)

Silente, è la terza volta che te lo dico e la pazienza comincia ad essere al limite .. si dice SKETCH e NON Scotch (non è un whisky o whiskey ... a seconda dell'origine) ... altrimenti, se NON sai l'Inglese, scrivi in ITALIANO ... "programma" !!!

Guglielmo

gpb01:
Vuole solo dire che, in realtà, le stime dei produttori sono molto prudenti e che, spesso, la vita delle EEPROM è molto più alta di quanto dichiarato. :slight_smile:

Probabilmente, oltre che essere comunque conservative, si riferiscono al caso peggiore, in primis a far lavorare il micro costantemente alla sua temperatura massima.

Di fatto anch'io ho riscontrato un valore reale superiore alle 100 volte a quello dichiarato, la prova qui.

seguendo i consigli di silente ho modificato il codice, ma non mi è chiaro un punto.
perchè mi serve una variabile unsigned long e una boolean con lo stesso valore?

questo è quello che ho scritto:

#include <EEPROM.h>

int temppiu=A0; //pulsante +
int tempmeno=A1; //pulsante -
byte val=24; //temperatura limite che modifico coi pulsanti ed andrà scritta su eeprom
int flag=0; //flag per incremento
int flag1=0; //flag per decremento
int flag2=0; //flag per scrittura su eeprom 
unsigned long modval=0;
boolean attesamodval=0;
#define attesamodval 30000
void setup()
{
  pinMode(temppiu,INPUT_PULLUP);
  pinMode(tempmeno,INPUT_PULLUP);
  val= EEPROM.read(0);
  Serial.begin(9600); // inizializzo la comunicazione seriale
}

void loop()
{
  if (digitalRead(temppiu)==0 and flag == 0) //aumento temperatura
  {
    val= (val+1);
     flag=1;
     flag2=1;
     modval=millis();
    Serial.println( val);
   
 }

 if (digitalRead(temppiu)==1 and flag == 1) 
    flag=0;
 }
if (digitalRead(tempmeno)==0 and flag1 == 0) //diminuzione temperatura
  {
    val= (val-1);
     flag1=1;
     flag2=1;
     modval=millis();
    Serial.println( val);
   
 }

 if (digitalRead(tempmeno)==1 and flag1 == 1) 
  {
    flag1=0;
 }

 if (flag2==1 and ( millis()-modval) > attesamodval) //scrittura su eeprom
  {
    EEPROM.write(0,val);
    flag2=0;
 }
 delay (50);
    }

p.s.: nell'if di scrittura su eeprom mi dà un errore "expected unqualified-id before 'if'" che non capisco da cos'è causato

Anche se la parola "and" viene capita dalle ultime versioni del compilatore, ti conisglio caldamente di NON usarla dato che NON fa parte del 'C' classico e quindi potresti trovare ambienti in cui NON viene accettata.

In 'C' la 'and' booleana si scrive '&&' mentre la 'or' si scrive '||'

Ti consiglio, più che altro per chiarezza e compatiblità di sostituire tutte quelle 'and' con il corretto operatore '&&'.

Poi esaminiamo gli altri problemi.

Guglielmo

ok, ho usato "and" per chiarezza mia non essendo molto pratico di programmazione

#include <EEPROM.h>

int temppiu=A0; //pulsante +
int tempmeno=A1; //pulsante -
byte val=24; //temperatura limite che modifico coi pulsanti ed andrà scritta su eeprom
int flag=0; //flag per incremento
int flag1=0; //flag per decremento
int flag2=0; //flag per scrittura su eeprom 
unsigned long modval=0;
boolean attesamodval=0;
#define attesamodval 30000
void setup()
{
  pinMode(temppiu,INPUT_PULLUP);
  pinMode(tempmeno,INPUT_PULLUP);
  val= EEPROM.read(0);
  Serial.begin(9600); // inizializzo la comunicazione seriale
}

void loop()
{
  if (digitalRead(temppiu)==0 && flag == 0) //aumento temperatura
  {
    val= (val+1);
     flag=1;
     flag2=1;
     modval=millis();
    Serial.println( val);
   
 }

 if (digitalRead(temppiu)==1 && flag == 1) 
    flag=0;
 }
if (digitalRead(tempmeno)==0 && flag1 == 0) //diminuzione temperatura
  {
    val= (val-1);
     flag1=1;
     flag2=1;
     modval=millis();
    Serial.println( val);
   
 }

 if (digitalRead(tempmeno)==1 && flag1 == 1) 
  {
    flag1=0;
 }

 if (flag2==1 && ( millis()-modval) > attesamodval) //scrittura su eeprom
  {
    EEPROM.write(0,val);
    flag2=0;
 }
 delay (50);
    }

Altra buona norma, specie se non ricordi/sai le "precedenze" degli operatori, è racchiudere sempre i confronti tra parentesi così da evitare qualsiasi ambiguità ...

Tipo:

if (digitalRead(temppiu)==0 && flag == 0) //aumento temperatura

meglio scriverla:

if ( ( digitalRead(temppiu)==0 ) && ( flag == 0) ) //aumento temperatura

... così sei sicuro che effettui i confronti che vuoi veramente tu :wink:

Guglielmo

Ancora ... vai nelle "impostazioni" (preferences) dell'IDE e metti il segno di spunta alle due flag di dettaglio sia in compilazione che in upload.

Vedrai che ti darà molti più dettagli nella compilazione e ... scoprirai dove è l'errore nel tuo codice (... attento alle parentesi graffe :wink:) :smiley:

Guglielmo

esatto, mancava proprio una graffa ::slight_smile:
ho sistemato il programma con le tue dritte, la logica adesso è corretta?

#include <EEPROM.h>

int temppiu = A0; //pulsante +
int tempmeno = A1; //pulsante -
byte val = 24; //temperatura limite che modifico coi pulsanti ed andrà scritta su eeprom
int flag = 0; //flag per incremento
int flag1 = 0; //flag per decremento
int flag2 = 0; //flag per scrittura su eeprom
unsigned long modval = 0;
boolean attesamodval = 0;
#define attesamodval 30000
void setup()
{
  pinMode(temppiu, INPUT_PULLUP);
  pinMode(tempmeno, INPUT_PULLUP);
  val = EEPROM.read(0);
  Serial.begin(9600); // inizializzo la comunicazione seriale
}

void loop()
{
  if ((digitalRead(temppiu) == 0) && (flag == 0)) //aumento temperatura
  {
    val = (val + 1);
    flag = 1;
    flag2 = 1;
    modval = millis();
    Serial.println( val);

  }

  if ((digitalRead(temppiu) == 1) && (flag == 1))
   {
    flag = 0;
}
if ((digitalRead(tempmeno) == 0) && (flag1 == 0)) //diminuzione temperatura
{
  val = (val - 1);
  flag1 = 1;
  flag2 = 1;
  modval = millis();
  Serial.println(val);

}

if ((digitalRead(tempmeno) == 1) && (flag1 == 1))
{
  flag1 = 0;
}

if ((flag2 == 1) && (( millis() - modval) > attesamodval)) //scrittura su eeprom
{
  EEPROM.write(0, val);
  flag2 = 0;
}
delay (50);
}