ESP32-C3 마이크로파이썬 NVS(Non-Volatile Storage) 완벽 가이드
ESP32-C3와 MicroPython을 활용하면서 가장 고민되는 부분 중 하나가 **’전원이 꺼져도 데이터를 어떻게 유지할 것인가’**입니다. 일반적인 변수는 재부팅 시 초기화되지만, **NVS(Non-Volatile Storage)**를 사용하면 설정값이나 상태 정보를 안전하게 저장할 수 있습니다.
오늘은 MicroPython에서 ESP32-C3의 NVS를 다루는 방법과 활용 팁을 정리해 보겠습니다.
1. NVS(Non-Volatile Storage)란?
NVS는 ESP32의 메인 플래시 메모리 일부를 사용하여 데이터를 저장하는 방식입니다. 일반적인 파일 시스템(LittleFS)과 달리 Key-Value 쌍으로 데이터를 저장하며, 다음과 같은 장점이 있습니다.
- 설정값 저장에 최적: Wi-Fi 비번, 센서 보정값, 장치 ID 등을 저장하기 좋습니다.
- 파일 시스템보다 가볍다: 복잡한 파일 입출력 과정 없이 간단하게 읽고 쓸 수 있습니다.
- 안정성: 전원이 갑자기 차단되어도 데이터가 손상될 확률이 적습니다.
2. MicroPython에서 NVS 사용하기
MicroPython은 esp32 모듈 내에 NVS 클래스를 제공합니다.
🛠️ 기본 코드 스켈레톤
Python
from esp32 import NVS
# 1. NVS 객체 생성 (네임스페이스 지정)
# 'storage'라는 이름의 공간을 생성합니다.
nvs = NVS("storage")
# 2. 데이터 쓰기 (Set)
# 정수(i)와 바이너리(blob) 형태를 지원합니다.
nvs.set_i32("boot_count", 10) # 32비트 정수 저장
nvs.set_blob("dev_name", "My-ESP32-C3") # 문자열/바이너리 저장
# 3. 변경사항 적용
nvs.commit()
# 4. 데이터 읽기 (Get)
# 읽어올 때는 변수 타입을 맞춰야 하며, 값이 없을 경우를 대비해 버퍼가 필요할 수 있습니다.
val = nvs.get_i32("boot_count")
print(f"부팅 횟수: {val}")
3. 실전 활용: 부팅 횟수 카운터 만들기
ESP32-C3가 켜질 때마다 숫자를 1씩 증가시켜 NVS에 저장하는 간단한 예제입니다.
Python
from esp32 import NVS
import time
nvs = NVS("counter_db")
buf = bytearray(10) # 문자열 등을 읽을 때 사용
try:
# 기존 카운트 읽기 (값이 없으면 에러 발생할 수 있음)
count = nvs.get_i32("count")
except OSError:
# 처음 실행 시 초기값 설정
count = 0
count += 1
nvs.set_i32("count", count)
nvs.commit()
print("-" * 30)
print(f"현재 부팅 횟수: {count}")
print("-" * 30)
4. ESP32-C3 사용 시 주의사항
- 쓰기 횟수 제한: NVS는 플래시 메모리를 사용합니다. 수만 번 이상의 반복적인 쓰기는 메모리 수명을 단축시킬 수 있으므로, 루프 내에서 무분별하게
commit()을 호출하는 것은 피해야 합니다. - 데이터 타입 제한: MicroPython의 NVS 구현은 주로
i32(정수)와blob(바이너리 데이터)을 지원합니다. 부동소수점(Float)을 저장하려면 바이너리로 변환하거나 100을 곱해 정수로 변환하여 저장하는 트릭이 필요합니다. - 네임스페이스 관리: 여러 라이브러리를 사용할 경우 네임스페이스(
NVS("이름"))를 구분하여 데이터 충돌을 방지하세요.
5. 마치며
ESP32-C3는 저전력과 효율성이 강조된 칩인 만큼, 굳이 무거운 파일을 만들지 않고도 설정을 유지할 수 있는 NVS 활용은 필수적입니다. Wi-Fi 정보나 마지막 센서 상태값을 저장해두어 스마트한 IoT 기기를 만들어보세요!
** NVS 활용방법 **
1. 스마트 기기 설정값 관리 (Wi-Fi 정보 및 API 키)
파일 시스템(LittleFS)에 config.json을 만들어 관리할 수도 있지만, NVS를 쓰면 파일 파손 위험이 적고 접근 속도가 빠릅니다.
- 활용 예시: 사용자가 설정한 Wi-Fi SSID, 비밀번호, 센서 보정(Calibration) 값, 클라우드 서버 API 토큰 저장.
- 장점: 설정값이 바뀌어도 코드 수정 없이 NVS의 특정 키값만 업데이트하면 됩니다.
Python
# 초기 설정 시 1회 저장
nvs.set_blob("wifi_ssid", "Home_AP")
nvs.set_blob("wifi_pass", "12345678")
nvs.commit()
2. 장치 상태 복원 (Last State Save)
갑작스러운 정전이나 재부팅 후에도 장치가 **”이전의 상태”**를 기억해야 할 때 유용합니다.
- 활용 예시:
- 스마트 전구: 전원이 꺼지기 전 마지막 밝기와 색상 유지.
- 스마트 팜: 마지막으로 물을 준 시간 기록.
- 산업용 카운터: 누적 생산량 카운트 유지.
- 팁: 상태가 바뀔 때마다 쓰기보다는, 특정 주기나 임계값이 변했을 때만
commit()하여 플래시 메모리 수명을 보호하세요.
3. Deep Sleep 데이터 전달
ESP32-C3의 Deep Sleep 모드에 들어가면 RAM의 모든 데이터가 사라집니다. 이때 RTC Memory를 쓸 수도 있지만, 데이터가 복잡하거나 전원이 아예 차단될 가능성이 있다면 NVS가 정답입니다.
- 활용 예시: 딥슬립에서 깨어날 때마다 센서 데이터를 NVS에 누적 저장한 뒤, 10개가 쌓이면 한꺼번에 서버로 전송.
Python
# NVS에서 누적 데이터 가져오기
try:
data_count = nvs.get_i32("count")
except:
data_count = 0
# 데이터가 일정 수치 이상 쌓였는지 확인
if data_count >= 10:
send_to_server() # 서버 전송 함수
nvs.set_i32("count", 0) # 카운트 초기화
else:
nvs.set_i32("count", data_count + 1)
nvs.commit()
4. OTA(Over-the-Air) 펌웨어 업데이트 상태 기록
무선으로 펌웨어를 업데이트할 때, 업데이트 진행 단계나 성공 여부 플래그를 NVS에 저장합니다.
- 활용 예시: 업데이트 도중 재부팅되었을 때 NVS를 읽어 “아직 업데이트 중”임을 인지하고 작업을 재개할 수 있습니다.
💡 전문가의 꿀팁: 데이터 타입 변환
MicroPython NVS는 정수와 바이너리(blob)만 지원합니다. 리스트나 딕셔너리를 저장하고 싶다면 ujson을 활용하세요.
Python
import ujson
# 저장할 때 (Dictionary -> JSON String -> Blob)
data = {"temp": 25.5, "hum": 60}
nvs.set_blob("env_data", ujson.dumps(data))
nvs.commit()
# 읽어올 때 (Blob -> JSON String -> Dictionary)
buf = bytearray(50) # 적절한 크기의 버퍼 생성
nvs.get_blob("env_data", buf)
original_data = ujson.loads(buf.decode().strip('\x00'))
NVS를 적절히 활용하면 단순한 임베디드 코딩을 넘어, 실제 상용 제품 수준의 안정적인 시스템을 구축할 수 있습니다.
** NVS 용량은 어느정도인가? esp32-c3 mini 의 경우 **
ESP32-C3 Mini와 같은 모듈에서 MicroPython을 사용할 때, NVS의 용량은 ‘파티션 테이블(Partition Table)’ 설정에 따라 결정됩니다. 하지만 일반적인 사용 환경에서의 기준은 다음과 같습니다.
1. 기본 할당 용량 (Default)
따로 설정을 건드리지 않고 공식 MicroPython 펌웨어를 올렸다면, 보통 24KB (0x6000 bytes) 정도가 NVS 영역으로 잡혀 있습니다.
- 실제 사용 가능량: 내부 관리용 헤더와 메타데이터(약 4~8KB)를 제외하면, 실제로 우리가 데이터를 저장할 수 있는 공간은 약 15~18KB 정도입니다.
- 저장 가능 개수: 아주 작은 정수나 짧은 문자열 기준으로는 약 100~200개 이상의 Key-Value 쌍을 저장할 수 있습니다.
2. 최대 확장 가능 용량
만약 24KB보다 더 큰 공간이 필요하다면, 파티션 테이블을 직접 수정하여 NVS 용량을 늘릴 수 있습니다.
- 이론적 한계: NVS 파티션은 수 MB 단위까지 키울 수 있습니다.
- 권장 한계: 에스프레시프(Espressif) 가이드에 따르면, NVS는 64KB 이하로 유지하는 것을 권장합니다.
- 이유 1 (속도): NVS는 데이터를 찾을 때 인덱스를 훑어야 하므로, 용량이 너무 크면 읽기/쓰기 속도가 느려집니다.
- 이유 2 (RAM): NVS를 초기화할 때 파티션 크기에 비례하여 RAM(Heap)을 사용합니다. (1MB당 약 22KB의 RAM 소모)
3. 데이터 유형별 크기 제한
용량만큼 중요한 것이 한 번에 저장할 수 있는 데이터의 크기입니다.
| 데이터 타입 | 최대 크기 제한 | 비고 |
|---|---|---|
| 정수 (Integer) | 64-bit (8 bytes) | set_i32, set_i64 등으로 저장 |
| 문자열 (String) | 4,000 bytes | 종료 문자 포함 |
| 바이너리 (Blob) | 약 500 KB | 파티션 크기의 97.6% 또는 508,000 bytes 중 작은 값 |
4. 요약 및 제안
- 간단한 설정값(Wi-Fi, ID, 보정값): 기본 24KB로도 차고 넘칩니다.
- 큰 데이터(이미지, 로그 파일, 긴 텍스트): NVS 대신 **LittleFS(파일 시스템)**를 사용하세요. NVS는 “설정값 저장용”이지 “데이터베이스나 파일 저장용”이 아닙니다.
- ESP32-C3 Mini 주의점: 보통 4MB 플래시를 탑재하고 있는데, NVS를 너무 크게 잡으면 펌웨어가 들어갈 자리나 일반 파일 저장 공간이 부족해질 수 있으니 주의해야 합니다.
결론: 특별한 설정 없이 사용하신다면 약 15KB 내외의 데이터를 Key-Value 형태로 쪼개서 저장한다고 생각하시면 가장 안전합니다!
** 파일로 저장 vs NVS 저장 비교 **
open("config.txt", "w") 같은 방식으로 파일을 직접 다루는 것이 훨씬 직관적이고, PC에 연결했을 때 내부를 직접 들여다볼 수 있어 디버깅도 편하죠.
그럼에도 불구하고 왜 임베디드 전문가들이 NVS를 따로 사용하는지, 파일 시스템(LittleFS/FAT)과 비교해 장단점을 명확히 짚어 드릴게요.
1. NVS vs 파일 시스템(File System) 비교
| 비교 항목 | NVS (Key-Value) | 파일 시스템 (open/write) |
|---|---|---|
| 저장 방식 | 키-값 쌍 (딕셔너리 형태) | 전체 파일 (바이트 스트림) |
| 안정성 | 매우 높음 (원자성 보장) | 상대적으로 낮음 (전원 차단 시 깨짐) |
| 속도 | 빠름 (특정 값만 즉시 수정) | 느림 (파일 전체를 읽고 써야 함) |
| 메모리 수명 | 분산 저장으로 플래시 보호 | 동일 위치 반복 쓰기로 수명 단축 위험 |
| 직관성 | 코드 안에서만 확인 가능 | 파일 탐색기에서 바로 확인 가능 |
2. 왜 NVS가 더 유리할까? (장점)
① 전원 차단에 대한 내성 (Atomic Write) 파일 시스템의 최대 약점은 **”파일을 쓰는 도중 전원이 꺼지면 파일 자체가 깨질 수 있다”**는 점입니다. 반면 NVS는 내부적으로 트랜잭션 처리가 되어 있어, 쓰기 작업 도중 전원이 나가도 이전 데이터가 유지되거나 최신 데이터가 완전히 저장되는 것을 보장합니다.
② 플래시 메모리 수명 연장 (Wear Leveling) 파일 시스템에서 특정 설정 파일 하나를 계속 수정하면, 실제 플래시 메모리의 같은 물리적 위치를 반복해서 긁게 될 확률이 높습니다. NVS는 데이터를 저장할 때 내부적으로 위치를 골고루 분산시켜 저장하므로 칩의 수명을 더 길게 유지해 줍니다.
③ 부분 수정의 효율성 설정값이 50개인데 그중 ‘밝기’ 하나만 바꾸고 싶을 때:
- 파일: 전체 파일을 읽어서 메모리에 올리고 -> 값 수정 -> 전체 다시 쓰기 (무겁고 위험)
- NVS:
nvs.set_i32("bright", 100)한 줄로 끝. 해당 위치만 갱신 (가볍고 안전)
3. 왜 파일 시스템이 더 편할까? (단점)
① 시각적 확인 불가 질문하신 대로 NVS는 툴을 사용하지 않는 이상 눈에 보이지 않습니다. 파일은 main.py 옆에 config.json이 딱 버티고 있으니 관리하기가 심리적으로 편하죠.
② 복잡한 데이터 구조 NVS는 단순한 숫자나 문자열에는 강하지만, 리스트 안에 딕셔너리가 있고 그 안에 또 리스트가 있는 복잡한 계층 구조를 저장하기엔 파일(JSON 형태 등)이 훨씬 유리합니다.
💡 결론: 언제 무엇을 쓸까?
- NVS를 쓰세요:
- Wi-Fi 비번, 센서 보정값, 장치 ID 등 시스템 핵심 설정값
- 장치가 작동 중에 자주 업데이트되는 상태 정보 (부팅 카운트, 마지막 동작 모드 등)
- 절대 깨지면 안 되는 최소한의 기본값
- 파일 시스템(open)을 쓰세요:
- 센서 데이터 로그(CSV), 웹 서버용 HTML 파일 등 데이터 양이 많을 때
- PC에서 수시로 파일을 직접 열어서 내용을 확인하거나 수정해야 할 때
- 복잡한 객체나 대량의 텍스트를 저장할 때
한 줄 요약: “안전하고 가볍게 유지하고 싶은 시스템 설정은 NVS, 덩치가 크고 눈으로 보고 싶은 데이터는 파일“이 정답입니다!


답글 남기기