EEProm - Lettura/Scrittura con coppie Chiave - Valore

Questo risulta essere il mio primo intervento (oltre a quello della presentazione). Vorrei far presente che ho riiniziato a scrivere qualcosa in “C “dopo 30 anni di digiuno completo ( 30 anni fa si iniziava a parlare di C++ ma non ce lo avevano insegnato ) . Leggendo alcuni listati sinceramente mi sono alquanto depresso vedendo quanto indietro sono rimasto e mi chiedevo se avesse senso pubblicare il mio misero codice. Visto però che mi sembra funzionare e che dalle mie ricerche non ho trovato qualcosa di simile mi sono deciso.
Qualcuno potrebbe giustamente far notare che se non c’è nulla di simile è perché probabilmente non serve. Al limite i moderatori lo rimuoveranno…
Per quanto detto chiedo anticipatamente scusa se ci fossero degli errori grossolani o se il codice scritto non rispettasse le giuste indentazioni, regole per i nomi delle variabili o posizione delle parentesi graffe ecc, ecc.

Fatta questa premessa vorrei presentarvi un header file che ho scritto per leggere e scrivere sulla EEProm tramite coppie Chiave-Valore.
L’idea mi è nata cercando i metodi per scrivere nella EEProm e trovando che tutti ( se ho cercato bene ) prevedono sempre, oltre al dato, l’inserimento dell’indirizzo da cui iniziare a scrivere. Per quanto efficiente la cosa non mi piaceva molto perché obbligava sempre a tener conto di dove si era arrivati a scrivere e della dimensioni dei dati da inserire. Ho cercato quindi un metodo alternativo che automatizzasse la cosa.
Premetto che per ottenere quanto esposto si "sprecano" preziosi cicli macchia al micro ed anche vari byte della già piccola EEProm presente sull'Arduino. Per ogni dato inserito infatti, di qualsiasi lunghezza questo sia, vengono aggiunti 6 byte necessari per l'inserimento e successive ricerche oltre al numero di byte utili alla memorizzazione della chiave ( array di caratteri ).

Traducendo con un esempio: per memorizzare una coppia con nome “pippo” e valore intero 1234 avrò bisogno di 14 byte.
6 byte per contenere 3 interi 16 bit ( 2 utilizzati come puntatore al dato precedente e successivo ed 1 per memorizzare la dimensione del dato)
6 byte per contenere l’array contenente i caratteri della parola “Pippo” + valore “0” di fine array.
2 byte per contenere l’ intero 16 bit contenente il valore 1234.

I dati, come si sarà intuito dalla presenza dei primi 2 puntatori, vengono memorizzati in una struttura di tipo B-Tree. Inizialmente avevo pensato ad una struttura tipo Hash Table ma le risorse limitate e la non semplicissima gestione delle collisioni mi hanno spinto a cercare qualcosa di più elementare. (teoricamente funziona con EEprom fino a 64KB).

Evidenzio di aver utilizzato chiamate alle funzioni EEPROM.put ed EEPROM.get presenti in EEPROM.h.

Avendo ricevuto la segnalazione di aver superato i 9000 caratteri riporto in allegato il codice dell'header file DS_EEProm.h e di seguito un codice riportante un esempio di utilizzo.

#include <stdio.h>
#include "DS_EEProm.h"

struct MioTipo
  { 
    char Nome[10];
    int Valore;
  } ;

char StringaCercata[100];
int InteroCercato;

void setup() {
  
  Serial.begin(9600);  
      
  MioTipo Struttura_a;
  strcpy(Struttura_a.Nome,"Pippo");
  Struttura_a.Valore=12345;   
    
  uint8_t i=0;

  Serial.println("Inizio scrittura");
  i=DS_put("Nome","Diego Sartori");
  // if (i==2) {  }
  i=DS_put("Anno",(int)1967);
  i=DS_put("ChiaveStruttura",Struttura_a);
  i=DS_put("IP","192.168.0.1");
  Serial.println("Fine scrittura");
}


void loop() {

  DS_get("Nome",StringaCercata);
  Serial.print("Nome = ");
  Serial.println(StringaCercata);
  
  DS_get("Anno",InteroCercato);
  Serial.print("Anno = ");
  Serial.println(InteroCercato);
  
  DS_get("IP",StringaCercata);
  Serial.print("IP = ");
  Serial.println(StringaCercata);
  
  MioTipo Struttura_a1;
  DS_get("ChiaveStruttura",Struttura_a1);
  Serial.print("Struttura Nome = ");
  Serial.print(Struttura_a1.Nome);
  Serial.print(" Struttura Valore = ");
  Serial.println(Struttura_a1.Valore);

  delay(1000);                  // Attende 1 secondo
                
}

DS_EEProm.h (5.02 KB)

sembra un bell'oggetto da tenere nella casstta dei ferri,
uno sguardo rapide e mi è piaciuto
grazie

Diego67:
... L’idea mi è nata cercando i metodi per scrivere nella EEProm e trovando che tutti ( se ho cercato bene ) prevedono sempre, oltre al dato, l’inserimento dell’indirizzo da cui iniziare a scrivere. Per quanto efficiente la cosa non mi piaceva molto perché obbligava sempre a tener conto di dove si era arrivati a scrivere e della dimensioni dei dati da inserire. Ho cercato quindi un metodo alternativo che automatizzasse la cosa.

Esiste già questa possibilità ed è molto più semplice ed efficiente ... mi spiace ... :confused:

Si deve include la libraria, da cui TUTTE le altre derivano, la avr/eeprom.h, dopo di che basta scrivere (è un esempio), per definire le variabili:

uint32_t EEMEM EEsignature;
uint8_t  EEMEM EEnoOfTel;
uint8_t  EEMEM EEnoOfEmergency;
uint8_t  EEMEM EEuserPsw[10];
uint8_t  EEMEM EEtelID[5][10];
...

... mentre per usarle:

eeprom_write_byte(&EEnoOfTel      , 0);
eeprom_write_byte(&EEnoOfEmergency, 0);
eeprom_write_block(userPsw, &EEuserPsw, 10);
...

Come vedi NON occorre assolutamnete ricordarsi la posizione, ma solo il nome della variabile ed il gioco è fatto :slight_smile:

Trovi tutti i dettagli e molto altro nell'allegato.

Guglielmo

P.S.: Comunque ... complimenti per l'impegno, l'idea ed il lavoro fatto :slight_smile:

EEPROM.pdf (181 KB)

oramai la lista dei documenti da tenere a portata di mano comincia ad essere grandicella :slight_smile:

gpb01:
uint8_t EEMEM EEnoOfTel;
uint8_t EEMEM EEnoOfEmergency;
uint8_t EEMEM EEuserPsw[10];
uint8_t EEMEM EEtelID[5][10];

eeprom_write_byte(&EEnoOfTel , 0);
eeprom_write_byte(&EEnoOfEmergency, 0);
eeprom_write_block(userPsw, &EEuserPsw, 10);

Ti ringrazio per la risposta. Vorrei però ragionare su di una cosa per capire se il mio pensiero risulta corretto. E' vero che con il sistema da te riportato basta ricordarsi il nome della variabile ma comunque tu non passi la variabile o il suo nome ma il suo indirizzo ("&NomeVariabile"). Nella EEProm verrà memorizzato quindi a partire da quell'indirizzo il contenuto passato. Da qui per me possono sorgere 2 problemi:
1 -> non so quali porzioni di EEProm siano utilizzate e quali no.
2 -> Data una certa variabile con un dato indirizzo, se modifico il codice, non è detto che quella variabile avrà il medesimo indirizzo e questo renderebbe non più rintracciabile il contenuto associato al vecchio indirizzo. In ogni caso, anche senza modifiche al codice, penso che potrebbe funzionare unicamente con variabili globali e non con variabili locali allocate dinamicamente con indirizzi variabili.

Con il mio sistema ho cercato di rendere indipendente dall'indirizzo la memorizzazione di un dato e comunque rintracciabile anche con un cambio globale del programma.

Resto in attesa per capire se, come è facile, non ho capito proprio nulla. :frowning:

... allora, o tu vuoi SLEGARTI dalla posizione fisica o NON vuoi.

Quindi, se ti vuoi slegare dalla posizione fisica usi EEMEM, se ti occorre la posizione fisica usi l'indirizzo e non sprechi preziosa memoria con inutili sistemi di indicizzazione (NON sei su un PC, sei su MCU che hanno una manciata si SRAM e ancor meno di EEPROM).

La posizione è comunque dettatta dall'ordine in cui dichiari le EEMEM, quindi, anche se modifichi il programma, la cosa non cambia. Ovvio che se devi aggiungere altre cose in EEPROM, le aggiungi a seguire e NON sconvolgi l'ordine delle dichiarazioni.

Cosa c'entrano variabili locali e globali ? ? ? :o :o :o
La EEPROM non ha nulla a che vedere con le variabili allocate staticamente (.data o .bss) o dinamicamente (nello stack) ... essa è praticamente SEMPRE globale e visibile ovunque.

Insomma, forse, applicando l'idea su un PC con memoria che abbonda, potrebbe essere una cosa di una certa uiltà, su microcontrollori, dove si fa di tutto per risparmiare anche un byte, la cosa, pur essendo ben ideata e concepita, è piuttosto inutile ... :confused:

Guglielmo

gpb01:
... allora, o tu vuoi SLEGARTI dalla posizione fisica o NON vuoi.

Con la frase: "Con il mio sistema ho cercato di rendere indipendente dall'indirizzo la memorizzazione di un dato e comunque rintracciabile anche con un cambio globale del programma. " pensavo fosse chiara la mia volontà di rendere completamente indipendente la memorizzazione dei dati dalla posizione.

Riguardo le incomprensioni successive ammetto che sono dovute ad un mio errore. Non avevo ( gravemente ) notato l'attributo EEMEM e quindi pensavo che fossero normali dichiarazioni di variabili. Ora leggendo l'allegato che hai reso disponibile ha capito meglio l'utilizzo di quell' attributo.

Grazie, ciao!

gpb01:
Insomma, forse, applicando l'idea su un PC con memoria che abbonda, potrebbe essere una cosa di una certa utilità, su microcontrollori, dove si fa di tutto per risparmiare anche un byte, la cosa, pur essendo ben ideata e concepita, è piuttosto inutile ...

Guglielmo

Nonostante gli scarsi incoraggiamenti e nonostante abbia provato ad utilizzare le EEMEM continuo a credere che la mia idea ( non il codice sopra propostovi così com’è ) possa risultare utile in certi casi. Ho quindi sviluppato una versione semplificata sotto certi aspetti e migliorata sotto altri del programma in oggetto. Prima però di proporvelo per evitare altre scritte maiuscole, poco piacevoli e non in linea coi toni da me utilizzati vorrei chiedervi, se possibile di fornirmi un’ idea di come risolvereste voi un problema che mi sono posto.

Immaginiamo di voler gestire con Arduino un piccolo antifurto. Immaginiamo poi di voler inserire “in corsa” ( ad esempio tramite seriale) una trentina di stringhe da visualizzare su di un display al verificarsi di una cerca combinazione degli ingressi. Supponiamo poi che la gran parte delle scritte siano al massimo di una decina di caratteri ma che per alcune siano necessari fino a 50 caratteri. Logicamente per non doverle inserire ogni volta che viene rimossa l’alimentazione queste stringhe dovranno essere memorizzate in un’area di memoria non volatile ed è anche plausibile pensare che ogni singolo utilizzatore voglia inserire dei propri messaggi personalizzati e quindi non impostabili staticamente.
Quale struttura dati converrebbe utilizzare per non sprecare byte e come potrebbe essere fatta l’associazione “valore intero corrispondente allo stato degli ingressi” --> ”stringa segnalazione” ?

Ringrazio anticipatamente quanti vorranno rispondermi.

Vista l'assenza completa di risposte penso che la mia domanda fosse o banale o poco chiara. Avendo comunque ormai fatto il lavoro seguendo quella che secondo me potrebbe essere una soluzione al quesito posto ve lo allego di seguito.

Si divide in un programma di test:

#include <stdio.h>
#include "DS_EEProm_New.h"

void setup() {
  Serial.begin(9600);  
}


void ReadStringFromSerial(char searchedString[]){
  while(!Serial.available()){delay(20);}
  byte j=0;
   while(Serial.available())
    {
      searchedString[j] = Serial.read();
      ++j;
      searchedString[j] = '\0';  
    }
   //return searchedString;
  }


void loop() {

  char searchedString[100];
  char keyString[50];
  char valueString[100];
  char command[5];
  
  Serial.println("-----------------Insert command-----------------");
  Serial.println("Format EEPROM                           ----->  f");
  Serial.println("List EEPROM byte                        ----->  l"); 
  Serial.println("Write int Key int and *char Value       ----->  wi");
  Serial.println("Write *char Key int and *char Value     ----->  ws");
  Serial.println("Search Value with int Key               ----->  ri");
  Serial.println("Search Value with *char Key             ----->  rs"); 

  ReadStringFromSerial(command);
  if (strcmp(command,"f")==0)
    {   
      Serial.println("Format begin");
       DS_format();
      Serial.println("Format end");
    }
  else if (strcmp(command,"l")==0)
    {
      uint8_t val;
      for (int cont=0; cont<100; cont++)
      {
        val=eeprom_read_byte(cont);
        Serial.print(cont,DEC);
        Serial.print("-->");
        Serial.println(val,DEC);
      }
    }
  else if (strcmp(command,"wi")==0)   
    { 
      Serial.println("Insert Key");
      ReadStringFromSerial(keyString);
      Serial.println("Insert Value");
      ReadStringFromSerial(valueString);
      DS_put(atoi(keyString),valueString);       
    }
  else if (strcmp(command,"ws")==0)   
    { 
      Serial.println("Insert Key");
      ReadStringFromSerial(keyString);
      Serial.println("Insert Value");
      ReadStringFromSerial(valueString);
      DS_put(keyString,valueString);              
    }
  else if (strcmp(command,"ri")==0)   
    { 
      Serial.println("Insert Key");
      ReadStringFromSerial(keyString);     
      if (DS_get(atoi(keyString),searchedString)==1)
        {
          Serial.print(atoi(keyString));
          Serial.print(" ------------> ");
          Serial.println(searchedString);
        }     
    }
  else if (strcmp(command,"rs")==0)   
    { 
      Serial.println("Insert Key");
      ReadStringFromSerial(keyString);     
      if (DS_get(keyString,searchedString)==1)
        {
          Serial.print(keyString);
          Serial.print(" ------------> ");
          Serial.println(searchedString);
        }         
      
    }      

  delay(1000);                 
                
}

e il nuovo Header file in allegato.

DS_EEProm_New.h (8.28 KB)