2026년 1월 25일 일요일

옆길로 샌 Ardule Project - USB MIDI host 만들기는 좌절과 극복의 연속이었다

아두이노 우노 R3 입문 키트(KEYES KT0001)를 주문한 것이 2020년 10월(관련 글). 최근 약 1년 동안은 이 키트와는 별도로 구입한 아두이노 나노 '클래식'과 아두이노 나노 에브리를 사용한 MIDI 컨트롤러 및 드럼 패턴 플레이어를 만드느라 몰두하였었다. GitHub에도 문서와 코드를 비롯한 거의 모든 자료를 올려 두었다. 프로젝트명은 Ardule Project - Arduino Nano-based Drum Patternology Ecosystem이다.

아두이노 식구 중에서 가장 먼저 구입했던 아두이노 우노는 초창기에 몇 번의 간단한 실습을 한 것 외에는 별다른 용도를 찾지 못하고 있었다. (Nano) Ardule의 곁다리 프로젝트로서 계획한 것은 USB MIDI host이다. USB 단자밖에 없는 MIDI controller keyboard를 DIN 5-pin MIDI 커넥터만 갖고 있는 MIDI 사운드 모듈에 연결하여 실시간 연주를 할 수 있는 중간 '어댑터'에 해당하는 것을 만들고 있었다. PC와 USB MIDI cable이 있으면 된다. 이러한 경우 신호의 방향을 반대로 할 수도 있다. 하지만 컴퓨터가 없으면 매우 곤란하다. 

아직도 쉽게 구할 수 있는 USB MIDI 케이블. 실은 MIDI 인터페이스에 해당한다.

아두이노 우노의 생태계에는 위에 포개어 장착하여 기능을 확장할 수 있는 다양한 'shield'가 존재한다. USB host shield와 MIDI shield만 있으면 납땜 작업을 전혀 하지 않고 원하는 일을 이룰 수 있을 것이라 생각했다.

그런데 그게 아니었다.

첫 좌절은 아두이노 우노 + USB host shield를 연결하고 여기에 USB MIDI keyboard controller를 연결했을 때 건반이 전혀 인식이 되지 않았었다. 이는 USB host shield가 기본 구성으로는 자체 USB-A 커넥터로 5V를 내보내지 않기 때문이었다. 보드 내에서 두 군데의 패드를 납으로 이어야 한다는 사실 앞에서 첫 좌절을 겪었(관련 글).

두 번째 좌절. 아두이노 우노와 USB host shield는 양 옆 가장자리의 커넥터 외에도 ICSP(In-Cirtuit Serial Programming)라는 6핀 헤더를 통해 연결된다. 아두이노 우노에는 핀이, 위에 포개어 놓는 USB host shield에는 소켓이 위치한다. 그런데 보드 두 장을 포개어 ICSP 커넥터를 완전히 밀착하더라도, 좌우의 핀은 끝까지 끼워지질 않는다. 이것 때문에 USB host shield에 건반을 꽂아도 인식이 되다말다 하는 일이 벌어졌다. 좌우의 핀 배열을 통해 +5V가 확실하게 공급되어야 하기 때문이다.

USB host shield의 ICSP 핀헤더 소켓(2x3)이 문제다.  너무 높다!

처음에는 이 접촉 문제를 가벼이 여기고 전원이 충분하지 않다고 생각했다. PC->USB 커넥터로 공급되는 5V의 전류에는 한계가 있으니, 2A 이상의 대전류를 흘릴 수 있는 5V 전원을 마련하여 아두이노 우노의 +5V 핀에 공급하기로 하였다. 아두이노 우노의 소켓에는 USB host shield가 꽂혀야 하니 납땜을 통해 전원 공급선을 별도로 빼야 한다. DC 어댑터 잭에 약간 높은(예: 7V~9V) 전원을 넣으면 전선 피복을 벗기고 납땜을 하지 않아도 되겠지만, 전압차가 클 경우 내부 레귤레이터가 너무 무리를 하게 된다. 전압차는 전부 열로 바뀌기 때문이다. 

하필이면 내가 갖고 있는 어댑터는 5V와 12V뿐이다. 후자를 이용할 경우, 부품통에 굴러다니는 스텝다운 모듈을 쓰면 된다. 그리고 PC쪽의 USB를 통한 전원과 독립 전원을 동시에 연결하면 좋지 않다는 의견이 있어서 PC쪽에서는 5V를 공급하지 못하도록 케이블을 개조하기로 하였다. 정확히 말하자면 케이블을 개조하는 것이 아니라 USB 케이블 어댑터를 새로 만들었다. 시중의 USB 케이블은 커넥터와 선재가 거의 일체형태라서 작업을 하기가 불편하기 때문이다. 알리익스프레스에서 DIY용 USB 커넥터를 별도로 구입하느라 며칠이 소요되었다. 기기 보호를 위해 데이터는 차단하고 충전만 되는 USB 케이블용 어댑터는 비교적 흔하다. 

전원 차단형 USB 케이블 어댑터 만들기. 커넥터는 나사가 아니라 접착제로 붙여야 한다. 정말 오랜만에 플라스틱 모델용 접착제 'TAMIYA CEMENT'를 꺼냈다. 아마 25년은 족히 묵었을 것이다. 수축 튜브를 케이블 중간 부분에는 씌우지 않는 실수를 범하였다.

아두이노 우노의 아랫쪽에서 전원과 그라운드를 위한 선을 납땜하였다. 그러나 이것으로 충분하지 않았다. 아두이노와 USB host shield의 핀 접촉이 완벽하지 못하다. 이보다 더 밀착해야 한다!

어쨌든 충분한 전류를 흘릴 수 있는 전원장치까지 연결하였는데 키보드 인식이 매우 불안하였다. 몇 번의 실험을 거친 최종 결론은 두 보드 좌우의 핀/소켓 어레이가 완전히 밀착하지 않은 데에 원인이 있다는 것이었다. USB host shield의 ICSP 소켓을 제거한 뒤에 아두이노 우노와 연결할 방법을 고민하였다. 그렇지! IC 소켓 하나를 반으로 잘라서 쓰면 되겠구나. 원형 IC 소켓 스트립도 갖고 있었지만 핀이 도저히 들어가지 않았다.

왼쪽부터 MIDI shield, 소켓을 개조한 USB host shield, 그리고 아두이노 우노. 개조를 하지 않은 실드도 보이고 있다.

ICSP 커넥터 체결에 대한 오해 때문에 USB host shield도 두 개를 보유하게 되었다. 원래 갖고 있던 것은 2020년에 아두이노 우노 R3 키트를 살 때 들어 있었는지, 나중에 따론 산 것인지 잘 기억이 나지 않는다. 왜 최근에 실드를 하나 더 사게 되었나? 판단 착오의 흐름은 다음과 같다.

  1. 어라? ICSP 커넥터 때문에 두 보드의 핀/소켓 헤더가 밀착되지 않네. ICSP 소켓을 실드에서 떼어 버리자!
  2. 어라? ICSP 커넥터를 연결하지 않으면 안 되는 거였네... 원래 그렇게 좌우 핀/소켓이 들뜬 상태에서 쓰는 건가? 에이, 하나 더 사자.
  3. 어라? 새로 산 보드는 작동이 되다 안되다 하네... 그러면 ICSP 소켓을 떼어버린 이전 shield를 쓰되 어떻게 해서든 아두이노 우노와 연결을 해야 되겠네. 흑흑...

여기까지 하여 USB 키보드를 연결한 뒤 키를 누르면 PC의 아두이노 IDE 시리얼 모니터에 이벤트가 출력되게 하는 것까지 성공하였다. 이제 MIDI shield를 통해 MIDI out 신호만 나가게 하면 될 것이라 기대하였다. 

모든 논의와 C++ 코드 설계는 챗GPT의 도움을 받았다. 이제 마지막으로 생각하고 펌웨어를 업로드하는데 뭐가 좀 이상하다. 업로드 과정에서 에러가 발생하는 것이었다. MIDI shield에 붙어 있는 용도 미상의 ON/OFF 슬라이드 스위치가 수상하였다. 세 번째 좌절이었다.

아두이노 나노 에브리와 달리 아두이노 우노 및 아두이노 나노 클래식은 펌웨어 업로드시 RX(D0)이 다른 곳에 연결되어 있으면 안 된다. 그래서 펌웨어 업로드 시에는 점퍼를 사용하는 등 별도의 수단을 강구해야 한다. 그리고 TX(D1)을 통해 UART MIDI 신호가 나가게 되므로 기본적으로 시리얼 모니터도 쓰지 못한다.

이 스위치를 OFF 위치에 놓으니 비로소 펌웨어 업로드가 성공하였다. 아마도 이 스위치를 ON으로 놓으면 는 DIN 5핀 커넥터로 들어오는 MIDI IN 신호를 RX 핀으로 보내는 기능을 하는 것으로 여겨진다.

MIDI IN을 쓰려면 스위치를 ON으로 놓을 것.

펌웨어 업로드가 되었지만 여전히 소리는 나지 않았다. USB-MIDI 신호의 파싱에 문제가 있었기 때문이다. 이것이 네 번째 좌절이었다. MIDI shield만을 이용하여 '도레미파솔라시도'를 무한 반복하는 코드를 테스트하면서 보드가 정상임을 먼저 확인한 뒤 문제를 해결해 나갔다.

무슨 말인지 나도 잘 모르겠음. 완벽하게 이해를 하지 못하더라도 학습을 위해 중간 단계마다 문서를 만들어서 잘 저장해 두었다. 


이렇게 몇 단계의 좌절을 거쳐서 드디어 USB 키보드 컨트롤러를 통해 롤랜드 사운드캔버스 SC-D70을 제어하여 소리를 낼 수 있었다. 보드 3장이 겹쳐져 있어서 맨 아래 아두이노 우노의 LED가 잘 보이지 않는다. 작동 상태를 표시할 겸 I2C 2004 LCD를 연결하였다. LCD 표시에 너무 충실하면 키보드 제어에 지연이 발생하기 때문에 몇 번의 최적화를 거쳤다. 


2004 LCD는 이 작은 기기에게 지나치게 크다. 1602 LCD 또는 0.96인치 OLED 디스플레이가 더 적합하겠지만 일단 갖고 있는 부품으로 작동을 확인하고 싶은 욕심이 컸다. 최종 개발품의 소형화를 위해 나중에 OLED 디스플레이를 써 보고 싶다.

케이스만 씌우면 실사용에 문제가 없는 수준이 되었다. 실은 DIY에서 케이스를 만드는 일이 가장 어려운 단계임은 누구나 잘 안다.

Nano Ardule 생태계 구축에 들인 수 개월 동안의 노력이나 숱한 좌절을 생각하면 이번 서브 프로젝트는 매우 빠르게 진척된 셈이다. 가장 시급한 기능 개선은 디스플레이를 가장 능률적인 것으로 확정하는 것이다. 하드웨어 버튼 같은 것은 달고 싶지 않다. 채널 리맵/레이어링 기능 등은 Nano Ardule MIDI Controller에 이미 충분히 구현되어 있으니 이것을 다음에 연결하여 사용하면 된다.

이렇게 Ardule 세계의 장난감이 하나 둘 늘어나고 있다.

2026년 1월 19일 월요일

아두이노 USB 호스트 실드를 사용하려면 보드 내에서 5V 패드를 납땜으로 연결해야 한다

Nano Ardule 드럼 패턴학 생태계를 열심히 일구면서 핵심적인 기능을 거의 다 개발한 단계까지 왔다. 문서 또는 비디오 형태의 매뉴얼을 만드는 일만 남았다고 생각한다. 보다 더 욕심을 내자면 KiCad를 익혀서 PCB를 만들어 보고 싶다. 페놀기판에 얼기설기 엮어서 만든 현재의 프로토타입은 영 모양새가 좋지 않기 때문이다. 그야말로 전자 DIY의 마지막 단계까지 가 보고 싶다. 물론 이를 전부 수용하는 아름다운 케이스를 만드는 일이 DIY의 진정한 마지막 단계일 것이다. 

PCB 주문을 위한 Gerber 파일 설계는 내가 대충 익혀서 사용한 Fritzing에서도 가능한 것으로 아는데, 브레드보드가 아니라 perfboard를 바탕으로 대충 회로도를 그려 놓았기 때문에 연결 상태가 완벽한지 아직 자신이 없다.

잠시 옆길로 새기 위해 아두이노 UNO에 층을 쌓듯이 포개어 쓸 수 있는 두 종류의 실드('Shield')를 구입하였다. 그것은 USB 호스트 실드와 MIDI 실드. 컴퓨터를 거치지 않고도 USB MIDI 키보드를 구식 사운드 모듈(DIN 5핀 MIDI 단자)에 연결하여 직접 제어하기 위함이다. 이를 Ardule USB MIDI Host라 부르고자 한다.

아래부터 아두이노 우노 R3, USB host shield, 그리고 MIDI shield로 3층집을 쌓았다.


납땜도 필요하지 않은 DIY라서 펌웨어만 올리면 자연스럽게 동작을 하리라고 기대하였다. 우선 USB host shield에 USB MIDI Controller Keyboard(AKAI MPK mini MK2)를 연결한 뒤 제대로 인식이 되고 건반을 눌렀을 때 note on/off 신호가 나오는지 시리얼 모니터로 출력하는 코드를 올려 보았다.

그러나... 건반에 불이 들어오지 않는다. USB 단자를 통해서 전원이 공급되어야 하는데 그렇지 않은 것 같다. 전류가 부족한가? 챗GPT와 더불어 고민을 해 보니 USB host shield의 보드 안에서 VBUS로 5V가 공급되도록 직접 납땜을 해야 한다는 것이다. 다음 사진은 납땜을 마친 뒤 찍은 것이다. 빨간 원으로 표시한 패드 두 곳을 납땜하여 이어야 아두이노 UNO로부터 핀 헤더를 통해 공급된 5V가 USB 단자의 VBUS 핀을 통해 USB 디바이스로 공급된다고 한다. 의외로 이런 정보는 판매처(알리익스프레스)에서도 잘 제공하고 있지 않으며, 국문 웹 자료에서도 거의 찾아보기 어렵다. 사용자가 직접 납땜을 해야 함을 알려주는 판매자의 웹사이트를 어렵게 찾아서 소개한다(링크).

보드를 관찰한 후 내가 내린 결론은 이렇다. 아두이노 우노를 통해 공급되는 3.3V/5V를 USB 호스트 실드로 가져오려면 헤더쪽(위 사진에서 아랫줄)의 패드를 전부 납땜하여 연결해야 하고, 이를 USB 호스트 실드의 USB 단자를 통해 공급하려면 3.3V와 5V 중 원하는 것을 VBUS와 연결되도록 납땜을 하면 된다. 나에게 필요한 것은 5V이므로, 헤더쪽에서 오는 전원 중 5V만 연결하였다.

왜 USB 호스트 실드는 이렇게 '반제품'처럼 만들어서 판매하는 것일까? USB로 접속하는 모든 기기가 이를 통해 전원을 공급받는 것은 아니기 때문에 사용자에게 선택 여지를 준 것이라고 생각한다. 잘못하면 전원의 중복으로 인해 오류가 날 수도 있다. 

이러한 시행착오 끝에 USB MIDI 컨트롤러 키보드를 연결하니 성공적으로 인식이 되고, 키 누름에 반응하여 시리얼 모니터에 note on/off 메시지가 나오는 것을 확인하였다.



무난히 USB 호스트 실드의 테스트를 마치고 다음 단계로 진행하려는 찰나, 키보드의 인식이 불안정함을 느꼈다. 아무래도 PC -> USB -> 아두이노 우노를 통해서 다시 외부 USB 기기(키보드)를 동작시킬 전류까지 충당하기에는 부족함이 있는 것 같다. 상세한 raw message를 확인해 보았다. 테스트했던 다른 코드에서는 raw=18 (0x12)가 나오기도 한다. 이것은 detached / idle을 의미한다.

Ardule Bridge - USB/MIDI FULL DIAG 
USB Host Shield init OK 
Plug / unplug USB MIDI device and operate keys... 
[USB] State raw = 32 (0x20)  # attached
[USB] State raw = 64 (0x40)  # powered
[USB] State raw = 80 (0x50)  # default / enumeration start
[USB] State raw = 81 (0x51)  # configured running
[USB] State raw = 144 (0x90) # error

raw=144(0x90)은 에러라고 한다. PC USB 전원만 쓰면 자주 생기는 브라운 아웃(brown-out, 전원은 살아 있는데 전압이 모자라서 오동작하는 상태)라고 하였다. 실제로 USB 키보드를 꽂았음에도 불구하고 작동을 하지 않는 경우 멀티미터로 실드 쪽의 VBUS를 찍어보면 3V 약간 넘는 전압이 나왔다. 정전은 black-put, 전압이 떨어져서 오동작하는 상태는 brown-out이다.

그렇다면 충분한 전류를 흘릴 수 있는 양질의 5V를 어떻게 공급하면 좋을까?

  1. 7~9V의 DC 어댑터가 있다면, 아두이노 우노의 어댑터 잭을 통해 꽂으면 된다. 내부적으로 선형 레귤레이터를 통해 5V로 낮추어서 제공하므로 발열이나 순간 전류에 취약하다.
  2. 가장 바람직한 것은 최소한 1A를 공급할 수 있는 안정적인 5V(4.8~5.2V)를 소켓 헤더쪽에 공급하는 것이다. Vin 단자는 곤란하다. 왜냐하면 내부적으로 1번과 같기 때문이다. 리니어 레귤레이터는 목표 전압보다 높은 전압을 주어야 작동한다.

여기에 더하여 PC와 아두이노 우노를 연결하는 USB 케이블에서 VBUS를 끊어서 오직 데이터만 전송하게 만드는 것이 바람직하다고 한다. 보통 PC USB와 외부 5V를 동시에 연결해도 문제는 없지만, 안전을 위한다면 외부 5V 단독이 최선이다. 12V 2.5V DC 어댑터와 LM317을 이용한 스텝다운 모듈(아래 사진)이 있으니 양질의 5V를 공급하는 것은 당장 가능하다. 그러나 전원만 차단되는 USB 연장 케이블을 구하기는 아주 어려우니(데이터는 차단하고 충전만 제공하기 위한 제품은 흔히 구할 수 있음) USB 커넥터를 별도로 주문하여 직접 만들기로 하였다.



간단히 끝날줄 알았는데 그게 아니었다.

[업데이트] 전원부를 보강한 뒤 수정한 펌웨어로 테스트한 결과

PC <-> 아두이노 우노 연결은 일반 USB 케이블을 그대로 사용하되 레귤레이터를 통해 12V에서 5V로 낮춘 전원을 헤더쪽에 공급해 보았다. 이번의 펌웨어는 상태값을 기본 상태(base)와 에러 플래그(err)로 분리하여 출력하도록 만들었다. 반복하여 키보드를 연결하였을 때 100% 정확하게 인식되었으나 0x90은 일정한 위치에서 계속 따라다닌다. 맨 끝의 raw=18은 키보드를 분리했을 때의 메시지이다.

Ardule Bridge - USB MIDI Monitor (robust)
USB Host Shield init OK
Plug USB MIDI keyboard and play...
[USB] raw=18 (0x12) base=18 (0x12) err=NO
[USB] raw=32 (0x20) base=32 (0x20) err=NO
[USB] raw=64 (0x40) base=64 (0x40) err=NO
[USB] raw=80 (0x50) base=80 (0x50) err=NO
[USB] raw=81 (0x51) base=81 (0x51) err=NO
[USB] raw=144 (0x90) base=16 (0x10) err=YES
CC       | CH 1 | CC 64 | VAL 127
CC       | CH 1 | CC 1 | VAL 34
CC       | CH 1 | CC 7 | VAL 94
CC       | CH 1 | CC 5 | VAL 12
CC       | CH 1 | CC 2 | VAL 41
CC       | CH 1 | CC 3 | VAL 70
CC       | CH 1 | CC 4 | VAL 37
NOTE ON  | CH 1 | NOTE 60 | VEL 88
NOTE OFF | CH 1 | NOTE 60
NOTE ON  | CH 1 | NOTE 62 | VEL 77
NOTE OFF | CH 1 | NOTE 62
NOTE ON  | CH 1 | NOTE 64 | VEL 85
NOTE OFF | CH 1 | NOTE 64
[USB] raw=18 (0x12) base=18 (0x12) err=NO

raw=144 (0x90) base=16 (0x10) err=YES의 의미에 대한 챗GPT의 해석은 다음과 같다.

이 의미는?

USB Host Library 2.0 계열에서:

  • 0x51 : CONFIGURED / RUNNING

  • 그 직후:

    • 클래스 드라이버가 첫 엔드포인트 트래픽을 시작

    • 첫 IN/OUT 트랜잭션 중

      • NAK

      • 일시적 timeout

      • 또는 class-specific 요청에 대한 지연 응답
        USB 규격상 정상인 현상

하지만 라이브러리는 이걸:

  • “연결 실패”가 아니라

  • ERROR 비트(0x80)를 세트해 둡니다

그리고 중요한 포인트 👇
👉 이 err 비트는 자동으로 클리어되지 않습니다. (래치됨)

그래서:

  • 매번 같은 타이밍에

  • 항상 0x90이 “한 번 보이는 것”입니다.


왜 항상 “같은 위치”에서만 나오나?

이건 오히려 정상적인 결정론적 패턴입니다.

  • 0x51 = 열거 완료

  • 그 다음 프레임에서:

    • 첫 SOF

    • 첫 인터럽트 IN 폴링

    • 첫 클래스 초기화 동작

정확히 같은 지점에서
해당 키보드 + Host Shield + 라이브러리 조합이
“한 번 NAK/지연”을 만들고,
라이브러리는 그걸 ERROR로 기록하는 겁니다. //여기까지

결론은 이러하다. 지금의 0x90은 USB  열거 직후 첫 트래픽에서 발생하는 결정론적, 그리고 무해한 ERROR 플래그라서 정상 동작의 일부이다. 따라서 이러한 로직을 본 코드에 흡수하면 된다. 


2026년 1월 17일 토요일

Nano Ardule의 MIDI 파일 재생 기능 개선

Nano Ardule의 펌웨어 개발이 이제 대부분의 기능 구현을 마쳤고 리팩토링 단계에 접어들었다고 바로 이전 글에서 호기롭게 선언하였지만, 아직 모든 것이 완벽하지는 않다. 가장 불편하다고 느끼는 것은 일반적인 type 0 MIDI file을 재생할 때 시작 부분에서 음이 와르르 쏟아지듯 들리는 것이다. 시작 포인트를 약간 지나서 음이 밀려서 쏟아져 나오면서 음 간격은 정상보다 더 좁게 느낀다. 다음 동영상(FLOURISH.MID)에서 첫번째 것이 코드 수정 전의 상태이다. 녹음이 썩 깨끗하게 되지는 않았지만 귀를 기울여 들어보면 뭔가 자연스럽지 않다는 것을 알 수 있다. 이후 두 번의 녹음은 한결 나아진 모습을 보인다.



이 현상을 'Starting Burst'라 부르기로 하고 원인 분석 및 해결에 나서기로 했다. 비상용 (드럼) 패턴이나 단일 패턴 재생에서는 관찰되지 않는다. 녹음한 사운드를 챗GPT에 업로드하여 분석을 실시하고, 이로부터 해결 방안을 도출하여 코드를 수정하는 사이클을 두 차례 걸쳤다. 문제의 원인은 타이밍 스케쥴링 에 있다고 판단하였다. 구체적인 원인 분석과 개선 전략은 별도의 글에 정리하였다. 완벽하게 문제가 해결되었다고 볼 수는 없으나, 실용적인 면에서는 이것으로 충분하다. 이 펌웨어는 type 0 MIDI 파일 재생을 목표로 하는 것이 아니기 때문이다. 드럼 전용 패턴 또는 ARR 파일의 재생은 아주 매끄럽게 잘 되고 있다.

만약 아두이노용 MIDI 라이브러리를 사용하여 펌웨어 개발을 했다면 이런 문제를 겪지 않았을지도 모른다. 그러나 그만큼 더 많은 메모리 공간이 필요하고, 내가 원하는 기능까지 넣는 것은 불가능하다고 판단하였었다. 그래서 다음과 같은 것이 전부 내가 만드는 펌웨어의 몫이었다.

  • MIDI 파일 파싱(VLQ, running status, meta event)
  • tick -> time 변환
  • 스케쥴러(catch-up, drift, blocking)
  • SD/IO의 블로킹 특성
  • MCU 타이밍(마이크로초/밀리초 해상도)

MIDI와 관련된 라이브러리를 전혀 사용하지 않은 '날것 코딩'을 선택하였기 때문에 MIDI 파일을 일단 재생하게 만드는 것조차 쉽지 않았다. 그러나 더욱 강력한 통제력을 얻을 수 있었다. 문제의 원인을 정확히 진단하고, 실용적으로 문제가 느껴지지 않는 수준까지(만) 개선하는 것. 충분히 합리적인 자세였다고 자체 판단하기로 했다.

몇 시간 지난 후 재평가를 해 보았다. 아직도 약간의 지연이 느껴져서 재생 시작 시 200 msec의 고정 지연 시각을 주었더니 훨씬 나아진 것 같다.

가끔 git push를 하다가 다음과 같은 메시지를 접하고 놀랄 때가 있다.

> git push
To https://github.com/jeong0449/NanoArdule.git
 ! [rejected]        main -> main (fetch first)
error: failed to push some refs to 'https://github.com/jeong0449/NanoArdule.git'
hint: Updates were rejected because the remote contains work that you do not
hint: have locally. This is usually caused by another repository pushing to
hint: the same ref. If you want to integrate the remote changes, use
hint: 'git pull' before pushing again.
hint: See the 'Note about fast-forwards' in 'git push --help' for details.

README.md 같은 것을 GitHub 웹사이트에서 직접 고쳐 놓은 다음 로컬에 이를 반영하지 않은 상태에서 다른 파일을 고친 뒤 push를 하려다가 이런 일을 겪는다. 이때에는 'git pull origin main'(= git pull, 조건이 맞는 경우)을 실행한 뒤 git push 명령어를 날리면 된다. 'git status'에서 내가 지금 고친 파일만 눈에 뜨인다고 안심하고 있을 일이 아니다.



2026년 1월 16일 금요일

리팩토링 단계에 들어간 Nano Ardule 드럼 패턴 플레이어

리팩토링(refactoring)은 소프트웨어 공학의 전문 용어라고 한다. 코드의 외부 동작은 변경하지 않으면서 내부 구조를 개선하고 정리하는 작업이다. 마틴 파울러(Martin Fowler)의 저서 『Refactoring: Improving the Design of Existing Code』를 통해 널리 알려진 개념이라고 한다. 챗GPT와 더불어 코딩 작업을 하면서 이제 리팩토링 단계에 접어들었다는 말을 들었지만, 이렇게 정립된 개념이라는 것은 미처 알지 못하였다.

이미지 출처: 교보문고 


"Refactoring is a disciplined technique for restructuring existing code,
without changing its observable behavior."

이것은 마이크로SD카드가 인식 불능일 때 쓸 수 있도록 코드 안에 심어 놓은 'emergency payload' 드럼 패턴이다.

이 책의 내용을 친절하게 국문으로 정리한 리포지토리가 있어서 소개한다(링크). GitHub는 공동개발 및 코드 배포용으로만 쓴다고 생각을 했는데 문서 보관용으로도 쓸 수 있었다.

패턴 목록이 보이는 화면에서 인코더를 돌려서 6번을 선택하여 진입했다고 가정하자. 재생을 마친 뒤, 상위 메뉴로 복귀했을 때 커서가 여전히 6번에 있어야 하는가, 또는 1번으로 돌아가야 하는가? 현재까지의 동작은 1번으로 돌아가는 것이었다. 그러나 상위에서 하위 메뉴(목록)로 내려갈 때에는 언제나 1번부터 시작하는 것이 자연스럽지만, 그보다 하위 메뉴에서 목록으로 올라갈 때에는 원래의 위치에 있는 것이 낫다고 생각하여 개선을 진행하였다. 이것도 리팩토링인가? 단순한 UI 개선이라고 볼 수도 있으나. 기존 흐름을 더욱 일관되게 만드는 것이라서 전형적인 리팩토링이다.

구분분류
버튼 기능 추가새 롱프레스 동작기능 추가
재생 로직 변경BPM 처리 방식 변경기능 변경
커서 위치 기억상태 복원리팩토링
화면 문구 변경“Ready” → “Ready to Play”UI polish (리팩토링의 일부)

아두이노 나노에서 내가 원하는 드럼 패턴 재생 동작은 다 할 수 있게 된 것 같다. 코드의 가독성을 높이고, 내부에 어지럽게 널려 있는 한글 코멘트를 영문하하는 일이 남았다. 또다른 펌웨어인 MIDI controller는 EEPROM에 설정을 저장하고 불러오는 기능을 아직 더 넣어야 한다.

코딩의 능률은 Git/GitHub를 쓰면서 획기적으로 높아졌다. Git와 GitHub가 기여한 정도는 뚜렷하게 차이가 있다. Git는 버전 관리를 매우 편리하게 만들어 주었고, GitHub는 보다 책임감을 갖고 코드와 문서를 정리하는 동기를 부여해 주었다. 앞으로 이 두 가지의 도구가 없는 개발은 상상하기 어렵다. 문서는 GitHub 공개용과 내부용으로 철저히 나누었다. 공개용은 앞으로도 계속 기록으로 남을 것을 감안하여 매우 신중하게 작성하였고, 단기적인 목표와 개선 경험을 담은 내부용은 위키 사이트에만 두었다. 엄밀히 말하면 비공개는 아니다. 내부용 문서는 챗GPT에 작업 지시문과 더불어 업로드하니 시행착오를 크게 줄일 수 있었다. 

나의 삶을 리팩토링 대상으로 삼을 수 있을까? 삶에서 겉으로 보이는 것은 바뀌지 않지만 내부 구조를 개선한다면 그 이유는 무엇이겠는가? '유지보수'가 쉬워지고, 미래를 위한 대비를 감당할 여지가 생기기 때문이다. 당장은 드러나지 않는 내면의 변화란 비용이 드는 일이지만 그만한 가치가 있다.

다음은 어제 도착한 아두이노 우노용 USB 호스트 쉴드 및 MIDI 쉴드이다. USB MIDI 키보드 컨트롤러를 DIN 5P MIDI 커넥터가 달린 사운드 모듈에 연결하여 사용하기 위함이다. 할 일이 끊이지 않는다. 올해는 LibreCAD로 그린 앰프 상판도 주문하여 6V6 싱글 앰프를 다시 만드는 일도 기다리고 있는데...




2026년 1월 15일 목요일

LilyPond 다시 설치하기

앞으로 악보를 그릴 일이 종종 생길 것 같아서 한동안 잊고 있었던 LilyPond를 다시 설치하였다. 마우스 클릭 몇 번으로 설치되는 그런 친절한 프로그램은 아니다. 윈도우용 zip을 가져다가 C:\Apps에 설치하고, GUI 프로그램인 더불어 필요한 프로그램인 Frescobali(웹사이트GitHub)를 최신 버전인 4.0.4도 아닌 3.3.0를 가져다가 깔았다. 최신 버전에서는 msi 파일을 제공하는데, 설치 후 첫 실행에서 에러가 생겨서 아무것도 할 수 없었기 때문이었다. 3.3.0에는 .exe 파일이 제공되고, 첫 실행을 한 뒤에 Edit -> Preferences로 들어가서 LilyPond 실행파일의 위치를 지정할 수 있었다.



LilyPond가 만들어내는 악보 자체의 품질은 매우 우수하다. 그러나 GUI 방식으로 음표를 찍어서 갖다 놓는 방식이 아니라 텍스트 입력 파일을 만들어 '컴파일'을 해야 한다. 마치 LaTex을 다시 쓰는 것만 같은 그런 느낌. 미려한 악보를 출판하기 위한 목적으로는 적당하지만 밴드 합주용으로 사용할 음악 스케치를 그려서 나누어 주는 용도로 쓰기에는 배울 것이 많다. 가파른 학습곡선 덕분에 당장 빠르게 활용하려면 오선지에 손으로 악보를 그려서 배포하고 싶은 생각도 들지만, LilyPond는 그 나름대로의 '재미'가 있다. 

Girolamo Frescobaldi(1583-1643)은 바로크 시대의 건반음악 작곡자이자 오르가니스트이다. 자료에 의하면 즉흥처럼 보이지만 치밀하게 설계된 음악의 원형을 만들었다고 한다. LilyPond/Frescobaldi는 이 소프트웨어를 연주용 GUI가 아니라 악보를 언어처럼 다루는 도구라는 의미를 전달하기 위해 이렇게 이름을 붙였다고 볼 수 있다. 프레스코발디의 음악 정신을 따르는 것.

어쩌면 내가 Nano Ardule 드럼 패턴 생태계 조성이라는 취미 프로젝트에서 노력을 하는 것도 드럼 연주를 단순한 MIDI 이벤트가 아니라 인간과 기계 모두가 읽고 편집하며 연주할 수 있는 재사용 가능한 언어로 만들고자 함이었다. 

LilyPond/Frescobaldi와 Nano Ardule! 인간과 악기(또는 기계)를 연결하는 정보 체계로서 자유로운 발상을 제한하지 않으면서 정확한 전달을 위한 규약이 되기도 한다. 조만간 이 연결성을 가지고서 에세이를 또 한 편 써야 되겠다. 


2026년 1월 14일 수요일

Ardule 드럼 패턴학 생태계 - 절대시간('MetaTime')이 지배하다

Achieving MetaTime
Achieving MetaTime

2026년 1월 13일에서 14일로 넘어가는 자정 즈음에 Ardule 드럼 패턴학 생태계에서는 하나의 이정표가 세워졌다. 시간에 대한 가설이 파일 포맷과 코드, 그리고 하드웨어를 통해 실제 소리로 검증된 순간이기 때문이다. ADS(Ardule Data Stream, 원래는 Ardule Drum Stream이었음)가 Nano Ardule 드럼 패턴 플레이어에서 처음으로 정상적으로 재생되었다. 이는 단순히 계획한 것이 잘 구현되었다는 사실보다 “시간을 어떻게 다룰 것인가”라는 문제에 대한 설계가 옳았음을 증명한 사건이었다.

MetaTime(논리적 절대시간) 원칙이란 MIDI 파일처럼 이벤트 사이의 델타타임(상대적 시간)이 아니라, 곡의 시작점을 기준으로 한 절대적 시간 시점에 모든 이벤트를 직접 매핑하여 처리하는 원칙이다. 간격이 아니라 좌표이다. 이 원칙은 중간 템포 변경에 불리할 수는 있으나, 이를 고려하여 ADS로 컴파일해 두면 모든 시간 해석은 오프라인에서 종료된다. 따라서 재생 단계에서는 어떠한 문제도 발생하지 않는다.

이 프로젝트는 처음의 단순한 드럼 패턴 플레이어로 시작하여 점점 진화하였다. 왜 패턴은 2마디여야 하는가, 템포는 어느 파일이 결정하나, 왜 재생 엔진은 계산을 하면서 동시에 해석자 역할을 해야 하나. 이러한 질문은 ADT, ARR, ADS라는 파일 계층으로 점차 분화했고, 오늘의 개발 과정을 통해 ADS라는 실행 전용 포맷이 제 역할을 충실하게 수행했음을 의미한다.

이 모든 수정은 단순한 예외 처리가 아니라, 미리 설계한 스펙의 의도를 코드에 정확히 반영하는 작업이었다. 오늘의 결과에 특별히 보람을 느끼는 이유는, 이 모든 과정이 챗GPT의 표현을 빌리자면 “최소 침습 패치”라는 원칙 아래 이루어졌다는 점이다. 기존 Type 0 MIDI 재생 엔진을 망가뜨리지 않고, ADS라는 새로운 재생 경로를 병렬로 추가했다. SONG 모드는 이제 .MID와 .ADS를 동시에 인식하며, 파일 확장자에 따라 서로 다른 재생 엔진으로 분기된다. UI는 엔코더와 버튼이라는 제한된 입력 수단과 1602 LCD 디스플레이(그렇다. 16문자 x 2줄에 불과한!) 안에서 이를 자연스럽게 드러낸다. 내부적인 시스템은 당연히 많은 기능을 품느라 복잡해졌지만, 사용법은 매우 단순하다.

이제 Ardule은 패턴 편집기와 플레이어의 집합이 아니다. ADT는 편집을 위한 텍스트, ARR은 곡 구조를 위한 조립 지시서, ADS는 실행을 위한 절대 시간 스트림이다. 각 포맷은 책임이 분리되어 있고, authority가 명확하다. 엔진은 해석자가 아니라 실행자이며, 시간은 계산이 아니라 참조의 대상이다.

오늘의 기록은 아마 나중에 이렇게 읽힐 것이다. “여기서 Ardule은 하나의 시스템이 되었다.” 소리는 그 증거일 뿐이다. 진짜 성취는, 시간과 책임과 구조가 올바른 자리에 놓였다는 사실이다.

2026년 1월 13일 화요일

대장균의 변화하는 역할

The changing roles of Escherichia coli

이것은 2025년 12월 19일 Nature Microbiology에 실린 리처드 렌스키(Richard E. Lenski) 교수의 글 제목이다('microbe matters', 링크 - 기관에서 구독하는 상태가 아니라면 안타깝게도 유료!). 반가운 마음에 전문을 읽어 보았다. 국내 학회에서 강연을 하기 위해 초청한 렌스키 교수를 인천국제공항에 마중을 나가서 만나서 대전까지 동행했던 기억이 난다. 2004~5년 무렵이 아니었나 싶은데, 무주리조트 티롤호텔에서 열렸던 한국미생물·생명공학회 30주년 기념 학술대회였던가? 당시 미생물과 관련한 굵직한 학술 행사가 국내에서 여럿 열렸었다. 웹브라우저의 번역 기능 덕분에 영문을 빨리 읽기가 좋다. 

"미생물학자들은 제가 연구 대상은 맞지만 질문이 잘못되었다고 생각할 것이고, 진화생물학자들은 제가 질문은 맞지만 연구 체계가 잘못되었다고 생각할 것이라 하였습니다" 정말 뼈 있는 농담이다. 엉뚱해 보이는 두 시스템의 접점에서 중대한 발견이 이루어지기도 한다.

정말 게으르다면, 원문을 챗GPT에 밀어넣고 다섯 줄 요약을 부탁해도 된다. 다음은 핵심 메시지 5줄 요약이다.

  1. Escherichia coli는 분자생물학의 핵심 모델이었을 뿐 아니라, 진화가 실제로 작동하는 과정을 실험적으로 검증할 수 있게 한 결정적 생물이다.
  2. 루리아–델브뤽 실험을 통해 돌연변이는 필요에 의해 생기는 것이 아니라 무작위로 발생하며, 박테리아도 철저히 다윈적으로 진화함이 입증되었다.
  3. E. coli 집단에서는 유리한 돌연변이가 선택적 스윕을 통해 집단을 대체하는 적응의 동역학이 관찰된다.
  4. 렌스키의 장기 진화 실험(LTEE)은 동일한 조건에서 진화의 반복성과 우연성의 긴장 관계를 수만 세대에 걸쳐 보여주었다.
  5. 과학 역시 진화처럼 변화하며, E. coli현대 과학이 진화 그 자체를 이해하는 데 여전히 살아 있는 모델임을 증명한다.

1988년에 시작한 대장균의 장기진화실험이 4만 세대에 이르렀을 때, 렌스키 교수는 한국의 21C 프론티어 미생물유전체활용기술사업단과 인연이 닿아서 우리가 보유한 NGS 기술을 이용하여 유전체에는 어떤 변이가 축적되었는지, 적응도(fitness)의 변화 양상과는 어떻게 다른지를 설명하는 논문을 2009년에 Nature에 발표하게 되었다. 한국과 미국, 그리고 프랑스의 연구진이 함께 힘을 모았던 명실상부한 국제적인 프로젝트였다. 훌륭한 연구진과 함께 일했던 사람으로서 나도 저자의 일원이 되는 영광을 누릴 수 있었다. 당시 내 블로그에는 짤막하게 Nature에 논문이 나갔다!라는 글을 남겨 두었다. 원글의 논문 링크는 대부분 in press 상태였기 때문에 지금은 유효하지 않으며, PubMed나 저널 웹사이트에서 제대로 찾아야 한다. 우연이었을까? 당시 이 Nature 논문의 제1저자였던 유동수 박사(농업유전자원센터)를 랜스키 교수의 글을 접하기 하루 전인 어제 우연히 만났다(KOBIC 방문 소식 링크).

컴퓨터를 뒤지니 대장균의 비교유전체학적 분석을 하던 당시에 '발'로 그린 그림 원본도 나온다. MUMmer로 Whole genome alignment를 만든 뒤 Perl로 파싱하여 Xfig용 데이터 파일을 만들고, 이를 다시 포스트스크립트로 전환하였었다. 이 그림이 실제로 출판된 논문 혹은 단행본 챕터에 쓰였는지 혹은 초안 수준으로 끝났었는지는 잘 기억이 나지 않는다.

누구나 AI를 이용하여 고도화된 업무를 수행하는 지금 기준으로 생각하면 정말 원시적인(?) 방법으로 그린 그림이었다.

Lenski의 글에서 그림 하나를 가져왔다. '단일 클론'에서 시작한 집단에서 돌연변이가 발생하여 전체로 퍼지고, 또 새로운 변이가 생겨서 집단 전체를 휩쓰는(sweep, '싹쓸이') 모습을 시각적으로 잘 표현하였다. 이것이 바로 selective sweep 아니겠는가. 이 그림을 대장균 집단이 아니라 인류사회 전체로확대한다면 인공지능 기술은 어쩌면 그림의 중간쯤에 위치한 주황색 물결일지도 모른다. 연구와 코딩, 모든 방법이 새로운 신기술에 따라 변한다. 공부와 구글 검색을 통해 힘겹게 스크립트를 짜던 것이 엊그제 같은데, 이제는 챗GPT에 지시만 하면 아주 그럴싸한 스크립트를 제공해 주니 말이다.

비슷한 느낌을 주는 또 다른 실험 그림이 있다. 항생제 농도를 단계적으로 높여서 축구장으로 치자면 하프라인에 가장 농도를 높게 한 거대한 agar plate를 만든 뒤, 골대쯤 해당되는 양 끝에서 미생물을 접종하여 내성 변이체가 자라나고 변화하는 모습을 모니터링하는 것이다. 이것도 벌써 10년 전에 화제가 되었던 Kishony의 연구 결과였다. 공교롭게도 2020년에 내가 쓴 글 기원이 같은 미생물의 유전체 염기서열은 얼마나 차이가 날까?에서 렌스키와 키쇼니의 연구를 짤막하게 다루었었다.

출처: Visualizing the evolution of bacterial resistance

미생물은 진화를 관찰하기에 아주 적합한 대상은 아니다. 눈에 보이는, 다시 말해서 관찰 가능한 현란한 특성('거시적')을 보이지는 않기 때문이다. 그러나 실험 방법이 발달하고 유전체 및 오믹스 분석이라는 신기술에 힘입어 진화의 모델로서 당당히 등장할 수 있었다.

진화는 필요에 의해서 일어나는 일이 아니다. 요즘의 신기술 또한 필요에 의해서 만들어지는 것 같지는 않다. 기술 발전의 가장 강력한 원동력은 호기심이 아닐까? 간혹 '글로벌 난제 해결'로 포장되기도 하지만. 어쩌면 끊임없이 투자처와 자기 확장 및 이윤을 추구하는 자본이 기술 발전의 가장 큰 원동력인지도 모른다.

아, 800쪽이 넘는 토마 피케티의 『21세기 자본』을 빌려다 놓았는데 과연 일주일 내에 읽을 수 있을런지...

인간이 세상을 이해하려는 수많은 방법 중에서 과학은 진화화 가장 유사합니다. 과학은 무지의 공백을 메우기 위해 변화하고 확장하며 다양화됩니다. 물론 그 과정에서 시행착오도 겪지만, 자연에 대한 더 완전한 이해를 향해 끝없이 노력합니다. 대장균은 과학처럼 끊임없이 진화하기 때문에 우리가 세상을 이해하는데 있어 중요한 역할을 합니다.

Among the many ways we humans try to understand the world, science is the most like evolution. Science changes, expands and diversifies to fill the voids of ignorance, with missteps along the way, but striving for a fuller understanding of nature. E. coli  is a star in our understanding because, like science, it keeps on evolving.

렌스키의 글 마지막 단락을 소개하는 것으로 이 글을 마무리한다.