2026년 5월 24일 일요일

[Fluid Ardule] 아두이노 우노 펌웨어에 자동 보정 기능 도입

자정을 넘겨서 그림을 만들도록 했더니 5월 24일 업데이트라는 제목을 달아 버렸다.

저항 '사다리'를 이용한 5-버튼 키패드는 겨우 하나의 아날로그 입력을 사용하기 때문에 매우 단순하다. 하지만 사용을 거듭하면서 동일한 문제가 자꾸 반복하여 발생하기 시작하였다. ADC 값이 항상 일정하지는 않기 때문에 고정 threshold 방식만으로는 안정적으로 처리하기 어려웠다. 어제는 잘 작동하던 Left 버튼이 오늘은 Up 버튼처럼 행세한다.

Threshold를 이따금 조정하는 방식으로는 아두이노 우노의 펌웨어 자체를 매번 고쳐서 업로드해야 하니 여간 성가신 것이 아니다. 근본적으로는 "현재 환경의 실제 ADC 중간값"을 다시 학습해야 한다는 결론에 이르렀다. 이에 따라서 키패드의 self calibration 기능을 아예 구현하여 넣기로 했다.

새로운 펌웨어에서는 엔코더를 길게 누르면 캘리브레이션 모드로 진입한다. LCD는 순서대로 각 버튼을 누르라고 안내하며, 사용자가 버튼을 충분히 길게 누르고 있는 동안 여러 차례 ADC 값을 샘플링한다. 이 과정에서 원래 작동 상태를 나타내는 LED가 아닌 LCD 자체를 blink시키도록 하였는데, 결과적으로 꽤 직관적인 UI가 되었다. 측정 중에는 LCD가 점멸하고, 안정된 값이 확보되면 steady on 상태로 바뀌며 “Release key” 메시지가 나타난다. 사용자는 그때 손을 떼면 된다.

측정된 값은 EEPROM에 저장된다. 따라서 매번 threshold를 코드에 하드코딩할 필요가 없어졌다. 사용 환경이나 부품 상태가 변하더라도 사용자가 직접 다시 보정할 수 있다. 또한 calibration 중에는 runtime BTN/ENC/POT 이벤트를 모두 억제하고, Pi와의 링크는 heartbeat만 최소한으로 유지하도록 하여 watchdog 문제를 피하도록 하였다.

오늘의 개선은 CHANGELOG.md 파일에 5월 24일의 것으로 기록해 넣었다. 내가 생각해도 꽤 버튼 작동과 관련한 부품을 바꾸지 않은 상태에서 가장 최적의 개선을 이룬 것 같다.

Fluid Ardule을 케이스에 넣기 위하여 전후판(3mm 알루미늄)을 설계하면서 디스플레이도 전면판에 수직으로 고정하기로 하였다. 이를 위해서 라즈베리 파이와 TFT-LCD를 연결할 40핀 리본 케이블도 구입하였다. 특별한 것이 아니라 예전에 40핀 IDE HDD용 케이블로 쓰던 것을 그대로 팔고 있었다. 약간 생각을 잘못하여 케이블 양 말단이 female로 마감된 것을 구입하였다. 라즈베리 파이에 붙어 있던 GPIO 헤더 확장용 직각 커넥터를 이용하여 얼추 연결을 마쳤다. 이번에는 라즈베리 파이 -> 40핀 케이블 -> 확장용 직각 커넥터 -> TFT-LCD의 순서로 연결되므로 여기에서 분기하는 I2C DAC(PCM5102A) 등의 점퍼 연결에 주의해야 한다. 라즈베리 파이에 붙어있는 40핀 GPIO와는 좌우가 반전된 것으로 생각해야 하기 때문이다. 글로 옮기려니 쉽지 않다. 배선을 한참 들여다보며 점퍼를 연결하여 겨우 시리얼 콘솔 화면이 뜨게 만들었다.


아이베란다에서 주문한 자작나무 합판도 마침 도착하였다. 수 주일 내에 완성된 모습의 Fluid Ardule을 보게 될 것으로 기대한다.

[사족] 왜 threshold max보다 center 값이 더 나은가

다음의 글은 실제 UNO-1 펌웨어 개선 과정에서 얻는 경험을 바탕으로 정리한 것이다.

Fluid Ardule의 UNO-1은 하나의 아날로그 입력(A0)에 연결된 5-button keypad를 읽는다. 이 키패드는 여러 개의 저항으로 이루어진 일종의 저항 사다리(resistor ladder)이며, 각 버튼을 누르면 서로 다른 ADC 값이 나온다.

처음에는 다음과 같은 방식으로 버튼을 판정할 수 있다. 극히 최근까지 이 방법을 사용했었다.

if (adc <= TH_LEFT_MAX)   LEFT;
else if (adc <= TH_UP_MAX)     UP;
else if (adc <= TH_DOWN_MAX)   DOWN;
else if (adc <= TH_RIGHT_MAX)  RIGHT;
else if (adc <= TH_SELECT_MAX) SELECT;
else NONE;

이 방식은 단순하고 이해하기 쉽다. 그러나 이 값들은 사실 각 버튼의 중심값이 아니라, 버튼 구간의 상한선(max threshold)이다. 즉 “현재 ADC 값이 어느 버튼 값에 가장 가까운가?”를 보는 것이 아니라, “정해진 경계값보다 작은가?”를 순서대로 검사하는 방식이다.

반면 center 기반 판정은 각 버튼의 대표 ADC 값을 저장해 두고, 현재 읽은 ADC 값이 어느 버튼의 중심값에 가장 가까운지를 계산한다. 예를 들어 다음과 같은 값이 있다고 하자.

LEFT   = 60
UP     = 195
DOWN   = 355
RIGHT  = 560
SELECT = 805
NONE   = 1015

ADC 값이 390이라면 DOWN 중심값 355와의 거리는 35이고, RIGHT 중심값 560과의 거리는 170이다. 따라서 이 값은 DOWN으로 보는 것이 자연스럽다.

이 방식은 저항값의 오차, 모듈마다 다른 특성, 전원 전압 변화에 더 유연하다. 특히 self-calibration을 도입한 경우에는 각 버튼의 실제 ADC 대표값을 측정하게 되므로, threshold를 직접 저장하기보다 버튼별 center 값을 저장하는 편이 더 타당하다. threshold는 필요하면 center 값 사이의 중간점으로 나중에 계산할 수 있다.

threshold = (center_A + center_B) / 2

즉 center는 원본 측정값이고, threshold는 그로부터 파생되는 값이다. 원본 데이터를 저장해 두면 나중에 허용 오차, hysteresis, release 판정 방식을 바꾸더라도 기존 calibration 데이터를 더 유연하게 활용할 수 있다.

물론 max threshold 방식에도 장점은 있다. 코드가 짧고 빠르며, Arduino 예제에서 흔히 쓰이는 방식이다. 하지만 Fluid Ardule의 UNO-1처럼 long press, rotary encoder, potentiometer, self-calibration, EEPROM 저장까지 포함하는 입력 컨트롤러라면 center 기반 판정이 더 안정적인 설계에 가깝다.

정리하면 다음과 같다.

max threshold 방식:
- 단순하다
- 디버깅이 쉽다
- 하지만 모듈 편차와 전압 변화에 약하다

center 기반 방식:
- 실제 버튼의 대표 ADC 값을 저장한다
- self-calibration과 잘 맞는다
- 가장 가까운 버튼을 찾는 구조라 더 자연스럽다
- threshold는 center 값에서 계산할 수 있다

따라서 Fluid Ardule UNO-1에서는 fallback 값도 고정 threshold보다는 버튼별 기본 center 값으로 관리하는 것이 바람직하다. EEPROM에 저장된 calibration 값이 없거나 손상되었을 때는 이 기본 center 값을 사용하고, 사용자가 self-calibration을 수행하면 실제 장치에 맞는 center 값으로 갱신하는 구조가 가장 안전하다.

댓글 없음: