4
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Nerves Systems Builder で USB カメラ対応カスタム Nerves System を作る

4
Last updated at Posted at 2026-06-01

はじめに

Nerves では、公式に用意されている Nerves System を使うだけであれば、通常は mix.exs に対象機器用の依存関係を追加するだけで済みます。

しかし、やりたい内容によっては Nerves System を自分でカスタマイズしてビルドしたくなることもあると思います。

この記事では、Nerves Systems Builder を使って、USB カメラ対応のカスタム Nerves System を作ります。

今回の例では Raspberry Pi 5 を対象にします。

  • 対象機器: Raspberry Pi 5
  • System: nerves_system_rpi5
  • 作業場所: ~/nerves_systems
  • Nerves アプリ: ~/Projects/my_nerves_app

同じ考え方は Raspberry Pi 4 など、他の対象機器にも応用できます。

この記事で作るもの

今回は、最小限の変更で USB カメラを認識し、v4l2-ctl で静止画相当のフレームを取得できるところまで確認します。

最終的に有効にする主な設定は以下です。

BR2_PACKAGE_LIBV4L=y
BR2_PACKAGE_LIBV4L_UTILS=y
CONFIG_USB_VIDEO_CLASS=m

まずは小さく始めて、ffmpeg や配信機能は、最初から入れない方針にします。

全体像

Nerves System のビルド作業と、通常の Nerves アプリのビルド作業は分けて考えると理解しやすいです。

~/nerves_systems/
  カスタム Linux / Nerves System をビルドする場所

~/Projects/my_nerves_app/
  そのカスタム System を使って Elixir ファームウェアをビルドする場所

もう少し具体的には、次のような関係です。

+-------------------------+
| ~/nerves_systems        |
|                         |
|  src/nerves_system_rpi5 |  保存すべき System 側の変更
|  o/rpi5                 |  Buildroot の生成物
+------------+------------+
             |
             | source o/rpi5/nerves-env.sh
             v
+---------------------------+
| ~/Projects/my_nerves_app  |
|                           |
|  mix firmware             |  カスタム System を使ってビルド
+---------------------------+

重要なのは、o/rpi5 は生成物であり、長期的に残すべき変更は src/nerves_system_rpi5 側に保存するという点です。

主なディレクトリ

この記事では、次の構成を前提にします。

~/nerves_systems/
├── config/
│   └── config.exs
├── src/
│   └── nerves_system_rpi5/
└── o/
    └── rpi5/

~/Projects/
└── my_nerves_app/

それぞれの意味は以下です。

config/config.exs
  nerves_systems がどの System を clone / build するかを指定する

src/nerves_system_rpi5/
  実際の Nerves System のソースコード
  長期的に残す変更はここに保存し、コミットする

o/rpi5/
  Buildroot の生成物
  make menuconfig や make linux-menuconfig はここで実行する
  必要に応じて削除して再生成できる

~/Projects/my_nerves_app/
  通常の Nerves アプリ
  USB カメラ専用アプリである必要はない

Linux マシンを準備する

Nerves System のビルドには、ある程度の容量と時間が必要です。

目安として、次のような環境を用意します。

  • CPU: x86_64 または aarch64
  • OS: Linux
  • 空き容量: 128 GB 以上

Debian 系の Linux であれば、必要なパッケージはおおむね次のように入れられます。

sudo apt update

sudo apt install \
  git \
  build-essential \
  bc \
  cmake \
  cvs \
  wget \
  curl \
  mercurial \
  python3 \
  python3-aiohttp \
  python3-flake8 \
  python3-ijson \
  python3-nose2 \
  python3-pexpect \
  python3-pip \
  python3-requests \
  rsync \
  subversion \
  unzip \
  gawk \
  jq \
  squashfs-tools \
  libssl-dev \
  automake \
  autoconf \
  libncurses5-dev

Erlang、Elixir、nerves_bootstrap については、通常の Nerves 開発環境と同じです。

mix local.hex
mix local.rebar
mix archive.install hex nerves_bootstrap

nerves_systems を clone する

作業場所は任意ですが、ここではホームディレクトリ配下に nerves_systems を clone します。

cd ~

git clone https://github.com/nerves-project/nerves_systems.git
cd ~/nerves_systems

Raspberry Pi 5 だけをビルド対象にする

設定ファイルを作ります。

cp config/starter-config.exs config/config.exs

config/config.exs を編集して、Raspberry Pi 5 の System だけを残します。

[
  systems: [
    {:nerves_system_rpi5, "https://github.com/nerves-project/nerves_system_rpi5.git"}
  ]
]

Raspberry Pi 4 の場合は、たとえば次のようになります。

[
  systems: [
    {:nerves_system_rpi4, "https://github.com/nerves-project/nerves_system_rpi4.git"}
  ]
]

複数の System を同時に扱うこともできますが、最初は対象を一つに絞る方が分かりやすいです。

一度そのままビルドする

まず、何も変更せずに一度ビルドします。

cd ~/nerves_systems

mix deps.get
mix ns.clone
mix ns.build

完了すると、次のディレクトリができているはずです。

~/nerves_systems/src/nerves_system_rpi5/
~/nerves_systems/o/rpi5/

確認します。

ls ~/nerves_systems/src/nerves_system_rpi5
ls ~/nerves_systems/o/rpi5

カスタム用の branch を作る

生成物は o/rpi5 にありますが、保存すべき System のソースコードは src/nerves_system_rpi5 にあります。

まず、System 側のソースコードで branch を作ります。

cd ~/nerves_systems/src/nerves_system_rpi5

git checkout -b custom-usb-camera

最初の実験では fork しなくても大丈夫です。ローカル branch で十分です。

長期的に使う場合や、チームで共有する場合は、fork するか、自分たち用の System repository を用意するとよさそうです。

Buildroot の設定を変更する

Buildroot の設定変更は、生成物側のディレクトリで行います。

cd ~/nerves_systems/o/rpi5

make menuconfig

image.png

USB カメラの最初の確認では、v4l2-ctl があると便利です。

今回有効にした設定は以下です。

BR2_PACKAGE_LIBV4L=y
BR2_PACKAGE_LIBV4L_UTILS=y

make menuconfig 上では、次のあたりから辿れました。

Target packages
  -> Libraries
    -> Hardware handling
      -> libv4l
        -> v4l-utils tools

Buildroot の版によって表示名が少し違う可能性があります。その場合は / で検索し、LIBV4L または v4l を探します。

make menuconfig の基本操作は以下です。

矢印キー  移動
Enter     下位項目へ移動
Space     有効、無効の切り替え
/         検索
Esc Esc   戻る
Save      一時保存
Exit      終了

終了後、Buildroot の設定を System 側のソースコードへ保存します。

make savedefconfig

差分を確認します。

cd ~/nerves_systems/src/nerves_system_rpi5

git diff nerves_defconfig

期待する差分は、おおむね次のような内容です。

+BR2_PACKAGE_LIBV4L=y
+BR2_PACKAGE_LIBV4L_UTILS=y

Linux kernel の設定を変更する

USB カメラを使うには、Linux kernel 側でも USB Video Class を有効にする必要があります。

こちらも生成物側のディレクトリで作業します。

cd ~/nerves_systems/o/rpi5

make linux-menuconfig

検索で次の語を探します。

USB Video Class
UVC
CONFIG_USB_VIDEO_CLASS

今回の実験では、MEDIA_SUPPORT は既に module として構成されていたため、最小限の有効な変更は次でした。

CONFIG_USB_VIDEO_CLASS=m

make linux-menuconfig 上では、次のあたりから辿れました。

Device Drivers
  -> Multimedia support
    -> Media drivers
      -> Media USB Adapters
        -> USB Video Class (UVC)

ここで次を有効にします。

<M> USB Video Class (UVC)

最初の実験では、古い機種や特定機種向けの USB カメラドライバは有効にしませんでした。

< > GSPCA based webcams
< > USB Philips Cameras
< > USB Sensoray 2255 video capture device
< > USBTV007 video capture support
< > Empia EM28xx USB devices support

まずは UVC 対応の一般的な USB カメラを動かすことに集中します。

終了後、Linux kernel の設定を System 側のソースコードへ保存します。

make linux-update-defconfig

差分を確認します。

cd ~/nerves_systems/src/nerves_system_rpi5

git diff linux-*.defconfig

期待する差分は、おおむね次のような内容です。

+CONFIG_USB_VIDEO_CLASS=m

ファイル名は Linux kernel の版に依存します。

今回の実験では、次のファイルでした。

linux-6.12.defconfig

System 側の変更をコミットする

System 側の repository で差分を確認します。

cd ~/nerves_systems/src/nerves_system_rpi5

git status
git diff

問題なければコミットします。

git add nerves_defconfig linux-*.defconfig
git commit -m "Enable USB camera support"

今回の最小構成では、意味のある変更は次でした。

nerves_defconfig
  BR2_PACKAGE_LIBV4L=y
  BR2_PACKAGE_LIBV4L_UTILS=y

linux-6.12.defconfig
  CONFIG_USB_VIDEO_CLASS=m

rootfs_overlay/ などに追加ファイルを置いた場合は、それも一緒にコミットします。

カスタム System を再ビルドする

保存した設定をもとに、System を再ビルドします。

cd ~/nerves_systems

rm -rf o/rpi5
mix ns.build

学習中は、o/rpi5 を消してから再ビルドする方が分かりやすいです。

Buildroot の生成物が古い状態で残っていると、何が反映されているのか分かりにくくなるためです。

Nerves アプリからカスタム System を使う

ここからは通常の Nerves アプリ側の作業です。

新しい terminal を開いて、Nerves アプリのディレクトリへ移動します。

cd ~/Projects/my_nerves_app

先ほどビルドしたカスタム System の環境変数を読み込みます。

. ~/nerves_systems/o/rpi5/nerves-env.sh
export MIX_TARGET=rpi5

その後、通常通りファームウェアをビルドします。

mix deps.get
mix firmware

カスタム System が使われているか確認します。

mix nerves.info

次のように、systemtoolchain~/nerves_systems/o/rpi5 を指していれば成功です。

system:     /home/mnishiguchi/nerves_systems/o/rpi5
toolchain:  /home/mnishiguchi/nerves_systems/o/rpi5/host

ファームウェアを書き込む

通常の Nerves の流れで書き込みます。

SD カードに書き込む場合の例
mix burn
ネットワーク経由で更新する場合の例
mix firmware.gen.script
./upload.sh nerves.local

使うコマンドは、対象機器やアプリの構成によって変わります。

USB カメラを実機で確認する

ここからは Nerves IEx 上で確認します。

まず、v4l2-ctl が入っているか確認します。

cmd "type v4l2-ctl"

期待する結果は以下の通りです。

v4l2-ctl is /usr/bin/v4l2-ctl

V4L2 device を一覧する

USB カメラを挿した状態で、device を一覧を出します。

cmd "v4l2-ctl --list-devices"

今回の実験では、ELECOM の USB カメラが次のように見えました。

ELECOM 2MP Webcam: ELECOM 2MP W (usb-xhci-hcd.1-1):
/dev/video0
/dev/video1
/dev/media3

Raspberry Pi 5 では、platform 側の video device も多数見えました。

/dev/video19
/dev/video20
...
/dev/video35

USB カメラとして表示された項目の下にある /dev/video0/dev/video1 を確認するのが大事です。

正しい video node を確認する

まず /dev/video0 を確認します。

cmd "v4l2-ctl -d /dev/video0 --info"

今回の実験では、/dev/video0 が実際の映像取得用 node でした。

Driver name      : uvcvideo
Card type        : ELECOM 2MP Webcam: ELECOM 2MP W
Device Caps      : 0x04200001
Video Capture
Streaming
Extended Pix Format

次に /dev/video1 も確認します。

cmd "v4l2-ctl -d /dev/video1 --info"

今回の実験では、/dev/video1 は metadata 用でした。

Device Caps      : 0x04a00000
Metadata Capture
Streaming
Extended Pix Format

したがって、画像取得には次を使いました。

/dev/video0

Elixir から device node を見る場合は、次のようにも確認できます。

cmd "ls /dev/video*"
Path.wildcard("/dev/video*")

カメラの対応形式を確認する

対応形式を確認します。

cmd "v4l2-ctl -d /dev/video0 --list-formats-ext"

今回の ELECOM の USB カメラでは、主に次の形式が使えました。

YUYV  YUYV 4:2:2
MJPG  Motion-JPEG, compressed

解像度は、たとえば次のようなものがありました。

1280x720
800x600
640x480
352x288
1600x1200

最初の確認では、MJPG が便利でした。

USB カメラ側が圧縮済みの JPEG 相当の frame を返してくれるため、最初から ffmpeg を追加しなくても、画像として扱いやすいデータを得られるためです。

raw frame を取得する

まず raw frame を保存してみます。

cmd "v4l2-ctl -d /dev/video0 --stream-mmap --stream-count=1 --stream-to=/data/frame.raw"

保存されたか確認します。

cmd "ls -lh /data/frame.raw"

今回の実験では、次のようなファイルができました。

-rw-------    1 root     root        1.8M Jun  1 11:19 /data/frame.raw

これで、USB カメラから frame を取得できていることが分かります。

MJPEG / JPEG frame を取得する

次に、MJPG 形式で frame を保存します。

cmd "v4l2-ctl -d /dev/video0 --set-fmt-video=width=1280,height=720,pixelformat=MJPG --stream-mmap --stream-count=1 --stream-to=/data/frame.jpg"

保存されたか確認します。

cmd "ls -lh /data/frame.jpg"

今回の実験では、次のようなファイルができました。

-rw-------    1 root     root       61.6K Jun  1 11:21 /data/frame.jpg

これで、USB カメラから JPEG 相当の frame を取得できました。

od が入っていない場合は、Elixir から先頭 bytes を確認できます。

File.read!("/data/frame.jpg")
|> binary_part(0, 4)
|> Base.encode16(case: :lower)

JPEG であれば、通常は次のような先頭になります。

ffd8ff

今回の実験結果

今回の最小構成では、次の設定で USB カメラを使えるところまで確認できました。

BR2_PACKAGE_LIBV4L=y
BR2_PACKAGE_LIBV4L_UTILS=y
CONFIG_USB_VIDEO_CLASS=m

実機では次を確認しました。

v4l2-ctl is /usr/bin/v4l2-ctl
Driver name: uvcvideo
Camera: ELECOM 2MP Webcam
Capture node: /dev/video0
Metadata node: /dev/video1
Raw frame capture: /data/frame.raw
MJPG frame capture: /data/frame.jpg

USB カメラは機種によって対応形式や node の出方が変わります。

そのため、特定の /dev/video0 を決め打ちするよりも、まず v4l2-ctl --list-devicesv4l2-ctl --info で実機の状態を確認するのが良さそうです。

おわりに

Nerves System のカスタムは、最初は少し大げさに見えます。

しかし、今回のように目的を小さくすると、作業の流れはかなり明確になります。

  • Buildroot で userland の道具を入れる
  • Linux kernel で driver を有効にする
  • System 側に設定を保存する
  • Nerves アプリ側で custom System を読み込む
  • 実機で小さく確認する

USB カメラ対応であれば、最初の到達点は「動画再生」や「配信」ではなく、v4l2-ctl で 1 frame 取得するところに置くのが良さそうです。

そこまで確認できれば、その先に画像保存、定期撮影、MJPEG 配信、Livebook 連携などを積み上げていけます。

:tada::tada::tada:

toukon-qiita-macbook_20230912_091808.jpg

4
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?