OTGで実現するEthernet・Serial・Storage – 各機能を個別に設定

前回の記事で、USB OTGの3つの機能(USB Ethernet、USBシリアル通信、USBストレージ)をConfigfs方式で設定しました。複数機能を同時に有効にするための設定でした。もし単機能でよければ、Compositeではなく、各機能を個別に指定します。

LinuxにはUSB Gadget Frameworkという仕組みがあります。

ガジェット内容
g_etherUSB Ethernet
g_serialUSB Serial
g_mass_storageUSBストレージ
g_hidキーボードやマウス
Composite複数機能を同時提供

前回は表のCompositeの設定方法でした。
今回はお手軽なg_ether、g_serial、g_mass_storageで1つの機能だけ使う方法を試します。

単独モジュールだと、複数の機能は同時設定はできない代わりに、kernelで制御されているため自動起動の追加作業が要りません。
それぞれの設定方法と単機能のテストをご紹介します。

記事執筆時点での注意事項

今回の環境は、CM5を元にした産業用Raspberry Piの「PL-R5」を使用しました。OSはRaspberry Pi OS(bookworm)です。ホストPCはLinux/macOSでテストしました。

検証環境
Model: 産業用ラズパイ「PL-R5」(Raspberry Pi Compute Module 5 Rev 1.0)
OS: Debian GNU/Linux 12 (bookworm)
Kernel: 6.12.62+rpt-rpi-v8
macOS: Tahoe 26.3

ホストPCとしてWindowsを利用する場合は、ECM(Ethernet Control Model)が非対応なため、RNDIS(Remote Network Driver Interface Specification)のドライバが必要になります。USBケーブルを接続後、デバイスマネージャーでドライバ更新します。

USB Ethernetだけなら有効にするだけ

Raspberry Pi OS Trixieからは、rpi-usb-gadgetパッケージが含まれています。これを有効にすれば、手作業の設定はぜずともUSB Ethernetデバイスとして認識され、Wi-Fiや有線LANを設定しなくてもすぐにSSHで接続できます。

以下、公式サイトからの引用です。

“Starting with Raspberry Pi OS Trixie images dated 20.10.2025 or later, a new package called rpi-usb-gadget is included by default. Once activated, the Raspberry Pi presents itself as a USB Ethernet device, and you can connect via SSH immediately, without configuring Wi-Fi or Ethernet.”
2025年10月20日以降のRaspberry Pi OS Trixieイメージには、rpi-usb-gadgetという新しいパッケージがデフォルトで含まれています。有効化すると、Raspberry PiはUSB Ethernetデバイスとして認識され、Wi-FiやEthernetの設定をすることなく、すぐにSSH接続できるようになります。
https://www.raspberrypi.com/news/usb-gadget-mode-in-raspberry-pi-os-ssh-over-usb/

USB Ethernetだけであれば、現在は簡単ですね。
raspi-configから選ぶだけです。

sudo raspi-config

USB OTGのケーブル選びと接続先ポート

PL-R5の場合、PCとUSBケーブルで接続するのは、通常のUSB Type-Aではなく、OSイメージを書き込む時に使用するUSB Type-Cのポートです。これは分かりやすいです。(書き込みスイッチはOFFのまま)

しかし、Pi 4/5では、OTGに使用するのは普段電源アダプターを接続しているUSB Type-Cポートです。電源もUSB経由で給電されてOSも起動します。

ただしPi 5では、USB Type-CケーブルでPCと直結した場合、どちらが操作する側になるかの判断がうまく決まらないことがあります。ケーブルによってはMass Storage Gadgetモードとして正しく認識されなくなります。

USB Type-A to USB Type-C(USB 2.0仕様)のケーブルであれば、USB-A側が常に操作する側のホストとして固定されやすく、役割決定が安定して最も確実です。

次の候補は、USB Type-C to Type-C(USB2.0のみ対応)です。これは確実ではありませんが概ね認識できる筈です。
但し、e-Marker入りのUSB 3.x/PD対応、高速充電を謳っているケーブルや、Thunderbolt 3/4ケーブルでは、環境によって認識が不安定になる場合があります。

例外として「PL-R5」でUSB OTGを実現する場合、電源をON/OFF可能なスイッチつきのUSBケーブルを使用してください。スイッチをOFFにしてUSBケーブル経由で給電されないようにする必要があります。
rpiboot時とは違い、本体が既に電源アダプターで稼働している状態だからです。

なお、どのモデルでも給電専用のUSB Type-Cケーブルではデータ通信はできないため、当然ながらgadgetモードにはなりません。

Raspberry Pi各モデルによっては、USBケーブルを選ぶ際に注意が必要です。

事前準備:USB OTGポートをDeviceモードにする(手動)

前段のraspi-configでのUSB Ethernet化を使わないなら、手動で設定する必要があります。

PL-R5のUSB2.0 OTGポートをデバイス側で使うには、先ずはブート時にUSBコントローラをPeripheralモードで有効化する設定をします。
これはUSB Ethernet、シリアル通信、USBストレージのどれも基本は同じです。

/boot/firmware/config.txtに追記

dtoverlay=dwc2,dr_mode=peripheral

但し、CM5系はhostがデフォルトで入っていることが多いため、次のようになっていれば、コメントアウトしてから追記してください。

[cm5]
#dtoverlay=dwc2,dr_mode=host
dtoverlay=dwc2,dr_mode=peripheral

/boot/firmware/cmdline.txtに追記(最後尾に半角スペースで区切り改行なし)

modules-load=dwc2

追記したら再起動します。
これで有効になりました。

※前段のように、raspi-configを使ったUSB Ethernetを使うなら、この設定は要りません。

1.USB Ethernet(手動設定)

USB Ethernetとは何かというと、USBケーブル1本でPL-R5が「USB接続のLANアダプタ」としてPCに認識される仕組みです。
IPアドレスを割り当てられ、LANケーブルは必要なくピアツーピアで直結するネットワークです。

仮想ネットワークインターフェースusb0で繋がります。

実用例
• 初期設定Web UIへアクセス
• 保守用アクセス方法として
• DHCPが無い環境で緊急接続用
• 直結のSSH接続

”とりあえずUSBケーブルで繋げば管理画面が開く”といった仕組みを作ることも可能です。

仮にRaspberry Pi側でサーバが稼働していれば、2台での接続でもホストPC側からRaspberry Pi側のサーバーを利用できます。
既存のDHCPなどが必要なく動作させられます。

USB Ethernetの単機能テスト

※前段のように、raspi-configを使ったUSB Ethernetを使うなら、この設定は要りません。

仮想ネットワークカード(NIC)としてusb0を作成し、ホストPCと繋ぐテストをします。

/boot/firmware/cmdline.txtの記述を次に変更します。

modules-load=dwc2
    ↓
modules-load=dwc2,g_ether

ホストPCがWindowsなら記述はしません。
再起動で有効になります。

ヒント:ホストPCがWindowsの場合

WindowsをホストPCにする場合、g_etherの表記は要りません。modules-load=dwc2のままで良いです。
代わりにWindows側でRNDISを有効にしてドライバをインストールする必要があります。

Windows側の動作が以下のように自動認識されればドライバのインストールは必要ありません。

✅ 自動で「Remote NDIS Compatible Device」として認識される
✅ ネットワークアダプタに表示される

もし自動認識しない場合は次の手順です。
1. デバイスマネージャを開く
2. 「不明なデバイス」を右クリック
3. ドライバー更新
4. 「コンピューターを参照」
5. 「一覧から選択」
6. 「ネットワークアダプター」
7. 「Microsoft」
8. 「Remote NDIS Compatible Device」

ホストPCが今回のようにLinux/macOSであればこの手順は要りません。

仮想NIC(usb0)の作成

先ほど、OTGポートをDeviceモードとして有効にする手だてはしてありますので、次は仮想ネットワークカード(NIC)としてusb0を作成し、固定IPアドレスを割り振ります。
同時に自動接続させるnmcliコマンドを実行するだけです。

sudo nmcli con add type ethernet \
ifname usb0 con-name usb0 \
ipv4.method manual ipv4.addresses 192.168.7.2/24 \
connection.autoconnect yes

接続 ‘usb0’ (449a53a2-8cf5-4c9c-9320-ee8eddbdd3c4) が正常に追加されました。

このように表示されればOK。
リンクアップしておきます。

sudo ip link set usb0 up

次は、ホストPC側ではネットワークの設定でIPアドレスを192.168.7.1/24と設定します。
例えばmacOSであれば、ケーブルを繋ぐと「RNDIS/Ethernet Gadget」などが新たに追加されています。もし無ければサービスの追加からEthernetを追加します。

詳細を開いて次のようにIPアドレスを手動で設定します。

ホストPC側のネットワークIPアドレス

構成:手動
IPアドレス:192.168.7.1
サブネットマスク:255.255.255.0
ルーター:空欄

その後、次のコマンドをPi側(PL-R5)で実行して接続できれば完了です。

sudo nmcli con up usb0

接続が正常にアクティベートされました (D-Bus アクティブパス: /org/freedesktop/NetworkManager/ActiveConnection/3)

以下のようにPi側でip aコマンド、ホストPC側からping、ssh接続もできる筈です。

# pi側でsub0を確認
ip a

##ホストPCからpiへ
ping 192.168.7.2
ssh ユーザー名@192.168.7.2

Pi側の有線LANやWi-Fiを切断しても通信が接続できていれば、USB-OTGのEthernetで繋がったことが分かります。

仮にPi側にApacheをインストールすれば、ホストPC側のWebブラウザでApacheのデフォルトページが表示されます。

sudo apt install apache2

ホストPCのWEBブラウザで、http://192.168.7.2/を表示します。
Apache2 Debian Default Pageは表示されましたか?
これでUSBケーブル1本で接続ができました。

2.USB Serial

次はシリアル通信です。
USBケーブル1本で、PL-R5が「USB接続のシリアル変換器」としてPCに認識される仕組みです。

実用例
• PL-R5のログを確認
• コマンド操作
• 双方向通信
• マイコンとのブリッジ用途

今度はLAN(ネットワークに参加しない)も不要です。
最も確実な保守経路になりますから、産業用途で使う機会が多いでしょう。

USB Serialの単機能テスト

先ほどの/boot/firmware/cmdline.txtの記述を今度は次に変更します。

modules-load=dwc2,g_ether
    ↓
modules-load=dwc2,g_serial

再起動で有効になります。

/dev/ttyGS0がUSB Serialを担います。
これは標準では無効になっていますから、手動で有効にしてサービスを開始してあげます。

sudo systemctl status serial-getty@ttyGS0
○ serial-getty@ttyGS0.service - Serial Getty on ttyGS0
     Loaded: loaded (/lib/systemd/system/serial-getty@.service; disabled; preset: enabled)
     Active: inactive (dead)
       Docs: man:agetty(8)
             man:systemd-getty-generator(8)
             https://0pointer.de/blog/projects/serial-console.html

有効化してサービスをスタート。

sudo systemctl enable serial-getty@ttyGS0
sudo systemctl start serial-getty@ttyGS0

Created symlink /etc/systemd/system/getty.target.wants/serial-getty@ttyGS0.service → /lib/systemd/system/serial-getty@.service.とシンボリックリンクが張られます。

サービスをスタートした後、もう一度ステータスを覗いてみると、active (running)になっていますね。

● serial-getty@ttyGS0.service - Serial Getty on ttyGS0
     Loaded: loaded (/lib/systemd/system/serial-getty@.service; enabled; preset: enabled)
     Active: active (running) since Thu 2026-02-26 14:19:12 JST; 2s ago
       Docs: man:agetty(8)
             man:systemd-getty-generator(8)
             https://0pointer.de/blog/projects/serial-console.html
   Main PID: 2221 (agetty)
      Tasks: 1 (limit: 4693)
        CPU: 2ms
     CGroup: /system.slice/system-serial\x2dgetty.slice/serial-getty@ttyGS0.service
             └─2221 /sbin/agetty -o "-p -- \\u" --keep-baud 115200,57600,38400,9600 - vt220

Linux/macOS側のターミナルで、シリアル通信で使うポートを調べます。

ls -l /dev/tty.usb*

crw-rw-rw-  1 root  wheel  0x9000004  2月 26 14:14 /dev/tty.usbmodem21101

どうやら、私の環境ではusbmodem21101なので、これで接続してみます。各自の環境で調べたポートを利用します。

macOSのターミナルでは、screenコマンドで接続してみました。
最後に通信速度を追加して実行します。(デフォルト(標準)では9600bps)

WindowsならPuTTYで同様のことができます。

screen /dev/tty.usbmodem21101 115200

初回はSSH接続みたいにログイン名とパスワードを訊かれます。
どうやら接続はOKですね。

終了方法

screenコマンドで実行した後、元に戻すのはexitではなく、macOSの場合はCtrl + Aの後に続けてKでkillするか訊かれますので、Yで終了できます。

Ctrl + A → K → y

双方向通信のテスト

PL-R5側から次のようにttyGS0に対してlog〜と記述したものを渡すと、ホスト側のターミナルに表示されます。
本来は実機でコマンドを実行しますが、SSH接続でターミナルを2枚にしても結果は同じです。

echo "log: sensor=26.8C" > /dev/ttyGS0

ホスト側の表示。

log: sensor=26.8C

USB Serialはネットワークが不要です。
IPアドレスもDHCP設定も必要なく、ログインや双方向通信を可能にします。
速度は遅いですがテキスト中心の保守経路としてなら十分ですね。

Pingコマンドの実行結果

ホストPCからPi側へPingの実行をしてみます。OKですね。

screen画面が真っ白?

問題はありません。Pi側で何かコマンドを実行してみましょう。先ほどのようにコマンドを実行すれば、ホストPC側の真っ白なターミナル画面に結果が表示される筈です。
(例えば、uname -aを実行)

真っ白なのは待ち受けている状態です。
この画面を終了するのも同じように先の終了方法で終了させます。

3.USB Mass Storage

USB Mass Storageは、EthernetやSerial同様に、USBケーブル1本でPL-R5の一部保存領域を「USBメモリ」としてPCに認識される仕組みです。

ホストPC側から見ると、リムーバブルディスクとして表示され、ファイル管理のFinderやExplorで開けます。権限がrwであれば、ファイルの読み書きが可能です。

つまり、稼働しているPL-R5の指定領域をUSBメモリーのように扱える機能です。
指定できるのは.binや.imgとしたイメージファイルや、パーティション毎も可能です。

rpiboot と USB Mass Storage Gadget の本質的な違い

分かりにくいのですが、PL-R5やCM5のeMMCにOSイメージファイルを書き込むこと、USB OTGとしてUSB Mass Storage Gadgetモードで接続すること、この2つの意味は異なります。

rpibootで繋ぐ場合、PL-R5/CM5のOSは起動していません。ブートROM経由でeMMCを直接PCに接続して見せています。
一方、Gadgetモードでは、PL-R5/CM5のOSが起動していて、予め指定した領域をUSBメモリーとして見せている状態です。

項目rpibootMass Storage Gadget
OS起動していないしている
対象eMMC全体任意の領域
用途OSの書き込みデータ転送
制御ブートROMOS

rpibootを実行したターミナル画面は次の通りで、実行後はFinderといったファイラーでマウントされた中身が表示されます。
rpibootの場合はbootfsだけがマウントされ、中身はOSの構成ファイルですね。

一方、USB Mass Storageの場合は、指定した領域の中身が表示されます。

どちらもホストPCから見ると、USBドライブのように見えるため、少しややこしいですね。

指定した領域を共有

USB Mass Storageの実体は、1つのイメージファイルまたはパーティション単位で扱えます。
PL-R5のOS側で”この領域をUSBメモリとして見せる”と決められます。

eMMC全体ではなく、任意のイメージファイルでも、任意のパーティションでもOKなのです。

PL-R5側で作成したusb.imgやusb.binといったファイルをホストPC側に見せることができますが、マウントするのはどちらか片方にしないと危険です。どちらからもデータの書き換えができてしまうからです。

今回のテストではパーティションは分けていません。/opt/usbを作成し、そこにpiusb.binとしたイメージファイル作成、ホストPC側へ公開しています。

運用の構成例

実用的な運用としてログ回収用にマウントするパターンで説明します。

例えばPL-R5側では、ログを保存する場所を専用パーティションとして作り、パーティション単位で扱わせる方法です。
これなら万が一壊れても、システムへの影響が少ないでしょう。

eMMC内
 ├ /rootfs
 ├ /data
 └ /log_export  ← このパーティションをUSBストレージとして公開

通常、ログは /var/logに出力されます。
運用として考えられるのは、定期的にUSBメモリー化する領域(/log_export)へログデータをコピーさせ、ホストPCには読み取り専用で公開する設定が良いでしょう。

PL-R5側で/log_exportを普段はマウントせず、ログデータをコピー時のみマウントさせ、コピー後はアンマウントすることで事故はかなり防げそうです。

USB Mass Storage単機能のテスト

前回まで同様に、/boot/firmware/cmdline.txtの記述を次に変更します。

modules-load=dwc2,g_serial
    ↓
modules-load=dwc2,g_mass_storage

再起動して有効になります。

モジュールオプションも設定

USB Mass Storageの場合は、もう1手だけcmdline.txtに記述します。

半角スペースで区切ってから次も1行で末尾に追記してください。(cmdline.txtは改行しない)
ファイルへのパスを変更する場合は気をつけてください。

g_mass_storage.file=/opt/usb/piusb.bin g_mass_storage.stall=0 g_mass_storage.removable=1 g_mass_storage.ro=0

マウントするファイルは/opt/usb/piusb.binとしました。この後で作成します。
stall=0はエラー処理なし、removable=1はファイラーのFinderやエクスプローラーで「取り外し可能」と表示させます。
ro=0は読み書き可能で、1にすると読み取り専用になります。

イメージファイルの作成

/opt配下にusbディレクトリを作成しその中に配置します。今回は128MBの容量です。

sudo mkdir -p /opt/usb
sudo dd if=/dev/zero of=/opt/usb/piusb.bin bs=1M count=128
sudo mkfs.vfat -n CM5USB /opt/usb/piusb.bin

-n CM5USBの部分は、イメージファイルをマウントした時に表示されるラベルです。任意で変更してください。

これで再起動すれば、Linux/Mac/WindowsのファイラーなどでUSBメモリーのように表示されます。

エラーの調査

詳しい説明は省きますが、うまくいかない場合に試すコマンドを残しておきます。
すべてPi側で実行です。

通信の調査で試したいコマンドで調べると分かったです。

ip route
ip route | grep usb0
ip addr show usb0
cat /sys/class/net/usb0/carrier
ip link show
systemctl status cm5-usb-gadget.service
nmcli dev status

単機能USB OTGならお手軽

前回の記事のように、Configfs方式で設定すれば3つの機能を同時に設定できます。ただし、やや設定箇所が多いですね。
もし単機能で使うなら、g_etherなどで設定する方が最もお手軽です。

特にUSB Ethernetの場合、現行のRaspberry Pi OSには予め組み込まれています。raspi-configから有効にするだけで済みます。

USB OTGはUSBケーブル1本で直接アクセスできるので、産業用でも個人利用でも設定しておくと何かと便利かもしれません。
どのような動作になるのか、USBケーブルの選定を含め、事前に一度テストしてみてください。


記事寄稿:ラズパイダ

非エンジニアでも楽しく扱えるRaspberry Pi 情報サイト raspida.com を運営。ラズベリーパイに長年触れた経験をもとに、ラズベリーパイを知る人にも、これから始めたいと興味を持つ人にも参考になる情報・トピックを数多く発信。PiLinkのサイトへは産業用ラズベリーパイについて技術ブログ記事を寄稿。