44
47

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【シリアル通信】「実機がないからテストできない」は言い訳?SocketDebuggerで巨大な産業機器を丸ごと模擬してみた🔥

44
Posted at

1. はじめに

前回の記事「新入社員でも3分でわかる!シリアル通信の基礎(UART・USB・RS-232C)」では、シリアル通信の概念や、TCP/UDPとの違い、通信方式や使用するケーブルなどについて学びました。

今回はその続きとして、より「現場」に近いシチュエーションでの実践的なデバッグ手法をご紹介します。

エンジニアを襲う「実機がない」という絶望

想像してみてください。あなたは今、新しいマイコン(今回は Adafruit Feather 32u4 としましょう)の設計を任されています。

<Adafruit Feather 32u4>
image.png

このマイコンの任務は、工場にある「巨大な産業機器」とシリアル通信で対話し、その動作を制御すること。仕様書も読み込み、コードも完璧に書き上げました。

いざ通信試験!……と思いきや、ここで大きな壁にぶつかります。

  • 相手の機器がデカすぎる: 自分のデスクや会議室には持ち込めない
  • 相手の機器がまだない: 相手方も開発中。完成は3ヶ月後
  • でも納期は迫っている: 「実機がないのでテストできませんでした」は通用しない

「仕様書通りに動くはず」という確信が持てないまま、ぶっつけ本番で実機に繋ぐのは、エンジニアにとって胃が痛くなるようなストレスですよね。

「見る」だけでは足りない

「とりあえず自分の書いたコードからデータが出ているか確認しよう」と思い、Arduino IDEのシリアルモニタを開くかもしれません。

確かに、マイコンから送信されたデータを目で追うことはできます。しかし、産業機器との通信には「ルール(プロトコル)」があります。

  • 信号を送ったら、50ミリ秒以内に返事(ACK)が来ること
  • 返事には、さっき送ったデータに特定の値を加算して返すこと

……これを人間がシリアルモニタ越しに手入力で再現するのは、物理的に不可能です。

PCを「仮想の相棒」に仕立てる

そこで登場するのが、シリアル通信デバッグツール SocketDebugger です。 これを使えば、手元のPCをあたかも「本物の産業機器」であるかのように振る舞わせることができます。

今回は、手元のマイコンを使い、PC上の SocketDebugger を「まだ見ぬ巨大な産業機器」に見立てて、机の上だけで通信試験を完結させる方法を解説します。

<全体構成図>
image.png

2. シリアルモニタの限界:人間は「機械」にはなれない

「通信の確認なら、Arduino IDEのシリアルモニタで十分じゃないの?」 そう思う方もいるかもしれません。確かに、自分で送ったデータが文字として表示されるのを見るだけなら、シリアルモニタは手軽で便利です。

しかし、今回のミッションは「産業機器との対話」です。ここには、人間が手作業で介入できない「3つの壁」が立ちはだかります。

① 速度の壁(レスポンスタイム)

今回の仕様書には、こう書かれているとします。

「マイコンからの要求に対し、産業機器は50ミリ秒以内にレスポンスを返送すること。一定時間を超えた場合、マイコン側は通信タイムアウトと判断し、エラーとする。」

マイコンは、律儀に200ミリ秒間隔で信号を送ってきます。対するあなたは、シリアルモニタの送信欄に返信用のデータを打ち込み、マウスで「送信」ボタンを押さなければなりません。

……どう頑張っても間に合いませんよね? 人間がキーボードを叩いている間に、マイコンは「相手が反応しない!異常事態だ!」と判断してしまいます。

② ロジックの壁(動的な返信内容)

産業機器のプロトコルは、単なる固定の挨拶ではありません。

「受信したデータに含まれる『シーケンス番号』を読み取り、その値に +1 して返送せよ。」

マイコンから 0x01 が来たら 0x02 を、 0x05 が来たら 0x06 を返す……。 これをリアルタイムで読み取り、暗算し、16進数で打ち込んで送信する。これを2秒おきに延々と繰り返すのは、もはや苦行です。人間が介在した瞬間に、それは「通信試験」ではなく「動体視力とタイピングの訓練」になってしまいます。

③ 視認性の壁(バイナリと非表示文字)

シリアルモニタは基本的に「テキスト(文字)」を表示するためのツールです。 しかし、産業機器との通信では、データの先頭に STX (0x02)、末尾には ETX (0x03) や チェックサム といった、目に見えない制御文字が並ぶことは一般的です。

シリアルモニタでこれらを受信しても、画面上では空白になったり、文字化けしたりして、正しく届いているのか判断がつきません。 「データは来ているっぽいけど、中身が正しいかわからない」という状態では、デバッグは一歩も前に進みません。

3. ミッション発令:提示された「通信仕様書」を読み解く

上司から「相手方の仕様書が届いたぞ。これに合わせてマイコン側を実装してくれ」とPDFを渡された……という、エンジニアなら誰もが経験するシチュエーションからスタートしましょう。

渡された仕様書には、以下のような内容が記載されていました。

3.1 共通の電文フォーマット(バイナリ形式)

通信は以下の5バイト固定フォーマットで行うこと、と指定されています。

バイト位置 項目 備考
[0] ヘッダ 0x02 STX(Start of Text)
[1] コマンド 0x10 データ転送コマンド(固定)
[2] シーケンス番号 0x00〜0xFF やり取りの度にインクリメントされる値。
[3] ダミー 0x00 将来の拡張用
[4] フッタ 0x03 ETX(End of Text)

3.2 産業機器側(相手機)の振る舞い

PDFには、相手となる産業機器の仕様がこう記されています

  • 動作モード: スレーブ(マイコンからの要求に対して応答する)
  • 応答条件: 受信パケットの各バイトをチェックし、正常であれば受信から 50ms 以内にレスポンスを返送する
  • 応答ロジック: 受信したパケットの「シーケンス番号」を +1 して送り返す
    • 例:02 10 05 00 03 受信 → 02 10 06 00 03 返信

3.3 マイコン側(自分)に課せられた要件

これに対し、私たちが作るマイコンプログラムには以下の要件が求められています。

  • ポーリング間隔: 200ms間隔で産業機器の状態を確認すること
  • タイムアウト判定: 送信後、100ms以内に正しい返信がない場合は「通信異常」とみなすこと
  • 成功時の挙動: 受信した「シーケンス番号+1」の値を、次回の送信時に使用すること
  • 可視化: 通信成功時には、基板上のLEDを点滅させて動作状況を示すこと

3.4 物理層(レイヤ1)の通信条件

マイコンのシリアルポート(UART)を、以下の設定で初期化する必要があります。

項目 設定値 備考
ボーレート 9600 bps 産業機器では今も現役の安定速度 
データビット 8 bit 標準的 
パリティ None なし 
ストップビット 1 Bit 標準的 
フロー制御 None ハードウェア・ソフトウェア共に無し 

3.5 タイムライン(時間軸)の整理

電文の内容と物理設定が固まったら、最後に「時間の流れ」をイメージしておきます。ここを読み飛ばすと、マイコンが早口すぎて相手が聞き取れなかったり、逆に待ちすぎて効率が悪くなったりします。

  • マイコンの送信周期: 200ms(1秒間に5回パケットを投げる)
  • 産業機器の応答: 受信完了から 50ms 以内
  • マイコンの受信タイムアウト: 送信完了から 100ms

<通信仕様のシーケンス>

これで、「電文フォーマット」「ロジック」「物理設定」「時間」 という、実装に必要な4つのピースがすべて揃いました。

上司からの指示: 「よし、仕様は把握できたな。まずはマイコンで、このルール通りに動く『攻め(要求>側)』のコードを書いてみてくれ。LEDの点滅で通信成功がわかるようにするのを忘れるなよ!」

エンジニアの視点:なぜ「仕様を整理」するのか?
顧客から渡された仕様書をそのままコードに書き始めるのは危険です。

まずはこのように、「相手(産業機器)がどう動くのか」と「自分(マイコン)はどう振る舞うべきか」を整理した「メモ」を作ることが、実装ミスを防ぐための第一歩になります。

「相手が +1 して返すと言っている。だったら自分は +1 された値が届くのを待てばいいんだな」というゴールを明確にするのが、このフェーズの目的です。

4. マイコン側の実装:仕様を「動くコード」へ落とし込む

ここからは、手元のマイコンを「仕様書通りに振る舞うデバイス」へと仕立てていきます。

プログラミングのゴールは、ただデータを送ることではありません。「相手(産業機器)の状態を正しく受け取り、次のアクションへ繋げる」という、双方向のロジックを完成させることです。

4.1 マイコンのセットアップと初期設定

まずは、プログラムを書く準備を整えます。今回は Adafruit Feather 32u4 を使用します。このマイコンは、一般的な Arduino IDE を使ってプログラムを書き込むことができるため、特別な開発環境をゼロから構築する必要はありません。

  1. ボードの選択: Arduino IDEの ツール > ボード から Adafruit Feather 32u4 を選択します(※事前にボードマネージャでAdafruit AVR Boardsの追加が必要です)

  2. ポートの選択: PCと接続し、認識されたCOMポートを選択します

<Arduino IDEでボードとポート選択後>

準備ができたら、まずは「物理層」の仕様をコードに反映させます。

C++
void setup() {
  // 顧客仕様書に基づき、ボーレートを 9600 に設定
  Serial.begin(9600); 

  // Adafruit Feather 32u4特有の処理:USBシリアルが認識されるまで待機
  while (!Serial); 
  
  // 成功を可視化するためのLED(13番ピン)を出力設定に
  pinMode(LED_BUILTIN, OUTPUT);
}

Adafruit Feather 32u4を使う際のポイント💡
Adafruit Feather 32u4のようなUSB内蔵マイコンは、起動直後にUSBの準備が、整わないことがあります。while (!Serial); を入れないと、プログラムが先に走り出してしまい「最初の送信データがPCに届かない」というトラブルに繋がるため、おまじないとして入れておきましょう。

4.2 メインロジックの実装:投げたボールを受け取るまで

仕様通り、「200ms間隔で送信し、100ms以内に正しい返信が来ればLEDを光らせる」というロジックを loop() 関数の中に組み立てます。

C++
byte sequenceNum = 0;  // シーケンス番号(0〜255)

void loop() {
// --- 周期計測のスタート地点 ---
  unsigned long cycleStartTime = millis();

  // --- 1. 送信フェーズ:5バイトのパケットを組み立てて送る ---
  // [STX(02)] [CMD(10)] [SEQ] [DUMMY(00)] [ETX(03)]
  byte txPacket[] = {0x02, 0x10, sequenceNum, 0x00, 0x03};
  Serial.write(txPacket, sizeof(txPacket)); // write関数でバイナリとして送信

  // --- 2. 受信待ちフェーズ:100msのカウントダウン ---
  bool success = false;
  byte rxPacket[5];
  int index = 0;

  // 100msが経過するか、5バイト受信するまでループ
  while (millis() - cycleStartTime < 100) {
    if (Serial.available() > 0) {
      rxPacket[index++] = Serial.read();
      if (index >= 5) {
        // 全バイト受信完了。シーケンス番号が「送信値+1」かチェック
        if (rxPacket[0] == 0x02 && rxPacket[4] == 0x03 && rxPacket[2] == (byte)(sequenceNum + 1)) {
          success = true;
        }
        break;
      }
    }
  }

  // --- 3. 判定フェーズ:通信結果をフィードバック ---
  if (success) {
    // 【成功】LEDをチカッと光らせ、次のシーケンス番号をセット
    digitalWrite(LED_BUILTIN, HIGH);
    sequenceNum = rxPacket[2]; // 相手から届いた「+1」された値を次に使う
    delay(20);                 // LED点滅が見えるように少し待つ
    digitalWrite(LED_BUILTIN, LOW);
  } else {
    // 【失敗】受信バッファを掃除して、同じ番号でリトライする準備
    while(Serial.available() > 0) Serial.read(); 
    // ※sequenceNumを更新しないので、次回も同じ番号で送信(再送処理)
  }

  // --- 4. 調整フェーズ:全体の周期を約200msに調整 ---
  unsigned long elapsedTime = millis() - cycleStartTime;
  if (elapsedTime < 200) {
    delay(200 - elapsedTime); // 200msに満たない「差分」だけを待つ
  }
}

エンジニアがこのコードに込めた「意図」💡
Serial.write の採用: Serial.print は数値を「文字列」として送ってしまいますが、産業機器との通信は「生データ」を扱うため、write を使って 1 バイトずつ確実に送るのがバイナリ通信の定石です。

タイムアウト(millis())の管理: delay() で待ってしまうと、その間マイコンは何もできなくなります。millis() を使って経過時間を監視することで、「データが来たら即座に反応し、来なければ100msで切り上げる」というキビキビとした動作を実現しています。

成否の可視化: 「通信できたかな?」と不安になりながらログを見続けるのは疲れます。「LEDがチカチカ光っていれば正常」という物理的なフィードバックを設けることで、デバッグの効率が劇的に上がります。

4.3 コンパイルと書き込み:コードを「魂」として吹き込む

コードが書けたら、いよいよマイコンへ書き込みます。

  • 検証(コンパイル): Arduino IDE左上の「チェックマーク(検証)」ボタンを押します。ここで「コンパイル完了」と出れば、文法上の間違いはありません
  • 書き込み: その右隣の「右矢印(書き込み)」ボタンを押します

Adafruit Feather 32u4特有の「書き込み完了」のサイン💡
書き込み中、IDEの下部にある黒いコンソール画面を注視してください。

Connecting to programmer: .
Found programmer: Id = "CATERIN"; type = S
|| Software Version = 1.0; No Hardware Version given.
Programmer supports auto addr increment.
Programmer supports buffered memory access with buffersize=128 bytes.

Programmer supports the following devices:
|| Device code: 0x44

このように Found programmer: Id = "CATERIN"Device code: 0x44 というメッセージが出れば成功です。

もしエラーが出る場合は、USBケーブルがデータ通信対応のものか、ポートの選択(COMポート)が間違っていないかを確認しましょう。Adafruit Feather 32u4は、書き込みの瞬間にポート番号が一時的に変わることがあるため、うまくいかない時は「書き込みボタンを押した直後にポートを選び直す」のがコツです。

4.4 書き込み直後の「孤独な」動作確認

書き込みが完了すると、マイコンは即座にプログラムを実行し始めます。ここで一度、Arduino IDEの「シリアルモニタ」を開いてみましょう。

すると、そこには200msおきに謎の文字(バイナリデータなので文字化けして見えます)が、止まることなく出力され続けているはずです。

<シリアルモニタの文字化け>
image.png

  • データが出続けている: マイコンが「誰か返事をしてくれ!」と200msおきにパケットを投げている証拠

  • LEDが光っていない: 相手(産業機器)がまだいないので、返信が来ず、成功判定が行われていない証拠

この「虚空に向かって問いかけ続けている」状態こそが、これからSocketDebuggerを接続する直前の正しい姿です。シリアルモニタを閉じて、いよいよ「仮想の相棒」を立ち上げましょう。

5. 仮想・産業機器の召喚:SocketDebuggerに「知能」を吹き込む

マイコン側の実装が完了したら、次は受け手となるPC側の準備です。

汎用的な通信ソフト(シリアルモニタ)は「届いた文字を表示する」だけですが、SocketDebugger はそこに Luaスクリプト という「知能」を加えることで、特定の動きをする「専用機」へと変貌させることができます。

ここからは、顧客仕様書にある「50ms以内に+1して返す」という振る舞いを、SocketDebugger上に再現していきます。

5.1 SocketDebuggerの通信設定:バイナリ通信を確実に捉える

まずはSocketDebuggerを起動し、マイコンと通信するための設定を行います。ここで最も重要なのは、「1パケットがどこからどこまでか」をSocketDebuggerに教えてあげることです。

① 通信ポートとパラメータの設定
「ポート1」の接続設定を開き、顧客仕様書通りの値を入力します。

項目 設定内容
通信タイプ COM (シリアルポート)
通信ポート マイコンが接続されているポート番号(例:COM8)
ボーレート 9600
データビット 8
パリティ none
ストップビット 1 Bit
フロー制御 default

② 「固定サイズ区切り」の指定(バイナリ通信の要)
今回の仕様は「5バイト固定」のパケットです。SocketDebuggerには、指定したバイト数が届いた瞬間に「1行」として処理してくれる便利な機能があります。

項目 設定内容
データの区切り 固定サイズ
データサイズ 5

なぜ「固定サイズ」にするのか?
もし「0x03(ETX)」などの文字区切りにしてしまうと、シーケンス番号の中にたまたま 0x03 という数値が現れた際、そこでデータがぶった切られてしまいます(バイナリ透過性の問題)。「5バイト届いたら1パケット」と物理的な数で区切るのが、バイナリ通信デバッグの定石です。

③ スクリプト制御の有効化
最後に、これから書く「産業機器の脳(ロジック)」を動かすためのスイッチを入れます。「ポート1」の動作設定を開き、「スクリプト制御を行う」にチェックを入れます。

<SocketDebuggerの設定>
image.png

5.2 Luaスクリプトで産業機器の振る舞いを再現する

SocketDebuggerで「スクリプト1」のエディタを開き、以下のコードを記述します。これが、まだ見ぬ「産業機器」の脳内ロジックとなります。

Luaスクリプト
---------------------------------------------
-- 受信通知
---------------------------------------------
function OnReceive(recv)
    Logput(1,'OnReceive')

    -- 仕様の確認:[STX(02)] [CMD(10)] [SEQ] [DUMMY(00)] [ETX(03)]
    -- 1バイト目(recv[1])がSTX、5バイト目(recv[5])がETXかチェック
    if recv[1] == 0x02 and recv[5] == 0x03 then
        
        -- 1. 受信したシーケンス番号(3バイト目)を抽出
        local seq = recv[3]
        
        -- 2. 仕様:シーケンス番号を +1 して返送する
        -- (255の次は0に戻るよう %256 で計算)
        local next_seq = (seq + 1) % 256
        
        -- 3. 返送用データの組み立て
        local send_data = { 0x02, 0x10, next_seq, 0x00, 0x03 }
        
        -- 4. データを送信
        SendData(send_data)
    end

    return 0
end

このスクリプトが解決すること💡
シリアルモニタを使っていたときは、私たちが目で見て、暗算して、手で打ち込んでいました。

このスクリプトを走らせることで、PCは人間には不可能な速度で受信パケットを解析し、正確に「+1」した値を生成してマイコンに投げ返してくれます。

これで、マイコン側が「200ms周期」という高速なペースでボールを投げてきても、PC側は一歩も引かずにキャッチボールを続けることができるようになります。

6. 運命の接続:机上デバッグで「確信」を手に入れる

すべてのコードと設定が揃いました。いよいよ、マイコン(Adafruit Feather 32u4)とPC(SocketDebugger)を接続し、設計したプロトコルが正しく息づくかを確認します。

6.1 接続:仕様が「対話」に変わる瞬間

いよいよ運命の瞬間です。ここで一つ重要な注意点があります。

シリアルポート(COMポート)は、同時に一つのアプリからしか接続できません。 4章で動作確認に使用したArduino IDEのシリアルモニタが開いたままだと、SocketDebuggerがポートを掴めずエラーになってしまいます。

シリアルモニタは完全に閉じたか?

それを確認したら、SocketDebuggerの「ポート1 通信開始」ボタンをクリックしましょう。
その瞬間、先ほどまでシリアルモニタで意味不明な文字列として流れていたデータが、意味を持った「対話」としてログに刻まれ始めます。

[Rx] 02 10 00 00 03 (マイコンからの問いかけ)
[Tx] 02 10 01 00 03 (SocketDebuggerが瞬時に「+1」して返信)
[Rx] 02 10 01 00 03 (マイコンが正解を確認し、次の番号を送信)
[Tx] 02 10 02 00 03 (さらに+1して返信……)

<SocketDebbugerの実際のログ>
image.png

ログのタイムスタンプを見てください。およそ200ms周期でマイコンが投げ、それに対してPC側が即座に打ち返す。人間業では不可能な、機械同士の高速なキャッチボールが成立しています。

6.2 基板上で光る「合格サイン」

ログと同時に、手元のマイコンに目を向けてください。 先ほどまで沈黙していた基板上のLEDが、小気味よくチカチカと点滅を始めているはずです。

<SocketDebuggerの送受信ログとマイコンのLEDの点滅>
マイコンLED点滅.gif

この光は、単にプログラムが走っている証拠ではありません。 「PC(SocketDebugger)が仕様通りに送った『+1』のパケットを、マイコンが100ms以内に正しく受け取り、中身を精査して『合格』と判定した」 という、一連のシーケンスが完璧に成立している揺るぎない証拠なのです。

6.3 「仮想・故障」でリトライ処理を検証する

ここで、シミュレーターならではの実験をしてみましょう。SocketDebuggerの「ポート1 通信終了」ボタンを押して、通信をわざと遮断してみてください。

LEDの消灯: 返信が来なくなった(タイムアウト)ため、マイコンは即座に成功判定を止め、LEDが消えます。

リトライの確認: 再び「ポート1 通信開始」を押すと、マイコンは諦めずに送り続けていた「前と同じシーケンス番号」に対して、SocketDebuggerが再び応答を返し、LEDが即座に再点灯します。

実機がない環境で、「異常系(タイムアウト)から正常系への復帰」までを机の上で検証できる。これこそが、デバッグツールを使いこなす最大のメリットです。

7. まとめ:SocketDebugger が「実機がない」を言い訳にさせない

「実機がないからテストできない」 そんなエンジニアの悩みを過去のものにするのが、SocketDebugger という強力な相棒です。

単なる通信ログ表示ソフトの枠を超え、PCを「知能を持った産業機器」へと変貌させるこのツールを使いこなすことで、開発の質は劇的に変わります。

  • ✅ シリアルモニタからの脱却: 人間には不可能な「0msレスポンス」や「複雑な演算返信」を SocketDebugger に肩代わりさせる
  • ✅ 仕様を「対話」として検証する: 投げっぱなしのコードを卒業し、相手の反応をトリガーに動く堅牢なシステムを構築する
  • ✅ 机上で勝利を確定させる: 異常系のリトライまで SocketDebugger で徹底的に追い込めば、実機合わせはただの「答え合わせ」に変わります

現場にマイコンを持ち込むその日、あなたは「動くかな?」という不安ではなく、「SocketDebugger で検証済みだから、仕様通りに動くはずだ」 というエンジニアとしての確信を持って挑めるはずです。

さあ、SocketDebugger という強力な武器を味方につけて、自信を持って現場へ向かいましょう!

記事リンク

株式会社ユードム

株式会社ユードムはITと人間力で社会に貢献します。

SocketDebuggerのご購入はこちら(期間限定の試用版もあります)
https://www.udom.co.jp/sdg/index.html

44
47
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
44
47

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?