Float in eprom usando i puntatori

Ciao a tutti,
coma da oggetto, ho scritto questo sketch:

#include <EEPROM.h>

float miodatofloat;
byte indirizzo = 10;

//byte *appoggio;

void float_to_eeprom (float dato, byte cella)
	{	byte *appoggio = (byte*)(&dato);
                Serial.println("Memorizzo 4 byte a partire da cella con i valori puntati da *appoggio");
                for (int i=0; i<4; i++)
			{	EEPROM.write(cella + i, *appoggio);
                                Serial.print("Appoggio"); Serial.print(i); Serial.print(" = ");Serial.println(*appoggio, BIN);
                                appoggio += i;
			}    
	}
	

float float_from_eeprom (byte cella)
	{	float dato;
		byte *appoggio = (byte*)(&dato);
                Serial.println("leggo 4 byte a partire da cella e col puntatore  *appoggio li ricompongo in dato");
		for (int i=0; i<4; i++)
			{	*appoggio = EEPROM.read(cella+i);
                                Serial.print("Cella"); Serial.print(i); Serial.print(" = ");Serial.print(EEPROM.read(cella+i), BIN);
                                Serial.print("   -   Appoggio"); Serial.print(i); Serial.print(" = ");Serial.println(*appoggio);
				appoggio += i;
			}  
		return dato;
	}



void setup() {
  
  Serial.begin(9600);
  
  miodatofloat = 20.059;
  
  Serial.print("miodatofloat DEC = "); Serial.print(miodatofloat, DEC); Serial.println("    miodatofloat BIN = 01000001101000000111100011010101");

  float_to_eeprom (miodatofloat, indirizzo); 
  
  miodatofloat = 0.0; // Azzero variabile
  
  miodatofloat = float_from_eeprom (indirizzo);
  
  Serial.print("miodatofloat = ");
  Serial.println(miodatofloat);
}
void loop() {

}

Ovviamente non funziona, pensavo di aver capito come si usano i puntatori, ma non è così e non riesco nemmeno a capire cosa sbaglio, ho messo dei serial print nel codice per vedere cosa combino, ma la cosa non aiuta :blush:

miodatofloat DEC = 20.0590000152    miodatofloat BIN = 01000001101000000111100011010101
Memorizzo 4 byte a partire da cella con i valori puntati da *appoggio
Appoggio0 = 11010101
Appoggio1 = 11010101
Appoggio2 = 1111000
Appoggio3 = 1000001
leggo 4 byte a partire da cella e col puntatore  *appoggio li ricompongo in dato
Cella0 = 11010101   -   Appoggio0 = 213
Cella1 = 11010101   -   Appoggio1 = 213
Cella2 = 1111000   -   Appoggio2 = 120
Cella3 = 1000001   -   Appoggio3 = 65
miodatofloat = 8.03

La float in binario l’ho ottenuta con un coverter online: http://www.h-schmidt.net/FloatConverter/IEEE754.html

Mi aiutate a capire… :sob:

Riccardo

I puntatori, amore odio :slight_smile:
Dopo se trovo un attimo vedo se ti riesvo ad aiutare

Mamma mia ragazzi come vi complicate la vita solo per … non dedicarvi un attimo di più a studiare la libreria AVR libc che, per di più, è sempre automaticamente inclusa dal IDE … :roll_eyes:

Riccardo, includi <avr/eeprom.h> e ti troverai definita una bellissima macro più una serie di potenti funzioni …

… la macro è la EEMEM e le funzioni sono tutte quelle che ti servono per spostare 1 (char), 2 (integer), 4 (double) o N (array) bytes, da e verso la EEPROM in modo … banale :smiley:

Esempio :

#include <avr/eeprom.h>

uint8_t   EEMEM mioByteInEE;
uint16_t  EEMEM mioIntegerinEE;
uint8_t   EEMEM mioArrayInEE [10];

uint8_t   mioByte;
uint16_t  mioInteger;
uint8_t   mioArray [10];

void setup() {

   mioByte     = eeprom_read_byte(&mioByteInEE);
   mioInteger  = eeprom_read_word(&mioIntegerInEE); 
   eeprom_read_block((void*)mioArray , (const void*)mioArrayinEE, 10);

}

void loop() {

}


… semplice e veloce no ? ? ? :grin: :grin: :grin:

Ovviamente, come vedi nel link, oltre a quelle dell’esempio con cui si legge, esistono le equivalenti funzioni per scrivere nella EEPROM :wink:

Guglielmo

... aggiungo che, in quella libreria, c'è anche un altro bellissimo gruppo di funzioni che sono quelle chiamate :

eeprom_update_xxxxx() ... esempio : eeprom_update_byte()

che scrivono nella EEPROM SOLO SE veramente necessario (ovvero, se il valore che vado a riscrivere è uguale a quello già presente, NON lo riscrivono, riducendo le scritture fisiche e aumentando la vita della EEPROM) ;D

This functions read each byte first and skip the burning if the old value is the same with new. The scanning direction is from high address to low, to obtain quick return in common cases.

Guglielmo

C'è anche il metodo per "badass programmer", una bella union :slight_smile:

union my_float // union per scomporre un float in byte
{
 float MyFloat;
 char byte_s[4]; 
} u_Float;

Se copi dentro "u_Float.MyFloat" un valore float automaticamente ottieni i relativi quattro byte nel array "u_float.byte_s[n]", con n compreso tra 0 e 3, pronti per essere copiati sulla EEPROM.
Viceversa se copy nel array i quattro byte presi dalla EEPROM ottieni il tuo valore float dentro u_Float.MyFloat.

... ovvio Astro, ma ... vuoi mettere che semplicità/leggibilità/eleganza dichiarare direttamente le variabili in EEPROM con la macro EEMEM e spostare le variabili con una semplice chiamata a funzione :smiley:

Guglielmo

gpb01:
... ovvio Astro, ma ... vuoi mettere che semplicità/leggibilità/eleganza dichiarare direttamente le variabili in EEPROM con la macro EEMEM e spostare le variabili con una semplice chiamata a funzione :smiley:

Si ma quella roba è per mammolette, non puoi mettere il bollino che attesta quanto sei "cool" :grin:

astrobeed:
Si ma quella roba è per mammolette, non puoi mettere il bollino che attesta quanto sei "cool" :grin:

Guglielmo

bellissima quella dell’union, non ci sarei mai arrivato :slight_smile:

anche io consiglio la strada del Guglielmo,
ma a titolo di studio e’ interessante anche il tuo codice (o se per qualche motivo vuoi usare quello)
io per ora ci vedo un errore sull’incremento del puntatore appoggio
appoggio += i;

essendo i al primo giro uguale a 0, il risultato e’ che sia il primo giro che il secondo sono uguali
esempio appoggio = 10
appoggio=appoggio+0=10

infatti la stampa dei primi due byte ti vengono uguali :wink:

Prova con “appoggio++;”

Io EEMEM non lo uso quasi mai, ma dipenda dall'applicazione. Comunque usando EEMEM si scrive la eeprom nel momento in cui si scrive la flash, per cui occhio che oltre alla flash consumate anche la eeprom. Se poi consideri che scrivi in eeprom sempre i stessi dati allora li si deve considerare scritture non necessarie. Ho usato EEMEM senza arduino IDE e allora si deve tenere traccia della versione di file eeprom e ogni volta che cambia oltre al firmware devi scrivere anche il file eeprom.bin

Io uso queste due funzione e macro.

typedef struct {
    float kp;
    float ki;
    float kd;
} PidArg;

typedef struct {
    char name[12];
    uint8_t minVersion;
    uint8_t majVersion;
    uint8_t release;
} ProgInfo;


typedef struct {
    ProgInfo progInfo;
    uint8_t freqRete;
    uint8_t outEnabled; // this is bitfield
    PidArg pidArgOut0;
    PidArg pidArgOut1;
    PidArg pidArgOut2;
    PidArg pidArgOut3;

} DataCollection;

#define EEPROM_ADDR_DATACOLLECTION 0
#define SIZE_DATACOLLECTION() (sizeof(DataCollection))

/** La collezione di dati */
DataCollection dataCollection;

void datacollection_read() {
    eeprom_read_block((void*)&dataCollection, EEPROM_ADDR_DATACOLLECTION, SIZE_DATACOLLECTION());
}

void datacollection_update() {
    eeprom_update_block((void*)&dataCollection, EEPROM_ADDR_DATACOLLECTION, SIZE_DATACOLLECTION());
}

Le funzioni non sono generiche ma legate all'applicazione, la quale richiede una struct di nome DataCollection e una variabile dataCollection.

A quest'ora leggendo quello linee di codice, leggo solo scap... a destra, scap... a destra, scap... a destra :grin:

gpb01:
Mamma mia ragazzi come vi complicate la vita solo per … non dedicarvi un attimo di più a studiare la libreria AVR libc che, per di più, è sempre automaticamente inclusa dal IDE … :roll_eyes:

Riccardo, includi <avr/eeprom.h> e ti troverai definita una bellissima macro più una serie di potenti funzioni …

… la macro è la EEMEM e le funzioni sono tutte quelle che ti servono per spostare 1 (char), 2 (integer), 4 (double) o N (array) bytes, da e verso la EEPROM in modo … banale :smiley:

Guglielmo

gpb01:
… aggiungo che, in quella libreria, c’è anche un altro bellissimo gruppo di funzioni che sono quelle chiamate :

eeprom_update_xxxxx() … esempio : eeprom_update_byte()

che scrivono nella EEPROM SOLO SE veramente necessario (ovvero, se il valore che vado a riscrivere è uguale a quello già presente, NON lo riscrivono, riducendo le scritture fisiche e aumentando la vita della EEPROM) ;D

Guglielmo

Ciao Guglielmo, grazie infinite, hai sempre un consiglio o una soluzione efficace da dare, ovviamente hai ragione, dovrei approfondire meglio le mia conoscenze della AVR libc, in questo caso però vorrei evitare, perché le funzioni che mi proponi, sono si molto performanti ed eleganti, ma anche estremamente pesanti in fase di compilazione, mentre a me serve qualcosa di estremamente leggero da poter includere nel mio progetto, (immagina che inizialmente per rendere le due funzioni più generiche avevo usato sizeof() per determinare la dimesione delle variabili ed avere il valore massimo di i, poi o cancellato tutto per alleggerire).
Userò le due procedurine, per memorizzare/leggere i dati di calibrazione della sonda PH, quindi letture e scritture saranno abbastanza limitate nel tempo considerando mediamente una calibrazione al mese.
Cosa pensi delle mie considerazioni?

Testato:
bellissima quella dell’union, non ci sarei mai arrivato :slight_smile:

anche io consiglio la strada del Guglielmo,
ma a titolo di studio e’ interessante anche il tuo codice (o se per qualche motivo vuoi usare quello)
io per ora ci vedo un errore sull’incremento del puntatore appoggio
appoggio += i;

essendo i al primo giro uguale a 0, il risultato e’ che sia il primo giro che il secondo sono uguali
esempio appoggio = 10
appoggio=appoggio+0=10

infatti la stampa dei primi due byte ti vengono uguali :wink:

Prova con “appoggio++;”

Ciao Testato, grazie!!!
l’inghippo era proprio lì, da buon principiante, se non affogo in un bicchier d’acqua non mi diverto :blush:

astrobeed:
C’è anche il metodo per “badass programmer”, una bella union :slight_smile:

union my_float // union per scomporre un float in byte

{
float MyFloat;
char byte_s[4];
} u_Float;




Se copi dentro "u_Float.MyFloat" un valore float automaticamente ottieni i relativi quattro byte nel array "u_float.byte_s[n]", con n compreso tra 0 e 3, pronti per essere copiati sulla EEPROM.
Viceversa se copy nel array i quattro byte presi dalla EEPROM ottieni il tuo valore float dentro u_Float.MyFloat.

Astrobeed, grazie infinite anche a te, non conoscevo le union, prima o poi dovrò trovare il tempo di aprire e studiare il K&R che ho acquistato dopo aver letto nel tuo profilo. “Il Vero Programmatore ha imparato il C sul K&R, qualunque altro testo è inutile e deviante.”

@ MauroTec Grazie anche per il tuo suggerimento.

@ Tutti

Credo che userò il mio codice ora che grazie a Testato funziona, il motivo è che a mio avviso è un pò più Arduino like e leggero rispetto alle soluzioni che mi avete dato, cerco sempre di fare in modo che tutto il mio codice sia più easy che si possa, a beneficio dei principianti come me, ovviamente non sempre è possibile, ma ci provo.

Ancora grazie a tutti :slight_smile:

Dimenticavo il codice corretto…

#include <EEPROM.h>

float miodatofloat;
byte indirizzo = 10;

//byte *appoggio;

void float_to_eeprom (float dato, byte cella)
	{	byte *appoggio = (byte*)(&dato);
                Serial.println("Memorizzo 4 byte a partire da cella con i valori puntati da *appoggio");
                for (int i=0; i<4; i++)
			{	EEPROM.write(cella + i, *appoggio);
                                Serial.print("Appoggio"); Serial.print(i); Serial.print(" = ");Serial.println(*appoggio, BIN);
                                appoggio ++;
			}    
	}
	

float float_from_eeprom (byte cella)
	{	float dato;
		byte *appoggio = (byte*)(&dato);
                Serial.println("leggo 4 byte a partire da cella e col puntatore  *appoggio li ricompongo in dato");
		for (int i=0; i<4; i++)
			{	*appoggio = EEPROM.read(cella+i);
                                Serial.print("Cella"); Serial.print(i); Serial.print(" = ");Serial.print(EEPROM.read(cella+i), BIN);
                                Serial.print("   -   Appoggio"); Serial.print(i); Serial.print(" = ");Serial.println(*appoggio);
				appoggio ++;
			}  
		return dato;
	}



void setup() {
  
  Serial.begin(9600);
  
  miodatofloat = 20.059;
  
  Serial.print("miodatofloat DEC = "); Serial.print(miodatofloat, DEC); Serial.println("    miodatofloat BIN = 01000001101000000111100011010101");

  float_to_eeprom (miodatofloat, indirizzo); 
  
  miodatofloat = 0.0; // Azzero variabile
  
  miodatofloat = float_from_eeprom (indirizzo);
  
  Serial.print("miodatofloat = ");
  Serial.println(miodatofloat, DEC);
}

void loop() {


}

riciweb:
Ciao Guglielmo, grazie infinite, hai sempre un consiglio o una soluzione efficace da dare, ovviamente hai ragione, dovrei approfondire meglio le mia conoscenze della AVR libc, in questo caso però vorrei evitare, perché le funzioni che mi proponi, sono si molto performanti ed eleganti, ma anche estremamente pesanti in fase di compilazione

Riccardo … ma che dici ? ? ? :o :o :o Dove l’hai letta questa ? ? ? :o :o :o Ma hai mai guardato dentro la EEPROM standard che usi tu ? ? ? :o :o :o

Sei TU che stai appesantendo il codice aggiungendo una INUTILE libreria C++ che … altro NON fa che richiamare le funzioni che ti ho indicato io !!!

Ecco il codice estratto da EEPROM.cpp (… che tu includi con la EEPROM.h) :

/******************************************************************************
 * Includes
 ******************************************************************************/

#include <avr/eeprom.h>
#include "Arduino.h"
#include "EEPROM.h"

/******************************************************************************
 * Definitions
 ******************************************************************************/

/******************************************************************************
 * Constructors
 ******************************************************************************/

/******************************************************************************
 * User API
 ******************************************************************************/

uint8_t EEPROMClass::read(int address)
{
	return eeprom_read_byte((unsigned char *) address);
}

void EEPROMClass::write(int address, uint8_t value)
{
	eeprom_write_byte((unsigned char *) address, value);
}

… e, secondo te … cosa usa se non le funzioni che ti ho detto io ? :smiling_imp: :smiling_imp: :smiling_imp:

E, sempre secondo te, cosa appesantisce di più, chiamarle direttamente o, per chiamarle, passare per una classe C++ ? :smiling_imp:

Guglielmo

riciweb:
Ciao Testato, grazie!!!!!

Uno è lieto di servire :slight_smile:

Infatti mi stavo chiedendo se il discorso che eemen appesantisce era un tuo parere, rici, oppure lo hai compilato ed hai + flash + ram usata ?
Se è così c'è un bug nel core arduino perché concordo con guglielmo.

Mauro per quanto riguarda il .bin che ne deriva (io ho usato eemem su AStudio ma mai sull ide arduino) viene uplodato automaticamente dall'ide ad ogni caricamento ? Un idea malsana al volo, nei casi in cui si modifica il codice che non ha a che fare con la eeprom si può forse compilare, cancellare il .bin dalla directory, uplodare ?

@ Guglielmo e Testato

Scusatemi, ho scritto le mie considerazioni, supportato unicamente dalla mia ignoranza delle cose, pensavo fosse un’ulteriore libreria che si aggiungeva alle altre che già uso, non ho fatto test o compilazioni, ma grazie a voi ora ho capito come funzionano queste cose.

Grazie, appena ho un minuto applico i vs. suggerimenti.

@Riccardo : ... nessun problema, ma ricorda sempre che le librerie Arduino che usi con l'IDE servono solo a semplificare/nascondere (... hai meno esperti) le chiamate "native" (AVR libc, accesso diretto a registri, ecc. ecc.) e che quindi ... appesantiscono sempre rispetto a chiamare direttamente le "fondamenta" su cui si basano !!! :wink:

Guglielmo

Grazie a te te/voi ora lo so anche io che sono moooolto meno esperto :blush:

Grazie ancora… sinceramente!

Grazie a te te/voi ora lo so anche io che sono moooolto meno esperto :blush:

Grazie ancora… sinceramente!

Ieri eri meno esperto di oggi. :stuck_out_tongue:

Un idea malsana al volo, nei casi in cui si modifica il codice che non ha a che fare con la eeprom si può forse compilare, cancellare il .bin dalla directory, uplodare ?

Anche a me sembra l’unica soluzione, anche perché prima o poi dovrai rinominare il file .bin aggiungendo la versione, quindi lo rinomini e poi lo sposti nella cartella dei sorgenti.

Ciao.