📷️

Spresense で Hello World に苦労した

に公開

Sony Spresense

Sony が出している Spresense というRaspberry Pi Pico とかArduino っぽいサムシングがあります。

Spresense SDK スタートガイド (CLI 版) に従って Hello World してみましたが、結構苦労したのでそのメモ。

え? IDE?知らん。勝手に C/C++ を入れられてVSCodeの環境ぶっ壊されたので速攻でアンインスコしたので……。

本記事は基本的には Spresense SDK スタートガイド (CLI 版) の手順に従って作業します。

環境

  • Windows11
  • WSL2
    • Ubuntu 24.04

レポジトリ

https://github.com/nodamushi/zenn-program/tree/main/src/spresense/hello

手順1: WSL2 で Podman を使う

ちょっと公式手順から脱線します。

使ってみるとわかると思いますが、 Spresens の SDK はなかなかに 💩です。VSCode の IDE は 💩 ですが、こちらもなかなかに香ばしい ~/.spresense_env を排泄します。

私は Spresense の SDK を環境に入れたくありません。というわけで、Podman (Docker) を使って閉じ込めましょう。

WSL2 への Podman のインストール手順は 以前の記事 を参照してください。

Ubuntu 24.04 での導入手順をざっくり書くと以下です。

  1. systemd の有効化
cat <<EOF | sudo tee /etc/wsl.conf
[boot]
systemd=true
EOF
  1. WSL 再起動: Windows の PowerShell から wsl --shutdown を実行→再度 Ubuntu 24.04 を起動
  2. / の shared 化
cat <<EOF | sudo tee /etc/systemd/system/rshared.service
[Unit]
Description=Mount rshared for rootless containers

[Service]
Type=oneshot
RemainAfterExit=yes
ExecStart=/usr/bin/mount --make-rshared /
TimeoutSec=30s

[Install]
WantedBy=multi-user.target
EOF
sudo systemctl enable rshared && sudo systemctl start rshared
  1. インストール
sudo apt install podman

手順2: WSL2 で USB を使う

もうちょっと脱線します。基本的に Podman (Docker) を使いますが、このとき困るのが USB です。

公式の手順に従うと WSL 内から COMx の形でデバイスにアクセス 出来ます。これは結構不思議です。

泣く泣く壊しても良い WSL2 Ubuntu 20.04 環境を用意して確認したところ、なんと OpenOCD の Windwos 版バイナリを入れていました。つまり、 WSL2 から Windows のプログラムを起動する形で COMx によるアクセスを実現してるようです。これは考えたな、と思いました。

でも断ります。💩は嫌です。 コンテナで開発作業できるようにしましょう。

そのためには USB を WSL から使えるようにします。

まずは usbipd-win を Windows にインストールします。

  1. 公式手順(Windows) に従い、CP210x USB to serial driver を入れる
    • v11.1.0 を入れろと書いてありますが、ガン無視して v11.4.0 を入れました。
  2. PowerShell を管理者権限で起動し、Winget で usbpid-win をインストール
winget install usbipd
  1. Powershell で usbipd list を実行してバスID を調べる。(私の環境では 1-8
BUSID  VID:PID    DEVICE                                                        STATE
1-8    10c4:ea60  Silicon Labs CP210x USB to UART Bridge (COM3)                 Not shared
  1. 管理者権限の Powershell で usbipd bind --busid=バスID を実行(バスIDは3で調べたもの. 私の環境では --busid=1-8)
  2. usbipd list を実行して状態を確認
BUSID  VID:PID    DEVICE                                                        STATE
1-8    10c4:ea60  Silicon Labs CP210x USB to UART Bridge (COM3)                 Shared
  1. WSL Ubuntu24.04 を起動する
  2. 管理者権限の PowerShell でusbipd attach --wsl --busid=バスID を実行(バスIDは3で調べたもの. 私の環境では --busid=1-8)
  3. WSLlsusb を実行。
    • lsusb がなければ sudo apt install usbutils でインストール
❯ lsusb
Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
Bus 001 Device 002: ID 10c4:ea60 Silicon Labs CP210x UART Bridge
Bus 002 Device 001: ID 1d6b:0003 Linux Foundation 3.0 root hub

以上で WSL 側から見えるようになりました。

手順3: コンテナ イメージを作成する

さて、ここまでちょっと公式手順から脱線してきましたが、そろそろ公式手順に戻りましょう。

続いて、公式手順から少し脱線し、コンテナのイメージを用意します。

一応公式にも Docker イメージはあるものの、 ファームウェアが入ってません

ファームウェアは単純にはダウンロードできず、同意画面を経る必要があり、入れるのが面倒くさいです。なので、ファームウェア入りのイメージを作成して、プライベートな Docker Registry などで配布したほうがどう考えても楽です。Ubuntu のバージョンが上がるたびにダウンロードし直しとか避けたい。

今回は Spresense の v3.4.2 の開発用イメージを用意しました。ブートローダをダウンロードして container ディレクトリに格納し、

./build_image.sh

でイメージを作成します。

中で開発するために screen とか jq とか cland とか余計なものも入れてます。

そして、 start.sh でコンテナを起動します。 バックグラウンドで起動する場合は -d をつけます。 -d をつけた場合のコンテナ名は dev-spresense です。

./start.sh -d

USB は自動判別します。また、勝手に /dev/ttyUSBx の権限を USER に変えます (sudo chown $USER:$USER /dev/ttyUSBx)。 WSL で問題になることはまぁ多分無いでしょう。

コンテナを立ち上げたら、後は VSCode で開発コンテナに入って作業すれば簡単です。

手順4: プロジェクト(approot)を作成する

さて、いい加減、公式の Hello World の手順に戻りましょう。

公式の手順に従うと、なんのプロジェクトも作成せずにいきなり Hello World が動くという、なんの意味もない手順だったり、アプリケーションも SDK の下とか spresense のgit レポジトリの下に作れとかいう、割と意味不明な解説しか無いので、ガン無視して全部自作していきます。

アプリを作成する前に approot なる、なんていうかプロジェクト的サムシングを作る必要があります。

GitHub に乗せているコードでは既に src が実装済みなのですでにプロジェクト(approot)が存在します。しかし、0からやる場合は起動直後は空っぽの状態です。空っぽの状態から始める場合は、コンテナに入ったら以下を実行します。

spr-create-approot /work

これで、 /work (src)がアプリケーションのルートディレクトリというマークが付きます。で、 ~/.spresense_env という💩にこの情報が記録され、次回以降参照されます。これがどれだけ💩かわかりますか?1ユーザーにつき、Spresense は一つのプロジェクトしか作るなって話ですよ。実験しててなーんかおかしいなって思ってたら、このファイルが他のプロジェクトを参照してたっていうね。

これが悪意じゃなくて何だという。というわけで、コンテナ推奨です。

手順5: Hello World アプリを作成する

ここからは公式の手順に則ってやります。

公式の手順に従うと、 sdk/apps/examples/hello をコピーすれば良いそうです。ふざけてんのかと思いましたマル

で、もうちょっとマシな方法はないのかと調べたら、以下を実行すれば良い様です。

spr-create-app my_hello
rm -rf my_hello/configs

configs は消していいです。Pythonを解析したところ、あってもマジで意味ない。configs を作るなら /work (src) 直下に作らないとなんの意味もありません。消していいです。

コンフィグはSpresense がベースにしてる NuttX の設定で、ようするに kconfig です。

今回のように単に Hello World するなら default を使えばいいし、他のプロパティを追加するなら default にプラスして features から適宜選択していくのが良さそうです。なので、基本いらないので Git を汚す前に消して、必要になったら /work (src) 直下に configs を作れば OK です。

今回は単純に以下で設定は OK

spr-config default

手順5.5: VSCode の clangd で補完を出す方法

/work/compile_flags.txt に以下の内容を書き出しておきましょう。これで VSCode で clangd を使ってるユーザーは補完が出せるようになります。その他必要なコンパイルオプションがあれば適宜追加してください。

-I/spresense/sdk/include
-I/spresense/nuttx/include

ただ、この状態で my_hello/my_hello_main.c を以下のようにしても、 #include <nuttx/config.h>FAR でエラーが表示されると思います。一度ビルドすると直ると思います。

#include <nuttx/config.h>
#include <stdio.h>

int main(int argc, FAR char *argv[])
{
  printf("Hello World!\n");
  return 0;
}

手順6: ビルドする

素直に公式の手順に従い、ビルドします。

うだうだと config に関する説明が書いてありますが、もうここでは以下のコマンドでOKです。

spr-config default は先の説明で既に実施済みならやらなくていいです。また、コンテナから出ても生成ファイル (nuttx.spk) を参照できるように /work/dist ディレクトリにコピーしています。

spr-config default
spr-make -j
mkdir -p /work/dist
cp /spresense/sdk/nuttx.spk /work/dist/nuttx.spk

手順7: 書き込む

公式の手順に従い、作成したバイナリ(nuttx.spk)を書き込みます。

  1. ブートローダを書き込む (一度書いたことあるなら書かなくても良い)
  2. アプリを書き込む
/spresense/sdk/tools/flash.sh -l /spresense/firmware/spresense -c /dev/ttyUSB●
/spresense/sdk/tools/flash.sh -c /dev/ttyUSB● /work/dist/nuttx.spk

コンテナ内では上記コマンドを以下で行えます。(ただのエイリアス)

write-bootloader
write-app

手順8: Hello World を動かす

書き込んだら実際に Spresense のターミナルみたいなものに接続して my_hello を実行してみましょう。

コンテナ内では以下のコマンドで screen でシリアル接続します。ただの screen /dev/ttyUSB● 115200 へのエイリアスです。

serial

入れたら my_hello と入力して ENTER を押すと、以下のように Hello World! と表示されます。

nsh> my_hello
Hello World!
nsh> my_hello
Hello World!
nsh>

まとめ

公式のスタートガイド手順に従って Hello World を出すところまでやってみましたが、たかだか Hello World に思った以上に苦労しました。

もうちょっと、こう、うん、まぁ、ねぇ……

Discussion