From 13889124c1d5655c30f46d3bea6fc9c2e0588eeb Mon Sep 17 00:00:00 2001 From: Tim Gunter Date: Sun, 21 Mar 2021 18:29:20 -0700 Subject: [PATCH 01/16] Add option to set python interpreter used for device-install.sh and device-update.sh --- bin/device-install.sh | 19 ++++++++++++------- bin/device-update.sh | 15 ++++++++++----- 2 files changed, 22 insertions(+), 12 deletions(-) diff --git a/bin/device-install.sh b/bin/device-install.sh index 3d5f27af8..0adfc575d 100755 --- a/bin/device-install.sh +++ b/bin/device-install.sh @@ -1,21 +1,24 @@ #!/bin/sh +PYTHON=${PYTHON:-python} + set -e # Usage info show_help() { cat << EOF -Usage: ${0##*/} [-h] [-p ESPTOOL_PORT] [-f FILENAME] +Usage: ${0##*/} [-h] [-p ESPTOOL_PORT] [-P PYTHON] [-f FILENAME] Flash image file to device, but first erasing and writing system information" -h Display this help and exit -p ESPTOOL_PORT Set the environment variable for ESPTOOL_PORT. If not set, ESPTOOL iterates all ports (Dangerrous). - -f FILENAME The .bin file to flash. Custom to your device type and region. + -P PYTHON Specify alternate python interpreter to use to invoke esptool. (Default: "$PYTHON") + -f FILENAME The .bin file to flash. Custom to your device type and region. EOF } -while getopts ":h:p:f:" opt; do +while getopts ":hp:P:f:" opt; do case "${opt}" in h) show_help @@ -23,6 +26,8 @@ while getopts ":h:p:f:" opt; do ;; p) export ESPTOOL_PORT=${OPTARG} ;; + P) PYTHON=${OPTARG} + ;; f) FILENAME=${OPTARG} ;; *) @@ -36,10 +41,10 @@ shift "$((OPTIND-1))" if [ -f "${FILENAME}" ]; then echo "Trying to flash ${FILENAME}, but first erasing and writing system information" - esptool.py --baud 921600 erase_flash - esptool.py --baud 921600 write_flash 0x1000 system-info.bin - esptool.py --baud 921600 write_flash 0x00390000 spiffs-*.bin - esptool.py --baud 921600 write_flash 0x10000 ${FILENAME} + $PYTHON -m esptool --baud 921600 erase_flash + $PYTHON -m esptool --baud 921600 write_flash 0x1000 system-info.bin + $PYTHON -m esptool --baud 921600 write_flash 0x00390000 spiffs-*.bin + $PYTHON -m esptool --baud 921600 write_flash 0x10000 ${FILENAME} else echo "Invalid file: ${FILENAME}" show_help diff --git a/bin/device-update.sh b/bin/device-update.sh index 894230364..ec0e839d7 100755 --- a/bin/device-update.sh +++ b/bin/device-update.sh @@ -1,19 +1,22 @@ #!/bin/sh +PYTHON=${PYTHON:-python} + # Usage info show_help() { cat << EOF -Usage: ${0##*/} [-h] [-p ESPTOOL_PORT] -f FILENAME +Usage: ${0##*/} [-h] [-p ESPTOOL_PORT] [-P PYTHON] -f FILENAME Flash image file to device, leave existing system intact." -h Display this help and exit -p ESPTOOL_PORT Set the environment variable for ESPTOOL_PORT. If not set, ESPTOOL iterates all ports (Dangerrous). - -f FILENAME The .bin file to flash. Custom to your device type and region. + -P PYTHON Specify alternate python interpreter to use to invoke esptool. (Default: "$PYTHON") + -f FILENAME The .bin file to flash. Custom to your device type and region. EOF } -while getopts ":h:p:f:" opt; do +while getopts ":hp:P:f:" opt; do case "${opt}" in h) show_help @@ -21,6 +24,8 @@ while getopts ":h:p:f:" opt; do ;; p) export ESPTOOL_PORT=${OPTARG} ;; + P) PYTHON=${OPTARG} + ;; f) FILENAME=${OPTARG} ;; *) @@ -34,9 +39,9 @@ shift "$((OPTIND-1))" if [ -f "${FILENAME}" ]; then echo "Trying to flash update ${FILENAME}." - esptool.py --baud 921600 write_flash 0x10000 ${FILENAME} + $PYTHON -m esptool --baud 921600 write_flash 0x10000 ${FILENAME} echo "Erasing the otadata partition, which will turn off flash flippy-flop and force the first image to be used" - esptool.py --baud 921600 erase_region 0xe000 0x2000 + $PYTHON -m esptool --baud 921600 erase_region 0xe000 0x2000 else echo "Invalid file: ${FILENAME}" show_help From b9fd726c1403e3ba30852e6bc0574dd7cffc3a47 Mon Sep 17 00:00:00 2001 From: Vadim Furman Date: Mon, 22 Mar 2021 19:39:49 -0700 Subject: [PATCH 02/16] Push RSSI to the phone --- src/mesh/SX1262Interface.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/mesh/SX1262Interface.cpp b/src/mesh/SX1262Interface.cpp index 7d4046731..ad35d700d 100644 --- a/src/mesh/SX1262Interface.cpp +++ b/src/mesh/SX1262Interface.cpp @@ -142,6 +142,7 @@ void SX1262Interface::addReceiveMetadata(MeshPacket *mp) { // DEBUG_MSG("PacketStatus %x\n", lora.getPacketStatus()); mp->rx_snr = lora.getSNR(); + mp->rx_rssi = lround(lora.getRSSI()); } /** We override to turn on transmitter power as needed. From d32386a02732efb6dd340b6bc3fa8962fd6e7aa0 Mon Sep 17 00:00:00 2001 From: Kevin Hester Date: Tue, 23 Mar 2021 11:44:51 +0800 Subject: [PATCH 03/16] Return errors for unauthorized requests or out of bound channel nums --- docs/software/TODO.md | 6 +++- proto | 2 +- src/mesh/MeshPlugin.cpp | 50 +++++++++++++++++++++++++++--- src/mesh/MeshPlugin.h | 6 ++++ src/mesh/generated/deviceonly.pb.h | 2 +- src/mesh/generated/mesh.pb.h | 23 ++++++++------ src/plugins/AdminPlugin.cpp | 16 +++++++--- src/plugins/RoutingPlugin.cpp | 23 +++----------- 8 files changed, 88 insertions(+), 40 deletions(-) diff --git a/docs/software/TODO.md b/docs/software/TODO.md index aff62aaa8..0ffeffb99 100644 --- a/docs/software/TODO.md +++ b/docs/software/TODO.md @@ -4,8 +4,12 @@ You probably don't care about this section - skip to the next one. ## before next release -* document how to do remote admin +* changing channels requires a reboot to take effect https://github.com/meshtastic/Meshtastic-device/issues/752 +* add UI in android app to reset to defaults https://github.com/meshtastic/Meshtastic-Android/issues/263 +* bug report with remote info request timing out * firmware OTA updates of is_router true nodes fails? +* move remote admin doc from forum into git +* ask for a documentation czar * DONE timestamps on oled screen are wrong - don't seem to be updating based on message rx (actually: this is expected behavior when no node on the mesh has GPS time) * DONE add ch-del * DONE channel hash suffixes are wrong on android diff --git a/proto b/proto index b8c0499f2..8a39bac88 160000 --- a/proto +++ b/proto @@ -1 +1 @@ -Subproject commit b8c0499f28f9673d1df17d04da562e30703f01cb +Subproject commit 8a39bac88206a8aa9305ac380d150946c1796ac5 diff --git a/src/mesh/MeshPlugin.cpp b/src/mesh/MeshPlugin.cpp index 99bf7643a..1d64cd281 100644 --- a/src/mesh/MeshPlugin.cpp +++ b/src/mesh/MeshPlugin.cpp @@ -31,6 +31,39 @@ MeshPlugin::~MeshPlugin() assert(0); // FIXME - remove from list of plugins once someone needs this feature } +MeshPacket *MeshPlugin::allocAckNak(Routing_Error err, NodeNum to, PacketId idFrom, ChannelIndex chIndex) +{ + Routing c = Routing_init_default; + + c.error_reason = err; + + // Now that we have moded sendAckNak up one level into the class heirarchy we can no longer assume we are a RoutingPlugin + // So we manually call pb_encode_to_bytes and specify routing port number + // auto p = allocDataProtobuf(c); + MeshPacket *p = router->allocForSending(); + p->decoded.portnum = PortNum_ROUTING_APP; + p->decoded.payload.size = pb_encode_to_bytes(p->decoded.payload.bytes, sizeof(p->decoded.payload.bytes), Routing_fields, &c); + + p->priority = MeshPacket_Priority_ACK; + + p->hop_limit = 0; // Assume just immediate neighbors for now + p->to = to; + p->decoded.request_id = idFrom; + p->channel = chIndex; + DEBUG_MSG("Alloc an err=%d,to=0x%x,idFrom=0x%x,id=0x%x\n", err, to, idFrom, p->id); + + return p; +} + +MeshPacket *MeshPlugin::allocErrorResponse(Routing_Error err, const MeshPacket *p) +{ + auto r = allocAckNak(err, getFrom(p), p->id, p->channel); + + setReplyTo(r, *p); + + return r; +} + void MeshPlugin::callPlugins(const MeshPacket &mp) { // DEBUG_MSG("In call plugins\n"); @@ -56,9 +89,17 @@ void MeshPlugin::callPlugins(const MeshPacket &mp) bool rxChannelOk = !pi.boundChannel || (mp.from == 0) || (strcmp(ch.settings.name, pi.boundChannel) == 0); /// We only call plugins that are interested in the packet (and the message is destined to us or we are promiscious) - bool wantsPacket = rxChannelOk && (pi.isPromiscuous || toUs) && pi.wantPacket(&mp); - // DEBUG_MSG("Plugin %s wantsPacket=%d\n", pi.name, wantsPacket); - if (wantsPacket) { + if (!rxChannelOk && toUs) { + // no one should have already replied! + assert(!currentReply); + + if (mp.decoded.want_response) { + DEBUG_MSG("packet on wrong channel, returning error\n"); + currentReply = pi.allocErrorResponse(Routing_Error_NOT_AUTHORIZED, &mp); + } else + DEBUG_MSG("packet on wrong channel, but client didn't want response\n"); + } else if ((pi.isPromiscuous || toUs) && pi.wantPacket(&mp)) { + // DEBUG_MSG("Plugin %s wantsPacket=%d\n", pi.name, wantsPacket); pluginFound = true; bool handled = pi.handleReceived(mp); @@ -90,8 +131,7 @@ void MeshPlugin::callPlugins(const MeshPacket &mp) DEBUG_MSG("Sending response\n"); service.sendToMesh(currentReply); currentReply = NULL; - } - else { + } else { // No one wanted to reply to this requst, tell the requster that happened DEBUG_MSG("No one responded, send a nak\n"); routingPlugin->sendAckNak(Routing_Error_NO_RESPONSE, getFrom(&mp), mp.id, mp.channel); diff --git a/src/mesh/MeshPlugin.h b/src/mesh/MeshPlugin.h index 2295d1224..23a9cf754 100644 --- a/src/mesh/MeshPlugin.h +++ b/src/mesh/MeshPlugin.h @@ -1,5 +1,6 @@ #pragma once +#include "mesh/Channels.h" #include "mesh/MeshTypes.h" #include @@ -90,6 +91,11 @@ class MeshPlugin */ virtual bool wantUIFrame() { return false; } + MeshPacket *allocAckNak(Routing_Error err, NodeNum to, PacketId idFrom, ChannelIndex chIndex); + + /// Send an error response for the specified packet. + MeshPacket *allocErrorResponse(Routing_Error err, const MeshPacket *p); + private: /** * If any of the current chain of plugins has already sent a reply, it will be here. This is useful to allow diff --git a/src/mesh/generated/deviceonly.pb.h b/src/mesh/generated/deviceonly.pb.h index a325dfc50..ababb0e0c 100644 --- a/src/mesh/generated/deviceonly.pb.h +++ b/src/mesh/generated/deviceonly.pb.h @@ -125,7 +125,7 @@ extern const pb_msgdesc_t ChannelFile_msg; /* Maximum encoded size of messages (where known) */ #define LegacyRadioConfig_size 4 #define LegacyRadioConfig_LegacyPreferences_size 2 -#define DeviceState_size 4898 +#define DeviceState_size 4920 #define ChannelFile_size 832 #ifdef __cplusplus diff --git a/src/mesh/generated/mesh.pb.h b/src/mesh/generated/mesh.pb.h index 84bdbbb43..8ef7f16f7 100644 --- a/src/mesh/generated/mesh.pb.h +++ b/src/mesh/generated/mesh.pb.h @@ -57,7 +57,9 @@ typedef enum _Routing_Error { Routing_Error_MAX_RETRANSMIT = 5, Routing_Error_NO_CHANNEL = 6, Routing_Error_TOO_LARGE = 7, - Routing_Error_NO_RESPONSE = 8 + Routing_Error_NO_RESPONSE = 8, + Routing_Error_BAD_REQUEST = 32, + Routing_Error_NOT_AUTHORIZED = 33 } Routing_Error; typedef enum _MeshPacket_Priority { @@ -150,6 +152,7 @@ typedef struct _MeshPacket { uint8_t hop_limit; bool want_ack; MeshPacket_Priority priority; + int32_t rx_rssi; } MeshPacket; typedef struct _NodeInfo { @@ -206,8 +209,8 @@ typedef struct _ToRadio { #define _CriticalErrorCode_ARRAYSIZE ((CriticalErrorCode)(CriticalErrorCode_Brownout+1)) #define _Routing_Error_MIN Routing_Error_NONE -#define _Routing_Error_MAX Routing_Error_NO_RESPONSE -#define _Routing_Error_ARRAYSIZE ((Routing_Error)(Routing_Error_NO_RESPONSE+1)) +#define _Routing_Error_MAX Routing_Error_NOT_AUTHORIZED +#define _Routing_Error_ARRAYSIZE ((Routing_Error)(Routing_Error_NOT_AUTHORIZED+1)) #define _MeshPacket_Priority_MIN MeshPacket_Priority_UNSET #define _MeshPacket_Priority_MAX MeshPacket_Priority_MAX @@ -228,7 +231,7 @@ extern "C" { #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} -#define MeshPacket_init_default {0, 0, 0, 0, {Data_init_default}, 0, 0, 0, 0, 0, _MeshPacket_Priority_MIN} +#define MeshPacket_init_default {0, 0, 0, 0, {Data_init_default}, 0, 0, 0, 0, 0, _MeshPacket_Priority_MIN, 0} #define NodeInfo_init_default {0, false, User_init_default, false, Position_init_default, 0} #define MyNodeInfo_init_default {0, 0, 0, "", "", "", _CriticalErrorCode_MIN, 0, 0, 0, 0, 0} #define LogRecord_init_default {"", 0, "", _LogRecord_Level_MIN} @@ -239,7 +242,7 @@ extern "C" { #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} -#define MeshPacket_init_zero {0, 0, 0, 0, {Data_init_zero}, 0, 0, 0, 0, 0, _MeshPacket_Priority_MIN} +#define MeshPacket_init_zero {0, 0, 0, 0, {Data_init_zero}, 0, 0, 0, 0, 0, _MeshPacket_Priority_MIN, 0} #define NodeInfo_init_zero {0, false, User_init_zero, false, Position_init_zero, 0} #define MyNodeInfo_init_zero {0, 0, 0, "", "", "", _CriticalErrorCode_MIN, 0, 0, 0, 0, 0} #define LogRecord_init_zero {"", 0, "", _LogRecord_Level_MIN} @@ -291,6 +294,7 @@ extern "C" { #define MeshPacket_hop_limit_tag 10 #define MeshPacket_want_ack_tag 11 #define MeshPacket_priority_tag 12 +#define MeshPacket_rx_rssi_tag 13 #define NodeInfo_num_tag 1 #define NodeInfo_user_tag 2 #define NodeInfo_position_tag 3 @@ -362,7 +366,8 @@ X(a, STATIC, SINGULAR, FIXED32, rx_time, 7) \ X(a, STATIC, SINGULAR, FLOAT, rx_snr, 8) \ X(a, STATIC, SINGULAR, UINT32, hop_limit, 10) \ X(a, STATIC, SINGULAR, BOOL, want_ack, 11) \ -X(a, STATIC, SINGULAR, UENUM, priority, 12) +X(a, STATIC, SINGULAR, UENUM, priority, 12) \ +X(a, STATIC, SINGULAR, INT32, rx_rssi, 13) #define MeshPacket_CALLBACK NULL #define MeshPacket_DEFAULT NULL #define MeshPacket_payloadVariant_decoded_MSGTYPE Data @@ -454,12 +459,12 @@ extern const pb_msgdesc_t ToRadio_msg; #define RouteDiscovery_size 40 #define Routing_size 42 #define Data_size 260 -#define MeshPacket_size 298 +#define MeshPacket_size 309 #define NodeInfo_size 126 #define MyNodeInfo_size 89 #define LogRecord_size 81 -#define FromRadio_size 307 -#define ToRadio_size 301 +#define FromRadio_size 318 +#define ToRadio_size 312 #ifdef __cplusplus } /* extern "C" */ diff --git a/src/plugins/AdminPlugin.cpp b/src/plugins/AdminPlugin.cpp index 65c901121..ae09d5af8 100644 --- a/src/plugins/AdminPlugin.cpp +++ b/src/plugins/AdminPlugin.cpp @@ -56,13 +56,21 @@ bool AdminPlugin::handleReceivedProtobuf(const MeshPacket &mp, const AdminMessag case AdminMessage_set_channel_tag: DEBUG_MSG("Client is setting channel %d\n", r->set_channel.index); - handleSetChannel(r->set_channel); + if (r->set_channel.index < 0 || r->set_channel.index >= MAX_NUM_CHANNELS) + reply = allocErrorResponse(Routing_Error_BAD_REQUEST, &mp); + else + handleSetChannel(r->set_channel); break; - case AdminMessage_get_channel_request_tag: - DEBUG_MSG("Client is getting channel %d\n", r->get_channel_request - 1); - handleGetChannel(mp, r->get_channel_request - 1); + case AdminMessage_get_channel_request_tag: { + uint32_t i = r->get_channel_request - 1; + DEBUG_MSG("Client is getting channel %d\n", i); + if (i >= MAX_NUM_CHANNELS) + reply = allocErrorResponse(Routing_Error_BAD_REQUEST, &mp); + else + handleGetChannel(mp, i); break; + } case AdminMessage_get_radio_request_tag: DEBUG_MSG("Client is getting radio\n"); diff --git a/src/plugins/RoutingPlugin.cpp b/src/plugins/RoutingPlugin.cpp index 4fe0b88d5..e04239b27 100644 --- a/src/plugins/RoutingPlugin.cpp +++ b/src/plugins/RoutingPlugin.cpp @@ -18,17 +18,16 @@ bool RoutingPlugin::handleReceivedProtobuf(const MeshPacket &mp, const Routing * printPacket("Delivering rx packet", &mp); service.handleFromRadio(&mp); } - + return false; // Let others look at this message also if they want } - MeshPacket *RoutingPlugin::allocReply() { assert(currentRequest); // We only consider making replies if the request was a legit routing packet (not just something we were sniffing) - if(currentRequest->decoded.portnum == PortNum_ROUTING_APP) { + if (currentRequest->decoded.portnum == PortNum_ROUTING_APP) { assert(0); // 1.2 refactoring fixme, Not sure if anything needs this yet? // return allocDataProtobuf(u); } @@ -37,26 +36,12 @@ MeshPacket *RoutingPlugin::allocReply() void RoutingPlugin::sendAckNak(Routing_Error err, NodeNum to, PacketId idFrom, ChannelIndex chIndex) { - Routing c = Routing_init_default; - - c.error_reason = err; - - auto p = allocDataProtobuf(c); - p->priority = MeshPacket_Priority_ACK; - - p->hop_limit = 0; // Assume just immediate neighbors for now - p->to = to; - p->decoded.request_id = idFrom; - p->channel = chIndex; - DEBUG_MSG("Sending an err=%d,to=0x%x,idFrom=0x%x,id=0x%x\n", err, to, idFrom, p->id); + auto p = allocAckNak(err, to, idFrom, chIndex); router->sendLocal(p); // we sometimes send directly to the local node } -RoutingPlugin::RoutingPlugin() - : ProtobufPlugin("routing", PortNum_ROUTING_APP, Routing_fields) +RoutingPlugin::RoutingPlugin() : ProtobufPlugin("routing", PortNum_ROUTING_APP, Routing_fields) { isPromiscuous = true; } - - From 1fcec8ce3b25748693e2e733a427deb3fb6456c8 Mon Sep 17 00:00:00 2001 From: Kevin Hester Date: Tue, 23 Mar 2021 11:54:53 +0800 Subject: [PATCH 04/16] always fix up channel list, even if we just did factory reset --- proto | 2 +- src/mesh/Channels.cpp | 12 ++++++------ src/mesh/NodeDB.cpp | 15 +++++++++------ src/plugins/AdminPlugin.cpp | 2 +- 4 files changed, 17 insertions(+), 14 deletions(-) diff --git a/proto b/proto index 8a39bac88..820fa497d 160000 --- a/proto +++ b/proto @@ -1 +1 @@ -Subproject commit 8a39bac88206a8aa9305ac380d150946c1796ac5 +Subproject commit 820fa497dfde07e129cad6955bf2f4b2b9cecebc diff --git a/src/mesh/Channels.cpp b/src/mesh/Channels.cpp index 135ff4329..1e49d9319 100644 --- a/src/mesh/Channels.cpp +++ b/src/mesh/Channels.cpp @@ -33,7 +33,7 @@ int16_t Channels::generateHash(ChannelIndex channelNum) return -1; // invalid else { const char *name = getName(channelNum); - uint8_t h = xorHash((const uint8_t *) name, strlen(name)); + uint8_t h = xorHash((const uint8_t *)name, strlen(name)); h ^= xorHash(k.bytes, k.length); @@ -184,7 +184,7 @@ void Channels::onConfigChanged() Channel &Channels::getByIndex(ChannelIndex chIndex) { - assert(chIndex < channelFile.channels_count); + assert(chIndex < channelFile.channels_count); // This should be equal to MAX_NUM_CHANNELS Channel *ch = channelFile.channels + chIndex; return *ch; } @@ -278,11 +278,11 @@ const char *Channels::getPrimaryName() */ bool Channels::decryptForHash(ChannelIndex chIndex, ChannelHash channelHash) { - if(chIndex > getNumChannels() || getHash(chIndex) != channelHash) { - // DEBUG_MSG("Skipping channel %d (hash %x) due to invalid hash/index, want=%x\n", chIndex, getHash(chIndex), channelHash); + if (chIndex > getNumChannels() || getHash(chIndex) != channelHash) { + // DEBUG_MSG("Skipping channel %d (hash %x) due to invalid hash/index, want=%x\n", chIndex, getHash(chIndex), + // channelHash); return false; - } - else { + } else { DEBUG_MSG("Using channel %d (hash 0x%x)\n", chIndex, channelHash); setCrypto(chIndex); return true; diff --git a/src/mesh/NodeDB.cpp b/src/mesh/NodeDB.cpp index 35f54fa72..37e1e9f02 100644 --- a/src/mesh/NodeDB.cpp +++ b/src/mesh/NodeDB.cpp @@ -86,7 +86,9 @@ bool NodeDB::resetRadioConfig() DEBUG_MSG("Performing factory reset!\n"); installDefaultDeviceState(); didFactoryReset = true; - } else if (channelFile.channels_count == 0) { + } + + if (channelFile.channels_count != MAX_NUM_CHANNELS) { DEBUG_MSG("Setting default channel and radio preferences!\n"); channels.initDefaults(); @@ -206,12 +208,13 @@ void NodeDB::init() // removed from 1.2 (though we do use old values if found) // We set these _after_ loading from disk - because they come from the build and are more trusted than // what is stored in flash - //if (xstr(HW_VERSION)[0]) + // if (xstr(HW_VERSION)[0]) // strncpy(myNodeInfo.region, optstr(HW_VERSION), sizeof(myNodeInfo.region)); - // else DEBUG_MSG("This build does not specify a HW_VERSION\n"); // Eventually new builds will no longer include this build flag + // else DEBUG_MSG("This build does not specify a HW_VERSION\n"); // Eventually new builds will no longer include this build + // flag // DEBUG_MSG("legacy region %d\n", devicestate.legacyRadio.preferences.region); - if(radioConfig.preferences.region == RegionCode_Unset) + if (radioConfig.preferences.region == RegionCode_Unset) radioConfig.preferences.region = devicestate.legacyRadio.preferences.region; // Check for the old style of region code strings, if found, convert to the new enum. @@ -226,7 +229,7 @@ void NodeDB::init() } strncpy(myNodeInfo.firmware_version, optstr(APP_VERSION), sizeof(myNodeInfo.firmware_version)); - + // hw_model is no longer stored in myNodeInfo (as of 1.2.11) - we now store it as an enum in nodeinfo myNodeInfo.hw_model_deprecated[0] = '\0'; // strncpy(myNodeInfo.hw_model, HW_VENDOR, sizeof(myNodeInfo.hw_model)); @@ -373,7 +376,7 @@ void NodeDB::saveChannelsToDisk() FS.mkdir("/prefs"); #endif saveProto(channelfile, ChannelFile_size, sizeof(ChannelFile), ChannelFile_fields, &channelFile); - } + } } void NodeDB::saveToDisk() diff --git a/src/plugins/AdminPlugin.cpp b/src/plugins/AdminPlugin.cpp index ae09d5af8..69e96875c 100644 --- a/src/plugins/AdminPlugin.cpp +++ b/src/plugins/AdminPlugin.cpp @@ -64,7 +64,7 @@ bool AdminPlugin::handleReceivedProtobuf(const MeshPacket &mp, const AdminMessag case AdminMessage_get_channel_request_tag: { uint32_t i = r->get_channel_request - 1; - DEBUG_MSG("Client is getting channel %d\n", i); + DEBUG_MSG("Client is getting channel %u\n", i); if (i >= MAX_NUM_CHANNELS) reply = allocErrorResponse(Routing_Error_BAD_REQUEST, &mp); else From 49b16fdf0c63d87671699f3df716003faabcd12a Mon Sep 17 00:00:00 2001 From: Kevin Hester Date: Tue, 23 Mar 2021 12:07:04 +0800 Subject: [PATCH 05/16] fix channel !authorized check --- src/mesh/MeshPlugin.cpp | 60 ++++++++++++++++++++++------------------- 1 file changed, 33 insertions(+), 27 deletions(-) diff --git a/src/mesh/MeshPlugin.cpp b/src/mesh/MeshPlugin.cpp index 1d64cd281..2081def6e 100644 --- a/src/mesh/MeshPlugin.cpp +++ b/src/mesh/MeshPlugin.cpp @@ -85,41 +85,47 @@ void MeshPlugin::callPlugins(const MeshPacket &mp) auto ch = channels.getByIndex(mp.channel); assert(ch.has_settings); - /// Is the channel this packet arrived on acceptable? (security check) - bool rxChannelOk = !pi.boundChannel || (mp.from == 0) || (strcmp(ch.settings.name, pi.boundChannel) == 0); - /// We only call plugins that are interested in the packet (and the message is destined to us or we are promiscious) - if (!rxChannelOk && toUs) { - // no one should have already replied! - assert(!currentReply); + bool wantsPacket = (pi.isPromiscuous || toUs) && pi.wantPacket(&mp); - if (mp.decoded.want_response) { - DEBUG_MSG("packet on wrong channel, returning error\n"); - currentReply = pi.allocErrorResponse(Routing_Error_NOT_AUTHORIZED, &mp); - } else - DEBUG_MSG("packet on wrong channel, but client didn't want response\n"); - } else if ((pi.isPromiscuous || toUs) && pi.wantPacket(&mp)) { + if (wantsPacket) { // DEBUG_MSG("Plugin %s wantsPacket=%d\n", pi.name, wantsPacket); pluginFound = true; - bool handled = pi.handleReceived(mp); + /// Is the channel this packet arrived on acceptable? (security check) + bool rxChannelOk = !pi.boundChannel || (mp.from == 0) || (strcmp(ch.settings.name, pi.boundChannel) == 0); - // Possibly send replies (but only if the message was directed to us specifically, i.e. not for promiscious sniffing) - // also: we only let the one plugin send a reply, once that happens, remaining plugins are not considered + if (!rxChannelOk) { + // no one should have already replied! + assert(!currentReply); - // NOTE: we send a reply *even if the (non broadcast) request was from us* which is unfortunate but necessary because - // currently when the phone sends things, it sends things using the local node ID as the from address. A better - // solution (FIXME) would be to let phones have their own distinct addresses and we 'route' to them like any other - // node. - if (mp.decoded.want_response && toUs && (getFrom(&mp) != ourNodeNum || mp.to == ourNodeNum) && !currentReply) { - pi.sendResponse(mp); - DEBUG_MSG("Plugin %s sent a response\n", pi.name); + if (mp.decoded.want_response) { + DEBUG_MSG("packet on wrong channel, returning error\n"); + currentReply = pi.allocErrorResponse(Routing_Error_NOT_AUTHORIZED, &mp); + } else + DEBUG_MSG("packet on wrong channel, but client didn't want response\n"); } else { - DEBUG_MSG("Plugin %s considered\n", pi.name); - } - if (handled) { - DEBUG_MSG("Plugin %s handled and skipped other processing\n", pi.name); - break; + + bool handled = pi.handleReceived(mp); + + // Possibly send replies (but only if the message was directed to us specifically, i.e. not for promiscious + // sniffing) also: we only let the one plugin send a reply, once that happens, remaining plugins are not + // considered + + // NOTE: we send a reply *even if the (non broadcast) request was from us* which is unfortunate but necessary + // because currently when the phone sends things, it sends things using the local node ID as the from address. A + // better solution (FIXME) would be to let phones have their own distinct addresses and we 'route' to them like + // any other node. + if (mp.decoded.want_response && toUs && (getFrom(&mp) != ourNodeNum || mp.to == ourNodeNum) && !currentReply) { + pi.sendResponse(mp); + DEBUG_MSG("Plugin %s sent a response\n", pi.name); + } else { + DEBUG_MSG("Plugin %s considered\n", pi.name); + } + if (handled) { + DEBUG_MSG("Plugin %s handled and skipped other processing\n", pi.name); + break; + } } } From 9e0a2964a43c82d77394f3a5165a5dfadadf260e Mon Sep 17 00:00:00 2001 From: Kevin Hester Date: Tue, 23 Mar 2021 12:16:23 +0800 Subject: [PATCH 06/16] move channel docs into git --- docs/software/TODO.md | 2 +- docs/software/channels.md | 17 +++++ docs/software/remote-admin.md | 121 ++++++++++++++++++++++++++++++++++ docs/software/sw-design.md | 2 + 4 files changed, 141 insertions(+), 1 deletion(-) create mode 100644 docs/software/channels.md create mode 100644 docs/software/remote-admin.md diff --git a/docs/software/TODO.md b/docs/software/TODO.md index 0ffeffb99..e879c5779 100644 --- a/docs/software/TODO.md +++ b/docs/software/TODO.md @@ -4,7 +4,7 @@ You probably don't care about this section - skip to the next one. ## before next release -* changing channels requires a reboot to take effect https://github.com/meshtastic/Meshtastic-device/issues/752 +* TEST THIS! changing channels requires a reboot to take effect https://github.com/meshtastic/Meshtastic-device/issues/752 * add UI in android app to reset to defaults https://github.com/meshtastic/Meshtastic-Android/issues/263 * bug report with remote info request timing out * firmware OTA updates of is_router true nodes fails? diff --git a/docs/software/channels.md b/docs/software/channels.md new file mode 100644 index 000000000..49ecccb7a --- /dev/null +++ b/docs/software/channels.md @@ -0,0 +1,17 @@ +# Multiple channel support + +Version 1.2 of the software adds support for "multiple (simultaneous) channels". The idea behind this feature is that a mesh can allow multiple users/groups to be share common mesh infrastructure. Even including routing messages for others when no one except that subgroup of users has the encryption keys for their private channel. + +### What is the PRIMARY channel + +The way this works is that each node keeps a list of channels it knows about. One of those channels (normally the first 1) is labelled as the "PRIMARY" channel. The primary channel is the **only** channel that is used to set radio parameters. i.e. this channel controls things like spread factor, coding rate, bandwidth etc... Indirectly this channel also is used to select the specific frequency that all members of this mesh are talking over. + +This channel may or may not have a PSK (encryption). If you are providing mesh to 'the public' we recommend that you always leave this channel with its default psk. The default PSK is technically encrypted (and random users sniffing the ether would have to use meshtastic to decode it), but the key is included in the github source code and you should assume any 'attacker' would have it. But for a 'public' mesh you want this, because it allows anyone using meshtastic in your area to send packets through 'your' mesh. + +Note: Older meshtastic applications that don't yet understand multi-channel support will only show the user this channel. + +### How to use SECONDARY channels + +Any channel you add after that PRIMARY channel is SECONDARY. Secondary channels are used only for encyryption and (in the case of some special applications) security. If you would like to have a private channel over a more public mesh, you probably want to create a SECONDARY channel. When sharing that URL with your private group you will share the "Complete URL". The complete URL includes your secondary channel (for encryption) and the primary channel (to provide radio/mesh access). + +Secondary channels **must** have a PSK (encryption). \ No newline at end of file diff --git a/docs/software/remote-admin.md b/docs/software/remote-admin.md new file mode 100644 index 000000000..94b1107fb --- /dev/null +++ b/docs/software/remote-admin.md @@ -0,0 +1,121 @@ +# Remote node administration + +This is the first documentation for how to use the [multiple channels](channels.md) feature to enable remote adminstration of meshtastic nodes. i.e. let you talk through the mesh to some far away node and change that nodes settings. This is an advanced feature that (currently) few users would need. Also, keep in mind it is possible (if you are not careful) to assign settings to that remote node that cause it to completely drop off of your mesh. + +Btw: I promised to document how multi-channel is now used to secure remote GPIO/serial access. But probably best to debug these instructions first, so I'll wait on that. If you **do** need to use remote GPIO/serial now, just follow these instructions but name your new channel "gpio" or "serial". + +## Creating the "admin" channel + +Okay - now that we've summarized what multiple-channel support is, we can move on to using it to provide remote administrative access to a node. + +By default, nodes will **only** respond to adminstrative commands via the local USB/bluetooth/TCP interface. This provides basic security to prevent unauthorized access. This is actually how 'normal' administration and settings changes work. The only difference for the remote case is that we are sending those commands over the mesh. + +Before a node will allow remote admin access, it must find a channel +``` +meshtastic --info +Connected to radio +... +Channels: + PRIMARY psk=default { "modemConfig": "Bw125Cr48Sf4096", "psk": "AQ==" } + +Primary channel URL: https://www.meshtastic.org/d/#CgUYAyIBAQ +``` + +So from this output you see that this node knows about only one channel and that its PSK is set to the default value. + +But if you then add an admin channel (with "meshtastic --ch-add admin"). Note: the name is important it must be "admin" (sorry): + +Your channels will now look like this: +``` +meshtastic --ch-add admin +Connected to radio +Writing modified channels to device + +meshtastic --info +Connected to radio +... +Channels: + PRIMARY psk=default { "modemConfig": "Bw125Cr48Sf4096", "psk": "AQ==" } + SECONDARY psk=secret { "psk": "HW7E3nMbiNbvr6MhsDonLCmj7eSAhttzjbIx/r5OQmg=", "name": "admin" } + +Primary channel URL: https://www.meshtastic.org/d/#CgUYAyIBAQ +Complete URL (includes all channels): https://www.meshtastic.org/d/#CgUYAyIBAQopIiAdbsTecxuI1u-voyGwOicsKaPt5ICG23ONsjH-vk5CaCoFYWRtaW4 +``` + +Notice that now we have a new secondary channel. Also, the "--info" option prints out TWO URLs. The "complete URL" includes all of the channels this node understands. You should consider this URL something you should be very cautious about sharing. In the case of remote adminstration, you only need the node you want to adminster and the node you are locally connected to know this new "admin" channel. + +## Sharing the admin channel with other nodes + +I'm going to assume you've already created the admin channel on your "local node" i.e. the meshtastic node sitting on your desk at your home. But now you want to enable access on the "remote node" you want to eventually have far away from you. + +For this step you need physical access to both the nodes. + +1. Create the "admin" channel on the "local node" using the instructions above. +2. Copy the "Complete URL" someplace for permanent reference/access. +3. Connect meshtastic-python to the "remote node" over the USB port. +4. For the "remote node" type "meshtastic --seturl the-url-from-step-2". +5. Run "meshtastic --info" and confirm that the "Complete URL" is the same for both of the nodes. +6. Done! + +At this point you can take your remote node and install it far away and still be able to change any of its settings. + +## Remotely administering your node + +Now that both your local node and the remote node contain your secret admin channel key, you can do things like this: + +Get the node list from the local node. + +``` +meshtastic --nodes +Connected to radio +/----------------------------------------------------------------------------------------------------------\ +|N| User |AKA| ID | Position |Battery| SNR | LastHeard | Since | +|-+------------+---+---------+------------------------+-------+---------+-------------------+--------------| +|1|Unknown 9058|?58|!28979058|25.0382°, 121.5731°, N/A| N/A |-13.50 dB|2021-03-22 09:25:42|19 seconds ago| +\----------------------------------------------------------------------------------------------------------/ +``` + +Using the node ID from that list, send a message through the mesh telling that node to change its owner name. + +``` +meshtastic --dest \!28979058 --set-owner "Im Remote" +Connected to radio +Setting device owner to Im Remote +INFO:root:Requesting configuration from remote node (this could take a while) +``` + +And you can now confirm via the local node that the remote node has changed: + +``` +meshtastic --nodes +Connected to radio +/----------------------------------------------------------------------------------------------------\ +|N| User |AKA| ID | Position |Battery| SNR | LastHeard | Since | +|-+---------+---+---------+------------------------+-------+-------+-------------------+-------------| +|1|Im Remote|IR |!28979058|25.0382°, 121.5731°, N/A| N/A |8.75 dB|2021-03-22 09:35:42|3 minutes ago| +\----------------------------------------------------------------------------------------------------/ +``` + +Note: you can change **any** parameter, add channels or get info from the remote node. Here's an example of setting ls_secs and printing the complete device info from the remote node. + +``` +meshtastic --dest \!28979058 --set ls_secs 301 --info +Connected to radio +INFO:root:Requesting configuration from remote node (this could take a while) +Set ls_secs to 301 +Writing modified preferences to device + + +Preferences: { "lsSecs": 301, "region": "TW" } + +Channels: + PRIMARY psk=default { "modemConfig": "Bw125Cr48Sf4096", "psk": "AQ==" } + SECONDARY psk=secret { "psk": "HW7E3nMbiNbvr6MhsDonLCmj7eSAhttzjbIx/r5OQmg=", "name": "admin" } + +Primary channel URL: https://www.meshtastic.org/d/#CgUYAyIBAQ +Complete URL (includes all channels): https://www.meshtastic.org/d/#CgUYAyIBAQopIiAdbsTecxuI1u-voyGwOicsKaPt5ICG23ONsjH-vk5CaCoFYWRtaW4 +``` + +## Areas for future development + +In the future we will add a "deadman timer" to this feature so that the remote node will revert any changes if you fail to send a special "commit changes" command. This will protect against sending bad settings to nodes that you can't physically access. Instead if the node does not receive a commit message within 10 minutes it will revert all changes and (hopefully) rejoin the mesh. \ No newline at end of file diff --git a/docs/software/sw-design.md b/docs/software/sw-design.md index 4f715dec5..eeaab7f18 100644 --- a/docs/software/sw-design.md +++ b/docs/software/sw-design.md @@ -6,5 +6,7 @@ This is a mini design doc for developing the meshtastic software. * Our [project board](https://github.com/orgs/meshtastic/projects/1) - shows what things we are currently working on and remaining work items for the current release. * [Power Management](power.md) * [Mesh algorithm](mesh-alg.md) +* [Channels](channels.md) - documentation on how multiple simultaneous channels are used +* [Remote adminstration](remote-admin.md) * [External client API](device-api.md) and porting guide for new clients (iOS, python, etc...) * TODO: how to port the device code to a new device. From 049e7913827f6d3319e3b38e1e5ca71e2ef313a3 Mon Sep 17 00:00:00 2001 From: Vadim Furman Date: Mon, 22 Mar 2021 21:36:24 -0700 Subject: [PATCH 07/16] Updated proto --- proto | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proto b/proto index 94bd0aae4..820fa497d 160000 --- a/proto +++ b/proto @@ -1 +1 @@ -Subproject commit 94bd0aae44e2c16c7776289225c804100c856cd4 +Subproject commit 820fa497dfde07e129cad6955bf2f4b2b9cecebc From 7b4f8fb6d679f513a6ab653ba6c1e07db494cc3f Mon Sep 17 00:00:00 2001 From: Kevin Hester Date: Tue, 23 Mar 2021 14:44:50 +0800 Subject: [PATCH 08/16] Fix firmware OTA update while is_router --- docs/software/TODO.md | 10 ++++++---- src/PowerFSM.cpp | 12 +++++++++--- src/PowerFSM.h | 1 + src/esp32/BluetoothSoftwareUpdate.cpp | 15 +++++++-------- 4 files changed, 23 insertions(+), 15 deletions(-) diff --git a/docs/software/TODO.md b/docs/software/TODO.md index e879c5779..57c99e507 100644 --- a/docs/software/TODO.md +++ b/docs/software/TODO.md @@ -4,12 +4,14 @@ You probably don't care about this section - skip to the next one. ## before next release -* TEST THIS! changing channels requires a reboot to take effect https://github.com/meshtastic/Meshtastic-device/issues/752 +* DONE firmware OTA updates of is_router true nodes fails? * add UI in android app to reset to defaults https://github.com/meshtastic/Meshtastic-Android/issues/263 +* TEST THIS! changing channels requires a reboot to take effect https://github.com/meshtastic/Meshtastic-device/issues/752 * bug report with remote info request timing out -* firmware OTA updates of is_router true nodes fails? -* move remote admin doc from forum into git -* ask for a documentation czar +* retest channel changing in android (using sim?) +* DONE move remote admin doc from forum into git +* DONE check crashlytics +* DONE ask for a documentation czar * DONE timestamps on oled screen are wrong - don't seem to be updating based on message rx (actually: this is expected behavior when no node on the mesh has GPS time) * DONE add ch-del * DONE channel hash suffixes are wrong on android diff --git a/src/PowerFSM.cpp b/src/PowerFSM.cpp index 1303329e5..86bc91639 100644 --- a/src/PowerFSM.cpp +++ b/src/PowerFSM.cpp @@ -97,7 +97,8 @@ static void lsIdle() static void lsExit() { // setGPSPower(true); // restore GPS power - if (gps) gps->forceWake(true); + if (gps) + gps->forceWake(true); } static void nbEnter() @@ -172,7 +173,7 @@ Fsm powerFSM(&stateBOOT); void PowerFSM_setup() { bool isRouter = radioConfig.preferences.is_router; - + // If we are not a router and we already have AC power go to POWER state after init, otherwise go to ON // We assume routers might be powered all the time, but from a low current (solar) source bool isLowPower = radioConfig.preferences.is_low_power || isRouter; @@ -180,7 +181,7 @@ void PowerFSM_setup() /* To determine if we're externally powered, assumptions 1) If we're powered up and there's no battery, we must be getting power externally. (because we'd be dead otherwise) - 2) If we detect USB power from the power management chip, we must be getting power externally. + 2) If we detect USB power from the power management chip, we must be getting power externally. */ bool hasPower = !isLowPower && powerStatus && (!powerStatus->getHasBattery() || powerStatus->getHasUSB()); @@ -249,6 +250,11 @@ void PowerFSM_setup() powerFSM.add_transition(&stateDARK, &stateDARK, EVENT_CONTACT_FROM_PHONE, NULL, "Contact from phone"); + // each time we get a new update packet make sure we are staying in the ON state so the screen stays awake (also we don't + // shutdown bluetooth if is_router) + powerFSM.add_transition(&stateDARK, &stateON, EVENT_FIRMWARE_UPDATE, NULL, "Got firmware update"); + powerFSM.add_transition(&stateON, &stateON, EVENT_FIRMWARE_UPDATE, NULL, "Got firmware update"); + powerFSM.add_transition(&stateNB, &stateDARK, EVENT_PACKET_FOR_PHONE, NULL, "Packet for phone"); powerFSM.add_timed_transition(&stateON, &stateDARK, getPref_screen_on_secs() * 1000, NULL, "Screen-on timeout"); diff --git a/src/PowerFSM.h b/src/PowerFSM.h index 4af62040c..e48b55d87 100644 --- a/src/PowerFSM.h +++ b/src/PowerFSM.h @@ -18,6 +18,7 @@ #define EVENT_SERIAL_DISCONNECTED 12 #define EVENT_POWER_CONNECTED 13 #define EVENT_POWER_DISCONNECTED 14 +#define EVENT_FIRMWARE_UPDATE 15 // We just received a new firmware update packet from the phone extern Fsm powerFSM; extern State statePOWER, stateSERIAL; diff --git a/src/esp32/BluetoothSoftwareUpdate.cpp b/src/esp32/BluetoothSoftwareUpdate.cpp index 33707c57a..843b14cf6 100644 --- a/src/esp32/BluetoothSoftwareUpdate.cpp +++ b/src/esp32/BluetoothSoftwareUpdate.cpp @@ -1,14 +1,14 @@ #include #include "../concurrency/LockGuard.h" +#include "../graphics/Screen.h" +#include "../main.h" #include "BluetoothSoftwareUpdate.h" +#include "NodeDB.h" #include "PowerFSM.h" #include "RadioLibInterface.h" #include "configuration.h" #include "nimble/BluetoothUtil.h" -#include "NodeDB.h" -#include "../graphics/Screen.h" -#include "../main.h" #include #include @@ -51,8 +51,8 @@ int update_size_callback(uint16_t conn_handle, uint16_t attr_handle, struct ble_ screen->startFirmwareUpdateScreen(); if (RadioLibInterface::instance) - RadioLibInterface::instance->disable(); // FIXME, nasty hack - the RF95 ISR/SPI code on ESP32 can fail while we are - // writing flash - shut the radio off during updates + RadioLibInterface::instance->disable(); // FIXME, nasty hack - the RF95 ISR/SPI code on ESP32 can fail while we + // are writing flash - shut the radio off during updates } } @@ -78,7 +78,7 @@ int update_data_callback(uint16_t conn_handle, uint16_t attr_handle, struct ble_ crc.update(data, len); Update.write(data, len); updateActualSize += len; - powerFSM.trigger(EVENT_CONTACT_FROM_PHONE); + powerFSM.trigger(EVENT_FIRMWARE_UPDATE); return 0; } @@ -107,8 +107,7 @@ int update_crc32_callback(uint16_t conn_handle, uint16_t attr_handle, struct ble if (update_region == U_SPIFFS) { DEBUG_MSG("SPIFFS updated!\n"); nodeDB.saveToDisk(); // Since we just wiped spiffs, we need to save our current state - } - else { + } else { DEBUG_MSG("Appload updated, rebooting in 5 seconds!\n"); rebootAtMsec = millis() + 5000; } From eb684aac038c0e0efd4b8ea0027b41801593d5f4 Mon Sep 17 00:00:00 2001 From: Kevin Hester Date: Tue, 23 Mar 2021 14:54:56 +0800 Subject: [PATCH 09/16] tested OTA is_router --- docs/software/TODO.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/software/TODO.md b/docs/software/TODO.md index 57c99e507..221affb3d 100644 --- a/docs/software/TODO.md +++ b/docs/software/TODO.md @@ -4,6 +4,7 @@ You probably don't care about this section - skip to the next one. ## before next release +* DONE test latest firmware update with is_router * DONE firmware OTA updates of is_router true nodes fails? * add UI in android app to reset to defaults https://github.com/meshtastic/Meshtastic-Android/issues/263 * TEST THIS! changing channels requires a reboot to take effect https://github.com/meshtastic/Meshtastic-device/issues/752 From c5973f9a554d2df4990bda8710b4680827f53738 Mon Sep 17 00:00:00 2001 From: Kevin Hester Date: Wed, 24 Mar 2021 11:41:14 +0800 Subject: [PATCH 10/16] @mc-hamster it is VERY important to not accidentally turn this in in master ;-) --- src/plugins/esp32/StoreForwardPlugin.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/plugins/esp32/StoreForwardPlugin.cpp b/src/plugins/esp32/StoreForwardPlugin.cpp index e5694caa3..8e0686825 100644 --- a/src/plugins/esp32/StoreForwardPlugin.cpp +++ b/src/plugins/esp32/StoreForwardPlugin.cpp @@ -280,9 +280,9 @@ StoreForwardPlugin::StoreForwardPlugin() Uncomment the preferences below if you want to use the plugin without having to configure it from the PythonAPI or WebUI. - */ radioConfig.preferences.store_forward_plugin_enabled = 1; radioConfig.preferences.is_router = 1; + */ if (radioConfig.preferences.store_forward_plugin_enabled) { @@ -305,7 +305,7 @@ StoreForwardPlugin::StoreForwardPlugin() DEBUG_MSG("Store & Forward Plugin - Aborting Startup.\n"); } - // Client + // Client } else { DEBUG_MSG("Initializing Store & Forward Plugin - Enabled as Client\n"); } From 78c665abb9357be936f77cd0542b8ecb46fe02c1 Mon Sep 17 00:00:00 2001 From: Kevin Hester Date: Wed, 24 Mar 2021 13:25:10 +0800 Subject: [PATCH 11/16] properly discard messages with fromradio queue is full (Rather than blocking forever) --- docs/software/TODO.md | 8 ++++---- src/mesh/Router.cpp | 39 +++++++++++++++++++++++---------------- src/mesh/TypedQueue.h | 4 +++- 3 files changed, 30 insertions(+), 21 deletions(-) diff --git a/docs/software/TODO.md b/docs/software/TODO.md index 221affb3d..2633d3bcf 100644 --- a/docs/software/TODO.md +++ b/docs/software/TODO.md @@ -6,10 +6,10 @@ You probably don't care about this section - skip to the next one. * DONE test latest firmware update with is_router * DONE firmware OTA updates of is_router true nodes fails? -* add UI in android app to reset to defaults https://github.com/meshtastic/Meshtastic-Android/issues/263 -* TEST THIS! changing channels requires a reboot to take effect https://github.com/meshtastic/Meshtastic-device/issues/752 -* bug report with remote info request timing out -* retest channel changing in android (using sim?) +* DONE add UI in android app to reset to defaults https://github.com/meshtastic/Meshtastic-Android/issues/263 +* DONE TEST THIS! changing channels requires a reboot to take effect https://github.com/meshtastic/Meshtastic-device/issues/752 +* DIBE bug report with remote info request timing out +* DONE retest channel changing in android (using sim?) * DONE move remote admin doc from forum into git * DONE check crashlytics * DONE ask for a documentation czar diff --git a/src/mesh/Router.cpp b/src/mesh/Router.cpp index 52b28eb55..fe207de1e 100644 --- a/src/mesh/Router.cpp +++ b/src/mesh/Router.cpp @@ -115,7 +115,8 @@ void Router::abortSendAndNak(Routing_Error err, MeshPacket *p) packetPool.release(p); } -void Router::setReceivedMessage() { +void Router::setReceivedMessage() +{ setInterval(0); // Run ASAP, so we can figure out our correct sleep time } @@ -123,9 +124,13 @@ ErrorCode Router::sendLocal(MeshPacket *p) { // No need to deliver externally if the destination is the local node if (p->to == nodeDB.getNodeNum()) { - printPacket("Enqueuing local", p); - fromRadioQueue.enqueue(p); - setReceivedMessage(); + if (fromRadioQueue.enqueue(p, 0)) { + printPacket("Enqueued local", p); + setReceivedMessage(); + } else { + printPacket("BUG! fromRadioQueue is full! Discarding!", p); + packetPool.release(p); + } return ERRNO_OK; } else if (!iface) { // We must be sending to remote nodes also, fail if no interface found @@ -143,9 +148,10 @@ ErrorCode Router::sendLocal(MeshPacket *p) } } -void printBytes(const char *label, const uint8_t *p, size_t numbytes) { +void printBytes(const char *label, const uint8_t *p, size_t numbytes) +{ DEBUG_MSG("%s: ", label); - for(size_t i = 0; i < numbytes; i++) + for (size_t i = 0; i < numbytes; i++) DEBUG_MSG("%02x ", p[i]); DEBUG_MSG("\n"); } @@ -189,7 +195,7 @@ ErrorCode Router::send(MeshPacket *p) return ERRNO_TOO_LARGE; } - //printBytes("plaintext", bytes, numbytes); + // printBytes("plaintext", bytes, numbytes); auto hash = channels.setActiveByIndex(p->channel); if (hash < 0) { @@ -247,19 +253,18 @@ bool Router::perhapsDecode(MeshPacket *p) rawSize); // we have to copy into a scratch buffer, because these bytes are a union with the decoded protobuf crypto->decrypt(p->from, p->id, rawSize, bytes); - //printBytes("plaintext", bytes, p->encrypted.size); + // printBytes("plaintext", bytes, p->encrypted.size); // Take those raw bytes and convert them back into a well structured protobuf we can understand - memset(&p->decoded, 0, sizeof(p->decoded)); + memset(&p->decoded, 0, sizeof(p->decoded)); if (!pb_decode_from_bytes(bytes, rawSize, Data_fields, &p->decoded)) { DEBUG_MSG("Invalid protobufs in received mesh packet (bad psk?)!\n"); - } else if(p->decoded.portnum == PortNum_UNKNOWN_APP) { + } else if (p->decoded.portnum == PortNum_UNKNOWN_APP) { DEBUG_MSG("Invalid portnum (bad psk?)!\n"); - } - else { + } else { // parsing was successful p->which_payloadVariant = MeshPacket_decoded_tag; // change type to decoded - p->channel = chIndex; // change to store the index instead of the hash + p->channel = chIndex; // change to store the index instead of the hash printPacket("decoded message", p); return true; } @@ -285,7 +290,7 @@ void Router::handleReceived(MeshPacket *p) p->rx_time = getValidTime(RTCQualityFromNet); // store the arrival timestamp for the phone // Take those raw bytes and convert them back into a well structured protobuf we can understand - bool decoded = perhapsDecode(p); + bool decoded = perhapsDecode(p); if (decoded) { // parsing was successful, queue for our recipient printPacket("handleReceived", p); @@ -293,18 +298,20 @@ void Router::handleReceived(MeshPacket *p) // call any promiscious plugins here, make a (non promisiocous) plugin for forwarding messages to phone api // sniffReceived(p); MeshPlugin::callPlugins(*p); + } else { + DEBUG_MSG("packet decoding failed\n"); } } void Router::perhapsHandleReceived(MeshPacket *p) { assert(radioConfig.has_preferences); - bool ignore = is_in_repeated(radioConfig.preferences.ignore_incoming, getFrom(p)); + bool ignore = is_in_repeated(radioConfig.preferences.ignore_incoming, p->from); if (ignore) DEBUG_MSG("Ignoring incoming message, 0x%x is in our ignore list\n", p->from); else if (ignore |= shouldFilterReceived(p)) { - // DEBUG_MSG("Incoming message was filtered 0x%x\n", p->from); + DEBUG_MSG("Incoming message was filtered 0x%x\n", p->from); } // Note: we avoid calling shouldFilterReceived if we are supposed to ignore certain nodes - because some overrides might diff --git a/src/mesh/TypedQueue.h b/src/mesh/TypedQueue.h index 0b60e6cf7..09d828a96 100644 --- a/src/mesh/TypedQueue.h +++ b/src/mesh/TypedQueue.h @@ -31,7 +31,9 @@ template class TypedQueue bool isEmpty() { return uxQueueMessagesWaiting(h) == 0; } - bool enqueue(T x, TickType_t maxWait = portMAX_DELAY) + /** euqueue a packet. Also, maxWait used to default to portMAX_DELAY, but we now want to callers to THINK about what blocking + * they want */ + bool enqueue(T x, TickType_t maxWait) { if (reader) { reader->setInterval(0); From 5b0e7c6e826761d136e74b57df79832db2e6d897 Mon Sep 17 00:00:00 2001 From: Kevin Hester Date: Wed, 24 Mar 2021 13:25:21 +0800 Subject: [PATCH 12/16] fix has_preferences init --- src/mesh/NodeDB.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/mesh/NodeDB.cpp b/src/mesh/NodeDB.cpp index 37e1e9f02..f925665e9 100644 --- a/src/mesh/NodeDB.cpp +++ b/src/mesh/NodeDB.cpp @@ -82,6 +82,7 @@ bool NodeDB::resetRadioConfig() radioGeneration++; + radioConfig.has_preferences = true; if (radioConfig.preferences.factory_reset) { DEBUG_MSG("Performing factory reset!\n"); installDefaultDeviceState(); @@ -92,8 +93,6 @@ bool NodeDB::resetRadioConfig() DEBUG_MSG("Setting default channel and radio preferences!\n"); channels.initDefaults(); - - radioConfig.has_preferences = true; } channels.onConfigChanged(); @@ -487,7 +486,7 @@ void NodeDB::updateUser(uint32_t nodeId, const User &p) /// we updateGUI and updateGUIforNode if we think our this change is big enough for a redraw void NodeDB::updateFrom(const MeshPacket &mp) { - if (mp.which_payloadVariant == MeshPacket_decoded_tag) { + if (mp.which_payloadVariant == MeshPacket_decoded_tag && mp.from) { DEBUG_MSG("Update DB node 0x%x, rx_time=%u\n", mp.from, mp.rx_time); NodeInfo *info = getOrCreateNode(getFrom(&mp)); @@ -497,7 +496,8 @@ void NodeDB::updateFrom(const MeshPacket &mp) info->position.time = mp.rx_time; } - info->snr = mp.rx_snr; // keep the most recent SNR we received for this node. + if (mp.rx_snr) + info->snr = mp.rx_snr; // keep the most recent SNR we received for this node. } } From 455d0f8d66f7b4c7feea75dbc86222fca6efdf2e Mon Sep 17 00:00:00 2001 From: Kevin Hester Date: Wed, 24 Mar 2021 13:27:18 +0800 Subject: [PATCH 13/16] 1.2.13 --- version.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version.properties b/version.properties index 565c90526..81be22bfa 100644 --- a/version.properties +++ b/version.properties @@ -1,4 +1,4 @@ [VERSION] major = 1 minor = 2 -build = 11 +build = 13 From e17fe7e075b97f6cb3c1101318faa2999c2939b9 Mon Sep 17 00:00:00 2001 From: Kevin Hester Date: Wed, 24 Mar 2021 19:24:01 +0800 Subject: [PATCH 14/16] update altitude in nodedb for received altitudes (reported by @iz1kga) --- src/mesh/NodeDB.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/mesh/NodeDB.cpp b/src/mesh/NodeDB.cpp index f925665e9..bc71dab3d 100644 --- a/src/mesh/NodeDB.cpp +++ b/src/mesh/NodeDB.cpp @@ -451,6 +451,8 @@ void NodeDB::updatePosition(uint32_t nodeId, const Position &p) info->position.latitude_i = p.latitude_i; info->position.longitude_i = p.longitude_i; } + if (p.altitude) + info->position.altitude = p.altitude; info->has_position = true; updateGUIforNode = info; notifyObservers(true); // Force an update whether or not our node counts have changed From f298c7d053f12e1eb53641d6cc50331a5d29fce2 Mon Sep 17 00:00:00 2001 From: IZ1IVA <75425638+IZ1IVA@users.noreply.github.com> Date: Thu, 25 Mar 2021 10:43:25 +0100 Subject: [PATCH 15/16] Update device-install.sh Please have a look at https://github.com/meshtastic/Meshtastic-device/issues/760 --- bin/device-install.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bin/device-install.sh b/bin/device-install.sh index 0adfc575d..3b1bd50f2 100755 --- a/bin/device-install.sh +++ b/bin/device-install.sh @@ -1,6 +1,6 @@ #!/bin/sh -PYTHON=${PYTHON:-python} +PYTHON=${PYTHON:-python3} set -e From 97a5405293c00ab8b6322cddde37be1bc457a36f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Adamski?= Date: Thu, 25 Mar 2021 21:49:06 +0100 Subject: [PATCH 16/16] captive portal for Android devices --- src/mesh/http/ContentHandler.cpp | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/mesh/http/ContentHandler.cpp b/src/mesh/http/ContentHandler.cpp index e46fdb1f7..3eefc11e2 100644 --- a/src/mesh/http/ContentHandler.cpp +++ b/src/mesh/http/ContentHandler.cpp @@ -77,7 +77,8 @@ void registerHandlers(HTTPServer *insecureServer, HTTPSServer *secureServer) ResourceNode *nodeAPIv1ToRadio = new ResourceNode("/api/v1/toradio", "PUT", &handleAPIv1ToRadio); ResourceNode *nodeAPIv1FromRadio = new ResourceNode("/api/v1/fromradio", "GET", &handleAPIv1FromRadio); - ResourceNode *nodeHotspot = new ResourceNode("/hotspot-detect.html", "GET", &handleHotspot); + ResourceNode *nodeHotspotApple = new ResourceNode("/hotspot-detect.html", "GET", &handleHotspot); + ResourceNode *nodeHotspotAndroid = new ResourceNode("/generate_204", "GET", &handleHotspot); ResourceNode *nodeFavicon = new ResourceNode("/favicon.ico", "GET", &handleFavicon); ResourceNode *nodeRoot = new ResourceNode("/", "GET", &handleRoot); ResourceNode *nodeStaticBrowse = new ResourceNode("/static", "GET", &handleStaticBrowse); @@ -96,7 +97,8 @@ void registerHandlers(HTTPServer *insecureServer, HTTPSServer *secureServer) secureServer->registerNode(nodeAPIv1ToRadioOptions); secureServer->registerNode(nodeAPIv1ToRadio); secureServer->registerNode(nodeAPIv1FromRadio); - secureServer->registerNode(nodeHotspot); + secureServer->registerNode(nodeHotspotApple); + secureServer->registerNode(nodeHotspotAndroid); secureServer->registerNode(nodeFavicon); secureServer->registerNode(nodeRoot); secureServer->registerNode(nodeStaticBrowse); @@ -117,7 +119,8 @@ void registerHandlers(HTTPServer *insecureServer, HTTPSServer *secureServer) insecureServer->registerNode(nodeAPIv1ToRadioOptions); insecureServer->registerNode(nodeAPIv1ToRadio); insecureServer->registerNode(nodeAPIv1FromRadio); - insecureServer->registerNode(nodeHotspot); + insecureServer->registerNode(nodeHotspotApple); + insecureServer->registerNode(nodeHotspotAndroid); insecureServer->registerNode(nodeFavicon); insecureServer->registerNode(nodeRoot); insecureServer->registerNode(nodeStaticBrowse);