1 minute read

웹과 클라우드 이야기만 쓰다가 오늘은 완전히 다른 층위의 이야기입니다. 무인카페에 들어가는 커피머신 제어 보드 펌웨어를 새로 만든 경험담입니다.

소스가 없다

상용 커피머신 보드(M500/M400)를 쓰는데, 제조사 소스코드가 없습니다. 있는 것은 보드에서 뽑아낸 hex 파일과 시리얼 프로토콜 스펙 엑셀 문서뿐. 기능을 고치거나 새 하드웨어에 대응하려면 방법은 하나였습니다. hex를 디스어셈블해서 동작을 복원하고, 처음부터 다시 쓰는 것.

ATmega2560 기반으로 클린 재작성을 시작했습니다.

클럭이 16MHz가 아니었다

이 프로젝트에서 가장 기억에 남는 순간입니다. Arduino Mega 보드니까 당연히 16MHz겠거니 하고 작성했는데, 커피 추출 시퀀스 타이밍이 미묘하게 안 맞았습니다. 약 8% 빨랐습니다.

원본 펌웨어의 디스어셈블리로 돌아가서 Timer1의 preload 값과 prescaler를 역산해보니 답이 나왔습니다. F_CPU = 14.7456MHz. 시리얼 통신(38400 baud)에 오차가 없는 크리스탈을 쓴 겁니다. 16MHz로 가정하면 100ms 시퀀스 틱이 8% 빨라져서 추출 시간이 다 틀어집니다. 하드웨어는 “그럴 것이다”가 아니라 “그렇다”를 확인해야 한다는 교훈을 몸으로 배웠습니다.

논블로킹으로 전부 다시 쓰기

원본 펌웨어의 구조적 문제는 블로킹이었습니다. EEPROM 쓰는 동안 프로토콜 응답이 늦고, ADC 읽는 동안 시퀀스가 멈추고. 재작성하면서 원칙을 하나 세웠습니다. 메인 루프에서 그 무엇도 기다리지 않는다.

  • ADC 센서 읽기: 논블로킹 상태 머신
  • EEPROM 쓰기: 틱당 1바이트씩 비동기로
  • 커피 추출 시퀀스: 100ms 틱 기반 상태 머신
  • 프로토콜(키오스크와의 시리얼 통신): 최우선 처리, 워치독 리셋 후에도 부트로더 진입 보장

M400 보드의 브로멕(추출 유닛)은 위치 센서가 없어서, 모터 전류를 읽어 위치를 추정하고 과부하를 감지하는 상태 머신을 넣었습니다.

검증 도구까지가 펌웨어다

펌웨어만 짜서는 끝나지 않습니다. Python으로 도구를 같이 만들었습니다. 보드 없이 프로토콜을 테스트하는 시뮬레이터, 실보드 시리얼 모니터, 펌웨어 업로더, 자동 테스터. 호스트에서 도는 단위 테스트도 붙였습니다(임베디드 코드를 PC에서 컴파일해 로직만 검증하는 방식). 최종적으로 실보드에서 검증을 마치고 실제 매장 머신에 올라갔습니다.

왜 이 일이 즐거웠나

18년 경력의 시작이 MFC와 임베디드 가상키보드, 통신 서버였습니다. 한 바퀴 돌아 다시 로우레벨로 내려와 보니, 그 사이에 쌓인 것들이 보였습니다. 예전엔 오실로스코프와 감으로 하던 일을, 지금은 디스어셈블리를 Claude Code에 던져 “이 타이머 설정이면 실제 주파수가 얼마냐”를 같이 계산하고, Python 시뮬레이터를 한나절 만에 만들어 검증합니다. 도구는 바뀌었지만 본질은 같습니다. 기계가 실제로 어떻게 동작하는지 끝까지 파고드는 것.

웹 개발자에게 임베디드는 멀게 느껴지지만, 논블로킹 루프는 이벤트 루프고, 시리얼 프로토콜은 결국 파싱과 상태 머신입니다. 여러분이 이미 아는 것들입니다. 기회가 되면 한번 내려와 보세요. 재미있습니다.

Updated: