meshtastic 0.0.182 → 0.0.183
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/documentation/README.md +4 -0
- data/documentation/admin-channel.md +6 -6
- data/documentation/admin-config.md +5 -5
- data/documentation/admin-firmware-hex.md +57 -0
- data/documentation/admin-firmware-serial.md +1 -1
- data/documentation/admin-firmware-uf2.md +94 -0
- data/documentation/admin-firmware.md +52 -10
- data/documentation/admin.md +8 -2
- data/documentation/module-config.md +8 -4
- data/documentation/rtttl.md +8 -4
- data/lib/meshtastic/admin/channel.rb +6 -3
- data/lib/meshtastic/admin/config.rb +14 -14
- data/lib/meshtastic/admin/firmware/hex.rb +209 -0
- data/lib/meshtastic/admin/firmware/uf2.rb +166 -0
- data/lib/meshtastic/admin/firmware.rb +55 -13
- data/lib/meshtastic/admin.rb +58 -23
- data/lib/meshtastic/module_config.rb +22 -4
- data/lib/meshtastic/rtttl.rb +22 -4
- data/lib/meshtastic/version.rb +1 -1
- data/spec/lib/meshtastic/admin/channel_spec.rb +23 -9
- data/spec/lib/meshtastic/admin/config_spec.rb +28 -12
- data/spec/lib/meshtastic/admin/firmware/hex_spec.rb +160 -0
- data/spec/lib/meshtastic/admin/firmware/uf2_spec.rb +234 -0
- data/spec/lib/meshtastic/admin/firmware_spec.rb +106 -3
- data/spec/lib/meshtastic/admin_spec.rb +106 -40
- data/spec/lib/meshtastic/module_config_spec.rb +47 -0
- data/spec/lib/meshtastic/rtttl_spec.rb +47 -0
- metadata +7 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: eea2644a6ee8e957223fd803b7b2e944bf0ceb1ca43704da2a58ebdfd6354aff
|
|
4
|
+
data.tar.gz: cf26d06b47b3bf0f7ec8d006dd791c77ce077b9b75e51e23a5b5b447d3906472
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: d1b64a05eba845736dd161a666e98464315fecfa242ccff79bf915711dd88a453cfe438788738070202f5e8d2c07f9e3657e102bfd42a020881c449e2a71ff63
|
|
7
|
+
data.tar.gz: cbd371281dd057d34c6e2d9c8bb697996b0c8e646ab6b475da637c579009cfc7095f6122e5a4a223c17ed358259d3bb701b1ae8bc70e98f7bffa18eb812cb75e
|
data/documentation/README.md
CHANGED
|
@@ -31,6 +31,10 @@ Do not open Serial and Bluetooth to the same radio at once. Always `disconnect`
|
|
|
31
31
|
- [Meshtastic::Admin::Channel](admin-channel.md)
|
|
32
32
|
- [Meshtastic::Admin::Config](admin-config.md)
|
|
33
33
|
- [Meshtastic::Admin::Firmware](admin-firmware.md)
|
|
34
|
+
- [UF2 mounted bootloader volumes](admin-firmware-uf2.md)
|
|
35
|
+
- [Intel HEX through an SWD programmer](admin-firmware-hex.md)
|
|
36
|
+
- [ESP serial ROM installer](admin-firmware-serial.md)
|
|
37
|
+
- [Nordic legacy BLE DFU packages](admin-firmware-nordic.md)
|
|
34
38
|
- [Meshtastic::ModuleConfig](module-config.md)
|
|
35
39
|
- [Meshtastic::Position](position.md)
|
|
36
40
|
- [Meshtastic::Telemetry](telemetry.md)
|
|
@@ -19,15 +19,15 @@ settings = Meshtastic::Admin::Channel.build_settings(
|
|
|
19
19
|
module_settings: { position_precision: 13, is_muted: false }
|
|
20
20
|
)
|
|
21
21
|
Meshtastic::Admin::Channel.set(
|
|
22
|
-
|
|
22
|
+
transport_obj: connection, index: 1, role: :SECONDARY, settings: settings
|
|
23
23
|
)
|
|
24
|
-
Meshtastic::Admin::Channel.get(
|
|
24
|
+
Meshtastic::Admin::Channel.get(transport_obj: connection, index: 1)
|
|
25
25
|
|
|
26
26
|
# Explicitly disable a slot.
|
|
27
|
-
Meshtastic::Admin::Channel.set(
|
|
27
|
+
Meshtastic::Admin::Channel.set(transport_obj: connection, index: 1, role: :DISABLED)
|
|
28
28
|
```
|
|
29
29
|
|
|
30
|
-
Admin transport/routing/authentication options pass through (`
|
|
30
|
+
Admin transport/routing/authentication options pass through (`transport_obj: connection`, `to`, `from`, `session_passkey`, etc.). Here `channel:` means a **Channel protobuf**, not the outgoing mesh transport channel selector; it is removed before delivery. Use the lower-level `Admin.set_channel(channel_settings: protobuf, channel: numeric_index, ...)` when a particular transport channel is necessary.
|
|
31
31
|
|
|
32
32
|
## Channel URLs
|
|
33
33
|
|
|
@@ -45,14 +45,14 @@ url = Meshtastic::Admin::Channel.export_url(
|
|
|
45
45
|
channel_set = Meshtastic::Admin::Channel.import_url(url: url)
|
|
46
46
|
|
|
47
47
|
# Only when replacement of slots from zero is intended:
|
|
48
|
-
Meshtastic::Admin::Channel.apply_url(
|
|
48
|
+
Meshtastic::Admin::Channel.apply_url(transport_obj: connection, url: url)
|
|
49
49
|
```
|
|
50
50
|
|
|
51
51
|
**URLs contain channel keys.** Treat them as credentials: do not log, publish, or send them to third-party QR services. Export does not redact PSKs. A URL carries settings, not original slot indexes/roles: its first entry becomes primary when applied.
|
|
52
52
|
|
|
53
53
|
## Operational limits
|
|
54
54
|
|
|
55
|
-
Writes are submissions, not delivery/persistence acknowledgements. No live hardware was exercised.
|
|
55
|
+
Writes are submissions, not delivery/persistence acknowledgements. No live hardware was exercised. Admin's automatic remote session-key acquisition applies. No automatic write-acknowledgment collection, readback, edit transaction, rollback, or add-only merge is performed. URL application can partially succeed if transport or firmware fails mid-sequence, and changing LoRa/primary settings can disconnect remote administration. For radio use, manage edit transactions and ACK/readback through Admin as appropriate for the firmware; verify slots and LoRa configuration after writing.
|
|
56
56
|
|
|
57
57
|
## Protocol evidence
|
|
58
58
|
|
|
@@ -27,23 +27,23 @@ ModuleConfig is a different protobuf: use `Admin.get_module_config` / `Admin.set
|
|
|
27
27
|
|
|
28
28
|
## Transport and authorization
|
|
29
29
|
|
|
30
|
-
Read/write helpers pass through Admin options, including `
|
|
30
|
+
Read/write helpers pass through Admin options, including `transport_obj: connection`, `to`, `from`, numeric transport `channel`, `want_ack`, `want_response`, `hop_limit`, and `session_passkey`. Use a supported connected transport. Session authentication and remote-node routing follow [Admin](admin.md).
|
|
31
31
|
|
|
32
|
-
A return value means transport submission, **not confirmed persistence**. These methods
|
|
32
|
+
A return value means transport submission, **not confirmed persistence**. These methods inherit Admin's automatic remote session-key acquisition, but do not wait for write acknowledgments or read settings back. Obtain the prior configuration, edit it, write, and request it again to confirm; configuration writes replace a whole section rather than patching only non-default fields. Omitting a field in a Hash can reset that setting to its protobuf default. Firmware version and hardware determine which fields are applied, and writes may reboot/disconnect the node.
|
|
33
33
|
|
|
34
34
|
## Example
|
|
35
35
|
|
|
36
36
|
```ruby
|
|
37
|
-
Meshtastic::Admin::Config.get_lora(
|
|
37
|
+
Meshtastic::Admin::Config.get_lora(transport_obj: connection)
|
|
38
38
|
|
|
39
39
|
# Supply the full desired section; prefer editing the returned protobuf.
|
|
40
40
|
Meshtastic::Admin::Config.set_position(
|
|
41
|
-
|
|
41
|
+
transport_obj: connection,
|
|
42
42
|
position: Meshtastic::Config::PositionConfig.new(position_broadcast_secs: 900)
|
|
43
43
|
)
|
|
44
44
|
|
|
45
45
|
Meshtastic::Admin::Config.set_device_ui(
|
|
46
|
-
|
|
46
|
+
transport_obj: connection,
|
|
47
47
|
device_ui: Meshtastic::DeviceUIConfig.new
|
|
48
48
|
)
|
|
49
49
|
```
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
# Intel HEX over SWD — nRF52840 only
|
|
2
|
+
|
|
3
|
+
`Meshtastic::Admin::Firmware::Hex` validates Intel HEX in native Ruby and programs an explicitly selected **nRF52840** through a separately installed, trusted **OpenOCD 0.12+ executable**. No Python flashing tools are used. Other nRF52 chips, other MCU families, serial HEX upload, and automatic probe/board discovery are unsupported.
|
|
4
|
+
|
|
5
|
+
## Safety and prerequisites
|
|
6
|
+
|
|
7
|
+
Calling `install` is destructive: OpenOCD erases sectors containing image data, writes, verifies, and resets the target. Erasing a sector can remove existing bytes outside the image's populated addresses in that sector. Select the exact firmware for your board, and include any required bootloader/SoftDevice components. A syntactically valid HEX is not authenticated firmware and does not prove board compatibility. UICR records can change bootloader/protection configuration.
|
|
8
|
+
|
|
9
|
+
Supply absolute paths to an installed OpenOCD executable and **trusted local interface and nRF52 target configuration files**. Configurations are executable Tcl, not sandboxed data; never use untrusted downloaded configuration files. They must configure one intended SWD target and its flash banks without independently programming/erasing hardware. Supply its exact OpenOCD target name, normally `nrf52.cpu`, and explicit `expected_chip: :nrf52840`. The implementation selects that target and checks FICR PART (`0x52840`) and flash geometry (256 × 4096 bytes) before issuing the flash write command. This identifies the chip, not the commercial board or probe serial number. Configure probe selection in the trusted interface file when multiple probes exist.
|
|
10
|
+
|
|
11
|
+
A supported probe, correct board-specific SWD wiring/power, permissions, and working OpenOCD configuration are operator prerequisites. Protected/debug-locked targets fail: no automatic recovery, mass erase, protection bypass, or retry is performed. Runtime uses POSIX process groups for timeout cleanup.
|
|
12
|
+
|
|
13
|
+
## API
|
|
14
|
+
|
|
15
|
+
```ruby
|
|
16
|
+
require 'meshtastic/admin/firmware/hex'
|
|
17
|
+
|
|
18
|
+
# Validation only: no subprocess or hardware access.
|
|
19
|
+
metadata = Meshtastic::Admin::Firmware::Hex.validate(
|
|
20
|
+
bytes: File.binread('/absolute/path/firmware-board.hex'),
|
|
21
|
+
expected_chip: :nrf52840
|
|
22
|
+
)
|
|
23
|
+
|
|
24
|
+
# Destructive: run only when intentionally installing onto the selected hardware.
|
|
25
|
+
result = Meshtastic::Admin::Firmware::Hex.install(
|
|
26
|
+
protocol: :swd,
|
|
27
|
+
firmware: '/absolute/path/firmware-board.hex',
|
|
28
|
+
expected_chip: :nrf52840,
|
|
29
|
+
expected_target: 'nrf52.cpu',
|
|
30
|
+
openocd: '/usr/bin/openocd',
|
|
31
|
+
interface_config: '/absolute/path/trusted-interface.cfg',
|
|
32
|
+
target_config: '/absolute/path/trusted-nrf52.cfg',
|
|
33
|
+
timeout: 120
|
|
34
|
+
)
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
Use exactly one of `firmware:` (regular file) or `bytes:` (String). Input is limited to 16 MiB. `timeout` is positive finite seconds, at most 3600 (default 120). Unknown installation options are rejected. `Hex.install` does not consume a `format:` option; the parent firmware dispatcher selects `format: :hex` and forwards backend options.
|
|
38
|
+
|
|
39
|
+
Validation checks every record's ASCII hexadecimal syntax, byte count and checksum; requires exactly one terminal EOF and nonempty data; rejects blank lines, trailing records, unknown types, empty data records, 64 KiB record crossings, and overlapping physical address ranges. Types 00/01/02/04 implement data, EOF, segment and linear addressing. Types 03/05 validate a single optional start address within flash; start metadata is not used to override the reset vector. Data may occupy internal flash `[0, 0x100000)` or UICR `[0x10001000, 0x10002000)` only. LF and CRLF are accepted. `validate` returns populated byte count, sorted exclusive-end address ranges and optional start address.
|
|
40
|
+
|
|
41
|
+
The installer snapshots validated bytes in a private temporary HEX file. It launches OpenOCD with separate argv entries, never a shell command; paths embedded in Tcl are escaped including braces, quotes, backslashes and substitution characters. It disables normal GDB/Telnet/Tcl listener ports before loading configuration. Its guarded script executes:
|
|
42
|
+
|
|
43
|
+
1. `init`, target selection, `reset init`, `halt`;
|
|
44
|
+
2. FICR chip/geometry checks;
|
|
45
|
+
3. `flash write_image erase <snapshot> 0 ihex`;
|
|
46
|
+
4. `verify_image <snapshot> 0 ihex`;
|
|
47
|
+
5. `reset run`, then a unique completion marker and `shutdown`.
|
|
48
|
+
|
|
49
|
+
Any script error invokes `shutdown error`. Both successful process termination and the exact post-verification/reset marker are required. Missing/nonexecutable dependencies and failed, incomplete or timed-out subprocesses raise; uncertain flash state is never automatically retried. Temporary files are removed. Returned success is `status: :verified`, `flash_verified: true`, **`reboot_verified: false`**, with image SHA-256, image byte count, chip/target and validation metadata. Reset command completion is not evidence of a healthy Meshtastic application; perform a separate fresh PhoneAPI health/version check.
|
|
50
|
+
|
|
51
|
+
## Official workflow and limits
|
|
52
|
+
|
|
53
|
+
The [official Meshtastic nRF52 SWD guide](https://meshtastic.org/docs/getting-started/flashing-firmware/nrf52/swdio/) describes an external SWD probe, OpenOCD interface configuration, `transport select swd`, `target/nrf52.cfg`, HEX firmware, and erase/program/verify/reset commands. Its recovery example uses `nrf5 mass_erase`; this narrower installer deliberately **does not mass-erase**. Recovery requiring full erase remains an explicit separate operator workflow.
|
|
54
|
+
|
|
55
|
+
[OpenOCD flash programming](https://openocd.org/doc/html/Flash-Programming.html) documents the reset-init, flash-write, verify-image and reset-run sequence. [OpenOCD general commands](https://openocd.org/doc/html/General-Commands.html) documents failure exit via `shutdown error`.
|
|
56
|
+
|
|
57
|
+
All five `.hex` files in the official `firmware-nrf52840-2.7.26.54e0d8d.zip` [release](https://github.com/meshtastic/firmware/releases/tag/v2.7.26.54e0d8d) were validated locally, including `firmware-wio-sdk-wm1110-2.7.26.54e0d8d.merged.hex` and four RAK4631 variants. This is parser compatibility evidence, not hardware installation evidence. Tests exercise actual fake executable processes and Tcl command simulation (test dependency: `tclsh`); no development test connects to or erases hardware. No claim is made that every Meshtastic board or every historical/future HEX variant is supported.
|
|
@@ -47,7 +47,7 @@ Supply exactly one of `firmware:` (path) or `bytes:` (binary String). `protocol:
|
|
|
47
47
|
is optional on the direct backend and defaults to `:esp_rom`. Unknown options
|
|
48
48
|
raise `ArgumentError` before opening the serial device.
|
|
49
49
|
|
|
50
|
-
- `port:` is a required dedicated serial device path, **not** `
|
|
50
|
+
- `port:` is a required dedicated serial device path, **not** `transport_obj:` from
|
|
51
51
|
an active Meshtastic PhoneAPI connection. Close that connection and stop its
|
|
52
52
|
reader before installing. Never flash while another process uses this port.
|
|
53
53
|
- `chip:` is required and must match both the image header and the connected ROM.
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
# UF2 mass-storage firmware submission
|
|
2
|
+
|
|
3
|
+
`Meshtastic::Admin::Firmware::UF2.install(opts)` validates an original UF2 image and copies it to an **explicitly selected bootloader directory**. It does not discover drives, mount filesystems, enter bootloader mode, convert `.bin` files, erase devices, or verify installed flash. Implementation and filesystem safety are currently supported on **Linux with procfs and `O_NOFOLLOW`**; other platforms fail closed rather than use an unpinned destination path.
|
|
4
|
+
|
|
5
|
+
## Prepare the device yourself
|
|
6
|
+
|
|
7
|
+
The official Meshtastic workflow is to download and extract the firmware for the **exact board**, then:
|
|
8
|
+
|
|
9
|
+
- **nRF52840:** double-click reset to enter the USB UF2 bootloader. The bootloader drive exposes `INFO_UF2.TXT`, usually alongside `CURRENT.UF2` and `INDEX.HTM`.
|
|
10
|
+
- **RP2040:** hold BOOTSEL while attaching USB. The drive exposes `INFO_UF2.TXT` and `INDEX.HTM`.
|
|
11
|
+
|
|
12
|
+
Read `INFO_UF2.TXT`, select its mounted directory yourself, and supply its exact `Board-ID`. Do not select a normal disk or copy identification files there. The marker is the UF2-standard identification mechanism, **not cryptographic device authentication or proof of an OS mount**. A directory deliberately populated with a matching marker can pass validation (including the temporary directories used by tests). Run as a normal user, and do not let untrusted users modify the selected directory.
|
|
13
|
+
|
|
14
|
+
## API
|
|
15
|
+
|
|
16
|
+
```ruby
|
|
17
|
+
require 'meshtastic/admin/firmware/uf2'
|
|
18
|
+
|
|
19
|
+
result = Meshtastic::Admin::Firmware::UF2.install(
|
|
20
|
+
protocol: :uf2,
|
|
21
|
+
firmware: '/absolute/path/firmware-for-your-board.uf2',
|
|
22
|
+
mount: '/media/operator/BOOTLOADER',
|
|
23
|
+
family_id: 0xada52840,
|
|
24
|
+
board_id: 'nRF52840-YourBoard-v1' # replace with the exact observed Board-ID
|
|
25
|
+
)
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
Options:
|
|
29
|
+
|
|
30
|
+
| Key | Contract |
|
|
31
|
+
| --- | --- |
|
|
32
|
+
| `protocol` | Required, exactly `:uf2`; no extension-based dispatch. |
|
|
33
|
+
| `firmware` | Regular source file, mutually exclusive with `bytes`. Contents, not filename extension, determine validity. |
|
|
34
|
+
| `bytes` | Complete original UF2 binary String, mutually exclusive with `firmware`. Snapshotted before validation/copy. |
|
|
35
|
+
| `mount` | Required existing canonical absolute directory; no root directory, symlink components, `..`, automatic discovery or directory creation. |
|
|
36
|
+
| `family_id` | Required Integer: `0xada52840` (nRF52840) or `0xe48bff56` (RP2040). |
|
|
37
|
+
| `board_id` | Required exact case-sensitive `Board-ID` in `INFO_UF2.TXT`. nRF52840 IDs must begin with `nRF52840-` (prefix case-insensitive); RP2040 must identify as `RPI-RP2`. |
|
|
38
|
+
| `flash_size` | Required **only for RP2040**, actual installed flash capacity in bytes, positive 4096-byte multiple from 4096 through 16 MiB. Check the board documentation; the generic RP2040 marker cannot report physical capacity. Rejected for nRF52840. |
|
|
39
|
+
|
|
40
|
+
RP2040 example options: `family_id: 0xe48bff56, board_id: 'RPI-RP2', flash_size: 2 * 1024 * 1024` for a board actually fitted with 2 MiB flash.
|
|
41
|
+
|
|
42
|
+
Unknown options are rejected. Reboot and PhoneAPI verification belong to the parent `Firmware.install` orchestration, not this backend.
|
|
43
|
+
|
|
44
|
+
## Accepted format and address policy
|
|
45
|
+
|
|
46
|
+
This is a deliberately narrow installer, not a universal UF2 interpreter:
|
|
47
|
+
|
|
48
|
+
- Source limit: 32 MiB, nonempty and an exact multiple of 512 bytes.
|
|
49
|
+
- Every block must have both start magic words and the end magic word, little-endian fields, and flags **exactly `0x2000`** (family present, main flash).
|
|
50
|
+
- Every block family must match the explicitly selected supported MCU. Concatenated mixed-family UF2 files are rejected even though the general UF2 format permits them.
|
|
51
|
+
- Payloads must be 256 bytes and target addresses 256-byte aligned, the supported bootloader profile (stricter than the generic UF2 format's four-byte alignment).
|
|
52
|
+
- Every block count must equal the physical file block count. Block numbers must cover `0...count` exactly once. Duplicates (even identical), missing numbers, inconsistent totals and overlapping address ranges are rejected. Out-of-order blocks and address holes are allowed and copied unchanged.
|
|
53
|
+
- **nRF52840:** conservative application-only address window `[0x27000, 0xf4000)`, excluding MBR/SoftDevice and bootloader/UICR space. This supports the S140-v7 application layout; old S140-v6 images beginning at `0x26000`, merged SoftDevice images and custom flash layouts are deliberately unsupported. Address validation alone cannot prove application/SoftDevice compatibility. Select the board's matching release and bootloader.
|
|
54
|
+
- **RP2040:** XIP flash only, `[0x10000000, 0x10000000 + flash_size)`. RAM downloads are unsupported.
|
|
55
|
+
- Non-main-flash metadata, file containers, MD5 descriptors, extension tags, reserved flags, other MCUs (including RP2350), and separately identified bootloader-update families are rejected. Raw BIN/HEX/ZIP data is never converted or reinterpreted as UF2. This API does not implement factory erase; an erase program encoded as an otherwise ordinary application image cannot be distinguished by structural checks, so only submit trusted firmware chosen for this board.
|
|
56
|
+
|
|
57
|
+
The UF2 family identifies the MCU, **not the precise board/pinout**. INFO target checks cannot prove that a family-only image was built for that exact board; correct release selection remains the operator's responsibility. Structural validation and the returned local SHA-256 are not authenticity or device-flash verification.
|
|
58
|
+
|
|
59
|
+
## Copy and result semantics
|
|
60
|
+
|
|
61
|
+
The installer checks one case-insensitive `INFO_UF2.TXT` filename, a bounded regular nonsymlink marker, the bootloader header, an unambiguous exact Board-ID and matching MCU. It pins the selected directory with an open file descriptor and writes relative to `/proc/self/fd/<fd>` so unplugging, replacing or unmounting the selected path cannot redirect firmware into the underlying host directory.
|
|
62
|
+
|
|
63
|
+
It creates only `FIRMWARE.UF2`, using exclusive creation and no symlink following. Existing files or symlinks are never overwritten. All image and target checks occur before destination creation. It writes the original UF2 bytes without padding, address relocation or reordering, flushes, calls `fsync`, and closes the file. A successful result contains:
|
|
64
|
+
|
|
65
|
+
```ruby
|
|
66
|
+
{
|
|
67
|
+
status: :copied, protocol: :uf2,
|
|
68
|
+
bytes: 512, # actual source byte count, not payload count
|
|
69
|
+
sha256: '...', # digest of the submitted file
|
|
70
|
+
family_id: 0xada52840,
|
|
71
|
+
board_id: 'nRF52840-YourBoard-v1',
|
|
72
|
+
destination: '/media/operator/BOOTLOADER/FIRMWARE.UF2',
|
|
73
|
+
flash_verified: false,
|
|
74
|
+
reboot_verified: false
|
|
75
|
+
}
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
`:copied` means host filesystem submission completed, **not that the bootloader accepted or flashed it**. UF2 mass storage has no universal flash-acknowledgment protocol. Readback of the created virtual file would not establish flash integrity, and the bootloader may disappear/reboot while the host closes or syncs the file. Any write, flush, sync or close error propagates; no disconnect is converted into success and no transfer is automatically retried. A partial file is left alone because deleting/retrying on a disappeared mount could act on the wrong filesystem or replay a partly flashed image. Inspect the device and re-enter its bootloader manually before deciding what to do next.
|
|
79
|
+
|
|
80
|
+
## Verification and limitations
|
|
81
|
+
|
|
82
|
+
RSpec uses actual temporary source files and bootloader directories, including symlinks/FIFOs, valid nRF52840/RP2040 images, invalid magic, truncation, flags, family/count/address errors, duplicate blocks, destination preservation and readback of the copied bytes. Mount replacement and disconnect-at-sync are injected around real filesystem operations. No USB device, physical flash, bootloader reboot, or on-device application health has been tested.
|
|
83
|
+
|
|
84
|
+
```sh
|
|
85
|
+
bundle exec rspec spec/lib/meshtastic/admin/firmware/uf2_spec.rb spec/conventions_spec.rb
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
## Sources
|
|
89
|
+
|
|
90
|
+
- [Microsoft UF2 specification](https://github.com/microsoft/uf2/blob/master/README.md): block layout, flags, alignment, family semantics and INFO identification.
|
|
91
|
+
- [Official UF2 family identifiers](https://github.com/microsoft/uf2/blob/master/utils/uf2families.json).
|
|
92
|
+
- [Meshtastic drag-and-drop nRF52/RP2040 workflow](https://meshtastic.org/docs/getting-started/flashing-firmware/nrf52/drag-n-drop/).
|
|
93
|
+
- [Adafruit nRF52 bootloader documentation](https://github.com/adafruit/Adafruit_nRF52_Bootloader): application start addresses, bootloader entry and separate bootloader family.
|
|
94
|
+
- [Adafruit UF2 bootloader implementation](https://github.com/adafruit/Adafruit_nRF52_Bootloader/blob/master/src/usb/uf2/ghostfat.c): 256-byte payload/address checks, family dispatch and INFO fields.
|
|
@@ -7,11 +7,12 @@
|
|
|
7
7
|
| Operation | What the Ruby implementation actually does |
|
|
8
8
|
| --- | --- |
|
|
9
9
|
| `sha256(firmware: ... \| bytes: ...)` | Returns a raw 32-byte SHA-256 digest. Exactly one nonempty image source is required. |
|
|
10
|
-
| `request_ota(...)` | Sends the real ESP32 `OTAEvent` admin request: raw SHA-256 plus
|
|
10
|
+
| `request_ota(...)` | Sends the real ESP32 `OTAEvent` admin request: raw SHA-256 plus an explicit `transfer: :ble` or `transfer: :wifi` (or the documented handle inference below). This pins the image hash and requests a reboot into an **already installed compatible loader**; it does not upload firmware or prove that the loader started. |
|
|
11
11
|
| `install(protocol: :unified_wifi, host: ..., firmware: ... \| bytes: ...)` | Separate ESP32 unified-loader TCP protocol, normally port 3232. Requires the image hash to have been provisioned using `request_ota`. |
|
|
12
12
|
| `install(protocol: :unified_ble, address: ..., firmware: ... \| bytes: ...)` | ESP32 unified-loader custom GATT protocol, with a native Ruby BlueZ backend and application ACK flow control. Not the old BLE-only firmware-ota protocol. |
|
|
13
13
|
| `install(protocol: :nordic_dfu, address: ..., package: ...)` | Adafruit SDK11 legacy Nordic BLE DFU for application-only legacy ZIP packages. [Exact scope and options](admin-firmware-nordic.md). Not Nordic Secure DFU or UF2. |
|
|
14
14
|
| `install(protocol: :esp_rom, ...)` | Native ESP ROM serial flashing with explicit chip, flash geometry and offset, ROM acknowledgements and flash MD5 verification. [Exact scope and options](admin-firmware-serial.md). |
|
|
15
|
+
| `install(protocol: :uf2, format: :uf2, ...)` | Validate and copy a UF2 image to an explicitly selected mounted bootloader volume. Copy completion is not flash or boot confirmation. See `UF2.help` for volume and image validation options. |
|
|
15
16
|
| `verify_reboot(...)` or `install(..., verify: {...})` | Fresh application connection, matching configuration handshake and a new request-ID/source-correlated Admin device-metadata reply. Checks exact firmware version and optional node identity. |
|
|
16
17
|
| `enter_dfu(...)` | Sends `enter_dfu_mode_request`; current upstream handles entry on nRF52/RP2040. It neither transfers a DFU package nor copies a UF2 image. |
|
|
17
18
|
| `reboot_ota`, `xmodem_blocks`, `send_xmodem` | Raise `NotImplementedError`. The legacy reboot field has no handler in the inspected firmware; XModem is filesystem transfer, not firmware installation. |
|
|
@@ -19,6 +20,46 @@
|
|
|
19
20
|
|
|
20
21
|
Admin commands accept the transport, addressing, and authentication options documented in [Admin](admin.md). Use exactly one transport. Successful submission is **not confirmation that hardware supports or performed the operation**. A routing acknowledgement alone cannot prove an ESP32 OTA loader/partition exists. No automatic board detection is performed.
|
|
21
22
|
|
|
23
|
+
## Control connection, transfer protocol, and image format
|
|
24
|
+
|
|
25
|
+
These are separate choices:
|
|
26
|
+
|
|
27
|
+
- **Control connection** (`transport_obj: connection`, the actual connected Serial, Bluetooth, TCP, or MQTT handle) carries an authenticated Admin preparation request to the running application. It does not carry the subsequent firmware bytes.
|
|
28
|
+
- **OTA transfer** (`request_ota(transfer: :wifi | :ble)`) chooses the ESP32 loader mode after reboot. A serial control request with `transfer: :wifi` is intentional: USB carries the request, then a separate WiFi loader endpoint receives the image. There is no `transfer: :serial` or `:mqtt` unified updater.
|
|
29
|
+
- **Installer protocol** (`install(protocol: ...)`) selects the actual data endpoint and wire protocol, not a PhoneAPI handle. Choose the matching unified WiFi/BLE loader, ESP serial ROM, legacy Nordic BLE DFU, or UF2 mounted-volume writer explicitly.
|
|
30
|
+
- **Image format** (`install(format: :bin | :zip | :uf2)`) describes the artifact. It defaults to the selected protocol's format for compatibility, not to the filename. It never silently selects a different protocol.
|
|
31
|
+
|
|
32
|
+
`request_ota` no longer silently defaults to BLE. With exactly one non-nil control handle, a Bluetooth handle infers `transfer: :ble` and a TCP handle infers `transfer: :wifi` as conveniences; these are not capability detection. Serial or MQTT handles require an explicit transfer or valid legacy `mode:`. A connected handle is required for the Admin request. Explicit transfer overrides handle inference (for example, a Bluetooth preparation request can select a WiFi loader). `mode: :OTA_WIFI` / `:OTA_BLE` remains supported; if both `mode` and `transfer` are supplied they must agree. Supply only `transport_obj:`; legacy transport-specific Admin keywords and ambiguous multi-transport handles are rejected.
|
|
33
|
+
|
|
34
|
+
### Supported artifact inventory
|
|
35
|
+
|
|
36
|
+
| Format | Installer protocol | Scope |
|
|
37
|
+
| --- | --- | --- |
|
|
38
|
+
| `:bin` | `:unified_wifi`, `:unified_ble` | Matching ESP32 application-update binary; not a merged factory image or bootloader. |
|
|
39
|
+
| `:bin` | `:esp_rom` | ESP image with explicit supported chip, flash geometry and offset; see the serial installer documentation. |
|
|
40
|
+
| `:zip` | `:nordic_dfu` | Application-only legacy Adafruit SDK11 Nordic DFU ZIP supplied through `package:` or `package_bytes:`; not any arbitrary ZIP or Secure DFU bundle. |
|
|
41
|
+
| `:uf2` | `:uf2` | UF2 block image supplied through `firmware:` or `bytes:` to the separately selected mounted bootloader volume. |
|
|
42
|
+
| `:hex` | `:swd` | nRF52840 Intel HEX through an explicitly configured OpenOCD/SWD programmer; [options and safety restrictions](admin-firmware-hex.md). |
|
|
43
|
+
| Standalone DAT / arbitrary DFU | None | Unsupported as standalone install inputs; no universal conversion or board support is claimed. |
|
|
44
|
+
|
|
45
|
+
A declared format/protocol mismatch is rejected before backend invocation. Binary routes also reject known UF2/ZIP/Intel HEX signatures and `.uf2`, `.zip`, `.hex`, `.dfu` filenames before connecting or erasing, including UF2 bytes renamed to `.bin`. Binary files are read once and the inspected bytes are passed to the backend. These guards are not a universal image validator: selecting `:bin` does not prove board, partition, signature, or bootloader compatibility. UF2 and Nordic package validation belongs to their respective backends. Entering DFU/UF2 bootloader mode and selecting/mounting its volume are separate preparation steps; `install` never automatically reboots the application.
|
|
46
|
+
|
|
47
|
+
### MQTT scope
|
|
48
|
+
|
|
49
|
+
`request_ota(transport_obj: connection, transfer: :wifi, ...)` can submit an authorized Admin preparation request through MQTT, subject to device routing/authentication support. Supply an explicit unicast `to:` and a valid `session_passkey:` obtained through an authenticated radio session; MQTT has no receive queue for automatic Admin session acquisition. That is not an MQTT firmware upload or confirmation that preparation succeeded. The image still needs a reachable, compatible loader data endpoint; `install(protocol: :mqtt, ...)` is unsupported. MQTT is also not a synchronous post-reboot verifier. Do not infer flashing support from broker publish success or an XModem protobuf field.
|
|
50
|
+
|
|
51
|
+
Stock firmware rejects decoded/plaintext MQTT Admin messages. Encrypted traffic
|
|
52
|
+
still requires the target's routing, downlink and Admin authorization settings;
|
|
53
|
+
this client's channel-PSK MQTT publisher does not implement host-side PKI.
|
|
54
|
+
The updater's complete transport selection is WiFi or BLE, not MQTT. Therefore
|
|
55
|
+
an MQTT-only connection cannot deliver an image. Adding such a path would require
|
|
56
|
+
device-side updater changes or an external bridge, not merely publishing image
|
|
57
|
+
chunks from Ruby. A hybrid preparation-through-MQTT followed by direct TCP/BLE
|
|
58
|
+
transfer is not an MQTT image uploader.
|
|
59
|
+
|
|
60
|
+
Sources: [MQTT receive filtering](https://github.com/meshtastic/firmware/blob/6d41e279f1f51bd59f687b9d441c1bf47b1594fc/src/mqtt/MQTT.cpp#L129-L151)
|
|
61
|
+
and [loader transport selection](https://github.com/meshtastic/esp32-unified-ota/blob/e7c0b95e14b6a1ffeca81b71c1ac477593911213/src/main.cpp#L54-L75).
|
|
62
|
+
|
|
22
63
|
## ESP32 unified WiFi example
|
|
23
64
|
|
|
24
65
|
Only use a matching application update `.bin`, not a merged full-flash image, UF2, ZIP, or bootloader. Confirm board, flash layout, power, WiFi configuration, and loader compatibility yourself. Wrong images or interrupted writes may leave the device unbootable; keep a recovery method available.
|
|
@@ -28,15 +69,16 @@ image = File.binread('firmware-matching-board-update.bin')
|
|
|
28
69
|
|
|
29
70
|
# Phase 1: use an existing authenticated Admin connection to pin this image.
|
|
30
71
|
Meshtastic::Admin::Firmware.request_ota(
|
|
31
|
-
|
|
72
|
+
transport_obj: connection,
|
|
32
73
|
bytes: image,
|
|
33
|
-
|
|
74
|
+
transfer: :wifi
|
|
34
75
|
)
|
|
35
76
|
|
|
36
77
|
# Phase 2: connect to the separate loader after it reboots and joins WiFi.
|
|
37
|
-
# Not the Meshtastic TCP PhoneAPI port 4403, nor an existing
|
|
78
|
+
# Not the Meshtastic TCP PhoneAPI port 4403, nor an existing TCP PhoneAPI handle.
|
|
38
79
|
result = Meshtastic::Admin::Firmware.install(
|
|
39
80
|
protocol: :unified_wifi,
|
|
81
|
+
format: :bin,
|
|
40
82
|
host: '192.0.2.10',
|
|
41
83
|
bytes: image,
|
|
42
84
|
port: 3232,
|
|
@@ -48,9 +90,9 @@ result = Meshtastic::Admin::Firmware.install(
|
|
|
48
90
|
# loader_version: 'hardware firmware reboot_count loader_version' }
|
|
49
91
|
```
|
|
50
92
|
|
|
51
|
-
`request_ota(ota_hash: ...)` also accepts an explicitly supplied **raw** 32-byte digest (not hex). When an image is supplied alongside the digest they must match. It rejects unknown modes. The loader itself checks that the upload hash equals its provisioned NVS hash and verifies the downloaded bytes.
|
|
93
|
+
`request_ota(ota_hash: ...)` also accepts an explicitly supplied **raw** 32-byte digest (not hex). When an image is supplied alongside the digest they must match. It rejects unknown transfers/modes and contradictory aliases before sending. The loader itself checks that the upload hash equals its provisioned NVS hash and verifies the downloaded bytes.
|
|
52
94
|
|
|
53
|
-
`install` intentionally rejects `serial_obj`, `tcp_obj`, `bluetooth_obj`, `mqtt_obj`, `mode`, mesh destinations, and other unknown options. These are not the unified WiFi transport. `host` must address the actual prepared loader. Protocol selection is explicit: no guessing or fallback to a different flasher.
|
|
95
|
+
`install` intentionally rejects `transport_obj`, `serial_obj`, `tcp_obj`, `bluetooth_obj`, `mqtt_obj`, `mode`, mesh destinations, and other unknown options. These are not the unified WiFi transport. `host` must address the actual prepared loader. Protocol selection is explicit: no guessing or fallback to a different flasher.
|
|
54
96
|
|
|
55
97
|
### Wire behavior and failure handling
|
|
56
98
|
|
|
@@ -66,7 +108,7 @@ result = Meshtastic::Admin::Firmware.install(
|
|
|
66
108
|
|
|
67
109
|
## ESP32 unified BLE example
|
|
68
110
|
|
|
69
|
-
The compatible unified loader must already be installed and its NVS hash pinned with `request_ota(
|
|
111
|
+
The compatible unified loader must already be installed and its NVS hash pinned with `request_ota(transfer: :ble, bytes: image, ...)`. Close the application transport before opening the loader; never open serial and BLE on the same radio concurrently. Identify the bootloader's **actual** address explicitly (it can differ from the application's). The backend never guesses an incremented MAC, discovers/selects another device, or pairs automatically. If BlueZ does not know the address, discover that loader explicitly before calling `install`. Application reconnection still requires normal Meshtastic BLE pairing.
|
|
70
112
|
|
|
71
113
|
```ruby
|
|
72
114
|
result = Meshtastic::Admin::Firmware.install(
|
|
@@ -92,7 +134,7 @@ For tests or an alternative Ruby GATT implementation, unified BLE accepts `backe
|
|
|
92
134
|
|
|
93
135
|
## Post-reboot verification
|
|
94
136
|
|
|
95
|
-
`verify:` is an **optional Hash** of `verify_reboot` options, validated before transfer. Omitting it preserves
|
|
137
|
+
`verify:` is an **optional Hash** of `verify_reboot` options, validated before transfer. Omitting it preserves the backend result (`:verified` for the native loaders; a UF2 copy alone is not verified flash); it never silently claims boot health. Providing it runs verification only after the installer returns and closes its loader connection. You can also call `verify_reboot` independently.
|
|
96
138
|
|
|
97
139
|
- Required: `transport: :tcp | :bluetooth | :serial`, `expected_version:` (exact nonempty String).
|
|
98
140
|
- Production default: `connection:` Hash for a **new** application connection, explicitly specifying `host`, `address`, or `block_dev`, respectively. TCP uses PhoneAPI port **4403**, not updater port 3232; set `connection[:port]` only for a custom PhoneAPI port. Do not supply an existing socket/handle.
|
|
@@ -100,13 +142,13 @@ For tests or an alternative Ruby GATT implementation, unified BLE accepts `backe
|
|
|
100
142
|
- Optional advanced `reconnect:` callable receives `{transport:, connection:, timeout:}` and must return a newly connected handle of the selected transport, with configuration requested. The verifier still waits for configuration and issues a fresh Admin metadata request; a callback cannot substitute a cached metadata Hash. The returned handle is closed afterward.
|
|
101
143
|
- Pre-metadata connection/configuration I/O failures retry every 0.25 seconds within the total deadline. Metadata errors/version mismatches do not cause a reflash or get converted to success.
|
|
102
144
|
- A fresh source/request-ID-matched `get_device_metadata_response` is required. Cached `handle[:metadata]`, configuration metadata, loader VERSION, a routing ACK or successful port open cannot satisfy verification.
|
|
103
|
-
- Success merges `status: :boot_verified`, `loader_status
|
|
145
|
+
- Success merges `status: :boot_verified`, `loader_status:` preserving the backend status (`:verified` or UF2 `:copied`), `boot_verified: true`, `reboot_verified: true`, current firmware version, node number and metadata into the transfer result. Failure raises; firmware might already have been written, so inspect the device rather than blindly rerunning the installer.
|
|
104
146
|
|
|
105
147
|
This proves that the selected application responds and reports the expected version/optional identity. It does **not** cryptographically attest the running image, establish board compatibility, or test radio/RF operation. Use the correct release artifact and retain a recovery path.
|
|
106
148
|
|
|
107
149
|
## Explicit gaps
|
|
108
150
|
|
|
109
|
-
- No legacy BLE-only updater, ArduinoOTA/espota WiFi updater, Nordic Secure DFU, serial Nordic DFU,
|
|
151
|
+
- No legacy BLE-only updater, ArduinoOTA/espota WiFi updater, Nordic Secure DFU, serial Nordic DFU, universal HEX conversion, or automatic bootloader installation. Native protocol support is deliberately scoped; a board name alone does not establish its installed bootloader or transport capabilities.
|
|
110
152
|
- No automatic discovery, board/image compatibility parser, OTA partition creation, or firmware downloads. ESP32 unified source targets ESP32/ESP32-S3; this is not a claim that every ESP32 variant or every Meshtastic board has that loader.
|
|
111
153
|
- MQTT can carry an authorized preparation request; it is not an image transport or synchronous post-reboot verifier.
|
|
112
154
|
- Tests exercise fake GATT loaders, real Ruby D-Bus signal marshalling over UNIX sockets, loopback TCP PhoneAPI/configuration/Admin exchanges and the retained TCP uploader. No hardware was contacted or flashed; physical device compatibility and reboot behavior remain hardware-unverified.
|
data/documentation/admin.md
CHANGED
|
@@ -1,6 +1,12 @@
|
|
|
1
1
|
# Meshtastic::Admin
|
|
2
2
|
|
|
3
|
-
Build and send `AdminMessage` on `ADMIN_APP` through
|
|
3
|
+
Build and send `AdminMessage` on `ADMIN_APP` through `transport_obj: connection`, where `connection` is the actual connected Serial, Bluetooth, TCP, or MQTT handle. These operations can change configuration, reboot, erase files, or reset a device. Sending is not confirmation of successful execution.
|
|
4
|
+
|
|
5
|
+
## Connection handle
|
|
6
|
+
|
|
7
|
+
All Admin send/request helpers and nested Channel, Config, and Firmware preparation APIs use `transport_obj: connection`. Pass the handle returned by `Meshtastic::Serial.connect`, `Meshtastic::Bluetooth.connect`, `Meshtastic::TCP.connect`, or `Meshtastic::MQTT.connect`, not a transport name, endpoint string, or wrapper containing `serial_obj:`. Admin detects the transport from the handle; `Admin.transport_type(transport_obj: connection)` returns its type. Missing, unsupported, or ambiguous multi-transport handles are rejected. The old Admin keywords `serial_obj:`, `bluetooth_obj:`, `tcp_obj:`, and `mqtt_obj:` are rejected rather than silently selected.
|
|
8
|
+
|
|
9
|
+
The low-level transport APIs are unchanged: connect using their documented endpoint arguments and disconnect with their original transport-specific keyword (for example, `Meshtastic::Serial.disconnect(serial_obj: connection)`). Firmware `install` uses a separate loader endpoint (`host:`, `address:`, `port:`, or mounted volume/programmer options); it does not accept the application `transport_obj:`. Post-reboot verification likewise opens a new application connection using its documented endpoint options.
|
|
4
10
|
|
|
5
11
|
## Addressing and defaults
|
|
6
12
|
|
|
@@ -59,7 +65,7 @@ Unrelated ports, non-response variants, encrypted/absent data, and mismatched ID
|
|
|
59
65
|
|
|
60
66
|
```ruby
|
|
61
67
|
reply = Meshtastic::Admin.request(
|
|
62
|
-
|
|
68
|
+
transport_obj: connection, # a connected Serial, Bluetooth, or TCP handle
|
|
63
69
|
to: '!aabbccdd',
|
|
64
70
|
message: Meshtastic::AdminMessage.new(get_device_metadata_request: true),
|
|
65
71
|
timeout: 10
|
|
@@ -4,20 +4,24 @@ Reopens the generated `Meshtastic::ModuleConfig` protobuf class. Get/set module
|
|
|
4
4
|
|
|
5
5
|
## Methods
|
|
6
6
|
|
|
7
|
-
- `get(
|
|
8
|
-
- `set(
|
|
7
|
+
- `get(transport_obj:, module_config_type: :MQTT_CONFIG)`
|
|
8
|
+
- `set(transport_obj:, module_config:)`
|
|
9
9
|
- `help` / `authors`
|
|
10
10
|
|
|
11
11
|
`module_config_type` values include `:MQTT_CONFIG`, `:SERIAL_CONFIG`, `:STOREFORWARD_CONFIG`, `:TELEMETRY_CONFIG`, `:REMOTEHARDWARE_CONFIG`, `:CANNEDMSG_CONFIG`, `:AUDIO_CONFIG`, `:PAXCOUNTER_CONFIG`, `:NEIGHBORINFO_CONFIG`, `:DETECTIONSENSOR_CONFIG`, `:EXTNOTIF_CONFIG`, `:RANGETEST_CONFIG`, `:AMBIENTLIGHTING_CONFIG`, `:STATUSMESSAGE_CONFIG`, `:MESHBEACON_CONFIG`, `:TAK_CONFIG`, `:TRAFFICMANAGEMENT_CONFIG`.
|
|
12
12
|
|
|
13
|
+
Supply `transport_obj: connection` with an actual connected Serial, Bluetooth, TCP, or MQTT handle. These non-Admin wrappers also retain `serial_obj:`, `bluetooth_obj:`, `tcp_obj:`, and `mqtt_obj:` for existing callers, translating them internally to Admin's `transport_obj:`. Supply exactly one non-nil connection option; mixed aliases are rejected even when they refer to the same handle. Nil aliases are ignored, and caller options are not mutated.
|
|
14
|
+
|
|
15
|
+
Routing, validation, and automatic remote session-key acquisition follow [Admin](admin.md). Setters may wait for session acquisition, but their return value is transport submission, not confirmed persistence. MQTT requires an explicit authorized passkey for remote writes; these helpers do not add synchronous MQTT readback.
|
|
16
|
+
|
|
13
17
|
## Example
|
|
14
18
|
|
|
15
19
|
```ruby
|
|
16
|
-
Meshtastic::ModuleConfig.get(
|
|
20
|
+
Meshtastic::ModuleConfig.get(transport_obj: connection, module_config_type: :MQTT_CONFIG)
|
|
17
21
|
|
|
18
22
|
mod = Meshtastic::ModuleConfig.new
|
|
19
23
|
mod.mqtt = Meshtastic::ModuleConfig::MQTTConfig.new(enabled: true, address: 'mqtt.example.test')
|
|
20
|
-
Meshtastic::ModuleConfig.set(
|
|
24
|
+
Meshtastic::ModuleConfig.set(transport_obj: connection, module_config: mod)
|
|
21
25
|
```
|
|
22
26
|
|
|
23
27
|
## Related
|
data/documentation/rtttl.md
CHANGED
|
@@ -5,18 +5,22 @@ Ringtone helpers. `encode` builds `RTTTLConfig`. `set` / `get` go through [Admin
|
|
|
5
5
|
## Methods
|
|
6
6
|
|
|
7
7
|
- `encode(ringtone:)`
|
|
8
|
-
- `set(
|
|
9
|
-
- `get(
|
|
8
|
+
- `set(transport_obj:, ringtone:)`
|
|
9
|
+
- `get(transport_obj:)`
|
|
10
10
|
- `help` / `authors`
|
|
11
11
|
|
|
12
|
+
Supply `transport_obj: connection` with an actual connected Serial, Bluetooth, TCP, or MQTT handle. These non-Admin wrappers also retain `serial_obj:`, `bluetooth_obj:`, `tcp_obj:`, and `mqtt_obj:` for existing callers, translating them internally to Admin's `transport_obj:`. Supply exactly one non-nil connection option; mixed aliases are rejected even when they refer to the same handle. Nil aliases are ignored, and caller options are not mutated.
|
|
13
|
+
|
|
14
|
+
Routing, validation, and automatic remote session-key acquisition follow [Admin](admin.md). Setters may wait for session acquisition, but their return value is transport submission, not confirmed persistence. MQTT requires an explicit authorized passkey for remote writes; these helpers do not add synchronous MQTT readback.
|
|
15
|
+
|
|
12
16
|
## Example
|
|
13
17
|
|
|
14
18
|
```ruby
|
|
15
19
|
Meshtastic::RTTTL.set(
|
|
16
|
-
|
|
20
|
+
transport_obj: connection,
|
|
17
21
|
ringtone: 'Mario:d=4,o=5,b=125:16e6'
|
|
18
22
|
)
|
|
19
|
-
Meshtastic::RTTTL.get(
|
|
23
|
+
Meshtastic::RTTTL.get(transport_obj: connection)
|
|
20
24
|
```
|
|
21
25
|
|
|
22
26
|
## Related
|
|
@@ -51,6 +51,9 @@ module Meshtastic
|
|
|
51
51
|
end
|
|
52
52
|
|
|
53
53
|
public_class_method def self.set(opts = {})
|
|
54
|
+
legacy = opts.keys & %i[serial_obj bluetooth_obj tcp_obj mqtt_obj]
|
|
55
|
+
raise ArgumentError, "#{legacy.join(', ')} are unsupported; use transport_obj" unless legacy.empty?
|
|
56
|
+
|
|
54
57
|
channel = build(opts.merge({}))
|
|
55
58
|
builder_keys = Meshtastic::ChannelSettings.descriptor.map { |field| field.name.to_sym } + %i[channel index role settings]
|
|
56
59
|
merged = opts.except(*builder_keys).merge(channel_settings: channel)
|
|
@@ -126,13 +129,13 @@ module Meshtastic
|
|
|
126
129
|
|
|
127
130
|
# Request a channel slot from the node.
|
|
128
131
|
#{self}.get(
|
|
129
|
-
|
|
132
|
+
transport_obj: 'required - connected Serial, Bluetooth, TCP handle or MQTT client',
|
|
130
133
|
index: 'optional - zero-based channel slot; Admin adds one on wire (default: 0)'
|
|
131
134
|
)
|
|
132
135
|
|
|
133
136
|
# Write a channel slot on the node.
|
|
134
137
|
#{self}.set(
|
|
135
|
-
|
|
138
|
+
transport_obj: 'required - connected Serial, Bluetooth, TCP handle or MQTT client',
|
|
136
139
|
channel: 'optional - Channel protobuf to write',
|
|
137
140
|
index: 'optional - channel slot index when building a channel',
|
|
138
141
|
role: 'optional - :PRIMARY, :SECONDARY, or :DISABLED',
|
|
@@ -152,7 +155,7 @@ module Meshtastic
|
|
|
152
155
|
# Write URL channels and optional LoRa configuration.
|
|
153
156
|
#{self}.apply_url(
|
|
154
157
|
url: 'required - Meshtastic channel URL replacing slots from zero',
|
|
155
|
-
|
|
158
|
+
transport_obj: 'required - connected Serial, Bluetooth, TCP handle or MQTT client'
|
|
156
159
|
)
|
|
157
160
|
|
|
158
161
|
# Print the AUTHOR(S) string for this module.
|
|
@@ -108,75 +108,75 @@ module Meshtastic
|
|
|
108
108
|
puts "USAGE:
|
|
109
109
|
# Request a radio Config section by ConfigType.
|
|
110
110
|
#{self}.get(
|
|
111
|
-
|
|
111
|
+
transport_obj: 'required - connected Serial, Bluetooth, TCP handle or MQTT client',
|
|
112
112
|
config_type: 'optional - :DEVICE_CONFIG or another ConfigType (default: :DEVICE_CONFIG)'
|
|
113
113
|
)
|
|
114
114
|
|
|
115
115
|
# Write a Config protobuf to the node.
|
|
116
116
|
#{self}.set(
|
|
117
|
-
|
|
117
|
+
transport_obj: 'required - connected Serial, Bluetooth, TCP handle or MQTT client',
|
|
118
118
|
config: 'required - Meshtastic::Config protobuf to write'
|
|
119
119
|
)
|
|
120
120
|
|
|
121
121
|
# Request DEVICE_CONFIG from the node.
|
|
122
122
|
#{self}.get_device(
|
|
123
|
-
|
|
123
|
+
transport_obj: 'required - connected Serial, Bluetooth, TCP handle or MQTT client'
|
|
124
124
|
)
|
|
125
125
|
|
|
126
126
|
# Request POSITION_CONFIG from the node.
|
|
127
127
|
#{self}.get_position(
|
|
128
|
-
|
|
128
|
+
transport_obj: 'required - connected Serial, Bluetooth, TCP handle or MQTT client'
|
|
129
129
|
)
|
|
130
130
|
|
|
131
131
|
# Request POWER_CONFIG from the node.
|
|
132
132
|
#{self}.get_power(
|
|
133
|
-
|
|
133
|
+
transport_obj: 'required - connected Serial, Bluetooth, TCP handle or MQTT client'
|
|
134
134
|
)
|
|
135
135
|
|
|
136
136
|
# Request NETWORK_CONFIG from the node.
|
|
137
137
|
#{self}.get_network(
|
|
138
|
-
|
|
138
|
+
transport_obj: 'required - connected Serial, Bluetooth, TCP handle or MQTT client'
|
|
139
139
|
)
|
|
140
140
|
|
|
141
141
|
# Request DISPLAY_CONFIG from the node.
|
|
142
142
|
#{self}.get_display(
|
|
143
|
-
|
|
143
|
+
transport_obj: 'required - connected Serial, Bluetooth, TCP handle or MQTT client'
|
|
144
144
|
)
|
|
145
145
|
|
|
146
146
|
# Request LORA_CONFIG from the node.
|
|
147
147
|
#{self}.get_lora(
|
|
148
|
-
|
|
148
|
+
transport_obj: 'required - connected Serial, Bluetooth, TCP handle or MQTT client'
|
|
149
149
|
)
|
|
150
150
|
|
|
151
151
|
# Request BLUETOOTH_CONFIG from the node.
|
|
152
152
|
#{self}.get_bluetooth(
|
|
153
|
-
|
|
153
|
+
transport_obj: 'required - connected Serial, Bluetooth, TCP handle or MQTT client'
|
|
154
154
|
)
|
|
155
155
|
|
|
156
156
|
# Request SECURITY_CONFIG from the node.
|
|
157
157
|
#{self}.get_security(
|
|
158
|
-
|
|
158
|
+
transport_obj: 'required - connected Serial, Bluetooth, TCP handle or MQTT client'
|
|
159
159
|
)
|
|
160
160
|
|
|
161
161
|
# Request SESSIONKEY_CONFIG from the node.
|
|
162
162
|
#{self}.get_sessionkey(
|
|
163
|
-
|
|
163
|
+
transport_obj: 'required - connected Serial, Bluetooth, TCP handle or MQTT client'
|
|
164
164
|
)
|
|
165
165
|
|
|
166
166
|
# Request DeviceUIConfig using dedicated firmware operation.
|
|
167
167
|
#{self}.get_device_ui(
|
|
168
|
-
|
|
168
|
+
transport_obj: 'required - connected Serial, Bluetooth, TCP handle or MQTT client'
|
|
169
169
|
)
|
|
170
170
|
|
|
171
171
|
# Write DeviceConfig wrapped in a Config protobuf.
|
|
172
172
|
#{self}.set_device(
|
|
173
|
-
|
|
173
|
+
transport_obj: 'required - connected Serial, Bluetooth, TCP handle or MQTT client',
|
|
174
174
|
device: 'required - DeviceConfig protobuf or complete section field Hash'
|
|
175
175
|
)
|
|
176
176
|
|
|
177
177
|
# Write LoRaConfig wrapped in a Config protobuf.
|
|
178
178
|
#{self}.set_lora(
|
|
179
|
-
|
|
179
|
+
transport_obj: 'required - connected Serial, Bluetooth, TCP handle or MQTT client',
|
|
180
180
|
lora: 'required - LoRaConfig protobuf or complete section field Hash'
|
|
181
181
|
)
|
|
182
182
|
|