2025年の単なる夜にキャンペーンで Spresence mini bot を買ってしばらくした後、
定期テストなどが終わってようやく
その後、自宅を少しの間を離れる事に...
このままでは Spresence mini bot 開発が止まってしまう💦
そこで、
以前 cuda 用に買ったjetson orin nano を起動し Spresence に接続して家に放置する。
jetson はVPNに参加させてSSH接続できる体制を整えて jetsonから SpresenceSDK を使い使用することにした。
本ブログの目標は
クライアントPC → TailscaleVPN → Jetson orin nano → Spresence
の体制構築を目指したもの。
1. Jetson > Spresence体制構築
まずは Spresence の mini bot を送られてきた動画(今回はURLは貼らない, 購入後メールの返信でもらえる)を元に組み立てる。
そして、Spresense Arduino スタートガイドをもとに組み立てる。
この時、
1.3. USB ドライバのインストール は Apple Silicon Mac では不要だった。
USBドライバのインストールは Apple Silicon Mac では必ずうまくいかない模様。
ArduinoIDE
→ Spresense Arduino board package のインストール
→ USBシリアルポートの接続
→ Spresense ブートローダーのインストール
まで完了すればSpresense Arduino スタートガイド同様に完了すれば Spresence の PC(ArduinoIDE) → Spresence の開発体制は十分。
本題
次に、Spresenceメインボード を Jetson に繋げる。
Spresenceメインボードの青色LEDが光れば進める
jetsonにSSH接続してUSB接続を確認
ls /dev/ttyUSB*
出力例:
/dev/ttyUSB0
これで接続できている。
Spresence かの確認は
lsusb
出力例
Bus 002 Device 003: ID 1d5c:5001 Fresco Logic USB3.0 Hub
Bus 002 Device 002: ID 0bda:0489 Realtek Semiconductor Corp. 4-Port USB 3.0 Hub
Bus 002 Device 001: ID 1d6b:0003 Linux Foundation 3.0 root hub
Bus 001 Device 003: ID 13d3:3549 IMC Networks Bluetooth Radio
Bus 001 Device 012: ID 10c4:ea60 Silicon Labs CP210x UART Bridge
Bus 001 Device 002: ID 0bda:5489 Realtek Semiconductor Corp. 4-Port USB 2.0 Hub
Bus 001 Device 006: ID 1d5c:5100 Fresco Logic PD3.0 USBillboard
Bus 001 Device 004: ID 1d5c:5011 Fresco Logic USB2.0 Hub
Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
その内
Bus 001 Device 012: ID 10c4:ea60 Silicon Labs CP210x UART Bridge
これがあればOK
こういったコマンドは grep を使って
lsusb | grep -i cp210
結果が
Bus 001 Device 012: ID 10c4:ea60 Silicon Labs CP210x UART Bridge
と実行して見つけるのがスマートで見やすい
これで Jetson から Spresence に対してコマンドの実行などが可能になってくる。
2. Jetson の Tailscale VPN への参加
Tailscaleとは?
WireGuardベースのゼロコンフィグVPN です。
技術的特徴
- プロトコル:WireGuard
- NAT越え(UDP hole punching)
- エンドツーエンド暗号化
- 中央サーバーは「制御面のみ」
- データ通信は原則P2P
軽量高速で設定が簡単でサーバー構築などが不要なこともあり、ITエンジニアが多く使っている。
無料でデバイスが100台まで接続可能。モバイル利用可能。
Tailscaleをクライアントで準備
まずは
ここからアカウントを作る、またはログインする。
クライアントPCのブラウザから自身のアカウントのTailscale環境を見られるようにする。
JetsonにTailscaleを設定,参加
jetsonにTailscaleをインストール Jetson(Ubuntu想定)で実行:
curl -fsSL https://tailscale.com/install.sh | sh
その後
sudo tailscale up
ブラウザ認証 URL が出ます。クリックしてクライアントPCのブラウザに自分のアカウントが表示されていればそのまま connect ボタンを押せば jetson はあなたのTailscale ネットワークに参加する。
Tailscale VPNに参加したjetsonに
systemctl status tailscaled
を実行し実際に参加しているか jetson 側から確認できる。
以下のように active (running) と緑色で出ていれば参加できている。
● tailscaled.service - Tailscale node agent
Loaded: loaded (/lib/systemd/system/tailscaled.service; enabled)
Active: active (running)
Status: "Connected; user@example.com; 100.xxx.xxx.xxx"
Main PID: 222840 (tailscaled)
jetsonの参加は
この Machines の画面にjetsonと同じ ubuntu などの項目が追加されていれば参加確認は完了。
TailscaleはWireGuardベースのオーバーレイVPNのため、既存のローカルネットワーク構成を直接変更しません。
そのためJetsonのTailscale参加後も引き続き同じIPアドレスでSSH接続が可能です。
ただし、--accept-routesやExit Nodeを利用するとルーティングテーブルが変更されるため、通信経路には注意が必要です。
従来のL2TP型VPNのようにデフォルトゲートウェイを強制的に変更する設計ではない点が、大きな違いです。
- JetsonのTailscaleVPN参加
- JetsonにSpresenceメインボードをUSB接続
この2つが完了すればあとは遠隔で jetson から操作する形で開発可能。
3. いざ、 Jetson 上に Spresense 開発環境を構築,操作する
Jetson (Ubuntu) に SSH でログインし、開発用ディレクトリを作成する。
そのディレクトリ配下に Spresense SDK をはじめとする関連リポジトリをクローンし、ビルドおよび書き込み環境を整備する。
まずは jetson に必要なパッケージをインストール
sudo apt update
sudo apt install -y \
git \
build-essential \
bison \
flex \
libncurses5-dev \
libusb-dev \
minicom \
python3 \
python3-pip \
gcc-arm-none-eabi
完了したらまず gcc コンパイラの有無を確認
arm-none-eabi-gcc --version
以下のような結果が出たら OK
arm-none-eabi-gcc (15:10.3-2021.07-4) 10.3.1 20210621 (release)
Copyright (C) 2020 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
Spresense SDK取得
git clone --recursive https://github.com/sonydevworld/spresense.git
※ --recursive オプションは必須(submodule取得)
ディレクトリ構造はこうなる:
workspace/
└── spresense
├── CONTRIBUTING.md
├── Dockerfile
├── LICENSE
├── NOTICE.txt
├── README.md
├── examples
├── externals
├── firmware
├── install-tools.sh
├── nuttx
├── sdk
└── spresense_env.sh
開発環境用の環境変数を定義する
毎回パスを手入力するのは面倒なので、プロジェクトルートを基準に
環境変数をまとめた env.sh を作成する。
#!/bin/bash
export JET2EX_ROOT=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)
export SPRESENSE_SDK=$JET2EX_ROOT/spresense
export MYAPPS=$JET2EX_ROOT/myapps
export APPDIR=$MYAPPS
-
JET2EX_ROOT: プロジェクトのルートディレクトリ -
SPRESENSE_SDK: Spresense SDK の配置場所 -
MYAPPS: 自作アプリケーション用ディレクトリ(今後使用予定) -
APPDIR: SDKビルド時に参照するアプリケーションディレクトリ
現時点では myapps ディレクトリに自作アプリは存在しないが、
将来的に追加する前提で構成だけ先に用意しておく。
workspaceディレクトリで
source env.sh
確認:
echo $SPRESENSE_SDK
正しくパスが表示されればOK。
ビルド前の前提事項
Spresense は組み込み向けボードのため、
Linuxのように「後からパッケージを追加する」ことはできない。
有効化した機能やアプリケーションのみが
ファームウェアにリンクされる。
つまり:
- 使いたいアプリはビルド前に選択する
- 無効な機能は実行時に追加できない
- シェルコマンドもビルド構成次第
この前提を理解した上で、ビルドを行う。
念のため distclean
過去のビルド設定が残っていると不整合が起きることがあるため、
最初にクリーン状態に戻しておく
cd $SPRESENSE_SDK/sdk
make distclean
distclean は以下を削除する:
.config- 生成済みオブジェクト
- 旧ビルド成果物
これにより完全に初期状態へ戻る。
その後に初期化
Spresense SDK では、用途ごとにビルド構成テンプレートが用意されている。
examples/xxx を指定することで、その機能を含む .config が生成される。
tools/config.py examples/hello
tools/config.pyの後に
-
examples/helloを入れれば Spresence の NSH (NuttShell) で Hello World が出せるようになる -
examples/led→ GPIO制御確認用 -
examples/usbmsc→ USB Mass Storage 機能 -
examples/audio_player→ オーディオ再生機能
私は今回 Sony の Spresence HDR カメラボード を買いメインボードに取り付けた。
そのため、カメラ撮影を可能にするために
spresence/sdkディレクトリからターミナルで以下を実行する。
tools/config.py default device/camera examples/camera
ビルド
完了したら spresence/sdk でそのままビルドを実行
make -j$(nproc)
-
make→ ビルド実行 -
-j→ 並列実行オプション -
$(nproc)→ CPUコア数取得
注意点は、稀に
-
依存関係の不整合
-
メモリ不足
-
並列数が多すぎる
で失敗することがある。その場合は
make -j4のように固定値にする。
-
組み込みでも通常は安全
-
問題が出たら並列数を下げて対応
Spresenceに書き込み
./tools/flash.sh -c /dev/ttyUSB0 nuttx.spk
意味は:
-
-c /dev/ttyUSB0→ 書き込みに使用するシリアルポート指定 (ls /dev/ttyUSB*で/dev/ttyUSB0と出ていれば上記のコマンドをそのまま使える。) -
nuttx.spk → ビルド生成されたファームウェアイメージ (
spresense/sdkに nuttx.spk があるのか確認してください。)
実行結果は以下のとおり
[BOX64] Box64 v0.4.1 3717266c3 built on Jan 12 2026 07:30:14
[BOX64] Running on Cortex-A78AE with 6 cores, pagesize: 4096
[BOX64] Will use time-based emulation for RDTSC, even if hardware counters are available
[BOX64] Will use software counter measured at 1.0 GHz emulating 2.0 GHz
[BOX64] Detected 48bits at least of address space
[BOX64] Counted 40 Env var
[BOX64] Library search path:
[BOX64] Binary search path: ./:bin/:/home/maysak/.kiro-server/bin/ead477af76b5b4fc4d4bd12d33c849984a0d1d93/bin/remote-cli/:/home/maysak/.local/bin/:/usr/local/sbin/:/usr/local/bin/:/usr/sbin/:/usr/bin/:/sbin/:/bin/:/usr/games/:/usr/local/games/:/snap/bin/
[BOX64] Looking for /home/maysak/Documents/Jet2exSpresence/spresense/sdk/tools/linux/flash_writer
[BOX64] Running on Cortex-A78AE with 6 cores, pagesize: 4096
[BOX64] Will use time-based emulation for RDTSC, even if hardware counters are available
[BOX64] Will use software counter measured at 1.0 GHz emulating 2.0 GHz
[BOX64] argv[1]="-s"
[BOX64] argv[2]="-c"
[BOX64] argv[3]="/dev/ttyUSB0"
[BOX64] argv[4]="-d"
[BOX64] argv[5]="-b"
[BOX64] argv[6]="115200"
[BOX64] argv[7]="-n"
[BOX64] argv[8]="nuttx.spk"
[BOX64] Rename process to "flash_writer"
[BOX64] Using native(wrapped) libdl.so.2
[BOX64] Using native(wrapped) libz.so.1
[BOX64] Using native(wrapped) libc.so.6
[BOX64] Using native(wrapped) ld-linux-x86-64.so.2
[BOX64] Using native(wrapped) libpthread.so.0
[BOX64] Using native(wrapped) libutil.so.1
[BOX64] Using native(wrapped) librt.so.1
[BOX64] Using native(wrapped) libbsd.so.0
[BOX64] Box64 v0.4.1 3717266c3 built on Jan 12 2026 07:30:14
[BOX64] Running on Cortex-A78AE with 6 cores, pagesize: 4096
[BOX64] Will use time-based emulation for RDTSC, even if hardware counters are available
[BOX64] Will use software counter measured at 1.0 GHz emulating 2.0 GHz
[BOX64] Detected 48bits at least of address space
[BOX64] Counted 42 Env var
[BOX64] Library search path:
[BOX64] Binary search path: ./:bin/:/home/maysak/.kiro-server/bin/ead477af76b5b4fc4d4bd12d33c849984a0d1d93/bin/remote-cli/:/home/maysak/.local/bin/:/usr/local/sbin/:/usr/local/bin/:/usr/sbin/:/usr/bin/:/sbin/:/bin/:/usr/games/:/usr/local/games/:/snap/bin/
[BOX64] Looking for /home/maysak/Documents/Jet2exSpresence/spresense/sdk/tools/linux/flash_writer
[BOX64] Running on Cortex-A78AE with 6 cores, pagesize: 4096
[BOX64] Will use time-based emulation for RDTSC, even if hardware counters are available
[BOX64] Will use software counter measured at 1.0 GHz emulating 2.0 GHz
[BOX64] argv[1]="-s"
[BOX64] argv[2]="-c"
[BOX64] argv[3]="/dev/ttyUSB0"
[BOX64] argv[4]="-d"
[BOX64] argv[5]="-b"
[BOX64] argv[6]="115200"
[BOX64] argv[7]="-n"
[BOX64] argv[8]="nuttx.spk"
[BOX64] Rename process to "flash_writer"
[BOX64] Using native(wrapped) libdl.so.2
[BOX64] Using native(wrapped) libz.so.1
[BOX64] Using native(wrapped) libc.so.6
[BOX64] Using native(wrapped) ld-linux-x86-64.so.2
[BOX64] Using native(wrapped) libpthread.so.0
[BOX64] Using native(wrapped) libutil.so.1
[BOX64] Using native(wrapped) librt.so.1
[BOX64] Using native(wrapped) libbsd.so.0
[BOX64] Using emulated /tmp/_MEI12xyoq/libpython3.5m.so.1.0
[BOX64] Using native(wrapped) libexpat.so.1
[BOX64] Using native(wrapped) libm.so.6
[BOX64] Using emulated /tmp/_MEI12xyoq/_ctypes.cpython-35m-x86_64-linux-gnu.so
[BOX64] Using emulated /tmp/_MEI12xyoq/_bz2.cpython-35m-x86_64-linux-gnu.so
[BOX64] Using native(wrapped) libbz2.so.1
[BOX64] Using emulated /tmp/_MEI12xyoq/_lzma.cpython-35m-x86_64-linux-gnu.so
[BOX64] Using native(wrapped) liblzma.so.5
[BOX64] Using emulated /tmp/_MEI12xyoq/termios.cpython-35m-x86_64-linux-gnu.so
>>> Install files ...
install -b 115200
Install nuttx.spk
|0%-----------------------------50%------------------------------100%|
######################################################################
232432 bytes loaded.
Package validation is OK.
Saving package to "nuttx"
updater# sync
updater# Restarting the board ...
reboot
- nuttx.spk の転送成功
- パッケージ整合性チェック成功
- フラッシュ書き込み成功
- 自動リブート実行
書き込みは 正常終了
Spresenseの NuttShell (NSH) でアプリの実行
JetsonからSpresense の NuttShell (NSH) へ、
ターミナルで以下を実行する。
sudo minicom -D /dev/ttyUSB0 -b 115200
成功すれば NuttShell へ
Welcome to minicom 2.8
OPTIONS: I18n
Port /dev/ttyUSB0, 04:43:39
Press CTRL-A Z for help on special keys
NuttShell (NSH) NuttX-12.3.0
nsh>
nsh> に
hello
実行結果
nsh> hello
Hello, World!!
これで完了
NuttShellの抜け方は Ctrl + A を押してすぐに X を押すと Leave Minicom? と出るので Yes で Enter でjetsonのターミナルに戻る。
Spresenceからカメラ撮影
Spresence HDR カメラボードでのカメラ撮影実行は
カメラボードをメインボードに差す。
参考
事前に exFAT でフォーマットしたSDカードを IoT拡張ボード に差せば画像を撮影した後に保存できるようになる。
nsh> camera
で結果が
nsh> camera
nximage_listener: Connected
nximage_initialize: Screen resolution (320,240)
Take 10 pictures as RGB file in /mnt/sd0 after 5 seconds.
After finishing taking pictures, this app will be finished after 10 seconds.
Expire time is pasted. GoTo next state.
Start capturing...
FILENAME:/mnt/sd0/VIDEO001.RGB
FILENAME:/mnt/sd0/VIDEO002.RGB
FILENAME:/mnt/sd0/VIDEO003.RGB
FILENAME:/mnt/sd0/VIDEO004.RGB
FILENAME:/mnt/sd0/VIDEO005.RGB
FILENAME:/mnt/sd0/VIDEO006.RGB
FILENAME:/mnt/sd0/VIDEO007.RGB
FILENAME:/mnt/sd0/VIDEO008.RGB
FILENAME:/mnt/sd0/VIDEO009.RGB
FILENAME:/mnt/sd0/VIDEO010.RGB
Finished capturing...
Expire time is pasted. GoTo next state.
普通にcameraだけだとRGB形式で10枚連続撮影なので
camera -jpg 1
-
-jpgjpg形式の指定 -
1連続1枚
結果が
nsh> camera -jpg 1
nximage_initialize: Screen resolution (320,240)
Take 1 pictures as JPEG file in /mnt/sd0 after 5 seconds.
After finishing taking pictures, this app will be finished after 10 seconds.
Expire time is pasted. GoTo next state.
Start capturing...
FILENAME:/mnt/sd0/VIDEO004.JPG
Finished capturing...
Expire time is pasted. GoTo next state.
これで撮影されSDカードに保存される
今回はカメラ位置を何度か調整し jetson orin nano を撮影した。
取得された画像が

jetson orin nano のシルエットがなんとなく取得できた。
でも近すぎたかな...
最初の記事はSpresenceを初めていじってみたといった内容。想定外に長くなりました。
カメラから取得,保存された画像をUSB接続経由でjetsonに送ることはできなかった。
SpresenceSDKはSONYから参考ガイドが出ている
これ全部理解できたら低レイヤーのお仕事ができるレベルだそうです...
遠隔開発のためにとんでも無く難しいことに手を出してしまった...(SpresenceSDKがスゴイのは確か)
てか普通に anydesk 使えるPCとか用意してに繋げばここまで難易度がこじれなかったかも...(まあ、すぐにはできないな〜)
今回の記事は物が少ない金欠学生が役立ててください🙇
目指すは Spresence mini bot で microROS を使って自宅内巡回ボットを作ること。
今後もSpresence開発は継続して参ります。
今回の記事の内容は ChatGPT をはじめとしたAIチャットアプリを利用し開発しました。
ここまで読んでいただき、ありがとうございました!

