/ RNS / Utilities / rnodeconf.py
rnodeconf.py
   1  #!/usr/bin/env python3
   2  
   3  # Reticulum License
   4  #
   5  # Copyright (c) 2016-2025 Mark Qvist
   6  #
   7  # Permission is hereby granted, free of charge, to any person obtaining a copy
   8  # of this software and associated documentation files (the "Software"), to deal
   9  # in the Software without restriction, including without limitation the rights
  10  # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  11  # copies of the Software, and to permit persons to whom the Software is
  12  # furnished to do so, subject to the following conditions:
  13  #
  14  # - The Software shall not be used in any kind of system which includes amongst
  15  #   its functions the ability to purposefully do harm to human beings.
  16  #
  17  # - The Software shall not be used, directly or indirectly, in the creation of
  18  #   an artificial intelligence, machine learning or language model training
  19  #   dataset, including but not limited to any use that contributes to the
  20  #   training or development of such a model or algorithm.
  21  #
  22  # - The above copyright notice and this permission notice shall be included in
  23  #   all copies or substantial portions of the Software.
  24  #
  25  # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  26  # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  27  # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  28  # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  29  # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  30  # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  31  # SOFTWARE.
  32  
  33  from time import sleep
  34  import argparse
  35  import threading
  36  import sys
  37  import os
  38  import os.path
  39  import struct
  40  import datetime
  41  import time
  42  import math
  43  import hashlib
  44  import zipfile
  45  from urllib.request import urlretrieve
  46  from importlib import util
  47  import RNS
  48  
  49  RNS.logtimefmt      = "%H:%M:%S"
  50  RNS.compact_log_fmt = True
  51  
  52  program_version = "2.5.0"
  53  eth_addr = "0x91C421DdfB8a30a49A71d63447ddb54cEBe3465E"
  54  btc_addr = "bc1pgqgu8h8xvj4jtafslq396v7ju7hkgymyrzyqft4llfslz5vp99psqfk3a6"
  55  xmr_addr = "87HcDx6jRSkMQ9nPRd5K9hGGpZLn2s7vWETjMaVM5KfV4TD36NcYa8J8WSxhTSvBzzFpqDwp2fg5GX2moZ7VAP9QMZCZGET"
  56  
  57  rnode = None
  58  rnode_serial = None
  59  rnode_port = None
  60  rnode_baudrate = 115200
  61  known_keys = [["unsigned.io", "30819f300d06092a864886f70d010101050003818d0030818902818100bf831ebd99f43b477caf1a094bec829389da40653e8f1f83fc14bf1b98a3e1cc70e759c213a43f71e5a47eb56a9ca487f241335b3e6ff7cdde0ee0a1c75c698574aeba0485726b6a9dfc046b4188e3520271ee8555a8f405cf21f81f2575771d0b0887adea5dd53c1f594f72c66b5f14904ffc2e72206a6698a490d51ba1105b0203010001"], ["unsigned.io", "30819f300d06092a864886f70d010101050003818d0030818902818100e5d46084e445595376bf7efd9c6ccf19d39abbc59afdb763207e4ff68b8d00ebffb63847aa2fe6dd10783d3ea63b55ac66f71ad885c20e223709f0d51ed5c6c0d0b093be9e1d165bb8a483a548b67a3f7a1e4580f50e75b306593fa6067ae259d3e297717bd7ff8c8f5b07f2bed89929a9a0321026cf3699524db98e2d18fb2d020300ff39"]]
  62  firmware_update_url = "https://github.com/markqvist/RNode_Firmware/releases/download/"
  63  fw_filename = None
  64  fw_url = None
  65  mapped_model = None
  66  
  67  class KISS():
  68      FEND            = 0xC0
  69      FESC            = 0xDB
  70      TFEND           = 0xDC
  71      TFESC           = 0xDD
  72      
  73      CMD_UNKNOWN     = 0xFE
  74      CMD_DATA        = 0x00
  75      CMD_FREQUENCY   = 0x01
  76      CMD_BANDWIDTH   = 0x02
  77      CMD_TXPOWER     = 0x03
  78      CMD_SF          = 0x04
  79      CMD_CR          = 0x05
  80      CMD_RADIO_STATE = 0x06
  81      CMD_RADIO_LOCK  = 0x07
  82      CMD_DETECT      = 0x08
  83      CMD_LEAVE       = 0x0A
  84      CMD_READY       = 0x0F
  85      CMD_STAT_RX     = 0x21
  86      CMD_STAT_TX     = 0x22
  87      CMD_STAT_RSSI   = 0x23
  88      CMD_STAT_SNR    = 0x24
  89      CMD_BLINK       = 0x30
  90      CMD_RANDOM      = 0x40
  91      CMD_DISP_INT    = 0x45
  92      CMD_NP_INT      = 0x65
  93      CMD_DISP_ADR    = 0x63
  94      CMD_DISP_BLNK   = 0x64
  95      CMD_DISP_ROT    = 0x67
  96      CMD_DISP_RCND   = 0x68
  97      CMD_BT_CTRL     = 0x46
  98      CMD_BT_PIN      = 0x62
  99      CMD_DIS_IA      = 0x69
 100      CMD_WIFI_MODE   = 0x6A
 101      CMD_WIFI_SSID   = 0x6B
 102      CMD_WIFI_PSK    = 0x6C
 103      CMD_WIFI_CHN    = 0x6E
 104      CMD_WIFI_IP     = 0x84
 105      CMD_WIFI_NM     = 0x85
 106      CMD_BOARD       = 0x47
 107      CMD_PLATFORM    = 0x48
 108      CMD_MCU         = 0x49
 109      CMD_FW_VERSION  = 0x50
 110      CMD_CFG_READ    = 0x6D
 111      CMD_ROM_READ    = 0x51
 112      CMD_ROM_WRITE   = 0x52
 113      CMD_ROM_WIPE    = 0x59
 114      CMD_CONF_SAVE   = 0x53
 115      CMD_CONF_DELETE = 0x54
 116      CMD_RESET       = 0x55
 117      CMD_DEV_HASH    = 0x56
 118      CMD_DEV_SIG     = 0x57
 119      CMD_HASHES      = 0x60
 120      CMD_FW_HASH     = 0x58
 121      CMD_FW_UPD      = 0x61
 122  
 123      DETECT_REQ      = 0x73
 124      DETECT_RESP     = 0x46
 125      
 126      RADIO_STATE_OFF = 0x00
 127      RADIO_STATE_ON  = 0x01
 128      RADIO_STATE_ASK = 0xFF
 129      
 130      CMD_ERROR           = 0x90
 131      ERROR_INITRADIO     = 0x01
 132      ERROR_TXFAILED      = 0x02
 133      ERROR_EEPROM_LOCKED = 0x03
 134  
 135      @staticmethod
 136      def escape(data):
 137          data = data.replace(bytes([0xdb]), bytes([0xdb, 0xdd]))
 138          data = data.replace(bytes([0xc0]), bytes([0xdb, 0xdc]))
 139          return data
 140  
 141  class ROM():
 142      PLATFORM_AVR        = 0x90
 143      PLATFORM_ESP32      = 0x80
 144      PLATFORM_NRF52      = 0x70
 145  
 146      MCU_1284P           = 0x91
 147      MCU_2560            = 0x92
 148      MCU_ESP32           = 0x81
 149      MCU_NRF52           = 0x71
 150  
 151      PRODUCT_RNODE       = 0x03
 152      MODEL_A1            = 0xA1
 153      MODEL_A6            = 0xA6
 154      MODEL_A4            = 0xA4
 155      MODEL_A9            = 0xA9
 156      MODEL_A3            = 0xA3
 157      MODEL_A8            = 0xA8
 158      MODEL_A2            = 0xA2
 159      MODEL_A7            = 0xA7
 160      MODEL_A5            = 0xA5
 161      MODEL_AA            = 0xAA
 162      MODEL_AC            = 0xAC
 163  
 164      PRODUCT_T32_10      = 0xB2
 165      MODEL_BA            = 0xBA
 166      MODEL_BB            = 0xBB
 167  
 168      PRODUCT_T32_20      = 0xB0
 169      MODEL_B3            = 0xB3
 170      MODEL_B8            = 0xB8
 171  
 172      PRODUCT_T32_21      = 0xB1
 173      MODEL_B4            = 0xB4
 174      MODEL_B9            = 0xB9
 175      MODEL_B4_TCXO       = 0x04 # The TCXO model codes are only used here to select the correct firmware,
 176      MODEL_B9_TCXO       = 0x09 # actual model codes in firmware is still 0xB4 and 0xB9.
 177      
 178      PRODUCT_H32_V2      = 0xC0
 179      MODEL_C4            = 0xC4
 180      MODEL_C9            = 0xC9
 181  
 182      PRODUCT_H32_V3      = 0xC1
 183      MODEL_C5            = 0xC5
 184      MODEL_CA            = 0xCA
 185  
 186      PRODUCT_H32_V4      = 0xC3
 187      MODEL_C8            = 0xC8 # 868/915/923 MHz with PA
 188  
 189      PRODUCT_TBEAM       = 0xE0
 190      MODEL_E4            = 0xE4
 191      MODEL_E9            = 0xE9
 192      MODEL_E3            = 0xE3
 193      MODEL_E8            = 0xE8
 194  
 195      PRODUCT_TBEAM_S_V1  = 0xEA
 196      MODEL_DB            = 0xDB
 197      MODEL_DC            = 0xDC
 198  
 199      PRODUCT_TDECK       = 0xD0
 200      MODEL_D4            = 0xD4
 201      MODEL_D9            = 0xD9
 202  
 203      PRODUCT_RAK4631     = 0x10
 204      MODEL_11            = 0x11
 205      MODEL_12            = 0x12
 206      MODEL_13            = 0x13
 207      MODEL_14            = 0x14
 208  
 209      PRODUCT_OPENCOM_XL  = 0x20
 210      MODEL_21            = 0x21
 211  
 212      PRODUCT_TECHO       = 0x15
 213      MODEL_16            = 0x16
 214      MODEL_17            = 0x17
 215  
 216      PRODUCT_HELTEC_T114 = 0xC2
 217      BOARD_HELTEC_T114   = 0x3C
 218      MODEL_C6            = 0xC6 # Heltec Mesh Node T114, 470-510 MHz (HT-n5262-LF)
 219      MODEL_C7            = 0xC7 # Heltec Mesh Node T114, 863-928 MHz (HT-n5262-HF)
 220      
 221      PRODUCT_XIAO_S3     = 0xEB
 222      BOARD_XIAO_S3       = 0x3E
 223      MODEL_DE            = 0xDE # Xiao ESP32S3 with Wio-SX1262 module, 433 MHz
 224      MODEL_DD            = 0xDD # Xiao ESP32S3 with Wio-SX1262 module, 868 MHz
 225  
 226      PRODUCT_HMBRW  = 0xF0
 227      MODEL_FF       = 0xFF
 228      MODEL_FE       = 0xFE
 229  
 230      ADDR_PRODUCT   = 0x00
 231      ADDR_MODEL     = 0x01
 232      ADDR_HW_REV    = 0x02
 233      ADDR_SERIAL    = 0x03
 234      ADDR_MADE      = 0x07
 235      ADDR_CHKSUM    = 0x0B
 236      ADDR_SIGNATURE = 0x1B
 237      ADDR_INFO_LOCK = 0x9B
 238      ADDR_CONF_SF   = 0x9C
 239      ADDR_CONF_CR   = 0x9D
 240      ADDR_CONF_TXP  = 0x9E
 241      ADDR_CONF_BW   = 0x9F
 242      ADDR_CONF_FREQ = 0xA3
 243      ADDR_CONF_OK   = 0xA7
 244  
 245      ADDR_CONF_BT   = 0xB0
 246      ADDR_CONF_DSET = 0xB1
 247      ADDR_CONF_DINT = 0xB2
 248      ADDR_CONF_DADR = 0xB3
 249      ADDR_CONF_DBLK = 0xB4
 250      ADDR_CONF_DROT = 0xB8
 251      ADDR_CONF_PSET = 0xB5
 252      ADDR_CONF_PINT = 0xB6
 253      ADDR_CONF_BSET = 0xB7
 254      ADDR_CONF_DIA  = 0xB9
 255      ADDR_CONF_WIFI = 0xBA
 256      ADDR_CONF_WCHN = 0xBB
 257      ADDR_CONF_SSID = 0x00
 258      ADDR_CONF_PSK  = 0x21
 259      ADDR_CONF_IP   = 0x42
 260      ADDR_CONF_NM   = 0x46
 261  
 262      INFO_LOCK_BYTE = 0x73
 263      CONF_OK_BYTE   = 0x73
 264  
 265      BOARD_RNODE         = 0x31
 266      BOARD_HMBRW         = 0x32
 267      BOARD_TBEAM         = 0x33
 268      BOARD_TDECK         = 0x3B
 269      BOARD_HUZZAH32      = 0x34
 270      BOARD_GENERIC_ESP32 = 0x35
 271      BOARD_LORA32_V2_0   = 0x36
 272      BOARD_LORA32_V2_1   = 0x37
 273      BOARD_TECHO         = 0x43
 274      BOARD_RAK4631       = 0x51
 275  
 276      MANUAL_FLASH_MODELS = []
 277  
 278  mapped_product = ROM.PRODUCT_RNODE
 279  products = {
 280      ROM.PRODUCT_RNODE:  "RNode",
 281      ROM.PRODUCT_HMBRW:  "Hombrew RNode",
 282      ROM.PRODUCT_TBEAM:  "LilyGO T-Beam",
 283      ROM.PRODUCT_TBEAM_S_V1:"LilyGO T-Beam Supreme",
 284      ROM.PRODUCT_TDECK:  "LilyGO T-Deck",
 285      ROM.PRODUCT_T32_10: "LilyGO LoRa32 v1.0",
 286      ROM.PRODUCT_T32_20: "LilyGO LoRa32 v2.0",
 287      ROM.PRODUCT_T32_21: "LilyGO LoRa32 v2.1",
 288      ROM.PRODUCT_H32_V2: "Heltec LoRa32 v2",
 289      ROM.PRODUCT_H32_V3: "Heltec LoRa32 v3",
 290      ROM.PRODUCT_H32_V4: "Heltec LoRa32 v4",
 291      ROM.PRODUCT_TECHO:  "LilyGO T-Echo",
 292      ROM.PRODUCT_RAK4631: "RAK4631",
 293      ROM.PRODUCT_OPENCOM_XL: "openCom XL",
 294      ROM.PRODUCT_HELTEC_T114: "Heltec Mesh Node T114",
 295      ROM.PRODUCT_XIAO_S3: "Seeed XIAO ESP32S3 Wio-SX1262",
 296  }
 297  
 298  platforms = {
 299      ROM.PLATFORM_AVR: "AVR",
 300      ROM.PLATFORM_ESP32:"ESP32",
 301      ROM.PLATFORM_NRF52: "NRF52",
 302  }
 303  
 304  mcus = {
 305      ROM.MCU_1284P: "ATmega1284P",
 306      ROM.MCU_2560:"ATmega2560",
 307      ROM.MCU_ESP32:"Espressif Systems ESP32",
 308      ROM.MCU_NRF52: "Nordic Semiconductor nRF52840",
 309  }
 310  
 311  models = {
 312      0xA4: [410000000, 525000000, 14, "410 - 525 MHz", "rnode_firmware.hex", "SX1278"],
 313      0xA9: [820000000, 1020000000, 17, "820 - 1020 MHz", "rnode_firmware.hex", "SX1276"],
 314      0xA1: [410000000, 525000000, 22, "410 - 525 MHz", "rnode_firmware_t3s3.zip", "SX1268"],
 315      0xA6: [820000000, 1020000000, 22, "820 - 960 MHz", "rnode_firmware_t3s3.zip", "SX1262"],
 316      0xA5: [410000000, 525000000, 17, "410 - 525 MHz", "rnode_firmware_t3s3_sx127x.zip", "SX1278"],
 317      0xAA: [820000000, 1020000000, 17, "820 - 960 MHz", "rnode_firmware_t3s3_sx127x.zip", "SX1276"],
 318      0xAC: [2400000000, 2500000000, 20, "2.4 - 2.5 GHz", "rnode_firmware_t3s3_sx1280_pa.zip", "SX1280"],
 319      0xA2: [410000000, 525000000, 17, "410 - 525 MHz", "rnode_firmware_ng21.zip", "SX1278"],
 320      0xA7: [820000000, 1020000000, 17, "820 - 1020 MHz", "rnode_firmware_ng21.zip", "SX1276"],
 321      0xA3: [410000000, 525000000, 17, "410 - 525 MHz", "rnode_firmware_ng20.zip", "SX1278"],
 322      0xA8: [820000000, 1020000000, 17, "820 - 1020 MHz", "rnode_firmware_ng20.zip", "SX1276"],
 323      0xB3: [420000000, 520000000, 17, "420 - 520 MHz", "rnode_firmware_lora32v20.zip", "SX1278"],
 324      0xB8: [850000000, 950000000, 17, "850 - 950 MHz", "rnode_firmware_lora32v20.zip", "SX1276"],
 325      0xB4: [420000000, 520000000, 17, "420 - 520 MHz", "rnode_firmware_lora32v21.zip", "SX1278"],
 326      0xB9: [850000000, 950000000, 17, "850 - 950 MHz", "rnode_firmware_lora32v21.zip", "SX1276"],
 327      0x04: [420000000, 520000000, 17, "420 - 520 MHz", "rnode_firmware_lora32v21_tcxo.zip", "SX1278"],
 328      0x09: [850000000, 950000000, 17, "850 - 950 MHz", "rnode_firmware_lora32v21_tcxo.zip", "SX1276"],
 329      0xBA: [420000000, 520000000, 17, "420 - 520 MHz", "rnode_firmware_lora32v10.zip", "SX1278"],
 330      0xBB: [850000000, 950000000, 17, "850 - 950 MHz", "rnode_firmware_lora32v10.zip", "SX1276"],
 331      0xC4: [420000000, 520000000, 17, "420 - 520 MHz", "rnode_firmware_heltec32v2.zip", "SX1278"],
 332      0xC9: [850000000, 950000000, 17, "850 - 950 MHz", "rnode_firmware_heltec32v2.zip", "SX1276"],
 333      0xC5: [420000000, 520000000, 22, "420 - 520 MHz", "rnode_firmware_heltec32v3.zip", "SX1268"],
 334      0xCA: [850000000, 950000000, 22, "850 - 950 MHz", "rnode_firmware_heltec32v3.zip", "SX1262"],
 335      0xC8: [860000000, 930000000, 28, "850 - 950 MHz", "rnode_firmware_heltec32v4pa.zip", "SX1262"],
 336      0xC6: [420000000, 520000000, 22, "420 - 520 MHz", "rnode_firmware_heltec_t114.zip", "SX1268"],
 337      0xC7: [850000000, 950000000, 22, "850 - 950 MHz", "rnode_firmware_heltec_t114.zip", "SX1262"],
 338      0xE4: [420000000, 520000000, 17, "420 - 520 MHz", "rnode_firmware_tbeam.zip", "SX1278"],
 339      0xE9: [850000000, 950000000, 17, "850 - 950 MHz", "rnode_firmware_tbeam.zip", "SX1276"],
 340      0xD4: [420000000, 520000000, 22, "420 - 520 MHz", "rnode_firmware_tdeck.zip", "SX1268"],
 341      0xD9: [850000000, 950000000, 22, "850 - 950 MHz", "rnode_firmware_tdeck.zip", "SX1262"],
 342      0xDB: [420000000, 520000000, 22, "420 - 520 MHz", "rnode_firmware_tbeam_supreme.zip", "SX1268"],
 343      0xDC: [850000000, 950000000, 22, "850 - 950 MHz", "rnode_firmware_tbeam_supreme.zip", "SX1262"],
 344      0xE3: [420000000, 520000000, 22, "420 - 520 MHz", "rnode_firmware_tbeam_sx1262.zip", "SX1268"],
 345      0xE8: [850000000, 950000000, 22, "850 - 950 MHz", "rnode_firmware_tbeam_sx1262.zip", "SX1262"],
 346      0x11: [430000000, 510000000, 22, "430 - 510 MHz", "rnode_firmware_rak4631.zip", "SX1262"],
 347      0x12: [779000000, 928000000, 22, "779 - 928 MHz", "rnode_firmware_rak4631.zip", "SX1262"],
 348      0x13: [430000000, 510000000, 22, "430 - 510 MHz", "rnode_firmware_rak4631_sx1280.zip", "SX1262 + SX1280"],
 349      0x14: [779000000, 928000000, 22, "779 - 928 MHz", "rnode_firmware_rak4631_sx1280.zip", "SX1262 + SX1280"],
 350      0x16: [779000000, 928000000, 22, "430 - 510 Mhz", "rnode_firmware_techo.zip", "SX1262"],
 351      0x17: [779000000, 928000000, 22, "779 - 928 Mhz", "rnode_firmware_techo.zip", "SX1262"],
 352      0x21: [820000000, 960000000, 22, "820 - 960 MHz", "rnode_firmware_opencom_xl.zip", "SX1262 + SX1280"],
 353      0xDE: [420000000, 520000000, 22, "420 - 520 MHz", "rnode_firmware_xiao_esp32s3.zip", "SX1262"],
 354      0xDD: [850000000, 950000000, 22, "850 - 950 MHz", "rnode_firmware_xiao_esp32s3.zip", "SX1262"],
 355      0xFE: [100000000, 1100000000, 17, "(Band capabilities unknown)", None, "Unknown"],
 356      0xFF: [100000000, 1100000000, 14, "(Band capabilities unknown)", None, "Unknown"],
 357  }
 358  
 359  CNF_DIR = None
 360  UPD_DIR = None
 361  FWD_DIR = None
 362  EXT_DIR = None
 363  
 364  try:
 365      CNF_DIR = os.path.expanduser("~/.config/rnodeconf")
 366      UPD_DIR = CNF_DIR+"/update"
 367      FWD_DIR = CNF_DIR+"/firmware"
 368      EXT_DIR = CNF_DIR+"/extracted"
 369      RT_PATH = CNF_DIR+"/recovery_esptool.py"
 370      TK_DIR  = CNF_DIR+"/trusted_keys"
 371      ROM_DIR = CNF_DIR+"/eeprom"
 372  
 373      if not os.path.isdir(CNF_DIR):
 374          os.makedirs(CNF_DIR)
 375      if not os.path.isdir(UPD_DIR):
 376          os.makedirs(UPD_DIR)
 377      if not os.path.isdir(FWD_DIR):
 378          os.makedirs(FWD_DIR)
 379      if not os.path.isdir(EXT_DIR):
 380          os.makedirs(EXT_DIR)
 381      if not os.path.isdir(TK_DIR):
 382          os.makedirs(TK_DIR)
 383      if not os.path.isdir(ROM_DIR):
 384          os.makedirs(ROM_DIR)
 385  
 386  except Exception as e:
 387      print("No access to directory "+str(CNF_DIR)+". This utility needs file system access to store firmware and data files. Cannot continue.")
 388      print("The contained exception was:")
 389      print(str(e))
 390      graceful_exit(99)
 391  
 392  squashvw = False
 393  
 394  class RNode():
 395      def __init__(self, serial_instance):
 396          self.serial = serial_instance
 397          self.timeout     = 100
 398  
 399          self.r_frequency = None
 400          self.r_bandwidth = None
 401          self.r_txpower   = None
 402          self.r_sf        = None
 403          self.r_state     = None
 404          self.r_lock      = None
 405  
 406          self.sf = None
 407          self.cr = None
 408          self.txpower = None
 409          self.frequency = None
 410          self.bandwidth = None
 411  
 412          self.detected = None
 413          self.usb_serial_id = None
 414  
 415          self.platform = None
 416          self.mcu = None
 417          self.eeprom = None
 418          self.cfg_sector = None
 419          self.major_version = None
 420          self.minor_version = None
 421          self.version = None
 422  
 423          self.provisioned = None
 424          self.product = None
 425          self.board = None
 426          self.model = None
 427          self.hw_rev = None
 428          self.made = None
 429          self.serialno = None
 430          self.checksum = None
 431          self.device_hash = None
 432          self.firmware_hash = None
 433          self.firmware_hash_target = None
 434          self.signature = None
 435          self.signature_valid = False
 436          self.locally_signed = False
 437          self.vendor = None
 438  
 439          self.min_freq = None
 440          self.max_freq = None
 441          self.max_output = None
 442  
 443          self.configured = None
 444          self.conf_sf = None
 445          self.conf_cr = None
 446          self.conf_txpower = None
 447          self.conf_frequency = None
 448          self.conf_bandwidth = None
 449  
 450      def disconnect(self):
 451          self.leave()
 452          self.serial.close()
 453  
 454      def readLoop(self):
 455          try:
 456              in_frame = False
 457              escape = False
 458              command = KISS.CMD_UNKNOWN
 459              data_buffer = b""
 460              command_buffer = b""
 461              last_read_ms = int(time.time()*1000)
 462  
 463              while self.serial.is_open:
 464                  try:
 465                      data_waiting = self.serial.in_waiting
 466                  except Exception as e:
 467                      data_waiting = False
 468  
 469                  if data_waiting:
 470                      byte = ord(self.serial.read(1))
 471                      last_read_ms = int(time.time()*1000)
 472  
 473                      if (in_frame and byte == KISS.FEND and command == KISS.CMD_ROM_READ):
 474                          self.eeprom = data_buffer
 475                          in_frame = False
 476                          data_buffer = b""
 477                          command_buffer = b""
 478                      elif (in_frame and byte == KISS.FEND and command == KISS.CMD_CFG_READ):
 479                          self.cfg_sector = data_buffer
 480                          in_frame = False
 481                          data_buffer = b""
 482                          command_buffer = b""
 483                      elif (byte == KISS.FEND):
 484                          in_frame = True
 485                          command = KISS.CMD_UNKNOWN
 486                          data_buffer = b""
 487                          command_buffer = b""
 488                      elif (in_frame and len(data_buffer) < 1024):
 489                          if (len(data_buffer) == 0 and command == KISS.CMD_UNKNOWN):
 490                              command = byte
 491                          elif (command == KISS.CMD_ROM_READ):
 492                              if (byte == KISS.FESC):
 493                                  escape = True
 494                              else:
 495                                  if (escape):
 496                                      if (byte == KISS.TFEND):
 497                                          byte = KISS.FEND
 498                                      if (byte == KISS.TFESC):
 499                                          byte = KISS.FESC
 500                                      escape = False
 501                                  data_buffer = data_buffer+bytes([byte])
 502                          elif (command == KISS.CMD_CFG_READ):
 503                              if (byte == KISS.FESC):
 504                                  escape = True
 505                              else:
 506                                  if (escape):
 507                                      if (byte == KISS.TFEND):
 508                                          byte = KISS.FEND
 509                                      if (byte == KISS.TFESC):
 510                                          byte = KISS.FESC
 511                                      escape = False
 512                                  data_buffer = data_buffer+bytes([byte])
 513                          elif (command == KISS.CMD_DATA):
 514                              if (byte == KISS.FESC):
 515                                  escape = True
 516                              else:
 517                                  if (escape):
 518                                      if (byte == KISS.TFEND):
 519                                          byte = KISS.FEND
 520                                      if (byte == KISS.TFESC):
 521                                          byte = KISS.FESC
 522                                      escape = False
 523                                  data_buffer = data_buffer+bytes([byte])
 524                          elif (command == KISS.CMD_FREQUENCY):
 525                              if (byte == KISS.FESC):
 526                                  escape = True
 527                              else:
 528                                  if (escape):
 529                                      if (byte == KISS.TFEND):
 530                                          byte = KISS.FEND
 531                                      if (byte == KISS.TFESC):
 532                                          byte = KISS.FESC
 533                                      escape = False
 534                                  command_buffer = command_buffer+bytes([byte])
 535                                  if (len(command_buffer) == 4):
 536                                      self.r_frequency = command_buffer[0] << 24 | command_buffer[1] << 16 | command_buffer[2] << 8 | command_buffer[3]
 537                                      RNS.log("Radio reporting frequency is "+str(self.r_frequency/1000000.0)+" MHz")
 538                                      self.updateBitrate()
 539  
 540                          elif (command == KISS.CMD_BANDWIDTH):
 541                              if (byte == KISS.FESC):
 542                                  escape = True
 543                              else:
 544                                  if (escape):
 545                                      if (byte == KISS.TFEND):
 546                                          byte = KISS.FEND
 547                                      if (byte == KISS.TFESC):
 548                                          byte = KISS.FESC
 549                                      escape = False
 550                                  command_buffer = command_buffer+bytes([byte])
 551                                  if (len(command_buffer) == 4):
 552                                      self.r_bandwidth = command_buffer[0] << 24 | command_buffer[1] << 16 | command_buffer[2] << 8 | command_buffer[3]
 553                                      RNS.log("Radio reporting bandwidth is "+str(self.r_bandwidth/1000.0)+" KHz")
 554                                      self.updateBitrate()
 555  
 556                          elif (command == KISS.CMD_BT_PIN):
 557                              if (byte == KISS.FESC):
 558                                  escape = True
 559                              else:
 560                                  if (escape):
 561                                      if (byte == KISS.TFEND):
 562                                          byte = KISS.FEND
 563                                      if (byte == KISS.TFESC):
 564                                          byte = KISS.FESC
 565                                      escape = False
 566                                  command_buffer = command_buffer+bytes([byte])
 567                                  if (len(command_buffer) == 4):
 568                                      self.r_bt_pin = command_buffer[0] << 24 | command_buffer[1] << 16 | command_buffer[2] << 8 | command_buffer[3]
 569                                      RNS.log("Bluetooth pairing PIN is: {:06d}".format(self.r_bt_pin))
 570  
 571                          elif (command == KISS.CMD_DEV_HASH):
 572                              if (byte == KISS.FESC):
 573                                  escape = True
 574                              else:
 575                                  if (escape):
 576                                      if (byte == KISS.TFEND):
 577                                          byte = KISS.FEND
 578                                      if (byte == KISS.TFESC):
 579                                          byte = KISS.FESC
 580                                      escape = False
 581                                  command_buffer = command_buffer+bytes([byte])
 582                                  if (len(command_buffer) == 32):
 583                                      self.device_hash = command_buffer
 584  
 585                          elif (command == KISS.CMD_HASHES):
 586                              if (byte == KISS.FESC):
 587                                  escape = True
 588                              else:
 589                                  if (escape):
 590                                      if (byte == KISS.TFEND):
 591                                          byte = KISS.FEND
 592                                      if (byte == KISS.TFESC):
 593                                          byte = KISS.FESC
 594                                      escape = False
 595                                  command_buffer = command_buffer+bytes([byte])
 596                                  if (len(command_buffer) == 33):
 597                                      if command_buffer[0] == 0x01:
 598                                          self.firmware_hash_target = command_buffer[1:]
 599                                      if command_buffer[0] == 0x02:
 600                                          self.firmware_hash = command_buffer[1:]
 601  
 602                          elif (command == KISS.CMD_FW_VERSION):
 603                              if (byte == KISS.FESC):
 604                                  escape = True
 605                              else:
 606                                  if (escape):
 607                                      if (byte == KISS.TFEND):
 608                                          byte = KISS.FEND
 609                                      if (byte == KISS.TFESC):
 610                                          byte = KISS.FESC
 611                                      escape = False
 612                                  command_buffer = command_buffer+bytes([byte])
 613                                  if (len(command_buffer) == 2):
 614                                      self.major_version = command_buffer[0]
 615                                      self.minor_version = command_buffer[1]
 616                                      self.updateVersion()
 617  
 618                          elif (command == KISS.CMD_BOARD):
 619                              self.board = byte
 620  
 621                          elif (command == KISS.CMD_PLATFORM):
 622                              self.platform = byte
 623  
 624                          elif (command == KISS.CMD_MCU):
 625                              self.mcu = byte
 626  
 627                          elif (command == KISS.CMD_TXPOWER):
 628                              self.r_txpower = byte
 629                              RNS.log("Radio reporting TX power is "+str(self.r_txpower)+" dBm")
 630                          elif (command == KISS.CMD_SF):
 631                              self.r_sf = byte
 632                              RNS.log("Radio reporting spreading factor is "+str(self.r_sf))
 633                              self.updateBitrate()
 634                          elif (command == KISS.CMD_CR):
 635                              self.r_cr = byte
 636                              RNS.log("Radio reporting coding rate is "+str(self.r_cr))
 637                              self.updateBitrate()
 638                          elif (command == KISS.CMD_RADIO_STATE):
 639                              self.r_state = byte
 640                          elif (command == KISS.CMD_RADIO_LOCK):
 641                              self.r_lock = byte
 642                          elif (command == KISS.CMD_STAT_RX):
 643                              if (byte == KISS.FESC):
 644                                  escape = True
 645                              else:
 646                                  if (escape):
 647                                      if (byte == KISS.TFEND):
 648                                          byte = KISS.FEND
 649                                      if (byte == KISS.TFESC):
 650                                          byte = KISS.FESC
 651                                      escape = False
 652                                  command_buffer = command_buffer+bytes([byte])
 653                                  if (len(command_buffer) == 4):
 654                                      self.r_stat_rx = ord(command_buffer[0]) << 24 | ord(command_buffer[1]) << 16 | ord(command_buffer[2]) << 8 | ord(command_buffer[3])
 655  
 656                          elif (command == KISS.CMD_STAT_TX):
 657                              if (byte == KISS.FESC):
 658                                  escape = True
 659                              else:
 660                                  if (escape):
 661                                      if (byte == KISS.TFEND):
 662                                          byte = KISS.FEND
 663                                      if (byte == KISS.TFESC):
 664                                          byte = KISS.FESC
 665                                      escape = False
 666                                  command_buffer = command_buffer+bytes([byte])
 667                                  if (len(command_buffer) == 4):
 668                                      self.r_stat_tx = ord(command_buffer[0]) << 24 | ord(command_buffer[1]) << 16 | ord(command_buffer[2]) << 8 | ord(command_buffer[3])
 669                          elif (command == KISS.CMD_STAT_RSSI):
 670                              self.r_stat_rssi = byte-157 # RSSI Offset
 671                          elif (command == KISS.CMD_STAT_SNR):
 672                              self.r_stat_snr = int.from_bytes(bytes([byte]), byteorder="big", signed=True) * 0.25
 673                          elif (command == KISS.CMD_RANDOM):
 674                              self.r_random = byte
 675                          elif (command == KISS.CMD_ERROR):
 676                              if (byte == KISS.ERROR_INITRADIO):
 677                                  RNS.log(str(self)+" hardware initialisation error (code "+RNS.hexrep(byte)+")")
 678                              elif (byte == KISS.ERROR_TXFAILED):
 679                                  RNS.log(str(self)+" hardware TX error (code "+RNS.hexrep(byte)+")")
 680                              else:
 681                                  RNS.log(str(self)+" hardware error (code "+RNS.hexrep(byte)+")")
 682                          elif (command == KISS.CMD_DETECT):
 683                              if byte == KISS.DETECT_RESP:
 684                                  self.detected = True
 685                              else:
 686                                  self.detected = False
 687                          
 688                  else:
 689                      time_since_last = int(time.time()*1000) - last_read_ms
 690                      if len(data_buffer) > 0 and time_since_last > self.timeout:
 691                          RNS.log(str(self)+" serial read timeout")
 692                          data_buffer = b""
 693                          in_frame = False
 694                          command = KISS.CMD_UNKNOWN
 695                          escape = False
 696                      sleep(0.08)
 697  
 698          except Exception as e:
 699              raise e
 700              graceful_exit()
 701  
 702      def updateBitrate(self):
 703          try:
 704              self.bitrate = self.r_sf * ( (4.0/self.r_cr) / (math.pow(2,self.r_sf)/(self.r_bandwidth/1000)) ) * 1000
 705              self.bitrate_kbps = round(self.bitrate/1000.0, 2)
 706          except Exception as e:
 707              self.bitrate = 0
 708  
 709      def updateVersion(self):
 710          minstr = str(self.minor_version)
 711          if len(minstr) == 1:
 712              minstr = "0"+minstr
 713          self.version = str(self.major_version)+"."+minstr
 714  
 715      def detect(self):
 716          kiss_command = bytes([KISS.FEND, KISS.CMD_DETECT, KISS.DETECT_REQ, KISS.FEND, KISS.CMD_FW_VERSION, 0x00, KISS.FEND, KISS.CMD_PLATFORM, 0x00, KISS.FEND, KISS.CMD_MCU, 0x00, KISS.FEND, KISS.CMD_BOARD, 0x00, KISS.FEND, KISS.CMD_DEV_HASH, 0x01, KISS.FEND, KISS.CMD_HASHES, 0x01, KISS.FEND, KISS.CMD_HASHES, 0x02, KISS.FEND])
 717          written = self.serial.write(kiss_command)
 718          if written != len(kiss_command):
 719              raise IOError("An IO error occurred while detecting hardware for "+self(str))
 720  
 721      def leave(self):
 722          kiss_command = bytes([KISS.FEND, KISS.CMD_LEAVE, 0xFF, KISS.FEND])
 723          written = self.serial.write(kiss_command)
 724          if written != len(kiss_command):
 725              raise IOError("An IO error occurred while sending host left command to device")
 726          sleep(1)
 727  
 728      def set_display_intensity(self, intensity):
 729          data = bytes([intensity & 0xFF])
 730          kiss_command = bytes([KISS.FEND])+bytes([KISS.CMD_DISP_INT])+data+bytes([KISS.FEND])
 731          written = self.serial.write(kiss_command)
 732          if written != len(kiss_command):
 733              raise IOError("An IO error occurred while sending display intensity command to device")
 734  
 735      def set_display_blanking(self, blanking_timeout):
 736          data = bytes([blanking_timeout & 0xFF])
 737          kiss_command = bytes([KISS.FEND])+bytes([KISS.CMD_DISP_BLNK])+data+bytes([KISS.FEND])
 738          written = self.serial.write(kiss_command)
 739          if written != len(kiss_command):
 740              raise IOError("An IO error occurred while sending display blanking timeout command to device")
 741  
 742      def set_display_rotation(self, rotation):
 743          data = bytes([rotation & 0xFF])
 744          kiss_command = bytes([KISS.FEND])+bytes([KISS.CMD_DISP_ROT])+data+bytes([KISS.FEND])
 745          written = self.serial.write(kiss_command)
 746          if written != len(kiss_command):
 747              raise IOError("An IO error occurred while sending display rotation command to device")
 748  
 749      def recondition_display(self):
 750          data = bytes([0x01])
 751          kiss_command = bytes([KISS.FEND])+bytes([KISS.CMD_DISP_RCND])+data+bytes([KISS.FEND])
 752          written = self.serial.write(kiss_command)
 753          if written != len(kiss_command):
 754              raise IOError("An IO error occurred while sending display recondition command to device")
 755  
 756      def set_disable_interference_avoidance(self, ia_disabled):
 757          if ia_disabled:
 758              data = bytes([0x01])
 759          else:
 760              data = bytes([0x00])
 761          kiss_command = bytes([KISS.FEND])+bytes([KISS.CMD_DIS_IA])+data+bytes([KISS.FEND])
 762          written = self.serial.write(kiss_command)
 763          if written != len(kiss_command):
 764              raise IOError("An IO error occurred while sending interference avoidance configuration command to device")
 765  
 766      def set_neopixel_intensity(self, intensity):
 767          data = bytes([intensity & 0xFF])
 768          kiss_command = bytes([KISS.FEND])+bytes([KISS.CMD_NP_INT])+data+bytes([KISS.FEND])
 769          written = self.serial.write(kiss_command)
 770          if written != len(kiss_command):
 771              raise IOError("An IO error occurred while sending NeoPixel intensity command to device")
 772  
 773      def set_display_address(self, address):
 774          data = bytes([address & 0xFF])
 775          kiss_command = bytes([KISS.FEND])+bytes([KISS.CMD_DISP_ADR])+data+bytes([KISS.FEND])
 776          written = self.serial.write(kiss_command)
 777          if written != len(kiss_command):
 778              raise IOError("An IO error occurred while sending display address command to device")
 779  
 780      def enable_bluetooth(self):
 781          kiss_command = bytes([KISS.FEND, KISS.CMD_BT_CTRL, 0x01, KISS.FEND])
 782          written = self.serial.write(kiss_command)
 783          if written != len(kiss_command):
 784              raise IOError("An IO error occurred while sending bluetooth enable command to device")
 785  
 786      def disable_bluetooth(self):
 787          kiss_command = bytes([KISS.FEND, KISS.CMD_BT_CTRL, 0x00, KISS.FEND])
 788          written = self.serial.write(kiss_command)
 789          if written != len(kiss_command):
 790              raise IOError("An IO error occurred while sending bluetooth disable command to device")
 791  
 792      def bluetooth_pair(self):
 793          kiss_command = bytes([KISS.FEND, KISS.CMD_BT_CTRL, 0x02, KISS.FEND])
 794          written = self.serial.write(kiss_command)
 795          if written != len(kiss_command):
 796              raise IOError("An IO error occurred while sending bluetooth pair command to device")
 797  
 798      def store_signature(self, signature_bytes):
 799          data = KISS.escape(signature_bytes)
 800          kiss_command = bytes([KISS.FEND])+bytes([KISS.CMD_DEV_SIG])+data+bytes([KISS.FEND])
 801  
 802          written = self.serial.write(kiss_command)
 803          if written != len(kiss_command):
 804              raise IOError("An IO error occurred while sending signature to device")
 805  
 806      def set_firmware_hash(self, hash_bytes):
 807          data = KISS.escape(hash_bytes)
 808          kiss_command = bytes([KISS.FEND])+bytes([KISS.CMD_FW_HASH])+data+bytes([KISS.FEND])
 809  
 810          written = self.serial.write(kiss_command)
 811          if written != len(kiss_command):
 812              raise IOError("An IO error occurred while sending firmware hash to device")
 813  
 814      def indicate_firmware_update(self):
 815          kiss_command = bytes([KISS.FEND])+bytes([KISS.CMD_FW_UPD])+bytes([0x01])+bytes([KISS.FEND])
 816  
 817          written = self.serial.write(kiss_command)
 818          if written != len(kiss_command):
 819              raise IOError("An IO error occurred while sending firmware update command to device")
 820  
 821      def set_wifi_mode(self, mode):
 822          kiss_command = bytes([KISS.FEND])+bytes([KISS.CMD_WIFI_MODE, mode])+bytes([KISS.FEND])
 823          written = self.serial.write(kiss_command)
 824          if written != len(kiss_command):
 825              raise IOError("An IO error occurred while sending wifi mode command to device")
 826  
 827      def set_wifi_channel(self, channel):
 828          try: ch = int(channel)
 829          except: raise ValueError("Invalid WiFi channel")
 830          if ch < 1 or ch > 14: raise ValueError("Invalid WiFi channel")
 831          ch_data = bytes([ch])
 832          data = KISS.escape(ch_data)
 833          kiss_command = bytes([KISS.FEND])+bytes([KISS.CMD_WIFI_CHN])+data+bytes([KISS.FEND])
 834  
 835          written = self.serial.write(kiss_command)
 836          if written != len(kiss_command):
 837              raise IOError("An IO error occurred while sending wifi channel to device")
 838  
 839      def set_wifi_ip(self, ip):
 840          if ip == None: ip_data = bytes([0x00, 0x00, 0x00, 0x00])
 841          else:
 842              ip_data = b""
 843              if not type(ip) == str: raise TypeError("Invalid IP address")
 844              octets = ip.split(".")
 845              if not len(octets) == 4: raise ValueError("Invalid IP address length")
 846              try:
 847                  for i in range(0, 4):
 848                      octet = int(octets[i])
 849                      if octet < 0 or octet > 255: raise ValueError("Invalid IP octet value")
 850                      else: ip_data += bytes([octet])
 851              except Exception as e:
 852                  raise ValueError(f"Could not decode IP address octet: {e}")
 853          
 854          data = KISS.escape(ip_data)
 855          kiss_command = bytes([KISS.FEND])+bytes([KISS.CMD_WIFI_IP])+data+bytes([KISS.FEND])
 856  
 857          written = self.serial.write(kiss_command)
 858          if written != len(kiss_command): raise IOError("An IO error occurred while sending wifi IP address to device")
 859  
 860      def set_wifi_nm(self, nm):
 861          if nm == None: nm_data = bytes([0x00, 0x00, 0x00, 0x00])
 862          else:
 863              nm_data = b""
 864              if not type(nm) == str: raise TypeError("Invalid IP address")
 865              octets = nm.split(".")
 866              if not len(octets) == 4: raise ValueError("Invalid IP address length")
 867              try:
 868                  for i in range(0, 4):
 869                      octet = int(octets[i])
 870                      if octet < 0 or octet > 255: raise ValueError("Invalid IP octet value")
 871                      else: nm_data += bytes([octet])
 872              except Exception as e:
 873                  raise ValueError(f"Could not decode IP address octet: {e}")
 874          
 875          data = KISS.escape(nm_data)
 876          kiss_command = bytes([KISS.FEND])+bytes([KISS.CMD_WIFI_NM])+data+bytes([KISS.FEND])
 877  
 878          written = self.serial.write(kiss_command)
 879          if written != len(kiss_command): raise IOError("An IO error occurred while sending wifi netmask to device")
 880  
 881      def set_wifi_ssid(self, ssid):
 882          if ssid == None: data = bytes([0x00])
 883          else:
 884              ssid_data = ssid.encode("utf-8")+bytes([0x00])
 885              if len(ssid_data) < 0 or len(ssid_data) > 33: raise ValueError("Invalid SSID length")
 886              data = KISS.escape(ssid_data)
 887  
 888          kiss_command = bytes([KISS.FEND])+bytes([KISS.CMD_WIFI_SSID])+data+bytes([KISS.FEND])
 889  
 890          written = self.serial.write(kiss_command)
 891          if written != len(kiss_command):
 892              raise IOError("An IO error occurred while sending wifi SSID to device")
 893  
 894      def set_wifi_psk(self, psk):
 895          if psk == None: data = bytes([0x00])
 896          else:
 897              psk_data = psk.encode("utf-8")+bytes([0x00])
 898              if len(psk_data) < 8 or len(psk_data) > 33: raise ValueError("Invalid psk length")
 899              data = KISS.escape(psk_data)
 900          
 901          kiss_command = bytes([KISS.FEND])+bytes([KISS.CMD_WIFI_PSK])+data+bytes([KISS.FEND])
 902  
 903          written = self.serial.write(kiss_command)
 904          if written != len(kiss_command):
 905              raise IOError("An IO error occurred while sending wifi SSID to device")
 906  
 907      def initRadio(self):
 908          self.setFrequency()
 909          self.setBandwidth()
 910          self.setTXPower()
 911          self.setSpreadingFactor()
 912          self.setCodingRate()
 913          self.setRadioState(KISS.RADIO_STATE_ON)
 914  
 915      def setFrequency(self):
 916          c1 = self.frequency >> 24
 917          c2 = self.frequency >> 16 & 0xFF
 918          c3 = self.frequency >> 8 & 0xFF
 919          c4 = self.frequency & 0xFF
 920          data = KISS.escape(bytes([c1])+bytes([c2])+bytes([c3])+bytes([c4]))
 921  
 922          kiss_command = bytes([KISS.FEND])+bytes([KISS.CMD_FREQUENCY])+data+bytes([KISS.FEND])
 923          written = self.serial.write(kiss_command)
 924          if written != len(kiss_command):
 925              raise IOError("An IO error occurred while configuring frequency for "+self(str))
 926  
 927      def setBandwidth(self):
 928          c1 = self.bandwidth >> 24
 929          c2 = self.bandwidth >> 16 & 0xFF
 930          c3 = self.bandwidth >> 8 & 0xFF
 931          c4 = self.bandwidth & 0xFF
 932          data = KISS.escape(bytes([c1])+bytes([c2])+bytes([c3])+bytes([c4]))
 933  
 934          kiss_command = bytes([KISS.FEND])+bytes([KISS.CMD_BANDWIDTH])+data+bytes([KISS.FEND])
 935          written = self.serial.write(kiss_command)
 936          if written != len(kiss_command):
 937              raise IOError("An IO error occurred while configuring bandwidth for "+self(str))
 938  
 939      def setTXPower(self):
 940          txp = bytes([self.txpower])
 941          kiss_command = bytes([KISS.FEND])+bytes([KISS.CMD_TXPOWER])+txp+bytes([KISS.FEND])
 942          written = self.serial.write(kiss_command)
 943          if written != len(kiss_command):
 944              raise IOError("An IO error occurred while configuring TX power for "+self(str))
 945  
 946      def setSpreadingFactor(self):
 947          sf = bytes([self.sf])
 948          kiss_command = bytes([KISS.FEND])+bytes([KISS.CMD_SF])+sf+bytes([KISS.FEND])
 949          written = self.serial.write(kiss_command)
 950          if written != len(kiss_command):
 951              raise IOError("An IO error occurred while configuring spreading factor for "+self(str))
 952  
 953      def setCodingRate(self):
 954          cr = bytes([self.cr])
 955          kiss_command = bytes([KISS.FEND])+bytes([KISS.CMD_CR])+cr+bytes([KISS.FEND])
 956          written = self.serial.write(kiss_command)
 957          if written != len(kiss_command):
 958              raise IOError("An IO error occurred while configuring coding rate for "+self(str))
 959  
 960      def setRadioState(self, state):
 961          kiss_command = bytes([KISS.FEND])+bytes([KISS.CMD_RADIO_STATE])+bytes([state])+bytes([KISS.FEND])
 962          written = self.serial.write(kiss_command)
 963          if written != len(kiss_command):
 964              raise IOError("An IO error occurred while configuring radio state for "+self(str))
 965  
 966      def setNormalMode(self):
 967          kiss_command = bytes([KISS.FEND, KISS.CMD_CONF_DELETE, 0x00, KISS.FEND])
 968          written = self.serial.write(kiss_command)
 969          if written != len(kiss_command):
 970              raise IOError("An IO error occurred while configuring device mode")
 971  
 972      def setTNCMode(self):
 973          kiss_command = bytes([KISS.FEND, KISS.CMD_CONF_SAVE, 0x00, KISS.FEND])
 974          written = self.serial.write(kiss_command)
 975          if written != len(kiss_command):
 976              raise IOError("An IO error occurred while configuring device mode")
 977  
 978          if self.platform == ROM.PLATFORM_ESP32:
 979              self.hard_reset()
 980  
 981      def wipe_eeprom(self):
 982          kiss_command = bytes([KISS.FEND, KISS.CMD_ROM_WIPE, 0xf8, KISS.FEND])
 983          written = self.serial.write(kiss_command)
 984          if written != len(kiss_command):
 985              raise IOError("An IO error occurred while wiping EEPROM")
 986          sleep(13);
 987          # Due to the current janky emulated EEPROM implementation for the
 988          # RAK4631, extra time must be given to allow for writing.
 989          if self.board == ROM.BOARD_RAK4631:
 990              sleep(10)
 991  
 992      def hard_reset(self):
 993          kiss_command = bytes([KISS.FEND, KISS.CMD_RESET, 0xf8, KISS.FEND])
 994          written = self.serial.write(kiss_command)
 995          if written != len(kiss_command):
 996              raise IOError("An IO error occurred while restarting device")
 997          sleep(2);
 998  
 999      def write_eeprom(self, addr, byte):
1000          write_payload = b"" + bytes([addr, byte])
1001          write_payload = KISS.escape(write_payload)
1002          kiss_command = bytes([KISS.FEND, KISS.CMD_ROM_WRITE]) + write_payload + bytes([KISS.FEND])
1003          written = self.serial.write(kiss_command)
1004          if written != len(kiss_command):
1005              raise IOError("An IO error occurred while writing EEPROM")
1006  
1007  
1008      def download_eeprom(self):
1009          self.eeprom = None
1010          kiss_command = bytes([KISS.FEND, KISS.CMD_ROM_READ, 0x00, KISS.FEND])
1011          written = self.serial.write(kiss_command)
1012          if written != len(kiss_command):
1013              raise IOError("An IO error occurred while downloading EEPROM")
1014  
1015          sleep(0.6)
1016          if self.eeprom == None:
1017              RNS.log("Could not download EEPROM from device. Is a valid firmware installed?")
1018              graceful_exit()
1019          else:
1020              self.parse_eeprom()
1021  
1022      def download_cfg_sector(self):
1023          self.cfg_sector = None
1024          kiss_command = bytes([KISS.FEND, KISS.CMD_CFG_READ, 0x00, KISS.FEND])
1025          written = self.serial.write(kiss_command)
1026          if written != len(kiss_command):
1027              raise IOError("An IO error occurred while downloading config sector")
1028  
1029          sleep(0.6)
1030  
1031      def parse_eeprom(self):
1032          global squashvw;
1033          try:
1034              if self.eeprom[ROM.ADDR_INFO_LOCK] == ROM.INFO_LOCK_BYTE:
1035                  from cryptography.hazmat.primitives import hashes
1036                  from cryptography.hazmat.backends import default_backend
1037  
1038                  self.provisioned = True
1039  
1040                  self.product = self.eeprom[ROM.ADDR_PRODUCT]
1041                  self.model = self.eeprom[ROM.ADDR_MODEL]
1042                  self.hw_rev = self.eeprom[ROM.ADDR_HW_REV]
1043                  self.serialno = bytes([self.eeprom[ROM.ADDR_SERIAL], self.eeprom[ROM.ADDR_SERIAL+1], self.eeprom[ROM.ADDR_SERIAL+2], self.eeprom[ROM.ADDR_SERIAL+3]])
1044                  self.made = bytes([self.eeprom[ROM.ADDR_MADE], self.eeprom[ROM.ADDR_MADE+1], self.eeprom[ROM.ADDR_MADE+2], self.eeprom[ROM.ADDR_MADE+3]])
1045                  self.checksum = b""
1046  
1047                  try:
1048                      self.min_freq = models[self.model][0]
1049                      self.max_freq = models[self.model][1]
1050                      self.max_output = models[self.model][2]
1051                  except Exception as e:
1052                      RNS.log("Error: Model band and output power capabilities are unknown!")
1053                      RNS.log("The contained exception was: "+str(e))
1054                      self.min_freq = 0
1055                      self.max_freq = 0
1056                      self.max_output = 0
1057  
1058                  for i in range(0,16):
1059                      self.checksum = self.checksum+bytes([self.eeprom[ROM.ADDR_CHKSUM+i]])
1060  
1061                  self.signature = b""
1062                  for i in range(0,128):
1063                      self.signature = self.signature+bytes([self.eeprom[ROM.ADDR_SIGNATURE+i]])
1064  
1065                  checksummed_info = b"" + bytes([self.product]) + bytes([self.model]) + bytes([self.hw_rev]) + self.serialno + self.made
1066                  digest = hashes.Hash(hashes.MD5(), backend=default_backend())
1067                  digest.update(checksummed_info)
1068                  checksum = digest.finalize()
1069  
1070                  if self.checksum != checksum:
1071                      self.provisioned = False
1072                      RNS.log("EEPROM checksum mismatch")
1073                      graceful_exit()
1074                  else:
1075                      RNS.log("EEPROM checksum correct")
1076  
1077                      from cryptography.hazmat.primitives import serialization
1078                      from cryptography.hazmat.primitives.serialization import load_der_public_key
1079                      from cryptography.hazmat.primitives.serialization import load_der_private_key
1080                      from cryptography.hazmat.primitives.asymmetric import padding
1081  
1082                      # Try loading local signing key for 
1083                      # validation of self-signed devices
1084                      if os.path.isdir(FWD_DIR) and os.path.isfile(FWD_DIR+"/signing.key"):
1085                          private_bytes = None
1086                          try:
1087                              file = open(FWD_DIR+"/signing.key", "rb")
1088                              private_bytes = file.read()
1089                              file.close()
1090                          except Exception as e:
1091                              RNS.log("Could not load local signing key")
1092  
1093                          try:
1094                              private_key = serialization.load_der_private_key(
1095                                  private_bytes,
1096                                  password=None,
1097                                  backend=default_backend()
1098                              )
1099                              public_key = private_key.public_key()
1100                              public_bytes = public_key.public_bytes(
1101                                  encoding=serialization.Encoding.DER,
1102                                  format=serialization.PublicFormat.SubjectPublicKeyInfo
1103                              )
1104                              public_bytes_hex = RNS.hexrep(public_bytes, delimit=False)
1105  
1106                              vendor_keys = []
1107                              for known in known_keys:
1108                                  vendor_keys.append(known[1])
1109  
1110                              if not public_bytes_hex in vendor_keys:
1111                                  local_key_entry = ["LOCAL", public_bytes_hex]
1112                                  known_keys.append(local_key_entry)
1113  
1114                          except Exception as e:
1115                              RNS.log("Could not deserialize local signing key")
1116                              RNS.log(str(e))
1117  
1118                      # Try loading trusted signing key for 
1119                      # validation of devices
1120                      if os.path.isdir(TK_DIR):
1121                          for f in os.listdir(TK_DIR):
1122                              if os.path.isfile(TK_DIR+"/"+f) and f.endswith(".pubkey"):
1123                                  try:
1124                                      file = open(TK_DIR+"/"+f, "rb")
1125                                      public_bytes = file.read()
1126                                      file.close()
1127  
1128                                      try:
1129                                          public_bytes_hex = RNS.hexrep(public_bytes, delimit=False)
1130  
1131                                          vendor_keys = []
1132                                          for known in known_keys:
1133                                              vendor_keys.append(known[1])
1134  
1135                                          if not public_bytes_hex in vendor_keys:
1136                                              local_key_entry = ["LOCAL", public_bytes_hex]
1137                                              known_keys.append(local_key_entry)
1138  
1139                                      except Exception as e:
1140                                          RNS.log("Could not deserialize trusted signing key "+str(f))
1141                                          RNS.log(str(e))
1142  
1143                                  except Exception as e:
1144                                      RNS.log("Could not load trusted signing key"+str(f))
1145  
1146  
1147                      for known in known_keys:
1148                          vendor = known[0]
1149                          public_hexrep = known[1]
1150                          public_bytes = bytes.fromhex(public_hexrep)
1151                          public_key = load_der_public_key(public_bytes, backend=default_backend())
1152                          try:
1153                              public_key.verify(
1154                                  self.signature,
1155                                  self.checksum,
1156                                  padding.PSS(
1157                                      mgf=padding.MGF1(hashes.SHA256()),
1158                                      salt_length=padding.PSS.MAX_LENGTH
1159                                  ),
1160                                  hashes.SHA256())
1161                              if vendor == "LOCAL":
1162                                  self.locally_signed = True
1163  
1164                              self.signature_valid = True
1165                              self.vendor = vendor
1166                          except Exception as e:
1167                              pass
1168  
1169                      if self.signature_valid:
1170                          RNS.log("Device signature validated")
1171                      else:
1172                          RNS.log("Device signature validation failed")
1173                          if not squashvw:
1174                              print("     ")
1175                              print("     WARNING! This device is NOT verifiable and should NOT be trusted.")
1176                              print("     Someone could have added privacy-breaking or malicious code to it.")
1177                              print("     ")
1178                              print("     Please verify the signing key is present on this machine.")
1179                              print("     Autogenerated keys will not match another machine's signature.")
1180                              print("     ")
1181                              print("     Proceed at your own risk and responsibility! If you created this")
1182                              print("     device yourself, please read the documentation on how to sign your")
1183                              print("     device to avoid this warning.")
1184                              print("     ")
1185                              print("     Always use a firmware downloaded as binaries or compiled from source")
1186                              print("     from one of the following locations:")
1187                              print("     ")
1188                              print("        https://github.com/markqvist/rnode_firmware")
1189                              print("        https://github.com/liberatedsystems/RNode_Firmware_CE")
1190                              print("     ")
1191                              print("     You can reflash and bootstrap this device to a verifiable state")
1192                              print("     by using this utility. It is recommended to do so NOW!")
1193                              print("     ")
1194                              print("     To initialise this device to a verifiable state, please run:")
1195                              print("     ")
1196                              print("              rnodeconf "+str(self.serial.name)+" --autoinstall")
1197                              print("")
1198  
1199  
1200  
1201                  if self.eeprom[ROM.ADDR_CONF_OK] == ROM.CONF_OK_BYTE:
1202                      self.configured = True
1203                      self.conf_sf = self.eeprom[ROM.ADDR_CONF_SF]
1204                      self.conf_cr = self.eeprom[ROM.ADDR_CONF_CR]
1205                      self.conf_txpower = self.eeprom[ROM.ADDR_CONF_TXP]
1206                      self.conf_frequency = self.eeprom[ROM.ADDR_CONF_FREQ] << 24 | self.eeprom[ROM.ADDR_CONF_FREQ+1] << 16 | self.eeprom[ROM.ADDR_CONF_FREQ+2] << 8 | self.eeprom[ROM.ADDR_CONF_FREQ+3]
1207                      self.conf_bandwidth = self.eeprom[ROM.ADDR_CONF_BW] << 24 | self.eeprom[ROM.ADDR_CONF_BW+1] << 16 | self.eeprom[ROM.ADDR_CONF_BW+2] << 8 | self.eeprom[ROM.ADDR_CONF_BW+3]
1208                  else:
1209                      self.configured = False
1210              else:
1211                  self.provisioned = False
1212          except Exception as e:
1213              self.provisioned = False
1214              RNS.log("Invalid EEPROM data, could not parse device EEPROM.")
1215              RNS.log("The contained exception was: "+str(e))
1216  
1217  
1218      def device_probe(self):
1219          sleep(2.5)
1220          self.detect()
1221          sleep(0.75)
1222          if self.detected == True:
1223              RNS.log("Device connected")
1224              RNS.log("Current firmware version: "+self.version)
1225              return True
1226          else:
1227              raise IOError("Got invalid response while detecting device")
1228  
1229  selected_version = None
1230  selected_hash = None
1231  firmware_version_url          = "https://github.com/markqvist/rnode_firmware/releases/latest/download/release.json"
1232  fallback_firmware_version_url = "https://github.com/markqvist/rnode_firmware/releases/latest/download/release.json"
1233  def ensure_firmware_file(fw_filename):
1234      global selected_version, selected_hash, upd_nocheck
1235      if fw_filename == "extracted_rnode_firmware.zip":
1236          vfpath = EXT_DIR+"/extracted_rnode_firmware.version"
1237          if os.path.isfile(vfpath):
1238              required_files = [
1239                  "extracted_console_image.bin",
1240                  "extracted_rnode_firmware.bin",
1241                  "extracted_rnode_firmware.boot_app0",
1242                  "extracted_rnode_firmware.bootloader",
1243                  "extracted_rnode_firmware.partitions",
1244              ]
1245              parts_missing = False
1246              for rf in required_files:
1247                  if not os.path.isfile(EXT_DIR+"/"+rf):
1248                      parts_missing = True
1249  
1250              if parts_missing:
1251                  RNS.log("One or more required firmware files are missing from the extracted RNode")
1252                  RNS.log("Firmware archive. Installation cannot continue. Please try extracting the")
1253                  RNS.log("firmware again with the --extract-firmware option.")
1254                  graceful_exit(184)
1255  
1256              vf = open(vfpath, "rb")
1257              release_info = vf.read().decode("utf-8").strip()
1258              selected_version = release_info.split()[0]
1259              selected_hash = release_info.split()[1]
1260              RNS.log("Using existing firmware file: "+fw_filename+" for version "+selected_version)
1261          else:
1262              RNS.log("No extracted firmware is available, cannot continue.")
1263              RNS.log("Extract a firmware from an existing RNode first, using the --extract-firmware option.")
1264              graceful_exit(183)
1265  
1266      else:
1267          try:
1268              if not upd_nocheck:
1269                  try:
1270                      # if custom firmware url, download latest release
1271                      if selected_version == None and fw_url == None:
1272                          urlretrieve(firmware_version_url, UPD_DIR+"/release_info.json")
1273                          import json
1274                          with open(UPD_DIR+"/release_info.json", "rb") as rif:
1275                              rdat = json.loads(rif.read())
1276                              variant = rdat[fw_filename]
1277                              with open(UPD_DIR+"/"+fw_filename+".version.latest", "wb") as verf:
1278                                  inf_str = str(variant["version"])+" "+str(variant["hash"])
1279                                  verf.write(inf_str.encode("utf-8"))
1280  
1281                      else:
1282                          if fw_url != None:
1283                              if selected_version == None:
1284                                  version_url = fw_url+"latest/download/release.json"
1285                              else:
1286                                  version_url = fw_url+"download/"+selected_version+"/release.json"
1287                          else:
1288                                  version_url = firmware_update_url+selected_version+"/release.json"
1289                          try:
1290                              RNS.log("Retrieving specified version info from "+version_url)
1291                              urlretrieve(version_url, UPD_DIR+"/version_release_info.json")
1292                              import json
1293                              with open(UPD_DIR+"/version_release_info.json", "rb") as rif:
1294                                  rdat = json.loads(rif.read())
1295                                  variant = rdat[fw_filename]
1296                                  with open(UPD_DIR+"/"+fw_filename+".version.latest", "wb") as verf:
1297                                      inf_str = str(variant["version"])+" "+str(variant["hash"])
1298                                      verf.write(inf_str.encode("utf-8"))
1299                          except Exception as e:
1300                              RNS.log("Failed to retrive version information for your board.")
1301                              RNS.log("Check your internet connection and try again.")
1302                              RNS.log("If you don't have Internet access currently, use the --fw-version option to manually specify a version.")
1303                              RNS.log("You can also use --extract to copy the firmware from a known-good RNode of the same model.")
1304                              graceful_exit()
1305                  except Exception as e:
1306                      # if custom firmware url, don't fallback
1307                      if fw_url != None:
1308                          RNS.log("Failed to retrive version information for your board from the specified url.")
1309                          RNS.log("Check your internet connection and try again.")
1310                          RNS.log("If you don't have Internet access currently, use the --fw-version option to manually specify a version.")
1311                          RNS.log("You can also use --extract to copy the firmware from a known-good RNode of the same model.")
1312                          graceful_exit()
1313  
1314                      RNS.log("")
1315                      RNS.log("WARNING!")
1316                      RNS.log("Failed to retrieve latest version information for your board from the default server.")
1317                      RNS.log("Will retry using the following fallback URL: "+fallback_firmware_version_url)
1318                      RNS.log("")
1319                      RNS.log("Hit enter if you want to proceed")
1320                      input()
1321                      try:
1322                          urlretrieve(fallback_firmware_version_url, UPD_DIR+"/fallback_release_info.json")
1323                          import json
1324                          with open(UPD_DIR+"/fallback_release_info.json", "rb") as rif:
1325                              rdat = json.loads(rif.read())
1326                              variant = rdat[fw_filename]
1327                              with open(UPD_DIR+"/"+fw_filename+".version.latest", "wb") as verf:
1328                                  inf_str = str(variant["version"])+" "+str(variant["hash"])
1329                                  verf.write(inf_str.encode("utf-8"))
1330  
1331                      except Exception as e:
1332                          RNS.log("Error while trying fallback URL: "+str(e))
1333                          raise e
1334  
1335                  import shutil
1336                  file = open(UPD_DIR+"/"+fw_filename+".version.latest", "rb")
1337                  release_info = file.read().decode("utf-8").strip()
1338                  selected_version = release_info.split()[0]
1339                  if selected_version == "not":
1340                      RNS.log("No valid version found for this board, exiting.")
1341                      graceful_exit(199)
1342  
1343                  selected_hash = release_info.split()[1]
1344                  if not os.path.isdir(UPD_DIR+"/"+selected_version):
1345                      os.makedirs(UPD_DIR+"/"+selected_version)
1346                  shutil.copy(UPD_DIR+"/"+fw_filename+".version.latest", UPD_DIR+"/"+selected_version+"/"+fw_filename+".version")
1347                  RNS.log("The selected firmware for this board is version "+selected_version)
1348  
1349              else:
1350                  if selected_version == None:
1351                      RNS.log("Online firmware version check was disabled, but no firmware version specified for install.")
1352                      RNS.log("use the --fw-version option to manually specify a version.")
1353                      graceful_exit(98)
1354  
1355              # if custom firmware url, use it
1356              if fw_url != None:
1357                  update_target_url = fw_url+"download/"+selected_version+"/"+fw_filename
1358                  RNS.log("Retrieving firmware from custom url "+update_target_url)
1359              else:
1360                  update_target_url = firmware_update_url+selected_version+"/"+fw_filename
1361  
1362              try:
1363                  if not os.path.isdir(UPD_DIR+"/"+selected_version):
1364                      os.makedirs(UPD_DIR+"/"+selected_version)
1365  
1366                  if not os.path.isfile(UPD_DIR+"/"+selected_version+"/"+fw_filename):
1367                      RNS.log("Firmware "+UPD_DIR+"/"+selected_version+"/"+fw_filename+" not found.")
1368                      RNS.log("Downloading missing firmware file: "+fw_filename+" for version "+selected_version)
1369                      urlretrieve(update_target_url, UPD_DIR+"/"+selected_version+"/"+fw_filename)
1370                      RNS.log("Firmware file downloaded")
1371                  else:
1372                      RNS.log("Using existing firmware file: "+fw_filename+" for version "+selected_version)
1373  
1374                  try:
1375                      if selected_hash == None:
1376                          try:
1377                              file = open(UPD_DIR+"/"+selected_version+"/"+fw_filename+".version", "rb")
1378                              release_info = file.read().decode("utf-8").strip()
1379                              selected_hash = release_info.split()[1]
1380                          except Exception as e:
1381                              RNS.log("Could not read locally cached release information.")
1382                              RNS.log("Ensure "+UPD_DIR+"/"+selected_version+"/"+fw_filename+".version exists and has the correct format and hash.")
1383                              RNS.log("You can clear the cache with the --clear-cache option and try again.")
1384  
1385                          if selected_hash == None:
1386                              RNS.log("No release hash found for "+fw_filename+". The firmware integrity could not be verified.")
1387                              graceful_exit(97)
1388  
1389                      RNS.log("Verifying firmware integrity...")
1390                      fw_file = open(UPD_DIR+"/"+selected_version+"/"+fw_filename, "rb")
1391                      expected_hash = bytes.fromhex(selected_hash)
1392                      file_hash = hashlib.sha256(fw_file.read()).hexdigest()
1393                      if file_hash == selected_hash:
1394                          pass
1395                      else:
1396                          RNS.log("")
1397                          RNS.log(f"Firmware hash {file_hash} but should be {selected_hash}, possibly due to download corruption.")
1398                          RNS.log("Firmware corrupt. Try clearing the local firmware cache with: rnodeconf --clear-cache")
1399                          graceful_exit(96)
1400  
1401                  except Exception as e:
1402                      RNS.log("An error occurred while checking firmware file integrity. The contained exception was:")
1403                      RNS.log(str(e))
1404                      graceful_exit(95)
1405  
1406              except Exception as e:
1407                  RNS.log("Could not download required firmware file: ")
1408                  RNS.log(str(update_target_url))
1409                  RNS.log("The contained exception was:")
1410                  RNS.log(str(e))
1411                  graceful_exit()
1412  
1413          except Exception as e:
1414              RNS.log("An error occurred while reading version information for "+str(fw_filename)+". The contained exception was:")
1415              RNS.log(str(e))
1416              graceful_exit()
1417  
1418  def rnode_open_serial(port):
1419      import serial
1420      return serial.Serial(
1421          port = port,
1422          baudrate = rnode_baudrate,
1423          bytesize = 8,
1424          parity = serial.PARITY_NONE,
1425          stopbits = 1,
1426          xonxoff = False,
1427          rtscts = False,
1428          timeout = 0,
1429          inter_byte_timeout = None,
1430          write_timeout = None,
1431          dsrdtr = False
1432      )
1433      
1434      
1435  def graceful_exit(C=0):
1436      if RNS.vendor.platformutils.is_windows():
1437          RNS.log("Windows detected; delaying DTR",RNS.LOG_VERBOSE) 
1438          if rnode:
1439              RNS.log("Sending \"Leave\" to Rnode",RNS.LOG_VERBOSE)
1440              rnode.leave() # Leave has wait built in
1441          elif rnode_serial:
1442              RNS.log("Closing raw serial",RNS.LOG_VERBOSE)
1443              sleep(1) # Wait for MCU to complete operation before DTR goes false
1444              rnode_serial.close()
1445      RNS.log("Exiting: Code "+str(C),RNS.LOG_INFO)
1446      exit(C)
1447  
1448  
1449  device_signer = None
1450  force_update = False
1451  upd_nocheck = False
1452  def main():
1453      global mapped_product, mapped_model, fw_filename, fw_url, selected_version, force_update, upd_nocheck, device_signer
1454  
1455      try:
1456          if not util.find_spec("serial"):
1457              raise ImportError("Serial module could not be found")
1458      except ImportError:
1459          print("")
1460          print("RNode Config Utility needs pyserial to work.")
1461          print("You can install it with: pip3 install pyserial")
1462          print("")
1463          graceful_exit()
1464  
1465      try:
1466          if not util.find_spec("cryptography"):
1467              raise ImportError("Cryptography module could not be found")
1468      except ImportError:
1469          print("")
1470          print("RNode Config Utility needs the cryptography module to work.")
1471          print("You can install it with: pip3 install cryptography")
1472          print("")
1473          graceful_exit()
1474  
1475      import serial
1476      from serial.tools import list_ports
1477  
1478      try:
1479          parser = argparse.ArgumentParser(description="RNode Configuration and firmware utility. This program allows you to change various settings and startup modes of RNode. It can also install, flash and update the firmware on supported devices.")
1480          parser.add_argument("-i", "--info", action="store_true", help="Show device info")
1481          parser.add_argument("-a", "--autoinstall", action="store_true", help="Automatic installation on various supported devices")
1482          parser.add_argument("-u", "--update", action="store_true", help="Update firmware to the latest version")
1483          parser.add_argument("-U", "--force-update", action="store_true", help="Update to specified firmware even if version matches or is older than installed version")
1484          parser.add_argument("--fw-version", action="store", metavar="version", default=None, help="Use a specific firmware version for update or autoinstall")
1485          parser.add_argument("--fw-url", action="store", metavar="url", default=None, help="Use an alternate firmware download URL")
1486          parser.add_argument("--nocheck", action="store_true", help="Don't check for firmware updates online")
1487          parser.add_argument("-e", "--extract", action="store_true", help="Extract firmware from connected RNode for later use")
1488          parser.add_argument("-E", "--use-extracted", action="store_true", help="Use the extracted firmware for autoinstallation or update")
1489          parser.add_argument("-C", "--clear-cache", action="store_true", help="Clear locally cached firmware files")
1490          parser.add_argument("--baud-flash", action="store", metavar="baud_flash", type=str, default="921600", help="Set specific baud rate when flashing device. Default is 921600")
1491  
1492          parser.add_argument("-N", "--normal", action="store_true", help="Switch device to normal mode")
1493          parser.add_argument("-T", "--tnc", action="store_true", help="Switch device to TNC mode")
1494  
1495          parser.add_argument("-b", "--bluetooth-on", action="store_true", help="Turn device bluetooth on")
1496          parser.add_argument("-B", "--bluetooth-off", action="store_true", help="Turn device bluetooth off")
1497          parser.add_argument("-p", "--bluetooth-pair", action="store_true", help="Put device into bluetooth pairing mode")
1498  
1499          parser.add_argument("-w", "--wifi", action="store", metavar="mode", default=None, help="Set WiFi mode (OFF, AP or STATION)")
1500          parser.add_argument("--channel", action="store", metavar="channel", default=None, help="Set WiFi channel")
1501          parser.add_argument("--ssid", action="store", metavar="ssid", default=None, help="Set WiFi SSID (NONE to delete)")
1502          parser.add_argument("--psk", action="store", metavar="psk", default=None, help="Set WiFi PSK (NONE to delete)")
1503          parser.add_argument("--show-psk", action="store_true", default=False, help="Display stored WiFi PSK")
1504          parser.add_argument("--ip", action="store", metavar="ip", default=None, help="Set static WiFi IP address (NONE for DHCP)")
1505          parser.add_argument("--nm", action="store", metavar="nm", default=None, help="Set static WiFi network mask (NONE for DHCP)")
1506  
1507          parser.add_argument("-D", "--display", action="store", metavar="i", type=int, default=None, help="Set display intensity (0-255)")
1508          parser.add_argument("-t", "--timeout", action="store", metavar="s", type=int, default=None, help="Set display timeout in seconds, 0 to disable")
1509          parser.add_argument("-R", "--rotation", action="store", metavar="rotation", type=int, default=None, help="Set display rotation, valid values are 0 through 3")
1510          parser.add_argument("--display-addr", action="store", metavar="byte", type=str, default=None, help="Set display address as hex byte (00 - FF)")
1511          parser.add_argument("--recondition-display", action="store_true", help="Start display reconditioning")
1512  
1513          parser.add_argument("--np", action="store", metavar="i", type=int, default=None, help="Set NeoPixel intensity (0-255)")
1514  
1515          parser.add_argument("--freq", action="store", metavar="Hz", type=int, default=None, help="Frequency in Hz for TNC mode")
1516          parser.add_argument("--bw", action="store", metavar="Hz", type=int, default=None, help="Bandwidth in Hz for TNC mode")
1517          parser.add_argument("--txp", action="store", metavar="dBm", type=int, default=None, help="TX power in dBm for TNC mode")
1518          parser.add_argument("--sf", action="store", metavar="factor", type=int, default=None, help="Spreading factor for TNC mode (7 - 12)")
1519          parser.add_argument("--cr", action="store", metavar="rate", type=int, default=None, help="Coding rate for TNC mode (5 - 8)")
1520  
1521          parser.add_argument("-x", "--ia-enable", action="store_true", help="Enable interference avoidance")
1522          parser.add_argument("-X", "--ia-disable", action="store_true", help="Disable interference avoidance")
1523  
1524          parser.add_argument("-c", "--config", action="store_true", help="Print device configuration")
1525  
1526          parser.add_argument("--eeprom-backup", action="store_true", help="Backup EEPROM to file")
1527          parser.add_argument("--eeprom-dump", action="store_true", help="Dump EEPROM to console")
1528          parser.add_argument("--eeprom-wipe", action="store_true", help="Unlock and wipe EEPROM")
1529  
1530          parser.add_argument("-P", "--public", action="store_true", help="Display public part of signing key")
1531          parser.add_argument("--trust-key", action="store", metavar="hexbytes", type=str, default=None, help="Public key to trust for device verification")
1532  
1533          parser.add_argument("--version", action="store_true", help="Print program version and exit")
1534  
1535          parser.add_argument("-f", "--flash", action="store_true", help="Flash firmware and bootstrap EEPROM")
1536          parser.add_argument("-r", "--rom", action="store_true", help="Bootstrap EEPROM without flashing firmware")
1537          parser.add_argument("-k", "--key", action="store_true", help="Generate a new signing key and exit") # 
1538          parser.add_argument("-S", "--sign", action="store_true", help="Display public part of signing key")
1539          parser.add_argument("-H", "--firmware-hash", action="store", help="Set installed firmware hash")
1540          parser.add_argument("-K", "--get-target-firmware-hash", action="store_true", help=argparse.SUPPRESS) # Get target firmware hash from device
1541          parser.add_argument("-L", "--get-firmware-hash", action="store_true", help=argparse.SUPPRESS) # Get calculated firmware hash from device
1542          parser.add_argument("--platform", action="store", metavar="platform", type=str, default=None, help="Platform specification for device bootstrap")
1543          parser.add_argument("--product", action="store", metavar="product", type=str, default=None, help="Product specification for device bootstrap") # 
1544          parser.add_argument("--model", action="store", metavar="model", type=str, default=None, help="Model code for device bootstrap")
1545          parser.add_argument("--hwrev", action="store", metavar="revision", type=int, default=None, help="Hardware revision for device bootstrap")
1546  
1547          parser.add_argument("port", nargs="?", default=None, help="serial port where RNode is attached", type=str)
1548          args = parser.parse_args()
1549  
1550          def print_donation_block():
1551              print("  Ethereum  : "+eth_addr)
1552              print("  Bitcoin   : "+btc_addr)
1553              print("  Monero    : "+xmr_addr)
1554              print("  Ko-Fi     : https://ko-fi.com/markqvist")
1555              print("  LiberaPay : https://liberapay.com/reticulum")
1556              print("")
1557              print("  Info      : https://reticulum.network")
1558              print("  Code      : https://github.com/markqvist")
1559  
1560          if args.version:
1561              print("rnodeconf "+program_version)
1562              graceful_exit(0)
1563  
1564          if args.clear_cache:
1565              RNS.log("Clearing local firmware cache...")
1566              import shutil
1567              shutil.rmtree(UPD_DIR)
1568              RNS.log("Done")
1569              graceful_exit(0)
1570  
1571          if args.fw_version != None:
1572              selected_version = args.fw_version
1573              try: 
1574                  check_float = float(selected_version)
1575              except ValueError:
1576                  RNS.log("Selected version \""+selected_version+"\" does not appear to be a number.")
1577                  graceful_exit()
1578  
1579          if args.fw_url != None:
1580              fw_url = args.fw_url
1581  
1582          if args.force_update:
1583              force_update = True
1584  
1585          if args.nocheck:
1586              upd_nocheck = True
1587              
1588          if args.public or args.key or args.flash or args.rom or args.autoinstall or args.trust_key:
1589              from cryptography.hazmat.primitives import hashes
1590              from cryptography.hazmat.backends import default_backend
1591              from cryptography.hazmat.primitives import serialization
1592              from cryptography.hazmat.primitives.serialization import load_der_public_key
1593              from cryptography.hazmat.primitives.serialization import load_der_private_key
1594              from cryptography.hazmat.primitives.asymmetric import rsa
1595              from cryptography.hazmat.primitives.asymmetric import padding
1596  
1597          clear = lambda: os.system('clear')
1598  
1599          if args.trust_key:
1600              try:
1601                  public_bytes = bytes.fromhex(args.trust_key)
1602                  try:
1603                      public_key = load_der_public_key(public_bytes, backend=default_backend())
1604                      key_hash = hashlib.sha256(public_bytes).hexdigest()
1605                      RNS.log("Trusting key: "+str(key_hash))
1606                      f = open(TK_DIR+"/"+str(key_hash)+".pubkey", "wb")
1607                      f.write(public_bytes)
1608                      f.close()
1609  
1610                  except Exception as e:
1611                      RNS.log("Could not create public key from supplied data. Check that the key format is valid.")
1612                      RNS.log(str(e))
1613  
1614              except Exception as e:
1615                  RNS.log("Invalid key data supplied")
1616              graceful_exit(0)
1617  
1618          if args.use_extracted and ((args.update and args.port != None) or args.autoinstall):
1619              print("")
1620              print("You have specified that rnodeconf should use a firmware extracted")
1621              print("from another device. Please note that this *only* works if you are")
1622              print("targeting a device of the same type that the firmware came from!")
1623              print("")
1624              print("Flashing this firmware to a device it was not created for will most")
1625              print("likely result in it being inoperable until it is updated with the")
1626              print("correct firmware. Hit enter to continue.")
1627              input()
1628  
1629          if args.extract:
1630              # clear()
1631              print("")
1632              print("RNode Firmware Extraction")
1633              print("")
1634              if not args.port:
1635                  ports = list_ports.comports()
1636                  portlist = []
1637                  for port in ports:
1638                      portlist.insert(0, port) 
1639                  
1640                  pi = 1
1641                  print("Detected serial ports:")
1642                  for port in portlist:
1643                      print("  ["+str(pi)+"] "+str(port.device)+" ("+str(port.product)+", "+str(port.serial_number)+")")
1644                      pi += 1
1645  
1646                  print("\nEnter the number of the serial port your device is connected to:\n? ", end="")
1647                  try:
1648                      c_port = int(input())
1649                      if c_port < 1 or c_port > len(ports):
1650                          raise ValueError()
1651  
1652                      selected_port = portlist[c_port-1]
1653                  except Exception as e:
1654                      print("That port does not exist, exiting now.")
1655                      graceful_exit()
1656  
1657                  if selected_port == None:
1658                      print("Could not select port, exiting now.")
1659                      graceful_exit()
1660  
1661                  port_path = selected_port.device
1662                  port_product = selected_port.product
1663                  port_serialno = selected_port.serial_number
1664  
1665                  print("\nOk, using device on "+str(port_path)+" ("+str(port_product)+", "+str(port_serialno)+")")
1666  
1667              else:
1668                  ports = list_ports.comports()
1669  
1670                  for port in ports:
1671                      if port.device == args.port:
1672                          selected_port = port
1673  
1674                  if selected_port == None:
1675                      print("Could not find specified port "+str(args.port)+", exiting now")
1676                      graceful_exit()
1677  
1678                  port_path = selected_port.device
1679                  port_product = selected_port.product
1680                  port_serialno = selected_port.serial_number
1681  
1682                  print("\nUsing device on "+str(port_path))
1683  
1684              print("\nProbing device...")
1685  
1686              try:
1687                  rnode_serial = rnode_open_serial(port_path)
1688              except Exception as e:
1689                  RNS.log("Could not open the specified serial port. The contained exception was:")
1690                  RNS.log(str(e))
1691                  graceful_exit()
1692  
1693              rnode = RNode(rnode_serial)
1694              rnode.usb_serial_id = port_serialno
1695              thread = threading.Thread(target=rnode.readLoop, daemon=True).start()
1696              try:
1697                  rnode.device_probe()
1698              except Exception as e:
1699                  RNS.log("No answer from device")
1700  
1701              if rnode.detected:
1702                  RNS.log("Trying to read EEPROM...")
1703                  rnode.download_eeprom()
1704              else:
1705                  RNS.log("Could not detect a connected RNode")
1706  
1707              if rnode.platform == ROM.PLATFORM_ESP32:
1708                  if rnode.provisioned:
1709                      if not rnode.signature_valid:
1710                          print("\nThe device signature in this RNode is unknown and cannot be verified. It is still")
1711                          print("possible to extract the firmware from it, but you should make absolutely sure that")
1712                          print("it comes from a trusted source. It is possible that someone could have modified the")
1713                          print("firmware. If that is the case, these modifications will propagate to any new RNodes")
1714                          print("descendent from this one!")
1715                          print("\nHit enter if you are sure you want to continue.")
1716                          input()
1717  
1718                      if rnode.firmware_hash != None:
1719                          extracted_hash = rnode.firmware_hash
1720                          extracted_version = rnode.version
1721                          rnode.disconnect()
1722                          v_str = str(extracted_version)+" "+RNS.hexrep(extracted_hash, delimit=False)
1723                          print("\nFound RNode Firmvare v"+v_str)
1724  
1725                          print("\nReady to extract firmware images from the RNode")
1726                          print("Press enter to start the extraction process")
1727                          input()
1728                          extract_recovery_esptool()
1729  
1730                          hash_f = open(EXT_DIR+"/extracted_rnode_firmware.version", "wb")
1731                          hash_f.write(v_str.encode("utf-8"))
1732                          hash_f.close()
1733  
1734                          extraction_parts = [
1735                              ("bootloader", "python \""+CNF_DIR+"/recovery_esptool.py\" --chip esp32 --port "+port_path+" --baud "+args.baud_flash+" --before default_reset --after hard_reset read_flash 0x1000 0x4650 \""+EXT_DIR+"/extracted_rnode_firmware.bootloader\""),
1736                              ("partition table", "python \""+CNF_DIR+"/recovery_esptool.py\" --chip esp32 --port "+port_path+" --baud "+args.baud_flash+" --before default_reset --after hard_reset read_flash 0x8000 0xC00 \""+EXT_DIR+"/extracted_rnode_firmware.partitions\""),
1737                              ("app boot", "python \""+CNF_DIR+"/recovery_esptool.py\" --chip esp32 --port "+port_path+" --baud "+args.baud_flash+" --before default_reset --after hard_reset read_flash 0xe000 0x2000 \""+EXT_DIR+"/extracted_rnode_firmware.boot_app0\""),
1738                              ("application image", "python \""+CNF_DIR+"/recovery_esptool.py\" --chip esp32 --port "+port_path+" --baud "+args.baud_flash+" --before default_reset --after hard_reset read_flash 0x10000 0x200000 \""+EXT_DIR+"/extracted_rnode_firmware.bin\""),
1739                              ("console image", "python \""+CNF_DIR+"/recovery_esptool.py\" --chip esp32 --port "+port_path+" --baud "+args.baud_flash+" --before default_reset --after hard_reset read_flash 0x210000 0x1F0000 \""+EXT_DIR+"/extracted_console_image.bin\""),
1740                          ]
1741                          import subprocess, shlex
1742                          for part, command in extraction_parts:
1743                              print("Extracting "+part+"...")
1744                              if subprocess.call(shlex.split(command), stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) != 0:
1745                                  print("The extraction failed, the following command did not complete successfully:\n"+command)
1746                                  exit(182)
1747  
1748                          print("\nFirmware successfully extracted!")
1749                          print("\nYou can now use this firmware to update or autoinstall other RNodes")
1750                          exit()
1751                      else:
1752                          print("Could not read firmware information from device")
1753  
1754                  print("\nRNode firmware extraction failed")
1755                  graceful_exit(180)
1756              else:
1757                  print("\nFirmware extraction is currently only supported on ESP32-based RNodes.")
1758                  graceful_exit(170)
1759  
1760          if args.autoinstall:
1761              clear()
1762              if not args.port:
1763                  print("\nHello!\n\nThis guide will help you install the RNode firmware on supported")
1764                  print("and homebrew devices. Please connect the device you wish to set\nup now. Hit enter when it is connected.")
1765                  input()
1766  
1767              global squashvw
1768              squashvw = True
1769  
1770              selected_port = None
1771              if not args.port:
1772                  ports = list_ports.comports()
1773                  portlist = []
1774                  for port in ports:
1775                      portlist.insert(0, port) 
1776                  
1777                  pi = 1
1778                  print("Detected serial ports:")
1779                  for port in portlist:
1780                      print("  ["+str(pi)+"] "+str(port.device)+" ("+str(port.product)+", "+str(port.serial_number)+")")
1781                      pi += 1
1782  
1783                  print("\nEnter the number of the serial port your device is connected to:\n? ", end="")
1784                  try:
1785                      c_port = int(input())
1786                      if c_port < 1 or c_port > len(ports):
1787                          raise ValueError()
1788  
1789                      selected_port = portlist[c_port-1]
1790                  except Exception as e:
1791                      print("That port does not exist, exiting now.")
1792                      graceful_exit()
1793  
1794                  if selected_port == None:
1795                      print("Could not select port, exiting now.")
1796                      graceful_exit()
1797  
1798                  port_path = selected_port.device
1799                  port_product = selected_port.product
1800                  port_serialno = selected_port.serial_number
1801  
1802                  clear()
1803                  print("\nOk, using device on "+str(port_path)+" ("+str(port_product)+", "+str(port_serialno)+")")
1804  
1805              else:
1806                  ports = list_ports.comports()
1807  
1808                  for port in ports:
1809                      if port.device == args.port:
1810                          selected_port = port
1811  
1812                  if selected_port == None:
1813                      print("Could not find specified port "+str(args.port)+", exiting now")
1814                      graceful_exit()
1815  
1816                  port_path = selected_port.device
1817                  port_product = selected_port.product
1818                  port_serialno = selected_port.serial_number
1819  
1820                  print("\nUsing device on "+str(port_path))
1821  
1822              print("\nProbing device...")
1823  
1824              try:
1825                  rnode_serial = rnode_open_serial(port_path)
1826              except Exception as e:
1827                  RNS.log("Could not open the specified serial port. The contained exception was:")
1828                  RNS.log(str(e))
1829                  graceful_exit()
1830  
1831              rnode = RNode(rnode_serial)
1832              rnode.usb_serial_id = port_serialno
1833              thread = threading.Thread(target=rnode.readLoop, daemon=True).start()
1834              try:
1835                  rnode.device_probe()
1836              except Exception as e:
1837                  RNS.log("No answer from device")
1838  
1839              if rnode.detected:
1840                  RNS.log("Trying to read EEPROM...")
1841                  rnode.download_eeprom()
1842  
1843              if rnode.provisioned and rnode.signature_valid:
1844                  print("\nThis device is already installed and provisioned. No further action will")
1845                  print("be taken. If you wish to completely reinstall this device, you must first")
1846                  print("wipe the current EEPROM. See the help for more info.\n\nExiting now.")
1847                  graceful_exit()
1848  
1849              print("\n---------------------------------------------------------------------------")
1850              print("                               Device Selection")
1851              if rnode.detected:
1852                  print("\nThe device seems to have an RNode firmware installed, but it was not")
1853                  print("provisioned correctly, or it is corrupt. We are going to reinstall the")
1854                  print("correct firmware and provision it.")
1855              else:
1856                  print("\nIt looks like this is a fresh device with no RNode firmware.")
1857                  
1858              print("")
1859              print("What kind of device is this?\n")
1860              print("       |   Select this option if you have an RNode of a specific")
1861              print("      \\ /  type, built from a recipe or bought from a vendor.")
1862              print("       '")
1863              print("[1]  A specific kind of RNode")
1864              print("")
1865              print("       |   Select this option if you have put together an RNode")
1866              print("      \\ /  of your own design, or if you are prototyping one.")
1867              print("       '")
1868              print("[2]  Homebrew RNode")
1869              print("")
1870              print("       |   Select one of these options if you want to easily turn")
1871              print("      \\ /  a supported development board into an RNode.")
1872              print("       '")
1873              print("[3]  LilyGO LoRa32 v2.1 (aka T3 v1.6 / T3 v1.6.1)")
1874              print("[4]  LilyGO LoRa32 v2.0")
1875              print("[5]  LilyGO LoRa32 v1.0")
1876              print("[6]  LilyGO T-Beam")
1877              print("[7]  Heltec LoRa32 v2")
1878              print("[8]  Heltec LoRa32 v3")
1879              print("[9]  Heltec LoRa32 v4")
1880              print("[10] LilyGO LoRa T3S3")
1881              print("[11] RAK4631")
1882              print("[12] LilyGo T-Echo")
1883              print("[13] LilyGO T-Beam Supreme")
1884              print("[14] LilyGO T-Deck")
1885              print("[15] Heltec T114")
1886              print("[16] Seeed XIAO ESP32S3 Wio-SX1262")
1887              print("")
1888              print("---------------------------------------------------------------------------")
1889              print("\nEnter the number that matches your device type:\n? ", end="")
1890  
1891              selected_product = None
1892              try:
1893                  c_dev = int(input())
1894                  c_mod = False
1895                  if c_dev < 1 or c_dev > 16:
1896                      raise ValueError()
1897                  elif c_dev == 1:
1898                      selected_product = ROM.PRODUCT_RNODE
1899                  elif c_dev == 2:
1900                      selected_product = ROM.PRODUCT_HMBRW
1901                      clear()
1902                      print("")
1903                      print("---------------------------------------------------------------------------")
1904                      print("                        Homebrew RNode Installer")
1905                      print("")
1906                      print("This option allows you to install and provision the RNode firmware on a")
1907                      print("custom board design, or a custom device created by coupling a generic")
1908                      print("development board with a supported transceiver module.")
1909                      print("")
1910                      print("Important! Using RNode firmware on homebrew devices should currently be")
1911                      print("considered experimental. It is not intended for production or critical use.")
1912                      print("The currently supplied firmware is provided AS-IS as a courtesy to those")
1913                      print("who would like to experiment with it. Hit enter to continue.")
1914                      print("---------------------------------------------------------------------------")
1915                      input()
1916                  elif c_dev == 6:
1917                      selected_product = ROM.PRODUCT_TBEAM
1918                      clear()
1919                      print("")
1920                      print("---------------------------------------------------------------------------")
1921                      print("                          T-Beam RNode Installer")
1922                      print("")
1923                      print("The RNode firmware can currently be installed on T-Beam devices using the")
1924                      print("SX1276, SX1278, SX1262 and SX1268 transceiver chips.")
1925                      print("")
1926                      print("Important! Using RNode firmware on T-Beam devices should currently be")
1927                      print("considered experimental. It is not intended for production or critical use.")
1928                      print("The currently supplied firmware is provided AS-IS as a courtesy to those")
1929                      print("who would like to experiment with it. Hit enter to continue.")
1930                      print("---------------------------------------------------------------------------")
1931                      input()
1932                  elif c_dev == 13:
1933                      selected_product = ROM.PRODUCT_TBEAM_S_V1
1934                      clear()
1935                      print("")
1936                      print("---------------------------------------------------------------------------")
1937                      print("                       T-Beam Supreme RNode Installer")
1938                      print("")
1939                      print("The RNode firmware can currently be installed on T-Beam Supreme devices")
1940                      print("using the SX1262 and SX1268 transceiver chips.")
1941                      print("")
1942                      print("Important! Using RNode firmware on T-Beam devices should currently be")
1943                      print("considered experimental. It is not intended for production or critical use.")
1944                      print("The currently supplied firmware is provided AS-IS as a courtesy to those")
1945                      print("who would like to experiment with it. Hit enter to continue.")
1946                      print("---------------------------------------------------------------------------")
1947                      input()
1948                  elif c_dev == 14:
1949                      selected_product = ROM.PRODUCT_TDECK
1950                      clear()
1951                      print("")
1952                      print("---------------------------------------------------------------------------")
1953                      print("                            T-Deck RNode Installer")
1954                      print("")
1955                      print("The RNode firmware can currently be installed on T-Deck devices using the")
1956                      print("SX1262 and SX1268 transceiver chips.")
1957                      print("")
1958                      print("Important! Using RNode firmware on T-Beam devices should currently be")
1959                      print("considered experimental. It is not intended for production or critical use.")
1960                      print("The currently supplied firmware is provided AS-IS as a courtesy to those")
1961                      print("who would like to experiment with it. Hit enter to continue.")
1962                      print("---------------------------------------------------------------------------")
1963                      input()
1964                  elif c_dev == 4:
1965                      selected_product = ROM.PRODUCT_T32_20
1966                      clear()
1967                      print("")
1968                      print("---------------------------------------------------------------------------")
1969                      print("                     LilyGO LoRa32 v2.0 RNode Installer")
1970                      print("")
1971                      print("Important! Using RNode firmware on LoRa32 devices should currently be")
1972                      print("considered experimental. It is not intended for production or critical use.")
1973                      print("The currently supplied firmware is provided AS-IS as a courtesy to those")
1974                      print("who would like to experiment with it. Hit enter to continue.")
1975                      print("---------------------------------------------------------------------------")
1976                      input()
1977                  elif c_dev == 5:
1978                      selected_product = ROM.PRODUCT_T32_10
1979                      clear()
1980                      print("")
1981                      print("---------------------------------------------------------------------------")
1982                      print("                     LilyGO LoRa32 v1.0 RNode Installer")
1983                      print("")
1984                      print("Important! Using RNode firmware on LoRa32 devices should currently be")
1985                      print("considered experimental. It is not intended for production or critical use.")
1986                      print("The currently supplied firmware is provided AS-IS as a courtesy to those")
1987                      print("who would like to experiment with it.")
1988                      print("")
1989                      print("Please Note! This device is known to have a faulty battery charging circuit,")
1990                      print("which can result in overcharging and damaging batteries. If at all possible,")
1991                      print("it is recommended to avoid this device.")
1992                      print("")
1993                      print("Hit enter if you're sure you wish to continue.")
1994                      print("---------------------------------------------------------------------------")
1995                      input()
1996                  elif c_dev == 3:
1997                      selected_product = ROM.PRODUCT_T32_21
1998                      clear()
1999                      print("")
2000                      print("---------------------------------------------------------------------------")
2001                      print("                     LilyGO LoRa32 v2.1 RNode Installer")
2002                      print("")
2003                      print("Important! Using RNode firmware on LoRa32 devices should currently be")
2004                      print("considered experimental. It is not intended for production or critical use.")
2005                      print("The currently supplied firmware is provided AS-IS as a courtesy to those")
2006                      print("who would like to experiment with it. Hit enter to continue.")
2007                      print("---------------------------------------------------------------------------")
2008                      input()
2009                  elif c_dev == 7:
2010                      selected_product = ROM.PRODUCT_H32_V2
2011                      clear()
2012                      print("")
2013                      print("---------------------------------------------------------------------------")
2014                      print("                     Heltec LoRa32 v2.0 RNode Installer")
2015                      print("")
2016                      print("Important! Using RNode firmware on Heltec devices should currently be")
2017                      print("considered experimental. It is not intended for production or critical use.")
2018                      print("")
2019                      print("The currently supplied firmware is provided AS-IS as a courtesy to those")
2020                      print("who would like to experiment with it. Hit enter to continue.")
2021                      print("---------------------------------------------------------------------------")
2022                      input()
2023                  elif c_dev == 10:
2024                      selected_product = ROM.PRODUCT_RNODE
2025                      c_mod = True
2026                      clear()
2027                      print("")
2028                      print("---------------------------------------------------------------------------")
2029                      print("                     LilyGO LoRa32 T3S3 RNode Installer")
2030                      print("")
2031                      print("Important! Using RNode firmware on T3S3 devices should currently be")
2032                      print("considered experimental. It is not intended for production or critical use.")
2033                      print("")
2034                      print("The currently supplied firmware is provided AS-IS as a courtesy to those")
2035                      print("who would like to experiment with it. Hit enter to continue.")
2036                      print("---------------------------------------------------------------------------")
2037                      input()
2038                  elif c_dev == 8:
2039                      selected_product = ROM.PRODUCT_H32_V3
2040                      clear()
2041                      print("")
2042                      print("---------------------------------------------------------------------------")
2043                      print("                     Heltec LoRa32 v3.0 RNode Installer")
2044                      print("")
2045                      print("Important! Using RNode firmware on Heltec devices should currently be")
2046                      print("considered experimental. It is not intended for production or critical use.")
2047                      print("")
2048                      print("The currently supplied firmware is provided AS-IS as a courtesy to those")
2049                      print("who would like to experiment with it. Hit enter to continue.")
2050                      print("---------------------------------------------------------------------------")
2051                      input()
2052                  elif c_dev == 9:
2053                      selected_product = ROM.PRODUCT_H32_V4
2054                      clear()
2055                      print("")
2056                      print("---------------------------------------------------------------------------")
2057                      print("                      Heltec LoRa32 v4 RNode Installer")
2058                      print("")
2059                      print("Important! Using RNode firmware on Heltec devices should currently be")
2060                      print("considered experimental. It is not intended for production or critical use.")
2061                      print("")
2062                      print("The currently supplied firmware is provided AS-IS as a courtesy to those")
2063                      print("who would like to experiment with it. Hit enter to continue.")
2064                      print("---------------------------------------------------------------------------")
2065                      input()
2066                  elif c_dev == 11:
2067                      selected_product = ROM.PRODUCT_RAK4631
2068                      clear()
2069                      print("")
2070                      print("---------------------------------------------------------------------------")
2071                      print("                     RAK4631 RNode Installer")
2072                      print("")
2073                      print("Important! Using RNode firmware on RAKwireless devices should currently be")
2074                      print("considered experimental. It is not intended for production or critical use.")
2075                      print("The currently supplied firmware is provided AS-IS as a courtesy to those")
2076                      print("who would like to experiment with it. Hit enter to continue.")
2077                      print("---------------------------------------------------------------------------")
2078                      input()
2079                  elif c_dev == 12:
2080                      selected_product = ROM.PRODUCT_TECHO
2081                      clear()
2082                      print("")
2083                      print("---------------------------------------------------------------------------")
2084                      print("                     LilyGo T-Echo RNode Installer")
2085                      print("")
2086                      print("Important! Using RNode firmware on LilyGo T-Echo devices should currently be")
2087                      print("considered experimental. It is not intended for production or critical use.")
2088                      print("The currently supplied firmware is provided AS-IS as a courtesy to those")
2089                      print("who would like to experiment with it. Hit enter to continue.")
2090                      print("---------------------------------------------------------------------------")
2091                      input()
2092                  elif c_dev == 15:
2093                      selected_product = ROM.PRODUCT_HELTEC_T114
2094                      clear()
2095                      print("")
2096                      print("---------------------------------------------------------------------------")
2097                      print("                      Heltec T114 RNode Installer")
2098                      print("")
2099                      print("Important! Using RNode firmware on Heltec T114 devices should currently be")
2100                      print("considered experimental. It is not intended for production or critical use.")
2101                      print("The currently supplied firmware is provided AS-IS as a courtesy to those")
2102                      print("who would like to experiment with it. Hit enter to continue.")
2103                      print("---------------------------------------------------------------------------")
2104                      input()
2105                  elif c_dev == 16:
2106                      selected_product = ROM.PRODUCT_XIAO_S3
2107                      clear()
2108                      print("")
2109                      print("---------------------------------------------------------------------------")
2110                      print("                 SeeedStudio XIAO esp32s3 wio RNode Installer")
2111                      print("")
2112                      print("Important! Using RNode firmware on SeeedStudio XIAO/wio devices should currently be")
2113                      print("considered experimental. It is not intended for production or critical use.")
2114                      print("The currently supplied firmware is provided AS-IS as a courtesy to those")
2115                      print("who would like to experiment with it. Hit enter to continue.")
2116                      print("---------------------------------------------------------------------------")
2117                      input()
2118  
2119              except Exception as e:
2120                  print("That device type does not exist, exiting now.")
2121                  graceful_exit()
2122  
2123              selected_platform = None
2124              selected_model = None
2125              selected_mcu = None
2126  
2127              if selected_product == ROM.PRODUCT_HMBRW:
2128                  print("\nWhat kind of microcontroller is your board based on?\n")
2129                  print("[1] AVR ATmega1284P")
2130                  print("[2] AVR ATmega2560")
2131                  print("[3] Espressif Systems ESP32")
2132                  print("\n? ", end="")
2133                  try:
2134                      c_mcu = int(input())
2135                      if c_mcu < 1 or c_mcu > 3:
2136                          raise ValueError()
2137                      elif c_mcu == 1:
2138                          selected_mcu = ROM.MCU_1284P
2139                          selected_platform = ROM.PLATFORM_AVR
2140                      elif c_mcu == 2:
2141                          selected_mcu = ROM.MCU_2560
2142                          selected_platform = ROM.PLATFORM_AVR
2143                      elif c_mcu == 3:
2144                          selected_mcu = ROM.MCU_ESP32
2145                          selected_platform = ROM.PLATFORM_ESP32
2146                      selected_model = ROM.MODEL_FF
2147  
2148                  except Exception as e:
2149                      print("That MCU type does not exist, exiting now.")
2150                      graceful_exit()
2151  
2152                  print("\nWhat transceiver module does your board use?\n")
2153                  print("[1] SX1276/SX1278 with antenna port on PA_BOOST pin")
2154                  print("[2] SX1276/SX1278 with antenna port on RFO pin")
2155                  print("\n? ", end="")
2156                  try:
2157                      c_trxm = int(input())
2158                      if c_trxm < 1 or c_trxm > 3:
2159                          raise ValueError()
2160                      elif c_trxm == 1:
2161                          selected_model = ROM.MODEL_FE
2162                      elif c_trxm == 2:
2163                          selected_model = ROM.MODEL_FF
2164  
2165                  except Exception as e:
2166                      print("That transceiver type does not exist, exiting now.")
2167                      graceful_exit()
2168  
2169  
2170              elif selected_product == ROM.PRODUCT_RNODE:
2171                  if not c_mod:
2172                      selected_mcu = ROM.MCU_1284P
2173                      print("\nWhat model is this RNode?\n")
2174                      print("[1] Handheld v2.1 RNode, 410 - 525 MHz")
2175                      print("[2] Handheld v2.1 RNode, 820 - 1020 MHz")
2176                      print("")
2177                      print("[3] Original v1.x RNode, 410 - 525 MHz")
2178                      print("[4] Original v1.x RNode, 820 - 1020 MHz")
2179                      print("")
2180                      print("[5] Prototype v2.2 RNode, 410 - 525 MHz")
2181                      print("[6] Prototype v2.2 RNode, 820 - 1020 MHz")
2182                      print("\n? ", end="")
2183                      try:
2184                          c_model = int(input())
2185                          if c_model < 1 or c_model > 6:
2186                              raise ValueError()
2187                          elif c_model == 1:
2188                              selected_model = ROM.MODEL_A2
2189                              selected_mcu = ROM.MCU_ESP32
2190                              selected_platform = ROM.PLATFORM_ESP32
2191                          elif c_model == 2:
2192                              selected_model = ROM.MODEL_A7
2193                              selected_mcu = ROM.MCU_ESP32
2194                              selected_platform = ROM.PLATFORM_ESP32
2195                          elif c_model == 3:
2196                              selected_model = ROM.MODEL_A4
2197                              selected_platform = ROM.PLATFORM_AVR
2198                          elif c_model == 4:
2199                              selected_model = ROM.MODEL_A9
2200                              selected_platform = ROM.PLATFORM_AVR
2201                          elif c_model == 5:
2202                              selected_model = ROM.MODEL_A1
2203                              selected_mcu = ROM.MCU_ESP32
2204                              selected_platform = ROM.PLATFORM_ESP32
2205                          elif c_model == 6:
2206                              selected_model = ROM.MODEL_A6
2207                              selected_mcu = ROM.MCU_ESP32
2208                              selected_platform = ROM.PLATFORM_ESP32
2209                          # elif c_model == 5:
2210                          #     selected_model = ROM.MODEL_A3
2211                          #     selected_mcu = ROM.MCU_ESP32
2212                          #     selected_platform = ROM.PLATFORM_ESP32
2213                          # elif c_model == 6:
2214                          #     selected_model = ROM.MODEL_A8
2215                          #     selected_mcu = ROM.MCU_ESP32
2216                          #     selected_platform = ROM.PLATFORM_ESP32
2217                      except Exception as e:
2218                          print("That model does not exist, exiting now.")
2219                          graceful_exit()
2220                  else:
2221                      print("\nWhat band is this T3S3 for?\n")
2222                      print("[1] 433 MHz         (with SX1278 chip)")
2223                      print("[2] 868/915/923 MHz (with SX1276 chip)")
2224                      print("");
2225                      print("[3] 433 MHz         (with SX1268 chip)")
2226                      print("[4] 868/915/923 MHz (with SX1262 chip)")
2227                      print("");
2228                      print("[5] 2.4 GHz         (with SX1280 chip and PA)")
2229                      print("\n? ", end="")
2230                      try:
2231                          c_model = int(input())
2232                          if c_model < 1 or c_model > 5:
2233                              raise ValueError()
2234                          elif c_model == 1:
2235                              selected_model = ROM.MODEL_A5
2236                              selected_mcu = ROM.MCU_ESP32
2237                              selected_platform = ROM.PLATFORM_ESP32
2238                          elif c_model == 2:
2239                              selected_model = ROM.MODEL_AA
2240                              selected_mcu = ROM.MCU_ESP32
2241                              selected_platform = ROM.PLATFORM_ESP32
2242                          elif c_model == 3:
2243                              selected_model = ROM.MODEL_A1
2244                              selected_mcu = ROM.MCU_ESP32
2245                              selected_platform = ROM.PLATFORM_ESP32
2246                          elif c_model == 4:
2247                              selected_model = ROM.MODEL_A6
2248                              selected_mcu = ROM.MCU_ESP32
2249                              selected_platform = ROM.PLATFORM_ESP32
2250                          elif c_model == 5:
2251                              selected_model = ROM.MODEL_AC
2252                              selected_mcu = ROM.MCU_ESP32
2253                              selected_platform = ROM.PLATFORM_ESP32
2254                      except Exception as e:
2255                          print("That model does not exist, exiting now.")
2256                          graceful_exit()
2257  
2258              elif selected_product == ROM.PRODUCT_TBEAM:
2259                  selected_mcu = ROM.MCU_ESP32
2260                  print("\nWhat band is this T-Beam for?\n")
2261                  print("[1] 433 MHz         (with SX1278 chip)")
2262                  print("[2] 868/915/923 MHz (with SX1276 chip)")
2263                  print("");
2264                  print("[3] 433 MHz         (with SX1268 chip)")
2265                  print("[4] 868/915/923 MHz (with SX1262 chip)")
2266                  print("\n? ", end="")
2267                  try:
2268                      c_model = int(input())
2269                      if c_model < 1 or c_model > 4:
2270                          raise ValueError()
2271                      elif c_model == 1:
2272                          selected_model = ROM.MODEL_E4
2273                          selected_platform = ROM.PLATFORM_ESP32
2274                      elif c_model == 2:
2275                          selected_model = ROM.MODEL_E9
2276                          selected_platform = ROM.PLATFORM_ESP32
2277                      elif c_model == 3:
2278                          selected_model = ROM.MODEL_E3
2279                          selected_platform = ROM.PLATFORM_ESP32
2280                      elif c_model == 4:
2281                          selected_model = ROM.MODEL_E8
2282                          selected_platform = ROM.PLATFORM_ESP32
2283                  except Exception as e:
2284                      print("That band does not exist, exiting now.")
2285                      graceful_exit()
2286  
2287              elif selected_product == ROM.PRODUCT_TBEAM_S_V1:
2288                  selected_mcu = ROM.MCU_ESP32
2289                  print("\nWhat band is this T-Beam Supreme for?\n")
2290                  print("[1] 433 MHz         (with SX1268 chip)")
2291                  print("[2] 868/915/923 MHz (with SX1262 chip)")
2292                  print("\n? ", end="")
2293                  try:
2294                      c_model = int(input())
2295                      if c_model < 1 or c_model > 2:
2296                          raise ValueError()
2297                      elif c_model == 1:
2298                          selected_model = ROM.MODEL_DB
2299                          selected_platform = ROM.PLATFORM_ESP32
2300                      elif c_model == 2:
2301                          selected_model = ROM.MODEL_DC
2302                          selected_platform = ROM.PLATFORM_ESP32
2303                  except Exception as e:
2304                      print("That band does not exist, exiting now.")
2305                      graceful_exit()
2306  
2307              elif selected_product == ROM.PRODUCT_TDECK:
2308                  selected_mcu = ROM.MCU_ESP32
2309                  print("\nWhat band is this T-Deck for?\n")
2310                  print("[1] 433 MHz         (with SX1268 chip)")
2311                  print("[2] 868/915/923 MHz (with SX1262 chip)")
2312                  print("\n? ", end="")
2313                  try:
2314                      c_model = int(input())
2315                      if c_model < 1 or c_model > 2:
2316                          raise ValueError()
2317                      elif c_model == 1:
2318                          selected_model = ROM.MODEL_D4
2319                          selected_platform = ROM.PLATFORM_ESP32
2320                      elif c_model == 2:
2321                          selected_model = ROM.MODEL_D9
2322                          selected_platform = ROM.PLATFORM_ESP32
2323                  except Exception as e:
2324                      print("That band does not exist, exiting now.")
2325                      graceful_exit()
2326  
2327              elif selected_product == ROM.PRODUCT_T32_10:
2328                  selected_mcu = ROM.MCU_ESP32
2329                  print("\nWhat band is this LoRa32 for?\n")
2330                  print("[1] 433 MHz")
2331                  print("[2] 868 MHz")
2332                  print("[3] 915 MHz")
2333                  print("[4] 923 MHz")
2334                  print("\n? ", end="")
2335                  try:
2336                      c_model = int(input())
2337                      if c_model < 1 or c_model > 4:
2338                          raise ValueError()
2339                      elif c_model == 1:
2340                          selected_model = ROM.MODEL_BA
2341                          selected_platform = ROM.PLATFORM_ESP32
2342                      elif c_model > 1:
2343                          selected_model = ROM.MODEL_BB
2344                          selected_platform = ROM.PLATFORM_ESP32
2345                  except Exception as e:
2346                      print("That band does not exist, exiting now.")
2347                      graceful_exit()
2348  
2349              elif selected_product == ROM.PRODUCT_T32_20:
2350                  selected_mcu = ROM.MCU_ESP32
2351                  print("\nWhat band is this LoRa32 for?\n")
2352                  print("[1] 433 MHz")
2353                  print("[2] 868 MHz")
2354                  print("[3] 915 MHz")
2355                  print("[4] 923 MHz")
2356                  print("\n? ", end="")
2357                  try:
2358                      c_model = int(input())
2359                      if c_model < 1 or c_model > 4:
2360                          raise ValueError()
2361                      elif c_model == 1:
2362                          selected_model = ROM.MODEL_B3
2363                          selected_platform = ROM.PLATFORM_ESP32
2364                      elif c_model > 1:
2365                          selected_model = ROM.MODEL_B8
2366                          selected_platform = ROM.PLATFORM_ESP32
2367                  except Exception as e:
2368                      print("That band does not exist, exiting now.")
2369                      graceful_exit()
2370  
2371              elif selected_product == ROM.PRODUCT_T32_21:
2372                  selected_mcu = ROM.MCU_ESP32
2373                  print("\nWhat band is this LoRa32 for?\n")
2374                  print("[1] 433 MHz")
2375                  print("[2] 868/915/923 MHz")
2376                  print("[3] 433 MHz, with TCXO")
2377                  print("[4] 868/915/923 MHz, with TCXO")
2378                  print("\n? ", end="")
2379                  try:
2380                      c_model = int(input())
2381                      if c_model < 1 or c_model > 4:
2382                          raise ValueError()
2383                      elif c_model == 1:
2384                          selected_model = ROM.MODEL_B4
2385                          selected_platform = ROM.PLATFORM_ESP32
2386                      elif c_model == 2:
2387                          selected_model = ROM.MODEL_B9
2388                          selected_platform = ROM.PLATFORM_ESP32
2389                      elif c_model == 3:
2390                          selected_model = ROM.MODEL_B4_TCXO
2391                          selected_platform = ROM.PLATFORM_ESP32
2392                      elif c_model == 4:
2393                          selected_model = ROM.MODEL_B9_TCXO
2394                          selected_platform = ROM.PLATFORM_ESP32
2395                  except Exception as e:
2396                      print("That band does not exist, exiting now.")
2397                      graceful_exit()
2398  
2399              elif selected_product == ROM.PRODUCT_H32_V2:
2400                  selected_mcu = ROM.MCU_ESP32
2401                  print("\nWhat band is this Heltec LoRa32 V2 for?\n")
2402                  print("[1] 433 MHz")
2403                  print("[2] 868 MHz")
2404                  print("[3] 915 MHz")
2405                  print("[4] 923 MHz")
2406                  print("\n? ", end="")
2407                  try:
2408                      c_model = int(input())
2409                      if c_model < 1 or c_model > 4:
2410                          raise ValueError()
2411                      elif c_model == 1:
2412                          selected_model = ROM.MODEL_C4
2413                          selected_platform = ROM.PLATFORM_ESP32
2414                      elif c_model > 1:
2415                          selected_model = ROM.MODEL_C9
2416                          selected_platform = ROM.PLATFORM_ESP32
2417                  except Exception as e:
2418                      print("That band does not exist, exiting now.")
2419                      graceful_exit()
2420  
2421              elif selected_product == ROM.PRODUCT_H32_V3:
2422                  selected_mcu = ROM.MCU_ESP32
2423                  print("\nWhat band is this Heltec LoRa32 V3 for?\n")
2424                  print("[1] 433 MHz")
2425                  print("[2] 868 MHz")
2426                  print("[3] 915 MHz")
2427                  print("[4] 923 MHz")
2428                  print("\n? ", end="")
2429                  try:
2430                      c_model = int(input())
2431                      if c_model < 1 or c_model > 4:
2432                          raise ValueError()
2433                      elif c_model == 1:
2434                          selected_model = ROM.MODEL_C5
2435                          selected_platform = ROM.PLATFORM_ESP32
2436                      elif c_model > 1:
2437                          selected_model = ROM.MODEL_CA
2438                          selected_platform = ROM.PLATFORM_ESP32
2439                  except Exception as e:
2440                      print("That band does not exist, exiting now.")
2441                      exit()
2442              
2443              elif selected_product == ROM.PRODUCT_H32_V4:
2444                  selected_mcu = ROM.MCU_ESP32
2445                  print("\nWhat band is this Heltec LoRa32 V4 for?\n")
2446                  print("[1] 868 MHz (28 dBm output)")
2447                  print("[2] 915 MHz (28 dBm output)")
2448                  print("[3] 923 MHz (28 dBm output)")
2449                  print("\n? ", end="")
2450                  try:
2451                      c_model = int(input())
2452                      if c_model < 1 or c_model > 3:
2453                          raise ValueError()
2454                      else:
2455                          selected_model = ROM.MODEL_C8
2456                          selected_platform = ROM.PLATFORM_ESP32
2457                  except Exception as e:
2458                      print("That band does not exist, exiting now.")
2459                      exit()
2460              
2461              elif selected_product == ROM.PRODUCT_HELTEC_T114:
2462                  selected_mcu = ROM.MCU_NRF52
2463                  print("\nWhat band is this Heltec T114 for?\n")
2464                  print("[1] 433 MHz")
2465                  print("[2] 868 MHz")
2466                  print("[3] 915 MHz")
2467                  print("[4] 923 MHz")
2468                  print("\n? ", end="")
2469                  try:
2470                      c_model = int(input())
2471                      if c_model < 1 or c_model > 4:
2472                          raise ValueError()
2473                      elif c_model == 1:
2474                          selected_model = ROM.MODEL_C6
2475                          selected_platform = ROM.PLATFORM_NRF52
2476                      elif c_model > 1:
2477                          selected_model = ROM.MODEL_C7
2478                          selected_platform = ROM.PLATFORM_NRF52
2479                  except Exception as e:
2480                      print("That band does not exist, exiting now.")
2481                      exit()
2482  
2483              elif selected_product == ROM.PRODUCT_XIAO_S3:
2484                  selected_mcu = ROM.MCU_ESP32
2485                  print("\nWhat band is this XIAO esp32s3 wio module for?\n")
2486                  print("[1] 433 MHz")
2487                  print("[2] 868 MHz")
2488                  print("\n? ", end="")
2489                  try:
2490                      c_model = int(input())
2491                      if c_model < 1 or c_model > 2:
2492                          raise ValueError()
2493                      elif c_model == 1:
2494                          selected_model = ROM.MODEL_DE
2495                          selected_platform = ROM.PLATFORM_ESP32
2496                      elif c_model == 2:
2497                          selected_model = ROM.MODEL_DD
2498                          selected_platform = ROM.PLATFORM_ESP32
2499                  except Exception as e:
2500                      print("That band does not exist, exiting now.")
2501                      exit()
2502  
2503              elif selected_product == ROM.PRODUCT_RAK4631:
2504                  selected_mcu = ROM.MCU_NRF52
2505                  print("\nWhat band is this RAK4631 for?\n")
2506                  print("[1] 433 MHz")
2507                  print("[2] 868 MHz")
2508                  print("[3] 915 MHz")
2509                  print("[4] 923 MHz")
2510                  print("\n? ", end="")
2511                  try:
2512                      c_model = int(input())
2513                      if c_model < 1 or c_model > 4:
2514                          raise ValueError()
2515                      elif c_model == 1:
2516                          selected_model = ROM.MODEL_11
2517                          selected_platform = ROM.PLATFORM_NRF52
2518                      elif c_model > 1:
2519                          selected_model = ROM.MODEL_12
2520                          selected_platform = ROM.PLATFORM_NRF52
2521                  except Exception as e:
2522                      print("That band does not exist, exiting now.")
2523                      graceful_exit()
2524              elif selected_product == ROM.PRODUCT_TECHO:
2525                  selected_mcu = ROM.MCU_NRF52
2526                  print("\nWhat band is this T-Echo for?\n")
2527                  print("[1] 433 MHz")
2528                  print("[2] 868 MHz")
2529                  print("[3] 915 MHz")
2530                  print("[4] 923 MHz")
2531                  print("\n? ", end="")
2532                  try:
2533                      c_model = int(input())
2534                      if c_model < 1 or c_model > 4:
2535                          raise ValueError()
2536                      elif c_model == 1:
2537                          selected_model = ROM.MODEL_16
2538                          selected_platform = ROM.PLATFORM_NRF52
2539                      elif c_model > 1:
2540                          selected_model = ROM.MODEL_17
2541                          selected_platform = ROM.PLATFORM_NRF52
2542                  except Exception as e:
2543                      print("That band does not exist, exiting now.")
2544                      graceful_exit()
2545  
2546              if selected_model != ROM.MODEL_FF and selected_model != ROM.MODEL_FE:
2547                  fw_filename = models[selected_model][4]
2548  
2549              else:
2550                  if selected_platform == ROM.PLATFORM_AVR:
2551                      if selected_mcu == ROM.MCU_1284P:
2552                          fw_filename = "rnode_firmware.hex"
2553                      elif selected_mcu == ROM.MCU_2560:
2554                          fw_filename = "rnode_firmware_m2560.hex"
2555                  
2556                  elif selected_platform == ROM.PLATFORM_ESP32:
2557                      fw_filename = None
2558                      print("\nWhat kind of ESP32 board is this?\n")
2559                      print("[1] Adafruit Feather ESP32 (HUZZAH32)")
2560                      print("[2] Generic ESP32 board")
2561                      print("\n? ", end="")
2562                      try:
2563                          c_eboard = int(input())
2564                          if c_eboard < 1 or c_eboard > 2:
2565                              raise ValueError()
2566                          elif c_eboard == 1:
2567                              fw_filename = "rnode_firmware_featheresp32.zip"
2568                          elif c_eboard == 2:
2569                              fw_filename = "rnode_firmware_esp32_generic.zip"
2570                      except Exception as e:
2571                          print("That ESP32 board does not exist, exiting now.")
2572                          graceful_exit()
2573  
2574              if fw_filename == None:
2575                  print("")
2576                  print("Sorry, no firmware for your board currently exists.")
2577                  print("Help making it a reality by contributing code or by")
2578                  print("donating to the project.")
2579                  print("")
2580                  print_donation_block()
2581                  print("")
2582                  graceful_exit()
2583  
2584              if args.use_extracted:
2585                  fw_filename = "extracted_rnode_firmware.zip"
2586  
2587              clear()
2588              print("")
2589              print("------------------------------------------------------------------------------")
2590              print("                               Installer Ready")
2591              print("")
2592              print("Ok, that should be all the information we need. Please confirm the following")
2593              print("summary before proceeding. In the next step, the device will be flashed and")
2594              print("provisioned, so make sure that you are satisfied with your choices.\n")
2595  
2596              print("Serial port     : "+str(selected_port.device))
2597              print("Device type     : "+str(products[selected_product])+" "+str(models[selected_model][3]))
2598              print("Platform        : "+str(platforms[selected_platform]))
2599              print("Device MCU      : "+str(mcus[selected_mcu]))
2600              print("Firmware file   : "+str(fw_filename))
2601  
2602              print("")
2603              print("------------------------------------------------------------------------------")
2604  
2605              print("\nIs the above correct? [y/N] ", end="")
2606              try:
2607                  c_ok = input().lower()
2608                  if c_ok != "y":
2609                      raise ValueError()
2610              except Exception as e:
2611                  print("OK, aborting now.")
2612                  graceful_exit()
2613  
2614              args.key = True
2615              args.port = selected_port.device
2616              args.platform = selected_platform
2617              args.hwrev = 1
2618              mapped_model = selected_model
2619              mapped_product = selected_product
2620              args.update = False
2621              args.flash = True
2622  
2623              try:
2624                  RNS.log("Checking firmware file availability...")
2625                  ensure_firmware_file(fw_filename)
2626              except Exception as e:
2627                  RNS.log("Could not obain firmware package for your board")
2628                  RNS.log("The contained exception was: "+str(e))
2629                  graceful_exit()
2630  
2631              rnode.disconnect()
2632  
2633          if args.public:
2634              private_bytes = None
2635              try:
2636                  file = open(FWD_DIR+"/signing.key", "rb")
2637                  private_bytes = file.read()
2638                  file.close()
2639              except Exception as e:
2640                  RNS.log("Could not load EEPROM signing key")
2641  
2642              try:
2643                  private_key = serialization.load_der_private_key(
2644                      private_bytes,
2645                      password=None,
2646                      backend=default_backend()
2647                  )
2648                  public_key = private_key.public_key()
2649                  public_bytes = public_key.public_bytes(
2650                      encoding=serialization.Encoding.DER,
2651                      format=serialization.PublicFormat.SubjectPublicKeyInfo
2652                  )
2653                  RNS.log("EEPROM Signing Public key:")
2654                  RNS.log(RNS.hexrep(public_bytes, delimit=False))
2655  
2656              except Exception as e:
2657                  RNS.log("Could not deserialize signing key")
2658                  RNS.log(str(e))
2659  
2660              try:
2661                  device_signer = RNS.Identity.from_file(FWD_DIR+"/device.key")
2662                  RNS.log("")
2663                  RNS.log("Device Signing Public key:")
2664                  RNS.log(RNS.hexrep(device_signer.get_public_key()[32:], delimit=True))
2665  
2666              except Exception as e:
2667                  RNS.log("Could not load device signing key")
2668                  
2669  
2670              graceful_exit()
2671  
2672          if args.key:
2673              if not os.path.isfile(FWD_DIR+"/device.key"):
2674                  try:
2675                      RNS.log("Generating a new device signing key...")
2676                      device_signer = RNS.Identity()
2677                      device_signer.to_file(FWD_DIR+"/device.key")
2678                      RNS.log("Device signing key written to "+str(FWD_DIR+"/device.key"))
2679                  except Exception as e:
2680                      RNS.log("Could not create new device signing key at "+str(FWD_DIR+"/device.key")+". The contained exception was:")
2681                      RNS.log(str(e))
2682                      RNS.log("Please ensure filesystem access and try again.")
2683                      graceful_exit(81)
2684              else:
2685                  try:
2686                      device_signer = RNS.Identity.from_file(FWD_DIR+"/device.key")
2687                  except Exception as e:
2688                      RNS.log("Could not load device signing key from "+str(FWD_DIR+"/device.key")+". The contained exception was:")
2689                      RNS.log(str(e))
2690                      RNS.log("Please restore or clear the key and try again.")
2691                      graceful_exit(82)
2692  
2693              if not os.path.isfile(FWD_DIR+"/signing.key"):
2694                  RNS.log("Generating a new EEPROM signing key...")
2695              private_key = rsa.generate_private_key(
2696                  public_exponent=65537,
2697                  key_size=1024,
2698                  backend=default_backend()
2699              )
2700              private_bytes = private_key.private_bytes(
2701                  encoding=serialization.Encoding.DER,
2702                  format=serialization.PrivateFormat.PKCS8,
2703                  encryption_algorithm=serialization.NoEncryption()
2704              )
2705              public_key = private_key.public_key()
2706              public_bytes = public_key.public_bytes(
2707                  encoding=serialization.Encoding.DER,
2708                  format=serialization.PublicFormat.SubjectPublicKeyInfo
2709              )
2710              os.makedirs(FWD_DIR, exist_ok=True)
2711              if os.path.isdir(FWD_DIR):
2712                  if os.path.isfile(FWD_DIR+"/signing.key"):
2713                      if not args.autoinstall:
2714                          RNS.log("EEPROM Signing key already exists, not overwriting!")
2715                          RNS.log("Manually delete this key to create a new one.")
2716                  else:
2717                      file = open(FWD_DIR+"/signing.key", "wb")
2718                      file.write(private_bytes)
2719                      file.close()
2720  
2721                      if not squashvw:
2722                          RNS.log("Wrote signing key")
2723                          RNS.log("Public key:")
2724                          RNS.log(RNS.hexrep(public_bytes, delimit=False))
2725              else:
2726                  RNS.log("The firmware directory does not exist, can't write key!")
2727  
2728              if not args.autoinstall:
2729                  graceful_exit()
2730  
2731          def get_partition_hash(platform, partition_file):
2732              try:
2733                  if platform == ROM.PLATFORM_ESP32 or platform == ROM.PLATFORM_AVR:
2734                      firmware_data = open(partition_file, "rb").read()
2735                      # Calculate the digest manually and see if it matches the
2736                      # SHA256 digest included in the ESP32 image.
2737                      calc_hash = hashlib.sha256(firmware_data[0:-32]).digest()
2738                      part_hash = firmware_data[-32:]
2739  
2740                      if calc_hash == part_hash:
2741                          return part_hash
2742                      else:
2743                          return None
2744                  
2745                  elif platform == ROM.PLATFORM_NRF52:
2746                      # Calculate digest manually, as it is not included in the image.
2747                      firmware_data = open(partition_file, "rb")
2748                      hash = hashlib.file_digest(firmware_data, 'sha256').digest()
2749                      firmware_data.close()
2750                      return hash
2751              except Exception as e:
2752                  RNS.log("Could not calculate firmware partition hash. The contained exception was:")
2753                  RNS.log(str(e))
2754  
2755          def get_flasher_call(platform, fw_filename):
2756              global selected_version
2757              from shutil import which
2758              if platform == "unzip":
2759                  flasher = "unzip"
2760                  if which(flasher) is not None:
2761                      return [flasher, "-o", UPD_DIR+"/"+selected_version+"/"+fw_filename, "-d", UPD_DIR+"/"+selected_version]
2762                  else:
2763                      RNS.log("")
2764                      RNS.log("You do not currently have the \""+flasher+"\" program installed on your system.")
2765                      RNS.log("Unfortunately, that means we can't proceed, since it is needed to flash your")
2766                      RNS.log("board. You can install it via your package manager, for example:")
2767                      RNS.log("")
2768                      RNS.log("  sudo apt install "+flasher)
2769                      RNS.log("")
2770                      RNS.log("Please install \""+flasher+"\" and try again.")
2771                      graceful_exit()
2772              elif platform == ROM.PLATFORM_AVR:
2773                  flasher = "avrdude"
2774                  if which(flasher) is not None:
2775                      # avrdude -C/home/markqvist/.arduino15/packages/arduino/tools/avrdude/6.3.0-arduino17/etc/avrdude.conf -q -q -V -patmega2560 -cwiring -P/dev/ttyACM0 -b115200 -D -Uflash:w:/tmp/arduino-sketch-0E260F46C421A84A7CBAD48E859C8E64/RNode_Firmware.ino.hex:i
2776                      # avrdude -q -q -V -patmega2560 -cwiring -P/dev/ttyACM0 -b115200 -D -Uflash:w:/tmp/arduino-sketch-0E260F46C421A84A7CBAD48E859C8E64/RNode_Firmware.ino.hex:i
2777                      if fw_filename == "rnode_firmware.hex":
2778                          return [flasher, "-P", args.port, "-p", "m1284p", "-c", "arduino", "-b", "115200", "-U", "flash:w:"+UPD_DIR+"/"+selected_version+"/"+fw_filename+":i"]
2779                      elif fw_filename == "rnode_firmware_m2560.hex":
2780                          return [flasher, "-P", args.port, "-p", "atmega2560", "-c", "wiring", "-D", "-b", "115200", "-U", "flash:w:"+UPD_DIR+"/"+selected_version+"/"+fw_filename]
2781                  else:
2782                      RNS.log("")
2783                      RNS.log("You do not currently have the \""+flasher+"\" program installed on your system.")
2784                      RNS.log("Unfortunately, that means we can't proceed, since it is needed to flash your")
2785                      RNS.log("board. You can install it via your package manager, for example:")
2786                      RNS.log("")
2787                      RNS.log("  sudo apt install avrdude")
2788                      RNS.log("")
2789                      RNS.log("Please install \""+flasher+"\" and try again.")
2790                      graceful_exit()
2791              elif platform == ROM.PLATFORM_ESP32:
2792                  numeric_version = float(selected_version)
2793                  flasher_dir = UPD_DIR+"/"+selected_version
2794                  flasher = flasher_dir+"/esptool.py"
2795                  if not os.path.isfile(flasher):
2796                      if os.path.isfile(CNF_DIR+"/recovery_esptool.py"):
2797                          import shutil
2798                          if not os.path.isdir(flasher_dir):
2799                              os.makedirs(flasher_dir)
2800                          shutil.copy(CNF_DIR+"/recovery_esptool.py", flasher)
2801                          RNS.log("No flasher present, using recovery flasher to write firmware to device")
2802  
2803                  if os.path.isfile(flasher):
2804                      import stat
2805                      os.chmod(flasher, stat.S_IRWXU | stat.S_IRGRP | stat.S_IXGRP)
2806  
2807                  if which(flasher) is not None:
2808                      if fw_filename == "rnode_firmware_tbeam.zip":
2809                          if numeric_version >= 1.55:
2810                              return [
2811                                  sys.executable, flasher,
2812                                  "--chip", "esp32",
2813                                  "--port", args.port,
2814                                  "--baud", args.baud_flash,
2815                                  "--before", "default_reset",
2816                                  "--after", "hard_reset",
2817                                  "write_flash", "-z",
2818                                  "--flash_mode", "dio",
2819                                  "--flash_freq", "80m",
2820                                  "--flash_size", "4MB",
2821                                  "0xe000",  UPD_DIR+"/"+selected_version+"/rnode_firmware_tbeam.boot_app0",
2822                                  "0x1000",  UPD_DIR+"/"+selected_version+"/rnode_firmware_tbeam.bootloader",
2823                                  "0x10000", UPD_DIR+"/"+selected_version+"/rnode_firmware_tbeam.bin",
2824                                  "0x210000",UPD_DIR+"/"+selected_version+"/console_image.bin",
2825                                  "0x8000",  UPD_DIR+"/"+selected_version+"/rnode_firmware_tbeam.partitions",
2826                              ]
2827                          else:
2828                              return [
2829                                  sys.executable, flasher,
2830                                  "--chip", "esp32",
2831                                  "--port", args.port,
2832                                  "--baud", args.baud_flash,
2833                                  "--before", "default_reset",
2834                                  "--after", "hard_reset",
2835                                  "write_flash", "-z",
2836                                  "--flash_mode", "dio",
2837                                  "--flash_freq", "80m",
2838                                  "--flash_size", "4MB",
2839                                  "0xe000",  UPD_DIR+"/"+selected_version+"/rnode_firmware_tbeam.boot_app0",
2840                                  "0x1000",  UPD_DIR+"/"+selected_version+"/rnode_firmware_tbeam.bootloader",
2841                                  "0x10000", UPD_DIR+"/"+selected_version+"/rnode_firmware_tbeam.bin",
2842                                  "0x8000",  UPD_DIR+"/"+selected_version+"/rnode_firmware_tbeam.partitions",
2843                              ]
2844                      elif fw_filename == "rnode_firmware_tbeam_sx1262.zip":
2845                          if numeric_version >= 1.55:
2846                              return [
2847                                  sys.executable, flasher,
2848                                  "--chip", "esp32",
2849                                  "--port", args.port,
2850                                  "--baud", args.baud_flash,
2851                                  "--before", "default_reset",
2852                                  "--after", "hard_reset",
2853                                  "write_flash", "-z",
2854                                  "--flash_mode", "dio",
2855                                  "--flash_freq", "80m",
2856                                  "--flash_size", "4MB",
2857                                  "0xe000",  UPD_DIR+"/"+selected_version+"/rnode_firmware_tbeam_sx1262.boot_app0",
2858                                  "0x1000",  UPD_DIR+"/"+selected_version+"/rnode_firmware_tbeam_sx1262.bootloader",
2859                                  "0x10000", UPD_DIR+"/"+selected_version+"/rnode_firmware_tbeam_sx1262.bin",
2860                                  "0x210000",UPD_DIR+"/"+selected_version+"/console_image.bin",
2861                                  "0x8000",  UPD_DIR+"/"+selected_version+"/rnode_firmware_tbeam_sx1262.partitions",
2862                              ]
2863                          else:
2864                              return [
2865                                  sys.executable, flasher,
2866                                  "--chip", "esp32",
2867                                  "--port", args.port,
2868                                  "--baud", args.baud_flash,
2869                                  "--before", "default_reset",
2870                                  "--after", "hard_reset",
2871                                  "write_flash", "-z",
2872                                  "--flash_mode", "dio",
2873                                  "--flash_freq", "80m",
2874                                  "--flash_size", "4MB",
2875                                  "0xe000",  UPD_DIR+"/"+selected_version+"/rnode_firmware_tbeam.boot_app0",
2876                                  "0x1000",  UPD_DIR+"/"+selected_version+"/rnode_firmware_tbeam.bootloader",
2877                                  "0x10000", UPD_DIR+"/"+selected_version+"/rnode_firmware_tbeam.bin",
2878                                  "0x8000",  UPD_DIR+"/"+selected_version+"/rnode_firmware_tbeam.partitions",
2879                              ]
2880                      elif fw_filename == "rnode_firmware_lora32v10.zip":
2881                          if numeric_version >= 1.59:
2882                              return [
2883                                  sys.executable, flasher,
2884                                  "--chip", "esp32",
2885                                  "--port", args.port,
2886                                  "--baud", args.baud_flash,
2887                                  "--before", "default_reset",
2888                                  "--after", "hard_reset",
2889                                  "write_flash", "-z",
2890                                  "--flash_mode", "dio",
2891                                  "--flash_freq", "80m",
2892                                  "--flash_size", "4MB",
2893                                  "0xe000",  UPD_DIR+"/"+selected_version+"/rnode_firmware_lora32v10.boot_app0",
2894                                  "0x1000",  UPD_DIR+"/"+selected_version+"/rnode_firmware_lora32v10.bootloader",
2895                                  "0x10000", UPD_DIR+"/"+selected_version+"/rnode_firmware_lora32v10.bin",
2896                                  "0x210000",UPD_DIR+"/"+selected_version+"/console_image.bin",
2897                                  "0x8000",  UPD_DIR+"/"+selected_version+"/rnode_firmware_lora32v10.partitions",
2898                              ]
2899                          else:
2900                              return [
2901                                  sys.executable, flasher,
2902                                  "--chip", "esp32",
2903                                  "--port", args.port,
2904                                  "--baud", args.baud_flash,
2905                                  "--before", "default_reset",
2906                                  "--after", "hard_reset",
2907                                  "write_flash", "-z",
2908                                  "--flash_mode", "dio",
2909                                  "--flash_freq", "80m",
2910                                  "--flash_size", "4MB",
2911                                  "0xe000",  UPD_DIR+"/"+selected_version+"/rnode_firmware_lora32v20.boot_app0",
2912                                  "0x1000",  UPD_DIR+"/"+selected_version+"/rnode_firmware_lora32v20.bootloader",
2913                                  "0x10000", UPD_DIR+"/"+selected_version+"/rnode_firmware_lora32v20.bin",
2914                                  "0x8000",  UPD_DIR+"/"+selected_version+"/rnode_firmware_lora32v20.partitions",
2915                              ]
2916                      elif fw_filename == "rnode_firmware_lora32v20.zip":
2917                          if numeric_version >= 1.55:
2918                              return [
2919                                  sys.executable, flasher,
2920                                  "--chip", "esp32",
2921                                  "--port", args.port,
2922                                  "--baud", args.baud_flash,
2923                                  "--before", "default_reset",
2924                                  "--after", "hard_reset",
2925                                  "write_flash", "-z",
2926                                  "--flash_mode", "dio",
2927                                  "--flash_freq", "80m",
2928                                  "--flash_size", "4MB",
2929                                  "0xe000",  UPD_DIR+"/"+selected_version+"/rnode_firmware_lora32v20.boot_app0",
2930                                  "0x1000",  UPD_DIR+"/"+selected_version+"/rnode_firmware_lora32v20.bootloader",
2931                                  "0x10000", UPD_DIR+"/"+selected_version+"/rnode_firmware_lora32v20.bin",
2932                                  "0x210000",UPD_DIR+"/"+selected_version+"/console_image.bin",
2933                                  "0x8000",  UPD_DIR+"/"+selected_version+"/rnode_firmware_lora32v20.partitions",
2934                              ]
2935                          else:
2936                              return [
2937                                  sys.executable, flasher,
2938                                  "--chip", "esp32",
2939                                  "--port", args.port,
2940                                  "--baud", args.baud_flash,
2941                                  "--before", "default_reset",
2942                                  "--after", "hard_reset",
2943                                  "write_flash", "-z",
2944                                  "--flash_mode", "dio",
2945                                  "--flash_freq", "80m",
2946                                  "--flash_size", "4MB",
2947                                  "0xe000",  UPD_DIR+"/"+selected_version+"/rnode_firmware_lora32v20.boot_app0",
2948                                  "0x1000",  UPD_DIR+"/"+selected_version+"/rnode_firmware_lora32v20.bootloader",
2949                                  "0x10000", UPD_DIR+"/"+selected_version+"/rnode_firmware_lora32v20.bin",
2950                                  "0x8000",  UPD_DIR+"/"+selected_version+"/rnode_firmware_lora32v20.partitions",
2951                              ]
2952                      elif fw_filename == "rnode_firmware_lora32v21.zip":
2953                          if numeric_version >= 1.55:
2954                              return [
2955                                  sys.executable, flasher,
2956                                  "--chip", "esp32",
2957                                  "--port", args.port,
2958                                  "--baud", args.baud_flash,
2959                                  "--before", "default_reset",
2960                                  "--after", "hard_reset",
2961                                  "write_flash", "-z",
2962                                  "--flash_mode", "dio",
2963                                  "--flash_freq", "80m",
2964                                  "--flash_size", "4MB",
2965                                  "0xe000",  UPD_DIR+"/"+selected_version+"/rnode_firmware_lora32v21.boot_app0",
2966                                  "0x1000",  UPD_DIR+"/"+selected_version+"/rnode_firmware_lora32v21.bootloader",
2967                                  "0x10000", UPD_DIR+"/"+selected_version+"/rnode_firmware_lora32v21.bin",
2968                                  "0x210000",UPD_DIR+"/"+selected_version+"/console_image.bin",
2969                                  "0x8000",  UPD_DIR+"/"+selected_version+"/rnode_firmware_lora32v21.partitions",
2970                              ]
2971                          else:
2972                              return [
2973                                  sys.executable, flasher,
2974                                  "--chip", "esp32",
2975                                  "--port", args.port,
2976                                  "--baud", args.baud_flash,
2977                                  "--before", "default_reset",
2978                                  "--after", "hard_reset",
2979                                  "write_flash", "-z",
2980                                  "--flash_mode", "dio",
2981                                  "--flash_freq", "80m",
2982                                  "--flash_size", "4MB",
2983                                  "0xe000",  UPD_DIR+"/"+selected_version+"/rnode_firmware_lora32v21.boot_app0",
2984                                  "0x1000",  UPD_DIR+"/"+selected_version+"/rnode_firmware_lora32v21.bootloader",
2985                                  "0x10000", UPD_DIR+"/"+selected_version+"/rnode_firmware_lora32v21.bin",
2986                                  "0x8000",  UPD_DIR+"/"+selected_version+"/rnode_firmware_lora32v21.partitions",
2987                              ]
2988                      elif fw_filename == "rnode_firmware_lora32v21_tcxo.zip":
2989                          return [
2990                              sys.executable, flasher,
2991                              "--chip", "esp32",
2992                              "--port", args.port,
2993                              "--baud", args.baud_flash,
2994                              "--before", "default_reset",
2995                              "--after", "hard_reset",
2996                              "write_flash", "-z",
2997                              "--flash_mode", "dio",
2998                              "--flash_freq", "80m",
2999                              "--flash_size", "4MB",
3000                              "0xe000",  UPD_DIR+"/"+selected_version+"/rnode_firmware_lora32v21_tcxo.boot_app0",
3001                              "0x1000",  UPD_DIR+"/"+selected_version+"/rnode_firmware_lora32v21_tcxo.bootloader",
3002                              "0x10000", UPD_DIR+"/"+selected_version+"/rnode_firmware_lora32v21_tcxo.bin",
3003                              "0x210000",UPD_DIR+"/"+selected_version+"/console_image.bin",
3004                              "0x8000",  UPD_DIR+"/"+selected_version+"/rnode_firmware_lora32v21_tcxo.partitions",
3005                          ]
3006                      elif fw_filename == "rnode_firmware_heltec32v2.zip":
3007                          if numeric_version >= 1.55:
3008                              return [
3009                                  sys.executable, flasher,
3010                                  "--chip", "esp32",
3011                                  "--port", args.port,
3012                                  "--baud", args.baud_flash,
3013                                  "--before", "default_reset",
3014                                  "--after", "hard_reset",
3015                                  "write_flash", "-z",
3016                                  "--flash_mode", "dio",
3017                                  "--flash_freq", "80m",
3018                                  "--flash_size", "8MB",
3019                                  "0xe000",  UPD_DIR+"/"+selected_version+"/rnode_firmware_heltec32v2.boot_app0",
3020                                  "0x1000",  UPD_DIR+"/"+selected_version+"/rnode_firmware_heltec32v2.bootloader",
3021                                  "0x10000", UPD_DIR+"/"+selected_version+"/rnode_firmware_heltec32v2.bin",
3022                                  "0x210000",UPD_DIR+"/"+selected_version+"/console_image.bin",
3023                                  "0x8000",  UPD_DIR+"/"+selected_version+"/rnode_firmware_heltec32v2.partitions",
3024                              ]
3025                          else:
3026                              return [
3027                                  sys.executable, flasher,
3028                                  "--chip", "esp32",
3029                                  "--port", args.port,
3030                                  "--baud", args.baud_flash,
3031                                  "--before", "default_reset",
3032                                  "--after", "hard_reset",
3033                                  "write_flash", "-z",
3034                                  "--flash_mode", "dio",
3035                                  "--flash_freq", "80m",
3036                                  "--flash_size", "8MB",
3037                                  "0xe000",  UPD_DIR+"/"+selected_version+"/rnode_firmware_heltec32v2.boot_app0",
3038                                  "0x1000",  UPD_DIR+"/"+selected_version+"/rnode_firmware_heltec32v2.bootloader",
3039                                  "0x10000", UPD_DIR+"/"+selected_version+"/rnode_firmware_heltec32v2.bin",
3040                                  "0x8000",  UPD_DIR+"/"+selected_version+"/rnode_firmware_heltec32v2.partitions",
3041                              ]
3042                      elif fw_filename == "rnode_firmware_heltec32v3.zip":
3043                          return [
3044                              sys.executable, flasher,
3045                              "--chip", "esp32-s3",
3046                              "--port", args.port,
3047                              "--baud", args.baud_flash,
3048                              "--before", "default_reset",
3049                              "--after", "hard_reset",
3050                              "write_flash", "-z",
3051                              "--flash_mode", "dio",
3052                              "--flash_freq", "80m",
3053                              "--flash_size", "8MB",
3054                              "0xe000",  UPD_DIR+"/"+selected_version+"/rnode_firmware_heltec32v3.boot_app0",
3055                              "0x0",  UPD_DIR+"/"+selected_version+"/rnode_firmware_heltec32v3.bootloader",
3056                              "0x10000", UPD_DIR+"/"+selected_version+"/rnode_firmware_heltec32v3.bin",
3057                              "0x210000",UPD_DIR+"/"+selected_version+"/console_image.bin",
3058                              "0x8000",  UPD_DIR+"/"+selected_version+"/rnode_firmware_heltec32v3.partitions",
3059                          ]
3060                      elif fw_filename == "rnode_firmware_heltec32v4pa.zip":
3061                          return [
3062                              sys.executable, flasher,
3063                              "--chip", "esp32-s3",
3064                              "--port", args.port,
3065                              "--baud", args.baud_flash,
3066                              "--before", "default_reset",
3067                              "--after", "hard_reset",
3068                              "write_flash", "-z",
3069                              "--flash_mode", "dio",
3070                              "--flash_freq", "80m",
3071                              "--flash_size", "16MB",
3072                              "0xe000",  UPD_DIR+"/"+selected_version+"/rnode_firmware_heltec32v4pa.boot_app0",
3073                              "0x0",     UPD_DIR+"/"+selected_version+"/rnode_firmware_heltec32v4pa.bootloader",
3074                              "0x10000", UPD_DIR+"/"+selected_version+"/rnode_firmware_heltec32v4pa.bin",
3075                              "0x210000",UPD_DIR+"/"+selected_version+"/console_image.bin",
3076                              "0x8000",  UPD_DIR+"/"+selected_version+"/rnode_firmware_heltec32v4pa.partitions",
3077                          ]
3078                      elif fw_filename == "rnode_firmware_featheresp32.zip":
3079                          if numeric_version >= 1.55:
3080                              return [
3081                                 sys.executable,  flasher,
3082                                  "--chip", "esp32",
3083                                  "--port", args.port,
3084                                  "--baud", args.baud_flash,
3085                                  "--before", "default_reset",
3086                                  "--after", "hard_reset",
3087                                  "write_flash", "-z",
3088                                  "--flash_mode", "dio",
3089                                  "--flash_freq", "80m",
3090                                  "--flash_size", "4MB",
3091                                  "0xe000",  UPD_DIR+"/"+selected_version+"/rnode_firmware_featheresp32.boot_app0",
3092                                  "0x1000",  UPD_DIR+"/"+selected_version+"/rnode_firmware_featheresp32.bootloader",
3093                                  "0x10000", UPD_DIR+"/"+selected_version+"/rnode_firmware_featheresp32.bin",
3094                                  "0x210000",UPD_DIR+"/"+selected_version+"/console_image.bin",
3095                                  "0x8000",  UPD_DIR+"/"+selected_version+"/rnode_firmware_featheresp32.partitions",
3096                              ]
3097                          else:
3098                              return [
3099                                  sys.executable, flasher,
3100                                  "--chip", "esp32",
3101                                  "--port", args.port,
3102                                  "--baud", args.baud_flash,
3103                                  "--before", "default_reset",
3104                                  "--after", "hard_reset",
3105                                  "write_flash", "-z",
3106                                  "--flash_mode", "dio",
3107                                  "--flash_freq", "80m",
3108                                  "--flash_size", "4MB",
3109                                  "0xe000",  UPD_DIR+"/"+selected_version+"/rnode_firmware_featheresp32.boot_app0",
3110                                  "0x1000",  UPD_DIR+"/"+selected_version+"/rnode_firmware_featheresp32.bootloader",
3111                                  "0x10000", UPD_DIR+"/"+selected_version+"/rnode_firmware_featheresp32.bin",
3112                                  "0x8000",  UPD_DIR+"/"+selected_version+"/rnode_firmware_featheresp32.partitions",
3113                              ]
3114                      elif fw_filename == "rnode_firmware_esp32_generic.zip":
3115                          if numeric_version >= 1.55:
3116                              return [
3117                                  sys.executable, flasher,
3118                                  "--chip", "esp32",
3119                                  "--port", args.port,
3120                                  "--baud", args.baud_flash,
3121                                  "--before", "default_reset",
3122                                  "--after", "hard_reset",
3123                                  "write_flash", "-z",
3124                                  "--flash_mode", "dio",
3125                                  "--flash_freq", "80m",
3126                                  "--flash_size", "4MB",
3127                                  "0xe000",  UPD_DIR+"/"+selected_version+"/rnode_firmware_esp32_generic.boot_app0",
3128                                  "0x1000",  UPD_DIR+"/"+selected_version+"/rnode_firmware_esp32_generic.bootloader",
3129                                  "0x10000", UPD_DIR+"/"+selected_version+"/rnode_firmware_esp32_generic.bin",
3130                                  "0x210000",UPD_DIR+"/"+selected_version+"/console_image.bin",
3131                                  "0x8000",  UPD_DIR+"/"+selected_version+"/rnode_firmware_esp32_generic.partitions",
3132                              ]
3133                          else:
3134                              return [
3135                                  sys.executable, flasher,
3136                                  "--chip", "esp32",
3137                                  "--port", args.port,
3138                                  "--baud", args.baud_flash,
3139                                  "--before", "default_reset",
3140                                  "--after", "hard_reset",
3141                                  "write_flash", "-z",
3142                                  "--flash_mode", "dio",
3143                                  "--flash_freq", "80m",
3144                                  "--flash_size", "4MB",
3145                                  "0xe000",  UPD_DIR+"/"+selected_version+"/rnode_firmware_esp32_generic.boot_app0",
3146                                  "0x1000",  UPD_DIR+"/"+selected_version+"/rnode_firmware_esp32_generic.bootloader",
3147                                  "0x10000", UPD_DIR+"/"+selected_version+"/rnode_firmware_esp32_generic.bin",
3148                                  "0x8000",  UPD_DIR+"/"+selected_version+"/rnode_firmware_esp32_generic.partitions",
3149                              ]
3150                      elif fw_filename == "rnode_firmware_ng20.zip":
3151                          if numeric_version >= 1.55:
3152                              return [
3153                                  sys.executable, flasher,
3154                                  "--chip", "esp32",
3155                                  "--port", args.port,
3156                                  "--baud", args.baud_flash,
3157                                  "--before", "default_reset",
3158                                  "--after", "hard_reset",
3159                                  "write_flash", "-z",
3160                                  "--flash_mode", "dio",
3161                                  "--flash_freq", "80m",
3162                                  "--flash_size", "4MB",
3163                                  "0xe000",  UPD_DIR+"/"+selected_version+"/rnode_firmware_ng20.boot_app0",
3164                                  "0x1000",  UPD_DIR+"/"+selected_version+"/rnode_firmware_ng20.bootloader",
3165                                  "0x10000", UPD_DIR+"/"+selected_version+"/rnode_firmware_ng20.bin",
3166                                  "0x210000",UPD_DIR+"/"+selected_version+"/console_image.bin",
3167                                  "0x8000",  UPD_DIR+"/"+selected_version+"/rnode_firmware_ng20.partitions",
3168                              ]
3169                          else:
3170                              return [
3171                                  sys.executable, flasher,
3172                                  "--chip", "esp32",
3173                                  "--port", args.port,
3174                                  "--baud", args.baud_flash,
3175                                  "--before", "default_reset",
3176                                  "--after", "hard_reset",
3177                                  "write_flash", "-z",
3178                                  "--flash_mode", "dio",
3179                                  "--flash_freq", "80m",
3180                                  "--flash_size", "4MB",
3181                                  "0xe000",  UPD_DIR+"/"+selected_version+"/rnode_firmware_ng20.boot_app0",
3182                                  "0x1000",  UPD_DIR+"/"+selected_version+"/rnode_firmware_ng20.bootloader",
3183                                  "0x10000", UPD_DIR+"/"+selected_version+"/rnode_firmware_ng20.bin",
3184                                  "0x8000",  UPD_DIR+"/"+selected_version+"/rnode_firmware_ng20.partitions",
3185                              ]
3186                      elif fw_filename == "rnode_firmware_ng21.zip":
3187                          if numeric_version >= 1.55:
3188                              return [
3189                                  sys.executable, flasher,
3190                                  "--chip", "esp32",
3191                                  "--port", args.port,
3192                                  "--baud", args.baud_flash,
3193                                  "--before", "default_reset",
3194                                  "--after", "hard_reset",
3195                                  "write_flash", "-z",
3196                                  "--flash_mode", "dio",
3197                                  "--flash_freq", "80m",
3198                                  "--flash_size", "4MB",
3199                                  "0xe000",  UPD_DIR+"/"+selected_version+"/rnode_firmware_ng21.boot_app0",
3200                                  "0x1000",  UPD_DIR+"/"+selected_version+"/rnode_firmware_ng21.bootloader",
3201                                  "0x10000", UPD_DIR+"/"+selected_version+"/rnode_firmware_ng21.bin",
3202                                  "0x210000",UPD_DIR+"/"+selected_version+"/console_image.bin",
3203                                  "0x8000",  UPD_DIR+"/"+selected_version+"/rnode_firmware_ng21.partitions",
3204                              ]
3205                          else:
3206                              return [
3207                                  sys.executable, flasher,
3208                                  "--chip", "esp32",
3209                                  "--port", args.port,
3210                                  "--baud", args.baud_flash,
3211                                  "--before", "default_reset",
3212                                  "--after", "hard_reset",
3213                                  "write_flash", "-z",
3214                                  "--flash_mode", "dio",
3215                                  "--flash_freq", "80m",
3216                                  "--flash_size", "4MB",
3217                                  "0xe000",  UPD_DIR+"/"+selected_version+"/rnode_firmware_ng21.boot_app0",
3218                                  "0x1000",  UPD_DIR+"/"+selected_version+"/rnode_firmware_ng21.bootloader",
3219                                  "0x10000", UPD_DIR+"/"+selected_version+"/rnode_firmware_ng21.bin",
3220                                  "0x8000",  UPD_DIR+"/"+selected_version+"/rnode_firmware_ng21.partitions",
3221                              ]
3222                      elif fw_filename == "rnode_firmware_t3s3.zip":
3223                          return [
3224                              sys.executable, flasher,
3225                              "--chip", "esp32s3",
3226                              "--port", args.port,
3227                              "--baud", args.baud_flash,
3228                              "--before", "default_reset",
3229                              "--after", "hard_reset",
3230                              "write_flash", "-z",
3231                              "--flash_mode", "dio",
3232                              "--flash_freq", "80m",
3233                              "--flash_size", "4MB",
3234                              "0xe000",  UPD_DIR+"/"+selected_version+"/rnode_firmware_t3s3.boot_app0",
3235                              "0x0",  UPD_DIR+"/"+selected_version+"/rnode_firmware_t3s3.bootloader",
3236                              "0x10000", UPD_DIR+"/"+selected_version+"/rnode_firmware_t3s3.bin",
3237                              "0x210000",UPD_DIR+"/"+selected_version+"/console_image.bin",
3238                              "0x8000",  UPD_DIR+"/"+selected_version+"/rnode_firmware_t3s3.partitions",
3239                          ]
3240                      elif fw_filename == "rnode_firmware_t3s3_sx127x.zip":
3241                          return [
3242                              sys.executable, flasher,
3243                              "--chip", "esp32s3",
3244                              "--port", args.port,
3245                              "--baud", args.baud_flash,
3246                              "--before", "default_reset",
3247                              "--after", "hard_reset",
3248                              "write_flash", "-z",
3249                              "--flash_mode", "dio",
3250                              "--flash_freq", "80m",
3251                              "--flash_size", "4MB",
3252                              "0xe000",  UPD_DIR+"/"+selected_version+"/rnode_firmware_t3s3_sx127x.boot_app0",
3253                              "0x0",  UPD_DIR+"/"+selected_version+"/rnode_firmware_t3s3_sx127x.bootloader",
3254                              "0x10000", UPD_DIR+"/"+selected_version+"/rnode_firmware_t3s3_sx127x.bin",
3255                              "0x210000",UPD_DIR+"/"+selected_version+"/console_image.bin",
3256                              "0x8000",  UPD_DIR+"/"+selected_version+"/rnode_firmware_t3s3_sx127x.partitions",
3257                          ]
3258                      elif fw_filename == "rnode_firmware_t3s3_sx1280_pa.zip":
3259                          return [
3260                              sys.executable, flasher,
3261                              "--chip", "esp32s3",
3262                              "--port", args.port,
3263                              "--baud", args.baud_flash,
3264                              "--before", "default_reset",
3265                              "--after", "hard_reset",
3266                              "write_flash", "-z",
3267                              "--flash_mode", "dio",
3268                              "--flash_freq", "80m",
3269                              "--flash_size", "4MB",
3270                              "0xe000",  UPD_DIR+"/"+selected_version+"/rnode_firmware_t3s3_sx1280_pa.boot_app0",
3271                              "0x0",  UPD_DIR+"/"+selected_version+"/rnode_firmware_t3s3_sx1280_pa.bootloader",
3272                              "0x10000", UPD_DIR+"/"+selected_version+"/rnode_firmware_t3s3_sx1280_pa.bin",
3273                              "0x210000",UPD_DIR+"/"+selected_version+"/console_image.bin",
3274                              "0x8000",  UPD_DIR+"/"+selected_version+"/rnode_firmware_t3s3_sx1280_pa.partitions",
3275                          ]
3276                      elif fw_filename == "rnode_firmware_tbeam_supreme.zip":
3277                          return [
3278                              sys.executable, flasher,
3279                              "--chip", "esp32s3",
3280                              "--port", args.port,
3281                              "--baud", args.baud_flash,
3282                              "--before", "default_reset",
3283                              "--after", "hard_reset",
3284                              "write_flash", "-z",
3285                              "--flash_mode", "dio",
3286                              "--flash_freq", "80m",
3287                              "--flash_size", "4MB",
3288                              "0xe000",  UPD_DIR+"/"+selected_version+"/rnode_firmware_tbeam_supreme.boot_app0",
3289                              "0x0",  UPD_DIR+"/"+selected_version+"/rnode_firmware_tbeam_supreme.bootloader",
3290                              "0x10000", UPD_DIR+"/"+selected_version+"/rnode_firmware_tbeam_supreme.bin",
3291                              "0x210000",UPD_DIR+"/"+selected_version+"/console_image.bin",
3292                              "0x8000",  UPD_DIR+"/"+selected_version+"/rnode_firmware_tbeam_supreme.partitions",
3293                          ]
3294                      elif fw_filename == "rnode_firmware_tdeck.zip":
3295                          return [
3296                              sys.executable, flasher,
3297                              "--chip", "esp32s3",
3298                              "--port", args.port,
3299                              "--baud", args.baud_flash,
3300                              "--before", "default_reset",
3301                              "--after", "hard_reset",
3302                              "write_flash", "-z",
3303                              "--flash_mode", "dio",
3304                              "--flash_freq", "80m",
3305                              "--flash_size", "4MB",
3306                              "0xe000",  UPD_DIR+"/"+selected_version+"/rnode_firmware_tdeck.boot_app0",
3307                              "0x0",  UPD_DIR+"/"+selected_version+"/rnode_firmware_tdeck.bootloader",
3308                              "0x10000", UPD_DIR+"/"+selected_version+"/rnode_firmware_tdeck.bin",
3309                              "0x210000",UPD_DIR+"/"+selected_version+"/console_image.bin",
3310                              "0x8000",  UPD_DIR+"/"+selected_version+"/rnode_firmware_tdeck.partitions",
3311                          ]
3312                      elif fw_filename == "rnode_firmware_xiao_esp32s3.zip":
3313                          return [
3314                              sys.executable, flasher,
3315                              "--chip", "esp32s3",
3316                              "--port", args.port,
3317                              "--baud", args.baud_flash,
3318                              "--before", "default_reset",
3319                              "--after", "hard_reset",
3320                              "write_flash", "-z",
3321                              "--flash_mode", "dio",
3322                              "--flash_freq", "80m",
3323                              "--flash_size", "8MB",
3324                              "0xe000",  UPD_DIR+"/"+selected_version+"/rnode_firmware_xiao_esp32s3.boot_app0",
3325                              "0x0",  UPD_DIR+"/"+selected_version+"/rnode_firmware_xiao_esp32s3.bootloader",
3326                              "0x10000", UPD_DIR+"/"+selected_version+"/rnode_firmware_xiao_esp32s3.bin",
3327                              "0x210000",UPD_DIR+"/"+selected_version+"/console_image.bin",
3328                              "0x8000",  UPD_DIR+"/"+selected_version+"/rnode_firmware_xiao_esp32s3.partitions",
3329                          ]
3330                      elif fw_filename == "extracted_rnode_firmware.zip":
3331                          return [
3332                              sys.executable, flasher,
3333                              "--chip", "esp32",
3334                              "--port", args.port,
3335                              "--baud", args.baud_flash,
3336                              "--before", "default_reset",
3337                              "--after", "hard_reset",
3338                              "write_flash", "-z",
3339                              "--flash_mode", "dio",
3340                              "--flash_freq", "80m",
3341                              "--flash_size", "4MB",
3342                              "0x1000",  EXT_DIR+"/extracted_rnode_firmware.bootloader",
3343                              "0xe000",  EXT_DIR+"/extracted_rnode_firmware.boot_app0",
3344                              "0x8000",  EXT_DIR+"/extracted_rnode_firmware.partitions",
3345                              "0x10000", EXT_DIR+"/extracted_rnode_firmware.bin",
3346                              "0x210000",EXT_DIR+"/extracted_console_image.bin",
3347                          ]
3348                      else:
3349                          RNS.log("No flasher available for this board, cannot install firmware.")
3350                  else:
3351                      RNS.log("")
3352                      RNS.log("You do not currently have the \""+flasher+"\" program installed on your system.")
3353                      RNS.log("Unfortunately, that means we can't proceed, since it is needed to flash your")
3354                      RNS.log("board. You can install it via your package manager, for example:")
3355                      RNS.log("")
3356                      RNS.log("  sudo apt install esptool")
3357                      RNS.log("")
3358                      RNS.log("Please install \""+flasher+"\" and try again.")
3359                      graceful_exit()
3360  
3361              elif platform == ROM.PLATFORM_NRF52:
3362                  flasher = "adafruit-nrfutil"
3363                  if which(flasher) is not None:
3364                      return [flasher, "dfu", "serial", "--package", UPD_DIR+"/"+selected_version+"/"+fw_filename, "-p", args.port, "-b", "115200", "-t", "1200"]
3365                  else:
3366                      RNS.log("")
3367                      RNS.log("You do not currently have the \""+flasher+"\" program installed on your system.")
3368                      RNS.log("Unfortunately, that means we can't proceed, since it is needed to flash your")
3369                      RNS.log("board. You can install it via your package manager, for example:")
3370                      RNS.log("")
3371                      RNS.log("  pip3 install --user adafruit-nrfutil")
3372                      RNS.log("")
3373                      RNS.log("Please install \""+flasher+"\" and try again.")
3374                      graceful_exit()
3375  
3376          if args.port:
3377              wants_fw_provision = False
3378              if args.flash:
3379                  from subprocess import call
3380                  
3381                  if fw_filename == None:
3382                      fw_filename = "rnode_firmware.hex"
3383  
3384                  if args.platform == None:
3385                      args.platform = ROM.PLATFORM_AVR
3386  
3387                  if selected_version == None:
3388                      RNS.log("Missing parameters, cannot continue")
3389                      graceful_exit(68)
3390  
3391                  if selected_model in ROM.MANUAL_FLASH_MODELS:
3392                      RNS.log("")
3393                      RNS.log("Please put the board into flashing mode now, by holding the BOOT or PRG button,")
3394                      RNS.log("while momentarily pressing the RESET button. Then release the BOOT or PRG button.")
3395                      RNS.log("Hit enter when this is done.")
3396                      input()
3397  
3398                  if fw_filename == "extracted_rnode_firmware.zip":
3399                      try:
3400                          RNS.log("Flashing RNode firmware to device on "+args.port)
3401                          from subprocess import call
3402                          rc = get_flasher_call(args.platform, fw_filename)
3403                          flash_status = call(rc)
3404                          if flash_status == 0:
3405                              RNS.log("Done flashing")
3406                              args.rom = True
3407                              if args.platform == ROM.PLATFORM_ESP32:
3408                                  wants_fw_provision = True
3409                                  RNS.log("Waiting for ESP32 reset...")
3410                                  time.sleep(7)
3411                          else:
3412                              graceful_exit()
3413  
3414                      except Exception as e:
3415                          RNS.log("Error while flashing")
3416                          RNS.log(str(e))
3417                          graceful_exit(1)
3418                  
3419                  else:
3420                      fw_src = UPD_DIR+"/"+selected_version+"/"
3421                      if os.path.isfile(fw_src+fw_filename):
3422                          try:
3423                              if fw_filename.endswith(".zip"):
3424                                  RNS.log("Decompressing firmware...")
3425                                  try:
3426                                      with zipfile.ZipFile(fw_src+fw_filename) as zip:
3427                                          zip.extractall(fw_src)
3428                                  except Exception as e:
3429                                      RNS.log("Could not decompress firmware from downloaded zip file")
3430                                      graceful_exit()
3431                                  RNS.log("Firmware decompressed")
3432  
3433                              RNS.log("Flashing RNode firmware to device on "+args.port)
3434                              from subprocess import call
3435                              rc = get_flasher_call(args.platform, fw_filename)
3436                              flash_status = call(rc)
3437                              if flash_status == 0:
3438                                  RNS.log("Done flashing")
3439                                  args.rom = True
3440                                  if args.platform == ROM.PLATFORM_ESP32:
3441                                      wants_fw_provision = True
3442                                      RNS.log("Waiting for ESP32 reset...")
3443                                      time.sleep(8)
3444                                  if args.platform == ROM.PLATFORM_NRF52:
3445                                      wants_fw_provision = True
3446                                      RNS.log("Waiting for NRF52 reset...")
3447                                      # Don't need to wait as long this time.
3448                                      time.sleep(6)
3449                              else:
3450                                  RNS.log("Error from flasher ("+str(flash_status)+") while writing.")
3451                                  RNS.log("Some boards have trouble flashing at high speeds, and you can")
3452                                  RNS.log("try flashing with a lower baud rate, as in this example:")
3453                                  RNS.log("rnodeconf --autoinstall --baud-flash 115200")
3454                                  graceful_exit()
3455  
3456                          except Exception as e:
3457                              RNS.log("Error while flashing")
3458                              RNS.log(str(e))
3459                              graceful_exit(1)
3460                      else:
3461                          RNS.log("Firmware file not found")
3462                          graceful_exit()
3463  
3464                  if selected_model in ROM.MANUAL_FLASH_MODELS:
3465                      RNS.log("")
3466                      RNS.log("Please take the board out of flashing mode by momentarily pressing the RESET button.")
3467                      RNS.log("Hit enter when this is done.")
3468                      input()
3469                      sleep(2.5)
3470  
3471              RNS.log("Opening serial port "+args.port+"...")
3472              try:
3473                  rnode_port = args.port
3474                  rnode_serial = rnode_open_serial(rnode_port)
3475              except Exception as e:
3476                  RNS.log("Could not open the specified serial port. The contained exception was:")
3477                  RNS.log(str(e))
3478                  graceful_exit()
3479  
3480              rnode = RNode(rnode_serial)
3481              ports = list_ports.comports()
3482              for port in ports:
3483                  if port.device == args.port:
3484                      rnode.usb_serial_id = port.serial_number
3485                      break
3486              thread = threading.Thread(target=rnode.readLoop, daemon=True).start()
3487  
3488              try:
3489                  rnode.device_probe()
3490              except Exception as e:
3491                  RNS.log("Serial port opened, but RNode did not respond. Is a valid firmware installed?")
3492                  print(e)
3493                  graceful_exit()
3494  
3495              if rnode.detected:
3496                  if rnode.platform == None or rnode.mcu == None:
3497                      rnode.platform = ROM.PLATFORM_AVR
3498                      rnode.mcu = ROM.MCU_1284P
3499  
3500  
3501              if args.eeprom_wipe:
3502                  RNS.log("WARNING: EEPROM is being wiped! Power down device NOW if you do not want this!")
3503                  rnode.wipe_eeprom()
3504  
3505                  if rnode.platform != ROM.PLATFORM_NRF52:
3506                      rnode.hard_reset()
3507  
3508                  graceful_exit()
3509  
3510              RNS.log("Reading EEPROM...")
3511              rnode.download_eeprom()
3512  
3513              if rnode.provisioned:
3514                  if rnode.model != ROM.MODEL_FF:
3515                      fw_filename = models[rnode.model][4]
3516                  else:
3517                      if args.use_extracted:
3518                          fw_filename = "extracted_rnode_firmware.zip"
3519                      else:
3520                          if rnode.platform == ROM.PLATFORM_AVR:
3521                              if rnode.mcu == ROM.MCU_1284P:
3522                                  fw_filename = "rnode_firmware.hex"
3523                              elif rnode.mcu == ROM.MCU_2560:
3524                                  fw_filename = "rnode_firmware_m2560.hex"
3525                          elif rnode.platform == ROM.PLATFORM_ESP32:
3526                              if rnode.board == ROM.BOARD_HUZZAH32:
3527                                  fw_filename = "rnode_firmware_featheresp32.zip"
3528                              elif rnode.board == ROM.BOARD_GENERIC_ESP32:
3529                                  fw_filename = "rnode_firmware_esp32_generic.zip"
3530                          elif rnode.platform == ROM.PLATFORM_NRF52:
3531                              if rnode.board == ROM.BOARD_RAK4631:
3532                                  fw_filename = "rnode_firmware_rak4631.zip"
3533                              else:
3534                                  fw_filename = None
3535  
3536                                  if args.update:
3537                                      RNS.log("ERROR: No firmware found for this board. Cannot update.")
3538                                      graceful_exit()
3539  
3540              if args.update:
3541                  if not rnode.provisioned:
3542                      RNS.log("Device not provisioned. Cannot update device firmware.")
3543                      graceful_exit(1)
3544  
3545                  if args.use_extracted:
3546                      fw_filename = "extracted_rnode_firmware.zip"
3547  
3548                  from subprocess import call
3549  
3550                  try:
3551                      RNS.log("Checking firmware file availability...")
3552                      fw_file_ensured = False
3553                      if selected_version == None:
3554                          ensure_firmware_file(fw_filename)
3555                          fw_file_ensured = True
3556  
3557                      if not force_update:
3558                          if rnode.version == selected_version:
3559                              if args.fw_version != None:
3560                                  RNS.log("Specified firmware version ("+selected_version+") is already installed on this device")
3561                                  RNS.log("Override with -U option to install anyway")
3562                                  graceful_exit(0)
3563                              else:
3564                                  RNS.log("Latest firmware version ("+selected_version+") is already installed on this device")
3565                                  RNS.log("Override with -U option to install anyway")
3566                                  graceful_exit(0)
3567  
3568                          if rnode.version > selected_version:
3569                              if args.fw_version != None:
3570                                  RNS.log("Specified firmware version ("+selected_version+") is older than firmware already installed on this device")
3571                                  RNS.log("Override with -U option to install anyway")
3572                                  graceful_exit(0)
3573                              else:
3574                                  RNS.log("Latest firmware version ("+selected_version+") is older than firmware already installed on this device")
3575                                  RNS.log("Override with -U option to install anyway")
3576                                  graceful_exit(0)
3577  
3578                      if not fw_file_ensured and selected_version != None:
3579                          ensure_firmware_file(fw_filename)
3580  
3581                      if fw_filename.endswith(".zip") and not fw_filename == "extracted_rnode_firmware.zip":
3582                          RNS.log("Decompressing firmware...")
3583                          fw_src = UPD_DIR+"/"+selected_version+"/"
3584                          try:
3585                              with zipfile.ZipFile(fw_src+fw_filename) as zip:
3586                                  zip.extractall(fw_src)
3587                          except Exception as e:
3588                              RNS.log("Could not decompress firmware from downloaded zip file")
3589                              graceful_exit()
3590                          RNS.log("Firmware decompressed")
3591  
3592                  except Exception as e:
3593                      RNS.log("Could not obtain firmware package for your board")
3594                      RNS.log("The contained exception was: "+str(e))
3595                      graceful_exit()
3596  
3597                  if fw_filename == "extracted_rnode_firmware.zip":
3598                      update_full_path = EXT_DIR+"/extracted_rnode_firmware.version"
3599                  else:
3600                      update_full_path = UPD_DIR+"/"+selected_version+"/"+fw_filename
3601                  if os.path.isfile(update_full_path): 
3602                      try:
3603                          args.info = False
3604                          RNS.log("Updating RNode firmware for device on "+args.port)
3605                          if fw_filename == "extracted_rnode_firmware.zip":
3606                              vf = open(update_full_path, "rb")
3607                              release_info = vf.read().decode("utf-8").strip()
3608                              partition_hash = bytes.fromhex(release_info.split()[1])
3609                              vf.close()
3610                          else:
3611                              partition_filename = fw_filename.replace(".zip", ".bin")
3612                              if fw_filename == "extracted_rnode_firmware.zip":
3613                                  partition_full_path = EXT_DIR+"/extracted_rnode_firmware.bin"
3614                              else:
3615                                  partition_full_path = UPD_DIR+"/"+selected_version+"/"+partition_filename
3616                              partition_hash = get_partition_hash(rnode.platform, partition_full_path)
3617                          if partition_hash != None:
3618                              try:
3619                                  rnode.indicate_firmware_update()
3620                              except Exception as e:
3621                                  RNS.log("Error while indicating firmware update start to board, attempting update anyway...")
3622                              rnode.set_firmware_hash(partition_hash)
3623                              sleep(1)
3624  
3625                              if rnode.platform == ROM.PLATFORM_NRF52:
3626                                  # Allow extra time for writing to EEPROM on NRF52. Current implementation is slow.
3627                                  sleep(14)
3628  
3629                          try:
3630                              rnode.disconnect()
3631                          except Exception as e:
3632                              RNS.log("Error while gracefully disconnecting device before firmware update, attempting update anyway...")
3633  
3634                          if rnode.model in ROM.MANUAL_FLASH_MODELS:
3635                              RNS.log("")
3636                              RNS.log("Please put the board into flashing mode now, by holding the BOOT or PRG button,")
3637                              RNS.log("while momentarily pressing the RESET button. Then release the BOOT or PRG button.")
3638                              RNS.log("Hit enter when this is done.")
3639                              input()
3640  
3641                          flash_status = call(get_flasher_call(rnode.platform, fw_filename))
3642                          if flash_status == 0:
3643                              RNS.log("Flashing new firmware completed")
3644                              if rnode.model in ROM.MANUAL_FLASH_MODELS:
3645                                  RNS.log("")
3646                                  RNS.log("Please take the board out of flashing mode by momentarily pressing the RESET button.")
3647                                  RNS.log("Hit enter when this is done.")
3648                                  input()
3649  
3650                              RNS.log("Opening serial port "+args.port+"...")
3651                              try:
3652                                  rnode_port = args.port
3653                                  rnode_serial = rnode_open_serial(rnode_port)
3654                              except Exception as e:
3655                                  RNS.log("Could not open the specified serial port. The contained exception was:")
3656                                  RNS.log(str(e))
3657                                  graceful_exit()
3658  
3659                              rnode = RNode(rnode_serial)
3660                              thread = threading.Thread(target=rnode.readLoop, daemon=True).start()
3661  
3662                              try:
3663                                  rnode.device_probe()
3664                              except Exception as e:
3665                                  RNS.log("Serial port opened, but RNode did not respond. Is a valid firmware installed?")
3666                                  print(e)
3667                                  graceful_exit()
3668  
3669                              if rnode.detected:
3670                                  if rnode.platform == None or rnode.mcu == None:
3671                                      rnode.platform = ROM.PLATFORM_AVR
3672                                      rnode.mcu = ROM.MCU_1284P
3673  
3674                                  RNS.log("Reading EEPROM...")
3675                                  rnode.download_eeprom()
3676  
3677                                  if rnode.provisioned:
3678                                      if rnode.model != ROM.MODEL_FF:
3679                                          fw_filename = models[rnode.model][4]
3680                                      else:
3681                                          fw_filename = None
3682                                      args.info = True
3683                                      if partition_hash != None:
3684                                          rnode.set_firmware_hash(partition_hash)
3685  
3686                              if args.info:
3687                                  RNS.log("")
3688                                  RNS.log("Firmware update completed successfully")
3689                          else:
3690                              RNS.log("An error occurred while flashing the new firmware, exiting now.")
3691                              graceful_exit()
3692  
3693                      except Exception as e:
3694                          RNS.log("Error while updating firmware")
3695                          RNS.log(str(e))
3696                  else:
3697                      RNS.log("Firmware update file not found")
3698                      graceful_exit()
3699  
3700              if args.config:
3701                  rnode.download_cfg_sector()
3702                  eeprom_reserved = 200
3703                  if rnode.platform == ROM.PLATFORM_ESP32: eeprom_size = 296
3704                  elif rnode.platform == ROM.PLATFORM_NRF52: eeprom_size = 296
3705                  else: eeprom_size = 4096
3706  
3707                  eeprom_offset = eeprom_size-eeprom_reserved
3708                  def ea(a): return a+eeprom_offset
3709                  ec_bt   = rnode.eeprom[ROM.ADDR_CONF_BT]
3710                  ec_dint = rnode.eeprom[ROM.ADDR_CONF_DINT]
3711                  ec_dadr = rnode.eeprom[ROM.ADDR_CONF_DADR]
3712                  ec_dblk = rnode.eeprom[ROM.ADDR_CONF_DBLK]
3713                  ec_drot = rnode.eeprom[ROM.ADDR_CONF_DROT]
3714                  ec_pset = rnode.eeprom[ROM.ADDR_CONF_PSET]
3715                  ec_pint = rnode.eeprom[ROM.ADDR_CONF_PINT]
3716                  ec_bset = rnode.eeprom[ROM.ADDR_CONF_BSET]
3717                  ec_dia  = rnode.eeprom[ROM.ADDR_CONF_DIA]
3718                  ec_wifi = rnode.eeprom[ROM.ADDR_CONF_WIFI]
3719                  ec_wchn = rnode.eeprom[ROM.ADDR_CONF_WCHN]
3720                  ec_ssid = None
3721                  ec_psk  = None
3722                  ec_ip   = None
3723                  ec_nm   = None
3724  
3725                  if ec_wchn < 1 or ec_wchn > 14: ec_wchn = 1
3726                  if rnode.cfg_sector:
3727                      ssid_bytes = b""
3728                      for i in range(0, 32):
3729                          byte = rnode.cfg_sector[ROM.ADDR_CONF_SSID+i]
3730                          if byte == 0xFF: byte = 0x00
3731                          if byte == 0x00: break
3732                          else: ssid_bytes += bytes([byte])
3733  
3734                      try: ec_ssid = ssid_bytes.decode("utf-8")
3735                      except Exception as e: print(f"Error: Could not decode WiFi SSID read from device")
3736  
3737                      psk_bytes = b""
3738                      for i in range(0, 32):
3739                          byte = rnode.cfg_sector[ROM.ADDR_CONF_PSK+i]
3740                          if byte == 0xFF: byte = 0x00
3741                          if byte == 0x00: break
3742                          else: psk_bytes += bytes([byte])
3743  
3744                      ip_bytes = b""
3745                      for i in range(0, 4):
3746                          byte = rnode.cfg_sector[ROM.ADDR_CONF_IP+i]
3747                          ip_bytes += bytes([byte])
3748                      if len(ip_bytes) == 4: ec_ip = f"{int(ip_bytes[0])}.{int(ip_bytes[1])}.{int(ip_bytes[2])}.{int(ip_bytes[3])}"
3749                      if ec_ip == "255.255.255.255" or ec_ip == "0.0.0.0": ec_ip = None
3750  
3751                      nm_bytes = b""
3752                      for i in range(0, 4):
3753                          byte = rnode.cfg_sector[ROM.ADDR_CONF_NM+i]
3754                          nm_bytes += bytes([byte])
3755                      if len(nm_bytes) == 4: ec_nm = f"{int(nm_bytes[0])}.{int(nm_bytes[1])}.{int(nm_bytes[2])}.{int(nm_bytes[3])}"
3756                      if ec_nm == "255.255.255.255" or ec_nm == "0.0.0.0": ec_nm = None
3757  
3758                      if ec_wifi == 0x02:
3759                          ec_ip = "10.0.0.1"
3760                          ec_nm = "255.255.255.0"
3761  
3762                      try: ec_psk = psk_bytes.decode("utf-8")
3763                      except Exception as e: print(f"Error: Could not decode WiFi PSK read from device")
3764                      if not args.show_psk and ec_psk: ec_psk = "*"*len(ec_psk)
3765  
3766                  print("\nDevice configuration:")
3767                  if ec_bt == 0x73:       print(f"  Bluetooth              : Enabled")
3768                  else:                   print(f"  Bluetooth              : Disabled")
3769                  if ec_wifi == 0x01:     print(f"  WiFi                   : Enabled (Station)")
3770                  if ec_wifi == 0x02:     print(f"  WiFi                   : Enabled (AP)")
3771                  else:                   print(f"  WiFi                   : Disabled")
3772                  if ec_wifi == 0x01 or ec_wifi == 0x02:
3773                      if not ec_wchn:     print(f"    Channel              : Unknown")
3774                      else:               print(f"    Channel              : {ec_wchn}")
3775                      if not ec_ssid:     print(f"    SSID                 : Not set")
3776                      else:               print(f"    SSID                 : {ec_ssid}")
3777                      if not ec_psk:      print(f"    PSK                  : Not set")
3778                      else:               print(f"    PSK                  : {ec_psk}")
3779                      if not ec_ip:       print(f"    IP Address           : DHCP")
3780                      else:               print(f"    IP Address           : {ec_ip}")
3781                      if ec_ip and ec_nm: print(f"    Network Mask         : {ec_nm}")
3782                  if ec_dia == 0x00:      print(f"  Interference avoidance : Enabled")
3783                  else:                   print(f"  Interference avoidance : Disabled")
3784                  print(    f"  Display brightness     : {ec_dint}")
3785                  if ec_dadr == 0xFF:     print(f"  Display address        : Default")
3786                  else:                   print(f"  Display address        : {RNS.hexrep(ec_dadr, delimit=False)}")
3787                  if ec_bset == 0x73 and ec_dblk != 0x00: print(f"  Display blanking       : {ec_dblk}s")
3788                  else:                                   print(f"  Display blanking       : Disabled")
3789                  if ec_drot != 0xFF:
3790                      if ec_drot == 0x00: rstr = "Landscape"
3791                      if ec_drot == 0x01: rstr = "Portrait"
3792                      if ec_drot == 0x02: rstr = "Landscape 180"
3793                      if ec_drot == 0x03: rstr = "Portrait 180"
3794                      print(f"  Display rotation       : {rstr}")
3795                  else:
3796                      print(f"  Display rotation       : Default")
3797                  if ec_pset == 0x73: print(f"  Neopixel Intensity     : {ec_pint}")
3798                  print("")
3799  
3800                  rnode.leave()
3801                  graceful_exit()
3802  
3803              if args.eeprom_dump:
3804                  RNS.log("EEPROM contents:")
3805                  RNS.log(RNS.hexrep(rnode.eeprom))
3806                  graceful_exit()
3807  
3808              if args.eeprom_backup:
3809                  try:
3810                      timestamp = time.time()
3811                      filename = str(time.strftime("%Y-%m-%d_%H-%M-%S"))
3812                      path = ROM_DIR + filename + ".eeprom"
3813                      file = open(path, "wb")
3814                      file.write(rnode.eeprom)
3815                      file.close()
3816                      RNS.log("EEPROM backup written to: "+path)
3817                  except Exception as e:
3818                      RNS.log("EEPROM was successfully downloaded from device,")
3819                      RNS.log("but file could not be written to disk.")
3820                  graceful_exit()
3821  
3822              if isinstance(args.display, int):
3823                  di = args.display
3824                  if di < 0:
3825                      di = 0
3826                  if di > 255:
3827                      di = 255
3828                  RNS.log("Setting display intensity to "+str(di))
3829                  rnode.set_display_intensity(di)
3830  
3831              if isinstance(args.timeout, int):
3832                  di = args.timeout
3833                  if di < 0:
3834                      di = 0
3835                  if di > 255:
3836                      di = 255
3837                  if di == 0:
3838                      RNS.log("Disabling display blanking")
3839                  else:
3840                      RNS.log("Setting display timeout to "+str(di))
3841                  rnode.set_display_blanking(di)
3842  
3843              if isinstance(args.rotation, int):
3844                  dr = args.rotation
3845                  if dr < 0:
3846                      dr = 0
3847                  if dr > 3:
3848                      dr = 3
3849  
3850                  RNS.log("Setting display rotation to "+str(dr))
3851                  rnode.set_display_rotation(dr)
3852  
3853              if isinstance(args.recondition_display, bool):
3854                  if args.recondition_display:
3855                      RNS.log("Starting display reconditioning")
3856                      rnode.recondition_display()
3857  
3858              if isinstance(args.ia_enable, bool):
3859                  if args.ia_enable:
3860                      RNS.log("Enabling interference avoidance")
3861                      rnode.set_disable_interference_avoidance(False)
3862  
3863              if isinstance(args.ia_disable, bool):
3864                  if args.ia_disable:
3865                      RNS.log("Disabling interference avoidance")
3866                      rnode.set_disable_interference_avoidance(True)
3867  
3868              if isinstance(args.np, int):
3869                  di = args.np
3870                  if di < 0:
3871                      di = 0
3872                  if di > 255:
3873                      di = 255
3874                  RNS.log("Setting NeoPixel intensity to "+str(di))
3875                  rnode.set_neopixel_intensity(di)
3876  
3877              if isinstance(args.display_addr, str):
3878                  set_addr = False
3879                  try:
3880                      if args.display_addr.startswith("0x"):
3881                          args.display_addr = args.display_addr[2:]
3882                      da = bytes.fromhex(args.display_addr)
3883                      set_addr = True
3884                  except Exception as e:
3885                      pass
3886  
3887                  if set_addr and len(da) == 1:
3888                      RNS.log("Setting display address to "+RNS.hexrep(da, delimit=False))
3889                      rnode.set_display_address(ord(da))
3890                      rnode.hard_reset()
3891                      graceful_exit()
3892                  else:
3893                      RNS.log("Invalid display address specified")
3894  
3895              if args.bluetooth_on:
3896                  RNS.log("Enabling Bluetooth...")
3897                  rnode.enable_bluetooth()
3898                  rnode.leave()
3899  
3900              if args.bluetooth_off:
3901                  RNS.log("Disabling Bluetooth...")
3902                  rnode.disable_bluetooth()
3903                  rnode.leave()
3904  
3905              if args.bluetooth_pair:
3906                  RNS.log("Putting device into Bluetooth pairing mode. Press enter to exit when done.")
3907                  rnode.bluetooth_pair()
3908                  input()
3909                  rnode.leave()
3910  
3911              if args.channel:
3912                  try:
3913                      RNS.log(f"Setting WiFi channel to {args.channel}")
3914                      rnode.set_wifi_channel(args.channel)
3915                  except Exception as e:
3916                      print(f"Could not set WiFi channel: {e}")
3917                      graceful_exit()
3918  
3919              if args.ssid:
3920                  try:
3921                      if args.ssid.lower() == "none":
3922                          ssid_str = None
3923                          RNS.log(f"Deleting WiFi SSID")
3924                      else:
3925                          ssid_str = str(args.ssid)
3926                          RNS.log(f"Setting WiFi SSID to: {ssid_str}")
3927                      rnode.set_wifi_ssid(ssid_str)
3928                  except Exception as e:
3929                      print(f"Could not set WiFi SSID: {e}")
3930                      graceful_exit()
3931  
3932              if args.psk:
3933                  try:
3934                      if args.psk.lower() == "none":
3935                          psk_str = None
3936                          RNS.log(f"Deleting WiFi PSK")
3937                      else:
3938                          psk_str = str(args.psk)
3939                          RNS.log(f"Setting WiFi PSK")
3940                      rnode.set_wifi_psk(psk_str)
3941                  except Exception as e:
3942                      print(f"Could not set WiFi PSK: {e}")
3943                      graceful_exit()
3944  
3945              if args.ip:
3946                  try:
3947                      if args.ip.lower() == "none":
3948                          RNS.log(f"Setting WiFi IP to DHCP...")
3949                          rnode.set_wifi_ip(None)
3950                      else:
3951                          RNS.log(f"Setting WiFi static IP to: {args.ip}")
3952                          rnode.set_wifi_ip(args.ip)
3953                  except Exception as e:
3954                      print(f"Could not set WiFi IP: {e}")
3955                      graceful_exit()
3956  
3957              if args.nm:
3958                  try:
3959                      if args.nm.lower() == "none":
3960                          RNS.log(f"Deleting WiFi static netmask configuration...")
3961                          rnode.set_wifi_nm(None)
3962                      else:
3963                          RNS.log(f"Setting WiFi static netmask to: {args.nm}")
3964                          rnode.set_wifi_nm(args.nm)
3965                  except Exception as e:
3966                      print(f"Could not set WiFi netmask: {e}")
3967                      graceful_exit()
3968  
3969              if args.wifi:
3970                  try:
3971                      mode = 0x00
3972                      if   str(args.wifi).lower().startswith("sta"): mode = 0x01
3973                      elif str(args.wifi).lower().startswith("ap"):  mode = 0x02
3974                      if mode == 0x00: RNS.log(f"Disabling WiFi...")
3975                      elif mode == 0x01: RNS.log(f"Setting WiFi to station mode")
3976                      elif mode == 0x02: RNS.log(f"Setting WiFi to AP mode")
3977                      rnode.set_wifi_mode(mode)
3978                  except Exception as e:
3979                      print(f"Could not set WiFi mode: {e}")
3980                      graceful_exit()
3981  
3982              if args.info:
3983                  if rnode.provisioned:
3984                      timestamp = struct.unpack(">I", rnode.made)[0]
3985                      timestring = datetime.datetime.fromtimestamp(timestamp).strftime("%Y-%m-%d %H:%M:%S")
3986                      sigstring = "Unverified"
3987                      if rnode.signature_valid:
3988                          if rnode.locally_signed:
3989                              sigstring = "Validated - Local signature"
3990                          else:
3991                              sigstring = "Genuine board, vendor is "+rnode.vendor
3992  
3993                      if rnode.board != None:
3994                          board_string = ":"+bytes([rnode.board]).hex()
3995                      else:
3996                          board_string = ""
3997  
3998                      RNS.log("")
3999                      RNS.log("Device info:")
4000                      RNS.log("\tProduct            : "+products[rnode.product]+" "+models[rnode.model][3]+" ("+bytes([rnode.product]).hex()+":"+bytes([rnode.model]).hex()+board_string+")")
4001                      RNS.log("\tDevice signature   : "+sigstring)
4002                      RNS.log("\tFirmware version   : "+rnode.version)
4003                      RNS.log("\tHardware revision  : "+str(int(rnode.hw_rev)))
4004                      RNS.log("\tSerial number      : "+RNS.hexrep(rnode.serialno))
4005                      RNS.log("\tModem chip         : "+str(models[rnode.model][5]))
4006                      RNS.log("\tFrequency range    : "+str(rnode.min_freq/1e6)+" MHz - "+str(rnode.max_freq/1e6)+" MHz")
4007                      RNS.log("\tMax TX power       : "+str(rnode.max_output)+" dBm")
4008                      RNS.log("\tManufactured       : "+timestring)
4009  
4010                      if rnode.configured:
4011                          rnode.bandwidth = rnode.conf_bandwidth
4012                          rnode.r_bandwidth = rnode.conf_bandwidth
4013                          rnode.sf = rnode.conf_sf
4014                          rnode.r_sf = rnode.conf_sf
4015                          rnode.cr = rnode.conf_cr
4016                          rnode.r_cr = rnode.conf_cr
4017                          rnode.updateBitrate()
4018                          txp_mw = round(pow(10, (rnode.conf_txpower/10)), 3)
4019                          RNS.log("");
4020                          RNS.log("\tDevice mode        : TNC")
4021                          RNS.log("\t  Frequency        : "+str((rnode.conf_frequency/1000000.0))+" MHz")
4022                          RNS.log("\t  Bandwidth        : "+str(rnode.conf_bandwidth/1000.0)+" KHz")
4023                          RNS.log("\t  TX power         : "+str(rnode.conf_txpower)+" dBm ("+str(txp_mw)+" mW)")
4024                          RNS.log("\t  Spreading factor : "+str(rnode.conf_sf))
4025                          RNS.log("\t  Coding rate      : "+str(rnode.conf_cr))
4026                          RNS.log("\t  On-air bitrate   : "+str(rnode.bitrate_kbps)+" kbps")
4027                      else:
4028                          RNS.log("\tDevice mode        : Normal (host-controlled)")
4029  
4030                      print("")
4031                      rnode.disconnect()
4032                      graceful_exit()
4033  
4034                  else:
4035                      RNS.log("EEPROM is invalid, no further information available")
4036                      graceful_exit()
4037  
4038              if args.rom:
4039                  if rnode.provisioned and not args.autoinstall:
4040                      RNS.log("EEPROM bootstrap was requested, but a valid EEPROM was already present.")
4041                      RNS.log("No changes are being made.")
4042                      graceful_exit()
4043  
4044                  else:
4045                      if rnode.signature_valid:
4046                          RNS.log("EEPROM bootstrap was requested, but a valid EEPROM was already present.")
4047                          RNS.log("No changes are being made.")
4048                          graceful_exit()
4049                      else:
4050                          if args.autoinstall:
4051                              RNS.log("Clearing old EEPROM, this will take about 15 seconds...")
4052                              rnode.wipe_eeprom()
4053                              
4054                          if rnode.platform == ROM.PLATFORM_ESP32:
4055                              RNS.log("Waiting for ESP32 reset...")
4056                              time.sleep(6)
4057                          elif rnode.platform == ROM.PLATFORM_NRF52:
4058                              rnode_serial.close()
4059                              RNS.log("Waiting for NRF52 reset...")
4060                              time.sleep(18)
4061                              selected_port = None
4062                              ports = list_ports.comports()
4063                              for port in ports:
4064                                  if port.serial_number == rnode.usb_serial_id:
4065                                      selected_port = port
4066                                      break
4067                              if selected_port is None:
4068                                  RNS.log("Could not detect new port for NRF52...")
4069                              else:
4070                                  try:
4071                                      rnode_serial = rnode_open_serial(selected_port.device)
4072                                      rnode.serial = rnode_serial
4073                                      thread = threading.Thread(target=rnode.readLoop, daemon=True).start()
4074                                  except Exception as e:
4075                                      RNS.log("Could not open the specified serial port. The contained exception was:")
4076                                      RNS.log(str(e))
4077                                      exit()
4078                          else:
4079                              time.sleep(3)
4080  
4081                      counter = None
4082                      counter_path = FWD_DIR+"/serial.counter"
4083                      try:
4084                          if os.path.isfile(counter_path):
4085                              file = open(counter_path, "r")
4086                              counter_str = file.read()
4087                              counter = int(counter_str)
4088                              file.close()
4089                          else:
4090                              counter = 0
4091                      except Exception as e:
4092                          RNS.log("Could not create device serial number, exiting")
4093                          RNS.log(str(e))
4094                          graceful_exit()
4095  
4096                      serialno = counter+1
4097                      model = None
4098                      hwrev = None
4099                      if args.product != None:
4100                          if args.product == "03":
4101                              mapped_product = ROM.PRODUCT_RNODE
4102                          elif args.product == "10":
4103                              mapped_product = ROM.PRODUCT_RAK4631
4104                          elif args.product == "f0":
4105                              mapped_product = ROM.PRODUCT_HMBRW
4106                          elif args.product == "e0":
4107                              mapped_product = ROM.PRODUCT_TBEAM
4108                          else:
4109                              if len(args.product) == 2:
4110                                  mapped_product = ord(bytes.fromhex(args.product))
4111  
4112                      if mapped_model != None:
4113                          if mapped_model == ROM.MODEL_B4_TCXO:
4114                              model = ROM.MODEL_B4
4115                          elif mapped_model == ROM.MODEL_B9_TCXO:
4116                              model = ROM.MODEL_B9
4117                          else:
4118                              model = mapped_model
4119                      else:
4120                          if args.model == "11":
4121                              model = ROM.MODEL_11
4122                          elif args.model == "12":
4123                              model = ROM.MODEL_12
4124                          elif args.model == "a4":
4125                              model = ROM.MODEL_A4
4126                          elif args.model == "a9":
4127                              model = ROM.MODEL_A9
4128                          elif args.model == "a1":
4129                              model = ROM.MODEL_A1
4130                          elif args.model == "a6":
4131                              model = ROM.MODEL_A6
4132                          elif args.model == "e4":
4133                              model = ROM.MODEL_E4
4134                          elif args.model == "e9":
4135                              model = ROM.MODEL_E9
4136                          elif args.model == "ff":
4137                              model = ROM.MODEL_FF
4138                          else:
4139                              if len(args.model) == 2:
4140                                  model = ord(bytes.fromhex(args.model))
4141  
4142                          # Initialize selected_model from specified model
4143                          selected_model = model
4144  
4145                      if args.hwrev != None and (args.hwrev > 0 and args.hwrev < 256):
4146                          hwrev = chr(args.hwrev)
4147  
4148                      if serialno > 0 and model != None and hwrev != None:
4149                          try:
4150                              from cryptography.hazmat.primitives import hashes
4151                              from cryptography.hazmat.backends import default_backend
4152  
4153                              timestamp = int(time.time())
4154                              time_bytes = struct.pack(">I", timestamp)
4155                              serial_bytes = struct.pack(">I", serialno)
4156                              file = open(counter_path, "w")
4157                              file.write(str(serialno))
4158                              file.close()
4159  
4160                              info_chunk  = b"" + bytes([mapped_product, model, ord(hwrev)])
4161                              info_chunk += serial_bytes
4162                              info_chunk += time_bytes
4163                              digest = hashes.Hash(hashes.MD5(), backend=default_backend())
4164                              digest.update(info_chunk)
4165                              checksum = digest.finalize()
4166  
4167                              RNS.log("Loading signing key...")
4168                              signature = None
4169                              key_path = FWD_DIR+"/signing.key"
4170                              if os.path.isfile(key_path):
4171                                  try:
4172                                      file = open(key_path, "rb")
4173                                      private_bytes = file.read()
4174                                      file.close()
4175                                      private_key = serialization.load_der_private_key(
4176                                          private_bytes,
4177                                          password=None,
4178                                          backend=default_backend()
4179                                      )
4180                                      public_key = private_key.public_key()
4181                                      public_bytes = public_key.public_bytes(
4182                                          encoding=serialization.Encoding.DER,
4183                                          format=serialization.PublicFormat.SubjectPublicKeyInfo
4184                                      )
4185                                      signature = private_key.sign(
4186                                          checksum,
4187                                          padding.PSS(
4188                                              mgf=padding.MGF1(hashes.SHA256()),
4189                                              salt_length=padding.PSS.MAX_LENGTH
4190                                          ),
4191                                          hashes.SHA256()
4192                                      )
4193                                  except Exception as e:
4194                                      RNS.log("Error while signing EEPROM")
4195                                      RNS.log(str(e))
4196                              else:
4197                                  RNS.log("No signing key found")
4198                                  graceful_exit()
4199  
4200                              if selected_model in ROM.MANUAL_FLASH_MODELS:
4201                                  rnode.serial.close()
4202                                  RNS.log("")
4203                                  RNS.log("Please reset the board by momentarily pressing the RESET button.")
4204                                  RNS.log("Hit enter when this is done.")
4205                                  input()
4206                                  sleep(2.5)
4207                                  rnode_serial = rnode_open_serial(rnode_port)
4208                                  rnode = RNode(rnode_serial)
4209  
4210                              RNS.log("Bootstrapping device EEPROM...")
4211  
4212                              rnode.write_eeprom(ROM.ADDR_PRODUCT, mapped_product)
4213                              time.sleep(0.006)
4214                              rnode.write_eeprom(ROM.ADDR_MODEL, model)
4215                              time.sleep(0.006)
4216                              rnode.write_eeprom(ROM.ADDR_HW_REV, ord(hwrev))
4217                              time.sleep(0.006)
4218                              rnode.write_eeprom(ROM.ADDR_SERIAL, serial_bytes[0])
4219                              time.sleep(0.006)
4220                              rnode.write_eeprom(ROM.ADDR_SERIAL+1, serial_bytes[1])
4221                              time.sleep(0.006)
4222                              rnode.write_eeprom(ROM.ADDR_SERIAL+2, serial_bytes[2])
4223                              time.sleep(0.006)
4224                              rnode.write_eeprom(ROM.ADDR_SERIAL+3, serial_bytes[3])
4225                              time.sleep(0.006)
4226                              rnode.write_eeprom(ROM.ADDR_MADE, time_bytes[0])
4227                              time.sleep(0.006)
4228                              rnode.write_eeprom(ROM.ADDR_MADE+1, time_bytes[1])
4229                              time.sleep(0.006)
4230                              rnode.write_eeprom(ROM.ADDR_MADE+2, time_bytes[2])
4231                              time.sleep(0.006)
4232                              rnode.write_eeprom(ROM.ADDR_MADE+3, time_bytes[3])
4233                              time.sleep(0.006)
4234  
4235                              for i in range(0,16):
4236                                  rnode.write_eeprom(ROM.ADDR_CHKSUM+i, checksum[i])
4237                                  time.sleep(0.006)
4238  
4239                              for i in range(0,128):
4240                                  rnode.write_eeprom(ROM.ADDR_SIGNATURE+i, signature[i])
4241                                  time.sleep(0.006)
4242  
4243                              rnode.write_eeprom(ROM.ADDR_INFO_LOCK, ROM.INFO_LOCK_BYTE)
4244                              if rnode.platform == ROM.PLATFORM_NRF52:
4245                                  # Allow extra time for writing to EEPROM on NRF52. Current implementation is slow.
4246                                  sleep(3)
4247  
4248                              RNS.log("EEPROM written! Validating...")
4249  
4250                              if wants_fw_provision:
4251                                  partition_hash = None
4252  
4253                                  if fw_filename == "extracted_rnode_firmware.zip":
4254                                      update_full_path = EXT_DIR+"/extracted_rnode_firmware.version"
4255                                      vf = open(update_full_path, "rb")
4256                                      release_info = vf.read().decode("utf-8").strip()
4257                                      partition_hash = bytes.fromhex(release_info.split()[1])
4258                                      vf.close()
4259                                  else:
4260                                      partition_filename = fw_filename.replace(".zip", ".bin")
4261                                      partition_hash = get_partition_hash(rnode.platform, UPD_DIR+"/"+selected_version+"/"+partition_filename)
4262  
4263                                  if partition_hash != None:
4264                                      time.sleep(0.75)
4265                                      RNS.log("Setting firmware checksum...")
4266                                      rnode.set_firmware_hash(partition_hash)
4267  
4268                              if rnode.platform == ROM.PLATFORM_ESP32:
4269                                  rnode.hard_reset()
4270                                  RNS.log("Waiting for ESP32 reset...")
4271                                  time.sleep(7)
4272                                  if selected_model in [ROM.MODEL_AC, ROM.MODEL_A6, ROM.MODEL_A1, ROM.MODEL_AA, ROM.MODEL_A5]:
4273                                      time.sleep(5)
4274  
4275                              elif rnode.platform == ROM.PLATFORM_NRF52:
4276                                  # Wait a few seconds before hard resetting.
4277                                  # Otherwise, macOS fails to set firmware hash on NRF52
4278                                  if RNS.vendor.platformutils.is_darwin():
4279                                      time.sleep(5)
4280  
4281                                  rnode.hard_reset()
4282                                  # The hard reset on this platform is different
4283                                  # to that of the ESP32 platform, it causes
4284                                  # disruption to the serial connection.
4285                                  # Therefore, we have to reestablish the serial
4286                                  # connection after the reset.
4287                                  rnode_serial.close()
4288                                  RNS.log("Waiting for NRF52 reset...")
4289  
4290                                  # Give plenty of time for to allow for
4291                                  # potential e-ink display refresh too.
4292                                  time.sleep(20)
4293  
4294                                  # After the hard reset, the port number will
4295                                  # change. We need to find the new port number,
4296                                  # which can be done non-interactively by
4297                                  # comparing the USB serial numbers of the
4298                                  # original port and the one we are currently
4299                                  # iterating.
4300                                  selected_port = None
4301                                  ports = list_ports.comports()
4302                                  for port in ports:
4303                                      if port.serial_number == rnode.usb_serial_id:
4304                                          selected_port = port
4305                                          break
4306                                  if selected_port is None:
4307                                      RNS.log("Could not detect new port for NRF52...")
4308                                  else:
4309                                      try:
4310                                          rnode_serial = rnode_open_serial(selected_port.device)
4311                                          rnode.serial = rnode_serial
4312                                          thread = threading.Thread(target=rnode.readLoop, daemon=True).start()
4313                                      except Exception as e:
4314                                          RNS.log("Could not open the specified serial port. The contained exception was:")
4315                                          RNS.log(str(e))
4316                                          exit()
4317                              else:
4318                                  rnode.hard_reset()
4319  
4320                              if selected_model in ROM.MANUAL_FLASH_MODELS:
4321                                  rnode.serial.close()
4322                                  RNS.log("")
4323                                  RNS.log("Please reset the board by momentarily pressing the RESET button.")
4324                                  RNS.log("Hit enter when this is done.")
4325                                  input()
4326                                  rnode.provisioned = True
4327                              else:
4328                                  rnode.download_eeprom()
4329  
4330                              if rnode.provisioned:
4331                                  RNS.log("EEPROM Bootstrapping successful!")
4332                                  if not selected_model in ROM.MANUAL_FLASH_MODELS:
4333                                      rnode.hard_reset()
4334  
4335                                  if args.autoinstall:
4336                                      print("")
4337                                      print("RNode Firmware autoinstallation complete!")
4338                                      print("")
4339                                      print("To use your device with Reticulum, read the documetation at:")
4340                                      print("")
4341                                      print("https://markqvist.github.io/Reticulum/manual/gettingstartedfast.html")
4342                                      print("")
4343                                      print("* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *")
4344                                      print("                              Important!            ")
4345                                      print("")
4346                                      print("ESP32-based RNodes are created with the RNode Bootstrap Console on-board.")
4347                                      print("")
4348                                      print("This repository is hosted directly on the RNode, and contains a wealth of")
4349                                      print("information, software and tools.")
4350                                      print("")
4351                                      print("The RNode Bootstrap Console also contains everything needed to build")
4352                                      print("and replicate RNodes, including detailed build recipes, 3D-printable")
4353                                      print("cases, and copies of the source code for both the RNode Firmware,")
4354                                      print("Reticulum and other utilities.")
4355                                      print("")
4356                                      print("To activate the RNode Bootstrap Console, power up your RNode and hold")
4357                                      print("down the user button for 10+ seconds, then release. The RNode will now")
4358                                      print("reboot into console mode, and activate a WiFi access point for you to")
4359                                      print("connect to. The console is then reachable at: http://10.0.0.1")
4360                                      print("")
4361                                      print("* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *")
4362                                      print("")
4363                                      print("Thank you for using this utility! Please help the project by")
4364                                      print("contributing code and reporting bugs, or by donating!")
4365                                      print("")
4366                                      print("Your contributions and donations directly further the realisation")
4367                                      print("of truly open, free and resilient communications systems.")
4368                                      print("")
4369                                      print_donation_block()
4370                                      print("")
4371                                  try:
4372                                      os.makedirs(FWD_DIR+"/device_db/", exist_ok=True)
4373                                      file = open(FWD_DIR+"/device_db/"+serial_bytes.hex(), "wb")
4374                                      written = file.write(rnode.eeprom)
4375                                      file.close()
4376                                  except Exception as e:
4377                                      RNS.log("WARNING: Could not backup device EEPROM to disk")
4378                                  graceful_exit()
4379                              else:
4380                                  RNS.log("EEPROM was written, but validation failed. Check your settings.")
4381                                  graceful_exit()
4382                          except Exception as e:
4383                              RNS.log("An error occurred while writing EEPROM. The contained exception was:")
4384                              RNS.log(str(e))
4385                              raise e
4386  
4387                      else:
4388                          RNS.log("Invalid data specified, cancelling EEPROM write")
4389                          graceful_exit()
4390  
4391              if args.sign:
4392                  if rnode.provisioned:
4393                      try:
4394                          device_signer = RNS.Identity.from_file(FWD_DIR+"/device.key")
4395                      except Exception as e:
4396                          RNS.log("Could not load device signing key")
4397  
4398                      if rnode.device_hash == None:
4399                          RNS.log("No device hash present, skipping device signing")
4400                      else:
4401                          if device_signer == None:
4402                              RNS.log("No device signer loaded, cannot sign device")
4403                              graceful_exit(78)
4404                          else:
4405                              new_device_signature = device_signer.sign(rnode.device_hash)
4406                              rnode.store_signature(new_device_signature)
4407                              RNS.log("Device signed")
4408                  else:
4409                      RNS.log("This device has not been provisioned yet, cannot create device signature")
4410                      graceful_exit(79)
4411  
4412              if args.firmware_hash != None:
4413                  if rnode.provisioned:
4414                      try:
4415                          hash_data = bytes.fromhex(args.firmware_hash)
4416                          if len(hash_data) != 32:
4417                              raise ValueError("Incorrect hash length")
4418  
4419                          rnode.set_firmware_hash(hash_data)
4420                          RNS.log("Firmware hash set")
4421                      except Exception as e:
4422                          RNS.log("The provided value was not a valid SHA256 hash")
4423                          graceful_exit(78)
4424  
4425                  else:
4426                      RNS.log("This device has not been provisioned yet, cannot set firmware hash")
4427                      graceful_exit(77)
4428  
4429              if args.get_target_firmware_hash:
4430                  if rnode.provisioned:
4431                      RNS.log(f"The target firmware hash is: {rnode.firmware_hash_target.hex()}")
4432  
4433                  else:
4434                      RNS.log("This device has not been provisioned yet, cannot get firmware hash")
4435                      exit(77)
4436                  
4437              if args.get_firmware_hash:
4438                  if rnode.provisioned:
4439                      RNS.log(f"The actual firmware hash is: {rnode.firmware_hash.hex()}")
4440  
4441                  else:
4442                      RNS.log("This device has not been provisioned yet, cannot get firmware hash")
4443                      exit(77)
4444  
4445              if rnode.provisioned:
4446                  if args.normal:
4447                      rnode.setNormalMode()
4448                      RNS.log("Device set to normal (host-controlled) operating mode")
4449                      graceful_exit()
4450                  if args.tnc:
4451                      if not (args.freq and args.bw and args.txp and args.sf and args.cr):
4452                          RNS.log("Please input startup configuration:")
4453  
4454                      print("")
4455                      if args.freq:
4456                          rnode.frequency = args.freq
4457                      else:
4458                          print("Frequency in Hz:\t", end="")
4459                          rnode.frequency = int(input())
4460  
4461  
4462                      if args.bw:
4463                          rnode.bandwidth = args.bw
4464                      else:
4465                          print("Bandwidth in Hz:\t", end="")
4466                          rnode.bandwidth = int(input())
4467  
4468                      if args.txp != None and (args.txp >= 0 and args.txp <= 17):
4469                          rnode.txpower = args.txp
4470                      else:
4471                          print("TX Power in dBm:\t", end="")
4472                          rnode.txpower = int(input())
4473  
4474                      if args.sf:
4475                          rnode.sf = args.sf
4476                      else:
4477                          print("Spreading factor:\t", end="")
4478                          rnode.sf = int(input())
4479  
4480                      if args.cr:
4481                          rnode.cr = args.cr
4482                      else:
4483                          print("Coding rate:\t\t", end="")
4484                          rnode.cr = int(input())
4485  
4486                      print("")
4487  
4488                      rnode.initRadio()
4489                      sleep(0.5)
4490                      rnode.setTNCMode()
4491                      RNS.log("Device set to TNC operating mode")
4492                      sleep(1.0)
4493  
4494                      graceful_exit()
4495              else:
4496                  RNS.log("This device contains a valid firmware, but EEPROM is invalid.")
4497                  RNS.log("Probably the device has not been initialised, or the EEPROM has been erased.")
4498                  RNS.log("Please correctly initialise the device and try again!")
4499  
4500          else:
4501              print("")
4502              parser.print_help()
4503              print("")
4504              graceful_exit()
4505  
4506  
4507      except KeyboardInterrupt:
4508          print("")
4509          graceful_exit()
4510          
4511      graceful_exit()
4512  
4513  def extract_recovery_esptool():
4514      if not os.path.isfile(RT_PATH):
4515          try:
4516              RNS.log("Extracting recovery ESP-Tool...")
4517              import base64, gzip
4518              recovery_esptool = "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"
4519              out = open(RT_PATH, "wb")
4520              out.write("#!/usr/bin/env python3\n\n".encode("utf-8")+gzip.decompress(base64.b64decode(recovery_esptool)))
4521              out.close()
4522          except Exception as e:
4523              RNS.log("Error: Could not extract recovery ESP-Tool. The contained exception was:")
4524              RNS.log(str(e))
4525              graceful_exit(181)
4526  
4527  if __name__ == "__main__":
4528      main()