Como utilizar a EEPROM do Arduino

A EEPROM do Arduino é uma memória que pode ser gravada e regravada, de forma a termos um lugar de armazenamento e consulta de dados. Existem também memórias EEPROM externas de diversos tamanhos, como já mostrei nesse outro artigo. Se você tem a necessidade de gravar valores que em algum momento podem mudar, esse é um dos caminhos, com custo adicional de R$0,00.

Características da EEPROM do Arduino

Enquanto memórias EEPROM externas permitem a pré-escolha do tamanho, a memória interna do Atmega328P é de 1024 Bytes. Não é um lugar para armazenar fotos, mas dá pra guardar valores de parametrização. Por acaso estou em um projeto que demandará guardar valores de calibração que podem variar conforme a altitude, por isso é fundamental que exista a possibilidade de fazer uma regravação desses parâmetros.

Quantas vezes a EEPROM pode ser gravada e lida?

Essa é a informação primordial para se ter em mente antes de usar a EEPROM como solução. Nela pode-se gravar e ler no mínimo 100.000 vezes, mas não há garantias do quanto a mais que isso ela pode ser lida ou escrita. A primeira coisa a considerar é se a EEPROM do Arduino é realmente a solução ideal para seu projeto. Explico as razões a seguir.

Usar a EEPROM como área de expansão

Se pretende expandir a área de gravação de dados do Arduino, não use a EEPROM para isso. A primeira razão é a velocidade, que poderá interferir no fluxo do programa. Lembre-se que a SRAM é mais rápida do que as demais memórias.

Uma outra questão importante em relação à EEPROM é que ela tem um ciclo de vida que pode ser relativamente curto, caso utilize-a de forma interativa ao fluxo do programa. Pense na EEPROM como um arquivo .ini, que conterá parâmetros a serem carregados na inicialização do programa principal. Considere também que uma microcontroladora tem por objetivo permanecer ligada tanto tempo quanto possível e outro comportamento que não esse tende a ser atípico. Se estiver dentro das condições de normalidade para o uso da EEPROM, então ela provavelmente será a solução ideal para seu projeto.

Tamanho da EEPROM do Arduino

Se estiver utilizando a EEPROM interna, terá no Atmega328P 1024 Bytes para escrita.  Como já citado, tamanhos diferentes podem ser encontrados nas EEPROM I2C, como a AT24C256, que conta com 32KB de espaço de armazenamento.

Tipos de memória do Arduino

Para ter o conceito devidamente firmado, é importante conhecer os 3 tipos de memória que se encontram no Arduino.

Flash – é o espaço do programa, onde se armazena o sketch do Arduino (o firmware compilado e gravado pelo avrdude).

SRAM – Essa é a memória estática de acesso randômico do Arduino, similar à memória RAM do computador pessoal, portanto, a memória onde são alocadas as variáveis do fluxo de execução do programa.

EEPROM – Essa memória tem o propósito de guardar informações variáveis sem a necessidade de gravar um novo sketch para isso.

Para finalizar, a memória flash e a EEPROM não são memórias voláteis (isto é, cujos dados se perdem em uma reinicialização do hardware). Seus dados persistem após uma reinicialização ou religamento.

Os tamanhos dessas memórias no Atmega328P (utilizado no UNO, Nano e similares) são:

flash: 32KB (5KB usados para o bootloader)
SRAM:   2KB
EEPROM: 1KB

O Atmega2560 (do Arduino Mega e similares) tem uma área consideravelmente maior:

flash: 256KB (8KB usados para o bootloader)
SRAM:   8KBB
EEPROM:  4KB

Algumas pessoas preferem dispensar o bootloader e fazer a gravação do programa diretamente na MCU para ganhar esses preciosos KB perdidos. Nunca tive a necessidade de fazê-lo por esgotamento de recursos.

Em relação à RAM, se houver esgotamento o programa falhará de formas inesperadas, como a não execução do programa ou valores estranhos. Uma boa ideia é deixar o compilador verboso na IDE que estiver utilizando para desenvolvimento e verificar a saída do avrdude para saber o quanto desses recursos está sendo consumido.

Alternativas e limitações

Também existe uma possibilidade que pode ser útil para alguns casos, como armazenar strings fora da memória RAM. Por exemplo, uma string de mensagem que deverá ser exibida em uma serial pode ser carregada utilizando a macro F(). Para tanto, basta fazer o print com essa chamada, como a seguir:

Serial.println(F("Alguma mensagem"));

No momento da compilação, o compilador compreenderá que essa é uma mensagem a ser armazenada na flash.

Para armazenar os dados de uma string estática (isto é, que não se modifica durante a utilização do programa) na memória flash, podemos usar a PROGMEM, que pode ser utilizada somente com os tipos de dados definidos no header pgmspace.h. Ela diz ao compilador para colocar a informação na memória flash, invés de colocá-la na SRAM, que é o caminho comum para variáveis de programa. Se quiser dar uma olhada nos tipos suportados, o link é esse. Como temos a possibilidade de utilizar o tipo void também, podemos à posteriori fazer casting do valor armazenado, o que também parece uma solução viável.

A biblioteca pgmspace.h é adicionada automaticamente nas IDEs mais atuais do Arduino, mas se em alguma IDE isso não acontecer, basta incluir:

#include <avr/pgmspace.h>

A implementação é um pouco atípica do que vemos todos os dias em códigos simples, então pode parecer visualmente desconfortável sua utilização, mas é tão simples quanto declarar um inteiro:

const dataType variableName[] PROGMEM = {dado1,dado2,dadoN};

Como recomendado na documentação do Arduino, o comportamento pode variar conforme o compilador, portanto a melhor forma de utilização do PROGMEM é adicioná-lo após o nome da variável, como demonstrado acima. Mas basicamente, a keyword PROGMEM pode estar antes de dataType ou entre dataTypevariableName.

Exemplo de uso da keyword PROGMEM

Apenas para que fique claro, aqui temos uma declaração  e leitura de um inteiro de 2 Bytes e uma array de char:

const uint16_t charSet[] PROGMEM = {65000,32796,16843,10,11234};
const char signMessage[] PROGMEM = {"String constante imutavel"};
...
void setup(){
    Serial.begin(9600);
    for (byte k = 0; k < 5; k++) {
        displayInt = pgm_read_word_near(charSet + k);
        Serial.println(displayInt);
    }

    for (byte k = 0; k < strlen_P(signMessage); k++) {
        myChar = pgm_read_byte_near(signMessage + k);
        Serial.print(myChar);
    }

    Serial.println();
}

void loop(){
...
}

Como pode-se notar, o loop anda no endereço de memória.

Onde é aplicável a utilização da keyword PROGMEM?

Por exemplo, para menus de displays ou mensagens transmitidas pela comunicação serial.

Lembre-se de que essas variáveis devem ser declaradas globalmente e nunca, jamais dentro de uma função.

Utilizando a EEPROM do Arduino

E pra finalizar, vamos retomemos o tema principal desse artigo, que é gravar e ler da EEPROM do Arduino.

A biblioteca EEPROM oferece diversos métodos para leitura, escrita e apagamento dos dados. Basicamente, dois desses métodos me foram fundamentais para o projeto; a EEPROM.put(address, data) e a EEPROM.get(address). Os métodos updateread esperam o tipo uint8_t, portanto não funcionarão com structs.

Para ficar bem fácil escrever na EEPROM sem precisar controlar o endereço de um determinado recurso, optei por criar uma struct, de modo a ter todas as variáveis de parâmetros em um lugar só, bastando recuperá-la no momento do boot da MCU.

struct exemplo {
    float offset;
    int records;
    int serial;
} parameters;

float offset = 0.0;
int records  = 1;
int serial   = 0x32;

void setup(){
    Serial.begin(9600);
    EEPROM.get(0,parameters);
...
}

void loop(){
    ...
    if (record_in_eeprom){
        EEPROM.put(addr, parameters);
    }
    ...
} 

Basicamente é isso. Agora já dá pra fazer umas brincadeiras extras com seu Arduino, hum?

Ainda estou caminhando com os projetos e meus trabalhos em paralelo, tenham paciência, a frequência de novos artigos serão restabelecidos em breve!

Case para Arduino UNO

O case em acrílico da imagem de destaque é uma reprodução do case em MDF ao lado, que cortei na CNC laser da ECNC, usando o acrílico especial da Sinteglas. O propósito inicial era vender pelo Mercado Livre, mas não há a menor possibilidade de vender esse case ao preço do feito em MDF. Ainda assim, se alguém demonstrar interesse e comentar no post lá na nossa página do facebook ou no Instagram digo o preço sugerido e corto para os interessados em um dos acrílicos especiais que colocarei no comentário também. O defeito que está nesse case cortado como teste já está corrigido e agora ambos estão com a mesma qualidade.

EEPROM do Arduino

 

Djames Suhanko

Djames Suhanko é Perito Forense Digital. Já atuou com deployer em sistemas de missão critica em diversos países pelo mundão. Programador Shell, Python, C, C++ e Qt, tendo contato com embarcados ( ora profissionalmente, ora por lazer ) desde 2009.