From 77de3f65684bb20688e8041cd03bbe5c225f6e0c Mon Sep 17 00:00:00 2001 From: Mark Qvist Date: Mon, 10 Jan 2022 02:11:19 +0100 Subject: [PATCH] Initial multiplatform support --- .gitignore | 4 + rnodeconf/rnodeconf.py | 599 +++++++++++++++++++++++++++++++++++------ setup.py | 2 +- 3 files changed, 515 insertions(+), 90 deletions(-) diff --git a/.gitignore b/.gitignore index 37b32e7..378cd31 100644 --- a/.gitignore +++ b/.gitignore @@ -4,6 +4,10 @@ firmware/*.key* firmware/*.counter firmware/device_db update/*.hex +update/*.zip +update/*.bin +update/*_tbeam* +update/esptool.py eeprom/*.eeprom bootstrap bootloader.sh diff --git a/rnodeconf/rnodeconf.py b/rnodeconf/rnodeconf.py index b75444a..ad46f98 100755 --- a/rnodeconf/rnodeconf.py +++ b/rnodeconf/rnodeconf.py @@ -2,7 +2,7 @@ # MIT License # -# Copyright (c) 2018 Mark Qvist - unsigned.io +# Copyright (c) 2018 Mark Qvist - unsigned.io/rnode # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal @@ -38,10 +38,12 @@ program_version = "1.1.0" rnode = None rnode_serial = None +rnode_port = None rnode_baudrate = 115200 -known_keys = [["unsigned.io", "30819f300d06092a864886f70d010101050003818d0030818902818100e5d46084e445595376bf7efd9c6ccf19d39abbc59afdb763207e4ff68b8d00ebffb63847aa2fe6dd10783d3ea63b55ac66f71ad885c20e223709f0d51ed5c6c0d0b093be9e1d165bb8a483a548b67a3f7a1e4580f50e75b306593fa6067ae259d3e297717bd7ff8c8f5b07f2bed89929a9a0321026cf3699524db98e2d18fb2d020300ff39"]] -ranges = { 0xA4: [410000000, 525000000, 14], 0xA9: [820000000, 1020000000, 17] } -firmware_update_url = "https://github.com/markqvist/RNode_Firmware/raw/master/Precompiled/rnode_firmware_latest.hex" +known_keys = [["unsigned.io", "30819f300d06092a864886f70d010101050003818d0030818902818100bf831ebd99f43b477caf1a094bec829389da40653e8f1f83fc14bf1b98a3e1cc70e759c213a43f71e5a47eb56a9ca487f241335b3e6ff7cdde0ee0a1c75c698574aeba0485726b6a9dfc046b4188e3520271ee8555a8f405cf21f81f2575771d0b0887adea5dd53c1f594f72c66b5f14904ffc2e72206a6698a490d51ba1105b0203010001"], ["unsigned.io", "30819f300d06092a864886f70d010101050003818d0030818902818100e5d46084e445595376bf7efd9c6ccf19d39abbc59afdb763207e4ff68b8d00ebffb63847aa2fe6dd10783d3ea63b55ac66f71ad885c20e223709f0d51ed5c6c0d0b093be9e1d165bb8a483a548b67a3f7a1e4580f50e75b306593fa6067ae259d3e297717bd7ff8c8f5b07f2bed89929a9a0321026cf3699524db98e2d18fb2d020300ff39"]] +firmware_update_url = "https://github.com/markqvist/RNode_Firmware/raw/master/Precompiled/" +fw_filename = None +mapped_model = None class RNS(): @staticmethod @@ -93,6 +95,8 @@ class KISS(): CMD_STAT_SNR = 0x24 CMD_BLINK = 0x30 CMD_RANDOM = 0x40 + CMD_PLATFORM = 0x48 + CMD_MCU = 0x49 CMD_FW_VERSION = 0x50 CMD_ROM_READ = 0x51 CMD_ROM_WRITE = 0x52 @@ -120,10 +124,24 @@ class KISS(): return data class ROM(): + PLATFORM_AVR = 0x90 + PLATFORM_ESP32 = 0x80 + + MCU_1284P = 0x91 + MCU_2560 = 0x92 + MCU_ESP32 = 0x81 + PRODUCT_RNODE = 0x03 MODEL_A4 = 0xA4 MODEL_A9 = 0xA9 + PRODUCT_TBEAM = 0xE0 + MODEL_E4 = 0xE4 + MODEL_E9 = 0xE9 + + PRODUCT_HMBRW = 0xF0 + MODEL_FF = 0xFF + ADDR_PRODUCT = 0x00 ADDR_MODEL = 0x01 ADDR_HW_REV = 0x02 @@ -142,6 +160,34 @@ class ROM(): INFO_LOCK_BYTE = 0x73 CONF_OK_BYTE = 0x73 +mapped_product = ROM.PRODUCT_RNODE +products = { + ROM.PRODUCT_RNODE: "RNode", + ROM.PRODUCT_HMBRW: "Hombrew RNode", + ROM.PRODUCT_TBEAM: "LilyGO T-Beam", +} + +platforms = { + ROM.PLATFORM_AVR: "AVR", + ROM.PLATFORM_ESP32:"ESP32", +} + +mcus = { + ROM.MCU_1284P: "ATmega1284P", + ROM.MCU_2560:"ATmega2560", + ROM.MCU_ESP32:"Espressif Systems ESP32", +} + +models = { + 0xA4: [410000000, 525000000, 14, "410 - 525 MHz", "rnode_firmware_latest.hex"], + 0xA9: [820000000, 1020000000, 17, "820 - 1020 MHz", "rnode_firmware_latest.hex"], + 0xF4: [410000000, 525000000, 14, "410 - 525 MHz", "rnode_firmware_latest_m2560.hex"], + 0xF9: [820000000, 1020000000, 17, "820 - 1020 MHz", "rnode_firmware_latest_m2560.hex"], + 0xE4: [420000000, 520000000, 14, "420 - 520 MHz", "rnode_firmware_latest_tbeam.zip"], + 0xE9: [850000000, 950000000, 17, "850 - 950 MHz", "rnode_firmware_latest_tbeam.zip"], + 0xFF: [100000000, 1100000000, 14, "(Band capabilities unknown)"], +} + class RNode(): def __init__(self, serial_instance): self.serial = serial_instance @@ -162,6 +208,8 @@ class RNode(): self.detected = None + self.platform = None + self.mcu = None self.eeprom = None self.major_version = None self.minor_version = None @@ -190,6 +238,9 @@ class RNode(): self.conf_frequency = None self.conf_bandwidth = None + def disconnect(self): + self.serial.close() + def readLoop(self): try: in_frame = False @@ -287,6 +338,12 @@ class RNode(): self.minor_version = command_buffer[1] self.updateVersion() + elif (command == KISS.CMD_PLATFORM): + self.platform = byte + + elif (command == KISS.CMD_MCU): + self.mcu = byte + elif (command == KISS.CMD_TXPOWER): self.r_txpower = byte RNS.log("Radio reporting TX power is "+str(self.r_txpower)+" dBm") @@ -376,10 +433,10 @@ class RNode(): self.version = str(self.major_version)+"."+minstr def detect(self): - kiss_command = bytes([KISS.FEND, KISS.CMD_DETECT, KISS.DETECT_REQ, KISS.FEND, KISS.CMD_FW_VERSION, 0x00, KISS.FEND]) + 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]) written = self.serial.write(kiss_command) if written != len(kiss_command): - raise IOError("An IO error occurred while configuring spreading factor for "+self(str)) + raise IOError("An IO error occurred while detecting hardware for "+self(str)) def initRadio(self): self.setFrequency() @@ -464,7 +521,7 @@ class RNode(): written = self.serial.write(kiss_command) if written != len(kiss_command): raise IOError("An IO error occurred while restarting device") - sleep(1); + sleep(2); def write_eeprom(self, addr, byte): write_payload = b"" + bytes([addr, byte]) @@ -476,6 +533,7 @@ class RNode(): def download_eeprom(self): + self.eeprom = None kiss_command = bytes([KISS.FEND, KISS.CMD_ROM_READ, 0x00, KISS.FEND]) written = self.serial.write(kiss_command) if written != len(kiss_command): @@ -504,14 +562,14 @@ class RNode(): self.checksum = b"" - self.min_freq = ranges[self.model][0] - self.max_freq = ranges[self.model][1] - self.max_output = ranges[self.model][2] + self.min_freq = models[self.model][0] + self.max_freq = models[self.model][1] + self.max_output = models[self.model][2] try: - self.min_freq = ranges[self.model][0] - self.max_freq = ranges[self.model][1] - self.max_output = ranges[self.model][2] + self.min_freq = models[self.model][0] + self.max_freq = models[self.model][1] + self.max_output = models[self.model][2] except Exception as e: RNS.log("Exception") RNS.log(str(e)) @@ -633,7 +691,25 @@ class RNode(): else: raise IOError("Got invalid response while detecting device") +def rnode_open_serial(port): + import serial + return serial.Serial( + port = port, + baudrate = rnode_baudrate, + bytesize = 8, + parity = serial.PARITY_NONE, + stopbits = 1, + xonxoff = False, + rtscts = False, + timeout = 0, + inter_byte_timeout = None, + write_timeout = None, + dsrdtr = False + ) + def main(): + global mapped_product, mapped_model, fw_filename + try: if not util.find_spec("serial"): raise ImportError("Serial module could not be found") @@ -655,17 +731,20 @@ def main(): exit() import serial + from serial.tools import list_ports try: 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 flash and update the firmware, and manage device EEPROM.") parser.add_argument("-i", "--info", action="store_true", help="Show device info") + parser.add_argument("-u", "--update", action="store_true", help="Update firmware to the latest version") + parser.add_argument("--nocheck", action="store_true", help="Don't check for firmware updates online, use existing local files if possible") + parser.add_argument("-a", "--autoinstall", action="store_true", help="Automatic installation on various supported devices") parser.add_argument("-T", "--tnc", action="store_true", help="Switch device to TNC mode") parser.add_argument("-N", "--normal", action="store_true", help="Switch device to normal mode") parser.add_argument("-b", "--backup", action="store_true", help="Backup EEPROM to file") parser.add_argument("-d", "--dump", action="store_true", help="Dump EEPROM to console") parser.add_argument("-f", "--flash", action="store_true", help="Flash firmware and bootstrap EEPROM") parser.add_argument("-r", "--rom", action="store_true", help="Bootstrap EEPROM without flashing firmware") - parser.add_argument("-u", "--update", action="store_true", help="Update firmware") parser.add_argument("-k", "--key", action="store_true", help="Generate a new signing key and exit") parser.add_argument("-p", "--public", action="store_true", help="Display public part of signing key") parser.add_argument("--freq", action="store", metavar="Hz", type=int, default=None, help="Frequency in Hz for TNC mode") @@ -673,9 +752,12 @@ def main(): parser.add_argument("--txp", action="store", metavar="dBm", type=int, default=None, help="TX power in dBm for TNC mode") parser.add_argument("--sf", action="store", metavar="factor", type=int, default=None, help="Spreading factor for TNC mode (7 - 12)") parser.add_argument("--cr", action="store", metavar="rate", type=int, default=None, help="Coding rate for TNC mode (5 - 8)") - parser.add_argument("--model", action="store", metavar="model", type=str, default=None, help="Model code for EEPROM bootstrap") - parser.add_argument("--hwrev", action="store", metavar="revision", type=int, default=None, help="Hardware revision EEPROM bootstrap") - parser.add_argument("--nocheck", action="store_true", help="Don't check for firmware updates online") + + parser.add_argument("--platform", action="store", metavar="platform", type=str, default=None, help="Platform specification for device bootstrap") + parser.add_argument("--product", action="store", metavar="product", type=str, default=None, help="Product specification for device bootstrap") + parser.add_argument("--model", action="store", metavar="model", type=str, default=None, help="Model code for device bootstrap") + parser.add_argument("--hwrev", action="store", metavar="revision", type=int, default=None, help="Hardware revision for device bootstrap") + parser.add_argument("--eepromwipe", action="store_true", help="Unlock and wipe EEPROM") parser.add_argument("--version", action="store_true", help="Print version and exit") @@ -686,7 +768,7 @@ def main(): print("rnodeconf "+program_version) exit(0) - if args.public or args.key or args.flash or args.rom: + if args.public or args.key or args.flash or args.rom or args.autoinstall: from cryptography.hazmat.primitives import hashes from cryptography.hazmat.backends import default_backend from cryptography.hazmat.primitives import serialization @@ -695,6 +777,202 @@ def main(): from cryptography.hazmat.primitives.asymmetric import rsa from cryptography.hazmat.primitives.asymmetric import padding + if args.autoinstall: + print("\nHello!\n\nThis guide will help you install the RNode firmware on supported") + print("and homebrew devices. Please connect the device you wish to set\nup now. Hit enter when it is connected.") + input() + + ports = list_ports.comports() + portlist = [] + for port in ports: + portlist.insert(0, port) + + pi = 1 + print("Detected serial ports:") + for port in portlist: + print(" ["+str(pi)+"] "+str(port.device)+" ("+str(port.product)+", "+str(port.serial_number)+")") + pi += 1 + + print("\nWhat serial port is your device connected to? ", end="") + selected_port = None + try: + c_port = int(input()) + if c_port < 1 or c_port > len(ports): + raise ValueError() + + selected_port = portlist[c_port-1] + except Exception as e: + print("That port does not exist, exiting now.") + exit() + + if selected_port == None: + print("Could not select port, exiting now.") + exit() + + port_path = selected_port.device + port_product = selected_port.product + port_serialno = selected_port.serial_number + + print("\nOk, using device on "+str(port_path)+" ("+str(port_product)+", "+str(port_serialno)+")") + + print("\nProbing device...") + + try: + rnode_serial = rnode_open_serial(port_path) + except Exception as e: + RNS.log("Could not open the specified serial port. The contained exception was:") + RNS.log(str(e)) + exit() + + rnode = RNode(rnode_serial) + thread = threading.Thread(target=rnode.readLoop) + thread.setDaemon(True) + thread.start() + + try: + rnode.device_probe() + except Exception as e: + RNS.log("No answer from device") + + if rnode.detected: + RNS.log("Trying to read EEPROM...") + rnode.download_eeprom() + + if rnode.provisioned: + print("\nThis device is already installed and provisioned. No further action will") + print("be taken. If you wish to completely reinstall this device, you must first") + print("wipe the current EEPROM and delete the firmware. See the help for more info.\n\nExiting now.") + exit() + + if rnode.detected: + print("\nThe device seems to have an RNode firmware installed, but it was not") + print("provisioned correctly, or it is corrupt. We are going to reinstall the") + print("correct firmware and provision it.") + else: + print("\nIt looks like this is a fresh device with no RNode firmware.") + + print("What kind of device is this?\n") + print("[1] Original RNode") + print("[2] Homebrew RNode") + print("[3] LilyGO T-Beam") + print("\n? ", end="") + + selected_product = None + try: + c_dev = int(input()) + if c_dev < 1 or c_dev > 3: + raise ValueError() + elif c_dev == 1: + selected_product = ROM.PRODUCT_RNODE + elif c_dev == 2: + selected_product = ROM.PRODUCT_HMBRW + elif c_dev == 3: + selected_product = ROM.PRODUCT_TBEAM + except Exception as e: + print("That device type does not exist, exiting now.") + exit() + + selected_platform = None + selected_model = None + selected_mcu = None + + if selected_product == ROM.PRODUCT_HMBRW: + selected_model = ROM.MODEL_FF + print("\nWhat kind of microcontroller is your board based on?\n") + print("[1] AVR ATmega1284P") + print("[2] AVR ATmega2560") + print("[3] Espressif Systems ESP32") + print("\n? ", end="") + try: + c_mcu = int(input()) + if c_mcu < 1 or c_mcu > 3: + raise ValueError() + elif c_mcu == 1: + selected_mcu = ROM.MCU_1284P + selected_platform = ROM.PLATFORM_AVR + elif c_mcu == 2: + selected_mcu = ROM.MCU_2560 + selected_platform = ROM.PLATFORM_AVR + elif c_mcu == 3: + selected_mcu = ROM.MCU_ESP32 + selected_platform = ROM.PLATFORM_ESP32 + except Exception as e: + print("That MCU type does not exist, exiting now.") + exit() + + elif selected_product == ROM.PRODUCT_RNODE: + selected_mcu = ROM.MCU_1284P + print("\nWhat model is this RNode?\n") + print("[1] RNode 410 - 525 MHz") + print("[2] RNode 820 - 1020 MHz") + print("\n? ", end="") + try: + c_model = int(input()) + if c_model < 1 or c_model > 2: + raise ValueError() + elif c_model == 1: + selected_model = ROM.MODEL_A4 + selected_platform = ROM.PLATFORM_AVR + elif c_model == 2: + selected_model = ROM.MODEL_A9 + selected_platform = ROM.PLATFORM_AVR + except Exception as e: + print("That model does not exist, exiting now.") + exit() + + elif selected_product == ROM.PRODUCT_TBEAM: + selected_mcu = ROM.MCU_ESP32 + print("\nWhat band is this T-Beam for?\n") + print("[1] 433 MHz") + print("[2] 868 MHz") + print("[3] 915 MHz") + print("[4] 923 MHz") + print("\n? ", end="") + try: + c_model = int(input()) + if c_model < 1 or c_model > 4: + raise ValueError() + elif c_model == 1: + selected_model = ROM.MODEL_E4 + selected_platform = ROM.PLATFORM_ESP32 + elif c_model > 1: + selected_model = ROM.MODEL_E9 + selected_platform = ROM.PLATFORM_ESP32 + except Exception as e: + print("That band does not exist, exiting now.") + exit() + + print("\nOk, that should be all the information we need. Please confirm the following") + print("summary before proceeding. In the next step, the device will be flashed and") + print("provisioned, so make that you are satisfied with your choices.\n") + + fw_filename = models[selected_model][4] + print("Device type : "+str(products[selected_product])+" "+str(models[selected_model][3])) + print("Platform : "+str(platforms[selected_platform])) + print("Device MCU : "+str(mcus[selected_mcu])) + print("Firmware file : "+str(fw_filename)) + + print("\nIs the above correct? [y/N] ", end="") + try: + c_ok = input().lower() + if c_ok != "y": + raise ValueError() + except Exception as e: + print("OK, aborting now.") + exit() + + args.key = True + args.port = selected_port + args.platform = selected_platform + args.hwrev = 0 + mapped_model = selected_model + mapped_product = selected_product + args.update = False + args.flash = True + + print("\nDone") + exit() + if args.public: private_bytes = None try: @@ -758,43 +1036,79 @@ def main(): exit() + def get_flasher_call(platform, fw_filename): + from shutil import which + if platform == "unzip": + flasher = "unzip" + if which(flasher) is not None: + return [flasher, "-o", "./update/"+fw_filename, "-d", "./update/"] + else: + RNS.log("") + RNS.log("You do not currently have the \""+flasher+"\" program installed on your system.") + RNS.log("Unfortunately, that means we can't proceed, since it is needed to flash your") + RNS.log("board. You can install it via your package manager, for example:") + RNS.log("") + RNS.log(" sudo apt install "+flasher) + RNS.log("") + RNS.log("Please install \""+flasher+"\" and try again.") + exit() + elif platform == ROM.PLATFORM_AVR: + flasher = "avrdude" + if which(flasher) is not None: + return [flasher, "-P", args.port, "-p", "m1284p", "-c", "arduino", "-b", "115200", "-U", "flash:w:update/"+fw_filename] + else: + RNS.log("") + RNS.log("You do not currently have the \""+flasher+"\" program installed on your system.") + RNS.log("Unfortunately, that means we can't proceed, since it is needed to flash your") + RNS.log("board. You can install it via your package manager, for example:") + RNS.log("") + RNS.log(" sudo apt install avrdude") + RNS.log("") + RNS.log("Please install \""+flasher+"\" and try again.") + exit() + elif platform == ROM.PLATFORM_ESP32: + flasher = "./update/esptool.py" + if which(flasher) is not None: + # esptool.py --chip esp32 --port /dev/ttyUSB0 --baud 921600 --before default_reset --after hard_reset write_flash -z --flash_mode dio --flash_freq 80m --flash_size 4MB + # 0xe000 /home/markqvist/.arduino15/packages/esp32/hardware/esp32/2.0.2/tools/partitions/boot_app0.bin + # 0x1000 /tmp/arduino-sketch-0E260F46C421A84A7CBAD48E859C8E64/RNode_Firmware.ino.bootloader.bin + # 0x10000 /tmp/arduino-sketch-0E260F46C421A84A7CBAD48E859C8E64/RNode_Firmware.ino.bin + # 0x8000 /tmp/arduino-sketch-0E260F46C421A84A7CBAD48E859C8E64/RNode_Firmware.ino.partitions.bin + # + return [ + flasher, + "--chip", "esp32", + "--port", args.port, + "--baud", "921600", + "--before", "default_reset", + "--after", "hard_reset", + "write_flash", "-z", + "--flash_mode", "dio", + "--flash_freq", "80m", + "--flash_size", "4MB", + "0xe000", "./update/rnode_firmware_latest_tbeam.boot_app0", + "0x1000", "./update/rnode_firmware_latest_tbeam.bootloader", + "0x10000", "./update/rnode_firmware_latest_tbeam.bin", + "0x8000", "./update/rnode_firmware_latest_tbeam.partitions", + ] + else: + RNS.log("") + RNS.log("You do not currently have the \""+flasher+"\" program installed on your system.") + RNS.log("Unfortunately, that means we can't proceed, since it is needed to flash your") + RNS.log("board. You can install it via your package manager, for example:") + RNS.log("") + RNS.log(" sudo apt install esptool") + RNS.log("") + RNS.log("Please install \""+flasher+"\" and try again.") + exit() + if args.port: - if args.update: - if not args.nocheck: - try: - RNS.log("Downloading latest firmware from GitHub...") - os.makedirs("./update", exist_ok=True) - urlretrieve(firmware_update_url, "update/rnode_update.hex") - RNS.log("Firmware download completed") - if os.path.isfile("./update/rnode_update.hex"): - try: - RNS.log("Updating RNode firmware for device on "+args.port) - from subprocess import call - flash_status = call(["avrdude", "-P", args.port, "-p", "m1284p", "-c", "arduino", "-b", "115200", "-U", "flash:w:update/rnode_update.hex"]) - if flash_status == 0: - RNS.log("Firmware updated") - args.info = True - else: - exit() - - except Exception as e: - RNS.log("Error while updating firmware") - RNS.log(str(e)) - else: - RNS.log("Firmware update file not found") - exit() - - except Exception as e: - RNS.log("Could not download firmware update") - RNS.log("The contained exception was: "+str(e)) - exit() - if args.flash: - if os.path.isfile("./firmware/rnode_firmware.hex"): + if os.path.isfile("./firmware/"+fw_filename): try: RNS.log("Flashing RNode firmware to device on "+args.port) from subprocess import call - flash_status = call(["avrdude", "-P", args.port, "-p", "m1284p", "-c", "arduino", "-b", "115200", "-U", "flash:w:firmware/rnode_firmware.hex"]) + flash_status = call(get_flasher_call()) if flash_status == 0: RNS.log("Done flashing") args.rom = True @@ -810,19 +1124,8 @@ def main(): RNS.log("Opening serial port "+args.port+"...") try: - rnode_serial = serial.Serial( - port = args.port, - baudrate = rnode_baudrate, - bytesize = 8, - parity = serial.PARITY_NONE, - stopbits = 1, - xonxoff = False, - rtscts = False, - timeout = 0, - inter_byte_timeout = None, - write_timeout = None, - dsrdtr = False - ) + rnode_port = args.port + rnode_serial = rnode_open_serial(rnode_port) except Exception as e: RNS.log("Could not open the specified serial port. The contained exception was:") RNS.log(str(e)) @@ -840,12 +1143,102 @@ def main(): print(e) exit() - RNS.log("Reading EEPROM...") - rnode.download_eeprom() + if rnode.detected: + if rnode.platform == None or rnode.mcu == None: + rnode.platform = ROM.PLATFORM_AVR + rnode.mcu = ROM.MCU_1284P + if args.eepromwipe: RNS.log("WARNING: EEPROM is being wiped! Power down device NOW if you do not want this!") rnode.wipe_eeprom() + exit() + + RNS.log("Reading EEPROM...") + rnode.download_eeprom() + + if rnode.provisioned: + fw_filename = models[rnode.model][4] + + if args.update: + rnode.disconnect() + from subprocess import call + + if not args.nocheck: + try: + RNS.log("Downloading latest firmware from GitHub...") + os.makedirs("./update", exist_ok=True) + urlretrieve(firmware_update_url+fw_filename, "update/"+fw_filename) + RNS.log("Firmware download completed") + if fw_filename.endswith(".zip"): + RNS.log("Extracting firmware...") + unzip_status = call(get_flasher_call("unzip", fw_filename)) + if unzip_status == 0: + RNS.log("Firmware extracted") + else: + RNS.log("Could not extract firmware from downloaded zip file") + exit() + + except Exception as e: + RNS.log("Could not download firmware update") + RNS.log("The contained exception was: "+str(e)) + exit() + else: + RNS.log("Skipping online check, using local firmware file: "+"./update/"+fw_filename) + + if os.path.isfile("./update/"+fw_filename): + try: + args.info = False + RNS.log("Updating RNode firmware for device on "+args.port) + flash_status = call(get_flasher_call(rnode.platform, fw_filename)) + if flash_status == 0: + RNS.log("Flashing new firmware completed") + RNS.log("Opening serial port "+args.port+"...") + try: + rnode_port = args.port + rnode_serial = rnode_open_serial(rnode_port) + except Exception as e: + RNS.log("Could not open the specified serial port. The contained exception was:") + RNS.log(str(e)) + exit() + + rnode = RNode(rnode_serial) + thread = threading.Thread(target=rnode.readLoop) + thread.setDaemon(True) + thread.start() + + try: + rnode.device_probe() + except Exception as e: + RNS.log("Serial port opened, but RNode did not respond. Is a valid firmware installed?") + print(e) + exit() + + if rnode.detected: + if rnode.platform == None or rnode.mcu == None: + rnode.platform = ROM.PLATFORM_AVR + rnode.mcu = ROM.MCU_1284P + + RNS.log("Reading EEPROM...") + rnode.download_eeprom() + + if rnode.provisioned: + fw_filename = models[rnode.model][4] + args.info = True + + if args.info: + RNS.log("") + RNS.log("Firmware update completed successfully") + else: + RNS.log("An error occurred while flashing the new firmware, exiting now.") + exit() + + except Exception as e: + RNS.log("Error while updating firmware") + RNS.log(str(e)) + else: + RNS.log("Firmware update file not found") + exit() if args.dump: RNS.log("EEPROM contents:") @@ -879,14 +1272,14 @@ def main(): RNS.log("") RNS.log("Device info:") - RNS.log("\tFirmware version:\t"+rnode.version) - RNS.log("\tProduct code:\t\t"+bytes([rnode.product]).hex()) - RNS.log("\tModel code:\t\t"+bytes([rnode.model]).hex()) - RNS.log("\tHardware revision:\t"+bytes([rnode.hw_rev]).hex()) - RNS.log("\tSerial number:\t\t"+RNS.hexrep(rnode.serialno)) - RNS.log("\tFrequency range:\t"+str(rnode.min_freq/1e6)+" MHz - "+str(rnode.max_freq/1e6)+" MHz") - RNS.log("\tMax TX power:\t\t"+str(rnode.max_output)+" dBm") - RNS.log("\tManufactured:\t\t"+timestring) + RNS.log("\tProduct : "+products[rnode.product]+" "+models[rnode.model][3]+" ("+bytes([rnode.product]).hex()+":"+bytes([rnode.model]).hex()+")") + RNS.log("\tDevice signature : "+sigstring) + RNS.log("\tFirmware version : "+rnode.version) + RNS.log("\tHardware revision : "+str(int(rnode.hw_rev))) + RNS.log("\tSerial number : "+RNS.hexrep(rnode.serialno)) + RNS.log("\tFrequency range : "+str(rnode.min_freq/1e6)+" MHz - "+str(rnode.max_freq/1e6)+" MHz") + RNS.log("\tMax TX power : "+str(rnode.max_output)+" dBm") + RNS.log("\tManufactured : "+timestring) if rnode.configured: rnode.bandwidth = rnode.conf_bandwidth @@ -897,18 +1290,16 @@ def main(): rnode.r_cr = rnode.conf_cr rnode.updateBitrate() txp_mw = round(pow(10, (rnode.conf_txpower/10)), 3) - RNS.log("\tDevice signature:\t"+sigstring) RNS.log(""); - RNS.log("\tDevice mode:\t\tTNC") - RNS.log("\t Frequency:\t\t"+str((rnode.conf_frequency/1000000.0))+" MHz") - RNS.log("\t Bandwidth:\t\t"+str(rnode.conf_bandwidth/1000.0)+" KHz") - RNS.log("\t TX power:\t\t"+str(rnode.conf_txpower)+" dBm ("+str(txp_mw)+" mW)") - RNS.log("\t Spreading factor:\t"+str(rnode.conf_sf)) - RNS.log("\t Coding rate:\t\t"+str(rnode.conf_cr)) - RNS.log("\t On-air bitrate:\t"+str(rnode.bitrate_kbps)+" kbps") + RNS.log("\tDevice mode : TNC") + RNS.log("\t Frequency : "+str((rnode.conf_frequency/1000000.0))+" MHz") + RNS.log("\t Bandwidth : "+str(rnode.conf_bandwidth/1000.0)+" KHz") + RNS.log("\t TX power : "+str(rnode.conf_txpower)+" dBm ("+str(txp_mw)+" mW)") + RNS.log("\t Spreading factor : "+str(rnode.conf_sf)) + RNS.log("\t Coding rate : "+str(rnode.conf_cr)) + RNS.log("\t On-air bitrate : "+str(rnode.bitrate_kbps)+" kbps") else: - RNS.log("\tDevice mode:\t\tNormal (host-controlled)") - RNS.log("\tDevice signature:\t"+sigstring) + RNS.log("\tDevice mode : Normal (host-controlled)") print("") exit() @@ -942,10 +1333,33 @@ def main(): serialno = counter+1 model = None hwrev = None - if args.model == "a4": - model = ROM.MODEL_A4 - if args.model == "a9": - model = ROM.MODEL_A9 + if args.product != None: + if args.product == "03": + mapped_product = ROM.PRODUCT_RNODE + if args.product == "f0": + mapped_product = ROM.PRODUCT_HMBRW + if args.product == "e0": + mapped_product = ROM.PRODUCT_TBEAM + + if mapped_model != None: + model = mapped_model + else: + if args.model == "a4": + model = ROM.MODEL_A4 + elif args.model == "a9": + model = ROM.MODEL_A9 + elif args.model == "f4": + model = ROM.MODEL_F4 + elif args.model == "f9": + model = ROM.MODEL_F9 + elif args.model == "e4": + model = ROM.MODEL_E4 + elif args.model == "e9": + model = ROM.MODEL_E9 + elif args.model == "ff": + model = ROM.MODEL_FF + + if args.hwrev != None and (args.hwrev > 0 and args.hwrev < 256): hwrev = chr(args.hwrev) @@ -961,7 +1375,7 @@ def main(): file.write(str(serialno)) file.close() - info_chunk = b"" + bytes([ROM.PRODUCT_RNODE, model, ord(hwrev)]) + info_chunk = b"" + bytes([mapped_product, model, ord(hwrev)]) info_chunk += serial_bytes info_chunk += time_bytes digest = hashes.Hash(hashes.MD5(), backend=default_backend()) @@ -1003,8 +1417,9 @@ def main(): RNS.log("Bootstrapping device EEPROM...") + rnode.hard_reset() - rnode.write_eeprom(ROM.ADDR_PRODUCT, ROM.PRODUCT_RNODE) + rnode.write_eeprom(ROM.ADDR_PRODUCT, mapped_product) time.sleep(0.006) rnode.write_eeprom(ROM.ADDR_MODEL, model) time.sleep(0.006) @@ -1038,9 +1453,15 @@ def main(): rnode.write_eeprom(ROM.ADDR_INFO_LOCK, ROM.INFO_LOCK_BYTE) RNS.log("EEPROM written! Validating...") + + if rnode.platform == ROM.PLATFORM_ESP32: + RNS.log("Waiting for ESP32 reset...") + time.sleep(5) + rnode.download_eeprom() if rnode.provisioned: RNS.log("EEPROM Bootstrapping successful!") + rnode.hard_reset() try: os.makedirs("./firmware/device_db/", exist_ok=True) file = open("./firmware/device_db/"+serial_bytes.hex(), "wb") diff --git a/setup.py b/setup.py index f4c8ec6..0a89cc9 100644 --- a/setup.py +++ b/setup.py @@ -5,7 +5,7 @@ with open("README.md", "r") as fh: setuptools.setup( name="rnodeconf", - version="1.0.2", + version="1.1.0", author="Mark Qvist", author_email="mark@unsigned.io", description="Configuration Utility for RNode hardware",