EEPROM do Arduino: como usar read, write, update, put e get
EEPROM do Arduino explicada: 1024 bytes no Uno, read, write, update, put e get, o limite de 100.000 gravações, como salvar contadores e senhas e Preferences no ESP32.
A EEPROM do Arduino é uma pequena memória permanente que mantém seu conteúdo mesmo quando a placa fica sem alimentação. O ATmega328P do Arduino Uno, Nano e Pro Mini tem 1024 bytes de EEPROM, e você a acessa com os comandos EEPROM.read(), EEPROM.write(), EEPROM.update(), EEPROM.put() e EEPROM.get() da biblioteca EEPROM que já vem com a Arduino IDE. A RAM que guarda suas variáveis é apagada a cada reinicialização, mas uma configuração, um contador ou uma senha que você grava na EEPROM continua lá quando a placa é ligada de novo.
Neste guia você vai ver como a EEPROM funciona, quanto dela cada placa tem, como os comandos se diferenciam e a pergunta que todo mundo faz: o que a vida útil de cerca de 100.000 gravações significa na prática. Depois vamos montar, passo a passo, um contador que lembra seu valor mesmo com quedas de energia, uma estrutura de configurações que guarda uma senha e opções de uma só vez e um buffer circular que multiplica a vida útil de gravação por 16. Nas últimas seções você vai aprender a fazer o mesmo no ESP32, que não tem EEPROM de verdade, com a biblioteca Preferences, e no Raspberry Pi Pico com o sistema de arquivos do MicroPython.

O que é EEPROM e como funciona?
EEPROM é a sigla de Electrically Erasable Programmable Read-Only Memory (memória somente de leitura programável e apagável eletricamente). Cada célula de memória guarda a informação como uma carga elétrica presa dentro de uma porta isolada, e essa carga permanece quando a energia acaba. Pense na EEPROM do Uno como um armário com 1024 gavetas: cada gaveta tem um endereço de 0 a 1023 e guarda exatamente 1 byte, um número entre 0 e 255. Uma célula que nunca foi gravada é lida como 255 (0xFF). A leitura é quase instantânea e ilimitada; a gravação leva cerca de 3,3 ms por byte e desgasta um pouco a célula. O datasheet do ATmega328P especifica pelo menos 100.000 ciclos de gravação/apagamento por célula de EEPROM e 100 anos de retenção de dados a 25 °C.
| Característica | Flash (memória de programa) | SRAM | EEPROM |
|---|---|---|---|
| Tamanho no Uno | 32 KB (0,5 KB para o bootloader) | 2 KB | 1 KB (1024 bytes) |
| O que é guardado? | O sketch que você carregou | Variáveis, arrays | Configurações, contadores, recordes |
| Quando falta energia | Mantido | Apagado | Mantido |
| Gravável com o sketch em execução? | Normalmente não (só leitura via PROGMEM) | Sim, ilimitado | Sim, com a biblioteca EEPROM |
| Vida útil de gravação | Cerca de 10.000 | Ilimitada | Cerca de 100.000 por célula |
| Velocidade de gravação | Só ao carregar o código | Um ciclo de clock | Cerca de 3,3 ms por byte |
Resumindo, a SRAM é um quadro branco e a EEPROM é um caderno. Você pode escrever e apagar no quadro quantas vezes quiser, mas tudo some quando as luzes se apagam; o que você escreve no caderno fica, mas não dá para apagar e reescrever a mesma página para sempre. Por isso a EEPROM é ideal para informações pequenas e que mudam pouco: valores de calibração, configurações do usuário, brilho ou volume, o recorde de um jogo, a senha de uma porta, cartões RFID cadastrados ou o último estado de uma lâmpada. Para dados grandes e gravados com frequência, como um registro de temperatura que grava a cada segundo, um módulo de cartão SD é a melhor escolha. Se você quer entender por que a RAM enche tão rápido, leia Arduino sem memória.
Quanto de EEPROM cada placa tem?
| Placa | Processador | EEPROM | Observação |
|---|---|---|---|
| Arduino Uno R3, Nano, Pro Mini | ATmega328P | 1024 bytes | EEPROM real |
| Arduino Mega 2560 | ATmega2560 | 4096 bytes | EEPROM real |
| Arduino Leonardo, Micro | ATmega32U4 | 1024 bytes | EEPROM real |
| Arduino Nano Every | ATmega4809 | 256 bytes | EEPROM real, mas pequena |
| Arduino Uno R4 Minima, WiFi | Renesas RA4M1 | 8 KB | Flash de dados; a biblioteca EEPROM funciona com os mesmos comandos |
| ESP32 | ESP32 (Xtensa) | Nenhuma | Partição NVS na flash; recomenda-se Preferences |
| ESP8266 (NodeMCU, Wemos D1) | ESP8266 | Nenhuma | Emulada na flash; requer EEPROM.begin() e commit() |
| Raspberry Pi Pico | RP2040 | Nenhuma | Um arquivo no MicroPython; EEPROM emulada na flash com o core Arduino |
Se o seu código vai passar de uma placa para outra, não fixe o tamanho da EEPROM no código; EEPROM.length() retorna o tamanho real da EEPROM da placa (1024 no Uno, 4096 no Mega), e assim o mesmo código de limpeza ou de leitura completa funciona em qualquer uma. Para outros detalhes de memória e pinos do Uno, veja Pinagem do Arduino Uno, e para escolher uma placa, leia Qual Arduino comprar?.
Comandos da EEPROM: read, write, update, put e get
A biblioteca EEPROM vem com o pacote da placa, então não há nada para instalar; basta adicionar a linha #include <EEPROM.h> no topo do sketch. Os endereços começam em 0 e terminam em 1023 no Uno. A tabela abaixo resume o que cada comando faz e se ele desgasta as células:
| Comando | O que faz | Tamanho | Desgaste |
|---|---|---|---|
| EEPROM.read(endereço) | Lê um byte (0–255) | 1 byte | Nenhum, a leitura é livre |
| EEPROM.write(endereço, valor) | Regrava o byte a cada chamada | 1 byte | Gasta um ciclo a cada chamada |
| EEPROM.update(endereço, valor) | Lê primeiro e só grava se o valor for diferente | 1 byte | Só quando o valor muda |
| EEPROM.put(endereço, dados) | Grava um int, float, array ou struct byte a byte; usa update internamente | sizeof(dados) | Só os bytes que mudaram |
| EEPROM.get(endereço, variável) | Lê de volta o que put gravou, no mesmo tipo | sizeof(variável) | Nenhum |
| EEPROM[endereço] | Acesso como array; ler é livre, atribuir com = funciona como write | 1 byte | A cada atribuição |
| EEPROM.length() | Retorna o tamanho total da EEPROM | - | Nenhum |
Primeiro sketch: gravar e ler dados na EEPROM
1#include <EEPROM.h>
2
3void setup() {
4 Serial.begin(9600);
5 Serial.print(F("EEPROM boyutu / size: "));
6 Serial.println(EEPROM.length()); // Uno'da 1024 / 1024 on the Uno
7
8 byte first = EEPROM.read(0); // 0. adresi oku / read address 0
9 Serial.print(F("Adres 0 / address 0: "));
10 Serial.println(first); // ilk seferde 255 / 255 the first time
11
12 EEPROM.update(0, 42); // farklıysa yazar / writes only if different
13 EEPROM.write(1, 7); // her seferinde yazar / writes every time
14
15 int number = 300;
16 EEPROM.put(10, number); // int = 2 bayt: 10 ve 11 / int = 2 bytes: 10 and 11
17 float temperature = 23.75;
18 EEPROM.put(12, temperature); // float = 4 bayt: 12-15 / float = 4 bytes: 12-15
19
20 int readNumber;
21 float readTemperature;
22 EEPROM.get(10, readNumber); // put ile aynı tür / same type as put
23 EEPROM.get(12, readTemperature);
24 Serial.println(readNumber); // 300
25 Serial.println(readTemperature); // 23.75
26
27 EEPROM.update(20, 300); // HATA: yalnız alt bayt (44) kalır / BUG: only the low byte (44) is kept
28 Serial.println(EEPROM.read(20)); // 44
29}
30
31void loop() {}Carregue o sketch e abra o Monitor Serial a 9600 baud. Na primeira execução o endereço 0 é lido como 255, porque essa célula nunca foi gravada. Agora aperte o botão de reset da placa ou desconecte e reconecte o cabo USB: desta vez a leitura é 42. Essa é a prova de que a EEPROM guarda seus dados. Já a linha write() do endereço 1 regrava a célula a cada inicialização, mesmo que o valor seja sempre 7; com update() ela teria gravado só na primeira vez. O valor 300 na última linha mostra a armadilha do aviso acima, e 44 aparece na tela. Se a configuração Avisos do compilador em Preferências estiver em qualquer opção diferente de Nenhum, o compilador marca essa linha com large integer implicitly truncated to unsigned type.
Ao usar put() e get(), você precisa saber quantos bytes cada dado ocupa; senão vai gravar duas variáveis uma em cima da outra e corromper uma delas. No exemplo, o int ocupa os endereços 10 e 11, então o float começa no endereço 12. Estes são os tamanhos dos tipos de dados no Uno:
| Tipo de dado | Tamanho no Uno | Faixa | Exemplo de uso |
|---|---|---|---|
| byte, bool, char (tipos de 1 byte) | 1 byte | 0–255, true/false ou um caractere | Estado ligado/desligado, seleção de modo |
| int | 2 bytes | −32.768 a 32.767 | Offset de calibração |
| unsigned int, uint16_t | 2 bytes | 0–65.535 | Recorde de jogo |
| long, unsigned long (inteiros de 4 bytes) | 4 bytes | Cerca de ±2,1 bilhões ou 0–4,29 bilhões | Contador de longa duração |
| float | 4 bytes | 6–7 dígitos de precisão | Fator de calibração de sensor |
| char vetor[10] | 10 bytes | 9 caracteres mais o terminador | Senha, nome do dispositivo |
Vida útil da EEPROM: o que significa o limite de 100.000?
100.000 gravações parece muito, mas um sketch mal estruturado pode esgotar isso em minutos. A fórmula da vida útil é simples: vida útil = 100.000 ÷ frequência de gravação na mesma célula. O ponto principal é que o limite vale para cada célula separadamente, não para a EEPROM inteira; se você continuar gravando no mesmo endereço, só essa célula se desgasta. A leitura não tem nenhum efeito na vida útil. A tabela mostra quanto tempo uma célula dura quando o mesmo endereço é gravado em ritmos diferentes:
| Frequência de gravação no mesmo endereço | Vida útil aproximada |
|---|---|
| write() no loop() sem espera (cerca de 300 vezes por segundo) | 5–6 minutos |
| A cada segundo | Cerca de 28 horas |
| A cada minuto | Cerca de 69 dias |
| A cada 10 minutos | Cerca de 1,9 ano |
| Uma vez por hora | Cerca de 11 anos |
| 10 vezes por dia | Cerca de 27 anos |
100.000 é o mínimo garantido pelo fabricante; muitos chips duram bem mais, mas não planeje seu projeto contando com isso. Uma célula desgastada não morre de repente; ela começa a não conseguir manter o valor gravado e alguns bits mudam sozinhos. Se isso acontecer, você pode mover os dados para outro endereço, porque as outras células não são afetadas. Formas de prolongar a vida útil:
- Use update() e put(): os dois leem a célula antes de gravar e não gravam nada se o valor for o mesmo.
- Grave só quando mudar: salve uma configuração quando o usuário realmente a alterar, não a cada passagem pelo loop().
- Atrase o salvamento: se um valor muda várias vezes seguidas, grave uma única vez alguns segundos após a última mudança; millis() facilita isso.
- Grave em eventos: salve o recorde quando o jogo acabar e as configurações do menu quando o usuário sair do menu.
- Espalhe as gravações: grave um contador que muda com frequência em vários endereços alternadamente, em vez de um só (veja o exemplo de buffer circular abaixo).
- Use memória externa se precisar: para salvamentos muito frequentes, escolha uma EEPROM externa de 1 milhão de ciclos, FRAM ou um cartão SD.
Exemplo 1: contador que sobrevive a quedas de energia
No primeiro exemplo vamos montar um contador que aumenta a cada vez que um botão é pressionado e continua de onde parou depois que a placa é desligada e ligada. Ligue um terminal do botão ao D2 e o outro ao GND; usamos o resistor de pull-up interno, então não é preciso resistor extra (detalhes em Como ligar um botão no Arduino, posição dos pinos na ferramenta Pinagem). O truque é que o sketch não salva o contador a cada toque, e sim 5 segundos após o último toque. Assim, mesmo que alguém aperte o botão 20 vezes seguidas, a EEPROM é gravada uma única vez.
1#include <EEPROM.h>
2
3const byte BUTTON_PIN = 2; // buton D2 ile GND arasında / button between D2 and GND
4const int COUNT_ADDR = 0; // sayaç 0-3. baytlarda / counter in bytes 0-3
5const unsigned long SAVE_DELAY = 5000; // son basıştan 5 sn sonra yaz / write 5 s after the last press
6
7unsigned long count = 0;
8bool dirty = false; // kaydedilmemiş değişiklik / unsaved change
9unsigned long lastPress = 0;
10unsigned long lastChange = 0;
11bool lastReading = HIGH;
12bool stableState = HIGH;
13
14void setup() {
15 Serial.begin(9600);
16 pinMode(BUTTON_PIN, INPUT_PULLUP);
17 EEPROM.get(COUNT_ADDR, count);
18 if (count == 0xFFFFFFFF) count = 0; // boş EEPROM / blank EEPROM
19 Serial.print(F("Kayitli sayac / stored count: "));
20 Serial.println(count);
21}
22
23void loop() {
24 bool reading = digitalRead(BUTTON_PIN);
25 if (reading != lastReading) { // pin değişti, sayacı başlat / pin changed, restart timer
26 lastChange = millis();
27 lastReading = reading;
28 }
29 if (millis() - lastChange > 30 && reading != stableState) { // 30 ms sabit kaldı / stable for 30 ms
30 stableState = reading;
31 if (stableState == LOW) { // yeni basış / new press
32 count++;
33 dirty = true;
34 lastPress = millis();
35 Serial.println(count);
36 }
37 }
38
39 if (dirty && millis() - lastPress > SAVE_DELAY) { // sessiz dönemden sonra yaz / write after a quiet period
40 EEPROM.put(COUNT_ADDR, count);
41 dirty = false;
42 Serial.println(F("EEPROM'a yazildi / saved to EEPROM"));
43 }
44}A variável dirty indica que existe uma alteração na RAM que ainda não foi salva. O contador é um unsigned long, então ocupa 4 bytes (endereços 0–3) e pode contar mais de 4 bilhões de toques. Numa EEPROM em branco esses quatro bytes são 0xFF, então a primeira inicialização lê 4.294.967.295; quando o código vê esse valor, começa o contador do zero. Como put() só grava os bytes que mudaram, normalmente só o byte mais baixo se desgasta enquanto o contador sobe. Se a energia cair nos 5 segundos antes de um salvamento, os últimos toques se perdem; é um preço pequeno para proteger a EEPROM.
Exemplo 2: guardar configurações e uma senha em uma struct
Em vez de gravar várias configurações em endereços separados, é mais organizado e seguro reuni-las em uma struct e salvá-las com um único put(). Neste exemplo guardamos o brilho do LED e uma senha de 4–6 caracteres. Os dois primeiros campos da estrutura são essenciais: magic (um número mágico) diz se as configurações deste sketch foram mesmo gravadas na EEPROM, e version impede que dados antigos e incompatíveis sejam lidos depois que você adicionar um novo campo à estrutura. Se algum dos dois não bater, o sketch grava as configurações padrão.
1#include <EEPROM.h>
2
3const uint16_t MAGIC = 0xB6A1; // "ayar yazıldı" işareti / "settings written" marker
4const byte VERSION = 1; // yapı değişince artır / bump when the struct changes
5const int SETTINGS_ADDR = 0;
6const byte LED_PIN = 9; // PWM pini / PWM pin
7
8struct Settings {
9 uint16_t magic;
10 byte version;
11 char password[7]; // en çok 6 karakter + '\0' / up to 6 chars + '\0'
12 byte brightness; // LED parlaklığı 0-255 / LED brightness 0-255
13};
14
15Settings cfg;
16
17void writeDefaults() {
18 cfg.magic = MAGIC;
19 cfg.version = VERSION;
20 strcpy(cfg.password, "1234");
21 cfg.brightness = 128;
22 EEPROM.put(SETTINGS_ADDR, cfg);
23}
24
25void setup() {
26 Serial.begin(9600);
27 pinMode(LED_PIN, OUTPUT);
28 EEPROM.get(SETTINGS_ADDR, cfg);
29 if (cfg.magic != MAGIC || cfg.version != VERSION) { // ilk açılış ya da eski yapı / first boot or old layout
30 writeDefaults();
31 Serial.println(F("Varsayilan ayarlar yazildi / defaults written"));
32 }
33 analogWrite(LED_PIN, cfg.brightness);
34 Serial.print(F("Ayar boyutu / settings size: "));
35 Serial.println(sizeof(Settings)); // Uno'da 11 bayt / 11 bytes on the Uno
36 Serial.println(F("Komutlar / commands: b0-255 p<eski>,<yeni> ?"));
37}
38
39void loop() {
40 if (!Serial.available()) return;
41 String line = Serial.readStringUntil('\n');
42 line.trim();
43
44 if (line.startsWith("b")) { // örnek / example: b200
45 cfg.brightness = constrain(line.substring(1).toInt(), 0, 255);
46 analogWrite(LED_PIN, cfg.brightness);
47 EEPROM.put(SETTINGS_ADDR, cfg); // yalnız değişen bayt yazılır / only the changed byte is written
48 Serial.println(F("Parlaklik kaydedildi / brightness saved"));
49 } else if (line.startsWith("p")) { // örnek / example: p1234,5678
50 int comma = line.indexOf(',');
51 if (comma < 0) {
52 Serial.println(F("Bicim / format: p1234,5678"));
53 return;
54 }
55 String oldPw = line.substring(1, comma);
56 String newPw = line.substring(comma + 1);
57 if (oldPw != cfg.password) {
58 Serial.println(F("Eski sifre yanlis / wrong old password"));
59 } else if (newPw.length() < 4 || newPw.length() > 6) {
60 Serial.println(F("Yeni sifre 4-6 karakter olmali / new password must be 4-6 chars"));
61 } else {
62 newPw.toCharArray(cfg.password, sizeof(cfg.password));
63 EEPROM.put(SETTINGS_ADDR, cfg);
64 Serial.println(F("Sifre degisti / password changed"));
65 }
66 } else if (line == "?") {
67 Serial.print(F("Parlaklik / brightness: "));
68 Serial.println(cfg.brightness);
69 }
70}Ligue o LED ao D9 através de um resistor para LED (220 ohms serve); o D9 é um pino PWM. Abra o Monitor Serial a 9600 baud, defina o final de linha como Nova linha e teste estes comandos: b200 define o brilho em 200, p1234,5678 muda a senha de 1234 para 5678 e ? mostra o brilho atual. Ao desligar e ligar a placa de novo, o LED volta com o mesmo brilho e a nova senha continua valendo. A struct ocupa 11 bytes no Uno; como put() só grava os bytes que mudaram, alterar o brilho desgasta uma única célula. Você pode adaptar a mesma estrutura para uma fechadura com senha e teclado matricial ou guardar os IDs de 4 bytes dos cartões autorizados em um array com o RFID RC522. Se comandos seriais são novidade para você, leia Monitor Serial do Arduino.
Multiplicando a vida útil: buffer circular (wear leveling)
Gravar um contador atualizado com frequência sempre no mesmo endereço desgasta rapidamente essa única célula. Com um buffer circular (ring buffer), você grava o valor em posições diferentes alternadamente: um salvamento vai para uma posição, o próximo para a seguinte, e depois da última posição você volta ao início. Cada posição é gravada só uma vez a cada 16 salvamentos, então a vida útil cresce 16 vezes, para cerca de 1,6 milhão de salvamentos. Na inicialização, todas as posições são lidas para encontrar o valor mais recente; como o contador só aumenta, o maior valor é o mais novo.
1#include <EEPROM.h>
2
3const int RING_START = 100; // halka 100. adresten başlar / ring starts at address 100
4const byte SLOTS = 16; // 16 yuva x 4 bayt = 64 bayt / 16 slots x 4 bytes = 64 bytes
5
6uint32_t counter = 0;
7byte slot = 0;
8
9void loadCounter() {
10 for (byte i = 0; i < SLOTS; i++) {
11 uint32_t v;
12 EEPROM.get(RING_START + i * 4, v);
13 if (v != 0xFFFFFFFF && v >= counter) { // en büyük değer en yenisidir / the largest value is the newest
14 counter = v;
15 slot = i;
16 }
17 }
18}
19
20void saveCounter() {
21 slot = (slot + 1) % SLOTS; // her kayıtta sıradaki yuva / next slot on every save
22 EEPROM.put(RING_START + slot * 4, counter);
23}
24
25void setup() {
26 Serial.begin(9600);
27 loadCounter();
28 counter++; // örnek: her açılışı say / example: count every boot
29 saveCounter();
30 Serial.print(F("Acilis sayisi / boot count: "));
31 Serial.println(counter);
32 Serial.print(F("Kullanilan yuva / slot used: "));
33 Serial.println(slot);
34}
35
36void loop() {}O exemplo incrementa o contador a cada inicialização; você pode trocar isso por um botão, um evento de sensor ou um horímetro. Mais posições significam proporcionalmente mais vida útil: 64 posições ocupam 256 bytes e duram cerca de 6,4 milhões de salvamentos. Se a placa já foi usada com outros sketches, podem ter sobrado dados antigos na área do buffer, então limpe a EEPROM com o código abaixo antes do primeiro uso. Em projetos mais avançados, você também pode adicionar um checksum CRC, como no exemplo eeprom_crc do próprio Arduino, para detectar dados corrompidos.
Como zerar a EEPROM? Carregar um código apaga a memória?
Um carregamento normal pelo cabo USB grava só a memória de programa; os dados da EEPROM são mantidos. Por isso, quando você carrega um projeto novo, as configurações do projeto anterior continuam na EEPROM e o novo código pode ler valores que não espera. No Uno, a EEPROM só é apagada quando você usa um programador ISP com Sketch > Carregar usando programador ou Ferramentas > Gravar bootloader; essas operações apagam o chip inteiro, e o bit EESAVE, que protege a EEPROM, fica desativado nos fuses do Uno. Para limpar a EEPROM você mesmo, carregue este sketch uma vez:
1#include <EEPROM.h>
2
3void setup() {
4 for (uint16_t i = 0; i < EEPROM.length(); i++) {
5 EEPROM.update(i, 255); // fabrika durumu 0xFF / factory state 0xFF
6 }
7 pinMode(LED_BUILTIN, OUTPUT);
8 digitalWrite(LED_BUILTIN, HIGH); // LED yandı: bitti / LED on: done
9}
10
11void loop() {}O exemplo Arquivo > Exemplos > EEPROM > eeprom_clear da Arduino IDE grava 0 em todas as células; nós, em vez disso, devolvemos as células ao estado de fábrica de 255. Assim, códigos que verificam um número mágico ou 0xFF reconhecem a EEPROM como vazia de novo. Como usamos update(), células que já estão em 255 nem são gravadas. Gravar todas as 1024 células do Uno leva no máximo cerca de 3,4 segundos; quando o LED integrado acender, terminou. Depois, carregue seu sketch de verdade.
EEPROM no ESP32: a biblioteca Preferences
O ESP32 não tem EEPROM de verdade. Os dados permanentes ficam na partição NVS (Non-Volatile Storage) da memória flash externa; na tabela de partições padrão essa área tem 20 KB. O caminho recomendado pela Espressif é a biblioteca Preferences, que vem com o pacote de placas do ESP32. A Preferences guarda os dados por nomes de namespace e de chave (key) em vez de endereços, então você nunca precisa lembrar em que endereço estava o contador. O NVS grava cada valor novo na próxima entrada livre da página e marca o antigo como apagado, então o nivelamento de desgaste acontece automaticamente. Os nomes de namespace e de chave podem ter no máximo 15 caracteres e diferenciam maiúsculas de minúsculas. Se você é novo no ESP32, comece por O que é o ESP32.
1#include <Preferences.h>
2
3Preferences prefs;
4
5void setup() {
6 Serial.begin(115200);
7 prefs.begin("bluegrays", false); // ad alanı; false = okuma + yazma / namespace; false = read + write
8
9 uint32_t boots = prefs.getUInt("boots", 0); // anahtar yoksa 0 / 0 if the key is missing
10 boots++;
11 prefs.putUInt("boots", boots);
12
13 if (!prefs.isKey("ssid")) { // ilk açılış / first boot
14 prefs.putString("ssid", "EvAgi"); // örnek değer / example value
15 }
16 String ssid = prefs.getString("ssid", ""); // kayıtlı Wi-Fi adı / stored Wi-Fi name
17 int brightness = prefs.getInt("bright", 128); // yoksa 128 / 128 if missing
18
19 Serial.print("Acilis / boot: ");
20 Serial.println(boots);
21 Serial.print("SSID: ");
22 Serial.println(ssid);
23 Serial.print("Parlaklik / brightness: ");
24 Serial.println(brightness);
25 Serial.print("Bos kayit yeri / free entries: ");
26 Serial.println(prefs.freeEntries());
27 prefs.end(); // ad alanını kapat / close the namespace
28}
29
30void loop() {}| Comando | O que faz |
|---|---|
| prefs.begin("nome", false) | Abre o namespace; false significa leitura e gravação, true significa somente leitura |
| putUInt, putInt, putFloat, putBool, putString, putBytes | Salva o valor com um nome de chave |
| getUInt("chave", padrão) | Lê o valor; retorna o padrão se a chave não existir |
| isKey("chave") | Verifica se a chave existe |
| remove("chave") | Apaga uma única chave |
| clear() | Apaga todas as chaves do namespace aberto |
| freeEntries() | Retorna o número de entradas livres restantes no NVS |
| end() | Fecha o namespace |
Você precisa ler um valor com o mesmo tipo com que o gravou: se gravar com putUInt e ler com getInt, recebe o valor padrão. Também há um motivo para chamarmos isKey() antes de getString(): se o Core Debug Level no menu Ferramentas estiver em Error ou acima, ler uma chave de texto que não existe imprime nvs_get_str len fail: ssid NOT_FOUND no Monitor Serial; é inofensivo, mas confunde. Carregar o código de novo pelo USB não apaga o NVS, mas se Erase All Flash Before Sketch Upload estiver em Enabled, cada carregamento apaga todas as entradas. Guardar os dados de Wi-Fi com Preferences é comum; se você prefere digitar o nome da rede e a senha pelo celular, o ESP32 WiFiManager também funciona, e o ESP32 guarda as configurações de Wi-Fi na mesma área do NVS. Para valores de curta duração que precisam sobreviver ao despertar do deep sleep, a memória RTC é mais adequada porque não desgasta a flash; veja deep sleep no ESP32.
Ao portar código antigo do Arduino para o ESP32, a biblioteca EEPROM também funciona, com duas diferenças: antes de usá-la você precisa informar quantos bytes vai usar com EEPROM.begin(tamanho) e, depois das alterações, chamar EEPROM.commit(). put() e write() só mudam a cópia na RAM; sem commit() nada chega à flash. No ESP32 essa biblioteca funciona como um único bloco de dados dentro do NVS e a Espressif a considera obsoleta (deprecated); todos os bytes de uma área recém-criada são 0xFF. O mesmo código também compila para o ESP8266, onde o tamanho pode ser de no máximo 4096 bytes e cada commit() apaga e regrava um setor de 4 KB da flash, então chame commit com moderação.
1#include <EEPROM.h>
2
3void setup() {
4 Serial.begin(115200);
5 EEPROM.begin(64); // kullanılacak bayt sayısı / number of bytes to use
6 uint32_t boots;
7 EEPROM.get(0, boots);
8 if (boots == 0xFFFFFFFF) boots = 0; // yeni alan 0xFF dolu / a fresh area is 0xFF
9 boots++;
10 EEPROM.put(0, boots); // yalnız RAM'deki kopyayı değiştirir / changes only the RAM copy
11 EEPROM.commit(); // flaşa şimdi yazılır / written to flash now
12 Serial.println(boots);
13 EEPROM.end();
14}
15
16void loop() {}Dados permanentes no Raspberry Pi Pico: o sistema de arquivos do MicroPython
O chip RP2040 do Raspberry Pi Pico também não tem EEPROM; a placa traz 2 MB de memória flash. O MicroPython reserva cerca de 1,4 MB dessa flash para um sistema de arquivos LittleFS (menos no Pico W, por causa do firmware de Wi-Fi). Então, no Pico, o equivalente à EEPROM é gravar suas configurações em um pequeno arquivo. O LittleFS espalha as gravações por blocos diferentes da flash, então faz seu próprio nivelamento de desgaste, e o sistema de arquivos não se corrompe mesmo que a energia caia durante uma gravação. O código abaixo guarda as configurações em JSON, conta cada inicialização e salva o contador cada vez que um botão ligado entre o GP15 e o GND é pressionado.
1import json, os, time
2from machine import Pin
3
4FILE = "settings.json"
5DEFAULTS = {"boots": 0, "count": 0, "password": "1234"}
6
7def load():
8 try:
9 with open(FILE) as f:
10 data = json.load(f)
11 except (OSError, ValueError): # dosya yok ya da bozuk / file missing or corrupt
12 data = {}
13 for key, value in DEFAULTS.items(): # eksik anahtarları tamamla / fill in missing keys
14 data.setdefault(key, value)
15 return data
16
17def save(data):
18 with open(FILE + ".tmp", "w") as f: # önce geçici dosyaya yaz / write a temp file first
19 json.dump(data, f)
20 os.rename(FILE + ".tmp", FILE) # sonra eskisinin yerine koy / then replace the old one
21
22settings = load()
23settings["boots"] += 1
24save(settings)
25print("Acilis / boot:", settings["boots"], "Sayac / count:", settings["count"])
26
27button = Pin(15, Pin.IN, Pin.PULL_UP) # buton GP15 ile GND arasında / button between GP15 and GND
28
29while True:
30 if button.value() == 0: # basıldı / pressed
31 time.sleep_ms(30) # sıçramayı bekle / let the bounce settle
32 if button.value() == 0:
33 settings["count"] += 1
34 save(settings) # seyrek olay, her basışta yaz / rare event, save on each press
35 print("Sayac / count:", settings["count"])
36 while button.value() == 0: # bırakılmasını bekle / wait for release
37 time.sleep_ms(10)
38 time.sleep_ms(30) # bırakma sıçraması / release bounce
39 time.sleep_ms(10)Em vez de sobrescrever o arquivo diretamente, primeiro gravamos em um arquivo temporário .tmp e depois usamos os.rename() para colocá-lo no lugar do antigo; assim o arquivo de configurações antigo continua intacto mesmo que uma gravação seja interrompida. Se o arquivo não existir ou o conteúdo estiver corrompido, load() volta aos valores padrão, e quando você adicionar uma nova configuração ao programa depois, setdefault() preenche automaticamente a chave que falta. Se você programa o Pico com a Arduino IDE, também pode usar a biblioteca EEPROM do core Arduino-Pico de Earle Philhower: EEPROM.begin(256) abre uma área de 256–4096 bytes e EEPROM.commit() salva suas alterações. Essa EEPROM emulada fica no último setor de 4 KB da flash e cada commit() apaga e regrava esse setor; a flash suporta cerca de 100.000 ciclos de apagamento por setor, então use commit com moderação. Se você está começando com o Pico, O que é o Raspberry Pi Pico e Fundamentos de MicroPython são bons pontos de partida.
Mais memória permanente: EEPROM externa, FRAM e cartões SD
Se 1 KB não for suficiente ou se você precisar salvar com muita frequência, pode adicionar uma memória externa. Chips de EEPROM I2C como o AT24C256 oferecem 32 KB e cerca de 1 milhão de ciclos de gravação, e se conectam só com os fios SDA e SCL (O que é I2C). Um detalhe pouco conhecido: muitos módulos de relógio RTC DS3231 do mercado já trazem uma EEPROM AT24C32 de 4 KB, normalmente acessível no endereço I2C 0x57. Para tarefas que salvam muitas vezes por segundo, módulos FRAM com cerca de um trilhão (10^12) de ciclos de gravação são mais adequados, e para grandes registros de dados, um módulo de cartão SD, que guarda os dados como arquivos.
Ideias de projetos com EEPROM
- Lâmpada que lembra o último estado: quando a energia voltar, devolva a lâmpada ligada a um relé ao estado em que estava antes da queda.
- Cofre com senha alterável: grave na EEPROM a nova senha digitada em um teclado matricial, para que a senha mude sem carregar o sketch de novo.
- Fechadura de porta com cartão: guarde os IDs dos cartões autorizados lidos com o RFID RC522 e adicione novos cartões com um cartão mestre.
- Recorde permanente: guarde a melhor pontuação no Simon, no T-Rex ou no jogo da cobrinha; há códigos de exemplo em Jogos com Arduino.
- Braço robótico que grava movimentos: grave os ângulos dos servos pose a pose e reproduza-os; o código de Braço robótico com Arduino faz isso com EEPROM.update().
- Balança que lembra a calibração: salve como float, com put(), o fator de calibração encontrado com a célula de carga HX711.
- Contador de resets: conte quantas vezes a placa reinicia para diagnosticar problemas de alimentação; o método está em Arduino reiniciando sozinho.
Erros comuns e como corrigi-los
| Sintoma | Causa | Solução |
|---|---|---|
| A primeira inicialização lê 255, -1, 65535 ou nan | A EEPROM nunca foi gravada; células vazias são 0xFF | Grave os valores padrão quando a verificação do número mágico ou de 0xFF falhar |
| Gravou 300, leu 44 | write() e read() carregam só 1 byte | Use put() e get() para dados de vários bytes |
| Dois valores se corrompem | Os endereços se sobrepõem; um int ocupa 2 bytes, um float 4 | Faça um mapa de endereços e calcule os endereços com sizeof |
| O texto vira lixo depois de reiniciar | Um objeto String foi gravado com put() | Guarde o texto como array de char |
| O código novo lê as configurações do projeto antigo | Um carregamento normal não apaga a EEPROM | Use um número mágico e um campo de versão, ou limpe a EEPROM |
| As configurações sumiram depois do carregamento | Carregamento com programador no Uno, opção Erase All Flash no ESP32 | Carregue normalmente pelo USB e coloque a opção do ESP32 em Disabled |
| Os valores não são salvos no ESP32 | Falta EEPROM.begin() ou EEPROM.commit() | Adicione os dois ou passe para Preferences |
| Preferences sempre retorna o padrão | Leitura com tipo diferente, nome com mais de 15 caracteres ou namespace diferente | Use os mesmos tipos no put e no get e mantenha os nomes curtos |
| Erro nvs_open failed: NOT_FOUND | Um namespace que nunca foi criado foi aberto como somente leitura (true) | Abra o namespace primeiro com false e grave um valor |
| Os valores se corrompem depois de um tempo | Gravações constantes no loop() ou queda de alimentação durante uma gravação | Use update() e salvamento atrasado, adicione buffer circular e CRC |
A EEPROM é o jeito mais fácil de dar memória aos seus projetos com Arduino: com algumas linhas de código, você protege as configurações, os contadores e as senhas do seu dispositivo contra quedas de energia. A única coisa a observar é a vida útil de gravação; desde que você use update() e put() e não grave sem necessidade, a EEPROM do Uno vai funcionar sem problemas por anos. No app BlueGrays você encontra projetos que pode ampliar com EEPROM, como o app de senha com teclado 4x4, o cofre eletrônico inteligente, a autenticação RFID e os jogos Simon e T-Rex, todos com esquema do circuito e código passo a passo.
Perguntas frequentes
Quantas vezes dá para gravar na EEPROM do Arduino?
O datasheet do ATmega328P especifica pelo menos 100.000 ciclos de gravação/apagamento para cada célula da EEPROM. O limite é por célula, e a leitura não o afeta. Gravar no mesmo endereço uma vez por segundo desgasta a célula em cerca de 28 horas; se você usar update() e gravar só quando o valor mudar, a EEPROM dura anos.
Carregar um novo sketch apaga a EEPROM do Arduino?
Não. Um carregamento normal pelo USB grava só a memória de programa, e os dados da EEPROM são mantidos. No Uno, a EEPROM só é apagada quando você carrega com um programador ISP ou grava o bootloader. Use uma verificação de número mágico para que um projeto novo não pegue configurações antigas.
Qual é a diferença entre EEPROM.write e EEPROM.update?
write() regrava o valor na célula a cada chamada e gasta um ciclo de gravação toda vez. update() lê a célula primeiro e não grava nada se o valor for o mesmo. O resultado é idêntico, mas update() preserva a vida útil da EEPROM; put() usa o mesmo método internamente.
Como limpar a EEPROM do Arduino?
Grave 255 (ou 0) em todos os endereços com um laço for que vai até EEPROM.length(); o exemplo eeprom_clear da Arduino IDE já grava 0. Limpar os 1024 bytes do Uno leva cerca de 3,4 segundos. Quando terminar, não deixe o sketch de limpeza na placa; carregue seu programa de verdade.
O ESP32 tem EEPROM?
O ESP32 não tem EEPROM de verdade; os dados permanentes ficam na partição NVS da memória flash. Para projetos novos, recomenda-se a biblioteca Preferences. A biblioteca EEPROM ainda funciona para códigos antigos, mas você precisa chamar EEPROM.begin(tamanho) primeiro e EEPROM.commit() após cada alteração.
Como salvar um int, um float ou um texto na EEPROM?
Grave dados de vários bytes, como int, long, float e structs, com EEPROM.put(endereço, variável) e leia com EEPROM.get(endereço, variável). Um int ocupa 2 bytes e um float 4, então escolha o próximo endereço de acordo. Guarde o texto como um array de char de tamanho fixo, não como String.
Projetos para montar depois desta leitura
Ver todos os projetos
Ligar e desligar um LED com botão
Pressione o botão uma vez para acender a luz e outra vez para apagar, como o interruptor do seu quarto. O Arduino detecta cada toque e inverte o estado do LED, aguardando um breve instante para que as

Contador com display de 7 segmentos e botões
Um contador simples com três botões: ao pressionar um deles, o algarismo no display aumenta em um; com outro, diminui em um; e o terceiro zera a contagem. O display de 7 segmentos de um dígito conta d

Fechadura com senha, teclado 4x4 e LCD
Uma pequena fechadura eletrônica que impede a entrada de quem não sabe a senha. Você pressiona a cerquilha e digita a senha no teclado. Se estiver correta, o display indica que o acesso foi permitido,

Cofre eletrônico inteligente
Um pequeno cofre para guardar seus objetos de valor, que abre com uma senha. Você digita sua senha de quatro dígitos no teclado; se estiver correta, o servo motor abre a trava, a luz verde acende e o

Autenticação por RFID
Como na entrada com cartão de um prédio: você aproxima o cartão do leitor RFID e o Arduino compara o número dele com o número salvo. Com o cartão correto, o LED verde acende por três segundos; com um

Jogo da memória com Arduino (4 LEDs + 4 botões + buzzer)
Você confia na sua memória? Quatro LEDs coloridos acendem em sequência, cada um tocando sua própria nota, e você repete a mesma ordem apertando os botões. A cada rodada correta, uma nova cor é adicion

Corrida do T-Rex no OLED: jogo do dinossauro com Arduino
O jogo do dinossauro que aparece quando a internet cai, agora na sua mesa: o dinossauro corre pelo pequeno display OLED, os cactos vêm na sua direção e você pula com um único botão. Sua pontuação aume

ESP32 em sono profundo — Economia de bateria (OLED + contador na RAM RTC)
O modo de sono profundo do ESP32 reduz o consumo de corrente para ~10 µA ao desligar a CPU e a maioria dos periféricos, sendo essencial para dispositivos IoT alimentados por bateria. Neste projeto, usamos três fontes de despertar: um temporizador (15 segundos), um pino externo (EXT0 — botão no GPIO 33) e toque (T0/GPIO 4). As variáveis marcadas com RTC_DATA_ATTR são preservadas durante o sono profundo, mantendo os contadores de inicializações e de despertares por temporizador e botão.
Mais artigos
App BlueGrays
Monte este circuito passo a passo pelo celular
Esquema do circuito, código e lista de componentes em uma só tela. Mais de 200 projetos com Arduino, ESP32 e Pico prontos para montar.
Escaneie com seu celular
Aponte a câmera para o código para abrir a loja de aplicativos do seu celular.
ou abrir a loja

