개발 중정식 출시 전입니다. 기능과 데이터가 예고 없이 바뀔 수 있습니다.

핀맵 계약 — 코드 한 줄도 안 고치고 그대로 돌아갑니다

프로젝트가 소유하는 불변의 핀 계약, MCU 가 바뀔 때 자동 생성되는 pins.h, 그리고 그 약속을 CI 테스트로 만드는 P8 회귀.

6분 읽기업데이트 2026년 9월 8일

사용자가 브레드보드 위에서 검증에 쏟은 노력이 PCB 로 넘어가며 버려지지 않는다는 보장 — 그것이 핀맵 계약(Pin Contract)입니다. 프로젝트가 소유하는 불변 계약이고, 레벨이 바뀌어도 유지됩니다.

계약의 형태

PinContract {
  "TRIG_PIN"  → { logical: D9,    direction: out, type: digital, net: "US_TRIG" }
  "ECHO_PIN"  → { logical: D8,    direction: in,  type: digital, net: "US_ECHO" }
  "TEMP_BUS"  → { logical: D4,    direction: bi,  type: onewire, net: "OW_BUS" }
  "OLED"      → { logical: A4/A5, type: i2c, addr: 0x3C }
  "RELAY_1"   → { logical: D7,    direction: out, type: digital, net: "RLY1" }
}

계약의 키는 코드에 있던 논리 이름입니다. #define TRIG_PIN 9 나 const int LED = 13 에서 읽어낸 이름이고, 값은 물리 핀 · 방향 · 신호 종류 · 넷 이름입니다. I²C 장치는 주소까지 계약에 들어갑니다. 파서는 이름의 근거가 된 코드 줄을 함께 기록하므로, 어떤 핀이 왜 그렇게 정해졌는지 추적할 수 있습니다.

계약이 보증하는 것

"작성하신 아두이노 코드, 한 줄도 안 고치고 그대로 돌아갑니다." 같은 MCU 안에서 레벨을 옮길 때(Uno → Uno Shield) 이 문장은 문자 그대로 참입니다. 물리 핀 번호가 하나도 바뀌지 않습니다.

MCU 가 바뀌면 — pins.h

L3 에서 ATmega328P 대신 ESP32-WROOM 이나 RP2040 을 고르면 물리 핀 번호는 바뀔 수밖에 없습니다. 이때 규칙은 넷입니다.

  1. 논리 이름은 유지합니다. TRIG_PIN 은 그대로 TRIG_PIN 입니다.
  2. 변경된 물리 핀에 맞춘 pins.h 헤더를 자동 생성해 제공합니다.
  3. 사용자 코드에서 고칠 것은 #include "pins.h" 한 줄뿐이 되도록 만듭니다.
  4. 변경 사항을 명시적 diff 로 보여줍니다. 조용히 바꾸지 않습니다.
// pins.h — generated by ibouPCB for target esp32-wroom-32
// Logical names are preserved; only physical numbers changed.
#pragma once
#define TRIG_PIN   25   // was D9  (Uno)
#define ECHO_PIN   26   // was D8  (Uno)  ← 5 V → 3.3 V divider inserted (R7/R8)
#define TEMP_BUS   4    // was D4  (Uno)
#define RELAY_1    27   // was D7  (Uno)

핀을 고를 때 검증기가 MCU 의 능력을 대조합니다. analogWrite 를 쓰는 핀은 PWM 이 되는 핀으로, attachInterrupt 를 쓰는 핀은 외부 인터럽트 핀으로만 배정됩니다. ESP32 의 부트 스트랩 핀(IO0 / 2 / 12 / 15)과 입력 전용 핀(IO34–39)은 출력 용도로 배정되지 않고, 배정이 불가피하면 경고가 붙습니다. 이것이 P5 핀 계약 검증입니다.

약속을 테스트로 만드는 P8 회귀

"코드가 그대로 돈다" 를 마케팅 문구가 아니라 CI 테스트로 만드는 장치가 P8 입니다.

변환 전 (소스 보드)  시뮬 실행 → 핀 파형 · I²C 트랜잭션 · UART 출력 기록
변환 후 (양산 타겟)  같은 스케치 + pins.h 로 재컴파일 → 시뮬 실행 → 동일 항목 기록
                       ↓
                 서버가 핀 계약으로 대조
                       ↓
          불일치 → 변환 거부 + 원인 리포트
  • 서버는 스케치의 #define / const int 핀 상수를 pins.h 의 값으로 재작성해(apply_pin_map) 양산 타겟용으로 다시 컴파일합니다.
  • 브라우저가 소스 빌드(avr8js)와 타겟 빌드(avr8js / rp2040js, ESP32 는 서버 QEMU)를 각각 5초씩 시뮬레이션합니다.
  • 서버가 비교합니다: 각 논리 핀의 방향이 일치하는가, 소스에서 토글한 핀은 타겟에서도 토글되는가. PWM 핀은 방향만 비교하고 경고를 남깁니다.
  • Uno → Pico 실측: 22초, 6핀 비교. 코어 차이도 잡힙니다 — 센서가 없을 때 analogWrite(-508) 을 AVR 코어는 PWM 으로, arduino-pico 는 0 으로 클램프하는 차이가 경고로 보고되었습니다.

P8 이 실패하면 변환은 진행되지 않습니다. 핀 하나를 의도적으로 어긋나게 한 케이스에서 반드시 불일치를 검출하는 것이 이 단계의 성공 기준이었습니다.

계약이 못 하는 것

핀 계약은 코드와 보드가 같은 핀을 가리킨다는 것을 보증합니다. 실제 센서가 그 타이밍에 응답하는지, 전원이 버티는지, 노이즈가 없는지는 보증하지 않습니다. 그것은 전기 검증과 실물 검증의 영역입니다.