Module: Meshtastic::Admin::Firmware
- Defined in:
- lib/meshtastic/admin/firmware.rb,
lib/meshtastic/admin/firmware/ble.rb,
lib/meshtastic/admin/firmware/hex.rb,
lib/meshtastic/admin/firmware/uf2.rb,
lib/meshtastic/admin/firmware/nordic_dfu.rb,
lib/meshtastic/admin/firmware/serial_bootloader.rb
Defined Under Namespace
Modules: BLE, Hex, NordicDFU, SerialBootloader, UF2
Class Method Summary collapse
- .authors ⇒ Object
- .enter_dfu(opts = {}) ⇒ Object
- .help ⇒ Object
- .install(opts = {}) ⇒ Object
- .reboot_ota(opts = {}) ⇒ Object
- .request_ota(opts = {}) ⇒ Object
- .send_xmodem(opts = {}) ⇒ Object
- .sha256(opts = {}) ⇒ Object
- .verify_reboot(opts = {}) ⇒ Object
- .xmodem_blocks(opts = {}) ⇒ Object
Class Method Details
.authors ⇒ Object
245 246 247 |
# File 'lib/meshtastic/admin/firmware.rb', line 245 public_class_method def self. "AUTHOR(S):\n 0day Inc. <[email protected]>\n " end |
.enter_dfu(opts = {}) ⇒ Object
48 49 50 |
# File 'lib/meshtastic/admin/firmware.rb', line 48 public_class_method def self.enter_dfu(opts = {}) Admin.send(opts.merge(enter_dfu_mode_request: true)) end |
.help ⇒ Object
249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 |
# File 'lib/meshtastic/admin/firmware.rb', line 249 public_class_method def self.help puts "USAGE: # SHA-256 the firmware image bytes or file. #{self}.sha256( firmware: 'optional - path to a firmware .bin on disk', bytes: 'optional - raw firmware image bytes if no path is given' ) # Send Admin ota_request with the image hash and OTA mode. #{self}.request_ota( transport_obj: 'required - connected Serial, Bluetooth, TCP handle or MQTT client', firmware: 'optional - path to a firmware .bin on disk', ota_hash: 'optional - 32-byte SHA-256 digest if not hashing firmware', transfer: 'optional - :wifi or :ble; required for serial/MQTT unless mode is explicit', mode: 'optional - explicit :OTA_BLE or :OTA_WIFI alias; must agree with transfer' # Bluetooth transport infers :ble; TCP transport infers :wifi. ) # Ask the node to enter DFU / UF2 bootloader mode. #{self}.enter_dfu( transport_obj: 'required - connected Serial, Bluetooth, TCP handle or MQTT client' ) # Reject the obsolete unhandled legacy OTA reboot field. #{self}.reboot_ota # Reject filesystem XModem as a firmware update mechanism. #{self}.xmodem_blocks # Reject filesystem XModem as a firmware update mechanism. #{self}.send_xmodem # Upload using an explicitly selected native loader protocol. #{self}.install( protocol: 'required - :unified_wifi, :unified_ble, :esp_rom, :nordic_dfu, :uf2 or :swd; no protocol guessing', format: 'optional - :bin, :zip, :uf2 or :hex; defaults to the selected protocol format; mismatches rejected', verify: 'optional - verify_reboot options Hash; success becomes :boot_verified only after a fresh reply', host: 'required - OTA loader IP address or hostname, not a mesh node ID', port: 'optional - separate OTA TCP service port (default: 3232)', firmware: 'optional - matching application .bin path, exclusive with bytes', bytes: 'optional - nonempty raw image String, exclusive with firmware', timeout: 'optional - positive seconds per connect and whole transfer (default: 120)', retries: 'optional - connection refusal/timeout retries, 0..20 (default: 3)', retry_delay: 'optional - nonnegative seconds between connection retries (default: 1)' ) # For unified OTA first use request_ota with the matching transfer to pin the same image hash. # install never sends preparation commands; :verified means loader OK, not boot confirmation. # BLE.help, NordicDFU.help, SerialBootloader.help, UF2.help and Hex.help document backend options. # Reconnect and request fresh correlated application firmware metadata. #{self}.verify_reboot( transport: 'required - :tcp, :bluetooth or :serial for the restarted application', connection: 'optional - connect options Hash with explicit host/address/block_dev; required without reconnect', expected_version: 'required - exact firmware version reported by the intended application', expected_node: 'optional - numeric node identity to verify after restart', reconnect: 'optional - callable accepting options Hash and returning a newly connected transport handle', timeout: 'optional - entire reboot/reconnect/config/metadata deadline seconds, default 60', reboot_delay: 'optional - initial wait for loader restart, default 3 seconds' ) # Print the AUTHOR(S) string for this module. #{self}.authors " end |
.install(opts = {}) ⇒ Object
67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 |
# File 'lib/meshtastic/admin/firmware.rb', line 67 public_class_method def self.install(opts = {}) validate_verification(opts[:verify]) if opts.key?(:verify) formats = { unified_wifi: :bin, unified_ble: :bin, esp_rom: :bin, nordic_dfu: :zip, uf2: :uf2, swd: :hex } expected_format = formats[opts[:protocol]] raise ArgumentError, "format must be #{expected_format.inspect} for protocol #{opts[:protocol].inspect}" if opts.key?(:format) && opts[:format] != expected_format = opts.except(:verify, :format) = () if expected_format == :bin result = case opts[:protocol] when :swd then Hex.install() when :uf2 then UF2.install() when :unified_ble then BLE.install() when :esp_rom then SerialBootloader.install() when :nordic_dfu then NordicDFU.install() when :unified_wifi then install_wifi() else raise NotImplementedError, 'install requires explicit protocol: :unified_wifi, :unified_ble, :esp_rom, :nordic_dfu, :uf2 or :swd' end return result unless opts[:verify] result.merge(verify_reboot(opts[:verify])).merge(loader_status: result[:status], reboot_verified: true, boot_verified: true) end |
.reboot_ota(opts = {}) ⇒ Object
52 53 54 55 |
# File 'lib/meshtastic/admin/firmware.rb', line 52 public_class_method def self.reboot_ota(opts = {}) opts.merge({}) raise NotImplementedError, 'reboot_ota_seconds is not handled by current firmware; use request_ota' end |
.request_ota(opts = {}) ⇒ Object
15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
# File 'lib/meshtastic/admin/firmware.rb', line 15 public_class_method def self.request_ota(opts = {}) legacy = opts.keys & %i[serial_obj bluetooth_obj tcp_obj mqtt_obj] raise ArgumentError, "#{legacy.join(', ')} are unsupported; use transport_obj" unless legacy.empty? mode = ota_mode(opts.merge({})) hash = opts[:ota_hash] || sha256(opts) raise ArgumentError, 'ota_hash must be a raw 32-byte String' unless hash.is_a?(String) && hash.bytesize == 32 raise ArgumentError, 'ota_hash does not match firmware bytes' if opts[:ota_hash] && (opts.key?(:bytes) || opts.key?(:firmware)) && hash != sha256(opts) event = Meshtastic::AdminMessage::OTAEvent.new( reboot_ota_mode: mode, ota_hash: hash.b ) Admin.send(opts.except(:bytes, :firmware, :ota_hash, :mode, :transfer).merge(ota_request: event)) end |
.send_xmodem(opts = {}) ⇒ Object
62 63 64 65 |
# File 'lib/meshtastic/admin/firmware.rb', line 62 public_class_method def self.send_xmodem(opts = {}) opts.merge({}) raise NotImplementedError, 'PhoneAPI XModem transfers filesystem files, not firmware images' end |
.sha256(opts = {}) ⇒ Object
11 12 13 |
# File 'lib/meshtastic/admin/firmware.rb', line 11 public_class_method def self.sha256(opts = {}) Digest::SHA256.digest(firmware_bytes(opts.merge({}))) end |
.verify_reboot(opts = {}) ⇒ Object
125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 |
# File 'lib/meshtastic/admin/firmware.rb', line 125 public_class_method def self.verify_reboot(opts = {}) validate_verification(opts.merge({})) handle = nil transport = { tcp: Meshtastic::TCP, bluetooth: Meshtastic::Bluetooth, serial: Meshtastic::Serial }.fetch(opts.fetch(:transport)) key = { tcp: :tcp_obj, bluetooth: :bluetooth_obj, serial: :serial_obj }.fetch(opts.fetch(:transport)) Timeout.timeout(opts.fetch(:timeout, 60)) do sleep opts.fetch(:reboot_delay, 3) begin handle = if opts[:reconnect] opts[:reconnect].call(transport: opts.fetch(:transport), connection: opts.fetch(:connection, {}), timeout: opts.fetch(:timeout, 60)) else transport.connect(opts.fetch(:connection).merge(want_config: true)) end transport.wait_for_config(key => handle, timeout: opts.fetch(:timeout, 60)) rescue IOError, SystemCallError transport.disconnect(key => handle) if handle handle = nil sleep 0.25 retry end reply = Admin.request(transport_obj: handle, get_device_metadata_request: true, timeout: opts.fetch(:timeout, 60)) = reply.fetch(:value).to_h raise IOError, "Firmware version mismatch: #{[:firmware_version].inspect}" unless [:firmware_version] == opts.fetch(:expected_version) raise IOError, 'Post-reboot node identity mismatch' if opts[:expected_node] && handle[:my_node_num] != opts[:expected_node] { status: :boot_verified, firmware_version: [:firmware_version], node_num: handle[:my_node_num], metadata: } end ensure transport.disconnect(key => handle) if handle end |
.xmodem_blocks(opts = {}) ⇒ Object
57 58 59 60 |
# File 'lib/meshtastic/admin/firmware.rb', line 57 public_class_method def self.xmodem_blocks(opts = {}) opts.merge({}) raise NotImplementedError, 'PhoneAPI XModem transfers filesystem files, not firmware images' end |