diff --git a/Changelog.md b/Changelog.md index 25fd8e0..b514126 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,3 +1,49 @@ +### 2024-02-14: RNS β 0.7.1 + +This release adds support for RNodes based on SX1262, SX1268 and SX1280 modems, and fixes a number of bugs. Thanks to @jacobeva, who contributed to this release! + +**Changes** +- Added support for SX1262, SX1268 and SX1280 based RNodes +- Updated `rnodeconf` to allow flashing T-Beam devices with SX126x chips +- Fixed an invalid RSSI offset reference + +**Release Hashes** +``` +8ecfbb42b6a699fd4ac5374ab5640e4bb164e80bb9ab4401ea82da132e497877 rns-0.7.1-py3-none-any.whl +e0ab487305ba1aee2d16044640e7eb72d332bbf51aeb0b8bf984d037a64cb493 rnspure-0.7.1-py3-none-any.whl +``` + +### 2024-01-17: RNS β 0.7.0 + +This maintenance release fixes a number of bugs. Thanks to @jooray and @jacobeva, who contributed to this release! + +**Changes** +- Fixed large resource transfers failing under some conditions +- Fixed a potential division by zero +- Fixed a missing check on malformed advertisement packets +- Fixed a formatting issue in `rnprobe` +- Improved resource timeout calculations + +**Release Hashes** +``` +0dc2abe5373b9afadfba7ec05bf7ddeff659c004aa339a94001ebed5b46f5b47 rns-0.7.0-py3-none-any.whl +97f6e65a20b53bbdccd54b4d2bdaa36dc1712e144a55f40800c63fe7113819a5 rnspure-0.7.0-py3-none-any.whl +``` + +### 2023-12-07: RNS β 0.6.9 + +This release adds a few convenience functions to the `rnid` utility, and improves roaming support on Android. + +**Changes** +- Added identity import and export in hex, base32 and base64 formats to the `rnid` utility. +- Added better carrier change detection for AutoInterface on Android. + +**Release Hashes** +``` +258daf22cb6e72c6cd04fe94447daedf51dfd968eb2f3370eab9c71ad0898dd0 rns-0.6.9-py3-none-any.whl +3644b64af5b4efd3969172bf0cf95ae1afba6c8ea99ce47d8e49e31a832bbaf8 rnspure-0.6.9-py3-none-any.whl +``` + ### 2023-11-14: RNS β 0.6.8 This maintenance release fixes a single bug. diff --git a/Contributing.md b/Contributing.md index 8735316..af3bc4d 100644 --- a/Contributing.md +++ b/Contributing.md @@ -2,21 +2,29 @@ Welcome, and thank you for your interest in contributing to Reticulum! -Apart from writing code, there are many ways in which you can contribute. Before getting started, please read these guidelines. +Apart from writing code, there are many ways in which you can contribute. Before interacting with this community, read these short and simple guidelines. + +## Expected Conduct + +First and foremost, there is one simple requirement for taking part in this community: While we primarily interact virtually, your actions matter and have real consequences. Therefore: **Act like a responsible, civilized person** - also in the face of disputes and heated disagreements. Speak your mind here, discussions are welcome. Just do so in the spirit of being face-to-face with everyone else. Thank you. ## Asking Questions -If you want to ask a question, do not open an issue. +If you want to ask a question, **do not open an issue**. The issue tracker is used by people *working on Reticulum* to track bugs, issues and improvements. -Instead, ask away on the [discussions](https://github.com/markqvist/Reticulum/discussions) or on the [Reticulum Matrix channel](https://unsigned.io/contact.html#reticulum:matrix.org) at `#reticulum:matrix.org` +Instead, ask away on the [discussions](https://github.com/markqvist/Reticulum/discussions) or on the [Reticulum Matrix channel](https://matrix.to/#/#reticulum:matrix.org) at `#reticulum:matrix.org` -## Providing Feedback +## Providing Feedback & Ideas -Likewise, feedback, ideas and feature requests are a very welcome way to contribute, and should also be posted on the [discussions](https://github.com/markqvist/Reticulum/discussions), or on the [Reticulum Matrix channel](https://unsigned.io/contact.html#reticulum:matrix.org) at `#reticulum:matrix.org` +Likewise, feedback, ideas and feature requests are a very welcome way to contribute, and should also be posted on the [discussions](https://github.com/markqvist/Reticulum/discussions), or on the [Reticulum Matrix channel](https://matrix.to/#/#reticulum:matrix.org) at `#reticulum:matrix.org`. + +Please do not post feature requests or general ideas on the issue tracker, or in direct messages to the primary developers. You are much more likely to get a response and start a constructive discussion by posting your ideas in the public channels created for these purposes. ## Reporting Issues -If you have found a bug or issue in Reticulum, please report it on the [issue tracker](https://github.com/markqvist/Reticulum/issues). +If you have found a bug or issue in this project, please report it using the [issue tracker](https://github.com/markqvist/Reticulum/issues). If at all possible, be sure to include details on how to reproduce the bug. + +Anything submitted to the issue tracker that does not follow these guidelines will be closed and removed without comments or explanation. ## Writing Code diff --git a/FUNDING.yml b/FUNDING.yml new file mode 100644 index 0000000..3755d31 --- /dev/null +++ b/FUNDING.yml @@ -0,0 +1,2 @@ +ko_fi: markqvist +custom: "https://unsigned.io/donate" \ No newline at end of file diff --git a/LICENSE b/LICENSE index af8ce34..aa936a7 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License, unless otherwise noted -Copyright (c) 2016-2022 Mark Qvist / unsigned.io +Copyright (c) 2016-2023 Mark Qvist / unsigned.io Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/README.md b/README.md index f6b49d8..62c7d7a 100755 --- a/README.md +++ b/README.md @@ -192,7 +192,7 @@ provide a dynamic performance envelope from 250 bits per second, to 1 gigabit per second on normal hardware. Currently, the usable performance envelope is approximately 150 bits per second -to 20 megabits per second, with physical mediums faster than that not being +to 40 megabits per second, with physical mediums faster than that not being saturated. Performance beyond the current level is intended for future upgrades, but not highly prioritised at this point in time. @@ -273,16 +273,16 @@ The testnet also contains a number of [Nomad Network](https://github.com/markqvi You can help support the continued development of open, free and private communications systems by donating via one of the following channels: - Monero: - ``` + ``` 84FpY1QbxHcgdseePYNmhTHcrgMX4nFfBYtz2GKYToqHVVhJp8Eaw1Z1EedRnKD19b3B8NiLCGVxzKV17UMmmeEsCrPyA5w ``` - Ethereum ``` - 0x81F7B979fEa6134bA9FD5c701b3501A2e61E897a + 0xFDabC71AC4c0C78C95aDDDe3B4FA19d6273c5E73 ``` - Bitcoin ``` - 3CPmacGm34qYvR6XWLVEJmi2aNe3PZqUuq + 35G9uWVzrpJJibzUwpNUQGQNFzLirhrYAH ``` - Ko-Fi: https://ko-fi.com/markqvist diff --git a/RNS/Interfaces/AutoInterface.py b/RNS/Interfaces/AutoInterface.py index ea4511d..0845c58 100644 --- a/RNS/Interfaces/AutoInterface.py +++ b/RNS/Interfaces/AutoInterface.py @@ -334,6 +334,8 @@ class AutoInterface(Interface): thread.daemon = True thread.start() + self.carrier_changed = True + except Exception as e: RNS.log("Could not get device information while updating link-local addresses for "+str(self)+". The contained exception was: "+str(e), RNS.LOG_ERROR) diff --git a/RNS/Interfaces/Interface.py b/RNS/Interfaces/Interface.py index d0afb51..9ba3996 100755 --- a/RNS/Interfaces/Interface.py +++ b/RNS/Interfaces/Interface.py @@ -68,6 +68,7 @@ class Interface: self.txb = 0 self.created = time.time() self.online = False + self.bitrate = 1e6 self.ingress_control = True self.ic_max_held_announces = Interface.MAX_HELD_ANNOUNCES diff --git a/RNS/Interfaces/RNodeInterface.py b/RNS/Interfaces/RNodeInterface.py index f0292a0..00c97a4 100644 --- a/RNS/Interfaces/RNodeInterface.py +++ b/RNS/Interfaces/RNodeInterface.py @@ -92,7 +92,7 @@ class RNodeInterface(Interface): MAX_CHUNK = 32768 FREQ_MIN = 137000000 - FREQ_MAX = 1020000000 + FREQ_MAX = 3000000000 RSSI_OFFSET = 157 @@ -109,7 +109,7 @@ class RNodeInterface(Interface): def __init__(self, owner, name, port, frequency = None, bandwidth = None, txpower = None, sf = None, cr = None, flow_control = False, id_interval = None, id_callsign = None, st_alock = None, lt_alock = None): if RNS.vendor.platformutils.is_android(): - raise SystemError("Invlaid interface type. The Android-specific RNode interface must be used on Android") + raise SystemError("Invalid interface type. The Android-specific RNode interface must be used on Android") import importlib if importlib.util.find_spec('serial') != None: @@ -190,15 +190,15 @@ class RNodeInterface(Interface): RNS.log("Invalid frequency configured for "+str(self), RNS.LOG_ERROR) self.validcfg = False - if (self.txpower < 0 or self.txpower > 17): + if (self.txpower < 0 or self.txpower > 22): RNS.log("Invalid TX power configured for "+str(self), RNS.LOG_ERROR) self.validcfg = False - if (self.bandwidth < 7800 or self.bandwidth > 500000): + if (self.bandwidth < 7800 or self.bandwidth > 1625000): RNS.log("Invalid bandwidth configured for "+str(self), RNS.LOG_ERROR) self.validcfg = False - if (self.sf < 7 or self.sf > 12): + if (self.sf < 5 or self.sf > 12): RNS.log("Invalid spreading factor configured for "+str(self), RNS.LOG_ERROR) self.validcfg = False diff --git a/RNS/Link.py b/RNS/Link.py index 24d8c4d..e7609ff 100644 --- a/RNS/Link.py +++ b/RNS/Link.py @@ -352,7 +352,7 @@ class Link: packed_request = umsgpack.packb(unpacked_request) if timeout == None: - timeout = self.rtt * self.traffic_timeout_factor + RNS.Resource.RESPONSE_MAX_GRACE_TIME/4.0 + timeout = self.rtt * self.traffic_timeout_factor + RNS.Resource.RESPONSE_MAX_GRACE_TIME*1.125 if len(packed_request) <= Link.MDU: request_packet = RNS.Packet(self, packed_request, RNS.Packet.DATA, context = RNS.Packet.REQUEST) @@ -687,7 +687,9 @@ class Link: remove = pending_request try: pending_request.response_size = response_size - pending_request.response_transfer_size = response_transfer_size + if pending_request.response_transfer_size == None: + pending_request.response_transfer_size = 0 + pending_request.response_transfer_size += response_transfer_size pending_request.response_received(response_data) except Exception as e: RNS.log("Error occurred while handling response. The contained exception was: "+str(e), RNS.LOG_ERROR) @@ -835,10 +837,15 @@ class Link: for pending_request in self.pending_requests: if pending_request.request_id == request_id: response_resource = RNS.Resource.accept(packet, callback=self.response_resource_concluded, progress_callback=pending_request.response_resource_progress, request_id = request_id) - pending_request.response_size = RNS.ResourceAdvertisement.read_size(packet) - pending_request.response_transfer_size = RNS.ResourceAdvertisement.read_transfer_size(packet) - pending_request.started_at = time.time() - pending_request.response_resource_progress(response_resource) + if response_resource != None: + if pending_request.response_size == None: + pending_request.response_size = RNS.ResourceAdvertisement.read_size(packet) + if pending_request.response_transfer_size == None: + pending_request.response_transfer_size = 0 + pending_request.response_transfer_size += RNS.ResourceAdvertisement.read_transfer_size(packet) + if pending_request.started_at == None: + pending_request.started_at = time.time() + pending_request.response_resource_progress(response_resource) elif self.resource_strategy == Link.ACCEPT_NONE: pass @@ -1132,7 +1139,8 @@ class RequestReceipt(): def request_resource_concluded(self, resource): if resource.status == RNS.Resource.COMPLETE: RNS.log("Request "+RNS.prettyhexrep(self.request_id)+" successfully sent as resource.", RNS.LOG_DEBUG) - self.started_at = time.time() + if self.started_at == None: + self.started_at = time.time() self.status = RequestReceipt.DELIVERED self.__resource_response_timeout = time.time()+self.timeout response_timeout_thread = threading.Thread(target=self.__response_timeout_job) @@ -1173,25 +1181,26 @@ class RequestReceipt(): def response_resource_progress(self, resource): - if not self.status == RequestReceipt.FAILED: - self.status = RequestReceipt.RECEIVING - if self.packet_receipt != None: - if self.packet_receipt.status != RNS.PacketReceipt.DELIVERED: - self.packet_receipt.status = RNS.PacketReceipt.DELIVERED - self.packet_receipt.proved = True - self.packet_receipt.concluded_at = time.time() - if self.packet_receipt.callbacks.delivery != None: - self.packet_receipt.callbacks.delivery(self.packet_receipt) + if resource != None: + if not self.status == RequestReceipt.FAILED: + self.status = RequestReceipt.RECEIVING + if self.packet_receipt != None: + if self.packet_receipt.status != RNS.PacketReceipt.DELIVERED: + self.packet_receipt.status = RNS.PacketReceipt.DELIVERED + self.packet_receipt.proved = True + self.packet_receipt.concluded_at = time.time() + if self.packet_receipt.callbacks.delivery != None: + self.packet_receipt.callbacks.delivery(self.packet_receipt) - self.progress = resource.get_progress() - - if self.callbacks.progress != None: - try: - self.callbacks.progress(self) - except Exception as e: - RNS.log("Error while executing response progress callback from "+str(self)+". The contained exception was: "+str(e), RNS.LOG_ERROR) - else: - resource.cancel() + self.progress = resource.get_progress() + + if self.callbacks.progress != None: + try: + self.callbacks.progress(self) + except Exception as e: + RNS.log("Error while executing response progress callback from "+str(self)+". The contained exception was: "+str(e), RNS.LOG_ERROR) + else: + resource.cancel() def response_received(self, response): diff --git a/RNS/Resource.py b/RNS/Resource.py index 533172a..a739bc1 100644 --- a/RNS/Resource.py +++ b/RNS/Resource.py @@ -25,6 +25,7 @@ import os import bz2 import math import time +import tempfile import threading from threading import Lock from .vendor import umsgpack as umsgpack @@ -106,7 +107,8 @@ class Resource: PART_TIMEOUT_FACTOR_AFTER_RTT = 2 MAX_RETRIES = 16 MAX_ADV_RETRIES = 4 - SENDER_GRACE_TIME = 10 + SENDER_GRACE_TIME = 10.0 + PROCESSING_GRACE = 1.0 RETRY_GRACE_TIME = 0.25 PER_RETRY_DELAY = 0.5 @@ -203,9 +205,19 @@ class Resource: resource_data = None self.assembly_lock = False + if data != None: + if not hasattr(data, "read") and len(data) > Resource.MAX_EFFICIENT_SIZE: + original_data = data + data_size = len(original_data) + data = tempfile.TemporaryFile() + data.write(original_data) + del original_data + if hasattr(data, "read"): - data_size = os.stat(data.name).st_size - self.total_size = data_size + if data_size == None: + data_size = os.stat(data.name).st_size + + self.total_size = data_size self.grand_total_parts = math.ceil(data_size/Resource.SDU) if data_size <= Resource.MAX_EFFICIENT_SIZE: @@ -278,7 +290,7 @@ class Resource: self.uncompressed_data = data compression_began = time.time() - if (auto_compress and len(self.uncompressed_data) < Resource.AUTO_COMPRESS_MAX_SIZE): + if (auto_compress and len(self.uncompressed_data) <= Resource.AUTO_COMPRESS_MAX_SIZE): RNS.log("Compressing resource data...", RNS.LOG_DEBUG) self.compressed_data = bz2.compress(self.uncompressed_data) RNS.log("Compression completed in "+str(round(time.time()-compression_began, 3))+" seconds", RNS.LOG_DEBUG) @@ -440,7 +452,7 @@ class Resource: sleep_time = None if self.status == Resource.ADVERTISED: - sleep_time = (self.adv_sent+self.timeout)-time.time() + sleep_time = (self.adv_sent+self.timeout+Resource.PROCESSING_GRACE)-time.time() if sleep_time < 0: if self.retries_left <= 0: RNS.log("Resource transfer timeout after sending advertisement", RNS.LOG_DEBUG) @@ -456,7 +468,7 @@ class Resource: self.adv_sent = self.last_activity sleep_time = 0.001 except Exception as e: - RNS.log("Could not resend advertisement packet, cancelling resource", RNS.LOG_VERBOSE) + RNS.log("Could not resend advertisement packet, cancelling resource. The contained exception was: "+str(e), RNS.LOG_VERBOSE) self.cancel() @@ -612,10 +624,26 @@ class Resource: self.callback(self) except Exception as e: RNS.log("Error while executing resource concluded callback from "+str(self)+". The contained exception was: "+str(e), RNS.LOG_ERROR) + finally: + try: + if hasattr(self, "input_file"): + if hasattr(self.input_file, "close") and callable(self.input_file.close): + self.input_file.close() + + except Exception as e: + RNS.log("Error while closing resource input file: "+str(e), RNS.LOG_ERROR) else: # Otherwise we'll recursively create the # next segment of the resource - Resource(self.input_file, self.link, callback = self.callback, segment_index = self.segment_index+1, original_hash=self.original_hash, progress_callback = self.__progress_callback) + Resource( + self.input_file, self.link, + callback = self.callback, + segment_index = self.segment_index+1, + original_hash=self.original_hash, + progress_callback = self.__progress_callback, + request_id = self.request_id, + is_response = self.is_response, + ) else: pass else: @@ -731,20 +759,6 @@ class Resource: i = 0; pn = self.consecutive_completed_height+1 search_start = pn search_size = self.window - - # TODO: Remove - # tpm = [] - # tpi = 0 - # try: - # for p in self.parts: - # if p == None: - # tpm.append(None) - # else: - # tpm.append(tpi) - # tpi+=1 - # except Exception as e: - # print(str(e)) - # RNS.log(f"Partmap: "+str(tpm)) for part in self.parts[search_start:search_start+search_size]: if part == None: diff --git a/RNS/Transport.py b/RNS/Transport.py index ca40d30..e41ffba 100755 --- a/RNS/Transport.py +++ b/RNS/Transport.py @@ -876,7 +876,7 @@ class Transport: interface.announce_queue = [] queued_announces = True if len(interface.announce_queue) > 0 else False - if not queued_announces and outbound_time > interface.announce_allowed_at: + if not queued_announces and outbound_time > interface.announce_allowed_at and interface.bitrate != None and interface.bitrate != 0: tx_time = (len(packet.raw)*8) / interface.bitrate wait_time = (tx_time / interface.announce_cap) interface.announce_allowed_at = outbound_time + wait_time diff --git a/RNS/Utilities/rnodeconf.py b/RNS/Utilities/rnodeconf.py index da1bc3d..87ba291 100755 --- a/RNS/Utilities/rnodeconf.py +++ b/RNS/Utilities/rnodeconf.py @@ -42,8 +42,8 @@ RNS.logtimefmt = "%H:%M:%S" RNS.compact_log_fmt = True program_version = "2.1.3" -eth_addr = "0x81F7B979fEa6134bA9FD5c701b3501A2e61E897a" -btc_addr = "3CPmacGm34qYvR6XWLVEJmi2aNe3PZqUuq" +eth_addr = "0xFDabC71AC4c0C78C95aDDDe3B4FA19d6273c5E73" +btc_addr = "35G9uWVzrpJJibzUwpNUQGQNFzLirhrYAH" xmr_addr = "87HcDx6jRSkMQ9nPRd5K9hGGpZLn2s7vWETjMaVM5KfV4TD36NcYa8J8WSxhTSvBzzFpqDwp2fg5GX2moZ7VAP9QMZCZGET" rnode = None @@ -120,12 +120,16 @@ class KISS(): class ROM(): PLATFORM_AVR = 0x90 PLATFORM_ESP32 = 0x80 + PLATFORM_NRF52 = 0x70 MCU_1284P = 0x91 MCU_2560 = 0x92 MCU_ESP32 = 0x81 + MCU_NRF52 = 0x71 PRODUCT_RNODE = 0x03 + MODEL_A1 = 0xA1 + MODEL_A6 = 0xA6 MODEL_A4 = 0xA4 MODEL_A9 = 0xA9 MODEL_A3 = 0xA3 @@ -144,6 +148,8 @@ class ROM(): PRODUCT_T32_21 = 0xB1 MODEL_B4 = 0xB4 MODEL_B9 = 0xB9 + MODEL_B4_TCXO = 0x04 + MODEL_B9_TCXO = 0x09 PRODUCT_H32_V2 = 0xC0 MODEL_C4 = 0xC4 @@ -152,6 +158,8 @@ class ROM(): PRODUCT_TBEAM = 0xE0 MODEL_E4 = 0xE4 MODEL_E9 = 0xE9 + MODEL_E3 = 0xE3 + MODEL_E8 = 0xE8 PRODUCT_HMBRW = 0xF0 MODEL_FF = 0xFF @@ -182,6 +190,7 @@ class ROM(): BOARD_GENERIC_ESP32 = 0x35 BOARD_LORA32_V2_0 = 0x36 BOARD_LORA32_V2_1 = 0x37 + BOARD_RAK4630 = 0x51 mapped_product = ROM.PRODUCT_RNODE products = { @@ -197,33 +206,41 @@ products = { platforms = { ROM.PLATFORM_AVR: "AVR", ROM.PLATFORM_ESP32:"ESP32", + ROM.PLATFORM_NRF52:"NRF52", } mcus = { ROM.MCU_1284P: "ATmega1284P", ROM.MCU_2560:"ATmega2560", ROM.MCU_ESP32:"Espressif Systems ESP32", + ROM.MCU_NRF52:"Nordic nRF52840", } models = { - 0xA4: [410000000, 525000000, 14, "410 - 525 MHz", "rnode_firmware.hex"], - 0xA9: [820000000, 1020000000, 17, "820 - 1020 MHz", "rnode_firmware.hex"], - 0xA2: [410000000, 525000000, 17, "410 - 525 MHz", "rnode_firmware_ng21.zip"], - 0xA7: [820000000, 1020000000, 17, "820 - 1020 MHz", "rnode_firmware_ng21.zip"], - 0xA3: [410000000, 525000000, 17, "410 - 525 MHz", "rnode_firmware_ng20.zip"], - 0xA8: [820000000, 1020000000, 17, "820 - 1020 MHz", "rnode_firmware_ng20.zip"], - 0xB3: [420000000, 520000000, 17, "420 - 520 MHz", "rnode_firmware_lora32v20.zip"], - 0xB8: [850000000, 950000000, 17, "850 - 950 MHz", "rnode_firmware_lora32v20.zip"], - 0xB4: [420000000, 520000000, 17, "420 - 520 MHz", "rnode_firmware_lora32v21.zip"], - 0xB9: [850000000, 950000000, 17, "850 - 950 MHz", "rnode_firmware_lora32v21.zip"], - 0xBA: [420000000, 520000000, 17, "420 - 520 MHz", "rnode_firmware_lora32v10.zip"], - 0xBB: [850000000, 950000000, 17, "850 - 950 MHz", "rnode_firmware_lora32v10.zip"], - 0xC4: [420000000, 520000000, 17, "420 - 520 MHz", "rnode_firmware_heltec32v2.zip"], - 0xC9: [850000000, 950000000, 17, "850 - 950 MHz", "rnode_firmware_heltec32v2.zip"], - 0xE4: [420000000, 520000000, 17, "420 - 520 MHz", "rnode_firmware_tbeam.zip"], - 0xE9: [850000000, 950000000, 17, "850 - 950 MHz", "rnode_firmware_tbeam.zip"], - 0xFE: [100000000, 1100000000, 17, "(Band capabilities unknown)", None], - 0xFF: [100000000, 1100000000, 14, "(Band capabilities unknown)", None], + 0xA4: [410000000, 525000000, 14, "410 - 525 MHz", "rnode_firmware.hex", "SX1278"], + 0xA9: [820000000, 1020000000, 17, "820 - 1020 MHz", "rnode_firmware.hex", "SX1276"], + 0xA1: [410000000, 525000000, 22, "410 - 525 MHz", "rnode_firmware_t3s3.zip", "SX1268"], + 0xA6: [820000000, 1020000000, 22, "820 - 960 MHz", "rnode_firmware_t3s3.zip", "SX1262"], + 0xA2: [410000000, 525000000, 17, "410 - 525 MHz", "rnode_firmware_ng21.zip", "SX1278"], + 0xA7: [820000000, 1020000000, 17, "820 - 1020 MHz", "rnode_firmware_ng21.zip", "SX1276"], + 0xA3: [410000000, 525000000, 17, "410 - 525 MHz", "rnode_firmware_ng20.zip", "SX1278"], + 0xA8: [820000000, 1020000000, 17, "820 - 1020 MHz", "rnode_firmware_ng20.zip", "SX1276"], + 0xB3: [420000000, 520000000, 17, "420 - 520 MHz", "rnode_firmware_lora32v20.zip", "SX1278"], + 0xB8: [850000000, 950000000, 17, "850 - 950 MHz", "rnode_firmware_lora32v20.zip", "SX1276"], + 0xB4: [420000000, 520000000, 17, "420 - 520 MHz", "rnode_firmware_lora32v21.zip", "SX1278"], + 0xB9: [850000000, 950000000, 17, "850 - 950 MHz", "rnode_firmware_lora32v21.zip", "SX1276"], + 0x04: [420000000, 520000000, 17, "420 - 520 MHz", "rnode_firmware_lora32v21_tcxo.zip", "SX1278"], + 0x09: [850000000, 950000000, 17, "850 - 950 MHz", "rnode_firmware_lora32v21_tcxo.zip", "SX1276"], + 0xBA: [420000000, 520000000, 17, "420 - 520 MHz", "rnode_firmware_lora32v10.zip", "SX1278"], + 0xBB: [850000000, 950000000, 17, "850 - 950 MHz", "rnode_firmware_lora32v10.zip", "SX1276"], + 0xC4: [420000000, 520000000, 17, "420 - 520 MHz", "rnode_firmware_heltec32v2.zip", "SX1278"], + 0xC9: [850000000, 950000000, 17, "850 - 950 MHz", "rnode_firmware_heltec32v2.zip", "SX1276"], + 0xE4: [420000000, 520000000, 17, "420 - 520 MHz", "rnode_firmware_tbeam.zip", "SX1278"], + 0xE9: [850000000, 950000000, 17, "850 - 950 MHz", "rnode_firmware_tbeam.zip", "SX1276"], + 0xE3: [420000000, 520000000, 22, "420 - 520 MHz", "rnode_firmware_tbeam_sx1262.zip", "SX1268"], + 0xE8: [850000000, 950000000, 22, "850 - 950 MHz", "rnode_firmware_tbeam_sx1262.zip", "SX1262"], + 0xFE: [100000000, 1100000000, 17, "(Band capabilities unknown)", None, "Unknown"], + 0xFF: [100000000, 1100000000, 14, "(Band capabilities unknown)", None, "Unknown"], } CNF_DIR = None @@ -516,7 +533,7 @@ class RNode(): if (len(command_buffer) == 4): self.r_stat_tx = ord(command_buffer[0]) << 24 | ord(command_buffer[1]) << 16 | ord(command_buffer[2]) << 8 | ord(command_buffer[3]) elif (command == KISS.CMD_STAT_RSSI): - self.r_stat_rssi = byte-RNodeInterface.RSSI_OFFSET + self.r_stat_rssi = byte-157 # RSSI Offset elif (command == KISS.CMD_STAT_SNR): self.r_stat_snr = int.from_bytes(bytes([byte]), byteorder="big", signed=True) * 0.25 elif (command == KISS.CMD_RANDOM): @@ -928,6 +945,7 @@ class RNode(): except Exception as e: self.provisioned = False RNS.log("Invalid EEPROM data, could not parse device EEPROM.") + RNS.log("The contained exception was: "+str(e)) def device_probe(self): @@ -1018,6 +1036,10 @@ def ensure_firmware_file(fw_filename): file = open(UPD_DIR+"/"+fw_filename+".version.latest", "rb") release_info = file.read().decode("utf-8").strip() selected_version = release_info.split()[0] + if selected_version == "not": + RNS.log("No valid version found for this board, exiting.") + exit(199) + selected_hash = release_info.split()[1] if not os.path.isdir(UPD_DIR+"/"+selected_version): os.makedirs(UPD_DIR+"/"+selected_version) @@ -1198,15 +1220,15 @@ def main(): parser.add_argument("--version", action="store_true", help="Print program version and exit") - parser.add_argument("-f", "--flash", action="store_true", help=argparse.SUPPRESS) # Flash firmware and bootstrap EEPROM - parser.add_argument("-r", "--rom", action="store_true", help=argparse.SUPPRESS) # Bootstrap EEPROM without flashing firmware - parser.add_argument("-k", "--key", action="store_true", help=argparse.SUPPRESS) # Generate a new signing key and exit - parser.add_argument("-S", "--sign", action="store_true", help=argparse.SUPPRESS) # Display public part of signing key - parser.add_argument("-H", "--firmware-hash", action="store", help=argparse.SUPPRESS) # Display public part of signing key - parser.add_argument("--platform", action="store", metavar="platform", type=str, default=None, help=argparse.SUPPRESS) # Platform specification for device bootstrap - parser.add_argument("--product", action="store", metavar="product", type=str, default=None, help=argparse.SUPPRESS) # Product specification for device bootstrap - parser.add_argument("--model", action="store", metavar="model", type=str, default=None, help=argparse.SUPPRESS) # Model code for device bootstrap - parser.add_argument("--hwrev", action="store", metavar="revision", type=int, default=None, help=argparse.SUPPRESS) # Hardware revision for device bootstrap + 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("-k", "--key", action="store_true", help="Generate a new signing key and exit") # + parser.add_argument("-S", "--sign", action="store_true", help="Display public part of signing key") + parser.add_argument("-H", "--firmware-hash", action="store", help="Display installed firmware hash") + 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("port", nargs="?", default=None, help="serial port where RNode is attached", type=str) args = parser.parse_args() @@ -1526,6 +1548,7 @@ def main(): print("[5] LilyGO LoRa32 v1.0") print("[6] LilyGO T-Beam") print("[7] Heltec LoRa32 v2") + print("[8] LilyGO LoRa T3S3") print(" .") print(" / \\ Select one of these options if you want to easily turn") print(" | a supported development board into an RNode.") @@ -1536,7 +1559,8 @@ def main(): selected_product = None try: c_dev = int(input()) - if c_dev < 1 or c_dev > 7: + c_mod = False + if c_dev < 1 or c_dev > 8: raise ValueError() elif c_dev == 1: selected_product = ROM.PRODUCT_RNODE @@ -1565,8 +1589,7 @@ def main(): print(" T-Beam RNode Installer") print("") print("The RNode firmware can currently be installed on T-Beam devices using the") - print("SX1276 and SX1278 transceiver chips. Support for devices with the newer") - print("SX1262 and SX1268 chips is in development.") + print("SX1276, SX1278, SX1262 and SX1268 transceiver chips.") print("") print("Important! Using RNode firmware on T-Beam devices should currently be") print("considered experimental. It is not intended for production or critical use.") @@ -1637,6 +1660,23 @@ def main(): print("who would like to experiment with it. Hit enter to continue.") print("---------------------------------------------------------------------------") input() + elif c_dev == 8: + selected_product = ROM.PRODUCT_RNODE + c_mod = True + clear() + print("") + print("---------------------------------------------------------------------------") + print(" LilyGO LoRa32 T3S3 RNode Installer") + print("") + print("Important! Using RNode firmware on T3S3 devices should currently be") + print("considered experimental. It is not intended for production or critical use.") + print("") + print("Please note that Bluetooth is currently not implemented on this board.") + print("") + print("The currently supplied firmware is provided AS-IS as a courtesey to those") + print("who would like to experiment with it. Hit enter to continue.") + print("---------------------------------------------------------------------------") + input() except Exception as e: print("That device type does not exist, exiting now.") exit() @@ -1689,53 +1729,86 @@ def main(): elif selected_product == ROM.PRODUCT_RNODE: - selected_mcu = ROM.MCU_1284P - print("\nWhat model is this RNode?\n") - print("[1] Handheld v2.x RNode, 410 - 525 MHz") - print("[2] Handheld v2.x RNode, 820 - 1020 MHz") - print("") - print("[3] Original v1.x RNode, 410 - 525 MHz") - print("[4] Original v1.x RNode, 820 - 1020 MHz") - # print("[5] Prototype v2 RNode, 410 - 525 MHz") - # print("[6] Prototype v2 RNode, 820 - 1020 MHz") - print("\n? ", end="") - try: - c_model = int(input()) - if c_model < 1 or c_model > 6: - raise ValueError() - elif c_model == 3: - selected_model = ROM.MODEL_A4 - selected_platform = ROM.PLATFORM_AVR - elif c_model == 4: - selected_model = ROM.MODEL_A9 - selected_platform = ROM.PLATFORM_AVR - elif c_model == 1: - selected_model = ROM.MODEL_A2 - selected_mcu = ROM.MCU_ESP32 - selected_platform = ROM.PLATFORM_ESP32 - elif c_model == 2: - selected_model = ROM.MODEL_A7 - selected_mcu = ROM.MCU_ESP32 - selected_platform = ROM.PLATFORM_ESP32 - # elif c_model == 5: - # selected_model = ROM.MODEL_A3 - # selected_mcu = ROM.MCU_ESP32 - # selected_platform = ROM.PLATFORM_ESP32 - # elif c_model == 6: - # selected_model = ROM.MODEL_A8 - # selected_mcu = ROM.MCU_ESP32 - # selected_platform = ROM.PLATFORM_ESP32 - except Exception as e: - print("That model does not exist, exiting now.") - exit() + if not c_mod: + selected_mcu = ROM.MCU_1284P + print("\nWhat model is this RNode?\n") + print("[1] Handheld v2.1 RNode, 410 - 525 MHz") + print("[2] Handheld v2.1 RNode, 820 - 1020 MHz") + print("") + print("[3] Original v1.x RNode, 410 - 525 MHz") + print("[4] Original v1.x RNode, 820 - 1020 MHz") + print("") + print("[5] Prototype v2.2 RNode, 410 - 525 MHz") + print("[6] Prototype v2.2 RNode, 820 - 1020 MHz") + # print("[5] Prototype v2 RNode, 410 - 525 MHz") + # print("[6] Prototype v2 RNode, 820 - 1020 MHz") + print("\n? ", end="") + try: + c_model = int(input()) + if c_model < 1 or c_model > 6: + raise ValueError() + elif c_model == 1: + selected_model = ROM.MODEL_A2 + selected_mcu = ROM.MCU_ESP32 + selected_platform = ROM.PLATFORM_ESP32 + elif c_model == 2: + selected_model = ROM.MODEL_A7 + selected_mcu = ROM.MCU_ESP32 + selected_platform = ROM.PLATFORM_ESP32 + elif c_model == 3: + selected_model = ROM.MODEL_A4 + selected_platform = ROM.PLATFORM_AVR + elif c_model == 4: + selected_model = ROM.MODEL_A9 + selected_platform = ROM.PLATFORM_AVR + elif c_model == 5: + selected_model = ROM.MODEL_A1 + selected_mcu = ROM.MCU_ESP32 + selected_platform = ROM.PLATFORM_ESP32 + elif c_model == 6: + selected_model = ROM.MODEL_A6 + selected_mcu = ROM.MCU_ESP32 + selected_platform = ROM.PLATFORM_ESP32 + # elif c_model == 5: + # selected_model = ROM.MODEL_A3 + # selected_mcu = ROM.MCU_ESP32 + # selected_platform = ROM.PLATFORM_ESP32 + # elif c_model == 6: + # selected_model = ROM.MODEL_A8 + # selected_mcu = ROM.MCU_ESP32 + # selected_platform = ROM.PLATFORM_ESP32 + except Exception as e: + print("That model does not exist, exiting now.") + exit() + else: + print("\nWhat model is this T3S3?\n") + print("[1] 410 - 525 MHz (with SX1268 chip)") + print("[2] 820 - 1020 MHz (with SX1268 chip)") + 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_A1 + selected_mcu = ROM.MCU_ESP32 + selected_platform = ROM.PLATFORM_ESP32 + elif c_model == 2: + selected_model = ROM.MODEL_A6 + selected_mcu = ROM.MCU_ESP32 + selected_platform = ROM.PLATFORM_ESP32 + 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("[1] 433 MHz (with SX1278 chip)") + print("[2] 868/915/923 MHz (with SX1276 chip)") + print(""); + print("[3] 433 MHz (with SX1268 chip)") + print("[4] 868/915/923 MHz (with SX1262 chip)") print("\n? ", end="") try: c_model = int(input()) @@ -1744,9 +1817,15 @@ def main(): elif c_model == 1: selected_model = ROM.MODEL_E4 selected_platform = ROM.PLATFORM_ESP32 - elif c_model > 1: + elif c_model == 2: selected_model = ROM.MODEL_E9 selected_platform = ROM.PLATFORM_ESP32 + elif c_model == 3: + selected_model = ROM.MODEL_E3 + selected_platform = ROM.PLATFORM_ESP32 + elif c_model == 4: + selected_model = ROM.MODEL_E8 + selected_platform = ROM.PLATFORM_ESP32 except Exception as e: print("That band does not exist, exiting now.") exit() @@ -1799,9 +1878,9 @@ def main(): selected_mcu = ROM.MCU_ESP32 print("\nWhat band is this LoRa32 for?\n") print("[1] 433 MHz") - print("[2] 868 MHz") - print("[3] 915 MHz") - print("[4] 923 MHz") + print("[2] 868/915/923 MHz") + print("[3] 433 MHz, with TCXO") + print("[4] 868/915/923 MHz, with TCXO") print("\n? ", end="") try: c_model = int(input()) @@ -1810,9 +1889,15 @@ def main(): elif c_model == 1: selected_model = ROM.MODEL_B4 selected_platform = ROM.PLATFORM_ESP32 - elif c_model > 1: + elif c_model == 2: selected_model = ROM.MODEL_B9 selected_platform = ROM.PLATFORM_ESP32 + elif c_model == 3: + selected_model = ROM.MODEL_B4_TCXO + selected_platform = ROM.PLATFORM_ESP32 + elif c_model == 4: + selected_model = ROM.MODEL_B9_TCXO + selected_platform = ROM.PLATFORM_ESP32 except Exception as e: print("That band does not exist, exiting now.") exit() @@ -2127,6 +2212,42 @@ def main(): "0x10000", UPD_DIR+"/"+selected_version+"/rnode_firmware_tbeam.bin", "0x8000", UPD_DIR+"/"+selected_version+"/rnode_firmware_tbeam.partitions", ] + elif fw_filename == "rnode_firmware_tbeam_sx1262.zip": + if numeric_version >= 1.55: + return [ + sys.executable, flasher, + "--chip", "esp32", + "--port", args.port, + "--baud", args.baud_flash, + "--before", "default_reset", + "--after", "hard_reset", + "write_flash", "-z", + "--flash_mode", "dio", + "--flash_freq", "80m", + "--flash_size", "4MB", + "0xe000", UPD_DIR+"/"+selected_version+"/rnode_firmware_tbeam_sx1262.boot_app0", + "0x1000", UPD_DIR+"/"+selected_version+"/rnode_firmware_tbeam_sx1262.bootloader", + "0x10000", UPD_DIR+"/"+selected_version+"/rnode_firmware_tbeam_sx1262.bin", + "0x210000",UPD_DIR+"/"+selected_version+"/console_image.bin", + "0x8000", UPD_DIR+"/"+selected_version+"/rnode_firmware_tbeam_sx1262.partitions", + ] + else: + return [ + sys.executable, flasher, + "--chip", "esp32", + "--port", args.port, + "--baud", args.baud_flash, + "--before", "default_reset", + "--after", "hard_reset", + "write_flash", "-z", + "--flash_mode", "dio", + "--flash_freq", "80m", + "--flash_size", "4MB", + "0xe000", UPD_DIR+"/"+selected_version+"/rnode_firmware_tbeam.boot_app0", + "0x1000", UPD_DIR+"/"+selected_version+"/rnode_firmware_tbeam.bootloader", + "0x10000", UPD_DIR+"/"+selected_version+"/rnode_firmware_tbeam.bin", + "0x8000", UPD_DIR+"/"+selected_version+"/rnode_firmware_tbeam.partitions", + ] elif fw_filename == "rnode_firmware_lora32v10.zip": if numeric_version >= 1.59: return [ @@ -2235,6 +2356,24 @@ def main(): "0x10000", UPD_DIR+"/"+selected_version+"/rnode_firmware_lora32v21.bin", "0x8000", UPD_DIR+"/"+selected_version+"/rnode_firmware_lora32v21.partitions", ] + elif fw_filename == "rnode_firmware_lora32v21_tcxo.zip": + return [ + sys.executable, flasher, + "--chip", "esp32", + "--port", args.port, + "--baud", args.baud_flash, + "--before", "default_reset", + "--after", "hard_reset", + "write_flash", "-z", + "--flash_mode", "dio", + "--flash_freq", "80m", + "--flash_size", "4MB", + "0xe000", UPD_DIR+"/"+selected_version+"/rnode_firmware_lora32v21_tcxo.boot_app0", + "0x1000", UPD_DIR+"/"+selected_version+"/rnode_firmware_lora32v21_tcxo.bootloader", + "0x10000", UPD_DIR+"/"+selected_version+"/rnode_firmware_lora32v21_tcxo.bin", + "0x210000",UPD_DIR+"/"+selected_version+"/console_image.bin", + "0x8000", UPD_DIR+"/"+selected_version+"/rnode_firmware_lora32v21_tcxo.partitions", + ] elif fw_filename == "rnode_firmware_heltec32v2.zip": if numeric_version >= 1.55: return [ @@ -2415,6 +2554,24 @@ def main(): "0x10000", UPD_DIR+"/"+selected_version+"/rnode_firmware_ng21.bin", "0x8000", UPD_DIR+"/"+selected_version+"/rnode_firmware_ng21.partitions", ] + elif fw_filename == "rnode_firmware_t3s3.zip": + return [ + sys.executable, flasher, + "--chip", "esp32s3", + "--port", args.port, + "--baud", args.baud_flash, + "--before", "default_reset", + "--after", "hard_reset", + "write_flash", "-z", + "--flash_mode", "dio", + "--flash_freq", "80m", + "--flash_size", "4MB", + "0xe000", UPD_DIR+"/"+selected_version+"/rnode_firmware_t3s3.boot_app0", + "0x1000", UPD_DIR+"/"+selected_version+"/rnode_firmware_t3s3.bootloader", + "0x10000", UPD_DIR+"/"+selected_version+"/rnode_firmware_t3s3.bin", + "0x210000",UPD_DIR+"/"+selected_version+"/console_image.bin", + "0x8000", UPD_DIR+"/"+selected_version+"/rnode_firmware_t3s3.partitions", + ] elif fw_filename == "extracted_rnode_firmware.zip": return [ sys.executable, flasher, @@ -2799,6 +2956,7 @@ def main(): 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("\tModem chip : "+str(models[rnode.model][5])) 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) @@ -2880,12 +3038,21 @@ def main(): mapped_product = ROM.PRODUCT_TBEAM if mapped_model != None: - model = mapped_model + if mapped_model == ROM.MODEL_B4_TCXO: + model = ROM.MODEL_B4 + elif mapped_model == ROM.MODEL_B9_TCXO: + model = ROM.MODEL_B9 + else: + model = mapped_model else: if args.model == "a4": model = ROM.MODEL_A4 elif args.model == "a9": model = ROM.MODEL_A9 + elif args.model == "a1": + model = ROM.MODEL_A1 + elif args.model == "a6": + model = ROM.MODEL_A6 elif args.model == "e4": model = ROM.MODEL_E4 elif args.model == "e9": diff --git a/RNS/Utilities/rnprobe.py b/RNS/Utilities/rnprobe.py index 6af4d84..83e84d1 100644 --- a/RNS/Utilities/rnprobe.py +++ b/RNS/Utilities/rnprobe.py @@ -132,7 +132,7 @@ def program_setup(configdir, destination_hexhash, size=None, full_name = None, v i = (i+1)%len(syms) if time.time() > _timeout: - print("\r \rProbe timed out") + print("\r \rProbe timed out") else: print("\b\b ") diff --git a/RNS/__init__.py b/RNS/__init__.py index 0e0b7a2..323439f 100755 --- a/RNS/__init__.py +++ b/RNS/__init__.py @@ -144,6 +144,12 @@ def rand(): result = instance_random.random() return result +def trace_exception(e): + import traceback + exception_info = "".join(traceback.TracebackException.from_exception(e).format()) + log(f"An unhandled {str(type(e))} exception occurred: {str(e)}", LOG_ERROR) + log(exception_info, LOG_ERROR) + def hexrep(data, delimit=True): try: iter(data) diff --git a/RNS/_version.py b/RNS/_version.py index afd45a4..a5f830a 100644 --- a/RNS/_version.py +++ b/RNS/_version.py @@ -1 +1 @@ -__version__ = "0.6.8" +__version__ = "0.7.1" diff --git a/docs/Reticulum Manual.epub b/docs/Reticulum Manual.epub index 0d8d076..6ae03cb 100644 Binary files a/docs/Reticulum Manual.epub and b/docs/Reticulum Manual.epub differ diff --git a/docs/Reticulum Manual.pdf b/docs/Reticulum Manual.pdf index 4258715..6950657 100644 Binary files a/docs/Reticulum Manual.pdf and b/docs/Reticulum Manual.pdf differ diff --git a/docs/manual/.buildinfo b/docs/manual/.buildinfo index 434fce0..185536d 100644 --- a/docs/manual/.buildinfo +++ b/docs/manual/.buildinfo @@ -1,4 +1,4 @@ # Sphinx build info version 1 # This file hashes the configuration used when building these files. When it is not found, a full rebuild will be done. -config: 01461792acfce5a3fb667a828829012a +config: ea2e78e752b7110746ccd0d4052a8cac tags: 645f666f9bcd5a90fca523b33c5a78b7 diff --git a/docs/manual/_static/documentation_options.js b/docs/manual/_static/documentation_options.js index ab6e730..f74470f 100644 --- a/docs/manual/_static/documentation_options.js +++ b/docs/manual/_static/documentation_options.js @@ -1,6 +1,6 @@ var DOCUMENTATION_OPTIONS = { URL_ROOT: document.getElementById("documentation_options").getAttribute('data-url_root'), - VERSION: '0.6.8 beta', + VERSION: '0.7.1 beta', LANGUAGE: 'en', COLLAPSE_INDEX: false, BUILDER: 'html', diff --git a/docs/manual/examples.html b/docs/manual/examples.html index 5cbb378..c831bb8 100644 --- a/docs/manual/examples.html +++ b/docs/manual/examples.html @@ -6,7 +6,7 @@ -