From 4785367915b3ad2e41cba7b1a034af74b647dc1f Mon Sep 17 00:00:00 2001 From: Jm Casler Date: Mon, 25 Apr 2022 08:02:51 -0700 Subject: [PATCH 1/6] Temp work on compression --- src/configuration.h | 2 +- src/mesh/RadioInterface.cpp | 2 +- src/mesh/RadioLibInterface.cpp | 25 ++++++++++++----- src/mesh/Router.cpp | 49 +++++++++++++++++++++++----------- 4 files changed, 55 insertions(+), 23 deletions(-) diff --git a/src/configuration.h b/src/configuration.h index 8f0d3cfa..2e71ab76 100644 --- a/src/configuration.h +++ b/src/configuration.h @@ -133,7 +133,7 @@ along with this program. If not, see . //#define DISABLE_NTP // Disable the welcome screen and allow -// #define DISABLE_WELCOME_UNSET + #define DISABLE_WELCOME_UNSET // ----------------------------------------------------------------------------- // OLED & Input diff --git a/src/mesh/RadioInterface.cpp b/src/mesh/RadioInterface.cpp index e4b816b5..4706279b 100644 --- a/src/mesh/RadioInterface.cpp +++ b/src/mesh/RadioInterface.cpp @@ -251,7 +251,7 @@ void printPacket(const char *prefix, const MeshPacket *p) DEBUG_MSG(" rxSNR=%g", p->rx_snr); } if (p->rx_rssi != 0) { - DEBUG_MSG(" rxSNR=%g", p->rx_rssi); + DEBUG_MSG(" rxRSSI=%g", p->rx_rssi); } if (p->priority != 0) DEBUG_MSG(" priority=%d", p->priority); diff --git a/src/mesh/RadioLibInterface.cpp b/src/mesh/RadioLibInterface.cpp index a0058893..76964c7e 100644 --- a/src/mesh/RadioLibInterface.cpp +++ b/src/mesh/RadioLibInterface.cpp @@ -93,6 +93,9 @@ bool RadioLibInterface::canSendImmediately() /// bluetooth comms code. If the txmit queue is empty it might return an error ErrorCode RadioLibInterface::send(MeshPacket *p) { + +#ifndef DISABLE_WELCOME_UNSET + if (radioConfig.preferences.region != RegionCode_Unset) { if (disabled || radioConfig.preferences.is_lora_tx_disabled) { DEBUG_MSG("send - lora_tx_disabled\n"); @@ -106,6 +109,16 @@ ErrorCode RadioLibInterface::send(MeshPacket *p) return ERRNO_DISABLED; } +#else + + if (disabled || radioConfig.preferences.is_lora_tx_disabled) { + DEBUG_MSG("send - lora_tx_disabled\n"); + packetPool.release(p); + return ERRNO_DISABLED; + } + +#endif + // Sometimes when testing it is useful to be able to never turn on the xmitter #ifndef LORA_DISABLE_SENDING printPacket("enqueuing for send", p); @@ -121,7 +134,7 @@ ErrorCode RadioLibInterface::send(MeshPacket *p) // set (random) transmit delay to let others reconfigure their radio, // to avoid collisions and implement timing-based flooding // DEBUG_MSG("Set random delay before transmitting.\n"); - setTransmitDelay(); + setTransmitDelay(); return res; #else @@ -154,10 +167,10 @@ bool RadioLibInterface::cancelSending(NodeNum from, PacketId id) /** radio helper thread callback. We never immediately transmit after any operation (either rx or tx). Instead we should start receiving and -wait a random delay of 100ms to 100ms+shortPacketMsec to make sure we are not stomping on someone else. The 100ms delay at the beginning ensures all -possible listeners have had time to finish processing the previous packet and now have their radio in RX state. The up to 100ms+shortPacketMsec -random delay gives a chance for all possible senders to have high odds of detecting that someone else started transmitting first -and then they will wait until that packet finishes. +wait a random delay of 100ms to 100ms+shortPacketMsec to make sure we are not stomping on someone else. The 100ms delay at the +beginning ensures all possible listeners have had time to finish processing the previous packet and now have their radio in RX +state. The up to 100ms+shortPacketMsec random delay gives a chance for all possible senders to have high odds of detecting that +someone else started transmitting first and then they will wait until that packet finishes. NOTE: the large flood rebroadcast delay might still be needed even with this approach. Because we might not be able to hear other transmitters that we are potentially stomping on. Requires further thought. @@ -215,7 +228,7 @@ void RadioLibInterface::onNotify(uint32_t notification) void RadioLibInterface::setTransmitDelay() { MeshPacket *p = txQueue.getFront(); - // We want all sending/receiving to be done by our daemon thread. + // We want all sending/receiving to be done by our daemon thread. // We use a delay here because this packet might have been sent in response to a packet we just received. // So we want to make sure the other side has had a chance to reconfigure its radio. diff --git a/src/mesh/Router.cpp b/src/mesh/Router.cpp index 68e53a50..d6be1082 100644 --- a/src/mesh/Router.cpp +++ b/src/mesh/Router.cpp @@ -274,6 +274,9 @@ void Router::sniffReceived(const MeshPacket *p, const Routing *c) bool perhapsDecode(MeshPacket *p) { + + //DEBUG_MSG("\n\n** perhapsDecode payloadVariant - %d\n\n", p->which_payloadVariant); + if (p->which_payloadVariant == MeshPacket_decoded_tag) return true; // If packet was already decoded just return @@ -304,12 +307,28 @@ bool perhapsDecode(MeshPacket *p) p->which_payloadVariant = MeshPacket_decoded_tag; // change type to decoded p->channel = chIndex; // change to store the index instead of the hash - - // Decompress if needed. jm - if (p->decoded.which_payloadVariant == Data_payload_compressed_tag) { - // Decompress the file + if (p->decoded.portnum == PortNum_TEXT_MESSAGE_APP) { + DEBUG_MSG("\n\n** TEXT_MESSAGE_APP payloadVariant - %d\n\n", p->decoded.which_payloadVariant); } + // Decompress if needed. jm + if (p->decoded.portnum == PortNum_TEXT_MESSAGE_APP) { + if (p->decoded.which_payloadVariant == Data_payload_compressed_tag) { + // Decompress the payload + char compressed_in[Constants_DATA_PAYLOAD_LEN] = {}; + char decompressed_out[Constants_DATA_PAYLOAD_LEN] = {}; + int decompressed_len; + memcpy(compressed_in, p->decoded.payload.bytes, p->decoded.payload.size); + + decompressed_len = unishox2_decompress_simple(compressed_in, p->decoded.payload.size, decompressed_out); + + DEBUG_MSG("\n\n**\n\nDecompressed length - %d \n", decompressed_len); + // Serial.println(p->decoded.payload.bytes); + Serial.println(decompressed_out); + + p->decoded.which_payloadVariant = Data_payload_tag; + } + } printPacket("decoded message", p); return true; @@ -341,37 +360,37 @@ Routing_Error perhapsEncode(MeshPacket *p) char compressed_out[Constants_DATA_PAYLOAD_LEN] = {0}; int compressed_len; - // compressed_len = unishox2_compress_simple(original_payload, p->decoded.payload.size, compressed_out); + compressed_len = unishox2_compress_simple(original_payload, p->decoded.payload.size, compressed_out); - Serial.print("Original length - "); - Serial.println(p->decoded.payload.size); + DEBUG_MSG("Original length - %d \n", p->decoded.payload.size); + DEBUG_MSG("Compressed length - %d \n", compressed_len); - Serial.print("Compressed length - "); - Serial.println(compressed_len); // Serial.println(compressed_out); // If the compressed length is greater than or equal to the original size, don't use the compressed form if (compressed_len >= p->decoded.payload.size) { - DEBUG_MSG("Not compressing message. Not enough benefit from doing so.\n"); + DEBUG_MSG("Not using compressing message.\n"); // Set the uncompressed payload varient anyway. Shouldn't hurt? p->decoded.which_payloadVariant = Data_payload_tag; // Otherwise we use the compressor } else { - DEBUG_MSG("Compressing message.\n"); + DEBUG_MSG("Using compressed message.\n"); // Copy the compressed data into the meshpacket - //p->decoded.payload_compressed.size = compressed_len; - //memcpy(p->decoded.payload_compressed.bytes, compressed_out, compressed_len); + p->decoded.payload_compressed.size = compressed_len; + memcpy(p->decoded.payload_compressed.bytes, compressed_out, compressed_len); - //p->decoded.which_payloadVariant = Data_payload_compressed_tag; + DEBUG_MSG("\n\n** TEXT_MESSAGE_APP payloadVariant - %d\n\n", p->decoded.which_payloadVariant); + p->decoded.which_payloadVariant = Data_payload_compressed_tag; + DEBUG_MSG("\n\n** TEXT_MESSAGE_APP payloadVariant - %d\n\n", p->decoded.which_payloadVariant); } if (0) { char decompressed_out[Constants_DATA_PAYLOAD_LEN] = {}; int decompressed_len; - // decompressed_len = unishox2_decompress_simple(compressed_out, compressed_len, decompressed_out); + decompressed_len = unishox2_decompress_simple(compressed_out, compressed_len, decompressed_out); Serial.print("Decompressed length - "); Serial.println(decompressed_len); From 800a4200efd71c19b8df2348fc1a4719b0274d59 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20G=C3=B6ttgens?= Date: Sun, 22 May 2022 13:54:24 +0200 Subject: [PATCH 2/6] make sure all segments are enabled and saved --- src/mesh/NodeDB.cpp | 28 ++++++++++++++++++++++++++-- src/modules/AdminModule.cpp | 13 +++++++++++++ 2 files changed, 39 insertions(+), 2 deletions(-) diff --git a/src/mesh/NodeDB.cpp b/src/mesh/NodeDB.cpp index d804aa8e..47b9d0bc 100644 --- a/src/mesh/NodeDB.cpp +++ b/src/mesh/NodeDB.cpp @@ -146,6 +146,13 @@ bool NodeDB::resetRadioConfig() void NodeDB::installDefaultConfig() { memset(&config, 0, sizeof(LocalConfig)); + config.has_device = true; + config.has_display = true; + config.has_lora = true; + config.has_position = true; + config.has_power = true; + config.has_wifi = true; + config.lora.region = Config_LoRaConfig_RegionCode_Unset; config.lora.modem_preset = Config_LoRaConfig_ModemPreset_LongFast; resetRadioConfig(); @@ -158,6 +165,13 @@ void NodeDB::installDefaultConfig() void NodeDB::installDefaultModuleConfig() { memset(&moduleConfig, 0, sizeof(ModuleConfig)); + moduleConfig.has_canned_message = true; + moduleConfig.has_external_notification = true; + moduleConfig.has_mqtt = true; + moduleConfig.has_range_test = true; + moduleConfig.has_serial = true; + moduleConfig.has_store_forward = true; + moduleConfig.has_telemetry = true; } // void NodeDB::installDefaultRadioConfig() @@ -341,7 +355,7 @@ void NodeDB::loadFromDisk() installDefaultConfig(); // Our in RAM copy might now be corrupt } - if (!loadProto(moduleConfigfile, ModuleConfig_size, sizeof(ModuleConfig), ModuleConfig_fields, &moduleConfig)) { + if (!loadProto(moduleConfigfile, LocalModuleConfig_size, sizeof(LocalModuleConfig), LocalModuleConfig_fields, &moduleConfig)) { installDefaultModuleConfig(); // Our in RAM copy might now be corrupt } @@ -402,6 +416,7 @@ void NodeDB::saveToDisk() FSCom.mkdir("/prefs"); #endif saveProto(preffile, DeviceState_size, sizeof(devicestate), DeviceState_fields, &devicestate); + // save all config segments config.has_device = true; config.has_display = true; @@ -410,7 +425,16 @@ void NodeDB::saveToDisk() config.has_power = true; config.has_wifi = true; saveProto(configfile, LocalConfig_size, sizeof(LocalConfig), LocalConfig_fields, &config); - saveProto(moduleConfigfile, Module_Config_size, sizeof(ModuleConfig), ModuleConfig_fields, &moduleConfig); + + moduleConfig.has_canned_message = true; + moduleConfig.has_external_notification = true; + moduleConfig.has_mqtt = true; + moduleConfig.has_range_test = true; + moduleConfig.has_serial = true; + moduleConfig.has_store_forward = true; + moduleConfig.has_telemetry = true; + saveProto(moduleConfigfile, LocalModuleConfig_size, sizeof(LocalModuleConfig), LocalModuleConfig_fields, &moduleConfig); + saveChannelsToDisk(); } else { diff --git a/src/modules/AdminModule.cpp b/src/modules/AdminModule.cpp index decce029..9f6f55fe 100644 --- a/src/modules/AdminModule.cpp +++ b/src/modules/AdminModule.cpp @@ -176,26 +176,32 @@ void AdminModule::handleSetConfig(const Config &c) switch (c.which_payloadVariant) { case Config_device_tag: DEBUG_MSG("Setting config: Device\n"); + config.has_device = true; config.device = c.payloadVariant.device; break; case Config_position_tag: DEBUG_MSG("Setting config: Position\n"); + config.has_position = true; config.position = c.payloadVariant.position; break; case Config_power_tag: DEBUG_MSG("Setting config: Power\n"); + config.has_power = true; config.power = c.payloadVariant.power; break; case Config_wifi_tag: DEBUG_MSG("Setting config: WiFi\n"); + config.has_wifi = true; config.wifi = c.payloadVariant.wifi; break; case Config_display_tag: DEBUG_MSG("Setting config: Display\n"); + config.has_display = true; config.display = c.payloadVariant.display; break; case Config_lora_tag: DEBUG_MSG("Setting config: LoRa\n"); + config.has_lora = true; config.lora = c.payloadVariant.lora; break; } @@ -208,30 +214,37 @@ void AdminModule::handleSetModuleConfig(const ModuleConfig &c) switch (c.which_payloadVariant) { case ModuleConfig_mqtt_tag: DEBUG_MSG("Setting module config: MQTT\n"); + moduleConfig.has_mqtt = true; moduleConfig.mqtt = c.payloadVariant.mqtt; break; case ModuleConfig_serial_tag: DEBUG_MSG("Setting module config: Serial\n"); + moduleConfig.has_serial = true; moduleConfig.serial = c.payloadVariant.serial; break; case ModuleConfig_external_notification_tag: DEBUG_MSG("Setting module config: External Notification\n"); + moduleConfig.has_external_notification = true; moduleConfig.external_notification = c.payloadVariant.external_notification; break; case ModuleConfig_store_forward_tag: DEBUG_MSG("Setting module config: Store & Forward\n"); + moduleConfig.has_store_forward = true; moduleConfig.store_forward = c.payloadVariant.store_forward; break; case ModuleConfig_range_test_tag: DEBUG_MSG("Setting module config: Range Test\n"); + moduleConfig.has_range_test = true; moduleConfig.range_test = c.payloadVariant.range_test; break; case ModuleConfig_telemetry_tag: DEBUG_MSG("Setting module config: Telemetry\n"); + moduleConfig.has_telemetry = true; moduleConfig.telemetry = c.payloadVariant.telemetry; break; case ModuleConfig_canned_message_tag: DEBUG_MSG("Setting module config: Canned Message\n"); + moduleConfig.has_canned_message = true; moduleConfig.canned_message = c.payloadVariant.canned_message; break; } From a0b4b4efa11e844c7aa72c83acd3930b8bb163a4 Mon Sep 17 00:00:00 2001 From: mc-hamster Date: Tue, 24 May 2022 04:10:51 +0000 Subject: [PATCH 3/6] [create-pull-request] automated change --- protobufs | 2 +- src/mesh/generated/mesh.pb.h | 31 ++++++++++-------------- src/mesh/generated/module_config.pb.h | 34 +++++++++++++-------------- src/mesh/generated/portnums.pb.h | 4 ++-- 4 files changed, 32 insertions(+), 39 deletions(-) diff --git a/protobufs b/protobufs index 98a888f8..40b82242 160000 --- a/protobufs +++ b/protobufs @@ -1 +1 @@ -Subproject commit 98a888f863a750464cd44526568af20c7bf12227 +Subproject commit 40b822424221f4f7704e291d2b422c9dcca84c83 diff --git a/src/mesh/generated/mesh.pb.h b/src/mesh/generated/mesh.pb.h index bc784d69..893760ab 100644 --- a/src/mesh/generated/mesh.pb.h +++ b/src/mesh/generated/mesh.pb.h @@ -464,7 +464,6 @@ typedef struct _User { } User; typedef PB_BYTES_ARRAY_T(237) Data_payload_t; -typedef PB_BYTES_ARRAY_T(237) Data_payload_compressed_t; /* (Formerly called SubPacket) The payload portion fo a packet, this is the actual bytes that are sent inside a radio packet (because from/to are broken out by the comms library) */ @@ -472,34 +471,30 @@ typedef struct _Data { /* Formerly named typ and of type Type */ PortNum portnum; /* TODO: REPLACE */ - pb_size_t which_payloadVariant; - union { - Data_payload_t payload; - Data_payload_compressed_t payload_compressed; - }; - /* TODO: REPLACE */ - bool want_response; + Data_payload_t payload; /* Not normally used, but for testing a sender can request that recipient responds in kind (i.e. if it received a position, it should unicast back it's position). Note: that if you set this on a broadcast you will receive many replies. */ - uint32_t dest; + bool want_response; /* The address of the destination node. This field is is filled in by the mesh radio device software, application layer software should never need it. RouteDiscovery messages _must_ populate this. Other message types might need to if they are doing multihop routing. */ - uint32_t source; + uint32_t dest; /* The address of the original sender for this message. This field should _only_ be populated for reliable multihop packets (to keep packets small). */ - uint32_t request_id; + uint32_t source; /* Only used in routing or response messages. Indicates the original message ID that this message is reporting failure on. (formerly called original_id) */ - uint32_t reply_id; + uint32_t request_id; /* If set, this message is intened to be a reply to a previously sent message with the defined id. */ - uint32_t emoji; + uint32_t reply_id; /* Defaults to false. If true, then what is in the payload should be treated as an emoji like giving a message a heart or poop emoji. */ + uint32_t emoji; + /* Location structure */ bool has_location; Location location; } Data; @@ -703,7 +698,7 @@ extern "C" { #define User_init_default {"", "", "", {0}, _HardwareModel_MIN, 0, 0, 0, 0} #define RouteDiscovery_init_default {0, {0, 0, 0, 0, 0, 0, 0, 0}} #define Routing_init_default {0, {RouteDiscovery_init_default}} -#define Data_init_default {_PortNum_MIN, 0, {{0, {0}}}, 0, 0, 0, 0, 0, 0, false, Location_init_default} +#define Data_init_default {_PortNum_MIN, {0, {0}}, 0, 0, 0, 0, 0, 0, false, Location_init_default} #define Location_init_default {0, 0, 0, 0, 0} #define MeshPacket_init_default {0, 0, 0, 0, {Data_init_default}, 0, 0, 0, 0, 0, _MeshPacket_Priority_MIN, 0, _MeshPacket_Delayed_MIN} #define NodeInfo_init_default {0, false, User_init_default, false, Position_init_default, 0, 0, false, DeviceMetrics_init_default} @@ -717,7 +712,7 @@ extern "C" { #define User_init_zero {"", "", "", {0}, _HardwareModel_MIN, 0, 0, 0, 0} #define RouteDiscovery_init_zero {0, {0, 0, 0, 0, 0, 0, 0, 0}} #define Routing_init_zero {0, {RouteDiscovery_init_zero}} -#define Data_init_zero {_PortNum_MIN, 0, {{0, {0}}}, 0, 0, 0, 0, 0, 0, false, Location_init_zero} +#define Data_init_zero {_PortNum_MIN, {0, {0}}, 0, 0, 0, 0, 0, 0, false, Location_init_zero} #define Location_init_zero {0, 0, 0, 0, 0} #define MeshPacket_init_zero {0, 0, 0, 0, {Data_init_zero}, 0, 0, 0, 0, 0, _MeshPacket_Priority_MIN, 0, _MeshPacket_Delayed_MIN} #define NodeInfo_init_zero {0, false, User_init_zero, false, Position_init_zero, 0, 0, false, DeviceMetrics_init_zero} @@ -792,7 +787,6 @@ extern "C" { #define User_ant_azimuth_tag 12 #define Data_portnum_tag 1 #define Data_payload_tag 2 -#define Data_payload_compressed_tag 10 #define Data_want_response_tag 3 #define Data_dest_tag 4 #define Data_source_tag 5 @@ -890,15 +884,14 @@ X(a, STATIC, ONEOF, UENUM, (variant,error_reason,error_reason), 3) #define Data_FIELDLIST(X, a) \ X(a, STATIC, SINGULAR, UENUM, portnum, 1) \ -X(a, STATIC, ONEOF, BYTES, (payloadVariant,payload,payload), 2) \ +X(a, STATIC, SINGULAR, BYTES, payload, 2) \ X(a, STATIC, SINGULAR, BOOL, want_response, 3) \ X(a, STATIC, SINGULAR, FIXED32, dest, 4) \ X(a, STATIC, SINGULAR, FIXED32, source, 5) \ X(a, STATIC, SINGULAR, FIXED32, request_id, 6) \ X(a, STATIC, SINGULAR, FIXED32, reply_id, 7) \ X(a, STATIC, SINGULAR, FIXED32, emoji, 8) \ -X(a, STATIC, OPTIONAL, MESSAGE, location, 9) \ -X(a, STATIC, ONEOF, BYTES, (payloadVariant,payload_compressed,payload_compressed), 10) +X(a, STATIC, OPTIONAL, MESSAGE, location, 9) #define Data_CALLBACK NULL #define Data_DEFAULT NULL #define Data_location_MSGTYPE Location diff --git a/src/mesh/generated/module_config.pb.h b/src/mesh/generated/module_config.pb.h index 9ec763e6..f68b1655 100644 --- a/src/mesh/generated/module_config.pb.h +++ b/src/mesh/generated/module_config.pb.h @@ -13,21 +13,21 @@ /* Enum definitions */ typedef enum _ModuleConfig_SerialConfig_Serial_Baud { ModuleConfig_SerialConfig_Serial_Baud_BAUD_Default = 0, - ModuleConfig_SerialConfig_Serial_Baud_BAUD_2400 = 1, - ModuleConfig_SerialConfig_Serial_Baud_BAUD_4800 = 2, - ModuleConfig_SerialConfig_Serial_Baud_BAUD_9600 = 3, - ModuleConfig_SerialConfig_Serial_Baud_BAUD_19200 = 4, - ModuleConfig_SerialConfig_Serial_Baud_BAUD_38400 = 5, - ModuleConfig_SerialConfig_Serial_Baud_BAUD_57600 = 6, - ModuleConfig_SerialConfig_Serial_Baud_BAUD_115200 = 7, - ModuleConfig_SerialConfig_Serial_Baud_BAUD_230400 = 8, - ModuleConfig_SerialConfig_Serial_Baud_BAUD_460800 = 9, - ModuleConfig_SerialConfig_Serial_Baud_BAUD_576000 = 10, - ModuleConfig_SerialConfig_Serial_Baud_BAUD_921600 = 11, - ModuleConfig_SerialConfig_Serial_Baud_BAUD_110 = 12, - ModuleConfig_SerialConfig_Serial_Baud_BAUD_300 = 13, - ModuleConfig_SerialConfig_Serial_Baud_BAUD_600 = 14, - ModuleConfig_SerialConfig_Serial_Baud_BAUD_1200 = 15 + ModuleConfig_SerialConfig_Serial_Baud_BAUD_110 = 1, + ModuleConfig_SerialConfig_Serial_Baud_BAUD_300 = 2, + ModuleConfig_SerialConfig_Serial_Baud_BAUD_600 = 3, + ModuleConfig_SerialConfig_Serial_Baud_BAUD_1200 = 4, + ModuleConfig_SerialConfig_Serial_Baud_BAUD_2400 = 5, + ModuleConfig_SerialConfig_Serial_Baud_BAUD_4800 = 6, + ModuleConfig_SerialConfig_Serial_Baud_BAUD_9600 = 7, + ModuleConfig_SerialConfig_Serial_Baud_BAUD_19200 = 8, + ModuleConfig_SerialConfig_Serial_Baud_BAUD_38400 = 9, + ModuleConfig_SerialConfig_Serial_Baud_BAUD_57600 = 10, + ModuleConfig_SerialConfig_Serial_Baud_BAUD_115200 = 11, + ModuleConfig_SerialConfig_Serial_Baud_BAUD_230400 = 12, + ModuleConfig_SerialConfig_Serial_Baud_BAUD_460800 = 13, + ModuleConfig_SerialConfig_Serial_Baud_BAUD_576000 = 14, + ModuleConfig_SerialConfig_Serial_Baud_BAUD_921600 = 15 } ModuleConfig_SerialConfig_Serial_Baud; typedef enum _ModuleConfig_SerialConfig_Serial_Mode { @@ -133,8 +133,8 @@ typedef struct _ModuleConfig { /* Helper constants for enums */ #define _ModuleConfig_SerialConfig_Serial_Baud_MIN ModuleConfig_SerialConfig_Serial_Baud_BAUD_Default -#define _ModuleConfig_SerialConfig_Serial_Baud_MAX ModuleConfig_SerialConfig_Serial_Baud_BAUD_1200 -#define _ModuleConfig_SerialConfig_Serial_Baud_ARRAYSIZE ((ModuleConfig_SerialConfig_Serial_Baud)(ModuleConfig_SerialConfig_Serial_Baud_BAUD_1200+1)) +#define _ModuleConfig_SerialConfig_Serial_Baud_MAX ModuleConfig_SerialConfig_Serial_Baud_BAUD_921600 +#define _ModuleConfig_SerialConfig_Serial_Baud_ARRAYSIZE ((ModuleConfig_SerialConfig_Serial_Baud)(ModuleConfig_SerialConfig_Serial_Baud_BAUD_921600+1)) #define _ModuleConfig_SerialConfig_Serial_Mode_MIN ModuleConfig_SerialConfig_Serial_Mode_MODE_Default #define _ModuleConfig_SerialConfig_Serial_Mode_MAX ModuleConfig_SerialConfig_Serial_Mode_MODE_PROTO diff --git a/src/mesh/generated/portnums.pb.h b/src/mesh/generated/portnums.pb.h index f824ce76..b4fd3738 100644 --- a/src/mesh/generated/portnums.pb.h +++ b/src/mesh/generated/portnums.pb.h @@ -46,6 +46,8 @@ typedef enum _PortNum { /* Admin control packets. Payload is a [AdminMessage](/docs/developers/protobufs/api#adminmessage) message */ PortNum_ADMIN_APP = 6, + /* Compressed TEXT_MESSAGE payloads. */ + PortNum_TEXT_MESSAGE_COMPRESSED_APP = 7, /* Provides a 'ping' service that replies to any packet it receives. Also serves as a small example module. */ PortNum_REPLY_APP = 32, @@ -69,8 +71,6 @@ typedef enum _PortNum { Maintained by Github user a-f-G-U-C (a Meshtastic contributor) Project files at https://github.com/a-f-G-U-C/Meshtastic-ZPS */ PortNum_ZPS_APP = 68, - /* Compressed payloads. */ - PortNum_COMPRESSION_APP = 69, /* Private applications should use portnums >= 256. To simplify initial development and testing you can use "PRIVATE_APP" in your code without needing to rebuild protobuf files (via [regen-protos.sh](https://github.com/meshtastic/Meshtastic-device/blob/master/bin/regen-protos.sh)) */ From 2631a9324eda53fadc6184ff6eba945f632ed38a Mon Sep 17 00:00:00 2001 From: Jm Casler Date: Mon, 23 May 2022 22:06:38 -0700 Subject: [PATCH 4/6] Update from radioConfig.preferences to config.lora --- src/mesh/RadioLibInterface.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/mesh/RadioLibInterface.cpp b/src/mesh/RadioLibInterface.cpp index 8603d06b..96dcb948 100644 --- a/src/mesh/RadioLibInterface.cpp +++ b/src/mesh/RadioLibInterface.cpp @@ -101,7 +101,7 @@ ErrorCode RadioLibInterface::send(MeshPacket *p) if (radioConfig.preferences.region != RegionCode_Unset) { - if (disabled || radioConfig.preferences.is_lora_tx_disabled) { + if (disabled || config.lora.tx_disabled) { DEBUG_MSG("send - lora_tx_disabled\n"); packetPool.release(p); return ERRNO_DISABLED; @@ -115,7 +115,7 @@ ErrorCode RadioLibInterface::send(MeshPacket *p) #else - if (disabled || radioConfig.preferences.is_lora_tx_disabled) { + if (disabled || config.lora.tx_disabled) { DEBUG_MSG("send - lora_tx_disabled\n"); packetPool.release(p); return ERRNO_DISABLED; From f3c15eb6cc2d71bc5f9838b910509fe1df226284 Mon Sep 17 00:00:00 2001 From: Jm Casler Date: Tue, 24 May 2022 17:42:46 -0700 Subject: [PATCH 5/6] Completed compression. Tested between two devices. --- src/configuration.h | 2 +- src/mesh/PhoneAPI.cpp | 3 -- src/mesh/Router.cpp | 58 +++++++++++---------------- src/modules/esp32/RangeTestModule.cpp | 6 +-- 4 files changed, 28 insertions(+), 41 deletions(-) diff --git a/src/configuration.h b/src/configuration.h index 49c7fd4d..1f4864f8 100644 --- a/src/configuration.h +++ b/src/configuration.h @@ -144,7 +144,7 @@ along with this program. If not, see . //#define DISABLE_NTP // Disable the welcome screen and allow - #define DISABLE_WELCOME_UNSET +//#define DISABLE_WELCOME_UNSET // ----------------------------------------------------------------------------- // OLED & Input diff --git a/src/mesh/PhoneAPI.cpp b/src/mesh/PhoneAPI.cpp index cc40ff72..033ef490 100644 --- a/src/mesh/PhoneAPI.cpp +++ b/src/mesh/PhoneAPI.cpp @@ -179,9 +179,6 @@ size_t PhoneAPI::getFromRadio(uint8_t *buf) // Encapsulate as a FromRadio packet fromRadioScratch.which_payloadVariant = FromRadio_packet_tag; fromRadioScratch.packet = *packetForPhone; - - // TODO: Remove with compression rework - fromRadioScratch.packet.decoded.which_payloadVariant = Data_payload_tag; } releasePhonePacket(); break; diff --git a/src/mesh/Router.cpp b/src/mesh/Router.cpp index 2546dbe8..d7dd7afc 100644 --- a/src/mesh/Router.cpp +++ b/src/mesh/Router.cpp @@ -275,7 +275,7 @@ void Router::sniffReceived(const MeshPacket *p, const Routing *c) bool perhapsDecode(MeshPacket *p) { - //DEBUG_MSG("\n\n** perhapsDecode payloadVariant - %d\n\n", p->which_payloadVariant); + // DEBUG_MSG("\n\n** perhapsDecode payloadVariant - %d\n\n", p->which_payloadVariant); if (p->which_payloadVariant == MeshPacket_decoded_tag) return true; // If packet was already decoded just return @@ -307,27 +307,31 @@ bool perhapsDecode(MeshPacket *p) p->which_payloadVariant = MeshPacket_decoded_tag; // change type to decoded p->channel = chIndex; // change to store the index instead of the hash + /* if (p->decoded.portnum == PortNum_TEXT_MESSAGE_APP) { - DEBUG_MSG("\n\n** TEXT_MESSAGE_APP payloadVariant - %d\n\n", p->decoded.which_payloadVariant); + DEBUG_MSG("\n\n** TEXT_MESSAGE_APP\n"); + } else if (p->decoded.portnum == PortNum_TEXT_MESSAGE_COMPRESSED_APP) { + DEBUG_MSG("\n\n** PortNum_TEXT_MESSAGE_COMPRESSED_APP\n"); } + */ + // Decompress if needed. jm - if (p->decoded.portnum == PortNum_TEXT_MESSAGE_APP) { - if (p->decoded.which_payloadVariant == Data_payload_compressed_tag) { - // Decompress the payload - char compressed_in[Constants_DATA_PAYLOAD_LEN] = {}; - char decompressed_out[Constants_DATA_PAYLOAD_LEN] = {}; - int decompressed_len; + if (p->decoded.portnum == PortNum_TEXT_MESSAGE_COMPRESSED_APP) { + // Decompress the payload + char compressed_in[Constants_DATA_PAYLOAD_LEN] = {}; + char decompressed_out[Constants_DATA_PAYLOAD_LEN] = {}; + int decompressed_len; - memcpy(compressed_in, p->decoded.payload.bytes, p->decoded.payload.size); + memcpy(compressed_in, p->decoded.payload.bytes, p->decoded.payload.size); - decompressed_len = unishox2_decompress_simple(compressed_in, p->decoded.payload.size, decompressed_out); + decompressed_len = unishox2_decompress_simple(compressed_in, p->decoded.payload.size, decompressed_out); - DEBUG_MSG("\n\n**\n\nDecompressed length - %d \n", decompressed_len); - // Serial.println(p->decoded.payload.bytes); - Serial.println(decompressed_out); + // DEBUG_MSG("\n\n**\n\nDecompressed length - %d \n", decompressed_len); - p->decoded.which_payloadVariant = Data_payload_tag; - } + memcpy(p->decoded.payload.bytes, decompressed_out, decompressed_len); + + // Switch the port from PortNum_TEXT_MESSAGE_COMPRESSED_APP to PortNum_TEXT_MESSAGE_APP + p->decoded.portnum = PortNum_TEXT_MESSAGE_APP; } printPacket("decoded message", p); @@ -364,38 +368,24 @@ Routing_Error perhapsEncode(MeshPacket *p) DEBUG_MSG("Original length - %d \n", p->decoded.payload.size); DEBUG_MSG("Compressed length - %d \n", compressed_len); - - // Serial.println(compressed_out); + DEBUG_MSG("Original message - %s \n", p->decoded.payload.bytes); // If the compressed length is greater than or equal to the original size, don't use the compressed form if (compressed_len >= p->decoded.payload.size) { DEBUG_MSG("Not using compressing message.\n"); // Set the uncompressed payload varient anyway. Shouldn't hurt? - p->decoded.which_payloadVariant = Data_payload_tag; + // p->decoded.which_payloadVariant = Data_payload_tag; // Otherwise we use the compressor } else { DEBUG_MSG("Using compressed message.\n"); // Copy the compressed data into the meshpacket - - p->decoded.payload_compressed.size = compressed_len; - memcpy(p->decoded.payload_compressed.bytes, compressed_out, compressed_len); - DEBUG_MSG("\n\n** TEXT_MESSAGE_APP payloadVariant - %d\n\n", p->decoded.which_payloadVariant); - p->decoded.which_payloadVariant = Data_payload_compressed_tag; - DEBUG_MSG("\n\n** TEXT_MESSAGE_APP payloadVariant - %d\n\n", p->decoded.which_payloadVariant); - } + p->decoded.payload.size = compressed_len; + memcpy(p->decoded.payload.bytes, compressed_out, compressed_len); - if (0) { - char decompressed_out[Constants_DATA_PAYLOAD_LEN] = {}; - int decompressed_len; - - decompressed_len = unishox2_decompress_simple(compressed_out, compressed_len, decompressed_out); - - Serial.print("Decompressed length - "); - Serial.println(decompressed_len); - Serial.println(decompressed_out); + p->decoded.portnum = PortNum_TEXT_MESSAGE_COMPRESSED_APP; } } diff --git a/src/modules/esp32/RangeTestModule.cpp b/src/modules/esp32/RangeTestModule.cpp index 71b1bbb5..1a871e81 100644 --- a/src/modules/esp32/RangeTestModule.cpp +++ b/src/modules/esp32/RangeTestModule.cpp @@ -36,8 +36,8 @@ int32_t RangeTestModule::runOnce() without having to configure it from the PythonAPI or WebUI. */ - // moduleConfig.range_test.enabled = 1; - // moduleConfig.range_test.sender = 45; + //moduleConfig.range_test.enabled = 1; + //moduleConfig.range_test.sender = 30; // moduleConfig.range_test.save = 1; // Fixed position is useful when testing indoors. @@ -115,7 +115,7 @@ void RangeTestModuleRadio::sendPayload(NodeNum dest, bool wantReplies) packetSequence++; - static char heartbeatString[20]; + static char heartbeatString[MAX_RHPACKETLEN]; snprintf(heartbeatString, sizeof(heartbeatString), "seq %u", packetSequence); p->decoded.payload.size = strlen(heartbeatString); // You must specify how many bytes are in the reply From c5f3cad0f9878718b770f0f775709032edcde9e6 Mon Sep 17 00:00:00 2001 From: Jm Casler Date: Tue, 24 May 2022 18:06:53 -0700 Subject: [PATCH 6/6] Fixed missing brackets --- src/mesh/RadioLibInterface.cpp | 490 +++++++++++++++++---------------- 1 file changed, 246 insertions(+), 244 deletions(-) diff --git a/src/mesh/RadioLibInterface.cpp b/src/mesh/RadioLibInterface.cpp index 96dcb948..c665ea35 100644 --- a/src/mesh/RadioLibInterface.cpp +++ b/src/mesh/RadioLibInterface.cpp @@ -93,24 +93,25 @@ bool RadioLibInterface::canSendImmediately() /// bluetooth comms code. If the txmit queue is empty it might return an error ErrorCode RadioLibInterface::send(MeshPacket *p) { - + #ifndef DISABLE_WELCOME_UNSET - + if (config.lora.region != Config_LoRaConfig_RegionCode_Unset) { if (disabled || config.lora.tx_disabled) { + if (config.lora.region != Config_LoRaConfig_RegionCode_Unset) { + if (disabled || config.lora.tx_disabled) { + DEBUG_MSG("send - lora_tx_disabled\n"); + packetPool.release(p); + return ERRNO_DISABLED; + } - if (radioConfig.preferences.region != RegionCode_Unset) { - if (disabled || config.lora.tx_disabled) { - DEBUG_MSG("send - lora_tx_disabled\n"); - packetPool.release(p); - return ERRNO_DISABLED; + } else { + DEBUG_MSG("send - lora_tx_disabled because RegionCode_Unset\n"); + packetPool.release(p); + return ERRNO_DISABLED; + } } - - } else { - DEBUG_MSG("send - lora_tx_disabled because RegionCode_Unset\n"); - packetPool.release(p); - return ERRNO_DISABLED; } #else @@ -123,265 +124,266 @@ ErrorCode RadioLibInterface::send(MeshPacket *p) #endif - // Sometimes when testing it is useful to be able to never turn on the xmitter + // Sometimes when testing it is useful to be able to never turn on the xmitter #ifndef LORA_DISABLE_SENDING - printPacket("enqueuing for send", p); + printPacket("enqueuing for send", p); - DEBUG_MSG("txGood=%d,rxGood=%d,rxBad=%d\n", txGood, rxGood, rxBad); - ErrorCode res = txQueue.enqueue(p) ? ERRNO_OK : ERRNO_UNKNOWN; + DEBUG_MSG("txGood=%d,rxGood=%d,rxBad=%d\n", txGood, rxGood, rxBad); + ErrorCode res = txQueue.enqueue(p) ? ERRNO_OK : ERRNO_UNKNOWN; + + if (res != ERRNO_OK) { // we weren't able to queue it, so we must drop it to prevent leaks + packetPool.release(p); + return res; + } + + // set (random) transmit delay to let others reconfigure their radio, + // to avoid collisions and implement timing-based flooding + // DEBUG_MSG("Set random delay before transmitting.\n"); + setTransmitDelay(); - if (res != ERRNO_OK) { // we weren't able to queue it, so we must drop it to prevent leaks - packetPool.release(p); return res; - } - - // set (random) transmit delay to let others reconfigure their radio, - // to avoid collisions and implement timing-based flooding - // DEBUG_MSG("Set random delay before transmitting.\n"); - setTransmitDelay(); - - return res; #else packetPool.release(p); return ERRNO_DISABLED; #endif -} + } -bool RadioLibInterface::canSleep() -{ - bool res = txQueue.empty(); - if (!res) // only print debug messages if we are vetoing sleep - DEBUG_MSG("radio wait to sleep, txEmpty=%d\n", res); + bool RadioLibInterface::canSleep() + { + bool res = txQueue.empty(); + if (!res) // only print debug messages if we are vetoing sleep + DEBUG_MSG("radio wait to sleep, txEmpty=%d\n", res); - return res; -} + return res; + } -/** Attempt to cancel a previously sent packet. Returns true if a packet was found we could cancel */ -bool RadioLibInterface::cancelSending(NodeNum from, PacketId id) -{ - auto p = txQueue.remove(from, id); - if (p) - packetPool.release(p); // free the packet we just removed + /** Attempt to cancel a previously sent packet. Returns true if a packet was found we could cancel */ + bool RadioLibInterface::cancelSending(NodeNum from, PacketId id) + { + auto p = txQueue.remove(from, id); + if (p) + packetPool.release(p); // free the packet we just removed - bool result = (p != NULL); - DEBUG_MSG("cancelSending id=0x%x, removed=%d\n", id, result); - return result; -} + bool result = (p != NULL); + DEBUG_MSG("cancelSending id=0x%x, removed=%d\n", id, result); + return result; + } -/** radio helper thread callback. + /** radio helper thread callback. -We never immediately transmit after any operation (either rx or tx). Instead we should start receiving and -wait a random delay of 100ms to 100ms+shortPacketMsec to make sure we are not stomping on someone else. The 100ms delay at the -beginning ensures all possible listeners have had time to finish processing the previous packet and now have their radio in RX -state. The up to 100ms+shortPacketMsec random delay gives a chance for all possible senders to have high odds of detecting that -someone else started transmitting first and then they will wait until that packet finishes. + We never immediately transmit after any operation (either rx or tx). Instead we should start receiving and + wait a random delay of 100ms to 100ms+shortPacketMsec to make sure we are not stomping on someone else. The 100ms delay + at the beginning ensures all possible listeners have had time to finish processing the previous packet and now have their + radio in RX state. The up to 100ms+shortPacketMsec random delay gives a chance for all possible senders to have high odds + of detecting that someone else started transmitting first and then they will wait until that packet finishes. -NOTE: the large flood rebroadcast delay might still be needed even with this approach. Because we might not be able to hear other -transmitters that we are potentially stomping on. Requires further thought. + NOTE: the large flood rebroadcast delay might still be needed even with this approach. Because we might not be able to + hear other transmitters that we are potentially stomping on. Requires further thought. -FIXME, the MIN_TX_WAIT_MSEC and MAX_TX_WAIT_MSEC values should be tuned via logic analyzer later. -*/ -void RadioLibInterface::onNotify(uint32_t notification) -{ - switch (notification) { - case ISR_TX: - handleTransmitInterrupt(); - startReceive(); - // DEBUG_MSG("tx complete - starting timer\n"); - startTransmitTimer(); - break; - case ISR_RX: - handleReceiveInterrupt(); - startReceive(); - // DEBUG_MSG("rx complete - starting timer\n"); - startTransmitTimer(); - break; - case TRANSMIT_DELAY_COMPLETED: - // DEBUG_MSG("delay done\n"); + FIXME, the MIN_TX_WAIT_MSEC and MAX_TX_WAIT_MSEC values should be tuned via logic analyzer later. + */ + void RadioLibInterface::onNotify(uint32_t notification) + { + switch (notification) { + case ISR_TX: + handleTransmitInterrupt(); + startReceive(); + // DEBUG_MSG("tx complete - starting timer\n"); + startTransmitTimer(); + break; + case ISR_RX: + handleReceiveInterrupt(); + startReceive(); + // DEBUG_MSG("rx complete - starting timer\n"); + startTransmitTimer(); + break; + case TRANSMIT_DELAY_COMPLETED: + // DEBUG_MSG("delay done\n"); - // If we are not currently in receive mode, then restart the random delay (this can happen if the main thread - // has placed the unit into standby) FIXME, how will this work if the chipset is in sleep mode? - if (!txQueue.empty()) { - if (!canSendImmediately()) { - // DEBUG_MSG("Currently Rx/Tx-ing: set random delay\n"); - setTransmitDelay(); // currently Rx/Tx-ing: reset random delay - } else { - if (isChannelActive()) { // check if there is currently a LoRa packet on the channel - // DEBUG_MSG("Channel is active: set random delay\n"); - setTransmitDelay(); // reset random delay + // If we are not currently in receive mode, then restart the random delay (this can happen if the main thread + // has placed the unit into standby) FIXME, how will this work if the chipset is in sleep mode? + if (!txQueue.empty()) { + if (!canSendImmediately()) { + // DEBUG_MSG("Currently Rx/Tx-ing: set random delay\n"); + setTransmitDelay(); // currently Rx/Tx-ing: reset random delay } else { - // Send any outgoing packets we have ready - MeshPacket *txp = txQueue.dequeue(); - assert(txp); - startSend(txp); + if (isChannelActive()) { // check if there is currently a LoRa packet on the channel + // DEBUG_MSG("Channel is active: set random delay\n"); + setTransmitDelay(); // reset random delay + } else { + // Send any outgoing packets we have ready + MeshPacket *txp = txQueue.dequeue(); + assert(txp); + startSend(txp); - // Packet has been sent, count it toward our TX airtime utilization. - uint32_t xmitMsec = getPacketTime(txp); - airTime->logAirtime(TX_LOG, xmitMsec); + // Packet has been sent, count it toward our TX airtime utilization. + uint32_t xmitMsec = getPacketTime(txp); + airTime->logAirtime(TX_LOG, xmitMsec); + } } + } else { + // DEBUG_MSG("done with txqueue\n"); } - } else { - // DEBUG_MSG("done with txqueue\n"); - } - break; - default: - assert(0); // We expected to receive a valid notification from the ISR - } -} - -void RadioLibInterface::setTransmitDelay() -{ - MeshPacket *p = txQueue.getFront(); - // We want all sending/receiving to be done by our daemon thread. - // We use a delay here because this packet might have been sent in response to a packet we just received. - // So we want to make sure the other side has had a chance to reconfigure its radio. - - /* We assume if rx_snr = 0 and rx_rssi = 0, the packet was generated locally. - * This assumption is valid because of the offset generated by the radio to account for the noise - * floor. - */ - if (p->rx_snr == 0 && p->rx_rssi == 0) { - startTransmitTimer(true); - } else { - // If there is a SNR, start a timer scaled based on that SNR. - DEBUG_MSG("rx_snr found. hop_limit:%d rx_snr:%f\n", p->hop_limit, p->rx_snr); - startTransmitTimerSNR(p->rx_snr); - } -} - -void RadioLibInterface::startTransmitTimer(bool withDelay) -{ - // If we have work to do and the timer wasn't already scheduled, schedule it now - if (!txQueue.empty()) { - uint32_t delay = !withDelay ? 1 : getTxDelayMsec(); - // DEBUG_MSG("xmit timer %d\n", delay); - notifyLater(delay, TRANSMIT_DELAY_COMPLETED, false); // This will implicitly enable - } -} - -void RadioLibInterface::startTransmitTimerSNR(float snr) -{ - // If we have work to do and the timer wasn't already scheduled, schedule it now - if (!txQueue.empty()) { - uint32_t delay = getTxDelayMsecWeighted(snr); - // DEBUG_MSG("xmit timer %d\n", delay); - notifyLater(delay, TRANSMIT_DELAY_COMPLETED, false); // This will implicitly enable - } -} - -void RadioLibInterface::handleTransmitInterrupt() -{ - // DEBUG_MSG("handling lora TX interrupt\n"); - // This can be null if we forced the device to enter standby mode. In that case - // ignore the transmit interrupt - if (sendingPacket) - completeSending(); -} - -void RadioLibInterface::completeSending() -{ - // We are careful to clear sending packet before calling printPacket because - // that can take a long time - auto p = sendingPacket; - sendingPacket = NULL; - - if (p) { - txGood++; - printPacket("Completed sending", p); - - // We are done sending that packet, release it - packetPool.release(p); - // DEBUG_MSG("Done with send\n"); - } -} - -void RadioLibInterface::handleReceiveInterrupt() -{ - uint32_t xmitMsec; - assert(isReceiving); - isReceiving = false; - - // read the number of actually received bytes - size_t length = iface->getPacketLength(); - - xmitMsec = getPacketTime(length); - - int state = iface->readData(radiobuf, length); - if (state != ERR_NONE) { - DEBUG_MSG("ignoring received packet due to error=%d\n", state); - rxBad++; - - airTime->logAirtime(RX_ALL_LOG, xmitMsec); - - } else { - // Skip the 4 headers that are at the beginning of the rxBuf - int32_t payloadLen = length - sizeof(PacketHeader); - const uint8_t *payload = radiobuf + sizeof(PacketHeader); - - // check for short packets - if (payloadLen < 0) { - DEBUG_MSG("ignoring received packet too short\n"); - rxBad++; - airTime->logAirtime(RX_ALL_LOG, xmitMsec); - } else { - const PacketHeader *h = (PacketHeader *)radiobuf; - - rxGood++; - - // Note: we deliver _all_ packets to our router (i.e. our interface is intentionally promiscuous). - // This allows the router and other apps on our node to sniff packets (usually routing) between other - // nodes. - MeshPacket *mp = packetPool.allocZeroed(); - - mp->from = h->from; - mp->to = h->to; - mp->id = h->id; - mp->channel = h->channel; - assert(HOP_MAX <= PACKET_FLAGS_HOP_MASK); // If hopmax changes, carefully check this code - mp->hop_limit = h->flags & PACKET_FLAGS_HOP_MASK; - mp->want_ack = !!(h->flags & PACKET_FLAGS_WANT_ACK_MASK); - - addReceiveMetadata(mp); - - mp->which_payloadVariant = MeshPacket_encrypted_tag; // Mark that the payload is still encrypted at this point - assert(((uint32_t)payloadLen) <= sizeof(mp->encrypted.bytes)); - memcpy(mp->encrypted.bytes, payload, payloadLen); - mp->encrypted.size = payloadLen; - - printPacket("Lora RX", mp); - - // xmitMsec = getPacketTime(mp); - airTime->logAirtime(RX_LOG, xmitMsec); - - deliverToReceiver(mp); + break; + default: + assert(0); // We expected to receive a valid notification from the ISR } } -} -/** start an immediate transmit */ -void RadioLibInterface::startSend(MeshPacket *txp) -{ - printPacket("Starting low level send", txp); - if (disabled || config.lora.tx_disabled) { - DEBUG_MSG("startSend is dropping tx packet because we are disabled\n"); - packetPool.release(txp); - } else { - setStandby(); // Cancel any already in process receives + void RadioLibInterface::setTransmitDelay() + { + MeshPacket *p = txQueue.getFront(); + // We want all sending/receiving to be done by our daemon thread. + // We use a delay here because this packet might have been sent in response to a packet we just received. + // So we want to make sure the other side has had a chance to reconfigure its radio. - configHardwareForSend(); // must be after setStandby + /* We assume if rx_snr = 0 and rx_rssi = 0, the packet was generated locally. + * This assumption is valid because of the offset generated by the radio to account for the noise + * floor. + */ + if (p->rx_snr == 0 && p->rx_rssi == 0) { + startTransmitTimer(true); + } else { + // If there is a SNR, start a timer scaled based on that SNR. + DEBUG_MSG("rx_snr found. hop_limit:%d rx_snr:%f\n", p->hop_limit, p->rx_snr); + startTransmitTimerSNR(p->rx_snr); + } + } - size_t numbytes = beginSending(txp); + void RadioLibInterface::startTransmitTimer(bool withDelay) + { + // If we have work to do and the timer wasn't already scheduled, schedule it now + if (!txQueue.empty()) { + uint32_t delay = !withDelay ? 1 : getTxDelayMsec(); + // DEBUG_MSG("xmit timer %d\n", delay); + notifyLater(delay, TRANSMIT_DELAY_COMPLETED, false); // This will implicitly enable + } + } - int res = iface->startTransmit(radiobuf, numbytes); - if (res != ERR_NONE) { - RECORD_CRITICALERROR(CriticalErrorCode_RadioSpiBug); + void RadioLibInterface::startTransmitTimerSNR(float snr) + { + // If we have work to do and the timer wasn't already scheduled, schedule it now + if (!txQueue.empty()) { + uint32_t delay = getTxDelayMsecWeighted(snr); + // DEBUG_MSG("xmit timer %d\n", delay); + notifyLater(delay, TRANSMIT_DELAY_COMPLETED, false); // This will implicitly enable + } + } - // This send failed, but make sure to 'complete' it properly + void RadioLibInterface::handleTransmitInterrupt() + { + // DEBUG_MSG("handling lora TX interrupt\n"); + // This can be null if we forced the device to enter standby mode. In that case + // ignore the transmit interrupt + if (sendingPacket) completeSending(); - startReceive(); // Restart receive mode (because startTransmit failed to put us in xmit mode) - } - - // Must be done AFTER, starting transmit, because startTransmit clears (possibly stale) interrupt pending register bits - enableInterrupt(isrTxLevel0); } -} + + void RadioLibInterface::completeSending() + { + // We are careful to clear sending packet before calling printPacket because + // that can take a long time + auto p = sendingPacket; + sendingPacket = NULL; + + if (p) { + txGood++; + printPacket("Completed sending", p); + + // We are done sending that packet, release it + packetPool.release(p); + // DEBUG_MSG("Done with send\n"); + } + } + + void RadioLibInterface::handleReceiveInterrupt() + { + uint32_t xmitMsec; + assert(isReceiving); + isReceiving = false; + + // read the number of actually received bytes + size_t length = iface->getPacketLength(); + + xmitMsec = getPacketTime(length); + + int state = iface->readData(radiobuf, length); + if (state != ERR_NONE) { + DEBUG_MSG("ignoring received packet due to error=%d\n", state); + rxBad++; + + airTime->logAirtime(RX_ALL_LOG, xmitMsec); + + } else { + // Skip the 4 headers that are at the beginning of the rxBuf + int32_t payloadLen = length - sizeof(PacketHeader); + const uint8_t *payload = radiobuf + sizeof(PacketHeader); + + // check for short packets + if (payloadLen < 0) { + DEBUG_MSG("ignoring received packet too short\n"); + rxBad++; + airTime->logAirtime(RX_ALL_LOG, xmitMsec); + } else { + const PacketHeader *h = (PacketHeader *)radiobuf; + + rxGood++; + + // Note: we deliver _all_ packets to our router (i.e. our interface is intentionally promiscuous). + // This allows the router and other apps on our node to sniff packets (usually routing) between other + // nodes. + MeshPacket *mp = packetPool.allocZeroed(); + + mp->from = h->from; + mp->to = h->to; + mp->id = h->id; + mp->channel = h->channel; + assert(HOP_MAX <= PACKET_FLAGS_HOP_MASK); // If hopmax changes, carefully check this code + mp->hop_limit = h->flags & PACKET_FLAGS_HOP_MASK; + mp->want_ack = !!(h->flags & PACKET_FLAGS_WANT_ACK_MASK); + + addReceiveMetadata(mp); + + mp->which_payloadVariant = MeshPacket_encrypted_tag; // Mark that the payload is still encrypted at this point + assert(((uint32_t)payloadLen) <= sizeof(mp->encrypted.bytes)); + memcpy(mp->encrypted.bytes, payload, payloadLen); + mp->encrypted.size = payloadLen; + + printPacket("Lora RX", mp); + + // xmitMsec = getPacketTime(mp); + airTime->logAirtime(RX_LOG, xmitMsec); + + deliverToReceiver(mp); + } + } + } + + /** start an immediate transmit */ + void RadioLibInterface::startSend(MeshPacket * txp) + { + printPacket("Starting low level send", txp); + if (disabled || config.lora.tx_disabled) { + DEBUG_MSG("startSend is dropping tx packet because we are disabled\n"); + packetPool.release(txp); + } else { + setStandby(); // Cancel any already in process receives + + configHardwareForSend(); // must be after setStandby + + size_t numbytes = beginSending(txp); + + int res = iface->startTransmit(radiobuf, numbytes); + if (res != ERR_NONE) { + RECORD_CRITICALERROR(CriticalErrorCode_RadioSpiBug); + + // This send failed, but make sure to 'complete' it properly + completeSending(); + startReceive(); // Restart receive mode (because startTransmit failed to put us in xmit mode) + } + + // Must be done AFTER, starting transmit, because startTransmit clears (possibly stale) interrupt pending register + // bits + enableInterrupt(isrTxLevel0); + } + }