開発中正式リリース前です。機能とデータは予告なく変わることがあります。

ピンマップ契約 — コードを 1 行も直さずそのまま動きます

プロジェクトが所有する不変のピン契約、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 デバイスはアドレスまで契約に含まれます。パーサーは名前の根拠となったコード行も併せて記録するので、どのピンがなぜそう決まったのかを追跡できます。

契約が保証するもの

「書いた Arduino コードは、1 行も直さずそのまま動きます。」同じ MCU 内でレベルを移すとき(Uno → Uno Shield)、この文は文字どおり真です。物理ピン番号は 1 つも変わりません。

MCU が変わったら — pins.h

L3 で ATmega328P の代わりに ESP32-WROOM や RP2040 を選ぶと、物理ピン番号は変わらざるを得ません。このときのルールは 4 つです。

  1. 論理名は維持します。TRIG_PIN はそのまま TRIG_PIN です。
  2. 変更された物理ピンに合わせた pins.h ヘッダーを自動生成して提供します。
  3. ユーザーコードで直すのが #include "pins.h" の 1 行だけで済むようにします。
  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 が失敗すると変換は進行しません。ピン 1 つを意図的にずらしたケースで必ず不一致を検出することが、この段階の成功基準でした。

契約にできないこと

ピン契約はコードとボードが同じピンを指していることを保証します。実際のセンサーがそのタイミングで応答するか、電源が持ちこたえるか、ノイズがないかは保証しません。それは電気検証と実機検証の領域です。