diff --git a/index.html b/index.html index ee7d984..ad2acdd 100644 --- a/index.html +++ b/index.html @@ -6,7 +6,7 @@ - RNode Flasher + RNode Flasher (DIY) @@ -22,121 +22,106 @@ - +
-
+
-
RNode Flasher
-
Developed by Liam Cottle
+
RNode Flasher for DIY devices
+
Developed by Liam Cottle
-
+
-
+
1. Select your device
-
-
Product
- +
-
-
Model
- +
-
-
-
- -
-
- Can't find your device? Open an issue on - GitHub -
-
-
-
+
-
+
2. Select firmware to flash (.zip)
- +
-
- Flashing: {{flashingProgress}}% - Flashing: please wait... -
+ Flashing: {{flashingProgress}}% + Flashing: please wait... +
-
+
-
- Official Firmware - - CE Firmware - - Transport Node Firmware +
+ Hardware Information + : {{ FirmwareRepo }}
-
+
Common error messages
-
• Hardware Failure: You need to provision the eeprom in step 3.
+
• Missing Config: You need to provision the EEPROM in step 3.
• Firmware Corrupt: You need to set the firmware hash in step 4.
-
+
-
+
3. Provision EEPROM (sets device info, checksum and blank signature)
-
@@ -146,20 +131,20 @@
-
Provisioning: please wait...
+
Provisioning: please wait...
-
+
-
+
4. Set Firmware Hash (uses hash from board, will fix later)
-
@@ -169,19 +154,19 @@
-
Setting Firmware Hash: please wait...
+
Setting Firmware Hash: please wait...
-
+
-
+
5. Done
-
+
• If you made it this far, and all previous steps were successful, your RNode should be ready to use.
• To use RNode with MeshChat, you will need to add an RNodeInterface in the Interfaces → Add Interface page.
• To use RNode with Sideband, you will need to configure it in Hardware → RNode and enable Connectivity → Connect via RNode.
@@ -190,34 +175,32 @@
-
+
-
+
Advanced Tools
- - - - -
-
EEPROM dumps are shown in dev tools console.
-
@@ -227,13 +210,13 @@
-
+
-
+
Configure Bluetooth (optional)
-
+
• Bluetooth is not supported on all devices.
• Some devices use Bluetooth Classic, and some use BLE (Bluetooth Low Energy)
• Put the RNode into Bluetooth Pairing mode, then connect to it from Android Bluetooth settings.
@@ -245,13 +228,13 @@
- - -
@@ -259,13 +242,13 @@
-
+
-
+
Configure TNC Mode (optional)
-
+
• TNC mode allows an RNode to be used as a KISS compatible TNC over the Serial Port.
• This mode makes it usable with amateur radio software that can talk to a KISS TNC over a serial port.
• You must leave TNC mode disabled when using RNode with apps like MeshChat or Sideband.
@@ -274,36 +257,36 @@
-
Frequency (Hz)
- +
Frequency (Hz)
+
-
Bandwidth
-
-
Tx Power (dBm)
- +
Tx Power (dBm)
+
-
Spreading Factor
-
-
Coding Rate
-
- -
@@ -312,46 +295,178 @@
-
- -
+
Configure Display (optional)
-
-
Rotation
- - - -
+
+
Intensity
+ +
+ +
+
+ +
+
Timeout (sec)
+ +
+ +
+
+
-
Reconditioning
- -
+
+
+
+ +
+ Configure WiFi (optional)
-
-
Setting display rotation requires firmware v1.80+
+
+
• WiFi configuration is supported only on ESP32 devices.
+
• Simultaneous operation of Bluetooth and WiFi is unsupported.
+
• For static IP configuration both the address and the netmask need to be set.
+
• Unset both IP and Netmask to use DHCP.
+
+
Mode
+ + + +
+ +
+
+ +
+
Channel
+ +
+ +
+
+ +
+
SSID
+ +
+ + +
+
+ +
+
PSK (Password)
+ + +
+ +
+ + + +
+ +
+
+ +
+
IP Address
+ +
+ + +
+
+ +
+
Netmask
+ +
+ + +
+
@@ -390,78 +505,63 @@ selectedModel: null, products: [ { - name: "Heltec LoRa32 v2", - id: ROM.PRODUCT_H32_V2, + name: "Aethernode", + id: ROM.PRODUCT_HMBRW, platform: ROM.PLATFORM_ESP32, models: [ { - id: ROM.MODEL_C4, + id: ROM.MODEL_FE, name: "433 MHz", }, - { - id: ROM.MODEL_C9, - name: "868 MHz / 915 MHz / 923 MHz", - }, ], - firmware_filename: "rnode_firmware_heltec32v2.zip", + firmware_filename: "rnode_firmware_aethernode.zip", + firmware_repo: "https://github.com/ahedproductions/aethernode", flash_config: { - flash_size: "8MB", + flash_size: "4MB", flash_files: { - "0xe000": "rnode_firmware_heltec32v2.boot_app0", - "0x1000": "rnode_firmware_heltec32v2.bootloader", - "0x10000": "rnode_firmware_heltec32v2.bin", + "0xe000": "rnode_firmware_aethernode.boot_app0", + "0x1000": "rnode_firmware_aethernode.bootloader", + "0x10000": "rnode_firmware_aethernode.bin", "0x210000": "console_image.bin", - "0x8000": "rnode_firmware_heltec32v2.partitions", + "0x8000": "rnode_firmware_aethernode.partitions", }, }, }, { - name: "Heltec LoRa32 v3", - id: ROM.PRODUCT_H32_V3, + name: "MeshAdventurer", + id: ROM.PRODUCT_HMBRW, platform: ROM.PLATFORM_ESP32, models: [ { - id: ROM.MODEL_C5, - name: "433 MHz", - }, - { - id: ROM.MODEL_CA, - name: "868 MHz / 915 MHz / 923 MHz", + id: ROM.MODEL_FE, + name: "868 MHz", }, ], - firmware_filename: "rnode_firmware_heltec32v3.zip", + firmware_filename: "rnode_firmware_meshadventurer.zip", + firmware_repo: "https://github.com/chrismyers2000/MeshAdventurer", flash_config: { - flash_size: "8MB", + flash_size: "4MB", flash_files: { - "0xe000": "rnode_firmware_heltec32v3.boot_app0", - "0x0": "rnode_firmware_heltec32v3.bootloader", - "0x10000": "rnode_firmware_heltec32v3.bin", + "0xe000": "rnode_firmware_meshadventurer.boot_app0", + "0x1000": "rnode_firmware_meshadventurer.bootloader", + "0x10000": "rnode_firmware_meshadventurer.bin", "0x210000": "console_image.bin", - "0x8000": "rnode_firmware_heltec32v3.partitions", + "0x8000": "rnode_firmware_meshadventurer.partitions", }, }, }, { - name: "Heltec LoRa32 v4", - id: ROM.PRODUCT_H32_V4, - platform: ROM.PLATFORM_ESP32, + name: "FakeTec (Promicro)", + id: ROM.PRODUCT_HMBRW, + platform: ROM.PLATFORM_NRF52, models: [ { - id: ROM.MODEL_C8, - name: "868 MHz / 915 MHz / 923 MHz with PA", + id: ROM.MODEL_FE, + name: "863-928 MHz", }, ], - firmware_filename: "rnode_firmware_heltec32v4pa.zip", - flash_config: { - flash_size: "16MB", - flash_files: { - "0xe000": "rnode_firmware_heltec32v4pa.boot_app0", - "0x0": "rnode_firmware_heltec32v4pa.bootloader", - "0x10000": "rnode_firmware_heltec32v4pa.bin", - "0x210000": "console_image.bin", - "0x8000": "rnode_firmware_heltec32v4pa.partitions", - }, - }, + firmware_filename: "rnode_firmware_promicro.zip", + firmware_repo: "https://github.com/gargomoma/fakeTec_pcb", }, { name: "Heltec T114", @@ -478,440 +578,24 @@ }, ], firmware_filename: "rnode_firmware_heltec_t114.zip", - }, - { - name: "LilyGO LoRa32 v1.0", - id: ROM.PRODUCT_T32_10, - platform: ROM.PLATFORM_ESP32, - models: [ - { - id: ROM.MODEL_BA, - name: "433 MHz", - }, - { - id: ROM.MODEL_BB, - name: "868 MHz / 915 MHz / 923 MHz", - }, - ], - firmware_filename: "rnode_firmware_lora32v10.zip", - flash_config: { - flash_size: "4MB", - flash_files: { - "0xe000": "rnode_firmware_lora32v10.boot_app0", - "0x1000": "rnode_firmware_lora32v10.bootloader", - "0x10000": "rnode_firmware_lora32v10.bin", - "0x210000": "console_image.bin", - "0x8000": "rnode_firmware_lora32v10.partitions", - }, - }, - }, - { - name: "LilyGO LoRa32 v2.0", - id: ROM.PRODUCT_T32_20, - platform: ROM.PLATFORM_ESP32, - models: [ - { - id: ROM.MODEL_B3, - name: "433 MHz", - }, - { - id: ROM.MODEL_B8, - name: "868 MHz / 915 MHz / 923 MHz", - }, - ], - firmware_filename: "rnode_firmware_lora32v20.zip", - flash_config: { - flash_size: "4MB", - flash_files: { - "0xe000": "rnode_firmware_lora32v20.boot_app0", - "0x1000": "rnode_firmware_lora32v20.bootloader", - "0x10000": "rnode_firmware_lora32v20.bin", - "0x210000": "console_image.bin", - "0x8000": "rnode_firmware_lora32v20.partitions", - }, - }, - }, - { - name: "LilyGO LoRa32 v2.1", - id: ROM.PRODUCT_T32_21, - platform: ROM.PLATFORM_ESP32, - models: [ - { - id: ROM.MODEL_B4, - name: "433 MHz", - firmware_filename: "rnode_firmware_lora32v21.zip", - flash_config: { - flash_size: "4MB", - flash_files: { - "0xe000": "rnode_firmware_lora32v21.boot_app0", - "0x1000": "rnode_firmware_lora32v21.bootloader", - "0x10000": "rnode_firmware_lora32v21.bin", - "0x210000": "console_image.bin", - "0x8000": "rnode_firmware_lora32v21.partitions", - }, - }, - }, - { - id: ROM.MODEL_B9, - name: "868/915/923 MHz", - firmware_filename: "rnode_firmware_lora32v21.zip", - flash_config: { - flash_size: "4MB", - flash_files: { - "0xe000": "rnode_firmware_lora32v21.boot_app0", - "0x1000": "rnode_firmware_lora32v21.bootloader", - "0x10000": "rnode_firmware_lora32v21.bin", - "0x210000": "console_image.bin", - "0x8000": "rnode_firmware_lora32v21.partitions", - }, - }, - }, - // The TCXO model codes are only used here to select the correct firmware, - // actual model codes in firmware is still 0xB4 and 0xB9. - // https://github.com/markqvist/Reticulum/blob/master/RNS/Utilities/rnodeconf.py#L156 - // https://github.com/markqvist/Reticulum/blob/master/RNS/Utilities/rnodeconf.py#L3283 - { - id: ROM.MODEL_B4_TCXO, - mapped_id: ROM.MODEL_B4, // this device uses the same model code, but different firmware file - name: "433 MHz, with TCXO", - firmware_filename: "rnode_firmware_lora32v21_tcxo.zip", - flash_config: { - flash_size: "4MB", - flash_files: { - "0xe000": "rnode_firmware_lora32v21_tcxo.boot_app0", - "0x1000": "rnode_firmware_lora32v21_tcxo.bootloader", - "0x10000": "rnode_firmware_lora32v21_tcxo.bin", - "0x210000": "console_image.bin", - "0x8000": "rnode_firmware_lora32v21_tcxo.partitions", - }, - }, - }, - { - id: ROM.MODEL_B9_TCXO, - mapped_id: ROM.MODEL_B9, // this device uses the same model code, but different firmware file - name: "868/915/923 MHz, with TCXO", - firmware_filename: "rnode_firmware_lora32v21_tcxo.zip", - flash_config: { - flash_size: "4MB", - flash_files: { - "0xe000": "rnode_firmware_lora32v21_tcxo.boot_app0", - "0x1000": "rnode_firmware_lora32v21_tcxo.bootloader", - "0x10000": "rnode_firmware_lora32v21_tcxo.bin", - "0x210000": "console_image.bin", - "0x8000": "rnode_firmware_lora32v21_tcxo.partitions", - }, - }, - }, - ], - }, - { - name: "LilyGO LoRa T3S3", - id: ROM.PRODUCT_RNODE, - platform: ROM.PLATFORM_ESP32, - models: [ - { - id: ROM.MODEL_A5, - name: "433 MHz (with SX1278 chip)", - firmware_filename: "rnode_firmware_t3s3_sx127x.zip", - flash_config: { - flash_size: "4MB", - flash_files: { - "0xe000": "rnode_firmware_t3s3_sx127x.boot_app0", - "0x0": "rnode_firmware_t3s3_sx127x.bootloader", - "0x10000": "rnode_firmware_t3s3_sx127x.bin", - "0x210000": "console_image.bin", - "0x8000": "rnode_firmware_t3s3_sx127x.partitions", - }, - }, - }, - { - id: ROM.MODEL_AA, - name: "868/915/923 MHz (with SX1276 chip)", - firmware_filename: "rnode_firmware_t3s3_sx127x.zip", - flash_config: { - flash_size: "4MB", - flash_files: { - "0xe000": "rnode_firmware_t3s3_sx127x.boot_app0", - "0x0": "rnode_firmware_t3s3_sx127x.bootloader", - "0x10000": "rnode_firmware_t3s3_sx127x.bin", - "0x210000": "console_image.bin", - "0x8000": "rnode_firmware_t3s3_sx127x.partitions", - }, - }, - }, - { - id: ROM.MODEL_A1, - name: "433 MHz (with SX1268 chip)", - firmware_filename: "rnode_firmware_t3s3.zip", - flash_config: { - flash_size: "4MB", - flash_files: { - "0xe000": "rnode_firmware_t3s3.boot_app0", - "0x0": "rnode_firmware_t3s3.bootloader", - "0x10000": "rnode_firmware_t3s3.bin", - "0x210000": "console_image.bin", - "0x8000": "rnode_firmware_t3s3.partitions", - }, - }, - }, - { - id: ROM.MODEL_A6, - name: "868/915/923 MHz (with SX1262 chip)", - firmware_filename: "rnode_firmware_t3s3.zip", - flash_config: { - flash_size: "4MB", - flash_files: { - "0xe000": "rnode_firmware_t3s3.boot_app0", - "0x0": "rnode_firmware_t3s3.bootloader", - "0x10000": "rnode_firmware_t3s3.bin", - "0x210000": "console_image.bin", - "0x8000": "rnode_firmware_t3s3.partitions", - }, - }, - }, - { - id: ROM.MODEL_AC, - name: "2.4 GHz (with SX1280 chip)", - firmware_filename: "rnode_firmware_t3s3_sx1280_pa.zip", - flash_config: { - flash_size: "4MB", - flash_files: { - "0xe000": "rnode_firmware_t3s3_sx1280_pa.boot_app0", - "0x0": "rnode_firmware_t3s3_sx1280_pa.bootloader", - "0x10000": "rnode_firmware_t3s3_sx1280_pa.bin", - "0x210000": "console_image.bin", - "0x8000": "rnode_firmware_t3s3_sx1280_pa.partitions", - }, - }, - }, - ], - }, - { - name: "LilyGO T-Beam", - id: ROM.PRODUCT_TBEAM, - platform: ROM.PLATFORM_ESP32, - models: [ - { - id: ROM.MODEL_E4, - name: "433 MHz (with SX1278 chip)", - firmware_filename: "rnode_firmware_tbeam.zip", - flash_config: { - flash_size: "4MB", - flash_files: { - "0xe000": "rnode_firmware_tbeam.boot_app0", - "0x1000": "rnode_firmware_tbeam.bootloader", - "0x10000": "rnode_firmware_tbeam.bin", - "0x210000": "console_image.bin", - "0x8000": "rnode_firmware_tbeam.partitions", - }, - }, - }, - { - id: ROM.MODEL_E9, - name: "868/915/923 MHz (with SX1276 chip)", - firmware_filename: "rnode_firmware_tbeam.zip", - flash_config: { - flash_size: "4MB", - flash_files: { - "0xe000": "rnode_firmware_tbeam.boot_app0", - "0x1000": "rnode_firmware_tbeam.bootloader", - "0x10000": "rnode_firmware_tbeam.bin", - "0x210000": "console_image.bin", - "0x8000": "rnode_firmware_tbeam.partitions", - }, - }, - }, - { - id: ROM.MODEL_E3, - name: "433 MHz (with SX1268 chip)", - firmware_filename: "rnode_firmware_tbeam_sx1262.zip", - flash_config: { - flash_size: "4MB", - flash_files: { - "0xe000": "rnode_firmware_tbeam_sx1262.boot_app0", - "0x1000": "rnode_firmware_tbeam_sx1262.bootloader", - "0x10000": "rnode_firmware_tbeam_sx1262.bin", - "0x210000": "console_image.bin", - "0x8000": "rnode_firmware_tbeam_sx1262.partitions", - }, - }, - }, - { - id: ROM.MODEL_E8, - name: "868/915/923 MHz (with SX1262 chip)", - firmware_filename: "rnode_firmware_tbeam_sx1262.zip", - flash_config: { - flash_size: "4MB", - flash_files: { - "0xe000": "rnode_firmware_tbeam_sx1262.boot_app0", - "0x1000": "rnode_firmware_tbeam_sx1262.bootloader", - "0x10000": "rnode_firmware_tbeam_sx1262.bin", - "0x210000": "console_image.bin", - "0x8000": "rnode_firmware_tbeam_sx1262.partitions", - }, - }, - }, - ], - }, - { - name: "LilyGO T-Beam Supreme", - id: ROM.PRODUCT_TBEAM_S_V1, - platform: ROM.PLATFORM_ESP32, - models: [ - { - id: ROM.MODEL_DB, - name: "433 MHz (with SX1268 chip)", - }, - { - id: ROM.MODEL_DC, - name: "868/915/923 MHz (with SX1262 chip)", - }, - ], - firmware_filename: "rnode_firmware_tbeam_supreme.zip", - flash_config: { - flash_size: "4MB", - flash_files: { - "0xe000": "rnode_firmware_tbeam_supreme.boot_app0", - "0x0": "rnode_firmware_tbeam_supreme.bootloader", - "0x10000": "rnode_firmware_tbeam_supreme.bin", - "0x210000": "console_image.bin", - "0x8000": "rnode_firmware_tbeam_supreme.partitions", - }, - }, - }, - { - name: "LilyGO T-Deck", - id: ROM.PRODUCT_TDECK, - platform: ROM.PLATFORM_ESP32, - models: [ - { - id: ROM.MODEL_D4, - name: "433 MHz (with SX1268 chip)", - }, - { - id: ROM.MODEL_D9, - name: "868/915/923 MHz (with SX1262 chip)", - }, - ], - firmware_filename: "rnode_firmware_tdeck.zip", - flash_config: { - flash_size: "4MB", - flash_files: { - "0xe000": "rnode_firmware_tdeck.boot_app0", - "0x0": "rnode_firmware_tdeck.bootloader", - "0x10000": "rnode_firmware_tdeck.bin", - "0x210000": "console_image.bin", - "0x8000": "rnode_firmware_tdeck.partitions", - }, - }, - }, - { - name: "LilyGO T-Echo", - id: ROM.PRODUCT_TECHO, - platform: ROM.PLATFORM_NRF52, - models: [ - { - id: ROM.MODEL_16, - name: "433 MHz", - }, - { - id: ROM.MODEL_17, - name: "868 MHz / 915 MHz / 923 MHz", - }, - ], - firmware_filename: "rnode_firmware_techo.zip", - }, - { - name: "RAK4631", - id: ROM.PRODUCT_RAK4631, - platform: ROM.PLATFORM_NRF52, - models: [ - { - id: ROM.MODEL_11, - name: "433 MHz", - }, - { - id: ROM.MODEL_12, - name: "868 MHz / 915 MHz / 923 MHz", - }, - ], - firmware_filename: "rnode_firmware_rak4631.zip", - }, - { - name: "RNode", - id: ROM.PRODUCT_RNODE, - platform: ROM.PLATFORM_ESP32, - models: [ - { - id: ROM.MODEL_A2, - name: "Handheld v2.1 RNode, 410 - 525 MHz", - firmware_filename: "rnode_firmware_ng21.zip", - flash_config: { - flash_size: "4MB", - flash_files: { - "0xe000": "rnode_firmware_ng21.boot_app0", - "0x1000": "rnode_firmware_ng21.bootloader", - "0x10000": "rnode_firmware_ng21.bin", - "0x210000": "console_image.bin", - "0x8000": "rnode_firmware_ng21.partitions", - }, - }, - }, - { - id: ROM.MODEL_A7, - name: "Handheld v2.1 RNode, 820 - 1020 MHz", - firmware_filename: "rnode_firmware_ng21.zip", - flash_config: { - flash_size: "4MB", - flash_files: { - "0xe000": "rnode_firmware_ng21.boot_app0", - "0x1000": "rnode_firmware_ng21.bootloader", - "0x10000": "rnode_firmware_ng21.bin", - "0x210000": "console_image.bin", - "0x8000": "rnode_firmware_ng21.partitions", - }, - }, - }, - { - id: ROM.MODEL_A1, - name: "Prototype v2.2 RNode, 410 - 525 MHz", - firmware_filename: "rnode_firmware_t3s3.zip", - flash_config: { - flash_size: "4MB", - flash_files: { - "0xe000": "rnode_firmware_t3s3.boot_app0", - "0x0": "rnode_firmware_t3s3.bootloader", - "0x10000": "rnode_firmware_t3s3.bin", - "0x210000": "console_image.bin", - "0x8000": "rnode_firmware_t3s3.partitions", - }, - }, - }, - { - id: ROM.MODEL_A6, - name: "Prototype v2.2 RNode, 820 - 1020 MHz", - firmware_filename: "rnode_firmware_t3s3.zip", - flash_config: { - flash_size: "4MB", - flash_files: { - "0xe000": "rnode_firmware_t3s3.boot_app0", - "0x0": "rnode_firmware_t3s3.bootloader", - "0x10000": "rnode_firmware_t3s3.bin", - "0x210000": "console_image.bin", - "0x8000": "rnode_firmware_t3s3.partitions", - }, - }, - }, - ], + firmware_repo: "https://heltec.org/project/mesh-node-t114/", }, ], - // Liam's default config for New Zealand / LongFast on Slot 10 - configFrequency: 917375000, + // Default config + configFrequency: 433175000, configBandwidth: 250000, configTxPower: 22, configSpreadingFactor: 11, configCodingRate: 5, + displayIntensity: 255, + displayTimeout: 0, + wifiMode: null, + wifiChannel: 1, + wifiSSID: "", + wifiPSK: "", + wifiIP: "", + wifiNM: "", RNodeInterfaceDefaults: { bandwidths: [ // bandwidth in hz @@ -942,10 +626,15 @@ ], }, + // Flag to track if we're in the initial setup + isInitializing: true, + }; }, mounted() { - + // Set default selections after component is mounted + this.selectedProduct = this.products[0]; + this.selectedModel = this.products[0].models[0]; }, methods: { async askForSerialPort() { @@ -1060,7 +749,7 @@ // close port console.log("Closing serial port"); try { - await this.serialPort.close(); + await serialPort.close(); } catch(e) { console.log("failed to close serial port, ignoring...", e); } @@ -1219,6 +908,7 @@ rx_stat: await rnode.getRxStat(), tx_stat: await rnode.getTxStat(), rssi_stat: await rnode.getRssiStat(), + wifi_mode: await rnode.getWifiMode(), }); await rnode.close(); @@ -1787,6 +1477,331 @@ "- Bluetooth pin will shown on your RNode screen and on this page.", ].join("\n")); + }, + + async setWiFiMode() { + + // ask for rnode + const rnode = await this.askForRNode(); + if (!rnode) { + return; + } + + // check if device has been provisioned + const rom = await rnode.getRomAsObject(); + const details = rom.parse(); + if (!details || !details.is_provisioned) { + alert("Eeprom is not provisioned. You must do this first!"); + await rnode.close(); + return; + } + + // normalize mode to number (safety) + const mode = Number(this.wifiMode); + + console.log("setting wifi mode to", mode); + + await rnode.enableWiFiMode(mode); + + console.log("setting wifi mode: done"); + + const modeMap = { + 0: "OFF", + 1: "STATION", + 2: "AP" + }; + + const modeLabel = modeMap[mode] ?? "UNKNOWN"; + + alert("WiFi mode has been set to " + modeLabel + "!"); + + // update UI if needed + this.wifiMode = mode; + + // done + await Utils.sleepMillis(1000); + await rnode.close(); + }, + + async setWiFiChannel() { + + // ask for rnode + const rnode = await this.askForRNode(); + if(!rnode){ + return; + } + + // check if device has been provisioned + const rom = await rnode.getRomAsObject(); + const details = rom.parse(); + if(!details || !details.is_provisioned){ + alert("Eeprom is not provisioned. You must do this first!"); + await rnode.close(); + return; + } + + // set wifi channel + console.log("setting wifi channel to", this.wifiChannel); + await rnode.setWiFiChannel(this.wifiChannel); + console.log("setting wifi channel: done"); + alert("WiFi channel has been set to " + this.wifiChannel + "!"); + + // done + await Utils.sleepMillis(1000); + await rnode.close(); + }, + async setWiFiSSID() { + + // ask for rnode + const rnode = await this.askForRNode(); + if(!rnode){ + return; + } + + // check if device has been provisioned + const rom = await rnode.getRomAsObject(); + const details = rom.parse(); + if(!details || !details.is_provisioned){ + alert("Eeprom is not provisioned. You must do this first!"); + await rnode.close(); + return; + } + + // set wifi SSID + console.log("setting wifi SSID to", this.wifiSSID); + await rnode.setWiFiSSID(this.wifiSSID); + console.log("setting wifi SSID: done"); + alert("WiFi SSID has been set to " + this.wifiSSID + "!"); + + // done + await Utils.sleepMillis(1000); + await rnode.close(); + + }, + async disableWiFiMode() { + + // ask for rnode + const rnode = await this.askForRNode(); + if(!rnode){ + return; + } + + // check if device has been provisioned + const rom = await rnode.getRomAsObject(); + const details = rom.parse(); + if(!details || !details.is_provisioned){ + alert("Eeprom is not provisioned. You must do this first!"); + await rnode.close(); + return; + } + + // disable wifi mode + console.log("disabling wifi mode"); + await rnode.disableWiFiMode(); + console.log("disabling wifi mode: done"); + alert("WiFi mode has been disabled!"); + + // done + await Utils.sleepMillis(1000); + await rnode.close(); + + }, + async setWiFiPSK() { + + // ask for rnode + const rnode = await this.askForRNode(); + if(!rnode){ + return; + } + + // check if device has been provisioned + const rom = await rnode.getRomAsObject(); + const details = rom.parse(); + if(!details || !details.is_provisioned){ + alert("Eeprom is not provisioned. You must do this first!"); + await rnode.close(); + return; + } + + // set wifi psk + console.log("setting wifi psk to", this.wifiPSK); + await rnode.setWiFiPSK(this.wifiPSK); + console.log("setting wifi psk: done"); + alert("WiFi PSK has been set!"); + + // done + await Utils.sleepMillis(1000); + await rnode.close(); + + }, + async unsetWiFiSSID() { + + // ask for rnode + const rnode = await this.askForRNode(); + if(!rnode){ + return; + } + + // check if device has been provisioned + const rom = await rnode.getRomAsObject(); + const details = rom.parse(); + if(!details || !details.is_provisioned){ + alert("Eeprom is not provisioned. You must do this first!"); + await rnode.close(); + return; + } + + // unset wifi SSID by setting it to "None" + console.log("unsetting wifi SSID"); + await rnode.setWiFiSSID("None"); + console.log("unsetting wifi SSID: done"); + alert("WiFi SSID has been unset!"); + + // update UI + this.wifiSSID = "None"; + + // done + await Utils.sleepMillis(1000); + await rnode.close(); + + }, + async unsetWiFiPSK() { + + // ask for rnode + const rnode = await this.askForRNode(); + if(!rnode){ + return; + } + + // check if device has been provisioned + const rom = await rnode.getRomAsObject(); + const details = rom.parse(); + if(!details || !details.is_provisioned){ + alert("Eeprom is not provisioned. You must do this first!"); + await rnode.close(); + return; + } + + // unset wifi psk by setting it to "None" + console.log("unsetting wifi psk"); + await rnode.setWiFiPSK(null); + console.log("unsetting wifi psk: done"); + alert("WiFi PSK has been unset!"); + + // update UI + this.wifiPSK = ""; + + // done + await Utils.sleepMillis(1000); + await rnode.close(); + + }, + + async unsetWiFiIP() { + + const rnode = await this.askForRNode(); + if (!rnode) return; + + const rom = await rnode.getRomAsObject(); + const details = rom.parse(); + if (!details || !details.is_provisioned) { + alert("Eeprom is not provisioned. You must do this first!"); + await rnode.close(); + return; + } + + console.log("unsetting wifi ip"); + + await rnode.setWiFiIP(null); + + console.log("unsetting wifi ip: done"); + + this.wifiIP = ""; + + await Utils.sleepMillis(1000); + await rnode.close(); + }, + + async unsetWiFiNM() { + + const rnode = await this.askForRNode(); + if (!rnode) return; + + const rom = await rnode.getRomAsObject(); + const details = rom.parse(); + if (!details || !details.is_provisioned) { + alert("Eeprom is not provisioned. You must do this first!"); + await rnode.close(); + return; + } + + console.log("unsetting wifi netmask"); + + await rnode.setWiFiNM(null); + + console.log("unsetting wifi netmask: done"); + + this.wifiNM = ""; + + await Utils.sleepMillis(1000); + await rnode.close(); + }, + + async setWiFiIP() { + + // ask for rnode + const rnode = await this.askForRNode(); + if(!rnode){ + return; + } + + // check if device has been provisioned + const rom = await rnode.getRomAsObject(); + const details = rom.parse(); + if(!details || !details.is_provisioned){ + alert("Eeprom is not provisioned. You must do this first!"); + await rnode.close(); + return; + } + + // set wifi ip + console.log("setting wifi ip to", this.wifiIP); + await rnode.setWiFiIP(this.wifiIP); + console.log("setting wifi ip: done"); + alert("WiFi IP has been set to " + this.wifiIP + "!"); + + // done + await Utils.sleepMillis(1000); + await rnode.close(); + + }, + async setWiFiNM() { + + // ask for rnode + const rnode = await this.askForRNode(); + if(!rnode){ + return; + } + + // check if device has been provisioned + const rom = await rnode.getRomAsObject(); + const details = rom.parse(); + if(!details || !details.is_provisioned){ + alert("Eeprom is not provisioned. You must do this first!"); + await rnode.close(); + return; + } + + // set wifi nm + console.log("setting wifi nm to", this.wifiNM); + await rnode.setWiFiNM(this.wifiNM); + console.log("setting wifi nm: done"); + alert("WiFi Netmask has been set to " + this.wifiNM + "!"); + + // done + await Utils.sleepMillis(1000); + await rnode.close(); + }, async setDisplayRotation(rotation) { @@ -1813,6 +1828,58 @@ // done await rnode.close(); + }, + async setDisplayIntensity(intensity) { + + // ask for rnode + const rnode = await this.askForRNode(); + if(!rnode){ + return; + } + + // check if device has been provisioned + const rom = await rnode.getRomAsObject(); + const details = rom.parse(); + if(!details || !details.is_provisioned){ + alert("Eeprom is not provisioned. You must do this first!"); + await rnode.close(); + return; + } + + // configure + console.log("setting display intensity"); + await rnode.setDisplayIntensity(intensity); + console.log("setting display intensity: done"); + + // done + await rnode.close(); + + }, + async setDisplayTimeout(timeout) { + + // ask for rnode + const rnode = await this.askForRNode(); + if(!rnode){ + return; + } + + // check if device has been provisioned + const rom = await rnode.getRomAsObject(); + const details = rom.parse(); + if(!details || !details.is_provisioned){ + alert("Eeprom is not provisioned. You must do this first!"); + await rnode.close(); + return; + } + + // configure + console.log("setting display timeout"); + await rnode.setDisplayTimeout(timeout); + console.log("setting display timeout: done"); + + // done + await rnode.close(); + }, async startDisplayReconditioning() { @@ -1854,11 +1921,63 @@ recommendedFirmwareFilename() { return this.selectedModel?.firmware_filename ?? this.selectedProduct?.firmware_filename; }, + FirmwareRepo() { + return this.selectedModel?.firmware_repo ?? this.selectedProduct?.firmware_repo; + }, + isSsidValid() { + if (!this.wifiSSID) return false; + + const encoder = new TextEncoder(); + const bytes = encoder.encode(this.wifiSSID); + + return bytes.length >= 1 && bytes.length <= 32; + }, + isPskValid() { + if (!this.wifiPSK) return false; + + const encoder = new TextEncoder(); + const bytes = encoder.encode(this.wifiPSK); + + return bytes.length >= 8; + }, + isIpValid() { + if (!this.wifiIP) return false; + + const parts = this.wifiIP.split("."); + if (parts.length !== 4) return false; + + for (const part of parts) { + if (part === "") return false; + + const num = Number(part); + if (!Number.isInteger(num)) return false; + if (num < 0 || num > 255) return false; + } + return true; + }, + isNetmaskValid() { + if (!this.wifiNM) return false; + + const parts = this.wifiNM.split("."); + if (parts.length !== 4) return false; + + for (const part of parts) { + if (part === "") return false; + + const num = Number(part); + if (!Number.isInteger(num)) return false; + if (num < 0 || num > 255) return false; + } + + return true; + }, }, watch: { selectedProduct() { - // reset selected model when changing selected product - this.selectedModel = null; + // reset selected model when changing selected product, but not during initialization + if (!this.isInitializing) { + this.selectedModel = null; + } }, }, }).mount('#app'); diff --git a/js/rnode.js b/js/rnode.js index 120ca40..f49c9df 100644 --- a/js/rnode.js +++ b/js/rnode.js @@ -80,7 +80,15 @@ class RNode { CMD_HASHES = 0x60; CMD_FW_UPD = 0x61; CMD_DISP_ROT = 0x67; + CMD_DISP_INT = 0x45; + CMD_DISP_BLNK = 0x64; CMD_DISP_RCND = 0x68; + CMD_WIFI_MODE = 0x6A; + CMD_WIFI_SSID = 0x6B; + CMD_WIFI_PSK = 0x6C; + CMD_WIFI_CHN = 0x6E; + CMD_WIFI_IP = 0x84; + CMD_WIFI_NM = 0x85; CMD_BT_CTRL = 0x46; CMD_BT_PIN = 0x62; @@ -610,6 +618,185 @@ class RNode { } + async enableWiFiMode(wifiMode) { + await this.sendKissCommand([ + this.CMD_WIFI_MODE, + wifiMode, // 0x00 = OFF, 0x01 = AP, 0x02 = STATION + ]); + } + + async disableWiFiMode() { + await this.sendKissCommand([ + this.CMD_WIFI_MODE, + 0x00, // OFF + ]); + } + + async setWiFiChannel(wifiChannel) { + await this.sendKissCommand([ + this.CMD_WIFI_CHN, + wifiChannel, // 1-14 + ]); + } + + async setWiFiSSID(wifiSSID) { + const encoder = new TextEncoder(); + const ssidBytes = encoder.encode(wifiSSID); + + // Add null terminator + const data = new Uint8Array(ssidBytes.length + 1); + data.set(ssidBytes); + data[data.length - 1] = 0x00; + + // KISS escape + const escaped = []; + for (const byte of data) { + if (byte === this.FEND) { + escaped.push(this.FESC, this.TFEND); + } else if (byte === this.FESC) { + escaped.push(this.FESC, this.TFESC); + } else { + escaped.push(byte); + } + } + + // Send command + await this.sendKissCommand([ + this.CMD_WIFI_SSID, + ...escaped, + ]); + } + + async setWiFiPSK(wifiPSK) { + if (wifiPSK == null) { + // Clear PSK + await this.sendKissCommand([ + this.CMD_WIFI_PSK, + 0x00 + ]); + return; + } + + const encoder = new TextEncoder(); + const pskBytes = encoder.encode(wifiPSK); + + // Enforce firmware length rules (8–32 characters) + if (pskBytes.length < 8 || pskBytes.length > 32) { + throw new Error("Invalid PSK length (must be 8–32 bytes)"); + } + + // Add null terminator + const data = new Uint8Array(pskBytes.length + 1); + data.set(pskBytes); + data[data.length - 1] = 0x00; + + // KISS escape + const escaped = []; + for (const byte of data) { + if (byte === this.FEND) { + escaped.push(this.FESC, this.TFEND); + } else if (byte === this.FESC) { + escaped.push(this.FESC, this.TFESC); + } else { + escaped.push(byte); + } + } + + // Send command + await this.sendKissCommand([ + this.CMD_WIFI_PSK, + ...escaped, + ]); + } + + async setWiFiIP(wifiIP) { + if (wifiIP == null) { + // Clear IP → 0.0.0.0 + await this.sendKissCommand([ + this.CMD_WIFI_IP, + 0x00, 0x00, 0x00, 0x00 + ]); + return; + } + + if (typeof wifiIP !== "string") { + throw new TypeError("Invalid IP address (not a string)"); + } + + const octets = wifiIP.split("."); + if (octets.length !== 4) { + throw new Error("Invalid IP address length"); + } + + const ipBytes = new Uint8Array(4); + + for (let i = 0; i < 4; i++) { + const octet = Number(octets[i]); + + if (!Number.isInteger(octet) || octet < 0 || octet > 255) { + throw new Error("Invalid IP octet value"); + } + + ipBytes[i] = octet; + } + + // KISS escape (same as PSK logic) + const escaped = []; + for (const byte of ipBytes) { + if (byte === this.KISS_FEND) { + escaped.push(this.KISS_FESC, this.KISS_TFEND); + } else if (byte === this.KISS_FESC) { + escaped.push(this.KISS_FESC, this.KISS_TFESC); + } else { + escaped.push(byte); + } + } + + // Send command exactly like PSK does + await this.sendKissCommand([ + this.CMD_WIFI_IP, + ...escaped, + ]); + } + + async setWiFiNM(wifiNM) { + if (wifiNM == null) { + // Clear netmask → 0.0.0.0 + await this.sendKissCommand([ + this.CMD_WIFI_NM, + 0x00, 0x00, 0x00, 0x00 + ]); + return; + } + + if (typeof wifiNM !== "string") { + throw new TypeError("Invalid netmask (not a string)"); + } + + const octets = wifiNM.split("."); + if (octets.length !== 4) { + throw new Error("Invalid netmask length"); + } + + const nmBytes = new Uint8Array(4); + + for (let i = 0; i < 4; i++) { + const octet = Number(octets[i]); + + if (!Number.isInteger(octet) || octet < 0 || octet > 255) { + throw new Error("Invalid netmask octet value"); + } + + nmBytes[i] = octet; + } + + // Send using same KISS pipeline as PSK/IP + await this.sendKissCommand([ + this.CMD_WIFI_NM, + ...nmBytes + ]); + } + async readDisplay() { const response = await this.sendCommand(this.CMD_DISP_READ, [ @@ -759,6 +946,20 @@ class RNode { ]); } + async setDisplayIntensity(intensity) { + await this.sendKissCommand([ + this.CMD_DISP_INT, + intensity & 0xFF, + ]); + } + + async setDisplayTimeout(timeout) { + await this.sendKissCommand([ + this.CMD_DISP_BLNK, + timeout & 0xFF, + ]); + } + async startDisplayReconditioning() { await this.sendKissCommand([ this.CMD_DISP_RCND, @@ -855,12 +1056,29 @@ class ROM { static ADDR_CHKSUM = 0x0B static ADDR_SIGNATURE = 0x1B static ADDR_INFO_LOCK = 0x9B + static ADDR_CONF_SF = 0x9C static ADDR_CONF_CR = 0x9D static ADDR_CONF_TXP = 0x9E static ADDR_CONF_BW = 0x9F static ADDR_CONF_FREQ = 0xA3 static ADDR_CONF_OK = 0xA7 + static ADDR_CONF_BT = 0xB0 + static ADDR_CONF_DSET = 0xB1 + static ADDR_CONF_DINT = 0xB2 + static ADDR_CONF_DADR = 0xB3 + static ADDR_CONF_DBLK = 0xB4 + static ADDR_CONF_DROT = 0xB8 + static ADDR_CONF_PSET = 0xB5 + static ADDR_CONF_PINT = 0xB6 + static ADDR_CONF_BSET = 0xB7 + static ADDR_CONF_DIA = 0xB9 + static ADDR_CONF_WIFI = 0xBA + static ADDR_CONF_WCHN = 0xBB + static ADDR_CONF_SSID = 0x00 + static ADDR_CONF_PSK = 0x21 + static ADDR_CONF_IP = 0x42 + static ADDR_CONF_NM = 0x46 static INFO_LOCK_BYTE = 0x73 static CONF_OK_BYTE = 0x73