아두이노 EEPROM을 활용한 전원 종료 시 설정 값 및 마지막 데이터 보존법

아두이노 EEPROM을 활용한 전원 종료 시 설정 값 및 마지막 데이터 보존법

아두이노 프로젝트를 진행하다 보면 전원이 꺼져도 특정 설정 값이나 마지막 상태를 기억하고 싶을 때가 많습니다. 예를 들어, 스마트 조명의 밝기 설정, 로봇의 마지막 위치, 또는 센서의 보정 값 등이 해당될 수 있습니다. 전원이 다시 켜졌을 때 이전에 설정했던 값들을 그대로 유지하여 사용자가 다시 설정할 필요 없이 프로젝트를 바로 이어서 사용할 수 있다면 사용자 경험이 훨씬 좋아질 것입니다. 이때 활용할 수 있는 것이 바로 아두이노의 EEPROM(Electrically Erasable Programmable Read-Only Memory)입니다.

EEPROM은 전원이 공급되지 않아도 데이터를 보존하는 비휘발성 메모리의 일종입니다. RAM(Random Access Memory)이 전원이 끊기면 저장된 모든 데이터를 잃어버리는 휘발성 메모리인 것과 대조적입니다. 아두이노 보드에 내장된 EEPROM은 프로그램 코드를 저장하는 플래시 메모리와는 또 다른 영역으로, 사용자가 직접 데이터를 쓰고 읽을 수 있도록 제공됩니다. 이 글에서는 아두이노 EEPROM을 효과적으로 사용하여 프로젝트의 설정 값과 데이터를 안전하게 보존하는 방법에 대해 자세히 알아보겠습니다.

EEPROM은 무엇이며 왜 중요할까요

EEPROM은 Electrically Erasable Programmable Read-Only Memory의 약자로, 전기적으로 지우고 다시 프로그래밍할 수 있는 읽기 전용 메모리입니다. 이름에서 알 수 있듯이, 한번 기록된 데이터는 전원이 없어도 유지되며, 필요할 때 다시 지우고 새로운 데이터를 기록할 수 있습니다. 아두이노에 내장된 EEPROM은 보통 수백 바이트에서 수 킬로바이트 정도의 작은 용량을 제공하지만, 설정 값이나 적은 양의 데이터를 저장하기에는 충분합니다.

아두이노 프로젝트에서 EEPROM이 중요한 이유는 다음과 같습니다.

  • 데이터 영속성 전원이 끊겨도 데이터가 사라지지 않으므로, 프로젝트의 마지막 상태나 사용자가 설정한 값들을 기억할 수 있습니다.
  • 사용자 편의성 증대 매번 전원을 켤 때마다 설정을 다시 할 필요가 없어 사용자가 프로젝트를 더욱 편리하게 사용할 수 있습니다.
  • 프로젝트의 안정성 향상 예기치 않은 전원 차단에도 중요한 데이터를 보존하여 프로젝트의 연속성과 안정성을 높일 수 있습니다.

실생활에서 EEPROM 활용 사례

EEPROM은 우리 주변의 다양한 전자제품에서 찾아볼 수 있으며, 아두이노 프로젝트에서도 무궁무진하게 활용될 수 있습니다.

  • 스마트 홈 기기 스마트 조명의 마지막 밝기, 색상 설정, 스마트 플러그의 타이머 설정, 온도 조절 장치의 희망 온도 등이 전원 종료 후에도 보존됩니다.
  • 로봇 및 자동화 시스템 로봇의 마지막 동작 상태, 특정 구간까지의 이동 횟수, 공장 자동화 라인의 생산량 카운터 등이 저장되어 재부팅 시에도 작업을 이어서 수행할 수 있습니다.
  • 측정 장비 센서의 보정 값, 마지막으로 측정된 최고 또는 최저 값, 특정 알림 임계값 등이 저장되어 정확한 측정을 유지하고 이전 데이터를 참조할 수 있습니다.
  • 개인 맞춤형 장치 개인용 웨어러블 기기의 사용자 프로필, 게임 콘솔의 점수 기록, 맞춤형 디스플레이의 설정 등이 저장되어 개인화된 경험을 제공합니다.
  • 카운터 및 타이머 전원이 꺼져도 누적 카운트나 타이머의 진행 상황이 리셋되지 않고 유지됩니다.

이처럼 EEPROM은 프로젝트에 ‘기억력’을 부여하여 훨씬 더 스마트하고 유용하게 만들어 줍니다.

아두이노 내장 EEPROM의 이해

대부분의 아두이노 보드에 사용되는 ATmega 마이크로컨트롤러는 내장 EEPROM을 가지고 있습니다. 아두이노 우노(ATmega328P)의 경우 512바이트의 EEPROM을 제공하며, 메가(ATmega2560)는 4KB의 EEPROM을 제공합니다. 용량은 크지 않지만 대부분의 설정 값을 저장하기에는 충분합니다.

EEPROM의 수명과 한계

EEPROM은 비휘발성 메모리이지만, 무한정 데이터를 쓰고 지울 수 있는 것은 아닙니다. 일반적으로 EEPROM 셀은 약 10만 번의 쓰기 주기를 가집니다. 즉, 한 주소에 10만 번 정도 데이터를 쓰면 해당 셀이 더 이상 데이터를 안정적으로 저장하지 못하게 될 수 있습니다. 이는 EEPROM을 사용할 때 가장 중요하게 고려해야 할 사항 중 하나입니다. 읽기 작업에는 이러한 제한이 없습니다.

기본적인 EEPROM 읽기 쓰기 함수

아두이노 IDE는 EEPROM 라이브러리를 통해 EEPROM에 쉽게 접근할 수 있는 함수들을 제공합니다.

  • EEPROM.write(address, value) 특정 주소(0부터 시작)에 1바이트의 데이터를 씁니다.
  • EEPROM.read(address) 특정 주소에서 1바이트의 데이터를 읽어옵니다.
  • EEPROM.update(address, value) 이 함수는 EEPROM.write()와 유사하지만, 현재 저장된 값과 쓰려는 값이 다를 경우에만 쓰기 작업을 수행합니다. 이는 EEPROM의 쓰기 수명을 보존하는 데 매우 유용하므로, EEPROM.write() 대신 EEPROM.update()를 사용하는 것이 좋습니다.

다양한 데이터 형식 저장하기

EEPROM은 기본적으로 1바이트(8비트) 단위로 데이터를 저장합니다. 하지만 아두이노 라이브러리는 EEPROM.put()EEPROM.get() 함수를 제공하여 더 큰 데이터 형식(정수, 실수, 구조체 등)을 쉽게 저장하고 읽을 수 있도록 도와줍니다.

바이트와 정수 저장하기

1바이트 데이터는 EEPROM.write() 또는 EEPROM.update()를 사용하여 직접 저장할 수 있습니다. int(2바이트), long(4바이트) 같은 정수형은 여러 바이트를 차지하므로, EEPROM.put()EEPROM.get()을 사용하는 것이 가장 편리합니다.

예시: 정수 저장 및 읽기 (개념 코드)



#include <EEPROM.h>


int settingValue = 12345;

int address = 0; // 저장할 EEPROM 시작 주소


void setup() {

  Serial.begin(9600);


  // EEPROM에 정수 쓰기

  EEPROM.put(address, settingValue);

  Serial.println("Setting value written to EEPROM.");


  // EEPROM에서 정수 읽기

  int readValue;

  EEPROM.get(address, readValue);

  Serial.print("Setting value read from EEPROM: ");

  Serial.println(readValue);

}


void loop() {

  // 아무것도 하지 않음

}

실수와 문자열 저장하기

float(4바이트)와 double(4 또는 8바이트) 같은 실수형도 EEPROM.put()EEPROM.get()을 사용하여 저장할 수 있습니다. 문자열은 각 문자가 1바이트이므로, 문자 배열로 선언한 후 EEPROM.put()을 사용하거나, 각 문자를 반복문을 통해 개별적으로 저장할 수 있습니다. 단, 문자열의 끝을 나타내는 널 종료 문자(‘’)를 함께 저장하여 읽을 때 문자열의 끝을 알 수 있도록 해야 합니다.

구조체와 사용자 정의 데이터 형식 저장하기

가장 강력하고 편리한 방법은 구조체(struct)를 사용하는 것입니다. 여러 변수를 하나의 구조체로 묶어 EEPROM에 한 번에 저장하고 읽을 수 있습니다. 이는 복잡한 설정 값들을 체계적으로 관리하는 데 매우 효과적입니다.

예시: 구조체 저장 및 읽기 (개념 코드)



#include <EEPROM.h>


// 설정 값을 담을 구조체 정의

struct MySettings {

  int brightness;

  float temperatureOffset;

  char deviceName[16];

  bool enabled;

};


MySettings currentSettings;

int address = 0; // EEPROM 시작 주소


void setup() {

  Serial.begin(9600);


  // 기본 설정 값 초기화 (또는 이전에 저장된 값을 읽어옴)

  currentSettings.brightness = 150;

  currentSettings.temperatureOffset = 2.5;

  strcpy(currentSettings.deviceName, "SmartLamp");

  currentSettings.enabled = true;


  // EEPROM에 구조체 쓰기

  EEPROM.put(address, currentSettings);

  Serial.println("Settings written to EEPROM.");


  // EEPROM에서 구조체 읽기

  MySettings readSettings;

  EEPROM.get(address, readSettings);


  Serial.println("Settings read from EEPROM:");

  Serial.print("Brightness: "); Serial.println(readSettings.brightness);

  Serial.print("Temperature Offset: "); Serial.println(readSettings.temperatureOffset);

  Serial.print("Device Name: "); Serial.println(readSettings.deviceName);

  Serial.print("Enabled: "); Serial.println(readSettings.enabled ? "True" : "False");

}


void loop() {

  // 아무것도 하지 않음

}

EEPROM 수명 보호를 위한 전략

EEPROM의 쓰기 수명은 제한적이므로, 이를 최대한 보호하면서 효율적으로 사용하는 것이 중요합니다.

필요할 때만 쓰기

가장 기본적인 방법은 데이터가 실제로 변경되었을 때만 EEPROM에 쓰는 것입니다. EEPROM.update() 함수가 이 역할을 자동으로 수행해 줍니다. 만약 수동으로 EEPROM.write()를 사용한다면, 현재 EEPROM에 저장된 값과 새로 쓸 값을 비교하여 다를 경우에만 쓰기 작업을 수행하도록 코드를 작성해야 합니다.

예시: 값 변경 시에만 쓰기 (개념 코드)



byte storedValue = EEPROM.read(address);

if (newValue != storedValue) {

  EEPROM.write(address, newValue);

}

// 또는 더 간단하게

EEPROM.update(address, newValue);

데이터 유효성 검증 체크섬 또는 매직 넘버

EEPROM 데이터가 어떤 이유로든 손상되었을 경우를 대비하여 데이터의 유효성을 검증하는 메커니즘을 추가하는 것이 좋습니다. ‘매직 넘버’는 특정 주소에 고유한 값(예: 0xDEADBEEF의 일부 바이트)을 저장해두고, 부팅 시 이 값이 올바른지 확인하는 방법입니다. 값이 일치하면 저장된 데이터가 유효하다고 판단하고, 그렇지 않으면 데이터가 손상되었거나 EEPROM이 초기화되지 않았다고 간주하여 기본 설정 값을 로드합니다.

더 나아가, 저장된 모든 데이터에 대한 체크섬(checksum)을 계산하여 함께 저장하고, 읽을 때 다시 체크섬을 계산하여 비교하는 방법도 있습니다. 이는 데이터의 무결성을 더욱 강력하게 보장합니다.

성공적인 EEPROM 활용을 위한 실용적인 팁

EEPROM을 효과적으로 활용하기 위한 몇 가지 추가적인 팁입니다.

메모리 맵 문서화

EEPROM은 주소 기반으로 데이터를 저장하므로, 어떤 주소에 어떤 종류의 데이터를 저장했는지 명확하게 문서화하는 것이 중요합니다. 특히 프로젝트가 복잡해지거나 여러 사람이 함께 작업할 때 혼란을 방지할 수 있습니다. 주소를 상수로 정의하여 사용하는 것이 좋습니다.

예시: EEPROM 메모리 맵 (개념 코드)



#define EEPROM_ADDRESS_BRIGHTNESS   0

#define EEPROM_ADDRESS_OFFSET       (EEPROM_ADDRESS_BRIGHTNESS + sizeof(int))

#define EEPROM_ADDRESS_DEVICENAME   (EEPROM_ADDRESS_OFFSET + sizeof(float))

#define EEPROM_ADDRESS_ENABLED      (EEPROM_ADDRESS_DEVICENAME + 16) // char 배열 크기

#define EEPROM_ADDRESS_MAGIC_NUMBER (EEPROM_ADDRESS_ENABLED + sizeof(bool))

기본 설정 값 로딩

EEPROM에 저장된 데이터가 없거나 손상되었을 경우를 대비하여, 항상 기본 설정 값을 로드하는 로직을 준비해야 합니다. 이는 프로젝트가 어떤 상황에서도 안정적으로 동작하도록 보장하는 중요한 단계입니다. 매직 넘버를 사용하여 이 로직을 구현할 수 있습니다.

EEPROM 초기화 기능

개발 및 테스트 과정에서 또는 사용자에게 ‘공장 초기화’ 기능을 제공해야 할 때 EEPROM의 모든 내용을 기본값으로 되돌리는 기능이 유용할 수 있습니다. 특정 버튼 조합이나 시리얼 명령을 통해 이 기능을 실행할 수 있도록 구현할 수 있습니다.

전원 공급 안정화

EEPROM 쓰기 작업 중 전원이 불안정하게 공급되면 데이터가 손상될 위험이 있습니다. 안정적인 전원 공급은 물론, 중요한 데이터 쓰기 직전에는 다른 전력 소모가 큰 작업을 피하는 것이 좋습니다.

흔한 오해와 사실 관계

EEPROM에 대한 몇 가지 흔한 오해와 그에 대한 사실을 정리했습니다.

  • 오해 EEPROM은 무한정 쓸 수 있다.

    사실 EEPROM 셀은 약 10만 번의 쓰기 주기를 가집니다. 이 한계를 넘어 쓰면 데이터가 불안정해질 수 있습니다. EEPROM.update()를 사용하여 쓰기 횟수를 최소화하는 것이 중요합니다.

  • 오해 EEPROM은 대용량 데이터 저장에 적합하다.사실 아두이노 내장 EEPROM은 보통 512바이트에서 4KB로 용량이 매우 작습니다. 대량의 센서 데이터 로깅이나 이미지 저장 등에는 적합하지 않습니다. 이런 경우에는 SD 카드 모듈이나 외부 플래시 메모리 등을 고려해야 합니다.
  • 오해 EEPROM은 RAM처럼 빠르다.사실 EEPROM 읽기 및 쓰기 속도는 RAM보다 훨씬 느립니다. 따라서 빈번하게 접근해야 하는 데이터보다는 전원 종료 시에도 유지되어야 하는 설정 값이나 상태 정보 저장에 적합합니다.
  • 오해 모든 아두이노 보드에 외부 EEPROM이 필요하다.사실 대부분의 아두이노 보드에 사용되는 ATmega 마이크로컨트롤러는 내장 EEPROM을 가지고 있습니다. 추가적인 EEPROM이 필요하다면 I2C 통신 방식의 외부 EEPROM 칩을 사용할 수 있습니다.

비용 효율적인 저장 솔루션

내장 EEPROM은 추가 비용 없이 비휘발성 저장 기능을 제공하는 가장 비용 효율적인 솔루션입니다. 대부분의 아두이노 프로젝트에서 필요한 설정 값 저장에는 내장 EEPROM으로 충분합니다.

만약 내장 EEPROM의 용량이 부족하다면, 외부 I2C EEPROM 칩(예: AT24Cxxx 시리즈)을 사용하는 것을 고려할 수 있습니다. 이 칩들은 매우 저렴하며, 몇 킬로바이트에서 수백 킬로바이트의 추가 저장 공간을 제공합니다. I2C 통신을 사용하므로 연결도 간단합니다.

대량의 데이터를 저장해야 한다면 SD 카드 모듈이 더 적합할 수 있습니다. SD 카드는 기가바이트 단위의 저장 공간을 제공하지만, EEPROM처럼 특정 주소에 직접 데이터를 쓰고 읽는 방식과는 다르며, 파일 시스템을 다루는 복잡성이 추가됩니다. 설정 값 저장용으로는 EEPROM, 대량 데이터 로깅용으로는 SD 카드를 용도에 맞게 선택하는 것이 좋습니다.

자주 묻는 질문과 답변

Q1. 아두이노 보드마다 EEPROM 용량이 다른가요

네, 아두이노 보드에 사용된 마이크로컨트롤러의 종류에 따라 EEPROM 용량이 다릅니다. 예를 들어, 아두이노 우노(ATmega328P)는 512바이트, 아두이노 메가(ATmega2560)는 4KB의 EEPROM을 제공합니다.

Q2. EEPROM에 저장된 데이터는 얼마나 오래 유지되나요

일반적으로 EEPROM에 저장된 데이터는 전원 없이도 10년에서 100년까지 유지될 수 있다고 알려져 있습니다. 이는 제조사의 스펙에 따라 다를 수 있습니다.

Q3. EEPROM 쓰기 횟수를 초과하면 어떻게 되나요

특정 EEPROM 셀이 쓰기 횟수 한계를 초과하면, 해당 셀은 더 이상 데이터를 안정적으로 저장하지 못하게 됩니다. 즉, 쓰기 작업을 해도 데이터가 올바르게 기록되지 않거나, 읽기 작업 시 손상된 데이터를 반환할 수 있습니다. 다른 셀들은 계속 사용할 수 있습니다.

Q4. EEPROM에 문자열이나 텍스트를 저장할 수 있나요

네, 문자열이나 텍스트를 저장할 수 있습니다. 각 문자를 1바이트로 취급하여 순차적으로 저장하면 됩니다. C 스타일 문자열의 경우 문자열의 끝을 나타내는 널 종료 문자(‘’)도 함께 저장해야 나중에 제대로 읽어올 수 있습니다. EEPROM.put()EEPROM.get()을 사용하면 문자 배열(char array)을 쉽게 저장하고 읽을 수 있습니다.

Q5. EEPROM 내용을 공장 초기화하는 방법은 무엇인가요

EEPROM의 모든 내용을 공장 초기화하려면, 모든 EEPROM 주소에 미리 정의된 기본 값(예: 0 또는 0xFF)을 쓰는 코드를 실행하면 됩니다. 이 작업을 수행하는 별도의 스케치를 업로드하거나, 메인 스케치 내에서 특정 조건을 만족할 때(예: 특정 버튼을 길게 누르거나 시리얼 명령을 받을 때) 초기화 함수를 호출하도록 구현할 수 있습니다.

Q6. EEPROM에 데이터가 없거나 손상되었는지 어떻게 알 수 있나요

가장 좋은 방법은 EEPROM의 특정 주소에 ‘매직 넘버’ 또는 ‘데이터 유효성 플래그’를 저장해 두는 것입니다. 아두이노가 부팅될 때 이 매직 넘버를 읽어서 예상 값과 일치하는지 확인합니다. 일치하지 않으면 EEPROM 데이터가 초기화되지 않았거나 손상되었다고 판단하고, 기본 설정 값을 로드하도록 로직을 구현합니다.

댓글 남기기