Ieri finalmente, dopo parecchio tempo, mi sono deciso a realizzare un progettino con il mio Arduino UNO, si tratta di un Data Logger:

un circuito che campiona un dato e lo salva su un E2PROM.

L’E2PROM in questione è la 24LC256 che ho ricevuto nel mio ultimo ordine dalla SparkFun Electronics, sfrutta un protocollo seriale quale l’i2c per comunicare con l’arduino; Non ho trovato una libreria per gestire le operazioni di scrittura e lettura, ma sul playground di arduino si può trovare un codice che presenta delle funzioni per scrivere e leggere sull’EEPROM: http://www.arduino.cc/playground/Code/I2CEEPROM.

L’E2PROM può memorizzare dati fino ad un massimo di 32Kbyte, ma non li sfrutteremo tutti.

In sostanza, si collega l’arduino al computer, si apre il monitor seriale e ci viene chiesto se leggere o scrivere (r\w) sull’eeprom, nel caso della scrittura il programma effettua delle letture analogiche sul pin A0 e ogni 10ms salva sulla eprom dei record formati da 3 dati: int N° Lettura, int Lettura e int Sec (millis() al momento della lettura).
Sappiamo che ogni int occupa 2 byte ed è per questo che divideremo ogni dato in due parti: MSB ed LSB.

Adesso sappiamo che ogni record occuperà 6 Byte sulla nostra memoria. Di seguito un immagine che mostra il concetto:

I collegamenti da effettuare sono scritti anche sulla pagina del playground arduino:

Arduino pin 4 to EEPROM pin 5
Arduino pin 5 to EEPROM pin 6
Arduino 5V to EEPROM pin 8
Arduino GND to EEPROM pin 1,2,3,4

Di seguito il codice sorgente:

#include <Wire.h> //I2C library
void i2c_eeprom_write_byte( int deviceaddress, unsigned int eeaddress, byte data ) {
  int rdata = data;
  Wire.beginTransmission(deviceaddress);
  Wire.send((int)(eeaddress >> 8)); // MSB
  Wire.send((int)(eeaddress & 0xFF)); // LSB
  Wire.send(rdata);
  Wire.endTransmission();
}

byte i2c_eeprom_read_byte( int deviceaddress, unsigned int eeaddress ) {
  byte rdata = 0xFF;
  Wire.beginTransmission(deviceaddress);
  Wire.send((int)(eeaddress >> 8)); // MSB
  Wire.send((int)(eeaddress & 0xFF)); // LSB
  Wire.endTransmission();
  Wire.requestFrom(deviceaddress,1);
  if (Wire.available()) rdata = Wire.receive();
  return rdata;
}

/* Unione e disione Byte
 somma = (b << 8) | a; Si uniscono MSB (b) ed LSB (a)

 //Si divide
 a = somma; LSB
 b = somma >> 8; MSB
 */

int no_lettura = 0;
int lettura = 0;
int sec = 0;

byte no_letturaMSB = B00000000;
byte no_letturaLSB = B00000000;
byte letturaMSB = B00000000;
byte letturaLSB = B00000000;
byte secMSB = B00000000;
byte secLSB = B00000000;

int mode = 0; //0 = Leggi; 1 = Scrivi;
void setup()
{
  Wire.begin(); // initialise the connection
  Serial.begin(9600);

  pinMode(A0, INPUT);

  Serial.print("Avvio del programma");
}

long wait = 0;
int indirizzo = 0;

void loop()
{
  chiedi();
  if (mode == 1){
    Serial.println("Inizio scrittura");
    for(no_lettura = 0; no_lettura <= 30;){
      //Se le letture sono inferiori a 30 deve continuare a scrivere l'eeprom
      if (wait < millis()){
        wait = millis() + 10;
        //Divido i dati

        sec = millis();
        secLSB = sec;
        secMSB = sec >> 8;

        lettura = analogRead(A0);
        letturaLSB = lettura;
        letturaMSB = lettura >> 8;

        no_letturaLSB = no_lettura;
        no_letturaMSB = no_lettura >> 8;

        //Scrivo sull'EEPROM; i2c_eeprom_write_byte( int deviceaddress, unsigned int eeaddress, byte data )
        i2c_eeprom_write_byte(0x50, indirizzo, no_letturaMSB);
        delay(5);
        indirizzo++;

        i2c_eeprom_write_byte(0x50, indirizzo, no_letturaLSB);
        delay(5);
        indirizzo++;

        i2c_eeprom_write_byte(0x50, indirizzo, letturaMSB);
        delay(5);
        indirizzo++;

        i2c_eeprom_write_byte(0x50, indirizzo, letturaLSB);
        delay(5);
        indirizzo++;

        i2c_eeprom_write_byte(0x50, indirizzo, secMSB);
        delay(5);
        indirizzo++;

        i2c_eeprom_write_byte(0x50, indirizzo, secLSB);
        delay(5);
        indirizzo++;

        Serial.print("\tLettura no: ");
        Serial.print(no_lettura);

        Serial.print("\tLettura: ");
        Serial.print(lettura);

        Serial.print("\tSec: ");
        Serial.println(sec);
        no_lettura++;
      }
    }
    mode = 2;
  }

  else if(mode == 0){

    //Se le letture sono 30 legge dall'eeprom
    Serial.println("Inizio lettura");
    //Reset variabili
    indirizzo = 0;
    no_letturaMSB = B00000000;
    no_letturaLSB = B00000000;
    letturaMSB = B00000000;
    letturaLSB = B00000000;
    secMSB = B00000000;
    secLSB = B00000000;
    delay(5);

    //Leggo dall'EEPROM; i2c_eeprom_read_byte( int deviceaddress, unsigned int eeaddress )
    for(int i = 0; i <= 30; i++){
      no_letturaMSB = i2c_eeprom_read_byte(0x50, indirizzo);
      delay(5);
      indirizzo++;

      no_letturaLSB = i2c_eeprom_read_byte(0x50, indirizzo);
      delay(5);
      indirizzo++;

      letturaMSB = i2c_eeprom_read_byte(0x50, indirizzo);
      delay(5);
      indirizzo++;

      letturaLSB = i2c_eeprom_read_byte(0x50, indirizzo);
      delay(5);
      indirizzo++;

      secMSB = i2c_eeprom_read_byte(0x50, indirizzo);
      delay(5);
      indirizzo++;

      secLSB = i2c_eeprom_read_byte(0x50, indirizzo);
      delay(5);
      indirizzo++;

      no_lettura = (no_letturaMSB << 8) | no_letturaLSB;
      lettura = (letturaMSB << 8) | letturaLSB;
      sec = (secMSB << 8) | secLSB;

      Serial.print("\tLettura no: ");
      Serial.print(no_lettura);

      Serial.print("\tLettura: ");
      Serial.print(lettura);

      Serial.print("\tSec: ");
      Serial.println(sec);

    }
    mode = 2;
  }
}

void chiedi() {
    Serial.println("\n\nLeggi o Scrivi? (r\\w)");
  while (Serial.available() <= 0){}

    if(Serial.read() == 'w')
      mode = 1;
    else
      mode = 0;
  }

C’è un piccolo accorgimento da fare riguardante il dato Sec: ho già detto prima che quel dato in sostanza fa lo stamp dei millisecondi passati dall’accensione del microprocessore. La funzione millis() restituisce un dato long, ma noi immagazziniamo il valore in un int per questione di comodità, con un int possiamo raggiungere un valore massimo di (2^16) – 1 = 65535 che in secondi corrisponde a circa 65 secondi, dunque dopo 65 secondi la variabile sec va in overflow memorizzando il valore massimo (come detto prima 65535). Per risolvere si potrebbe, anzichè salvare i millisecondi trascorsi dall’accensione del microprocessore, i secondi:

        sec = millis() / 1000;

 

Data Logger con Arduino ed EEPROM 24LC256
Tags: