Arduino EEPROM Kullanımı: Kalıcı Hafızaya Veri Yazma ve Okuma
Arduino EEPROM nasıl kullanılır? Uno'da 1024 bayt kalıcı hafıza, read, write, update, put, get, 100.000 yazma ömrü, sayaç ve şifre kaydı, ESP32 Preferences.
Arduino EEPROM, kartın elektriği kesildiğinde bile içindeki bilgiyi saklayan küçük bir kalıcı hafızadır. Arduino Uno, Nano ve Pro Mini'deki ATmega328P işlemcide 1024 baytlık EEPROM bulunur ve ona Arduino IDE ile hazır gelen EEPROM kütüphanesinin EEPROM.read(), EEPROM.write(), EEPROM.update(), EEPROM.put() ve EEPROM.get() komutlarıyla erişirsin. Değişkenlerin tutulduğu RAM her yeniden başlatmada sıfırlanırken EEPROM'a yazdığın bir ayar, sayaç ya da şifre kart yeniden açıldığında aynen yerinde durur.
Bu rehberde EEPROM'un nasıl çalıştığını, hangi kartta ne kadar yer olduğunu, komutların farkını ve en çok merak edilen konuyu, yani yaklaşık 100.000 yazmalık ömrün pratikte ne anlama geldiğini göreceksin. Ardından elektrik kesilse de unutmayan bir sayaç, şifreyi ve ayarları tek seferde saklayan bir ayar yapısı ve yazma ömrünü 16 katına çıkaran bir halka tampon örneğini adım adım kuracağız. Son bölümlerde gerçek EEPROM'u olmayan ESP32'de Preferences kütüphanesiyle, Raspberry Pi Pico'da ise MicroPython dosya sistemiyle aynı işi nasıl yapacağını öğreneceksin.

EEPROM nedir, nasıl çalışır?
EEPROM, İngilizce Electrically Erasable Programmable Read-Only Memory (elektrikle silinebilir, programlanabilir salt okunur bellek) ifadesinin kısaltmasıdır. Her bellek hücresi bilgiyi, yalıtılmış bir kapının içine hapsedilen elektrik yüküyle saklar; bu yük güç kesildiğinde de kaybolmaz. Uno'nun EEPROM'unu 1024 çekmeceli bir dolap gibi düşünebilirsin: her çekmecenin 0'dan 1023'e kadar bir adresi vardır ve her çekmeceye tam olarak 1 bayt, yani 0 ile 255 arasında bir sayı sığar. Hiç yazılmamış bir hücre 255 (0xFF) okunur. Okuma neredeyse anlıktır ve sınırsız yapılabilir; yazma ise bayt başına yaklaşık 3,3 ms sürer ve hücreyi biraz yıpratır. ATmega328P'nin veri sayfası her EEPROM hücresi için en az 100.000 yazma-silme döngüsü ve 25 °C'de 100 yıl veri saklama süresi verir.
| Özellik | Flash (program belleği) | SRAM | EEPROM |
|---|---|---|---|
| Uno'daki boyut | 32 KB (0,5 KB'ı önyükleyici) | 2 KB | 1 KB (1024 bayt) |
| Ne saklanır? | Yüklediğin program | Değişkenler, diziler | Ayarlar, sayaçlar, rekorlar |
| Elektrik kesilince | Korunur | Silinir | Korunur |
| Program çalışırken yazılabilir mi? | Normalde hayır (PROGMEM ile yalnızca okunur) | Evet, sınırsız | Evet, EEPROM kütüphanesiyle |
| Yazma ömrü | Yaklaşık 10.000 | Sınırsız | Hücre başına yaklaşık 100.000 |
| Yazma hızı | Yalnızca kod yüklerken | Tek saat döngüsü | Bayt başına yaklaşık 3,3 ms |
Kısacası SRAM bir karalama tahtası, EEPROM ise bir defterdir. Tahtaya istediğin kadar yazıp silebilirsin ama ışıklar sönünce her şey gider; deftere yazdığın kalır ama aynı sayfayı sonsuza kadar silip yazamazsın. Bu yüzden EEPROM; kalibrasyon değerleri, kullanıcı ayarları, parlaklık ya da ses seviyesi, oyun rekoru, kapı şifresi, kayıtlı RFID kartları ve lambanın son durumu gibi az yer kaplayan ve seyrek değişen bilgiler için idealdir. Saniyede bir ölçüm kaydeden bir sıcaklık günlüğü gibi büyük ve sık yazılan veriler için ise SD kart modülü daha doğru seçimdir. RAM'in neden çabuk dolduğunu merak ediyorsan Arduino bellek yetersiz yazısına göz at.
Hangi kartta ne kadar EEPROM var?
| Kart | İşlemci | EEPROM | Not |
|---|---|---|---|
| Arduino Uno R3, Nano, Pro Mini | ATmega328P | 1024 bayt | Gerçek EEPROM |
| Arduino Mega 2560 | ATmega2560 | 4096 bayt | Gerçek EEPROM |
| Arduino Leonardo, Micro | ATmega32U4 | 1024 bayt | Gerçek EEPROM |
| Arduino Nano Every | ATmega4809 | 256 bayt | Gerçek EEPROM, ama az yer |
| Arduino Uno R4 Minima, WiFi | Renesas RA4M1 | 8 KB | Veri flaşı; EEPROM kütüphanesi aynı komutlarla çalışır |
| ESP32 | ESP32 (Xtensa) | Yok | Flaştaki NVS bölümü; Preferences önerilir |
| ESP8266 (NodeMCU, Wemos D1) | ESP8266 | Yok | Flaşta taklit; EEPROM.begin() ve commit() gerekir |
| Raspberry Pi Pico | RP2040 | Yok | MicroPython'da dosya, Arduino çekirdeğinde flaşta taklit EEPROM |
Kodunu farklı kartlara taşıyacaksan EEPROM boyutunu sabit bir sayı olarak yazma; EEPROM.length() fonksiyonu kartın gerçek EEPROM boyutunu döndürür (Uno'da 1024, Mega'da 4096). Böylece aynı silme ya da tarama kodu her kartta doğru çalışır. Uno'nun diğer bellek ve pin özellikleri için Arduino Uno pinleri, kart seçimi için Hangi Arduino'yu almalıyım? yazılarına bakabilirsin.
EEPROM komutları: read, write, update, put ve get
EEPROM kütüphanesi kart paketiyle birlikte gelir; ayrıca kurman gerekmez, kodun en üstüne #include <EEPROM.h> satırını yazman yeterlidir. Adresler 0'dan başlar ve Uno'da 1023'te biter. Aşağıdaki tablo her komutun ne yaptığını ve hücreleri yıpratıp yıpratmadığını özetliyor:
| Komut | Ne yapar? | Boyut | Yıpranma |
|---|---|---|---|
| EEPROM.read(adres) | Bir baytı okur (0–255) | 1 bayt | Yok, okuma serbesttir |
| EEPROM.write(adres, değer) | Baytı her çağrıda yeniden yazar | 1 bayt | Her çağrıda bir döngü harcar |
| EEPROM.update(adres, değer) | Önce okur, değer farklıysa yazar | 1 bayt | Yalnızca değer değişince |
| EEPROM.put(adres, veri) | int, float, dizi ya da struct'ı bayt bayt yazar; içeride update kullanır | sizeof(veri) | Yalnızca değişen baytlar |
| EEPROM.get(adres, değişken) | put ile yazılanı aynı türde geri okur | sizeof(değişken) | Yok |
| EEPROM[adres] | Dizi gibi erişim; okumak serbest, = ile atama write gibi çalışır | 1 bayt | Her atamada harcar |
| EEPROM.length() | Toplam EEPROM boyutunu verir | - | Yok |
İlk program: EEPROM'a veri yazma ve okuma
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() {}Kodu yükle ve Seri Monitör'ü 9600 baud hızında aç. İlk çalıştırmada 0. adres 255 okunur, çünkü o hücreye daha önce hiç yazılmamıştır. Şimdi kartın reset düğmesine bas ya da USB kablosunu çıkarıp yeniden tak: bu kez 42 okunur. EEPROM'un kalıcılığının kanıtı budur. 1. adrese yazan write() satırı ise değer hep 7 olduğu hâlde her açılışta hücreyi yeniden yazar; update() kullansaydık yalnızca ilk seferde yazacaktı. Son satırdaki 300 değeri uyarıdaki tuzağı gösterir ve ekranda 44 görünür. Tercihler'de Derleyici uyarıları ayarı Hiçbiri dışında bir seviyedeyse derleyici bu satır için large integer implicitly truncated to unsigned type uyarısını verir.
put() ve get() kullanırken her verinin kaç bayt kapladığını bilmen gerekir; yoksa iki değişkeni üst üste yazıp birini bozarsın. Örnekte int 10 ve 11. adresleri kapladığı için float'u 12. adresten başlattık. Uno'daki veri türlerinin boyutları şöyledir:
| Veri türü | Uno'da boyut | Değer aralığı | Örnek kullanım |
|---|---|---|---|
| byte, bool, char | 1 bayt | 0–255, true/false ya da tek karakter | Açık-kapalı durumu, mod seçimi |
| int | 2 bayt | −32.768 ile 32.767 | Kalibrasyon farkı |
| unsigned int, uint16_t | 2 bayt | 0–65.535 | Oyun rekoru |
| long, unsigned long | 4 bayt | Yaklaşık ±2,1 milyar ya da 0–4,29 milyar | Uzun süreli sayaç |
| float | 4 bayt | 6–7 basamak hassasiyet | Sensör kalibrasyon katsayısı |
| char dizi[10] | 10 bayt | 9 karakter ve sonlandırıcı | Şifre, cihaz adı |
EEPROM yazma ömrü: 100.000 sınırı ne demek?
100.000 yazma çok gibi görünür ama yanlış kurulmuş bir kod bu sınırı birkaç dakikada tüketebilir. Ömrü hesaplamanın formülü basittir: ömür = 100.000 ÷ aynı hücreye yapılan yazma sıklığı. Önemli nokta, sınırın tüm EEPROM için değil her hücre için ayrı geçerli olmasıdır; aynı adrese yazmaya devam ettikçe yalnızca o hücre yıpranır. Okumanın ise ömre hiçbir etkisi yoktur. Aşağıdaki tablo aynı adrese farklı sıklıklarla yazıldığında hücrenin ne kadar dayanacağını gösteriyor:
| Aynı adrese yazma sıklığı | Yaklaşık ömür |
|---|---|
| loop() içinde beklemesiz write() (saniyede yaklaşık 300 kez) | 5–6 dakika |
| Her saniye | Yaklaşık 28 saat |
| Her dakika | Yaklaşık 69 gün |
| 10 dakikada bir | Yaklaşık 1,9 yıl |
| Saatte bir | Yaklaşık 11 yıl |
| Günde 10 kez | Yaklaşık 27 yıl |
100.000, üreticinin garanti ettiği en düşük değerdir; birçok yonga çok daha fazlasına dayanır ama projeni buna güvenerek tasarlama. Yıpranan bir hücre birden ölmez; önce yazdığın değeri tutamamaya, bazı bitleri kendiliğinden değiştirmeye başlar. Böyle bir durumda veriyi başka bir adrese taşıyabilirsin, çünkü diğer hücreler bundan etkilenmez. Ömrü uzatmanın yolları şunlardır:
- update() ve put() kullan: İkisi de yazmadan önce hücreyi okur ve değer aynıysa hiç yazmaz.
- Yalnızca değişince yaz: Ayarı her loop() turunda değil, kullanıcı gerçekten değiştirdiğinde kaydet.
- Kaydı geciktir: Değer art arda değişiyorsa son değişiklikten birkaç saniye sonra tek bir kez yaz; bunu millis() ile kolayca yaparsın.
- Olay anında yaz: Oyun rekorunu oyun bittiğinde, menü ayarını menüden çıkarken kaydet.
- Yazmaları dağıt: Sık değişen bir sayacı tek adres yerine birkaç adrese sırayla yaz (aşağıdaki halka tampon örneği).
- Gerekirse harici bellek kullan: Çok sık kayıt gerekiyorsa 1 milyon döngülü harici EEPROM, FRAM ya da SD kart seç.
Örnek 1: Elektrik kesilse de unutmayan sayaç
İlk örnekte bir butona her basıldığında artan ve kart kapanıp açıldığında kaldığı yerden devam eden bir sayaç yapacağız. Butonun bir bacağını D2'ye, diğerini GND'ye bağla; dahili pull-up direnci kullandığımız için ek dirence gerek yok (ayrıntılar Arduino'ya buton bağlama yazısında, pin konumları Pinout aracında). Kodun püf noktası, sayacı her basışta değil son basıştan 5 saniye sonra kaydetmesidir. Böylece birisi butona arka arkaya 20 kez bassa bile EEPROM'a yalnızca bir kez yazılır.
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}dirty değişkeni RAM'de henüz kaydedilmemiş bir değişiklik olduğunu gösterir. Sayaç unsigned long türünde olduğu için 4 bayt (0–3. adresler) kaplar ve 4 milyardan fazla basışı sayabilir. Hiç yazılmamış EEPROM'da bu dört bayt 0xFF olduğundan ilk açılışta 4.294.967.295 okunur; kod bu değeri görünce sayacı sıfırdan başlatır. put() yalnızca değişen baytları yazdığından sayaç artarken çoğu zaman yalnızca en alttaki bayt yıpranır. Kayıttan önceki 5 saniye içinde elektrik kesilirse son basışlar kaybolur; bu, EEPROM ömrünü korumak için ödenen küçük bir bedeldir.
Örnek 2: Ayarları ve şifreyi struct ile saklamak
Birden fazla ayarı tek tek adreslere yazmak yerine hepsini bir struct içinde toplayıp tek bir put() ile kaydetmek hem daha düzenli hem de daha güvenlidir. Bu örnekte LED parlaklığını ve 4–6 karakterli bir şifreyi saklıyoruz. Yapının başındaki iki alan çok önemlidir: magic (sihirli sayı) EEPROM'a bu programın ayarlarının gerçekten yazılıp yazılmadığını anlamamızı sağlar; version ise yapıya ileride yeni bir alan eklediğinde eski ve uyumsuz verinin okunmasını engeller. İkisinden biri tutmazsa kod varsayılan ayarları yazar.
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}LED'i bir LED direnci (220 ohm yeterli) üzerinden D9'a bağla; D9 bir PWM pinidir. Seri Monitör'ü 9600 baud hızında açıp satır sonu ayarını Yeni Satır yap ve şu komutları dene: b200 parlaklığı 200 yapar, p1234,5678 şifreyi 1234'ten 5678'e çevirir, ? mevcut parlaklığı gösterir. Kartı kapatıp açtığında LED aynı parlaklıkla yanar ve yeni şifre geçerli kalır. Struct Uno'da 11 bayt tutar; put() yalnızca değişen baytları yazdığı için parlaklığı değiştirmek tek bir hücreyi yıpratır. Aynı yapıyı keypad'li şifreli kilide uyarlayabilir ya da RFID RC522 ile yetkili kartların 4 baytlık kimliklerini bir dizi olarak saklayabilirsin. Seri komutlara yeniysen Arduino Serial Monitor yazısına bak.
Yazma ömrünü katlamak: halka tampon (wear leveling)
Sık güncellenen bir sayacı hep aynı adrese yazmak o tek hücreyi hızla tüketir. Halka tampon (ring buffer) yönteminde değeri sırayla farklı yuvalara yazarsın: ilk kayıt bir yuvaya, sonraki kayıt bir sonrakine gider ve son yuvadan sonra başa dönülür. Her yuva 16 kayıtta bir yazıldığı için ömür 16 kat, yani yaklaşık 1,6 milyon kayda çıkar. Açılışta en güncel değeri bulmak için tüm yuvalar okunur; sayaç hep arttığı için en büyük değer en yenisidir.
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() {}Örnek her açılışta sayacı bir artırır; sen bunu bir buton, bir sensör olayı ya da çalışma saati sayacıyla değiştirebilirsin. Yuva sayısını artırdıkça ömür de aynı oranda artar: 64 yuva 256 bayt kaplar ve yaklaşık 6,4 milyon kayda dayanır. Kart daha önce başka programlarda kullanıldıysa halka bölgesinde eski veriler kalmış olabilir; ilk kullanımdan önce aşağıdaki silme koduyla EEPROM'u temizle. Daha gelişmiş projelerde Arduino'nun kendi eeprom_crc örneğindeki gibi bir CRC sağlama toplamı ekleyerek bozulmuş veriyi de yakalayabilirsin.
EEPROM nasıl sıfırlanır? Kod yükleyince silinir mi?
USB kablosuyla normal yükleme yaptığında Arduino yalnızca program belleğini yazar; EEPROM'daki veriler korunur. Bu yüzden yeni bir proje yüklediğinde önceki projenin ayarları EEPROM'da kalır ve yeni kodun beklemediği değerleri okumasına yol açabilir. Uno'da EEPROM yalnızca bir ISP programlayıcıyla Eskiz > Programlayıcı Kullanarak Yükle ya da Araçlar > Bootloader'ı Yükle (Burn Bootloader) yapıldığında silinir; bu işlemler yongayı tamamen siler ve Uno'nun sigorta (fuse) ayarlarında EEPROM'u koruyan EESAVE biti kapalıdır. EEPROM'u kendin temizlemek için şu kodu bir kez yükle:
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() {}Arduino IDE'deki Dosya > Örnekler > EEPROM > eeprom_clear örneği tüm hücrelere 0 yazar; biz ise hücreleri fabrika durumu olan 255'e döndürüyoruz. Böylece magic sayı ve 0xFF kontrolü yapan kodlar EEPROM'u yeniden boş olarak tanır. update() kullandığımız için zaten 255 olan hücrelere hiç yazılmaz. Uno'da 1024 hücrenin hepsini yazmak en fazla yaklaşık 3,4 saniye sürer; dahili LED yandığında işlem bitmiştir. Ardından asıl programını yükle.
ESP32'de EEPROM: Preferences kütüphanesi
ESP32'de gerçek bir EEPROM yoktur. Kalıcı veriler, harici flaş belleğin NVS (Non-Volatile Storage) adlı bölümünde tutulur; varsayılan bölüm tablosunda bu alan 20 KB'tır. Espressif'in önerdiği yol, ESP32 kart paketiyle gelen Preferences kütüphanesidir. Preferences veriyi adres yerine ad alanı (namespace) ve anahtar (key) isimleriyle saklar; yani sayacın hangi adreste olduğunu düşünmezsin. NVS yeni değeri her seferinde sayfadaki bir sonraki boş kayda yazıp eskisini silinmiş olarak işaretlediği için aşınma dengelemesi kendiliğinden yapılır. Ad alanı ve anahtar isimleri en çok 15 karakter olabilir ve büyük-küçük harfe duyarlıdır. ESP32'ye yeniysen önce ESP32 nedir yazısına göz at.
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() {}| Komut | Ne yapar? |
|---|---|
| prefs.begin("ad", false) | Ad alanını açar; false okuma ve yazma, true yalnızca okuma |
| putUInt, putInt, putFloat, putBool, putString, putBytes | Değeri anahtar adıyla kaydeder |
| getUInt("anahtar", varsayılan) | Değeri okur; anahtar yoksa varsayılanı döndürür |
| isKey("anahtar") | Anahtarın var olup olmadığına bakar |
| remove("anahtar") | Tek bir anahtarı siler |
| clear() | Açık ad alanındaki tüm anahtarları siler |
| freeEntries() | NVS'te kalan boş kayıt sayısını verir |
| end() | Ad alanını kapatır |
Bir değeri hangi türle yazdıysan o türle okumalısın: putUInt ile yazıp getInt ile okursan varsayılan değer döner. Kodda getString() öncesinde isKey() kullanmamızın da bir sebebi var: Araçlar menüsündeki Core Debug Level ayarı Error ya da üstündeyse, olmayan bir metin anahtarını okumak Seri Monitör'e nvs_get_str len fail: ssid NOT_FOUND satırını yazar; zararsızdır ama kafa karıştırır. Kodu USB'den yeniden yüklemek NVS'yi silmez, ancak Erase All Flash Before Sketch Upload seçeneği Enabled ise her yüklemede tüm kayıtlar gider. Wi-Fi bilgilerini Preferences ile saklamak yaygındır; ağ adını ve şifreyi telefondan girmek için ESP32 WiFiManager de ESP32'nin Wi-Fi ayarlarını aynı NVS alanında tutar. Derin uykudan uyanırken saklanması gereken kısa ömürlü değerler için flaşı hiç yıpratmayan RTC belleği daha uygundur; ayrıntılar ESP32 deep sleep yazısında.
Eski Arduino kodlarını ESP32'ye taşırken EEPROM kütüphanesi de çalışır, ama iki farkı vardır: kullanmadan önce EEPROM.begin(boyut) ile kaç bayt kullanacağını söylemen, değişiklikten sonra da EEPROM.commit() çağırman gerekir. put() ve write() yalnızca RAM'deki kopyayı değiştirir; commit() olmadan hiçbir şey flaşa yazılmaz. ESP32'de bu kütüphane NVS içinde tek bir veri bloğu olarak çalışır ve Espressif tarafından eskimiş (deprecated) kabul edilir; yeni oluşturulan alanın tüm baytları 0xFF'tir. Aynı kod ESP8266'da da derlenir; orada boyut en çok 4096 bayt olabilir ve her commit() 4 KB'lık bir flaş sektörünü silip yeniden yazar, bu yüzden commit'i seyrek çağır.
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() {}Raspberry Pi Pico'da kalıcı veri: MicroPython dosya sistemi
Raspberry Pi Pico'nun RP2040 yongasında da EEPROM yoktur; kartın üzerinde 2 MB'lık bir flaş bellek bulunur. MicroPython bu flaşın yaklaşık 1,4 MB'lık kısmını LittleFS dosya sistemi olarak ayırır (Pico W'de Wi-Fi yazılımı yüzünden daha az). Yani Pico'da EEPROM'un karşılığı, ayarlarını küçük bir dosyaya yazmaktır. LittleFS yazmaları flaşın farklı bloklarına dağıttığı için aşınma dengelemesini kendisi yapar ve yazma sırasında elektrik kesilse bile dosya sistemi bozulmaz. Aşağıdaki kod ayarları JSON biçiminde saklar, her açılışı sayar ve GP15 ile GND arasına bağlı bir butona her basıldığında sayacı kaydeder.
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)Dosyayı doğrudan üzerine yazmak yerine önce .tmp uzantılı geçici bir dosyaya yazıp sonra os.rename() ile eskisinin yerine koyuyoruz; böylece yazma yarıda kalsa bile eski ayar dosyası sağlam kalır. Dosya yoksa ya da içeriği bozuksa load() varsayılan değerlere döner; programa sonradan yeni bir ayar eklediğinde setdefault() eksik anahtarı kendiliğinden tamamlar. Pico'yu Arduino IDE ile programlıyorsan Earle Philhower'ın Arduino-Pico çekirdeğindeki EEPROM kütüphanesini de kullanabilirsin: EEPROM.begin(256) ile 256–4096 bayt arasında bir alan açar, değişiklikleri EEPROM.commit() ile kaydedersin. Bu taklit EEPROM flaşın sonundaki 4 KB'lık sektörde durur ve her commit() o sektörü silip yeniden yazar; flaş da sektör başına yaklaşık 100.000 silme döngüsüne dayandığı için commit'i seyrek çağır. Pico'ya yeni başlıyorsan Raspberry Pi Pico nedir ve MicroPython temelleri yazıları iyi bir başlangıçtır.
Daha fazla kalıcı hafıza: harici EEPROM, FRAM ve SD kart
1 KB yetmiyorsa ya da çok sık kayıt yapman gerekiyorsa harici bir bellek ekleyebilirsin. AT24C256 gibi I2C EEPROM yongaları 32 KB yer ve yaklaşık 1 milyon yazma döngüsü sunar; yalnızca SDA ve SCL kablolarıyla bağlanır (I2C nedir). Pek bilinmeyen bir ayrıntı: piyasadaki birçok DS3231 RTC saat modülünün üzerinde zaten 4 KB'lık bir AT24C32 EEPROM bulunur ve genellikle 0x57 I2C adresinden erişilir. Saniyede defalarca kayıt gerektiren işler için yaklaşık bir trilyon (10^12) yazma döngüsüne dayanan FRAM modülleri, büyük veri günlükleri için ise veriyi dosya olarak saklayan SD kart modülü daha uygundur.
EEPROM ile proje fikirleri
- Son durumu hatırlayan lamba: Elektrik gidip geldiğinde röleye bağlı lambayı kesintiden önceki durumuna döndür.
- Şifresi değiştirilebilen kasa: Keypad ile girilen yeni şifreyi EEPROM'a yaz; kod yeniden yüklenmeden şifre değişsin.
- Kartlı kapı kilidi: RFID RC522 ile okutulan yetkili kartların kimliklerini sakla, yönetici kartıyla yeni kart ekle.
- Kalıcı oyun rekoru: Simon, T-Rex ya da yılan oyununda en yüksek puanı sakla; örnek kodlar Arduino oyun projeleri yazısında.
- Hareket kaydeden robot kol: Servo açılarını poz poz kaydedip tekrar oynat; Arduino robot kol yazısındaki kod bunu EEPROM.update() ile yapar.
- Kalibrasyonu unutmayan tartı: HX711 yük hücresi ile bulduğun kalibrasyon katsayısını float olarak put() ile sakla.
- Reset sayacı: Kartın kaç kez yeniden başladığını sayarak besleme sorunlarını teşhis et; yöntem Arduino sürekli resetleniyor yazısında.
Sık yapılan hatalar ve çözümleri
| Belirti | Sebep | Çözüm |
|---|---|---|
| İlk açılışta 255, -1, 65535 ya da nan okunuyor | EEPROM'a hiç yazılmamış; boş hücreler 0xFF'tir | Magic sayı ya da 0xFF kontrolüyle varsayılan değerleri yaz |
| 300 yazıldı, 44 okunuyor | write() ve read() yalnızca 1 bayt taşır | Çok baytlı veriler için put() ve get() kullan |
| İki değer birbirini bozuyor | Adresler çakışıyor; int 2, float 4 bayt kaplar | Adres haritası yap, adresleri sizeof ile hesapla |
| Yeniden başlatınca metin anlamsız karakterlere dönüşüyor | String nesnesi put() ile yazılmış | Metni char dizisi olarak sakla |
| Yeni kod eski projenin ayarlarını okuyor | Normal yükleme EEPROM'u silmez | Magic sayı ve sürüm alanı kullan ya da EEPROM'u temizle |
| Ayarlar kod yükleyince silindi | Uno'da programlayıcıyla yükleme, ESP32'de Erase All Flash seçeneği | USB'den normal yükle, ESP32'de seçeneği Disabled yap |
| ESP32'de değerler kaydedilmiyor | EEPROM.begin() ya da EEPROM.commit() eksik | İkisini ekle ya da Preferences'a geç |
| Preferences hep varsayılan değeri döndürüyor | Farklı türle okuma, 15 karakteri aşan isim ya da farklı ad alanı | put ve get türlerini eşleştir, isimleri kısa tut |
| nvs_open failed: NOT_FOUND hatası | Hiç oluşturulmamış bir ad alanı salt okunur (true) açılmış | Ad alanını önce false ile açıp bir değer yaz |
| Bir süre sonra değerler bozuluyor | loop() içinde sürekli yazma ya da yazma sırasında besleme düşmesi | update() ve gecikmeli kayıt kullan, halka tampon ve CRC ekle |
EEPROM, Arduino projelerine hafıza kazandırmanın en kolay yoludur: birkaç satır kodla cihazının ayarlarını, sayaçlarını ve şifrelerini elektrik kesintilerine karşı korursun. Dikkat etmen gereken tek şey yazma ömrüdür; update() ve put() kullandığın ve gereksiz yere yazmadığın sürece Uno'nun EEPROM'u yıllarca sorunsuz çalışır. BlueGrays uygulamasında 4x4 keypad ile şifre uygulaması, akıllı elektronik kasa, RFID kimlik doğrulama, Simon ve T-Rex oyunları gibi EEPROM'la geliştirebileceğin projeleri devre şemaları ve adım adım kodlarıyla bulabilirsin.
Sıkça sorulan sorular
Arduino EEPROM kaç kez yazılabilir?
ATmega328P'nin veri sayfası her EEPROM hücresi için en az 100.000 yazma-silme döngüsü verir. Sınır hücre başınadır ve okuma ömrü hiç etkilemez. Aynı adrese saniyede bir yazmak hücreyi yaklaşık 28 saatte tüketir; update() kullanıp yalnızca değer değişince yazarsan EEPROM yıllarca dayanır.
Arduino'ya yeni kod yükleyince EEPROM silinir mi?
Hayır. USB üzerinden normal yükleme yalnızca program belleğini yazar ve EEPROM'daki veriler korunur. EEPROM, Uno'da yalnızca ISP programlayıcıyla yükleme yapıldığında ya da önyükleyici yazıldığında silinir. Yeni projede eski ayarlarla karşılaşmamak için magic sayı kontrolü kullan.
EEPROM.write ile EEPROM.update arasındaki fark nedir?
write() değeri her çağrıda hücreye yeniden yazar ve her seferinde bir yazma döngüsü harcar. update() önce hücreyi okur, değer aynıysa hiç yazmaz. Sonuç aynıdır ama update() EEPROM ömrünü korur; put() de içeride aynı yöntemi kullanır.
Arduino EEPROM nasıl temizlenir?
EEPROM.length() kadar dönen bir for döngüsüyle tüm adreslere 255 (ya da 0) yazarsın; Arduino IDE'deki eeprom_clear örneği hazır olarak 0 yazar. Uno'da 1024 baytı silmek yaklaşık 3,4 saniye sürer. İşlem bitince silme kodunu kartta bırakma, asıl programını yükle.
ESP32'de EEPROM var mı?
ESP32'de gerçek bir EEPROM yoktur; kalıcı veriler flaş bellekteki NVS bölümünde tutulur. Yeni projelerde Preferences kütüphanesi önerilir. Eski kodlar için EEPROM kütüphanesi de çalışır, ama önce EEPROM.begin(boyut) ve her değişiklikten sonra EEPROM.commit() çağırmak zorunludur.
EEPROM'a int, float ya da metin nasıl kaydedilir?
int, long, float ve struct gibi çok baytlı verileri EEPROM.put(adres, değişken) ile yazar, EEPROM.get(adres, değişken) ile okursun. int 2, float 4 bayt kapladığı için sonraki verinin adresini buna göre seç. Metni String olarak değil, sabit boyutlu bir char dizisi olarak sakla.
Bu yazıdan sonra yapabileceğin projeler
Tüm projeleri gör
Buton İle Led Yakma (On Off)
Bir butona bir kez basınca ışık yanar, bir daha basınca söner; tıpkı odandaki lamba düğmesi gibi. Arduino her basışı fark edip LED'in durumunu tersine çevirir, düğme içindeki minik titreşimler ışığı k

7 Segment Display Ve Buton İle Sayaç
Üç butonlu basit bir sayaç: birine bastıkça ekrandaki rakam bir artar, diğerine bastıkça bir azalır, üçüncüsü sıfıra döndürür. Tek haneli 7 segment ekran 0 ile 9 arasında sayar; sınıra dayandığında kö

4x4 Keypad LCD Ekran Şifre Uygulaması
Doğru şifreyi bilmeyenin giremediği küçük bir elektronik kapı kilidi. Kare tuşuna basıp şifreyi tuş takımından girersin; şifre doğruysa ekranda giriş izni yazar, yeşil LED yanar ve servo motor kapıyı

Akıllı Elektronik Kasa
Değerli eşyalarını saklayabileceğin, şifreyle açılan küçük bir kasa. Tuş takımından dört haneli şifreni girersin; doğruysa servo motor kilidi açar, yeşil ışık yanar ve ekran kasanın durumunu yazar, he

RFID Kimlik Doğrulama
Apartman kapısındaki kartlı giriş gibi: RFID okuyucuya kartını yaklaştırırsın, Arduino kartın numarasını kayıtlı olanla karşılaştırır. Doğru kartta yeşil, yabancı kartta kırmızı LED üç saniye yanar ve

Arduino Hafıza Oyunu (4 LED + 4 Buton + Buzzer)
Hafızana güveniyor musun? Dört renkli LED sırayla yanar ve her biri kendi notasını çalar; sen de aynı sırayı butonlara basarak tekrar edersin. Her doğru turda sıraya yeni bir renk eklenir, küçük bir z

OLED T-Rex Run: Arduino Dinozor Oyunu
İnternet kesilince çıkan dinozor oyunu artık masanda: küçük OLED ekranda dinozor koşar, kaktüsler üstüne gelir ve sen tek butonla zıplarsın. Puanın yandaki 4 haneli ekranda canlı olarak artar, her 100

ESP32 Deep Sleep — Pil Tasarrufu (OLED + RTC RAM Sayaç)
ESP32'nin Deep Sleep modu, CPU ve çoğu çevre birimini kapatarak akım tüketimini ~10µA seviyesine düşürür ve pille çalışan IoT cihazları için kritik öneme sahiptir. Bu projede üç farklı uyandırma kaynağını kullanıyoruz: zamanlayıcı (timer, 15sn), harici pin (EXT0 — buton GPIO 33) ve touch (T0/GPIO 4). RTC_DATA_ATTR ile işaretlenen değişkenler deep sleep boyunca korunur — boot sayacı, timer wake sayacı ve buton wake sayacı kalıcı olarak izlenir. SSD1306 OLED ekranda her uyanışta wake reason, say
Diğer yazılar
BlueGrays uygulaması
Bu devreyi telefonunda adım adım kur
Devre şeması, kod ve parça listesi tek ekranda. 200'den fazla Arduino, ESP32 ve Pico projesi seni bekliyor.
Telefonunla tara
Kamerayı koda tut; telefonunun uygulama mağazası açılır.
ya da mağazada aç

