Pico搭載ブートスイッチ:電源投入前にOSを選択
電源を入れる前にスイッチを切り替えるだけで、起動するOSを事前に選択可能。OS選択のために待つ必要がなくなります。
ストーリー
Hackaday.ioでプロジェクトを物色中、Stephen Holdawayによるこのプロジェクトに出会いました。このプロジェクトでは、デュアルブートユーザーが直面する煩わしいタスク、すなわちWindowsに切り替えたいときに毎回GRUBメニューでOSを選択するために座って待つという問題を解決しています。彼はハードウェアスイッチを追加し、コンピューターの電源投入時に毎回どのOSをブートするかを決定できるようにしました。
彼はSTM32マイクロコントローラーをUSBマスストレージデバイスとして構成することでこれを実現しました。研究から実装までの全過程をHackaday投稿(クリック)で文書化しています。実装の理解を深めるために、そちらの投稿もご覧ください。
このプロジェクトでは、その変更をRaspberry Pi Picoに移植した方法を紹介します。私のバージョンはこのGitHubリポジトリ(クリック)で公開しています。
コンセプト
GNU GRUBは、オペレーティングシステムがロードされる前に実行されるプログラムです。このメニューでどのOSをロードするか選択できます。GRUBが提供するモジュールは非常に限られており、USB接続のマイクロコントローラーからデータを読み取ることはできません。しかし、ストレージディスクからデータを読み取ることは可能です。
そこで、マイクロコントローラーをマスストレージデバイスとして列挙させることで、GRUBを騙してマイクロコントローラーからデータを読み取らせます。
つまり、Raspberry Pi PicoをtinyUSBライブラリ経由でマスストレージデバイスとして列挙させ、switch.cfgファイルを持たせます。Picoはこのファイルにスイッチ位置、すなわち1でON、0でOFFを書き込みます。
GRUBにスクリプトを追加し、その機能でswitch.cfgファイルを読み取り、defaultを0(Ubuntu)/ 2(Windows)に設定します。
GRUBがロードされると、カスタムスクリプトが実行され、UUID識別子でデバイスを検索し、存在すればswitch.cfgファイルを読み取ります。スイッチ位置を取得後、抽出データに基づいてデフォルトOS選択を設定します。
まとめると、
- Picoがマスストレージデバイスとして構成される。
- GRUBメニューがスクリプトを呼び出し、特定のファイルを要求。
- Picoが読み取り要求に応じ、switch.cfgファイルにスイッチ位置を追加。
- GRUBのスクリプトがファイルから情報を抽出し、デフォルトオプションを設定。
Picoをマスストレージデバイスとして構成
tinyUSBのcdc_msc例を使用してこれを実現しました。この例ではPicoをマスストレージデバイスとして構成し、FAT12ファイルシステムを作成し、README.txtファイルを列挙します。
README.txtをswitch.cfgに変更し、ファイルに「set os_hw_switch=0\n」という行を追加。
#define SWITCH_CFG_CONTENTS \ "set os_hw_switch=0\n" ... //------------- Block3: Readme Content -------------// SWITCH_CFG_CONTENTS
これでPicoがマスストレージデバイスとして構成されました。UF2ファイルをPicoにコピーすると、ストレージデバイスとして列挙されます。GRUBスクリプトで必要となるデバイスのUUID IDは UUID=”0000-1234″ です。
$ sudo blkid ... /dev/sda: SEC_TYPE="msdos" LABEL_FATBOOT="TinyUSB MSC" LABEL="TinyUSB MSC" UUID="0000-1234" BLOCK_SIZE="512" TYPE="vfat"
回路
スイッチ位置の読み取りとファイルへの書き込み
スイッチ位置を読み取り、switch.cfgファイルの内容をそれに応じて変更する必要があります:
- スイッチがONの場合: set os_hw_switch=1\n
- スイッチがOFFの場合: set os_hw_switch=0\n
GPIO_PIN 28をスイッチピンとして使用し、プルダウンに設定。
read_switch_valueはスイッチ位置を返します。すなわち‘1’はオン(プルアップ)、‘0’はオフ(プルダウン)。
//-------------------------main.c---------------------
#define SWITCH_PIN 28
// read switch value
uint8_t read_switch_value()
{
return gpio_get(SWITCH_PIN) ? '1' : '0';
}
int main(void)
{
gpio_init(SWITCH_PIN);
// ピンをINPUTに設定
gpio_set_dir(SWITCH_PIN, false);
// ピンをPULL_DOWNに設定
gpio_set_pulls(SWITCH_PIN, false, true);
switch.cfgにスイッチ位置を書き込むため、readGRUBConfig()を使用し、内部でread_switch_valueを呼び出し、出力バッファにスイッチ位置を設定します。
第3ブロックlbaが3に設定されるときに読み取りが発生することを発見し、その呼び出しをインターセプトしてreadGrubConfigを呼び出し、ファイルの内容がコピーされるバッファを渡します。
//-------------------------msc_disk.c---------------------
static char grubConfigStr[] = "set os_hw_switch=0\n";
static void readGrubConfig(uint8_t* output)
{
// 現在のスイッチ値で設定文字列を更新
grubConfigStr[sizeof(grubConfigStr)-3] = read_switch_value();
memcpy(output, &grubConfigStr, sizeof(grubConfigStr));
}
// READ10コマンド受信時のコールバック。
// ディスクのデータをバッファにコピー(最大bufsize)し、コピーしたバイト数を返す。
int32_t tud_msc_read10_cb(uint8_t lun, uint32_t lba, uint32_t offset, void* buffer, uint32_t bufsize)
{
(void) lun;
// ファイル読み取り時
if(lba == 3){
readGrubConfig(buffer);
return bufsize;
}
...
...
}
Picoコードのコンパイル
Pico stdlibをコードに追加して、gpioピンアクセスを取得。
//-------------------------main.c----------------------------------- #include#include #include #include "bsp/board.h" #include "tusb.h" ... #include "pico/stdlib.h"
プロジェクトをビルド:
$ mkdir build $ cd build $ cmake .. $ make
GRUBを設定してファイル内容を読み取る
これらの変更はUbuntu 20.10で行いました。
$ sudo vim /etc/grub.d/40_custom
....
# ハードコードされたファイルシステムIDでハードウェアスイッチデバイスを検索
search --no-floppy --fs-uuid --set hdswitch 0000-1234
# 見つかった場合、動的設定ファイルを読み取り、各位置に適切なエントリーを選択
if [ "${hdswitch}" ] ; then
source ($hdswitch)/switch.cfg
if [ "${os_hw_switch}" == 0 ] ; then
# Linuxを起動
set default="0"
elif [ "${os_hw_switch}" == 1 ] ; then
# Windowsを起動
set default="2"
else
# フォールバック
set default="${GRUB_DEFAULT}"
fi
else
set default="${GRUB_DEFAULT}"
fi
まず、ファイルシステムを検索します。GRUBにはこのためのsearchサブコマンドがあります。
- -no-floppy オプションでフロッピーデバイスの検索を防止
- -fs–uuid 0000-1234 でUUID 0000-1234のファイルシステムを検索
デバイスが見つかった場合、最初に見つかったデバイスが環境変数の値として設定されます。
–set hdswitch hdswitch は環境変数で、見つかった場合はディスク名が設定されます。
次に、hdswitch変数が設定されている場合にswitch.cfgファイルをsourceし、別の環境変数 os_hw_switch をスイッチ位置(0または1)で作成します。
os_hw_switch の値を読み取り、defaultを0または2に設定。0はGRUBメニューでUbuntuが0番目、Windowsが2番目にあるためです。
最後に、hdswitchが設定されていなければ、defaultを GRUB_DEFAULT に設定。
GRUBを更新:
$ sudo update-grub
出典: Hardware Boot Select Switch Using Pico
製造プロセス
- 現代エピレーションデバイス総合ガイド:設計、歴史、未来
- Raspberry Pi 2とADS-WS1で屋外気象観測ステーションを構築
- AT89S52マイコンとLM35センサーによる温度ベース機器制御システム
- Raspberry Pi 3とPubNubでリモート気象監視
- TI SensorTagをNode-RED経由でBlynkと統合 – Raspberry Pi Zero編
- Raspberry PiでPIRモーションセンサーとLEDを制御
- Bridge Shield でスマートロボットを構築:Raspberry Pi と Arduino の統合
- Pythonで作るWi-Fi制御Raspberry Piロボット - ステップバイステップガイド
- IoTデバイス向け組み込みハードウェアハッキング:ツール、技術、ビジネスへの影響
- C# switch 文をマスターする:構文、例、ベストプラクティス
- C# usingステートメント完全ガイド:名前空間インポート、エイリアス、静的ディレクティブ