prremote 0.4.2 → 0.4.4
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 +4 -4
- data/README.md +3 -3
- data/VERSION +1 -1
- data/lib/prremote/commands/install.rb +62 -9
- data/lib/prremote/detector.rb +11 -0
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: a272e63d0fd00f66c93a868352e963daca972681cc4bb8498cf7fb4a52c23b74
|
|
4
|
+
data.tar.gz: 7fdb1514c2c8d017fd0263b72a802e1ba49b088f6d4df411d8e69f90200d26ca
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: aae5795b1205b04d59ee588bb157f23118ab57ce38fbd7a568a19780c68a479f2426db891f37333b689c96e6f6d20ea91e00818b1d5377cf5e997e982194c670
|
|
7
|
+
data.tar.gz: fee308552c72746fb69306510a4f5639fac4a7f040c3237d72587028f70d8c3ec88aec35a838cf24557ed0951fe022eb6eb8a64bd3a209ba34552e7a48c70dd4
|
data/README.md
CHANGED
|
@@ -77,7 +77,7 @@ prremote install -b picow --version 0.1.1 # specify a runtime version
|
|
|
77
77
|
|
|
78
78
|
The firmware is downloaded from GitHub Releases on first use and cached in `~/.prremote/runtime/`. Subsequent installs use the cache.
|
|
79
79
|
|
|
80
|
-
Pico boards:
|
|
80
|
+
Pico boards: if the board is already running the prremote runtime, `install` reboots it into BOOTSEL mode by itself (a 1200 bps touch on its serial port). Otherwise — a fresh board, or one running something else — put it into BOOTSEL mode (hold BOOTSEL, connect USB, release) when prompted. The drive appears as `RPI-RP2` on Pico / Pico W and as `RP2350` on Pico 2.
|
|
81
81
|
|
|
82
82
|
ESP32 (classic): no button dance and no extra tools needed — the firmware is written over the serial port by prremote's pure-Ruby implementation of the Espressif bootloader protocol (the chip is reset into its boot ROM automatically, and the write is verified with an on-chip MD5).
|
|
83
83
|
|
|
@@ -183,8 +183,8 @@ Show the gem version, mrbc version, and the connected device's runtime version.
|
|
|
183
183
|
|
|
184
184
|
```bash
|
|
185
185
|
prremote version
|
|
186
|
-
# prremote: 0.4.
|
|
187
|
-
# runtime: 0.4.
|
|
186
|
+
# prremote: 0.4.4
|
|
187
|
+
# runtime: 0.4.4 board=picow (/dev/tty.usbmodem101)
|
|
188
188
|
# mrbc: mruby 4.0.0 (2026-04-20) (/opt/homebrew/bin/mrbc)
|
|
189
189
|
```
|
|
190
190
|
|
data/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
0.4.
|
|
1
|
+
0.4.4
|
|
@@ -14,6 +14,15 @@ module Prremote
|
|
|
14
14
|
# The UF2 bootloader exposes this file on the BOOTSEL volume.
|
|
15
15
|
BOOTSEL_MARKER = 'INFO_UF2.TXT'.freeze
|
|
16
16
|
|
|
17
|
+
# pico-sdk's stdio_usb reboots into the BOOTSEL boot ROM when the host
|
|
18
|
+
# sets this baud rate (PICO_STDIO_USB_RESET_MAGIC_BAUD_RATE), so a board
|
|
19
|
+
# already running the runtime can be flashed without the button.
|
|
20
|
+
RESET_BAUD = 1200
|
|
21
|
+
|
|
22
|
+
# How long to wait for the BOOTSEL drive after the 1200 bps touch before
|
|
23
|
+
# falling back to the manual instructions.
|
|
24
|
+
AUTO_BOOTSEL_TIMEOUT = 10
|
|
25
|
+
|
|
17
26
|
# Boards whose install path has not been run on real hardware yet.
|
|
18
27
|
UNVERIFIED_BOARDS = %w[pico2].freeze
|
|
19
28
|
|
|
@@ -38,14 +47,7 @@ module Prremote
|
|
|
38
47
|
uf2_path = RuntimeManager.fetch(@version, @board)
|
|
39
48
|
|
|
40
49
|
warn_unverified
|
|
41
|
-
|
|
42
|
-
puts ' 1. Hold the BOOTSEL button'
|
|
43
|
-
puts ' 2. Connect USB (or press RUN while holding BOOTSEL)'
|
|
44
|
-
puts " 3. Release BOOTSEL — #{volume_name} should appear as a USB drive"
|
|
45
|
-
puts
|
|
46
|
-
puts "Waiting for #{volume_name}..."
|
|
47
|
-
|
|
48
|
-
volume = wait_for_volume
|
|
50
|
+
volume = mounted_volume || auto_bootsel || manual_bootsel
|
|
49
51
|
puts "Copying firmware to #{volume}..."
|
|
50
52
|
FileUtils.cp(uf2_path, File.join(volume, File.basename(uf2_path)))
|
|
51
53
|
|
|
@@ -57,6 +59,55 @@ module Prremote
|
|
|
57
59
|
|
|
58
60
|
private
|
|
59
61
|
|
|
62
|
+
# A board already sitting in BOOTSEL needs no reboot.
|
|
63
|
+
def mounted_volume
|
|
64
|
+
volume = volume_paths.find { |p| mounted?(p) }
|
|
65
|
+
puts "Found #{volume_name} at #{volume}." if volume
|
|
66
|
+
volume
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
# Reboots a board running the runtime into BOOTSEL with a 1200 bps
|
|
70
|
+
# touch. nil when there is no Pico port or the drive never shows up
|
|
71
|
+
# (no runtime on the board, a wedged runtime, or a WSL drive that still
|
|
72
|
+
# needs mounting), so the caller falls back to the button.
|
|
73
|
+
def auto_bootsel
|
|
74
|
+
port = @port || Detector.find_pico_port
|
|
75
|
+
return nil unless port
|
|
76
|
+
|
|
77
|
+
puts "Rebooting #{device_label} on #{port} into BOOTSEL mode..."
|
|
78
|
+
touch_reset_baud(port)
|
|
79
|
+
puts "Waiting for #{volume_name}..."
|
|
80
|
+
wait_for_volume(timeout: AUTO_BOOTSEL_TIMEOUT)
|
|
81
|
+
rescue RuntimeError
|
|
82
|
+
puts "#{volume_name} did not appear; falling back to manual BOOTSEL."
|
|
83
|
+
puts
|
|
84
|
+
nil
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
# Opening the port at the magic baud rate is the whole trigger. The board
|
|
88
|
+
# drops off USB right away, so errors while closing are expected.
|
|
89
|
+
def touch_reset_baud(port)
|
|
90
|
+
serial = Serial.new(port, RESET_BAUD)
|
|
91
|
+
rescue RubySerial::Error, SystemCallError => e
|
|
92
|
+
puts "Could not open #{port} at #{RESET_BAUD} bps: #{e.message}" if @verbose
|
|
93
|
+
ensure
|
|
94
|
+
begin
|
|
95
|
+
serial&.close
|
|
96
|
+
rescue RubySerial::Error, SystemCallError
|
|
97
|
+
nil
|
|
98
|
+
end
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
def manual_bootsel
|
|
102
|
+
puts "Put the #{device_label} into BOOTSEL mode:"
|
|
103
|
+
puts ' 1. Hold the BOOTSEL button'
|
|
104
|
+
puts ' 2. Connect USB (or press RUN while holding BOOTSEL)'
|
|
105
|
+
puts " 3. Release BOOTSEL — #{volume_name} should appear as a USB drive"
|
|
106
|
+
puts
|
|
107
|
+
puts "Waiting for #{volume_name}..."
|
|
108
|
+
wait_for_volume
|
|
109
|
+
end
|
|
110
|
+
|
|
60
111
|
# No BOOTSEL dance on ESP32: the flasher toggles DTR/RTS to enter the
|
|
61
112
|
# boot ROM by itself, so flashing works over the normal serial port.
|
|
62
113
|
def install_esp32
|
|
@@ -155,10 +206,12 @@ module Prremote
|
|
|
155
206
|
false
|
|
156
207
|
end
|
|
157
208
|
|
|
209
|
+
# The marker, not the directory, is the test: macOS can leave an empty
|
|
210
|
+
# /Volumes/RPI-RP2 behind after the board reboots off a copy.
|
|
158
211
|
def wait_for_volume(timeout: 60)
|
|
159
212
|
deadline = Time.now + timeout
|
|
160
213
|
loop do
|
|
161
|
-
path = volume_paths.find { |p|
|
|
214
|
+
path = volume_paths.find { |p| mounted?(p) }
|
|
162
215
|
return path if path
|
|
163
216
|
raise "Timed out waiting for #{volume_name} volume (#{timeout}s)" if Time.now > deadline
|
|
164
217
|
|
data/lib/prremote/detector.rb
CHANGED
|
@@ -44,6 +44,17 @@ module Prremote
|
|
|
44
44
|
'`usbipd list`, then `usbipd attach --wsl --busid <BUSID>`.'
|
|
45
45
|
end
|
|
46
46
|
|
|
47
|
+
def self.find_pico_port
|
|
48
|
+
new.find_pico_port
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
# A port whose USB vendor is Raspberry Pi — the only kind the 1200 bps
|
|
52
|
+
# BOOTSEL touch applies to. nil when none (or the vendor can't be read).
|
|
53
|
+
def find_pico_port
|
|
54
|
+
label = KNOWN_VENDORS.dig(R2P2_VENDOR_IDS.first, :label)
|
|
55
|
+
serial_ports.find { |p| port_label(p) == label }
|
|
56
|
+
end
|
|
57
|
+
|
|
47
58
|
def find_device
|
|
48
59
|
candidates = serial_ports
|
|
49
60
|
return candidates.first if candidates.size == 1
|