AI × 組込みファームウェア開発:書き込み確認の完全自動化
はじめに
組込みファームウェア開発において、「ビルド→フラッシュ→動作確認」のサイクルは日常的に繰り返されます。しかし、書き込みが正しく反映されたかの確認は意外と手間がかかります。
本記事では、GitHub Copilot (Claude Opus 4.5) を活用して、以下の自動化を実現した過程を詳細に記録します:
- ビルド時刻の自動埋め込み - ソースコードに現在時刻を自動挿入
- Flash後のスクリーンショット自動取得 - LCD表示を自動キャプチャ
- DMX/Art-Netでのタイムスタンプ出力 - ネットワーク経由で自動検証
- AIによるArt-Net受信検証 - プログラム的に書き込み成功を確認
環境
| 項目 | 内容 |
|---|---|
| ターゲット | STM32F405VG |
| IDE | Keil uVision v5 |
| プログラマ | ST-LINK V2 |
| 表示 | ILI9341 LCD (320x240) |
| 通信 | DMX512 → Art-Net変換 |
| AI | GitHub Copilot (Claude Opus 4.5) |
| OS | Windows 11 |
Phase 1: ビルド時刻の自動埋め込み
ユーザープロンプト
flash & キャプチャー必ずする
AIの作業
1. version_auto.h の自動生成
# build_flash_capture.ps1 より抜粋
$timestamp = Get-Date -Format "'yy MM/dd HH:mm"
$versionContent = @"
/**
* @file version_auto.h
* @brief Auto-generated version
*/
#pragma once
#define BUILD_TIMESTAMP "$timestamp"
"@
$versionContent | Out-File -FilePath "$PSScriptRoot\..\src\version_auto.h" -Encoding UTF8 -Force
2. main.c での表示
#include "version_auto.h"
/* 会場名 + ビルド時刻 */
char Ver[] = VENUE_NAME " " BUILD_TIMESTAMP;
// LCD表示
sprintf(buf, "%s", Ver);
drawString(buf, 130, 197, 1);
結果: LCDに NH-1_DEMO '25 12/13 09:20 と表示される
Phase 2: Flash後の自動スクリーンショット
ユーザープロンプト
画像をここに関連づけしてタイムスタンプ確認する方法
AIの作業
1. DPI対応スクリーンショット (take_screenshot.ps1)
Windows の DPI スケーリング (125%) に対応したスクリーンショット取得:
# DPI Awareness を有効化
Add-Type @"
using System;
using System.Runtime.InteropServices;
public class DPIAware {
[DllImport("user32.dll")]
public static extern bool SetProcessDPIAware();
}
"@
[DPIAware]::SetProcessDPIAware()
# スケール係数を計算して正確なサイズでキャプチャ
$scale = $reportedWidth / $actualWidth # 2048 / 2560 = 0.8 → scale = 1.25
$captureWidth = [int]($reportedWidth * $scale)
$captureHeight = [int]($reportedHeight * $scale)
2. Flash履歴のJSON記録 (flash_and_capture.ps1)
$logEntry = @{
monitor = $MonitorNumber
timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
screenshot = $destPath
buildTime = $buildTime
}
# flash_log.json に追記
$log = @(Get-Content $logFile | ConvertFrom-Json)
$log += $logEntry
$log | ConvertTo-Json | Out-File $logFile -Encoding UTF8
flash_log.json の例:
[
{
"monitor": 3,
"timestamp": "2025-12-13 09:20:53",
"screenshot": "...\Screenshot_Monitor3_2025-12-13_092052.png",
"buildTime": "'25 12/13 09:20"
}
]
Phase 3: DMX/Art-Netでのタイムスタンプ出力
ユーザープロンプト
dmx ch 115~ 最終 タイムスタンプ yy mm dd hh mm 出せば
Art-net で 確認できますね!
AIの作業
1. タイムスタンプパース (main.c)
/* DMX CH118-122 タイムスタンプ (YY MM DD HH MM) */
static uint8_t dmx_timestamp[5] = {0};
/* BUILD_TIMESTAMP "'YY MM/DD HH:MM" をパース */
static void parseBuildTimestamp(void)
{
const char *ts = BUILD_TIMESTAMP; /* "'25 12/13 09:09" */
if (ts[0] == '\'') {
dmx_timestamp[0] = (ts[1] - '0') * 10 + (ts[2] - '0'); /* YY */
dmx_timestamp[1] = (ts[4] - '0') * 10 + (ts[5] - '0'); /* MM */
dmx_timestamp[2] = (ts[7] - '0') * 10 + (ts[8] - '0'); /* DD */
dmx_timestamp[3] = (ts[10] - '0') * 10 + (ts[11] - '0'); /* HH */
dmx_timestamp[4] = (ts[13] - '0') * 10 + (ts[14] - '0'); /* MM */
}
}
2. DMX送信関数の拡張 (main.c)
void SendDMX(int TxC){
int maxFixtureCh = NUM_FIXTURES * KchM;
if (TxC <= maxFixtureCh) {
NH1(TxC); // 器具データ送信
} else {
/* タイムスタンプ送信 */
switch (TxC) {
case 118: USendData(dmx_timestamp[0]); break; /* CH118: 年 */
case 119: USendData(dmx_timestamp[1]); break; /* CH119: 月 */
case 120: USendData(dmx_timestamp[2]); break; /* CH120: 日 */
case 121: USendData(dmx_timestamp[3]); break; /* CH121: 時 */
case 122: USendData(dmx_timestamp[4]); break; /* CH122: 分 */
default: USendData(0); break;
}
}
}
3. Tmax の拡張 (main.h)
/* CH122まで送信(タイムスタンプ用) */
#define Tmax 125 /* DMX CH118-122にタイムスタンプ出力 */
結果: Art-Netモニターで CH118-122 に 25, 12, 13, 09, 20 が表示される
Phase 4: AIによるArt-Net受信検証
ユーザープロンプト
貴方が Art-net 受信して 確認できますか このパソコンです
AIの作業
artnet_receive.ps1 の作成
# Art-Net Receiver - Build Timestamp Checker
$udp = New-Object System.Net.Sockets.UdpClient
$udp.Client.SetSocketOption(
[System.Net.Sockets.SocketOptionLevel]::Socket,
[System.Net.Sockets.SocketOptionName]::ReuseAddress, $true)
$udp.Client.Bind((New-Object System.Net.IPEndPoint(
[System.Net.IPAddress]::Any, 6454)))
while (-not $found) {
$data = $udp.Receive([ref]$ep)
$opcode = [BitConverter]::ToUInt16($data, 8)
if ($opcode -eq 0x5000) { # OpDmx
$ch118 = $data[18 + 117] # Year
$ch119 = $data[18 + 118] # Month
$ch120 = $data[18 + 119] # Day
$ch121 = $data[18 + 120] # Hour
$ch122 = $data[18 + 121] # Minute
Write-Host "Build Time: '$ch118 $ch119/$ch120 $ch121:$ch122"
}
}
実行結果:
=== Art-Net Timestamp Receiver ===
Waiting for ArtDmx packet on port 6454...
ArtDmx received: 530 bytes from 192.168.1.101
=== Build Timestamp (CH118-122) ===
CH118 (Year): 25
CH119 (Month): 12
CH120 (Day): 13
CH121 (Hour): 9
CH122 (Minute): 20
Build Time: '25 12/13 09:20
完成した自動化フロー
┌─────────────────────────────────────────────────────────────┐
│ VS Code Task: Build+Flash (uVision) │
└─────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ 1. version_auto.h 更新 │
│ BUILD_TIMESTAMP = "'25 12/13 09:20" │
└─────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ 2. Keil uVision ビルド │
│ UV4.exe -b -j0 ILI9341SW16.uvproj -t"STM32" │
└─────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ 3. ST-LINK フラッシュ │
│ ST-LINK_CLI.exe -c SWD -P xxx.hex -V -Rst │
└─────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ 4. 3秒待機 (LCD表示安定) │
└─────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ 5. スクリーンショット取得 │
│ Monitor 3 (2560x1440) → _bak/screenshots/ │
└─────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ 6. flash_log.json 記録 │
│ { timestamp, buildTime, screenshot } │
└─────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ 7. VS Code でスクリーンショット表示 │
└─────────────────────────────────────────────────────────────┘
検証方法
方法1: スクリーンショット確認
LCD表示とビルドログの時刻を目視比較
方法2: Art-Net自動検証
# Art-Net受信でタイムスタンプ確認
.\tools\artnet_receive.ps1
方法3: ログファイル確認
# 最新のFlash情報を表示
Get-Content .\flash_log.json | ConvertFrom-Json | Select-Object -Last 1
作成されたファイル一覧
| ファイル | 役割 |
|---|---|
src/version_auto.h |
ビルド時刻定義(自動生成) |
src/main.c |
タイムスタンプパース・DMX送信 |
src/main.h |
Tmax拡張 (CH122まで送信) |
tools/build_flash_capture.ps1 |
Build+Flash+Screenshot統合スクリプト |
tools/take_screenshot.ps1 |
DPI対応スクリーンショット |
tools/flash_and_capture.ps1 |
Flash後のログ記録 |
tools/artnet_receive.ps1 |
Art-Net受信・検証 |
_bak/screenshots/flash_log.json |
Flash履歴記録 |
まとめ
実現できたこと
- Build→Flash→Screenshot が1コマンドで完了
- ビルド時刻がLCDとDMX両方に自動反映
- Art-Net経由でプログラム的に書き込み確認可能
- Flash履歴がJSON形式で自動記録
AIとの協働のポイント
- 具体的な課題を伝える: 「書き込み確認を自動化したい」
- 段階的に機能追加: スクリーンショット → ログ記録 → DMX出力 → Art-Net受信
- フィードバックを与える: 「出てません」「見にくいのでCH118〜に変更」
- 最終確認はAIに任せる: Art-Net受信でタイムスタンプを自動取得
今後の展望
- Flash後にArt-Net受信を自動実行し、ビルド時刻の一致を自動判定
- 不一致時にアラート通知
- CI/CDパイプラインへの統合
参考
タグ: #組込み #STM32 #AI #GitHub Copilot #自動化 #DMX512 #Art-Net

