暫定でEEPROMにログ保存して、読み出しを検証しようかとEEPROMをavrdudeコマンドで読みだそうとしたが、何をとち狂ったかFLASHの冒頭部分を読みだしていることに気が付いた。回避策もないようなので、Arduino スケッチ(Arduino に書き込むプログラム)とPowerShell実装の組み合わせで対処することにした。
Arduinoの仕組みの成果、なかなかめんどくさい。

目的
Arduino UNO 上の EEPROM 内容を確実に取得する。
avrdude(ブートローダ経由)では EEPROM/FLASH の取り違えや挙動差があるため、
Arduino 自身に EEPROM を読ませて PC に転送する方式を採用する。
全体構成
- Arduino 側
EEPROM → Intel HEX 形式 → UART(USB CDC)送信 - PC 側
UART 受信 → 同期マーカー判定 → HEX ファイルとして保存
Arduino 側の動作原理
EEPROM.read()を用いて EEPROM アドレス空間を順次走査- 読み出したバイト列を Intel HEX レコードとして整形
- アドレス
- データ長
- チェックサムを含む
- UART(
Serial)へ ASCII テキストとして出力
同期制御
- データ送信の前後に明示的なマーカーを出力
EEPROM_BEGIN
:10....
:10....
...
:00000001FF
EEPROM_ENDこれにより PC 側は、
- 受信開始点
- 正常終了点
を確実に判定できる。
再送設計
- 送信処理は一定周期(例:20秒)で繰り返す
- PC 側の起動タイミングや USB 再接続に依存しない
PC(PowerShell)側の動作原理
System.IO.Ports.SerialPortで COM ポートをオープン- 行単位(
ReadLine())で受信 - 受信内容を以下のルールで処理
| 受信内容 | 動作 |
|---|---|
EEPROM_BEGIN | 書き込み開始 |
: で始まる行 | HEX レコードとして保存 |
EEPROM_END | 書き込み終了・クローズ |
- ファイルは スクリプトの配置ディレクトリに生成
($PSScriptRootを基準に絶対パス化)
この方式のポイント
- EEPROM と FLASH の経路を完全に分離
- 読み出し主体は MCU 本体
- PC 側は「単なる受信・保存」
- ブートローダ/avrdude 非依存
- テキスト転送
- ロジアナ/ターミナルで可視
- 途中破損の検出が容易
- 再送前提設計
- 信頼性は通信層ではなく運用で確保
Arduinoスケッチ(EEPROMreader.ino)
#include <EEPROM.h>
const unsigned long INTERVAL = 20000;
uint8_t checksum(uint8_t *buf, uint8_t len) {
uint16_t sum = 0;
for (uint8_t i = 0; i < len; i++) sum += buf[i];
return (uint8_t)(-sum);
}
void sendHexLine(uint16_t addr) {
uint8_t rec[5 + 16];
rec[0] = 16; // length
rec[1] = addr >> 8;
rec[2] = addr & 0xFF;
rec[3] = 0x00; // record type
for (int i = 0; i < 16; i++) {
rec[4 + i] = EEPROM.read(addr + i);
}
uint8_t cs = checksum(rec, 4 + 16);
Serial.print(':');
for (int i = 0; i < 4 + 16; i++) {
if (rec[i] < 0x10) Serial.print('0');
Serial.print(rec[i], HEX);
}
if (cs < 0x10) Serial.print('0');
Serial.println(cs, HEX);
}
void setup() {
Serial.begin(115200);
while (!Serial) { ; }
}
void loop() {
Serial.println("EEPROM_BEGIN");
for (uint16_t addr = 0; addr < 1024; addr += 16) {
sendHexLine(addr);
}
Serial.println(":00000001FF"); // EOF
Serial.println("EEPROM_END");
digitalWrite(LED_BUILTIN, HIGH); // turn the LED on (HIGH is the voltage level)
delay(200); // wait for a second
digitalWrite(LED_BUILTIN, LOW); // turn the LED off by making the voltage LOW
delay(INTERVAL);
}
PowerShell(eepromGet.ps1)
$port = New-Object System.IO.Ports.SerialPort COM4,115200,None,8,one
$port.ReadTimeout = 5000
$port.Open()
#$outFile = "eeprom.hex"
#$sw = New-Object System.IO.StreamWriter $outFile
$outFile = Join-Path $PSScriptRoot "eeprom.hex"
$sw = New-Object System.IO.StreamWriter($outFile)
$sw.AutoFlush = $true
Write-Host "Waiting for EEPROM_BEGIN..."
# ===== BEGIN待ち =====
while ($true) {
try {
$line = $port.ReadLine().Trim()
if ($line -eq "EEPROM_BEGIN") {
Write-Host "BEGIN received"
break
}
} catch {
# timeout → 何もしないで待ち続ける
}
}
# ===== データ受信 =====
while ($true) {
try {
$line = $port.ReadLine().Trim()
if ($line -eq "EEPROM_END") {
Write-Host "END received"
break
}
$sw.WriteLine($line)
Write-Host $line
} catch {
# timeout 継続
}
}
$sw.Close()
$port.Close()
Write-Host "DONE"
初期状態で得られる結果
eeprom.hex
:1000000000000000000000000000000000000000F0
:1000100000000000000000000000000000000000E0
:1000200000000000000000000000000000000000D0
:1000300000000000000000000000000000000000C0
:1000400000000000000000000000000000000000B0
:1000500000000000000000000000000000000000A0
:100060000000000000000000000000000000000090
:100070000000000000000000000000000000000080
:100080000000000000000000000000000000000070
:100090000000000000000000000000000000000060
:1000A0000000000000000000000000000000000050
:1000B0000000000000000000000000000000000040
:1000C0000000000000000000000000000000000030
:1000D0000000000000000000000000000000000020
:1000E0000000000000000000000000000000000010
:1000F0000000000000000000000000000000000000
:1001000000000000000000000000000000000000EF
:1001100000000000000000000000000000000000DF
:1001200000000000000000000000000000000000CF
:1001300000000000000000000000000000000000BF
:1001400000000000000000000000000000000000AF
:10015000000000000000000000000000000000009F
:10016000000000000000000000000000000000008F
:10017000000000000000000000000000000000007F
:10018000000000000000000000000000000000006F
:10019000000000000000000000000000000000005F
:1001A000000000000000000000000000000000004F
:1001B000000000000000000000000000000000003F
:1001C000000000000000000000000000000000002F
:1001D000000000000000000000000000000000001F
:1001E000000000000000000000000000000000000F
:1001F00000000000000000000000000000000000FF
:1002000000000000000000000000000000000000EE
:1002100000000000000000000000000000000000DE
:1002200000000000000000000000000000000000CE
:1002300000000000000000000000000000000000BE
:1002400000000000000000000000000000000000AE
:10025000000000000000000000000000000000009E
:10026000000000000000000000000000000000008E
:10027000000000000000000000000000000000007E
:10028000000000000000000000000000000000006E
:10029000000000000000000000000000000000005E
:1002A000000000000000000000000000000000004E
:1002B000000000000000000000000000000000003E
:1002C000000000000000000000000000000000002E
:1002D000000000000000000000000000000000001E
:1002E000000000000000000000000000000000000E
:1002F00000000000000000000000000000000000FE
:1003000000000000000000000000000000000000ED
:1003100000000000000000000000000000000000DD
:1003200000000000000000000000000000000000CD
:1003300000000000000000000000000000000000BD
:1003400000000000000000000000000000000000AD
:10035000000000000000000000000000000000009D
:10036000000000000000000000000000000000008D
:10037000000000000000000000000000000000007D
:10038000000000000000000000000000000000006D
:10039000000000000000000000000000000000005D
:1003A000000000000000000000000000000000004D
:1003B000000000000000000000000000000000003D
:1003C000000000000000000000000000000000002D
:1003D000000000000000000000000000000000001D
:1003E000000000000000000000000000000000000D
:1003F00000000000000000000000000000000000FD
:00000001FF
1回PowerShellを実行して一部しかデータ取得できなかった場合は、即座にもう一回実行すると、20秒後には全データを取得できます。
とりあえず、これで、avrdudeコマンドで読みだしたのとほぼ同じ内容が取れていると思います。 本番とほぼ同じデータに形式になるので、このデータで解析用の実装を作成しておけば、ほとんど改修なしで流用できるはずです。