picotoolで設定だけを書き換える - Raspberry Pi PicoのFlash活用術

`picotool`[^1] はもっぱらファームウェア書き込みツールとして利用されることが多い、Raspberry Pi Pico 公式ツールですが、ファームウェア書き込みだけでなくフラッシュメモリの任意の領域のデータを読み書きする機能を備えています。これを使えば「設定だけ後から書き換える」運用ができます。

## 設定を書き換えられると何がうれしいのか？

pico-examples[^2]などもそうなのですが、ホビー向けのサンプルプログラムではWi-FiのSSIDやパスワードなどの設定をソースコードベタ書きでファームウェアに埋め込んでいる例が多いです。そのため、デバイスを別の場所で使う場面などでWi-Fiの設定を変更するには、ソースコードからビルドしなおす必要がありけっこう面倒です。ファームウェアとは別に設定情報だけ書き換えられるなら、デバイスの運用はずっとラクになります。

## 設定情報を書き込む手順

例えば、Picoのオンボードフラッシュメモリを以下のようなレイアウトで利用します。

| アドレス | 内容 |
| ---- | ---- |
|0x10000000〜   |ファームウェア |
|0x10100000〜 | 設定情報 |

#### 1. 設定ファイル `setting.txt` を用意する

ホストPCで、設定ファイル `setting.txt` を作成します。
```plaintext:setting.txt
MySSID
MyPassword
```
プレーンテキストで記述し、行末の改行（`\n`）を忘れずに。

#### 2. 設定ファイルをフラッシュメモリへ書き込む

```bash
picotool load -o 0x10100000 -t bin setting.txt
```
この例では`picotool load`コマンドに`-o` オプションを付与し、フラッシュメモリの先頭(0x10000000)から1MBオフセットした位置（0x10100000）に設定情報を書き込みます。`-t`オプションで拡張子を無視しバイナリーファイルとしてロードします。
⚠️ アドレスがファームウェア領域と重複しないよう注意してください。 設定情報を書き込む位置は、ファームウェアのサイズに応じて調整してください。

#### 設定情報をデバイスで読み込む（C: pico-sdk）

デバイスでは設定情報を以下のように読み込みます:
```c:example.c
#include <pico/stdlib.h>
#include <stdio.h>


#define SETTING_LOCATION  _u(0x10100000)

typedef struct {
    char ssid[32 + 1];
    char password[63 + 1];
} wifi_setting_t;

// srcバッファの'\n'で終端された文字列をdestバッファにコピー
static uint8_t *load_string(char *dest, size_t maxlen, const uint8_t *src) {
    uint8_t *end = (uint8_t *)src;
    while (*end && *end != '\n')
        end++;

    size_t len = end - src;
    if (len >= maxlen)
        len = maxlen - 1;
    memcpy(dest, src, len);
    dest[len] = '\0';
    if (len > 0 && dest[len - 1] == '\r')
        dest[len - 1] = '\0';

    return *end ? end + 1 : end;
}

// Flashから設定情報を読み込む
void load_settings(wifi_setting_t *setting, uint32_t offset) {
    uint8_t *flash = (uint8_t *)offset;
    flash = load_string(setting->ssid, sizeof(setting->ssid), flash);
    load_string(setting->password, sizeof(setting->password), flash);
}

int main() {
    wifi_setting_t setting;

    printf("Load Wi-Fi setting from Flash\n");
    load_settings(&setting, SETTING_LOCATION);
    printf("Setting: SSID=%s, password=%s\n", setting.ssid, setting.password);

    return 0;
}
```

実行時に `XIP_BASE` (`0x10000000`)から1MB離れた位置のバッファを、load_settings関数で読み込むだけです。オンボードフラッシュメモリの参照に特別なAPIは必要ありません。

## 応用: 設定情報を構造体バイナリとしてパックする

より複雑な設定情報を構成したい場合は、構造体にパックしたバイナリを書き込む方法もあります。Pythonなどで `setting.bin` を作成：

```python:create_setting_bin.py
import struct
ssid = "MySSID"
password = "MyPassword"
with open("setting.bin", "wb") as f:
    f.write(struct.pack("33s64s", ssid.encode(), password.encode()))
```
同様に`picotool`でフラッシュメモリに書き込めば、 `wifi_setting_t *` にキャストするだけで利用可能です。

## 補足: Pico2(RP2350)はパーテーションが利用できる

RP2350を搭載したPico2はパーテーションテーブルをサポートしています。一つのフラッシュメモリを「ファームウェア用パーテーション、設定データ用パーテーション」など複数の論理的領域に分割できるので、領域ごとの用途がより明確になります。RP2350環境で設定データをフラッシュメモリに埋め込む場合はパーテーションの利用も検討してみると良いでしょう。


## まとめ：picotool はファームウェアだけじゃない

- picotool の `-o` オプションでフラッシュメモリの任意アドレスにデータ書き込み可能
- 設定を分離することで、運用が圧倒的に楽になる
- 同一ファームウェアで複数構成・複数環境に対応できる

Raspberry Pi Pico を使った小規模な組み込み開発でも、こうした工夫ひとつで運用がずっと楽になります。この例では`\n`区切りのプレーンテキストで設定を記述しましたが、もう少し踏み込んでJSONや.INIといった構造化テキストを書き込んで、パースして利用する方式を取ることもできます。
picotool はファームウェア書き込みだけのツールではありません。「Pico用の汎用フラッシュメモリーライター」としての活用方法をぜひ試してみてください。

## 関連リンク

[^1]: [raspberrypi/picotool](https://github.com/raspberrypi/picotool)
[^2]: [raspberrypi/pico-examples](https://github.com/raspberrypi/pico-examples)
[^3]: [XIP](https://ja.wikipedia.org/wiki/Execute_in_place)