macOS での開発#
macOS は本書の標準的な開発環境です。ドライバの導入は要らず、つないだ時点で ポートが現れます。Windows / Linux と同じコマンドで全章をビルド・書き込みできます。
違うのは ポート名が /dev/cu.usbmodem… になることと、
同じボードに /dev/cu. と /dev/tty. の 2 つの名前ができることくらいです。
注釈
このページの出力例は Arduino UNO R4 WiFi の実機を macOS 26.5.1(Apple Silicon / arm64)につないで取ったものです。
1. 環境をつくる#
ターミナルを開く#
このページのコマンドは、すべてターミナルに打ち込みます。
Command + Space で Spotlight を開き、ターミナル(または Terminal)と
打って Enter が一番早いです。アプリケーション › ユーティリティ › ターミナル
からも開けます。
ターミナルでボードを探しているところ。% の右にコマンドを打って Enter します#
ユーザ名@マシン名 ~ % のような行(プロンプト)が出れば準備完了です。
以降、行頭の % は「ここに打つ」という意味なので、% 自体は入力しません。
注釈
プロンプトの記号はシェルによって変わります。macOS の既定は zsh なので % ですが、
bash なら $ です。どちらでも打つ内容は同じです。
uv を入れる#
uv は Python の環境とパッケージをまとめて 面倒みてくれる道具です。PlatformIO をこれで動かします。
curl -LsSf https://astral.sh/uv/install.sh | sh
インストール先は ~/.local/bin です。PATH は、コマンド名だけで実行できるように
OS が探しに行くフォルダの一覧のことです。入れた直後のシェルはまだ PATH を
知らないので、そのまま uv を打つと command not found になります。
ターミナルを開き直すか、次を実行してください。
source ~/.zshrc # bash なら ~/.bash_profile
uv --version
source ~/.local/bin/env でも PATH だけ通せます。
サンプルコードを取ってくる#
git clone https://github.com/iory/learning-os-from-arduino.git
cd learning-os-from-arduino/docs/os-on-arduino/code
uv sync
git が入っていなければ、初回に Xcode のコマンドラインツールの導入を促す
ダイアログが出ます。指示に従って入れてください。
2. ボードを見つける#
USB でつなぐだけです。ドライバの導入は要りません。
uv run pio device list
ただし macOS は Bluetooth 機器なども /dev/cu.* として並べるので、
そのままだと目的のボードが埋もれます。grep で絞ると見つけやすいです。
uv run pio device list | grep -A 3 usbmodem
/dev/cu.usbmodemXXXXXXXXXXXXX
-----------------------------
Hardware ID: USB VID:PID=2341:1002 SER=B43A45B9F1CC LOCATION=2-1
Description: UNO WiFi R4 CMSIS-DAP
grep は「その行だけ取り出す」道具
|(パイプ)は左のコマンドの出力を右のコマンドに渡す記号です。
grep usbmodem は、受け取った出力のうち usbmodem を含む行だけを通します。
-A 3 は「見つかった行の後ろ 3 行も一緒に出す」という意味です
(A は after)。ポート名の下に Hardware ID と Description が続くので、
これを付けないとポート名しか見えません。
Hardware ID が 2341:1002 になっていれば、それが Arduino UNO R4 WiFi です。
cu. と tty. の 2 つが現れます
同じボードに対して、macOS は次の 2 つを作ります。
/dev/cu.usbmodemXXXXXXXXXXXXX ← こちらを使う
/dev/tty.usbmodemXXXXXXXXXXXXX
cu(call-out)はこちらから話しかけるための名前、tty(call-in)は
かかってくるのを待つための名前です。書き込みもモニタも cu. のほうを
使ってください。uv run pio device list に出るのも cu. だけです。
末尾の英数字はボードごとに違うシリアル番号なので、手元では別の文字列に
なります。番号ではなくシリアルなので、抜き挿ししても変わりません
(Linux の ttyACM0 / ttyACM1 のように入れ替わることはありません)。
3. ビルドして書き込む#
uv run pio run -d 01_boot -t upload
Uploading .pio/build/uno_r4_wifi/firmware.bin
Erase flash
Done in 0.001 seconds
Write 35356 bytes to flash (9 pages)
[==============================] 100% (9/9 pages)
Done in 2.229 seconds
========================= [SUCCESS] Took 5.24 seconds =========================
ふつうはポートを指定しなくても見つかります。ほかに USB シリアル機器が
つながっていて誤検出されるときだけ、--upload-port を足してください。
uv run pio run -d 01_boot -t upload --upload-port /dev/cu.usbmodemXXXXXXXXXXXXX
4. シリアルモニタで見る#
uv run pio device monitor -b 115200
抜けるときは Ctrl-C です。
第1章は S を 1 文字送ると出力が始まります。モニタが開いたら S を
打ってください。
なぜ S を待たせているのか
ボードは電源が入った瞬間から全速力で走り出しますが、こちらは書き込みが
終わってからモニタを開くまでに数秒かかります。setup() の中で一度だけ
印字して終わる章では、普通に書くとモニタが開く前に出力が終わってしまい、
何も見えません。しかも UNO R4 WiFi は、シリアルポートを開いても
リセットされません(USB 通信はボード上の ESP32-S3 が受け持っていて、
プログラムが動く RA4M1 とは独立しているためです)。つまりモニタを開き直しても、
最初からやり直してはくれません。
そこで「PC 側の準備ができた」ことをボードに伝えます。S はその合図です。
while (Serial.read() != 'S') { delay(1); }
書籍では 01_boot / 01_boot_vector_dump / 応用編の adv3_heap と
adv4_fs がこの形です。ずっと印字し続ける章(第2章など)には要りません。
=== Boot Sequence Check ===
bss_var (should be 0): 0
data_var (should be 12345): 12345
=== Memory Addresses ===
&bss_var: 0x200000C4
&data_var: 0x20000000
VTOR: 0x20007F00
MSP: 0x20007EC8
If you see this, boot sequence completed!
5. macOS でつまずくところ#
モニタを開いたままだと書き込めない#
ポートを開けるのは一度に 1 つだけです。シリアルモニタ、Arduino IDE、
自分の Python スクリプトのうち、動かしていいのは 1 つです。
書き込む前に Ctrl-C でモニタを閉じてください。
ボードが見つからない#
USB ハブ経由だと失敗することがあります。 Mac 本体に直挿ししてください
ケーブルが充電専用だと、電源は入っても通信できません。データ線のあるものを 使ってください
それでも見つからないときは、RESET を素早く 2 回押してブートローダ(書き込みを 受け付けるためにマイコンに常駐している小さなプログラム)に入れてから、もう一度書き込んでください
/dev/cu. の名前が長い#
シリアル番号が入るので長くなりますが、打つ必要はほとんどありません。
pio は自動で見つけますし、Python からはベンダ ID で探せます(下記)。
6. Python からボードと通信する#
書き込みができるようになったら、PC 側のプログラムからボードと話したくなります。
uv sync の時点で pyserial が入っているので、すぐ書けます。
最小の例#
第5章(05_shell) を書き込んだ状態で試してください。
シェルが動いているので、ps を送るとタスク表が返ってきます。
import time
import serial
import serial.tools.list_ports
# Arduino の USB ベンダ ID。ポート名を書かずに済む
port = next(p.device for p in serial.tools.list_ports.comports() if p.vid == 0x2341)
print("見つかったポート:", port)
ser = serial.Serial(port, 115200, timeout=0.2)
time.sleep(1.0) # 挿した直後は取りこぼすことがあるので少し待つ
ser.reset_input_buffer()
ser.write(b"ps\n")
time.sleep(0.8)
print(ser.read(4096).decode("utf-8", errors="replace").replace("\r\n", "\n"))
ser.close()
上のコードを docs/os-on-arduino/code フォルダに hello_serial.py という名前で保存してから、実行します。
uv run python hello_serial.py
見つかったポート: /dev/cu.usbmodemXXXXXXXXXXXXX
ps
ID NAME STATE CPU%
-- ---- ----- ----
0 idle READY 100%
1 LED1 BLOCKED 0%
2 LED2 BLOCKED 0%
3 Heartbeat BLOCKED 0%
4 Shell RUNNING 0%
>
覚えておくと詰まらない 3 つ#
ポート名を書かない。 上の p.vid == 0x2341 で探す書き方なら、
同じコードが macOS / Windows / Linux のどれでも動きます。
/dev/cu.usbmodem… を直書きすると、別の Mac では動きません。
開いたらすこし待つ。 つないだ直後は取りこぼすことがあります。
1 秒ほど待ってから reset_input_buffer() すると安定します。
ポートは 1 つのプログラムだけ。 モニタを開いたままスクリプトを走らせると、 どちらかが失敗します。
7. 全章をまとめて確認する#
全章を順に書き込んで出力を照合する手順は 手元のボードで全章をまとめて確認する にまとめてあります。macOS でもそのまま使えます。
uv run python scripts/verify_chapters.py