05 março 2015

Como gravar dados na memória EEPROM do Arduino

Um recurso nem sempre utilizado no Arduino é a gravação de dados na memória EEPROM do microcontrolador. Com esse método de armazenamento, você grava os dados em uma parte, digamos, "protegida" da memória, de forma que eles não se perdem quando acontece uma queda de energia, ou mesmo em casos de travamento do Arduino.

gravar dados arduino memoria EEPROM


EEPROM significa Electrically-Erasable Programmable Read-Only Memory (ou, Memória Somente de Leitura Programável Apagável Eletricamente) e é uma memória que pode ser apagada e reprogramada várias vezes, apesar de possuir um limite no número de ciclos de leitura/escrita.


Gravando na EEPROM no Arduino

A quantidade de memória EEPROM varia conforme o modelo da placa. O Arduino Uno (microcontrolador ATMega328), tem 1 KB de memória EEPROM. Já o Arduino Mega (microcontrolador ATMega2560) tem 4 KB. No Arduino Nano com microcontrolador ATMega168, esse valor cai para 512 bytes.

O comando para escrever na memória EEPROM é o EEPROM.write(endereço, valor), e exige o uso da biblioteca EEPROM , que já faz parte da IDE do Arduino.

O endereço tem o valor inicial em 0, e vai até a capacidade total da memória EEPROM. Assim, se você estiver trabalhando com um Arduino Uno, você tem 1024 posições de memória pra gravar os dados, um byte (8 bits) de cada vez, ou seja, o maior valor que você consegue gravar em uma posição de memória é 255:

8 bits = 1 byte
A leitura é feita pelo comando EEPROM.read(endereço). Veja neste programa de exemplo :

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
// Programa : Escrita e leitura EEPROM
// Autor : Arduino e Cia

//Carrega a biblioteca
#include <EEPROM.h>

//Numero a ser armazenado
int numero = 123;
int endereco = 0;

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

void loop() 
{
  //Escrita
  Serial.print("Gravando numero na memoria EEPROM : ");
  Serial.println(numero);
  EEPROM.write(endereco, numero);
  delay(2000);
  //Leitura
  Serial.print("Lendo numero da memoria EEPROM : ");
  Serial.println(EEPROM.read(endereco));
  while(1)
  {}
}

Mas se cada posição armazena apenas um byte, como eu vou armazenar um número inteiro (2 bytes) na EEPROM ?

Gravando um número inteiro na memória EEPROM

Um INT (inteiro) no Arduino Uno e em outras placas baseadas no ATMega, é um número entre -32.768 e 32.767, que ocupa 2 bytes (16 bits) de memória.

Uma das maneiras de gravar um número inteiro na EEPROM é separar esse número em duas partes, e gravar cada uma separadamente.

Digo "uma das maneiras", porque você pode utilizar as próprias funções da biblioteca para gravar dados na EEPROM, usando os comandos abaixo :

void eeprom_write_word (uint16_t *__p, uint16_t __value)
void eeprom_write_float (float *__p, float __value)
void eeprom_write_block (const void *__src, void *__dst, size_t __n)

A primeira opção, por exemplo, utiliza o uint16_t, que é um unsigned int de 16 bits, cujo valor vai de 0 a 65.535.

No meu método mais arcaico, eu armazeno o INT dividindo-o em duas partes : primeiro divido o número que desejo armazenar por 256, obtendo a primeira parte, e depois uso a função % (modulo), para obter a segunda parte. Posteriormente, coloco cada uma delas em um endereço de memória diferente.

Eu faço isso no programa abaixo, gravando o número 23767 na memória separando-o assim:

Primeira parte : 23767 / 256 = 92 (apenas a parte inteira do número)
Segunda parte :  23767 % 256 = 215 (resto da divisão)

Depois de ler o valor em memória, preciso montar novamente o valor original, juntando as 2 partes. Para isso uso :

Valor original = (parte1 * 256) + parte2

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
// Programa : Escrita e leitura EEPROM - INT
// Autor : Arduino e Cia

// Carrega a biblioteca
#include <EEPROM.h>

// Numero a ser armazenado
int numero = 23767;
// Endereco onde sera armazenada a parte1 (primeiro byte)
int endereco1 = 0;
// Endereco onde sera armazenada a parte2 (segundo byte)
int endereco2 = 1;

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

void loop() 
{
  Serial.print("Gravando numero na memoria EEPROM : ");
  Serial.println(numero);
  
  // Grava a primeira parte do numero em endereco1
  EEPROM.write(0, numero/256);
  Serial.print("Parte 1 : ");
  Serial.println(numero/256);
  
  // Grava a segunda parte do numero em endereco2
  EEPROM.write(1, numero%256);
  Serial.print("Parte 2 : ");
  Serial.println(numero%256);
  
  delay(2000);
  
  // Leitura
  Serial.println("\nLendo numero da memoria EEPROM... ");
  
  Serial.print("Lendo parte 1 : ");
  int parte1 = EEPROM.read(0);
  Serial.println(parte1);
  
  Serial.print("Lendo parte 2 : ");  
  int parte2 = EEPROM.read(1);
  Serial.println(parte2);
  
  Serial.print("Valor original : ");
  int valor_original = (parte1 * 256) + parte2;
  Serial.println(valor_original);
  while(1)
  {}
}

Isso quer dizer que a partir de agora você pode gravar todos os dados na memória EEPROM do Arduino ? Infelizmente não.

Segundo o datasheet do ATMega328P , temos um limite de ciclos de leitura e escrita na memória EEPROM. Esse número é de aproximadamente 100.000 ciclos.

Apesar de ser um número relativamente grande, procure usar um intervalo maior para gravação, ou utilize-o apenas para valores essenciais, como por exemplo os valores máximo e mínimo de um sensor de temperatura.

21 comentários:

  1. Prezados senhores, apliquei o seu principio para um hidrometro que eu estou desenvolvendo, porem os valores estão na casa dos 56179 litros e da erro ao aplicar a formula. Tem como altera-la par a que possa trabalhar com 7 digitos ? Agradeço o retorno pelo e-mail marcelo@occ.pro.br .

    ResponderExcluir
    Respostas
    1. Boa tarde,

      Entendi... tente este artigo, creio que tem o que você precisa :

      http://playground.arduino.cc/Code/EEPROMWriteAnything

      Abraço !

      Excluir
  2. Boa tarde,
    Sou novo no mundo arduino, alias em eletrônica.
    Tenho um citcuto q acenfe 16 leds d forma sequencial, com intervalos programados e apresentados no lcd.
    Acontece q quando desligo tenho q programar tudo d novo.
    Gostaria d gravar na eeprom, tentei gravar e da sempre umnúmero diferente, ex: deixo programado 10000ms qdo ligo ta em 9994ms.
    O q pode ser?
    Obrigado.
    ps. Nao queria comentar como anônimo, mas nao consegui pela conta do Google
    carloscarrera47@gmail.com

    ResponderExcluir
    Respostas
    1. Boa noite Carlos,

      Você está usando o método de gravar um número inteiro ou o método "simples", que grava apenas até 255 ? Pode ser esse o problema.

      Abraço !

      Excluir
  3. boa noite , tenho um gerador de sinal ajustável controlado pelo arduino que gera um sinal de 1 a 30mhz , quando ele é desligado e eu religo volta sempre na mesma frequência inicial , gostaria defase-lo voltar na ultima frequência ajustada antes do desligamento , pode me ajudar ? parabéns pelo blog ..sempre estou lendo seus artigos

    ResponderExcluir
    Respostas
    1. Boa noite,

      Obrigado. Você vai gravar um número entre 0 e 255 ? Vc pode usar este exemplo do artigo mesmo.

      Abraço !

      Excluir
  4. Boa tarde. Utilizando o exemplo acima como gravar por exemplo o nr 65.535. Muito obrigado. Abraço.

    ResponderExcluir
    Respostas
    1. Boa noite,

      Utilize o segundo programa deste post, ou então a função void eeprom_write_word (uint16_t *__p, uint16_t __value).

      Abraço!

      Excluir
  5. olá boa noite amigo gostaria de lhe pedir ajuda:

    é seguinte amigo é que eu tou usando um projetinho arduino +bluetooth eu uso este código:



    void setup()
    {
    pinMode(13,OUTPUT);
    pinMode(12, OUTPUT);
    pinMode(11, OUTPUT);
    pinMode(10, OUTPUT);
    pinMode(9, OUTPUT);


    Serial.begin(9600);
    }

    void loop()
    {delay(1000);
    if (Serial.available())
    {int c=Serial.read();
    if(c=='y')
    {
    digitalWrite(13,HIGH);
    Serial.println("Ok, Moto ligada!");
    }
    else if(c=='w')
    {
    digitalWrite(13,LOW);
    Serial.println("Ok, Moto desligada!");
    }
    if(c=='b')
    {
    digitalWrite(12,HIGH);
    Serial.println("Ok, Partida ligada!");

    }
    else if(c=='v')
    {
    digitalWrite(12,LOW);
    Serial.println("Ok, led desligado!");
    }
    if(c=='c')
    {

    digitalWrite(11,HIGH);
    Serial.println("Ok, led ligado!");

    }
    else if(c=='x')
    {
    digitalWrite(11,LOW);
    Serial.println("Ok, led desligado!");
    }
    if(c=='d')

    {
    digitalWrite(10,HIGH);
    Serial.println("Ok, led ligado!");

    }
    else if(c=='z')
    {
    digitalWrite(10,LOW);
    Serial.println("Ok, led desligado!");
    }

    if(c=='e')

    {
    digitalWrite(9,HIGH);
    Serial.println("Ok, led ligado!");

    }
    else if(c=='t')
    {
    digitalWrite(9,LOW);
    Serial.println("Ok, led desligado!");
    }}} como é visto no código eu ligo e desligo as portas configuradas enviando caracteres. Eu quero quando eu enviar por ex: o "y" que liga a porta 13, quando eu desligar o Arduino e ligar novamente esta porta continue ligada, entende amigo, ou seja eu quero memorizar o ultimo estado da porta. porem não sei como fazer isso. por favor me ajude amigo

    ResponderExcluir
  6. Este comentário foi removido pelo autor.

    ResponderExcluir
  7. Este comentário foi removido pelo autor.

    ResponderExcluir
  8. Olá Amigo !!! Será que você pode me ajudar ? Eu utilizei esse segundo código mas que usa um numero conhecido: int numero = 23767; E se eu quisesse armazenar um numero que veio pela serial e que fica armazenado numa variável: Fiz esse código que eu mostro abaixo mas sempre retorna valor original 49. O que eu estou fazendo de errado ? Grande Abraço !!! Michel Ferrari.

    #include

    // Numero a ser armazenado
    int numero;
    int contador;

    // Endereco onde sera armazenada a parte1 (primeiro byte)
    int endereco1 = 0;
    // Endereco onde sera armazenada a parte2 (segundo byte)
    int endereco2 = 1;

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

    void loop()
    {

    if (Serial.available()){
    contador = Serial.read();
    numero = contador;
    }

    if (numero != 0){

    Serial.print("Gravando numero na memoria EEPROM : ");
    Serial.println(numero);

    // Grava a primeira parte do numero em endereco1
    EEPROM.write(0, numero/256);
    Serial.print("Parte 1 : ");
    Serial.println(numero/256);

    // Grava a segunda parte do numero em endereco2
    EEPROM.write(1, numero%256);
    Serial.print("Parte 2 : ");
    Serial.println(numero%256);

    delay(2000);

    // Leitura
    Serial.println("\nLendo numero da memoria EEPROM... ");

    Serial.print("Lendo parte 1 : ");
    int parte1 = EEPROM.read(0);
    Serial.println(parte1);

    Serial.print("Lendo parte 2 : ");
    int parte2 = EEPROM.read(1);
    Serial.println(parte2);

    Serial.print("Valor original : ");
    int valor_original = (parte1 * 256) + parte2;
    Serial.println(valor_original,DEC);
    while(1);
    {}
    }
    }

    ResponderExcluir
  9. pessoal,,, sou novo no assunto então vou direto ao meu ponto
    preciso acionar um botão fisico (entrada) que saia na saida como audio
    minha pergunta é,,, como estocar esse audio na memoria dele, e qual formato é aceito

    ResponderExcluir
    Respostas
    1. Bom dia Fernando,

      Dependendo do formato de áudio, vai ser mais fácil vc armazenar isso em um cartão SD, ou se quiser mesmo armazenar na memória, adquirir uma "expansão de memória EEPROM" para Arduino, já que a memória embutida no microcontrolador geralmente é pequena (1 ou 2K, por exemplo).

      Abraço!

      Excluir
  10. ola, boa noite, gostaria de saber como posso fazer pra gravar esse dado em hexa 03 A5 CE F7 ?

    ResponderExcluir
  11. Olá.
    Alguem teria um sketch referencia para gravar valores do teclado 4x3 pra gravar senha de 4 digitos. Somente preciso da parte da senha na EEPROM, o resto ja tenho tudo.
    Obrigaado

    ResponderExcluir
  12. Olá,
    estou precisando trabalhar com hexadecimal preciso gravar o valor diretamente na memoria, EX: gravar o valor "02 em hexadecimal" no endereço "0" da memoria eeprom. Existe alguma solução para isso?

    ResponderExcluir
  13. Olá, o que acontece após atingir os 100.000 ciclos da memória EEPROM?
    Mesmo, se eu inserir um valor zero na memória, é contada como um ciclo?
    Se eu apertar no botão Reset do arduino, eu vou a ter os 100.000 ciclos novamente?

    Obrigado.

    ResponderExcluir
    Respostas
    1. Boa noite,

      Não, quando ele atingir 100.000 ciclos, ele simplesmente não vai gravar nada naquela posição de memória. O botão de reset não tem efeito sobre esse contador.

      Abraço!

      Excluir
  14. olá pessoal, sou inciante ainda, e gostaria de saber se é possível gravar um valor lido no sensor apenas a primeira vez na eeprom e depois ele ler novamente o sensor e apenas comparar ? se for possível possuem algum código de exemplo ?

    ResponderExcluir
  15. O senhor pode nos dar um exemplo de como escrever um dado float na EEPROM?

    ResponderExcluir