rpremote 0.1.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ccf42c1aa0428ee4996626537122a820d94aac064a8bd40e15defb4b31c76901
4
- data.tar.gz: f7d7ac9163993a8c8075861bb750ebda0ba9031f9c4393be77be93428eae5d2d
3
+ metadata.gz: 900c268660317e50ca480983f9993698adb35ab699a82d3e0539dfb583e03723
4
+ data.tar.gz: 5e506856f83d7a48f91481cb0ef377df70d4e6f348e922d3412d068c5012fa15
5
5
  SHA512:
6
- metadata.gz: 01e9ec8f0d4df3d3979903eea9c823bbd8224432c9b6ab246737f499ddb096e908fa2a613d92cddbffa7c53dd5163ceaf22e9c1e50c8f3d664ea5aeae3ae1ff3
7
- data.tar.gz: ec02c4c3bb3b1bd28c51b56fb63895d48bac3afcc5c5211308be1f5112d161e175d5710aaa3ebdce24ff31104bc3569a2e49e5af7c3d712903b62444f7abdd12
6
+ metadata.gz: 7fe7454fe617b0a31004b17ca7e38c681711e90d4bb7889a801092fad8089246e7a1a93d89ff48058a320d60488bd825400b0bfded5380bd8f77d5bde8bded45
7
+ data.tar.gz: cd79792554f9174f2c416794f5973a3e5090f9494ea54ce22ca96bd21cbb72ef1dda7188b78eb9d5939f3dc44b8fcbd003bac772d41e7b13c24436bc9315f034
data/CHANGELOG.md CHANGED
@@ -2,23 +2,43 @@
2
2
 
3
3
  All notable changes to this project will be documented in this file.
4
4
 
5
- ## Unreleased
6
-
7
- ## [0.1.0] - 2026-08-24
8
-
9
- - Add `setup`, `build`, and RP2350 BOOTSEL `flash` commands for custom
10
- PicoRuby R2P2 firmware on Raspberry Pi Pico 2 and Pico 2 W.
11
- - Add target configuration for language, language version, board, cache,
12
- firmware path, serial port, and timeouts, with command-line precedence.
13
- - Add `Mrbgems` and `Mrbgems.lock` support for reproducible public and local
14
- mrbgems, including automatic regeneration of PicoRuby build output when a
15
- custom mrbgem changes.
16
- - Add PicoModem DFU staging, status, compatible bytecode compilation, and
17
- A/B-slot rollback support for Ruby and `.mrb` applications.
18
- - Add binary-safe file transfer and remote filesystem commands, plus `run`,
19
- `exec`, `monitor`, `repl`, and reset with reconnect waiting.
5
+ ## 0.3.0 - 2026-08-28
6
+
7
+ - Add `bootsel` to ask supported R2P2 firmware to enter USB BOOTSEL mode without pressing the button.
8
+ - Add `bootsel --reset-flash-memory` to erase Raspberry Pi Pico external flash memory with the official universal erase UF2, whether BOOTSEL is already mounted or entered through R2P2.
9
+ - Have `setup` download Raspberry Pi's official `nuke_universal.uf2` into `firmware/`; `--force` refreshes the downloaded file.
10
+ - Change `deploy FILE` to `deploy PATH`: build and flash mrbgem-enabled firmware, recursively copy `PATH/lib/NAME` to `:/lib/NAME`, then temporarily run `PATH/main.rb`.
11
+ - Add `fs cp --recursive LOCAL_DIR :/REMOTE_DIR` and its `fs push` alias to create missing remote directories and upload a local directory tree in one command.
12
+ - Add `dfu remove` to permanently clear both DFU boot-application slots; a separate `reset` stops an application already running in RAM.
13
+ - Improve `run`: accept a project directory and run its `main.rb`, treat `--timeout` as an idle timeout while output continues, add `--reset-on-timeout`, and print upload, execution, cleanup, and error diagnostics to stderr.
14
+ - Reuse one temporary remote path for `run` and `exec`, so a failed cleanup cannot accumulate temporary files on the device.
15
+ - Read mrbgem `require_name` values from `Mrbgems.lock` and automatically prepend the corresponding `require` calls to `run`, `exec`, and `deploy`; allow `Mrbgems` entries to specify `require:` explicitly.
16
+ - Change `flash` to accept an explicit UF2 only through `--firmware FILE`; positional UF2 arguments are no longer supported.
17
+ - Improve RP2350 BOOTSEL flashing on macOS by accepting the expected `ENXIO` volume-detach race after a successful copy.
18
+ - Add PicoRuby mrbgem examples for BMI270, HC-SR04 temperature, MAX30102, MPU6050, and WS2812 SPI, plus oximeter and Processing project examples.
19
+
20
+ ## 0.2.0 - 2026-08-26
21
+
22
+ - Make `run` and `exec` exit nonzero when compatible R2P2 firmware reports a Ruby exception, while preserving real-time program output.
23
+ - Increase the default command timeout from 10 to 20 seconds.
24
+ - Add a bilingual education example for practical PicoModem DFU application updates, startup confirmation, and A/B-slot rollback.
25
+
26
+ ## 0.1.0 - 2026-08-24
27
+
28
+ - Add `setup`, `build`, and RP2350 BOOTSEL `flash` commands for custom PicoRuby R2P2 firmware on Raspberry Pi Pico 2 and Pico 2 W.
29
+ - Add target configuration for language, language version, board, cache, firmware path, serial port, and timeouts, with command-line precedence.
30
+ - Add `Mrbgems` and `Mrbgems.lock` support for reproducible public and local mrbgems, including automatic regeneration of PicoRuby build output when a custom mrbgem changes.
31
+ - Add PicoModem DFU staging, status, compatible bytecode compilation, and A/B-slot rollback support for Ruby and `.mrb` applications.
32
+ - Add binary-safe file transfer and remote filesystem commands, plus `run`, `exec`, `monitor`, `repl`, and reset with reconnect waiting.
20
33
  - Add macOS R2P2 serial-port detection for Raspberry Pi Pico 2 and Pico 2 W.
21
- - Add bilingual repository, package, configuration, firmware, mrbgem, DFU,
22
- and electronic-craft example documentation.
23
- - Add RBS signatures, automated tests, macOS CI, and release validation with
24
- isolated gem installation and CLI smoke testing.
34
+ - Add bilingual repository, package, configuration, firmware, mrbgem, DFU, and electronic-craft example documentation.
35
+ - Add RBS signatures, automated tests, macOS CI, and release validation with isolated gem installation and CLI smoke testing.
36
+
37
+ ### Limitations
38
+
39
+ - rpremote currently supports PicoRuby only.
40
+ - Supported boards are Raspberry Pi Pico 2 and Pico 2 W; serial commands currently require macOS.
41
+ - `rpremote run` and `exec` are temporary R2P2 executions, not persistent deployments. Use `build` and `flash` to replace firmware.
42
+ - `rpremote deploy` requires PicoRuby 4.x firmware; use `build`, `flash`, `fs push`, and `run` separately with PicoRuby 3.4.x.
43
+ - PicoModem DFU updates only the boot application. Changes to PicoRuby, R2P2, or embedded mrbgems require rebuilding and flashing a UF2.
44
+ - DFU retains the complete application in RAM before writing flash. Verify available memory, bytecode compatibility, boot confirmation, and rollback on target hardware.
data/README.ja.md CHANGED
@@ -1,12 +1,10 @@
1
- # rpremote
1
+ # rpremote CLIリファレンス
2
2
 
3
3
  [English](README.md)
4
4
 
5
- Raspberry Pi Pico向けのカスタムPicoRuby R2P2ファームウェアを準備、ビルド、
6
- 書き込み、操作するためのコマンドラインツールです。
5
+ Raspberry Pi Pico向けのカスタムPicoRuby R2P2ファームウェアを準備、ビルド、書き込み、操作するためのコマンドラインツールです。
7
6
 
8
- 公開mrbgemとローカルmrbgemを再現可能なファームウェアへ組み込み、生成したUF2の
9
- BOOTSEL書き込み、R2P2経由のバイナリ安全なファイル転送とRuby実行を行えます。
7
+ 公開mrbgemとローカルmrbgemを再現可能なファームウェアへ組み込み、生成したUF2のBOOTSEL書き込み、R2P2経由のバイナリ安全なファイル転送とRuby実行を行えます。
10
8
 
11
9
  ## 必要環境
12
10
 
@@ -32,9 +30,15 @@ rpremote flash --mount /Volumes/RP2350
32
30
  rpremote run main.rb
33
31
  ```
34
32
 
35
- 既定では`firmware/picoruby-4.0.3/`にソースを準備し、
36
- `firmware/picoruby-4.0.3-pico2.uf2`を生成します。対象ボードを明示する必要がある
37
- 場合は、`rpremote ports`でR2P2のCDC 0ポートを確認してください。
33
+ `setup`はRaspberry Pi公式のリセット用`nuke_universal.uf2`も`firmware/`へダウンロードします。
34
+
35
+ 再利用するRubyコードを`lib/NAME`へ置くプロジェクトでは、`deploy`がファームウェアをビルドして書き込み、そのディレクトリをR2P2へコピーしてからエントリーファイルを実行します。
36
+
37
+ ```sh
38
+ rpremote deploy path/to/project
39
+ ```
40
+
41
+ 既定では`firmware/picoruby-4.0.3/`にソースを準備し、`firmware/picoruby-4.0.3-pico2.uf2`を生成します。対象ボードを明示する必要がある場合は、`rpremote ports`でR2P2のCDC 0ポートを確認してください。
38
42
 
39
43
  ## mrbgemを追加する
40
44
 
@@ -42,7 +46,6 @@ rpremote run main.rb
42
46
 
43
47
  ```ruby
44
48
  vm :mrubyc
45
-
46
49
  gem github: "ksbmyk/picoruby-ws2812-plus", branch: "main"
47
50
  gem path: "../mrbgems/my-device"
48
51
  ```
@@ -55,21 +58,17 @@ rpremote mrbgems lock
55
58
  rpremote build
56
59
  ```
57
60
 
58
- `Mrbgems.lock`はGitHubのcommitとローカルgemの内容hashを固定します。`build`は
59
- 既存lockを再利用し、`rpremote mrbgems update`だけが新しいcommitを解決します。
61
+ `Mrbgems.lock`はGitHubのコミットとローカルgemの内容ハッシュを固定します。`build`は既存のlockを再利用し、`rpremote mrbgems update`だけが新しいコミットを解決します。
60
62
 
61
63
  ## 対象を選択する
62
64
 
63
65
  ```sh
64
66
  rpremote setup --language picoruby --language-version 3.4.2
65
67
  rpremote build --language picoruby --language-version 3.4.2 --board pico2
66
- rpremote flash --language picoruby --language-version 3.4.2 --board pico2 \
67
- --mount /Volumes/RP2350
68
+ rpremote flash --language picoruby --language-version 3.4.2 --board pico2 --mount /Volumes/RP2350
68
69
  ```
69
70
 
70
- コマンドラインオプションは`config/setting.json`より優先されます。現在実装されて
71
- いるのはPicoRubyです。将来のMicroPythonと追加Picoボード対応に備え、
72
- `language`と`board`はインターフェースに残しています。
71
+ コマンドラインオプションは`config/setting.json`より優先されます。現在実装されているのはPicoRubyです。将来のMicroPythonと追加Picoボード対応に備え、`language`と`board`はインターフェースに残しています。
73
72
 
74
73
  ## コマンド
75
74
 
@@ -78,45 +77,70 @@ rpremote flash --language picoruby --language-version 3.4.2 --board pico2 \
78
77
  | `rpremote setup` | 設定を作成し、言語ソースを準備します。 |
79
78
  | `rpremote build` | mrbgemを含むカスタムUF2をビルドします。 |
80
79
  | `rpremote build clean` | 中間ビルドファイルを削除します。 |
80
+ | `rpremote bootsel` | 実行中のR2P2へBOOTSEL移行を要求し、USBボリュームの出現を待ちます。 |
81
+ | `rpremote deploy PATH` | ファームウェアをビルドして書き込み、存在する場合は`PATH/lib/NAME`を`:/lib/NAME`へコピーしてから`PATH/main.rb`を実行します。ハードウェア出力は次のコマンドまで維持されます。 |
81
82
  | `rpremote dfu app FILE` | PicoModem DFUでRubyまたは版を照合したバイトコードのアプリを更新します。 |
82
83
  | `rpremote dfu compile FILE` | DFU用にPicoRuby版と一致する`.mrb`を生成します。 |
83
84
  | `rpremote dfu status` | DFUのアクティブおよび起動候補スロットを表示します。 |
85
+ | `rpremote dfu remove` | DFU起動アプリを両スロットから削除します。実行中のアプリを停止するには別途リセットします。 |
84
86
  | `rpremote mrbgems …` | mrbgemを検査、表示、固定、更新します。 |
85
- | `rpremote flash` | 選択したUF2をBOOTSEL経由で書き込む。 |
87
+ | `rpremote flash` | 選択したUF2をBOOTSEL経由で書き込みます。 |
88
+ | `rpremote bootsel --reset-flash-memory` | BOOTSELへ移行し、Pico 2の外部フラッシュメモリ全体を消去します。 |
89
+ | `rpremote config show` | 設定ファイルとコマンドラインオプションを反映した実効設定を表示します。 |
86
90
  | `rpremote ports` | R2P2シリアルポートを表示します。 |
87
- | `rpremote run FILE` | Rubyファイルを転送して実行します。 |
88
- | `rpremote exec CODE` | 短いRubyコードを実行します。 |
89
- | `rpremote monitor` / `repl` | 対話型シリアルセッションを開く。 |
90
- | `rpremote reset` | R2P2を再起動して再接続まで待つ。 |
91
- | `rpremote fs cp/cat/ls/rm/mkdir` | R2P2ファイルシステムを操作します。 |
91
+ | `rpremote run FILE` | Rubyファイルを転送して実行します。ディレクトリ指定時は`main.rb`を実行し、出力をリアルタイム表示します。タイムアウトは無出力の継続時間として扱い、Ruby例外時は非0で終了します。`--reset-on-timeout`を指定すると実行タイムアウト後にR2P2をリセットします。 |
92
+ | `rpremote exec CODE` | 短いRubyコードを実行します。Ruby例外時は非0で終了します。 |
93
+ | `rpremote monitor` / `repl` | 対話型シリアルセッションを開きます。 |
94
+ | `rpremote reset` | R2P2を再起動して再接続まで待ちます。 |
95
+ | `rpremote fs cp/push/cat/ls/rm/mkdir` | R2P2ファイルシステムを操作します。 |
96
+
97
+ コマンド一覧は`rpremote --help`で確認できます。コマンド固有の構文、既定値、影響は`rpremote <command> --help`で確認できます。
98
+
99
+ ```sh
100
+ rpremote flash --help
101
+ rpremote dfu app --help
102
+ ```
103
+
104
+ `monitor`と`repl`は`Ctrl-]`で終了します。
105
+
106
+ `run`と`exec`の例外終了コードには、Ruby例外ステータスに対応したR2P2ファームウェアが必要です。[GitHubリポジトリ](https://github.com/ogom/rpremote)のPicoRubyソースから`rpremote build`で生成したUF2が対応しています。
92
107
 
93
- 完全な構文は`rpremote --help`で確認できます。`monitor`と`repl`は`Ctrl-]`で
94
- 終了します。
108
+ ## 操作モデル
109
+
110
+ - `run`と`exec`はRubyコードを一時的に転送・実行して出力を表示し、一時リモートファイルを削除します。対応するR2P2ファームウェアがRuby例外を報告した場合は、非0で終了します。
111
+ - `deploy PATH`はファームウェアをビルドし、BOOTSELへ移行して書き込んだ後、R2P2 Shellの起動完了を待ちます。存在する場合は`PATH/lib/NAME`を`:/lib/NAME`へコピーし、実行直前に読み込んだ`PATH/main.rb`を実行します。Shellジョブを保持するため、ハードウェア出力は次のコマンドまで維持されます。正常終了と出力バイト数を表示し、R2P2のRuby例外はコマンド失敗として扱います。ライブラリディレクトリがない場合、コピー処理はスキップします。PicoRuby 4系のファームウェアが必要です。
112
+ - `flash`はUF2をRP2350 BOOTSELボリュームへコピーし、永続的なR2P2ファームウェアを置き換えます。
113
+ - `dfu app`はRubyソースまたは対応するバイトコードのアプリを非アクティブDFUスロットへ登録します。R2P2を再起動して試行し、正常に起動したアプリは`DFU.confirm`を呼び出します。
114
+ - `dfu remove`はDFUのA/B両アプリスロットを完全に空にします。RAM上ですでに動作しているアプリは`rpremote reset`を実行するまで継続するため、起動アプリのログを混ぜずに`rpremote run`を使う場合は両方のコマンドを実行します。その他の`/home`ファイルとR2P2ファームウェアは削除しません。
115
+ - リモートパスには`:/REMOTE/PATH`を使います。`fs cp`はローカルパスとリモートパスの間で転送します。`fs push LOCAL_DIR :/REMOTE_DIR`は`fs cp --recursive`の別名で、不足しているリモートディレクトリを作成し、ローカルディレクトリの内容を一括転送します。リモートにだけ存在するファイルは削除しません。`fs rm`は指定したリモートパスを完全に削除します。
95
116
 
96
117
  ## ドキュメントとサンプル
97
118
 
98
- [GitHubリポジトリ](https://github.com/ogom/rpremote)に、英語・日本語のガイド、
99
- 設定リファレンス、Mrbgems.lockの解説、カスタムファームウェア手順、電子工作
100
- サンプルがあります。
119
+ [GitHubリポジトリ](https://github.com/ogom/rpremote)に、英語・日本語のガイド、設定リファレンス、Mrbgems.lockの解説、カスタムファームウェア手順、電子工作サンプルがあります。
101
120
 
102
121
  ## 関連プロジェクト
103
122
 
104
- - [mbremote](https://github.com/ogom/mbremote)は、BBC micro:bit向けの
105
- MicroPython/PicoRubyプロジェクトをビルド、書き込み、操作する、同じコンセプトの
106
- ツールです。
123
+ - [mbremote](https://github.com/ogom/mbremote)は、BBC micro:bit向けのMicroPython/PicoRubyプロジェクトをビルド、書き込み、操作する、同じコンセプトのツールです。
107
124
 
108
125
  ## 開発
109
126
 
127
+ 依存gemを導入して、テスト、静的解析、RBSを検証します。
128
+
110
129
  ```sh
111
130
  bundle install
112
131
  bundle exec rake
113
132
  bundle exec rbs -I sig validate
114
133
  ```
115
134
 
116
- 公開前は`bundle exec rake release:check`を実行し、
117
- [RELEASING.md](RELEASING.md)に従ってください。
135
+ 変更したローカル版を試すときは、次を実行します。ネットワークへ接続せずにgemをビルドし、現在のRuby環境へインストールします。
136
+
137
+ ```sh
138
+ bundle exec rake install:local
139
+ rpremote --version
140
+ ```
141
+
142
+ リリースする版は`lib/rpremote/version.rb`と`CHANGELOG.md`を更新します。公開前は`bundle exec rake release:check`を実行し、[RELEASING.md](RELEASING.md)に従ってください。
118
143
 
119
144
  ## ライセンス
120
145
 
121
- [MIT](LICENSE)です。外部ソフトウェアについては
122
- [第三者ソフトウェアに関する通知](THIRD_PARTY_NOTICES.md)を参照してください。
146
+ [MIT](LICENSE)です。外部ソフトウェアについては[第三者ソフトウェアに関する通知](THIRD_PARTY_NOTICES.md)を参照してください。
data/README.md CHANGED
@@ -1,13 +1,10 @@
1
- # rpremote
1
+ # rpremote CLI reference
2
2
 
3
3
  [日本語](README.ja.md)
4
4
 
5
- Command-line tools for preparing, building, flashing, and controlling custom
6
- PicoRuby R2P2 firmware on Raspberry Pi Pico boards.
5
+ Command-line tools for preparing, building, flashing, and controlling custom PicoRuby R2P2 firmware on Raspberry Pi Pico boards.
7
6
 
8
- rpremote embeds public and local mrbgems in reproducible firmware, flashes the
9
- generated UF2 through BOOTSEL, and provides binary-safe file transfer and Ruby
10
- execution through R2P2.
7
+ rpremote embeds public and local mrbgems in reproducible firmware, flashes the generated UF2 through BOOTSEL, and provides binary-safe file transfer and Ruby execution through R2P2.
11
8
 
12
9
  ## Requirements
13
10
 
@@ -33,9 +30,15 @@ rpremote flash --mount /Volumes/RP2350
33
30
  rpremote run main.rb
34
31
  ```
35
32
 
36
- The defaults prepare `firmware/picoruby-4.0.3/` and create
37
- `firmware/picoruby-4.0.3-pico2.uf2`. Use `rpremote ports` to locate the R2P2
38
- CDC 0 port when a board must be selected explicitly.
33
+ `setup` also downloads the official Raspberry Pi `nuke_universal.uf2` reset firmware into `firmware/`.
34
+
35
+ For a project that stores reusable Ruby code in `lib/NAME`, `deploy` builds and flashes the firmware, copies that directory to R2P2, and then runs its entry file:
36
+
37
+ ```sh
38
+ rpremote deploy path/to/project
39
+ ```
40
+
41
+ The defaults prepare `firmware/picoruby-4.0.3/` and create `firmware/picoruby-4.0.3-pico2.uf2`. Use `rpremote ports` to locate the R2P2 CDC 0 port when a board must be selected explicitly.
39
42
 
40
43
  ## Add mrbgems
41
44
 
@@ -43,7 +46,6 @@ Create a project-level `Mrbgems` file. Local paths are relative to that file.
43
46
 
44
47
  ```ruby
45
48
  vm :mrubyc
46
-
47
49
  gem github: "ksbmyk/picoruby-ws2812-plus", branch: "main"
48
50
  gem path: "../mrbgems/my-device"
49
51
  ```
@@ -56,22 +58,17 @@ rpremote mrbgems lock
56
58
  rpremote build
57
59
  ```
58
60
 
59
- `Mrbgems.lock` pins GitHub commits and hashes local gem contents. Existing
60
- locks are reused by `build`; `rpremote mrbgems update` deliberately resolves
61
- new commits.
61
+ `Mrbgems.lock` pins GitHub commits and hashes local gem contents. Existing locks are reused by `build`; `rpremote mrbgems update` deliberately resolves new commits.
62
62
 
63
63
  ## Select a target
64
64
 
65
65
  ```sh
66
66
  rpremote setup --language picoruby --language-version 3.4.2
67
67
  rpremote build --language picoruby --language-version 3.4.2 --board pico2
68
- rpremote flash --language picoruby --language-version 3.4.2 --board pico2 \
69
- --mount /Volumes/RP2350
68
+ rpremote flash --language picoruby --language-version 3.4.2 --board pico2 --mount /Volumes/RP2350
70
69
  ```
71
70
 
72
- Command-line options override `config/setting.json`. PicoRuby is implemented
73
- today; `language` and `board` are retained for planned MicroPython and
74
- additional Pico board support.
71
+ Command-line options override `config/setting.json`. PicoRuby is implemented today; `language` and `board` are retained for planned MicroPython and additional Pico board support.
75
72
 
76
73
  ## Commands
77
74
 
@@ -80,45 +77,71 @@ additional Pico board support.
80
77
  | `rpremote setup` | Create configuration and prepare language sources. |
81
78
  | `rpremote build` | Build a custom UF2 with project mrbgems. |
82
79
  | `rpremote build clean` | Remove intermediate build files. |
80
+ | `rpremote bootsel` | Ask the running R2P2 firmware to enter BOOTSEL and wait for its USB volume. |
81
+ | `rpremote deploy PATH` | Build and flash firmware, copy `PATH/lib/NAME` to `:/lib/NAME` when present, then run `PATH/main.rb` while preserving its hardware output until the next command. |
83
82
  | `rpremote dfu app FILE` | Stage a Ruby or version-checked bytecode app through PicoModem DFU. |
84
83
  | `rpremote dfu compile FILE` | Compile `.rb` to matching PicoRuby bytecode for DFU. |
85
84
  | `rpremote dfu status` | Show the active and candidate DFU slots. |
85
+ | `rpremote dfu remove` | Remove both DFU boot applications; reset separately to stop one already running. |
86
86
  | `rpremote mrbgems …` | Check, list, lock, or update mrbgems. |
87
87
  | `rpremote flash` | Flash the selected UF2 through BOOTSEL. |
88
+ | `rpremote bootsel --reset-flash-memory` | Enter BOOTSEL and erase all Pico 2 external flash memory. |
89
+ | `rpremote config show` | Show the effective configuration after file and command-line options are resolved. |
88
90
  | `rpremote ports` | List detected R2P2 serial ports. |
89
- | `rpremote run FILE` | Upload and run a Ruby file. |
90
- | `rpremote exec CODE` | Run a short Ruby expression. |
91
+ | `rpremote run FILE` | Upload and run a Ruby file, or `main.rb` when FILE is a directory, with real-time output; exit nonzero on a Ruby exception. The timeout measures idle time without output. Use `--reset-on-timeout` to reset R2P2 after a run timeout. |
92
+ | `rpremote exec CODE` | Run short Ruby code and exit nonzero on a Ruby exception. |
91
93
  | `rpremote monitor` / `repl` | Open an interactive serial session. |
92
94
  | `rpremote reset` | Reboot R2P2 and wait for reconnection. |
93
- | `rpremote fs cp/cat/ls/rm/mkdir` | Operate on the R2P2 filesystem. |
95
+ | `rpremote fs cp/push/cat/ls/rm/mkdir` | Operate on the R2P2 filesystem. |
96
+
97
+ Run `rpremote --help` for the complete command list. Run `rpremote <command> --help` for command-specific syntax, defaults, and effects.
98
+
99
+ ```sh
100
+ rpremote flash --help
101
+ rpremote dfu app --help
102
+ ```
103
+
104
+ `monitor` and `repl` exit with `Ctrl-]`.
105
+
106
+ Ruby-exception exit statuses for `run` and `exec` require R2P2 firmware with Ruby exception status support.
107
+ UF2 files built with `rpremote build` from the [GitHub repository](https://github.com/ogom/rpremote) include that support.
94
108
 
95
- Run `rpremote --help` for complete syntax. `monitor` and `repl` exit with
96
- `Ctrl-]`.
109
+ ## Operation model
110
+
111
+ - `run` and `exec` upload Ruby code temporarily, relay output, then remove the temporary remote file. A Ruby exception reported by compatible R2P2 firmware makes the command exit nonzero.
112
+ - `deploy PATH` builds the selected firmware, enters BOOTSEL, flashes it, waits until the R2P2 Shell is ready, copies `PATH/lib/NAME` to `:/lib/NAME` when present, then runs the current contents of `PATH/main.rb`. Its Shell job is retained so hardware output remains active until the next command. It reports successful completion and the output byte count; an R2P2 Ruby exception remains a command failure. If the library directory is absent, the copy step is skipped. It requires PicoRuby 4.x firmware.
113
+ - `flash` copies a UF2 to the RP2350 BOOTSEL volume and replaces persistent R2P2 firmware.
114
+ - `dfu app` stages a Ruby source or matching bytecode app in the inactive DFU slot. Restart R2P2 to try it; a successful app must call `DFU.confirm`.
115
+ - `dfu remove` permanently clears both DFU A/B application slots. The application already loaded in RAM continues until you run `rpremote reset`; use both commands before `rpremote run` when boot-app output would be unwanted. It does not remove other `/home` files or R2P2 firmware.
116
+ - Remote paths use `:/REMOTE/PATH`. `fs cp` transfers between one local and one remote path. `fs push LOCAL_DIR :/REMOTE_DIR` is an alias of `fs cp --recursive`; it creates missing remote directories and uploads the local directory contents. It does not delete remote files. `fs rm` permanently deletes the selected remote path.
97
117
 
98
118
  ## Documentation and examples
99
119
 
100
- The [GitHub repository](https://github.com/ogom/rpremote) contains the full
101
- English and Japanese guides, configuration reference, Mrbgems.lock reference,
102
- custom firmware guide, and electronic-craft examples.
120
+ The [GitHub repository](https://github.com/ogom/rpremote) contains the full English and Japanese guides, configuration reference, Mrbgems.lock reference, custom firmware guide, and electronic-craft examples.
103
121
 
104
122
  ## Related projects
105
123
 
106
- - [mbremote](https://github.com/ogom/mbremote) is a tool with the same concept
107
- for building, flashing, and controlling MicroPython and PicoRuby projects on
108
- BBC micro:bit boards.
124
+ - [mbremote](https://github.com/ogom/mbremote) is a tool with the same concept for building, flashing, and controlling MicroPython and PicoRuby projects on BBC micro:bit boards.
109
125
 
110
126
  ## Development
111
127
 
128
+ Install dependencies, then run the test and static checks:
129
+
112
130
  ```sh
113
131
  bundle install
114
132
  bundle exec rake
115
133
  bundle exec rbs -I sig validate
116
134
  ```
117
135
 
118
- Before publishing, run `bundle exec rake release:check` and follow
119
- [RELEASING.md](RELEASING.md).
136
+ To try the local gem without publishing it, install it into the current Ruby environment:
137
+
138
+ ```sh
139
+ bundle exec rake install:local
140
+ rpremote --version
141
+ ```
142
+
143
+ Before publishing, run `bundle exec rake release:check` and follow [RELEASING.md](RELEASING.md).
120
144
 
121
145
  ## License
122
146
 
123
- [MIT](LICENSE). See [Third-Party Notices](THIRD_PARTY_NOTICES.md) for
124
- externally downloaded software.
147
+ [MIT](LICENSE). See [Third-Party Notices](THIRD_PARTY_NOTICES.md) for externally downloaded software.
data/RELEASING.md CHANGED
@@ -1,7 +1,6 @@
1
1
  # Releasing rpremote
2
2
 
3
- This checklist prepares and publishes one immutable gem version. Run every
4
- command from `packages/rpremote`.
3
+ This checklist prepares and publishes one immutable gem version. Run every command from `packages/rpremote`.
5
4
 
6
5
  ## 1. Prepare the version
7
6
 
@@ -10,45 +9,46 @@ command from `packages/rpremote`.
10
9
  3. Commit all intended files and verify that `git status --short` is empty.
11
10
  4. Run the non-publishing release check:
12
11
 
13
- ```sh
14
- bundle exec rake release:check
15
- ```
12
+ ```sh
13
+ bundle exec rake release:check
14
+ ```
16
15
 
17
- The check runs the specs, RuboCop, and RBS validation; builds the gem; checks
18
- its metadata and packaged documentation; installs it into a temporary isolated
19
- gem directory; and executes the installed `rpremote --version` command.
16
+ The check runs the specs, RuboCop, and RBS validation; builds the gem; checks its metadata and packaged documentation; installs it into a temporary isolated gem directory; and executes the installed `rpremote --version` command.
20
17
 
21
18
  ## 2. Prepare publishing services
22
19
 
23
- The repository must have an `origin` remote before releasing. Confirm it with:
20
+ The repository must have an `origin` remote and GitHub CLI authentication before releasing. Confirm them with:
24
21
 
25
22
  ```sh
26
23
  git remote -v
24
+ gh auth status
27
25
  ```
28
26
 
29
- For the first RubyGems.org release, confirm that the `rpremote` name is
30
- available and configure either RubyGems MFA for a manual push or a pending
31
- trusted publisher for GitHub Actions. Never commit an API key or
32
- `~/.gem/credentials`.
27
+ Choose one publishing route before the first release:
28
+
29
+ - For a manual release, sign in to RubyGems.org, confirm that the account owns `rpremote`, and enable MFA:
30
+
31
+ ```sh
32
+ gem signin
33
+ gem owner rpremote
34
+ ```
35
+
36
+ - For a GitHub Actions trusted publisher, confirm the RubyGems.org trusted-publisher entry names this repository and workflow before starting the release.
37
+
38
+ Never commit an API key or `~/.gem/credentials`.
33
39
 
34
40
  ## 3. Publish deliberately
35
41
 
36
- `bundle exec rake release` is a publishing command: it creates and pushes the
37
- version tag and pushes the built gem to RubyGems.org. Run it only after the
38
- release check passes and the working tree is clean:
42
+ `bundle exec rake release` is a publishing command: it creates and pushes the version tag and pushes the built gem to RubyGems.org. It is not a dry run. Run it only after the release check passes and the working tree is clean:
39
43
 
40
44
  ```sh
41
45
  bundle exec rake release
42
46
  ```
43
47
 
44
- After the tag exists on GitHub, create a GitHub Release and attach the exact
45
- gem that passed validation. `--verify-tag` prevents GitHub CLI from silently
46
- creating a tag at another commit:
48
+ After the tag exists on GitHub, create a GitHub Release and attach the exact gem that passed validation. `--verify-tag` prevents GitHub CLI from silently creating a tag at another commit:
47
49
 
48
50
  ```sh
49
- gh release create v0.1.0 rpremote-0.1.0.gem \
50
- --verify-tag --generate-notes
51
+ gh release create v0.3.0 rpremote-0.3.0.gem --verify-tag --generate-notes
51
52
  ```
52
53
 
53
- Replace `0.1.0` in both places for later versions. Finally, install from
54
- RubyGems.org in a fresh environment and run `rpremote --version`.
54
+ Replace `0.3.0` in both places for later versions. Finally, install from RubyGems.org in a fresh environment and run `rpremote --version`.
@@ -1,8 +1,10 @@
1
1
  # Third-Party Notices
2
2
 
3
- `rpremote` does not bundle R2P2 firmware. The `setup` command downloads a
4
- selected PicoRuby/R2P2 4.0.3 or 3.4.2 release from the PicoRuby project and
5
- verifies its published SHA-256 digest.
3
+ `rpremote` does not bundle R2P2 firmware. The `setup` command downloads a selected PicoRuby/R2P2 4.0.3 or 3.4.2 release from the PicoRuby project and verifies its published SHA-256 digest.
4
+
5
+ `setup` also downloads Raspberry Pi's `nuke_universal.uf2` from the official Pico SDK prebuilts release. It is used only by `rpremote bootsel --reset-flash-memory` to erase external flash memory.
6
+
7
+ Source: <https://github.com/raspberrypi/pico-sdk-prebuilts/releases/latest/download/nuke_universal.uf2>
6
8
 
7
9
  ## PicoRuby and R2P2
8
10
 
@@ -10,20 +12,8 @@ Source: <https://github.com/picoruby/picoruby>
10
12
 
11
13
  Copyright © 2020 HASUMI Hitoshi
12
14
 
13
- Permission is hereby granted, free of charge, to any person obtaining a copy of
14
- this software and associated documentation files (the "Software"), to deal in
15
- the Software without restriction, including without limitation the rights to
16
- use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
17
- of the Software, and to permit persons to whom the Software is furnished to do
18
- so, subject to the following conditions:
19
-
20
- The above copyright notice and this permission notice shall be included in all
21
- copies or substantial portions of the Software.
22
-
23
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
24
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
25
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
26
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
27
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
28
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
29
- SOFTWARE.
15
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
16
+
17
+ The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
18
+
19
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,124 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "optparse"
4
+
5
+ module Rpremote
6
+ class BootselCommand
7
+ SHELL_READY_TIMEOUT = 2.0
8
+ RETRY_INTERVAL = 0.25
9
+ RESET_FLASH_FIRMWARE = File.join("firmware", "nuke_universal.uf2").freeze
10
+
11
+ class Error < Rpremote::Error; end
12
+
13
+ def self.run(args, defaults:, output: $stdout, services: {})
14
+ options = parse_options(args, defaults)
15
+ raise ArgumentError, "bootsel does not accept arguments" unless args.empty?
16
+
17
+ reset_flasher = nil
18
+ if options[:reset_flash_memory]
19
+ flasher = services.fetch(:flasher, Flasher)
20
+ reset_flasher = flasher.new(timeout: options[:timeout])
21
+ target = reset_flasher.find_mounted(options[:mount])
22
+ output.puts("BOOTSEL ready: #{target}") if target
23
+ end
24
+ target ||= enter(**options.slice(:mount, :port, :baud, :timeout), output: output, services: services)
25
+ reset_flash_memory(target, output: output, flasher: reset_flasher) if options[:reset_flash_memory]
26
+ target
27
+ end
28
+
29
+ def self.reset_flash_memory(target, output:, flasher:)
30
+ firmware_path = File.expand_path(RESET_FLASH_FIRMWARE, Dir.pwd)
31
+ raise Error, "reset firmware not found: #{firmware_path}; run `rpremote setup` first" unless File.file?(firmware_path)
32
+
33
+ output.puts("resetting Raspberry Pi Pico 2 external flash memory through #{target}; this erases all stored data and firmware")
34
+ flasher.flash(firmware_path, mount: target, wait_for_port: false)
35
+ output.puts("Pico 2 external flash memory reset; wait for the RP2350 BOOTSEL drive, then run `rpremote flash` to install R2P2")
36
+ end
37
+ private_class_method :reset_flash_memory
38
+
39
+ def self.enter(mount:, port:, baud:, timeout:, output:, services: {})
40
+ serial = services.fetch(:serial, Serial)
41
+ device = services.fetch(:device, Device)
42
+ shell = services.fetch(:shell, Shell)
43
+ flasher = services.fetch(:flasher, Flasher)
44
+ sleeper = services.fetch(:sleeper) { ->(seconds) { sleep(seconds) } }
45
+ clock = services.fetch(:clock) { -> { Process.clock_gettime(Process::CLOCK_MONOTONIC) } }
46
+ deadline = clock.call + timeout
47
+
48
+ request_bootsel(
49
+ port: port, baud: baud, deadline: deadline, output: output,
50
+ serial: serial, device: device, shell: shell, sleeper: sleeper, clock: clock
51
+ )
52
+
53
+ remaining = deadline - clock.call
54
+ raise Error, "timed out waiting for R2P2 Shell to accept a BOOTSEL request" unless remaining.positive?
55
+
56
+ target = flasher.new(timeout: remaining).wait_for_mount(mount: mount)
57
+ output.puts("BOOTSEL ready: #{target}")
58
+ target
59
+ end
60
+
61
+ def self.request_bootsel(context)
62
+ port = context.fetch(:port)
63
+ baud = context.fetch(:baud)
64
+ deadline = context.fetch(:deadline)
65
+ output = context.fetch(:output)
66
+ serial = context.fetch(:serial)
67
+ device = context.fetch(:device)
68
+ shell = context.fetch(:shell)
69
+ sleeper = context.fetch(:sleeper)
70
+ clock = context.fetch(:clock)
71
+ announced = false
72
+
73
+ loop do
74
+ port_path = device.main_port(port)
75
+ unless announced
76
+ output.puts("entering BOOTSEL mode: #{port_path}")
77
+ announced = true
78
+ end
79
+
80
+ requested = false
81
+ serial.open(port_path, baud: baud) do |connection|
82
+ remaining = deadline - clock.call
83
+ raise Shell::TimeoutError, "R2P2 Shell is not ready" unless remaining.positive?
84
+
85
+ remote_shell = shell.new(connection, timeout: [remaining, SHELL_READY_TIMEOUT].min)
86
+ remote_shell.synchronize!
87
+ unless remote_shell.execute("type bootsel").include?("bootsel is")
88
+ raise Error, "connected R2P2 firmware does not support BOOTSEL reset; flash a current rpremote UF2 once while holding BOOTSEL"
89
+ end
90
+
91
+ remote_shell.send_command("bootsel")
92
+ requested = true
93
+ end
94
+ break if requested
95
+ rescue Device::NotFoundError, Serial::ConfigurationError, Shell::TimeoutError, IOError, SystemCallError
96
+ raise Error, "timed out waiting for R2P2 Shell to accept a BOOTSEL request" if clock.call >= deadline
97
+
98
+ sleeper.call(RETRY_INTERVAL)
99
+ end
100
+ end
101
+ private_class_method :request_bootsel
102
+
103
+ def self.parse_options(args, defaults)
104
+ options = {
105
+ mount: defaults[:mount],
106
+ port: defaults[:port],
107
+ baud: defaults.fetch(:baud, Serial::BAUD_RATE),
108
+ timeout: defaults.fetch(:timeout, Flasher::DEFAULT_TIMEOUT)
109
+ }
110
+ OptionParser.new do |parser|
111
+ parser.on("--mount DIR") { |value| options[:mount] = value }
112
+ parser.on("--port PORT") { |value| options[:port] = value }
113
+ parser.on("--baud RATE", Integer) { |value| options[:baud] = value }
114
+ parser.on("--timeout SECONDS", Float) { |value| options[:timeout] = value }
115
+ parser.on("--reset-flash-memory") { options[:reset_flash_memory] = true }
116
+ end.parse!(args)
117
+ raise ArgumentError, "--baud must be positive" unless options[:baud].positive?
118
+ raise ArgumentError, "--timeout must be positive" unless options[:timeout].positive?
119
+
120
+ options
121
+ end
122
+ private_class_method :parse_options
123
+ end
124
+ end