From f5ff77c2b94c5aaea4dc049baefc158fede7dd4c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20G=C3=B6ttgens?= Date: Tue, 5 Mar 2024 14:50:52 +0100 Subject: [PATCH 01/29] Turn off certain modules not wanted in custom builds (#3337) --- src/configuration.h | 20 +++++++++++- src/modules/Modules.cpp | 72 ++++++++++++++++++++++++++++++++++------- 2 files changed, 80 insertions(+), 12 deletions(-) diff --git a/src/configuration.h b/src/configuration.h index d8b0dba5f4..03170c1c7a 100644 --- a/src/configuration.h +++ b/src/configuration.h @@ -221,4 +221,22 @@ along with this program. If not, see . #ifndef HW_VENDOR #error HW_VENDOR must be defined -#endif \ No newline at end of file +#endif + +// global switch to turn off all optional modules for a minimzed build +#ifdef MESHTASTIC_EXCLUDE_MODULES +#define MESHTASTIC_EXCLUDE_AUDIO 1 +#define MESHTASTIC_EXCLUDE_DETECTIONSENSOR 1 +#define MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR 1 +#define MESHTASTIC_EXCLUDE_EXTERNALNOTIFICATION 1 +#define MESHTASTIC_EXCLUDE_PAXCOUNTER 1 +#define MESHTASTIC_EXCLUDE_POWER_TELEMETRY 1 +#define MESHTASTIC_EXCLUDE_RANGETEST 1 +#define MESHTASTIC_EXCLUDE_REMOTEHARDWARE 1 +#define MESHTASTIC_EXCLUDE_STOREFORWARD 1 +#define MESHTASTIC_EXCLUDE_ATAK 1 +#define MESHTASTIC_EXCLUDE_CANNEDMESSAGES 1 +#define MESHTASTIC_EXCLUDE_NEIGHBORINFO 1 +#define MESHTASTIC_EXCLUDE_TRACEROUTE 1 +#define MESHTASTIC_EXCLUDE_WAYPOINT 1 +#endif diff --git a/src/modules/Modules.cpp b/src/modules/Modules.cpp index 2d45868fd8..4f0b8f2b0a 100644 --- a/src/modules/Modules.cpp +++ b/src/modules/Modules.cpp @@ -1,74 +1,112 @@ #include "configuration.h" +#if !MESHTASTIC_EXCLUDE_INPUTBROKER #include "input/InputBroker.h" #include "input/RotaryEncoderInterruptImpl1.h" #include "input/TrackballInterruptImpl1.h" #include "input/UpDownInterruptImpl1.h" #include "input/cardKbI2cImpl.h" #include "input/kbMatrixImpl.h" +#endif #include "modules/AdminModule.h" +#if !MESHTASTIC_EXCLUDE_ATAK #include "modules/AtakPluginModule.h" +#endif +#if !MESHTASTIC_EXCLUDE_CANNEDMESSAGES #include "modules/CannedMessageModule.h" +#endif +#if !MESHTASTIC_EXCLUDE_DETECTIONSENSOR #include "modules/DetectionSensorModule.h" +#endif +#if !MESHTASTIC_EXCLUDE_NEIGHBORINFO #include "modules/NeighborInfoModule.h" +#endif #include "modules/NodeInfoModule.h" #include "modules/PositionModule.h" +#if !MESHTASTIC_EXCLUDE_REMOTEHARDWARE #include "modules/RemoteHardwareModule.h" +#endif #include "modules/RoutingModule.h" #include "modules/TextMessageModule.h" +#if !MESHTASTIC_EXCLUDE_TRACEROUTE #include "modules/TraceRouteModule.h" +#endif +#if !MESHTASTIC_EXCLUDE_WAYPOINT #include "modules/WaypointModule.h" +#endif #if ARCH_PORTDUINO #include "input/LinuxInputImpl.h" #endif #if HAS_TELEMETRY #include "modules/Telemetry/DeviceTelemetry.h" #endif -#if HAS_SENSOR +#if HAS_SENSOR && !EXCLUDE_ENVIRONMENTAL_SENSOR #include "modules/Telemetry/AirQualityTelemetry.h" #include "modules/Telemetry/EnvironmentTelemetry.h" #endif -#if HAS_TELEMETRY && !defined(ARCH_PORTDUINO) +#if HAS_TELEMETRY && !defined(ARCH_PORTDUINO) && !EXCLUDE_POWER_TELEMETRY #include "modules/Telemetry/PowerTelemetry.h" #endif #ifdef ARCH_ESP32 -#ifdef USE_SX1280 +#if defined(USE_SX1280) && !MESHTASTIC_EXCLUDE_AUDIO #include "modules/esp32/AudioModule.h" #endif +#if !MESHTASTIC_EXCLUDE_PAXCOUNTER #include "modules/esp32/PaxcounterModule.h" +#endif +#if !MESHTASTIC_EXCLUDE_STOREFORWARD #include "modules/esp32/StoreForwardModule.h" #endif +#endif #if defined(ARCH_ESP32) || defined(ARCH_NRF52) || defined(ARCH_RP2040) +#if !MESHTASTIC_EXCLUDE_EXTERNALNOTIFICATION #include "modules/ExternalNotificationModule.h" +#endif +#if !MESHTASTIC_EXCLUDE_RANGETEST #include "modules/RangeTestModule.h" +#endif #if (defined(ARCH_ESP32) || defined(ARCH_NRF52) || defined(ARCH_RP2040)) && !defined(CONFIG_IDF_TARGET_ESP32S2) +#if !MESHTASTIC_EXCLUDE_SERIAL #include "modules/SerialModule.h" #endif #endif +#endif /** * Create module instances here. If you are adding a new module, you must 'new' it here (or somewhere else) */ void setupModules() { if (config.device.role != meshtastic_Config_DeviceConfig_Role_REPEATER) { -#if HAS_BUTTON || ARCH_PORTDUINO +#if (HAS_BUTTON || ARCH_PORTDUINO) && !MESHTASTIC_EXCLUDE_INPUTBROKER inputBroker = new InputBroker(); #endif adminModule = new AdminModule(); nodeInfoModule = new NodeInfoModule(); positionModule = new PositionModule(); +#if !MESHTASTIC_EXCLUDE_WAYPOINT waypointModule = new WaypointModule(); +#endif textMessageModule = new TextMessageModule(); +#if !MESHTASTIC_EXCLUDE_TRACEROUTE traceRouteModule = new TraceRouteModule(); +#endif +#if !MESHTASTIC_EXCLUDE_NEIGHBORINFO neighborInfoModule = new NeighborInfoModule(); +#endif +#if !MESHTASTIC_EXCLUDE_DETECTIONSENSOR detectionSensorModule = new DetectionSensorModule(); +#endif +#if !MESHTASTIC_EXCLUDE_ATAK atakPluginModule = new AtakPluginModule(); +#endif // Note: if the rest of meshtastic doesn't need to explicitly use your module, you do not need to assign the instance // to a global variable. +#if !MESHTASTIC_EXCLUDE_REMOTEHARDWARE new RemoteHardwareModule(); +#endif // Example: Put your module here // new ReplyModule(); -#if HAS_BUTTON || ARCH_PORTDUINO +#if (HAS_BUTTON || ARCH_PORTDUINO) && !MESHTASTIC_EXCLUDE_INPUTBROKER rotaryEncoderInterruptImpl1 = new RotaryEncoderInterruptImpl1(); if (!rotaryEncoderInterruptImpl1->init()) { delete rotaryEncoderInterruptImpl1; @@ -90,47 +128,59 @@ void setupModules() aLinuxInputImpl = new LinuxInputImpl(); aLinuxInputImpl->init(); #endif -#if HAS_TRACKBALL +#if HAS_TRACKBALL && !MESHTASTIC_EXCLUDE_INPUTBROKER trackballInterruptImpl1 = new TrackballInterruptImpl1(); trackballInterruptImpl1->init(); #endif -#if HAS_SCREEN +#if HAS_SCREEN && !MESHTASTIC_EXCLUDE_CANNEDMESSAGES cannedMessageModule = new CannedMessageModule(); #endif -#if HAS_TELEMETRY +#if HAS_TELEMETRY && !defined(ARCH_PORTDUINO) new DeviceTelemetryModule(); #endif -#if HAS_SENSOR +#if HAS_SENSOR && !EXCLUDE_ENVIRONMENTAL_SENSOR new EnvironmentTelemetryModule(); if (nodeTelemetrySensorsMap[meshtastic_TelemetrySensorType_PMSA003I].first > 0) { new AirQualityTelemetryModule(); } #endif -#if HAS_TELEMETRY && !defined(ARCH_PORTDUINO) +#if HAS_TELEMETRY && !defined(ARCH_PORTDUINO) && !EXCLUDE_POWER_TELEMETRY new PowerTelemetryModule(); #endif #if (defined(ARCH_ESP32) || defined(ARCH_NRF52) || defined(ARCH_RP2040)) && !defined(CONFIG_IDF_TARGET_ESP32S2) && \ !defined(CONFIG_IDF_TARGET_ESP32C3) +#if !MESHTASTIC_EXCLUDE_SERIAL new SerialModule(); #endif +#endif #ifdef ARCH_ESP32 // Only run on an esp32 based device. -#ifdef USE_SX1280 +#if defined(USE_SX1280) && !MESHTASTIC_EXCLUDE_AUDIO audioModule = new AudioModule(); #endif +#if !MESHTASTIC_EXCLUDE_STOREFORWARD storeForwardModule = new StoreForwardModule(); +#endif +#if !MESHTASTIC_EXCLUDE_PAXCOUNTER paxcounterModule = new PaxcounterModule(); #endif +#endif #if defined(ARCH_ESP32) || defined(ARCH_NRF52) || defined(ARCH_RP2040) +#if !MESHTASTIC_EXCLUDE_EXTERNALNOTIFICATION externalNotificationModule = new ExternalNotificationModule(); +#endif +#if !MESHTASTIC_EXCLUDE_RANGETEST new RangeTestModule(); +#endif #endif } else { adminModule = new AdminModule(); #if HAS_TELEMETRY new DeviceTelemetryModule(); #endif +#if !MESHTASTIC_EXCLUDE_TRACEROUTE traceRouteModule = new TraceRouteModule(); +#endif } // NOTE! This module must be added LAST because it likes to check for replies from other modules and avoid sending extra // acks From 9d37a8d17fcebfee819986b69104e820ba521994 Mon Sep 17 00:00:00 2001 From: Jonathan Bennett Date: Wed, 6 Mar 2024 13:09:46 -0600 Subject: [PATCH 02/29] Stop Fiddling with Newlines! (#3341) --- .vscode/settings.json | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index 03922dc72b..e86d31c7d1 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,5 +1,7 @@ { "editor.formatOnSave": true, "editor.defaultFormatter": "trunk.io", - "trunk.enableWindows": true + "trunk.enableWindows": true, + "files.insertFinalNewline": false, + "files.trimFinalNewlines": false } From e174328de36bc9a989986cf7a1d8b59802ec5e29 Mon Sep 17 00:00:00 2001 From: Jonathan Bennett Date: Wed, 6 Mar 2024 16:23:04 -0600 Subject: [PATCH 03/29] Native Webserver (#3343) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Added WebServer/WebServices for Native Linux Meshtastic and web gui * Fix bug in login functionality * Added customized config of portdunio.ini with LovyannGFX from marelab repro * Compile Problem resolved with developer version of LovyanGFX.git * Compile against dev version * Fixes to fit into main branch * Update variant.h, main.cpp, .gitignore, WebServer.cpp, esp32s2.ini, WebServer.h, ContentHandler.cpp, rp2040.ini, nrf52.ini, ContentHelper.cpp, Dockerfile, ContentHandler.h, esp32.ini, stm32wl5e.ini * Added linux pi std /usr/include dir * Adding /usr/innclude for Linux compile against native libs that are not hadled by platformio * Review log level changes & translation * Update Dockerfile * Fix Typo & VFS ref. Part1 * Fix Typo & VFS ref. * Dev Version for ulfius web lib * Update platformio.ini * Free VFS path string * Remove unintended changes * More unintentional changes * Make the HTTP server optional on native * Tune-up for Native web defaults * Don't modify build system yet * Remove more unneeded changes --------- Co-authored-by: marc hammermann Co-authored-by: Ben Meadors Co-authored-by: Thomas Göttgens --- .gitignore | 2 + arch/esp32/esp32.ini | 2 +- arch/esp32/esp32s2.ini | 5 +- arch/nrf52/nrf52.ini | 2 +- arch/portduino/portduino.ini | 1 + arch/rp2040/rp2040.ini | 2 +- arch/stm32/stm32wl5e.ini | 2 +- bin/config-dist.yaml | 4 + src/main.cpp | 6 + src/mesh/raspihttp/PiWebServer.cpp | 530 +++++++++++++++++++++++ src/mesh/raspihttp/PiWebServer.h | 61 +++ src/platform/portduino/PortduinoGlue.cpp | 5 + src/platform/portduino/PortduinoGlue.h | 5 +- variants/portduino/platformio.ini | 8 +- 14 files changed, 625 insertions(+), 10 deletions(-) create mode 100644 src/mesh/raspihttp/PiWebServer.cpp create mode 100644 src/mesh/raspihttp/PiWebServer.h diff --git a/.gitignore b/.gitignore index 89f8ee065e..0f2202f8d4 100644 --- a/.gitignore +++ b/.gitignore @@ -31,3 +31,5 @@ venv/ release/ .vscode/extensions.json /compile_commands.json +src/mesh/raspihttp/certificate.pem +src/mesh/raspihttp/private_key.pem \ No newline at end of file diff --git a/arch/esp32/esp32.ini b/arch/esp32/esp32.ini index bf84dd9395..39935b8491 100644 --- a/arch/esp32/esp32.ini +++ b/arch/esp32/esp32.ini @@ -4,7 +4,7 @@ extends = arduino_base platform = platformio/espressif32@6.3.2 # This is a temporary fix to the S3-based devices bluetooth issues until we can determine what within ESP-IDF changed and can develop a suitable patch. build_src_filter = - ${arduino_base.build_src_filter} - - - - + ${arduino_base.build_src_filter} - - - - - upload_speed = 921600 debug_init_break = tbreak setup diff --git a/arch/esp32/esp32s2.ini b/arch/esp32/esp32s2.ini index 3bde3465a1..5de0fa5497 100644 --- a/arch/esp32/esp32s2.ini +++ b/arch/esp32/esp32s2.ini @@ -2,7 +2,7 @@ extends = esp32_base build_src_filter = - ${esp32_base.build_src_filter} - + ${esp32_base.build_src_filter} - - monitor_speed = 115200 @@ -12,5 +12,4 @@ build_flags = lib_ignore = ${esp32_base.lib_ignore} - NimBLE-Arduino - + NimBLE-Arduino \ No newline at end of file diff --git a/arch/nrf52/nrf52.ini b/arch/nrf52/nrf52.ini index 04ca89a54d..5155eaadc8 100644 --- a/arch/nrf52/nrf52.ini +++ b/arch/nrf52/nrf52.ini @@ -11,7 +11,7 @@ build_flags = -Isrc/platform/nrf52 build_src_filter = - ${arduino_base.build_src_filter} - - - - - - - - - + ${arduino_base.build_src_filter} - - - - - - - - - - lib_deps= ${arduino_base.lib_deps} diff --git a/arch/portduino/portduino.ini b/arch/portduino/portduino.ini index 0dcc9afc21..368fb5d0e3 100644 --- a/arch/portduino/portduino.ini +++ b/arch/portduino/portduino.ini @@ -12,6 +12,7 @@ build_src_filter = - - - + + - - - diff --git a/arch/rp2040/rp2040.ini b/arch/rp2040/rp2040.ini index 48fe0dae65..edc4373ad4 100644 --- a/arch/rp2040/rp2040.ini +++ b/arch/rp2040/rp2040.ini @@ -12,7 +12,7 @@ build_flags = -D__PLAT_RP2040__ # -D _POSIX_THREADS build_src_filter = - ${arduino_base.build_src_filter} - - - - - - - - + ${arduino_base.build_src_filter} - - - - - - - - - lib_ignore = BluetoothOTA diff --git a/arch/stm32/stm32wl5e.ini b/arch/stm32/stm32wl5e.ini index 4483ff5262..4d74ade8fb 100644 --- a/arch/stm32/stm32wl5e.ini +++ b/arch/stm32/stm32wl5e.ini @@ -13,7 +13,7 @@ build_flags = -DVECT_TAB_OFFSET=0x08000000 build_src_filter = - ${arduino_base.build_src_filter} - - - - - - - - - - - - - + ${arduino_base.build_src_filter} - - - - - - - - - - - - - - board_upload.offset_address = 0x08000000 upload_protocol = stlink diff --git a/bin/config-dist.yaml b/bin/config-dist.yaml index b5b105e4c2..a241a929a2 100644 --- a/bin/config-dist.yaml +++ b/bin/config-dist.yaml @@ -117,3 +117,7 @@ Input: Logging: LogLevel: info # debug, info, warn, error + +Webserver: +# Port: 443 # Port for Webserver & Webservices +# RootPath: /usr/share/doc/meshtasticd/web # Root Dir of WebServer diff --git a/src/main.cpp b/src/main.cpp index fbfb983d24..3619b0053a 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -68,6 +68,7 @@ NRF52Bluetooth *nrf52Bluetooth; #ifdef ARCH_PORTDUINO #include "linux/LinuxHardwareI2C.h" +#include "mesh/raspihttp/PiWebServer.h" #include "platform/portduino/PortduinoGlue.h" #include #include @@ -857,6 +858,11 @@ void setup() #endif #ifdef ARCH_PORTDUINO +#if __has_include() + if (settingsMap[webserverport] != -1) { + piwebServerThread = new PiWebServerThread(); + } +#endif initApiServer(TCPPort); #endif diff --git a/src/mesh/raspihttp/PiWebServer.cpp b/src/mesh/raspihttp/PiWebServer.cpp new file mode 100644 index 0000000000..41f6727a4b --- /dev/null +++ b/src/mesh/raspihttp/PiWebServer.cpp @@ -0,0 +1,530 @@ +/* +Adds a WebServer and WebService callbacks to meshtastic as Linux Version. The WebServer & Webservices +runs in a real linux thread beside the portdunio threading emulation. It replaces the complete ESP32 +Webserver libs including generation of SSL certifcicates, because the use ESP specific details in +the lib that can't be emulated. + +The WebServices adapt to the two major phoneapi functions "handleAPIv1FromRadio,handleAPIv1ToRadio" +The WebServer just adds basaic support to deliver WebContent, so it can be used to +deliver the WebGui definded by the WebClient Project. + +Steps to get it running: +1.) Add these Linux Libs to the compile and target machine: + + sudo apt update && \ + apt -y install openssl libssl-dev libopenssl libsdl2-dev \ + libulfius-dev liborcania-dev + +2.) Configure the root directory of the web Content in the config.yaml file. + The followinng tags should be included and set at your needs + + Example entry in the config.yaml + Webserver: + Port: 9001 # Port for Webserver & Webservices + RootPath: /home/marc/web # Root Dir of WebServer + +3.) Checkout the web project + https://github.com/meshtastic/web.git + + Build it and copy the content of the folder web/dist/* to the folder you did set as "RootPath" + +!!!The WebServer should not be used as production system or exposed to the Internet. Its a raw basic version!!! + +Author: Marc Philipp Hammermann +mail: marchammermann@googlemail.com + +*/ +#ifdef PORTDUINO_LINUX_HARDWARE +#if __has_include() +#include "PiWebServer.h" +#include "NodeDB.h" +#include "PhoneAPI.h" +#include "PowerFSM.h" +#include "RadioLibInterface.h" +#include "airtime.h" +#include "graphics/Screen.h" +#include "main.h" +#include "mesh/wifi/WiFiAPClient.h" +#include "sleep.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "PortduinoFS.h" +#include "platform/portduino/PortduinoGlue.h" + +#define DEFAULT_REALM "default_realm" +#define PREFIX "" + +struct _file_config configWeb; + +// We need to specify some content-type mapping, so the resources get delivered with the +// right content type and are displayed correctly in the browser +char contentTypes[][2][32] = {{".txt", "text/plain"}, {".html", "text/html"}, + {".js", "text/javascript"}, {".png", "image/png"}, + {".jpg", "image/jpg"}, {".gz", "application/gzip"}, + {".gif", "image/gif"}, {".json", "application/json"}, + {".css", "text/css"}, {".ico", "image/vnd.microsoft.icon"}, + {".svg", "image/svg+xml"}, {".ts", "text/javascript"}, + {".tsx", "text/javascript"}, {"", ""}}; + +#undef str + +volatile bool isWebServerReady; +volatile bool isCertReady; + +HttpAPI webAPI; + +PiWebServerThread *piwebServerThread; + +/** + * Return the filename extension + */ +const char *get_filename_ext(const char *path) +{ + const char *dot = strrchr(path, '.'); + if (!dot || dot == path) + return "*"; + if (strchr(dot, '?') != NULL) { + //*strchr(dot, '?') = '\0'; + const char *empty = "\0"; + return empty; + } + return dot; +} + +/** + * Streaming callback function to ease sending large files + */ +static ssize_t callback_static_file_stream(void *cls, uint64_t pos, char *buf, size_t max) +{ + (void)(pos); + if (cls != NULL) { + return fread(buf, 1, max, (FILE *)cls); + } else { + return U_STREAM_END; + } +} + +/** + * Cleanup FILE* structure when streaming is complete + */ +static void callback_static_file_stream_free(void *cls) +{ + if (cls != NULL) { + fclose((FILE *)cls); + } +} + +/** + * static file callback endpoint that delivers the content for WebServer calls + */ +int callback_static_file(const struct _u_request *request, struct _u_response *response, void *user_data) +{ + size_t length; + FILE *f; + char *file_requested, *file_path, *url_dup_save, *real_path = NULL; + const char *content_type; + + /* + * Comment this if statement if you don't access static files url from root dir, like /app + */ + if (request->callback_position > 0) { + return U_CALLBACK_CONTINUE; + } else if (user_data != NULL && (configWeb.files_path != NULL)) { + file_requested = o_strdup(request->http_url); + url_dup_save = file_requested; + + while (file_requested[0] == '/') { + file_requested++; + } + file_requested += o_strlen(configWeb.url_prefix); + while (file_requested[0] == '/') { + file_requested++; + } + + if (strchr(file_requested, '#') != NULL) { + *strchr(file_requested, '#') = '\0'; + } + + if (strchr(file_requested, '?') != NULL) { + *strchr(file_requested, '?') = '\0'; + } + + if (file_requested == NULL || o_strlen(file_requested) == 0 || 0 == o_strcmp("/", file_requested)) { + o_free(url_dup_save); + url_dup_save = file_requested = o_strdup("index.html"); + } + + file_path = msprintf("%s/%s", configWeb.files_path, file_requested); + real_path = realpath(file_path, NULL); + if (0 == o_strncmp(configWeb.files_path, real_path, o_strlen(configWeb.files_path))) { + if (access(file_path, F_OK) != -1) { + f = fopen(file_path, "rb"); + if (f) { + fseek(f, 0, SEEK_END); + length = ftell(f); + fseek(f, 0, SEEK_SET); + + content_type = u_map_get_case(&configWeb.mime_types, get_filename_ext(file_requested)); + if (content_type == NULL) { + content_type = u_map_get(&configWeb.mime_types, "*"); + LOG_DEBUG("Static File Server - Unknown mime type for extension %s \n", get_filename_ext(file_requested)); + } + u_map_put(response->map_header, "Content-Type", content_type); + u_map_copy_into(response->map_header, &configWeb.map_header); + + if (ulfius_set_stream_response(response, 200, callback_static_file_stream, callback_static_file_stream_free, + length, STATIC_FILE_CHUNK, f) != U_OK) { + LOG_DEBUG("callback_static_file - Error ulfius_set_stream_response\n "); + } + } + } else { + if (configWeb.redirect_on_404 == NULL) { + ulfius_set_string_body_response(response, 404, "File not found"); + } else { + ulfius_add_header_to_response(response, "Location", configWeb.redirect_on_404); + response->status = 302; + } + } + } else { + if (configWeb.redirect_on_404 == NULL) { + ulfius_set_string_body_response(response, 404, "File not found"); + } else { + ulfius_add_header_to_response(response, "Location", configWeb.redirect_on_404); + response->status = 302; + } + } + + o_free(file_path); + o_free(url_dup_save); + free(real_path); // realpath uses malloc + return U_CALLBACK_CONTINUE; + } else { + LOG_DEBUG("Static File Server - Error, user_data is NULL or inconsistent\n"); + return U_CALLBACK_ERROR; + } +} + +static void handleWebResponse() {} + +/* + * Adapt the radioapi to the Webservice handleAPIv1ToRadio + * Trigger : WebGui(SAVE)->WebServcice->phoneApi + */ +int handleAPIv1ToRadio(const struct _u_request *req, struct _u_response *res, void *user_data) +{ + LOG_DEBUG("handleAPIv1ToRadio web -> radio \n"); + + ulfius_add_header_to_response(res, "Content-Type", "application/x-protobuf"); + ulfius_add_header_to_response(res, "Access-Control-Allow-Headers", "Content-Type"); + ulfius_add_header_to_response(res, "Access-Control-Allow-Origin", "*"); + ulfius_add_header_to_response(res, "Access-Control-Allow-Methods", "PUT, OPTIONS"); + ulfius_add_header_to_response(res, "X-Protobuf-Schema", + "https://raw.githubusercontent.com/meshtastic/protobufs/master/mesh.proto"); + + if (req->http_verb == "OPTIONS") { + ulfius_set_response_properties(res, U_OPT_STATUS, 204); + return U_CALLBACK_CONTINUE; + } + + byte buffer[MAX_TO_FROM_RADIO_SIZE]; + size_t s = req->binary_body_length; + + memcpy(buffer, req->binary_body, MAX_TO_FROM_RADIO_SIZE); + + // FIXME* Problem with portdunio loosing mountpoint maybe because of running in a real sep. thread + + portduinoVFS->mountpoint("/home/marc/.portduino/default"); + + LOG_DEBUG("Received %d bytes from PUT request\n", s); + webAPI.handleToRadio(buffer, s); + LOG_DEBUG("end web->radio \n"); + return U_CALLBACK_COMPLETE; +} + +/* + * Adapt the radioapi to the Webservice handleAPIv1FromRadio + * Trigger : WebGui(POLL)->handleAPIv1FromRadio->phoneapi->Meshtastic(Radio) events + */ +int handleAPIv1FromRadio(const struct _u_request *req, struct _u_response *res, void *user_data) +{ + + // LOG_DEBUG("handleAPIv1FromRadio radio -> web\n"); + std::string valueAll; + + // Status code is 200 OK by default. + ulfius_add_header_to_response(res, "Content-Type", "application/x-protobuf"); + ulfius_add_header_to_response(res, "Access-Control-Allow-Origin", "*"); + ulfius_add_header_to_response(res, "Access-Control-Allow-Methods", "GET"); + ulfius_add_header_to_response(res, "X-Protobuf-Schema", + "https://raw.githubusercontent.com/meshtastic/protobufs/master/mesh.proto"); + + uint8_t txBuf[MAX_STREAM_BUF_SIZE]; + uint32_t len = 1; + + if (valueAll == "true") { + while (len) { + len = webAPI.getFromRadio(txBuf); + ulfius_set_response_properties(res, U_OPT_STATUS, 200, U_OPT_BINARY_BODY, txBuf, len); + const char *tmpa = (const char *)txBuf; + ulfius_set_string_body_response(res, 200, tmpa); + // LOG_DEBUG("\n----webAPI response all:----\n"); + LOG_DEBUG(tmpa); + LOG_DEBUG("\n"); + } + // Otherwise, just return one protobuf + } else { + len = webAPI.getFromRadio(txBuf); + const char *tmpa = (const char *)txBuf; + ulfius_set_binary_body_response(res, 200, tmpa, len); + // LOG_DEBUG("\n----webAPI response:\n"); + LOG_DEBUG(tmpa); + LOG_DEBUG("\n"); + } + + // LOG_DEBUG("end radio->web\n", len); + return U_CALLBACK_COMPLETE; +} + +/* +OpenSSL RSA Key Gen +*/ +int generate_rsa_key(EVP_PKEY **pkey) +{ + EVP_PKEY_CTX *pkey_ctx = EVP_PKEY_CTX_new_id(EVP_PKEY_RSA, NULL); + if (!pkey_ctx) + return -1; + if (EVP_PKEY_keygen_init(pkey_ctx) <= 0) + return -1; + if (EVP_PKEY_CTX_set_rsa_keygen_bits(pkey_ctx, 2048) <= 0) + return -1; + if (EVP_PKEY_keygen(pkey_ctx, pkey) <= 0) + return -1; + EVP_PKEY_CTX_free(pkey_ctx); + return 0; // SUCCESS +} + +int generate_self_signed_x509(EVP_PKEY *pkey, X509 **x509) +{ + *x509 = X509_new(); + if (!*x509) + return -1; + if (X509_set_version(*x509, 2) != 1) + return -1; + ASN1_INTEGER_set(X509_get_serialNumber(*x509), 1); + X509_gmtime_adj(X509_get_notBefore(*x509), 0); + X509_gmtime_adj(X509_get_notAfter(*x509), 31536000L); // 1 YEAR ACCESS + + X509_set_pubkey(*x509, pkey); + + // SET Subject Name + X509_NAME *name = X509_get_subject_name(*x509); + X509_NAME_add_entry_by_txt(name, "C", MBSTRING_ASC, (unsigned char *)"DE", -1, -1, 0); + X509_NAME_add_entry_by_txt(name, "O", MBSTRING_ASC, (unsigned char *)"Meshtastic", -1, -1, 0); + X509_NAME_add_entry_by_txt(name, "CN", MBSTRING_ASC, (unsigned char *)"meshtastic.local", -1, -1, 0); + // Selfsigned, Issuer = Subject + X509_set_issuer_name(*x509, name); + + // Certificate signed with our privte key + if (X509_sign(*x509, pkey, EVP_sha256()) <= 0) + return -1; + + return 0; +} + +char *read_file_into_string(const char *filename) +{ + FILE *file = fopen(filename, "rb"); + if (file == NULL) { + LOG_ERROR("Error reading File : %s \n", filename); + return NULL; + } + + // Size of file + fseek(file, 0, SEEK_END); + long filesize = ftell(file); + rewind(file); + + // reserve mem for file + 1 byte + char *buffer = (char *)malloc(filesize + 1); + if (buffer == NULL) { + LOG_ERROR("Malloc of mem failed for file : %s \n", filename); + fclose(file); + return NULL; + } + + // read content + size_t readSize = fread(buffer, 1, filesize, file); + if (readSize != filesize) { + LOG_ERROR("Error reading file into buffer\n"); + free(buffer); + fclose(file); + return NULL; + } + + // add terminator sign at the end + buffer[filesize] = '\0'; + fclose(file); + return buffer; // return pointer +} + +int PiWebServerThread::CheckSSLandLoad() +{ + // read certificate + cert_pem = read_file_into_string("certificate.pem"); + if (cert_pem == NULL) { + LOG_ERROR("ERROR SSL Certificate File can't be loaded or is missing\n"); + return 1; + } + // read private key + key_pem = read_file_into_string("private_key.pem"); + if (key_pem == NULL) { + LOG_ERROR("ERROR file private_key can't be loaded or is missing\n"); + return 2; + } + + return 0; +} + +int PiWebServerThread::CreateSSLCertificate() +{ + + EVP_PKEY *pkey = NULL; + X509 *x509 = NULL; + + if (generate_rsa_key(&pkey) != 0) { + LOG_ERROR("Error generating RSA-Key.\n"); + return 1; + } + + if (generate_self_signed_x509(pkey, &x509) != 0) { + LOG_ERROR("Error generating of X509-Certificat.\n"); + return 2; + } + + // Ope file to write private key file + FILE *pkey_file = fopen("private_key.pem", "wb"); + if (!pkey_file) { + LOG_ERROR("Error opening private key file.\n"); + return 3; + } + // write private key file + PEM_write_PrivateKey(pkey_file, pkey, NULL, NULL, 0, NULL, NULL); + fclose(pkey_file); + + // open Certificate file + FILE *x509_file = fopen("certificate.pem", "wb"); + if (!x509_file) { + LOG_ERROR("Error opening certificate.\n"); + return 4; + } + // write cirtificate + PEM_write_X509(x509_file, x509); + fclose(x509_file); + + EVP_PKEY_free(pkey); + X509_free(x509); + LOG_INFO("Create SSL Certifictate -certificate.pem- succesfull \n"); + return 0; +} + +void initWebServer() {} + +PiWebServerThread::PiWebServerThread() +{ + int ret, retssl, webservport; + + if (CheckSSLandLoad() != 0) { + CreateSSLCertificate(); + if (CheckSSLandLoad() != 0) { + LOG_ERROR("Major Error Gen & Read SSL Certificate\n"); + } + } + + if (settingsMap[webserverport] != 0) { + webservport = settingsMap[webserverport]; + LOG_INFO("Using webserver port from yaml config. %i \n", webservport); + } else { + LOG_INFO("Webserver port in yaml config set to 0, so defaulting to port 443.\n"); + webservport = 443; + } + + // Web Content Service Instance + if (ulfius_init_instance(&instanceWeb, webservport, NULL, DEFAULT_REALM) != U_OK) { + LOG_ERROR("Webserver couldn't be started, abort execution\n"); + } else { + + LOG_INFO("Webserver started ....\n"); + u_map_init(&configWeb.mime_types); + u_map_put(&configWeb.mime_types, "*", "application/octet-stream"); + u_map_put(&configWeb.mime_types, ".html", "text/html"); + u_map_put(&configWeb.mime_types, ".htm", "text/html"); + u_map_put(&configWeb.mime_types, ".tsx", "application/javascript"); + u_map_put(&configWeb.mime_types, ".ts", "application/javascript"); + u_map_put(&configWeb.mime_types, ".css", "text/css"); + u_map_put(&configWeb.mime_types, ".js", "application/javascript"); + u_map_put(&configWeb.mime_types, ".json", "application/json"); + u_map_put(&configWeb.mime_types, ".png", "image/png"); + u_map_put(&configWeb.mime_types, ".gif", "image/gif"); + u_map_put(&configWeb.mime_types, ".jpeg", "image/jpeg"); + u_map_put(&configWeb.mime_types, ".jpg", "image/jpeg"); + u_map_put(&configWeb.mime_types, ".ttf", "font/ttf"); + u_map_put(&configWeb.mime_types, ".woff", "font/woff"); + u_map_put(&configWeb.mime_types, ".ico", "image/x-icon"); + u_map_put(&configWeb.mime_types, ".svg", "image/svg+xml"); + + webrootpath = settingsStrings[webserverrootpath]; + + configWeb.files_path = (char *)webrootpath.c_str(); + configWeb.url_prefix = ""; + configWeb.rootPath = strdup(portduinoVFS->mountpoint()); + + u_map_put(instanceWeb.default_headers, "Access-Control-Allow-Origin", "*"); + // Maximum body size sent by the client is 1 Kb + instanceWeb.max_post_body_size = 1024; + ulfius_add_endpoint_by_val(&instanceWeb, "GET", PREFIX, "/api/v1/fromradio/*", 1, &handleAPIv1FromRadio, NULL); + ulfius_add_endpoint_by_val(&instanceWeb, "PUT", PREFIX, "/api/v1/toradio/*", 1, &handleAPIv1ToRadio, configWeb.rootPath); + + // Add callback function to all endpoints for the Web Server + ulfius_add_endpoint_by_val(&instanceWeb, "GET", NULL, "/*", 2, &callback_static_file, &configWeb); + + // thats for serving without SSL + // retssl = ulfius_start_framework(&instanceWeb); + + // thats for serving with SSL + retssl = ulfius_start_secure_framework(&instanceWeb, key_pem, cert_pem); + + if (retssl == U_OK) { + LOG_INFO("Web Server framework started on port: %i \n", webservport); + LOG_INFO("Web Server root %s\n", (char *)webrootpath.c_str()); + } else { + LOG_ERROR("Error starting Web Server framework\n"); + } + } +} + +PiWebServerThread::~PiWebServerThread() +{ + u_map_clean(&configWeb.mime_types); + + ulfius_stop_framework(&instanceWeb); + ulfius_stop_framework(&instanceWeb); + free(configWeb.rootPath); + ulfius_clean_instance(&instanceService); + ulfius_clean_instance(&instanceService); + free(cert_pem); + LOG_INFO("End framework"); +} + +#endif +#endif \ No newline at end of file diff --git a/src/mesh/raspihttp/PiWebServer.h b/src/mesh/raspihttp/PiWebServer.h new file mode 100644 index 0000000000..c4c49e9197 --- /dev/null +++ b/src/mesh/raspihttp/PiWebServer.h @@ -0,0 +1,61 @@ +#pragma once +#ifdef PORTDUINO_LINUX_HARDWARE +#if __has_include() +#include "PhoneAPI.h" +#include "ulfius-cfg.h" +#include "ulfius.h" +#include +#include + +#define STATIC_FILE_CHUNK 256 + +void initWebServer(); +void createSSLCert(); +int callback_static_file(const struct _u_request *request, struct _u_response *response, void *user_data); +const char *get_filename_ext(const char *path); + +struct _file_config { + char *files_path; + char *url_prefix; + struct _u_map mime_types; + struct _u_map map_header; + char *redirect_on_404; + char *rootPath; +}; + +class PiWebServerThread +{ + private: + char *key_pem = NULL; + char *cert_pem = NULL; + // struct _u_map mime_types; + std::string webrootpath; + + public: + PiWebServerThread(); + ~PiWebServerThread(); + int CreateSSLCertificate(); + int CheckSSLandLoad(); + uint32_t requestRestart = 0; + struct _u_instance instanceWeb; + struct _u_instance instanceService; +}; + +class HttpAPI : public PhoneAPI +{ + + public: + // Nothing here yet + + private: + // Nothing here yet + + protected: + /// Check the current underlying physical link to see if the client is currently connected + virtual bool checkIsConnected() override { return true; } // FIXME, be smarter about this +}; + +extern PiWebServerThread *piwebServerThread; + +#endif +#endif \ No newline at end of file diff --git a/src/platform/portduino/PortduinoGlue.cpp b/src/platform/portduino/PortduinoGlue.cpp index 046509faba..997058406a 100644 --- a/src/platform/portduino/PortduinoGlue.cpp +++ b/src/platform/portduino/PortduinoGlue.cpp @@ -195,6 +195,11 @@ void portduinoSetup() settingsStrings[keyboardDevice] = (yamlConfig["Input"]["KeyboardDevice"]).as(""); } + if (yamlConfig["Webserver"]) { + settingsMap[webserverport] = (yamlConfig["Webserver"]["Port"]).as(-1); + settingsStrings[webserverrootpath] = (yamlConfig["Webserver"]["RootPath"]).as(""); + } + } catch (YAML::Exception e) { std::cout << "*** Exception " << e.what() << std::endl; exit(EXIT_FAILURE); diff --git a/src/platform/portduino/PortduinoGlue.h b/src/platform/portduino/PortduinoGlue.h index f8da20e37c..3fe5f74bf0 100644 --- a/src/platform/portduino/PortduinoGlue.h +++ b/src/platform/portduino/PortduinoGlue.h @@ -33,7 +33,10 @@ enum configNames { displayOffsetY, displayInvert, keyboardDevice, - logoutputlevel + logoutputlevel, + webserver, + webserverport, + webserverrootpath }; enum { no_screen, st7789, st7735, st7735s, ili9341 }; enum { no_touchscreen, xpt2046, stmpe610 }; diff --git a/variants/portduino/platformio.ini b/variants/portduino/platformio.ini index d37c6be21f..46417e388e 100644 --- a/variants/portduino/platformio.ini +++ b/variants/portduino/platformio.ini @@ -1,6 +1,10 @@ [env:native] extends = portduino_base -build_flags = ${portduino_base.build_flags} -O0 -I variants/portduino +; The pkg-config commands below optionally add link flags. +; the || : is just a "or run the null command" to avoid returning an error code +build_flags = ${portduino_base.build_flags} -O0 -I variants/portduino -I /usr/include + !pkg-config --libs libulfius --silence-errors || : + !pkg-config --libs openssl --silence-errors || : board = cross_platform lib_deps = ${portduino_base.lib_deps} -build_src_filter = ${portduino_base.build_src_filter} \ No newline at end of file +build_src_filter = ${portduino_base.build_src_filter} From 46ad6237859e60eb1e54b3bb5c69baa895d95c3b Mon Sep 17 00:00:00 2001 From: Jonathan Bennett Date: Wed, 6 Mar 2024 17:00:23 -0600 Subject: [PATCH 04/29] Add webroot to .deb --- .github/workflows/package_raspbian.yml | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/.github/workflows/package_raspbian.yml b/.github/workflows/package_raspbian.yml index 2f9a99e583..ee1643fbf2 100644 --- a/.github/workflows/package_raspbian.yml +++ b/.github/workflows/package_raspbian.yml @@ -23,6 +23,14 @@ jobs: ref: ${{github.event.pull_request.head.ref}} repository: ${{github.event.pull_request.head.repo.full_name}} + - name: Pull web ui + uses: dsaltares/fetch-gh-release-asset@a40c8b4a0471f9ab81bdf73a010f74cc51476ad4 + with: + repo: meshtastic/web + file: build.tar + target: build.tar + token: ${{ secrets.GITHUB_TOKEN }} + - name: Get release version string run: echo "version=$(./bin/buildinfo.py long)" >> $GITHUB_OUTPUT id: version @@ -37,9 +45,12 @@ jobs: - name: build .debpkg run: | + mkdir -p .debpkg/usr/share/doc/meshtasticd/web mkdir -p .debpkg/usr/sbin mkdir -p .debpkg/etc/meshtasticd mkdir -p .debpkg/usr/lib/systemd/system/ + tar -xf build.tar -C .debpkg/usr/share/doc/meshtasticd/web + gunzip .debpkg/usr/share/doc/meshtasticd/web/*.gz cp release/meshtasticd_linux_aarch64 .debpkg/usr/sbin/meshtasticd cp bin/config-dist.yaml .debpkg/etc/meshtasticd/config.yaml chmod +x .debpkg/usr/sbin/meshtasticd From bfce3938d24b7190ec78e2faf64041b916550139 Mon Sep 17 00:00:00 2001 From: Jonathan Bennett Date: Wed, 6 Mar 2024 18:54:09 -0600 Subject: [PATCH 05/29] Add openssl as dependency to meshtasticd .deb --- .github/workflows/package_raspbian.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/package_raspbian.yml b/.github/workflows/package_raspbian.yml index ee1643fbf2..377074e95f 100644 --- a/.github/workflows/package_raspbian.yml +++ b/.github/workflows/package_raspbian.yml @@ -63,7 +63,7 @@ jobs: maintainer: Jonathan Bennett version: ${{ steps.version.outputs.version }} # refs/tags/v*.*.* arch: arm64 - depends: libyaml-cpp0.7 + depends: libyaml-cpp0.7, openssl desc: Native Linux Meshtastic binary. - uses: actions/upload-artifact@v3 From 2dd751e3391e9c676f78e7b329a0cdccb03f3c40 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 7 Mar 2024 07:06:47 -0600 Subject: [PATCH 06/29] [create-pull-request] automated change (#3346) Co-authored-by: thebentern --- protobufs | 2 +- src/mesh/generated/meshtastic/portnums.pb.h | 10 +++++----- src/mesh/generated/meshtastic/telemetry.pb.h | 8 +++++--- 3 files changed, 11 insertions(+), 9 deletions(-) diff --git a/protobufs b/protobufs index 62b7d8b884..5a97acb175 160000 --- a/protobufs +++ b/protobufs @@ -1 +1 @@ -Subproject commit 62b7d8b884d70aed5ff18c3b0e228095eeb48de2 +Subproject commit 5a97acb17543a10e114675a205e3274a83e721af diff --git a/src/mesh/generated/meshtastic/portnums.pb.h b/src/mesh/generated/meshtastic/portnums.pb.h index 88342e5dcc..3f3e9aaee8 100644 --- a/src/mesh/generated/meshtastic/portnums.pb.h +++ b/src/mesh/generated/meshtastic/portnums.pb.h @@ -38,19 +38,19 @@ typedef enum _meshtastic_PortNum { ENCODING: Protobuf */ meshtastic_PortNum_REMOTE_HARDWARE_APP = 2, /* The built-in position messaging app. - Payload is a [Position](/docs/developers/protobufs/api#position) message + Payload is a Position message. ENCODING: Protobuf */ meshtastic_PortNum_POSITION_APP = 3, /* The built-in user info app. - Payload is a [User](/docs/developers/protobufs/api#user) message + Payload is a User message. ENCODING: Protobuf */ meshtastic_PortNum_NODEINFO_APP = 4, /* Protocol control packets for mesh protocol use. - Payload is a [Routing](/docs/developers/protobufs/api#routing) message + Payload is a Routing message. ENCODING: Protobuf */ meshtastic_PortNum_ROUTING_APP = 5, /* Admin control packets. - Payload is a [AdminMessage](/docs/developers/protobufs/api#adminmessage) message + Payload is a AdminMessage message. ENCODING: Protobuf */ meshtastic_PortNum_ADMIN_APP = 6, /* Compressed TEXT_MESSAGE payloads. @@ -60,7 +60,7 @@ typedef enum _meshtastic_PortNum { any incoming TEXT_MESSAGE_COMPRESSED_APP payload and convert to TEXT_MESSAGE_APP. */ meshtastic_PortNum_TEXT_MESSAGE_COMPRESSED_APP = 7, /* Waypoint payloads. - Payload is a [Waypoint](/docs/developers/protobufs/api#waypoint) message + Payload is a Waypoint message. ENCODING: Protobuf */ meshtastic_PortNum_WAYPOINT_APP = 8, /* Audio Payloads. diff --git a/src/mesh/generated/meshtastic/telemetry.pb.h b/src/mesh/generated/meshtastic/telemetry.pb.h index fc2780a963..d73c6baa1a 100644 --- a/src/mesh/generated/meshtastic/telemetry.pb.h +++ b/src/mesh/generated/meshtastic/telemetry.pb.h @@ -41,7 +41,9 @@ typedef enum _meshtastic_TelemetrySensorType { /* PM2.5 air quality sensor */ meshtastic_TelemetrySensorType_PMSA003I = 13, /* INA3221 3 Channel Voltage / Current Sensor */ - meshtastic_TelemetrySensorType_INA3221 = 14 + meshtastic_TelemetrySensorType_INA3221 = 14, + /* BMP085/BMP180 High accuracy temperature and pressure (older Version of BMP280) */ + meshtastic_TelemetrySensorType_BMP085 = 15 } meshtastic_TelemetrySensorType; /* Struct definitions */ @@ -141,8 +143,8 @@ extern "C" { /* Helper constants for enums */ #define _meshtastic_TelemetrySensorType_MIN meshtastic_TelemetrySensorType_SENSOR_UNSET -#define _meshtastic_TelemetrySensorType_MAX meshtastic_TelemetrySensorType_INA3221 -#define _meshtastic_TelemetrySensorType_ARRAYSIZE ((meshtastic_TelemetrySensorType)(meshtastic_TelemetrySensorType_INA3221+1)) +#define _meshtastic_TelemetrySensorType_MAX meshtastic_TelemetrySensorType_BMP085 +#define _meshtastic_TelemetrySensorType_ARRAYSIZE ((meshtastic_TelemetrySensorType)(meshtastic_TelemetrySensorType_BMP085+1)) From c860493e68112d83f5c3707bd4bc4401d8d681de Mon Sep 17 00:00:00 2001 From: Steven Osborn Date: Thu, 7 Mar 2024 05:11:25 -0800 Subject: [PATCH 07/29] Add delay so GPS and Radio have time to power up (#3334) * Add delay so GPS and Radio have time to power up * reduce the delay a bit * make delay more generic / configurable * remove whitespace changes --- boards/canaryone.json | 2 +- src/main.cpp | 5 +++++ variants/canaryone/variant.h | 3 +++ 3 files changed, 9 insertions(+), 1 deletion(-) diff --git a/boards/canaryone.json b/boards/canaryone.json index d8f966a475..da7c0986f4 100644 --- a/boards/canaryone.json +++ b/boards/canaryone.json @@ -7,7 +7,7 @@ "cpu": "cortex-m4", "extra_flags": "-DARDUINO_NRF52840_CANARY -DNRF52840_XXAA", "f_cpu": "64000000L", - "hwids": [["0x239A", "0x4405"]], + "hwids": [["0x239A", "0x4405"], ["0x239A", "0x009F"]], "usb_product": "CanaryOne", "mcu": "nrf52840", "variant": "canaryone", diff --git a/src/main.cpp b/src/main.cpp index 3619b0053a..80706d044f 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -680,6 +680,11 @@ void setup() digitalWrite(SX126X_ANT_SW, 1); #endif +#ifdef PIN_PWR_DELAY_MS + // This may be required to give the peripherals time to power up. + delay(PIN_PWR_DELAY_MS); +#endif + #ifdef ARCH_PORTDUINO if (settingsMap[use_sx1262]) { if (!rIf) { diff --git a/variants/canaryone/variant.h b/variants/canaryone/variant.h index e31ba3c58f..21aa921cec 100644 --- a/variants/canaryone/variant.h +++ b/variants/canaryone/variant.h @@ -103,6 +103,9 @@ static const uint8_t A0 = PIN_A0; #define EXTERNAL_FLASH_DEVICES MX25R1635F #define EXTERNAL_FLASH_USE_QSPI +// Add a delay on startup to allow LoRa and GPS to power up +#define PIN_PWR_DELAY_MS 100 + /* * Lora radio */ From b4940b476daa6817d990f8a019bda741589c76ed Mon Sep 17 00:00:00 2001 From: Ben Meadors Date: Thu, 7 Mar 2024 15:51:28 -0600 Subject: [PATCH 08/29] Trunk --- boards/canaryone.json | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/boards/canaryone.json b/boards/canaryone.json index da7c0986f4..f64a4a7c76 100644 --- a/boards/canaryone.json +++ b/boards/canaryone.json @@ -7,7 +7,10 @@ "cpu": "cortex-m4", "extra_flags": "-DARDUINO_NRF52840_CANARY -DNRF52840_XXAA", "f_cpu": "64000000L", - "hwids": [["0x239A", "0x4405"], ["0x239A", "0x009F"]], + "hwids": [ + ["0x239A", "0x4405"], + ["0x239A", "0x009F"] + ], "usb_product": "CanaryOne", "mcu": "nrf52840", "variant": "canaryone", From 7f1250571679131424952f01a0e44e489a332793 Mon Sep 17 00:00:00 2001 From: Ben Meadors Date: Thu, 7 Mar 2024 15:52:08 -0600 Subject: [PATCH 09/29] Update trunk --- .trunk/trunk.yaml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.trunk/trunk.yaml b/.trunk/trunk.yaml index af7d3d21db..0826b71d9d 100644 --- a/.trunk/trunk.yaml +++ b/.trunk/trunk.yaml @@ -4,19 +4,19 @@ cli: plugins: sources: - id: trunk - ref: v1.4.3 + ref: v1.4.4 uri: https://github.com/trunk-io/plugins lint: enabled: - - trufflehog@3.68.2 + - trufflehog@3.68.5 - yamllint@1.35.1 - bandit@1.7.7 - - checkov@3.2.26 - - terrascan@1.18.11 + - checkov@3.2.32 + - terrascan@1.19.1 - trivy@0.49.1 #- trufflehog@3.63.2-rc0 - taplo@0.8.1 - - ruff@0.2.2 + - ruff@0.3.1 - isort@5.13.2 - markdownlint@0.39.0 - oxipng@9.0.0 From 763ae9f2e2e7db779c80021474162a212fa409e8 Mon Sep 17 00:00:00 2001 From: Thomas Herrmann Date: Sat, 2 Mar 2024 22:14:34 +0100 Subject: [PATCH 10/29] add BMP085 (and BMP180) sensor (temperature and air pressure) --- platformio.ini | 1 + src/detect/ScanI2C.h | 1 + src/detect/ScanI2CTwoWire.cpp | 4 +++ src/main.cpp | 1 + .../Telemetry/EnvironmentTelemetry.cpp | 6 ++++ src/modules/Telemetry/Sensor/BMP085Sensor.cpp | 31 +++++++++++++++++++ src/modules/Telemetry/Sensor/BMP085Sensor.h | 17 ++++++++++ 7 files changed, 61 insertions(+) create mode 100644 src/modules/Telemetry/Sensor/BMP085Sensor.cpp create mode 100644 src/modules/Telemetry/Sensor/BMP085Sensor.h diff --git a/platformio.ini b/platformio.ini index 0033b6e469..b67ddc50ac 100644 --- a/platformio.ini +++ b/platformio.ini @@ -116,6 +116,7 @@ lib_deps = adafruit/Adafruit BusIO@^1.11.4 adafruit/Adafruit Unified Sensor@^1.1.11 adafruit/Adafruit BMP280 Library@^2.6.8 + adafruit/Adafruit BMP085 Library@^1.2.4 adafruit/Adafruit BME280 Library@^2.2.2 https://github.com/boschsensortec/Bosch-BSEC2-Library#v1.5.2400 boschsensortec/BME68x Sensor Library@^1.1.40407 diff --git a/src/detect/ScanI2C.h b/src/detect/ScanI2C.h index 2b4b8a735a..66e6839823 100644 --- a/src/detect/ScanI2C.h +++ b/src/detect/ScanI2C.h @@ -23,6 +23,7 @@ class ScanI2C BME_680, BME_280, BMP_280, + BMP_085, INA260, INA219, INA3221, diff --git a/src/detect/ScanI2CTwoWire.cpp b/src/detect/ScanI2CTwoWire.cpp index 990fb36ea4..b6eca5fa4c 100644 --- a/src/detect/ScanI2CTwoWire.cpp +++ b/src/detect/ScanI2CTwoWire.cpp @@ -242,6 +242,10 @@ void ScanI2CTwoWire::scanPort(I2CPort port) LOG_INFO("BME-280 sensor found at address 0x%x\n", (uint8_t)addr.address); type = BME_280; break; + case 0x55: + LOG_INFO("BMP-085 or BMP-180 sensor found at address 0x%x\n", (uint8_t)addr.address); + type = BMP_085; + break; default: LOG_INFO("BMP-280 sensor found at address 0x%x\n", (uint8_t)addr.address); type = BMP_280; diff --git a/src/main.cpp b/src/main.cpp index 80706d044f..b62ccf9865 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -500,6 +500,7 @@ void setup() SCANNER_TO_SENSORS_MAP(ScanI2C::DeviceType::BME_680, meshtastic_TelemetrySensorType_BME680) SCANNER_TO_SENSORS_MAP(ScanI2C::DeviceType::BME_280, meshtastic_TelemetrySensorType_BME280) SCANNER_TO_SENSORS_MAP(ScanI2C::DeviceType::BMP_280, meshtastic_TelemetrySensorType_BMP280) + SCANNER_TO_SENSORS_MAP(ScanI2C::DeviceType::BMP_085, meshtastic_TelemetrySensorType_BMP085) SCANNER_TO_SENSORS_MAP(ScanI2C::DeviceType::INA260, meshtastic_TelemetrySensorType_INA260) SCANNER_TO_SENSORS_MAP(ScanI2C::DeviceType::INA219, meshtastic_TelemetrySensorType_INA219) SCANNER_TO_SENSORS_MAP(ScanI2C::DeviceType::INA3221, meshtastic_TelemetrySensorType_INA3221) diff --git a/src/modules/Telemetry/EnvironmentTelemetry.cpp b/src/modules/Telemetry/EnvironmentTelemetry.cpp index e501f17c27..d4f423e549 100644 --- a/src/modules/Telemetry/EnvironmentTelemetry.cpp +++ b/src/modules/Telemetry/EnvironmentTelemetry.cpp @@ -16,12 +16,14 @@ // Sensors #include "Sensor/BME280Sensor.h" #include "Sensor/BME680Sensor.h" +#include "Sensor/BMP085Sensor.h" #include "Sensor/BMP280Sensor.h" #include "Sensor/LPS22HBSensor.h" #include "Sensor/MCP9808Sensor.h" #include "Sensor/SHT31Sensor.h" #include "Sensor/SHTC3Sensor.h" +BMP085Sensor bmp085Sensor; BMP280Sensor bmp280Sensor; BME280Sensor bme280Sensor; BME680Sensor bme680Sensor; @@ -67,6 +69,8 @@ int32_t EnvironmentTelemetryModule::runOnce() LOG_INFO("Environment Telemetry: Initializing\n"); // it's possible to have this module enabled, only for displaying values on the screen. // therefore, we should only enable the sensor loop if measurement is also enabled + if (bmp085Sensor.hasSensor()) + result = bmp085Sensor.runOnce(); if (bmp280Sensor.hasSensor()) result = bmp280Sensor.runOnce(); if (bme280Sensor.hasSensor()) @@ -219,6 +223,8 @@ bool EnvironmentTelemetryModule::sendTelemetry(NodeNum dest, bool phoneOnly) valid = lps22hbSensor.getMetrics(&m); if (shtc3Sensor.hasSensor()) valid = shtc3Sensor.getMetrics(&m); + if (bmp085Sensor.hasSensor()) + valid = bmp085Sensor.getMetrics(&m); if (bmp280Sensor.hasSensor()) valid = bmp280Sensor.getMetrics(&m); if (bme280Sensor.hasSensor()) diff --git a/src/modules/Telemetry/Sensor/BMP085Sensor.cpp b/src/modules/Telemetry/Sensor/BMP085Sensor.cpp new file mode 100644 index 0000000000..b0991749be --- /dev/null +++ b/src/modules/Telemetry/Sensor/BMP085Sensor.cpp @@ -0,0 +1,31 @@ +#include "BMP085Sensor.h" +#include "../mesh/generated/meshtastic/telemetry.pb.h" +#include "TelemetrySensor.h" +#include "configuration.h" +#include +#include + +BMP085Sensor::BMP085Sensor() : TelemetrySensor(meshtastic_TelemetrySensorType_BMP085, "BMP085") {} + +int32_t BMP085Sensor::runOnce() +{ + LOG_INFO("Init sensor: %s\n", sensorName); + if (!hasSensor()) { + return DEFAULT_SENSOR_MINIMUM_WAIT_TIME_BETWEEN_READS; + } + bmp085 = Adafruit_BMP085(); + status = bmp085.begin(nodeTelemetrySensorsMap[sensorType].first, nodeTelemetrySensorsMap[sensorType].second); + + return initI2CSensor(); +} + +void BMP085Sensor::setup() {} + +bool BMP085Sensor::getMetrics(meshtastic_Telemetry *measurement) +{ + LOG_DEBUG("BMP085Sensor::getMetrics\n"); + measurement->variant.environment_metrics.temperature = bmp085.readTemperature(); + measurement->variant.environment_metrics.barometric_pressure = bmp085.readPressure() / 100.0F; + + return true; +} diff --git a/src/modules/Telemetry/Sensor/BMP085Sensor.h b/src/modules/Telemetry/Sensor/BMP085Sensor.h new file mode 100644 index 0000000000..c4a9479b95 --- /dev/null +++ b/src/modules/Telemetry/Sensor/BMP085Sensor.h @@ -0,0 +1,17 @@ +#include "../mesh/generated/meshtastic/telemetry.pb.h" +#include "TelemetrySensor.h" +#include + +class BMP085Sensor : public TelemetrySensor +{ + private: + Adafruit_BMP085 bmp085; + + protected: + virtual void setup() override; + + public: + BMP085Sensor(); + virtual int32_t runOnce() override; + virtual bool getMetrics(meshtastic_Telemetry *measurement) override; +}; \ No newline at end of file From 585805c3b96d58224ffd2327fd6b4e1712a84abd Mon Sep 17 00:00:00 2001 From: GUVWAF <78759985+GUVWAF@users.noreply.github.com> Date: Fri, 8 Mar 2024 14:13:57 +0100 Subject: [PATCH 11/29] Add original hop limit to header to determine hops used (#3321) * Set `hop_start` in header to determine how many hops each packet traveled * Set hopLimit of response according to hops used by request * Identify neighbors based on `hopStart` and `hopLimit` * NeighborInfo: get all packets and assume a default broadcast interval * Add fail-safe in case node in between is running modified firmware * Add `viaMQTT` and `hopsAway` to NodeInfo * Replace `HOP_RELIABLE` with hopStart for repeated packet --------- Co-authored-by: Ben Meadors --- src/mesh/MeshModule.cpp | 9 ++++++--- src/mesh/MeshModule.h | 3 ++- src/mesh/NodeDB.cpp | 6 ++++++ src/mesh/RadioInterface.cpp | 5 ++++- src/mesh/RadioInterface.h | 6 ++++-- src/mesh/RadioLibInterface.cpp | 7 ++++--- src/mesh/ReliableRouter.cpp | 13 +++++++------ src/mesh/Router.cpp | 9 +++++++-- src/mesh/Router.h | 3 ++- src/modules/NeighborInfoModule.cpp | 12 +++++++++--- src/modules/NeighborInfoModule.h | 3 +++ src/modules/RoutingModule.cpp | 19 +++++++++++++++++-- src/modules/RoutingModule.h | 6 +++++- 13 files changed, 76 insertions(+), 25 deletions(-) diff --git a/src/mesh/MeshModule.cpp b/src/mesh/MeshModule.cpp index 9c6ca78ee2..ad0c781088 100644 --- a/src/mesh/MeshModule.cpp +++ b/src/mesh/MeshModule.cpp @@ -32,7 +32,8 @@ MeshModule::~MeshModule() assert(0); // FIXME - remove from list of modules once someone needs this feature } -meshtastic_MeshPacket *MeshModule::allocAckNak(meshtastic_Routing_Error err, NodeNum to, PacketId idFrom, ChannelIndex chIndex) +meshtastic_MeshPacket *MeshModule::allocAckNak(meshtastic_Routing_Error err, NodeNum to, PacketId idFrom, ChannelIndex chIndex, + uint8_t hopStart, uint8_t hopLimit) { meshtastic_Routing c = meshtastic_Routing_init_default; @@ -49,7 +50,7 @@ meshtastic_MeshPacket *MeshModule::allocAckNak(meshtastic_Routing_Error err, Nod p->priority = meshtastic_MeshPacket_Priority_ACK; - p->hop_limit = config.lora.hop_limit; // Flood ACK back to original sender + p->hop_limit = routingModule->getHopLimitForResponse(hopStart, hopLimit); // Flood ACK back to original sender p->to = to; p->decoded.request_id = idFrom; p->channel = chIndex; @@ -176,7 +177,8 @@ void MeshModule::callPlugins(meshtastic_MeshPacket &mp, RxSource src) // SECURITY NOTE! I considered sending back a different error code if we didn't find the psk (i.e. !isDecoded) // but opted NOT TO. Because it is not a good idea to let remote nodes 'probe' to find out which PSKs were "good" vs // bad. - routingModule->sendAckNak(meshtastic_Routing_Error_NO_RESPONSE, getFrom(&mp), mp.id, mp.channel); + routingModule->sendAckNak(meshtastic_Routing_Error_NO_RESPONSE, getFrom(&mp), mp.id, mp.channel, mp.hop_start, + mp.hop_limit); } } @@ -217,6 +219,7 @@ void setReplyTo(meshtastic_MeshPacket *p, const meshtastic_MeshPacket &to) assert(p->which_payload_variant == meshtastic_MeshPacket_decoded_tag); // Should already be set by now p->to = getFrom(&to); // Make sure that if we are sending to the local node, we use our local node addr, not 0 p->channel = to.channel; // Use the same channel that the request came in on + p->hop_limit = routingModule->getHopLimitForResponse(to.hop_start, to.hop_limit); // No need for an ack if we are just delivering locally (it just generates an ignored ack) p->want_ack = (to.from != 0) ? to.want_ack : false; diff --git a/src/mesh/MeshModule.h b/src/mesh/MeshModule.h index ebe3af1a0b..6c431adb47 100644 --- a/src/mesh/MeshModule.h +++ b/src/mesh/MeshModule.h @@ -153,7 +153,8 @@ class MeshModule virtual bool wantUIFrame() { return false; } virtual Observable *getUIFrameObservable() { return NULL; } - meshtastic_MeshPacket *allocAckNak(meshtastic_Routing_Error err, NodeNum to, PacketId idFrom, ChannelIndex chIndex); + meshtastic_MeshPacket *allocAckNak(meshtastic_Routing_Error err, NodeNum to, PacketId idFrom, ChannelIndex chIndex, + uint8_t hopStart = 0, uint8_t hopLimit = 0); /// Send an error response for the specified packet. meshtastic_MeshPacket *allocErrorResponse(meshtastic_Routing_Error err, const meshtastic_MeshPacket *p); diff --git a/src/mesh/NodeDB.cpp b/src/mesh/NodeDB.cpp index dc8d7540ca..787c16a797 100644 --- a/src/mesh/NodeDB.cpp +++ b/src/mesh/NodeDB.cpp @@ -875,6 +875,12 @@ void NodeDB::updateFrom(const meshtastic_MeshPacket &mp) if (mp.rx_snr) info->snr = mp.rx_snr; // keep the most recent SNR we received for this node. + + info->via_mqtt = mp.via_mqtt; // Store if we received this packet via MQTT + + // If hopStart was set and there wasn't someone messing with the limit in the middle, add hopsAway + if (mp.hop_start != 0 && mp.hop_limit <= mp.hop_start) + info->hops_away = mp.hop_start - mp.hop_limit; } } diff --git a/src/mesh/RadioInterface.cpp b/src/mesh/RadioInterface.cpp index cea3968ce3..c10eb26f6a 100644 --- a/src/mesh/RadioInterface.cpp +++ b/src/mesh/RadioInterface.cpp @@ -302,6 +302,8 @@ void printPacket(const char *prefix, const meshtastic_MeshPacket *p) out += DEBUG_PORT.mt_sprintf(" rxRSSI=%i", p->rx_rssi); if (p->via_mqtt != 0) out += DEBUG_PORT.mt_sprintf(" via MQTT"); + if (p->hop_start != 0) + out += DEBUG_PORT.mt_sprintf(" hopStart=%d", p->hop_start); if (p->priority != 0) out += DEBUG_PORT.mt_sprintf(" priority=%d", p->priority); @@ -561,6 +563,7 @@ size_t RadioInterface::beginSending(meshtastic_MeshPacket *p) p->hop_limit = HOP_RELIABLE; } h->flags = p->hop_limit | (p->want_ack ? PACKET_FLAGS_WANT_ACK_MASK : 0) | (p->via_mqtt ? PACKET_FLAGS_VIA_MQTT_MASK : 0); + h->flags |= (p->hop_start << PACKET_FLAGS_HOP_START_SHIFT) & PACKET_FLAGS_HOP_START_MASK; // if the sender nodenum is zero, that means uninitialized assert(h->from); @@ -569,4 +572,4 @@ size_t RadioInterface::beginSending(meshtastic_MeshPacket *p) sendingPacket = p; return p->encrypted.size + sizeof(PacketHeader); -} +} \ No newline at end of file diff --git a/src/mesh/RadioInterface.h b/src/mesh/RadioInterface.h index 83c5dae645..f85b3bfa54 100644 --- a/src/mesh/RadioInterface.h +++ b/src/mesh/RadioInterface.h @@ -10,9 +10,11 @@ #define MAX_RHPACKETLEN 256 -#define PACKET_FLAGS_HOP_MASK 0x07 +#define PACKET_FLAGS_HOP_LIMIT_MASK 0x07 #define PACKET_FLAGS_WANT_ACK_MASK 0x08 #define PACKET_FLAGS_VIA_MQTT_MASK 0x10 +#define PACKET_FLAGS_HOP_START_MASK 0xE0 +#define PACKET_FLAGS_HOP_START_SHIFT 5 /** * This structure has to exactly match the wire layout when sent over the radio link. Used to keep compatibility @@ -224,4 +226,4 @@ class RadioInterface }; /// Debug printing for packets -void printPacket(const char *prefix, const meshtastic_MeshPacket *p); +void printPacket(const char *prefix, const meshtastic_MeshPacket *p); \ No newline at end of file diff --git a/src/mesh/RadioLibInterface.cpp b/src/mesh/RadioLibInterface.cpp index 8a2bc53e5a..9f42afa6d2 100644 --- a/src/mesh/RadioLibInterface.cpp +++ b/src/mesh/RadioLibInterface.cpp @@ -359,8 +359,9 @@ void RadioLibInterface::handleReceiveInterrupt() 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; + assert(HOP_MAX <= PACKET_FLAGS_HOP_LIMIT_MASK); // If hopmax changes, carefully check this code + mp->hop_limit = h->flags & PACKET_FLAGS_HOP_LIMIT_MASK; + mp->hop_start = (h->flags & PACKET_FLAGS_HOP_START_MASK) >> PACKET_FLAGS_HOP_START_SHIFT; mp->want_ack = !!(h->flags & PACKET_FLAGS_WANT_ACK_MASK); mp->via_mqtt = !!(h->flags & PACKET_FLAGS_VIA_MQTT_MASK); @@ -407,4 +408,4 @@ void RadioLibInterface::startSend(meshtastic_MeshPacket *txp) // bits enableInterrupt(isrTxLevel0); } -} +} \ No newline at end of file diff --git a/src/mesh/ReliableRouter.cpp b/src/mesh/ReliableRouter.cpp index a1e9f281df..167a248ab0 100644 --- a/src/mesh/ReliableRouter.cpp +++ b/src/mesh/ReliableRouter.cpp @@ -71,12 +71,12 @@ bool ReliableRouter::shouldFilterReceived(const meshtastic_MeshPacket *p) i->second.nextTxMsec += iface->getPacketTime(p); } - /* Resend implicit ACKs for repeated packets (assuming the original packet was sent with HOP_RELIABLE) + /* Resend implicit ACKs for repeated packets (hopStart equals hopLimit); * this way if an implicit ACK is dropped and a packet is resent we'll rebroadcast again. * Resending real ACKs is omitted, as you might receive a packet multiple times due to flooding and * flooding this ACK back to the original sender already adds redundancy. */ - if (wasSeenRecently(p, false) && p->hop_limit == HOP_RELIABLE && !MeshModule::currentReply && p->to != nodeDB.getNodeNum()) { - // retransmission on broadcast has hop_limit still equal to HOP_RELIABLE + bool isRepeated = p->hop_start == 0 ? (p->hop_limit == HOP_RELIABLE) : (p->hop_start == p->hop_limit); + if (wasSeenRecently(p, false) && isRepeated && !MeshModule::currentReply && p->to != nodeDB.getNodeNum()) { LOG_DEBUG("Resending implicit ack for a repeated floodmsg\n"); meshtastic_MeshPacket *tosend = packetPool.allocCopy(*p); tosend->hop_limit--; // bump down the hop count @@ -107,10 +107,11 @@ void ReliableRouter::sniffReceived(const meshtastic_MeshPacket *p, const meshtas if (MeshModule::currentReply) { LOG_DEBUG("Some other module has replied to this message, no need for a 2nd ack\n"); } else if (p->which_payload_variant == meshtastic_MeshPacket_decoded_tag) { - sendAckNak(meshtastic_Routing_Error_NONE, getFrom(p), p->id, p->channel); + sendAckNak(meshtastic_Routing_Error_NONE, getFrom(p), p->id, p->channel, p->hop_start, p->hop_limit); } else { // Send a 'NO_CHANNEL' error on the primary channel if want_ack packet destined for us cannot be decoded - sendAckNak(meshtastic_Routing_Error_NO_CHANNEL, getFrom(p), p->id, channels.getPrimaryIndex()); + sendAckNak(meshtastic_Routing_Error_NO_CHANNEL, getFrom(p), p->id, channels.getPrimaryIndex(), p->hop_start, + p->hop_limit); } } @@ -255,4 +256,4 @@ void ReliableRouter::setNextTx(PendingPacket *pending) LOG_DEBUG("Setting next retransmission in %u msecs: ", d); printPacket("", pending->packet); setReceivedMessage(); // Run ASAP, so we can figure out our correct sleep time -} +} \ No newline at end of file diff --git a/src/mesh/Router.cpp b/src/mesh/Router.cpp index 1d6a2d96b9..7657d2268d 100644 --- a/src/mesh/Router.cpp +++ b/src/mesh/Router.cpp @@ -132,9 +132,10 @@ meshtastic_MeshPacket *Router::allocForSending() /** * Send an ack or a nak packet back towards whoever sent idFrom */ -void Router::sendAckNak(meshtastic_Routing_Error err, NodeNum to, PacketId idFrom, ChannelIndex chIndex) +void Router::sendAckNak(meshtastic_Routing_Error err, NodeNum to, PacketId idFrom, ChannelIndex chIndex, uint8_t hopStart, + uint8_t hopLimit) { - routingModule->sendAckNak(err, to, idFrom, chIndex); + routingModule->sendAckNak(err, to, idFrom, chIndex, hopStart, hopLimit); } void Router::abortSendAndNak(meshtastic_Routing_Error err, meshtastic_MeshPacket *p) @@ -240,6 +241,10 @@ ErrorCode Router::send(meshtastic_MeshPacket *p) // the lora we need to make sure we have replaced it with our local address p->from = getFrom(p); + // If we are the original transmitter, set the hop limit with which we start + if (p->from == getNodeNum()) + p->hop_start = p->hop_limit; + // If the packet hasn't yet been encrypted, do so now (it might already be encrypted if we are just forwarding it) assert(p->which_payload_variant == meshtastic_MeshPacket_encrypted_tag || diff --git a/src/mesh/Router.h b/src/mesh/Router.h index db810e42e7..98486745b0 100644 --- a/src/mesh/Router.h +++ b/src/mesh/Router.h @@ -104,7 +104,8 @@ class Router : protected concurrency::OSThread /** * Send an ack or a nak packet back towards whoever sent idFrom */ - void sendAckNak(meshtastic_Routing_Error err, NodeNum to, PacketId idFrom, ChannelIndex chIndex); + void sendAckNak(meshtastic_Routing_Error err, NodeNum to, PacketId idFrom, ChannelIndex chIndex, uint8_t hopStart = 0, + uint8_t hopLimit = 0); private: /** diff --git a/src/modules/NeighborInfoModule.cpp b/src/modules/NeighborInfoModule.cpp index 4541958fa6..2e0b04afa1 100644 --- a/src/modules/NeighborInfoModule.cpp +++ b/src/modules/NeighborInfoModule.cpp @@ -95,6 +95,7 @@ NeighborInfoModule::NeighborInfoModule() ourPortNum = meshtastic_PortNum_NEIGHBORINFO_APP; if (moduleConfig.neighbor_info.enabled) { + isPromiscuous = true; // Update neighbors from all packets this->loadProtoForModule(); setIntervalFromNow(35 * 1000); } else { @@ -202,9 +203,12 @@ Pass it to an upper client; do not persist this data on the mesh */ bool NeighborInfoModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp, meshtastic_NeighborInfo *np) { - if (enabled) { + if (np) { printNeighborInfo("RECEIVED", np); updateNeighbors(mp, np); + } else if (mp.hop_start != 0 && mp.hop_start == mp.hop_limit) { + // If the hopLimit is the same as hopStart, then it is a neighbor + getOrCreateNeighbor(mp.from, mp.from, 0, mp.rx_snr); // Set the broadcast interval to 0, as we don't know it } // Allow others to handle this packet return false; @@ -261,7 +265,7 @@ meshtastic_Neighbor *NeighborInfoModule::getOrCreateNeighbor(NodeNum originalSen nbr->snr = snr; nbr->last_rx_time = getTime(); // Only if this is the original sender, the broadcast interval corresponds to it - if (originalSender == n) + if (originalSender == n && node_broadcast_interval_secs != 0) nbr->node_broadcast_interval_secs = node_broadcast_interval_secs; saveProtoForModule(); // Save the updated neighbor return nbr; @@ -277,8 +281,10 @@ meshtastic_Neighbor *NeighborInfoModule::getOrCreateNeighbor(NodeNum originalSen new_nbr->snr = snr; new_nbr->last_rx_time = getTime(); // Only if this is the original sender, the broadcast interval corresponds to it - if (originalSender == n) + if (originalSender == n && node_broadcast_interval_secs != 0) new_nbr->node_broadcast_interval_secs = node_broadcast_interval_secs; + else // Assume the same broadcast interval as us for the neighbor if we don't know it + new_nbr->node_broadcast_interval_secs = moduleConfig.neighbor_info.update_interval; saveProtoForModule(); // Save the new neighbor return new_nbr; } diff --git a/src/modules/NeighborInfoModule.h b/src/modules/NeighborInfoModule.h index 0e3ec09ca3..df5c2c9489 100644 --- a/src/modules/NeighborInfoModule.h +++ b/src/modules/NeighborInfoModule.h @@ -75,6 +75,9 @@ class NeighborInfoModule : public ProtobufModule, priva /* Does our periodic broadcast */ int32_t runOnce() override; + // Override wantPacket to say we want to see all packets when enabled, not just those for our port number + virtual bool wantPacket(const meshtastic_MeshPacket *p) override { return enabled; } + /* These are for debugging only */ void printNeighborInfo(const char *header, const meshtastic_NeighborInfo *np); void printNodeDBNodes(const char *header); diff --git a/src/modules/RoutingModule.cpp b/src/modules/RoutingModule.cpp index edeb1fb860..37a7c37551 100644 --- a/src/modules/RoutingModule.cpp +++ b/src/modules/RoutingModule.cpp @@ -36,13 +36,28 @@ meshtastic_MeshPacket *RoutingModule::allocReply() return NULL; } -void RoutingModule::sendAckNak(meshtastic_Routing_Error err, NodeNum to, PacketId idFrom, ChannelIndex chIndex) +void RoutingModule::sendAckNak(meshtastic_Routing_Error err, NodeNum to, PacketId idFrom, ChannelIndex chIndex, uint8_t hopStart, + uint8_t hopLimit) { - auto p = allocAckNak(err, to, idFrom, chIndex); + auto p = allocAckNak(err, to, idFrom, chIndex, hopStart, hopLimit); router->sendLocal(p); // we sometimes send directly to the local node } +uint8_t RoutingModule::getHopLimitForResponse(uint8_t hopStart, uint8_t hopLimit) +{ + if (hopStart != 0) { + // Hops used by the request. If somebody in between running modified firmware modified it, ignore it + uint8_t hopsUsed = hopStart < hopLimit ? config.lora.hop_limit : hopStart - hopLimit; + if (hopsUsed > config.lora.hop_limit) { + return hopsUsed; // If the request used more hops than the limit, use the same amount of hops + } else if (hopsUsed + 2 < config.lora.hop_limit) { + return hopsUsed + 2; // Use only the amount of hops needed with some margin as the way back may be different + } + } + return config.lora.hop_limit; // Use the default hop limit +} + RoutingModule::RoutingModule() : ProtobufModule("routing", meshtastic_PortNum_ROUTING_APP, &meshtastic_Routing_msg) { isPromiscuous = true; diff --git a/src/modules/RoutingModule.h b/src/modules/RoutingModule.h index 06e76cfb4d..f085b307bf 100644 --- a/src/modules/RoutingModule.h +++ b/src/modules/RoutingModule.h @@ -13,7 +13,11 @@ class RoutingModule : public ProtobufModule */ RoutingModule(); - void sendAckNak(meshtastic_Routing_Error err, NodeNum to, PacketId idFrom, ChannelIndex chIndex); + void sendAckNak(meshtastic_Routing_Error err, NodeNum to, PacketId idFrom, ChannelIndex chIndex, uint8_t hopStart = 0, + uint8_t hopLimit = 0); + + // Given the hopStart and hopLimit upon reception of a request, return the hop limit to use for the response + uint8_t getHopLimitForResponse(uint8_t hopStart, uint8_t hopLimit); protected: friend class Router; From 7da1153c2c913903cc0c8603a81ade22f8cf34a8 Mon Sep 17 00:00:00 2001 From: Ben Meadors Date: Fri, 8 Mar 2024 08:31:49 -0600 Subject: [PATCH 12/29] Fix known_only panic by short circuiting for NULL before checking has_user (#3352) --- src/mesh/Router.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mesh/Router.cpp b/src/mesh/Router.cpp index 7657d2268d..7c739b8f28 100644 --- a/src/mesh/Router.cpp +++ b/src/mesh/Router.cpp @@ -297,7 +297,7 @@ bool perhapsDecode(meshtastic_MeshPacket *p) return false; if (config.device.rebroadcast_mode == meshtastic_Config_DeviceConfig_RebroadcastMode_KNOWN_ONLY && - !nodeDB.getMeshNode(p->from)->has_user) { + (nodeDB.getMeshNode(p->from) == NULL || !nodeDB.getMeshNode(p->from)->has_user)) { LOG_DEBUG("Node 0x%x not in NodeDB. Rebroadcast mode KNOWN_ONLY will ignore packet\n", p->from); return false; } From 0f1bc98305c3c6fd56b6d394aaf3d0e1ba1b66b9 Mon Sep 17 00:00:00 2001 From: Ben Meadors Date: Fri, 8 Mar 2024 20:15:00 -0600 Subject: [PATCH 13/29] Update MQTT topic to match (#3353) --- src/mqtt/MQTT.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/mqtt/MQTT.h b/src/mqtt/MQTT.h index 2b803e3fcd..e67958b25b 100644 --- a/src/mqtt/MQTT.h +++ b/src/mqtt/MQTT.h @@ -80,10 +80,10 @@ class MQTT : private concurrency::OSThread private: std::string statusTopic = "/2/stat/"; - std::string cryptTopic = "/2/c/"; // msh/2/c/CHANNELID/NODEID + std::string cryptTopic = "/2/e/"; // msh/2/e/CHANNELID/NODEID std::string jsonTopic = "/2/json/"; // msh/2/json/CHANNELID/NODEID - /** return true if we have a channel that wants uplink/downlink - */ + /** return true if we have a channel that wants uplink/downlink + */ bool wantsLink() const; /** Tell the server what subscriptions we want (based on channels.downlink_enabled) From 51df4fc7750b5affda52b82f901173195f201293 Mon Sep 17 00:00:00 2001 From: Andre K Date: Fri, 8 Mar 2024 23:15:37 -0300 Subject: [PATCH 14/29] fix: turn off T-Echo peripherals on deep sleep (#3162) Co-authored-by: Ben Meadors --- src/Power.cpp | 5 ----- src/graphics/EInkDisplay2.cpp | 5 ----- src/graphics/Screen.cpp | 3 +++ src/main.cpp | 9 +++++---- src/sleep.cpp | 7 +++++++ variants/rak10701/variant.h | 4 ---- variants/rak4631/variant.h | 4 ---- variants/rak4631_epaper/variant.h | 4 ---- variants/rak4631_epaper_onrxtx/variant.h | 4 ---- variants/t-echo/variant.h | 6 +++--- 10 files changed, 18 insertions(+), 33 deletions(-) diff --git a/src/Power.cpp b/src/Power.cpp index 8e44ddb983..3d1a1b9b27 100644 --- a/src/Power.cpp +++ b/src/Power.cpp @@ -473,11 +473,6 @@ bool Power::setup() void Power::shutdown() { - screen->setOn(false); -#if defined(USE_EINK) && defined(PIN_EINK_EN) - digitalWrite(PIN_EINK_EN, LOW); // power off backlight first -#endif - LOG_INFO("Shutting down\n"); #ifdef HAS_PMU diff --git a/src/graphics/EInkDisplay2.cpp b/src/graphics/EInkDisplay2.cpp index aee30c7f84..6ee4245b31 100644 --- a/src/graphics/EInkDisplay2.cpp +++ b/src/graphics/EInkDisplay2.cpp @@ -122,11 +122,6 @@ bool EInkDisplay::connect() { LOG_INFO("Doing EInk init\n"); -#ifdef PIN_EINK_PWR_ON - pinMode(PIN_EINK_PWR_ON, OUTPUT); - digitalWrite(PIN_EINK_PWR_ON, HIGH); // If we need to assert a pin to power external peripherals -#endif - #ifdef PIN_EINK_EN // backlight power, HIGH is backlight on, LOW is off pinMode(PIN_EINK_EN, OUTPUT); diff --git a/src/graphics/Screen.cpp b/src/graphics/Screen.cpp index 33df78462a..3ffea4a606 100644 --- a/src/graphics/Screen.cpp +++ b/src/graphics/Screen.cpp @@ -938,6 +938,9 @@ void Screen::doDeepSleep() static const int sleepFrameCount = sizeof(sleepFrames) / sizeof(sleepFrames[0]); ui->setFrames(sleepFrames, sleepFrameCount); ui->update(); +#ifdef PIN_EINK_EN + digitalWrite(PIN_EINK_EN, LOW); // power off backlight +#endif #endif setOn(false); } diff --git a/src/main.cpp b/src/main.cpp index b62ccf9865..ef1cd53c36 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -219,10 +219,11 @@ void setup() initDeepSleep(); - // Testing this fix für erratic T-Echo boot behaviour -#if defined(TTGO_T_ECHO) && defined(PIN_EINK_PWR_ON) - pinMode(PIN_EINK_PWR_ON, OUTPUT); - digitalWrite(PIN_EINK_PWR_ON, HIGH); + // power on peripherals +#if defined(TTGO_T_ECHO) && defined(PIN_POWER_EN) + pinMode(PIN_POWER_EN, OUTPUT); + digitalWrite(PIN_POWER_EN, HIGH); + digitalWrite(PIN_POWER_EN1, INPUT); #endif #if defined(VEXT_ENABLE_V03) diff --git a/src/sleep.cpp b/src/sleep.cpp index 1afba11730..bfacffeb9a 100644 --- a/src/sleep.cpp +++ b/src/sleep.cpp @@ -200,6 +200,13 @@ void doDeepSleep(uint32_t msecToWake, bool skipPreflight = false) nodeDB.saveToDisk(); +#ifdef TTGO_T_ECHO +#ifdef PIN_POWER_EN + pinMode(PIN_POWER_EN, INPUT); // power off peripherals + pinMode(PIN_POWER_EN1, INPUT_PULLDOWN); +#endif +#endif + // Kill GPS power completely (even if previously we just had it in sleep mode) if (gps) gps->setGPSPower(false, false, 0); diff --git a/variants/rak10701/variant.h b/variants/rak10701/variant.h index 837d081ffe..d6eeb71dcb 100644 --- a/variants/rak10701/variant.h +++ b/variants/rak10701/variant.h @@ -133,10 +133,6 @@ static const uint8_t SCK = PIN_SPI_SCK; #define PIN_EINK_SCLK (0 + 3) #define PIN_EINK_MOSI (0 + 30) // also called SDI -// Controls power for the eink display - Board power is enabled either by VBUS from USB or the CPU asserting PWR_ON -// FIXME - I think this is actually just the board power enable - it enables power to the CPU also -// #define PIN_EINK_PWR_ON (-1) - // #define USE_EINK // RAKRGB diff --git a/variants/rak4631/variant.h b/variants/rak4631/variant.h index 4ad99df44b..0ccf3b1d77 100644 --- a/variants/rak4631/variant.h +++ b/variants/rak4631/variant.h @@ -133,10 +133,6 @@ static const uint8_t SCK = PIN_SPI_SCK; #define PIN_EINK_SCLK (0 + 3) #define PIN_EINK_MOSI (0 + 30) // also called SDI -// Controls power for the eink display - Board power is enabled either by VBUS from USB or the CPU asserting PWR_ON -// FIXME - I think this is actually just the board power enable - it enables power to the CPU also -// #define PIN_EINK_PWR_ON (-1) - // #define USE_EINK // RAKRGB diff --git a/variants/rak4631_epaper/variant.h b/variants/rak4631_epaper/variant.h index d8a5e55977..b1bd84d217 100644 --- a/variants/rak4631_epaper/variant.h +++ b/variants/rak4631_epaper/variant.h @@ -133,10 +133,6 @@ static const uint8_t SCK = PIN_SPI_SCK; #define PIN_EINK_SCLK (0 + 3) #define PIN_EINK_MOSI (0 + 30) // also called SDI -// Controls power for the eink display - Board power is enabled either by VBUS from USB or the CPU asserting PWR_ON -// FIXME - I think this is actually just the board power enable - it enables power to the CPU also -// #define PIN_EINK_PWR_ON (-1) - #define USE_EINK // RAKRGB diff --git a/variants/rak4631_epaper_onrxtx/variant.h b/variants/rak4631_epaper_onrxtx/variant.h index 411e3eb171..ec53ebd33b 100644 --- a/variants/rak4631_epaper_onrxtx/variant.h +++ b/variants/rak4631_epaper_onrxtx/variant.h @@ -119,10 +119,6 @@ static const uint8_t SCK = PIN_SPI_SCK; #define PIN_EINK_SCLK (0 + 14) // SCL #define PIN_EINK_MOSI (0 + 13) // SDA -// Controls power for the eink display - Board power is enabled either by VBUS from USB or the CPU asserting PWR_ON -// FIXME - I think this is actually just the board power enable - it enables power to the CPU also -// #define PIN_EINK_PWR_ON (-1) - // RAKRGB #define HAS_NCP5623 diff --git a/variants/t-echo/variant.h b/variants/t-echo/variant.h index 1af68863e9..19a66719f2 100644 --- a/variants/t-echo/variant.h +++ b/variants/t-echo/variant.h @@ -156,9 +156,9 @@ External serial flash WP25R1635FZUIL0 #define PIN_EINK_SCLK (0 + 31) #define PIN_EINK_MOSI (0 + 29) // also called SDI -// Controls power for the eink display - Board power is enabled either by VBUS from USB or the CPU asserting PWR_ON -// FIXME - I think this is actually just the board power enable - it enables power to the CPU also -#define PIN_EINK_PWR_ON (0 + 12) +// Controls power for all peripherals (eink + GPS + LoRa + Sensor) +#define PIN_POWER_EN (0 + 12) +#define PIN_POWER_EN1 (0 + 13) #define USE_EINK From 29335a18f58d10ca7f86a234c921f5b3b8e1e469 Mon Sep 17 00:00:00 2001 From: Mark Trevor Birss Date: Sat, 9 Mar 2024 14:55:02 +0200 Subject: [PATCH 15/29] Update variant.h (#3354) --- variants/heltec_esp32c3/variant.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/variants/heltec_esp32c3/variant.h b/variants/heltec_esp32c3/variant.h index de6462a382..6641f9d218 100644 --- a/variants/heltec_esp32c3/variant.h +++ b/variants/heltec_esp32c3/variant.h @@ -9,7 +9,7 @@ #define LED_PIN 18 // LED #define LED_INVERTED 1 -#define HAS_SCREEN 0 +#define HAS_SCREEN 1 #define HAS_GPS 0 #undef GPS_RX_PIN #undef GPS_TX_PIN From 3efd606ea7fcc7cda763eff3d5cbab0aa80c2447 Mon Sep 17 00:00:00 2001 From: Ben Meadors Date: Sat, 9 Mar 2024 07:01:46 -0600 Subject: [PATCH 16/29] Bump to 2.3.0 --- version.properties | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/version.properties b/version.properties index 14d1884fb8..8927d17819 100644 --- a/version.properties +++ b/version.properties @@ -1,4 +1,4 @@ [VERSION] major = 2 -minor = 2 -build = 25 +minor = 3 +build = 0 From 3da7c0dba709c8b38a80371d59f1f693430cc65c Mon Sep 17 00:00:00 2001 From: GUVWAF <78759985+GUVWAF@users.noreply.github.com> Date: Sat, 9 Mar 2024 18:32:49 +0100 Subject: [PATCH 17/29] Add hops_away to JSON output (#3357) --- src/mqtt/MQTT.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/mqtt/MQTT.cpp b/src/mqtt/MQTT.cpp index 898607ecab..b250751770 100644 --- a/src/mqtt/MQTT.cpp +++ b/src/mqtt/MQTT.cpp @@ -808,6 +808,8 @@ std::string MQTT::meshPacketToJson(meshtastic_MeshPacket *mp) jsonObj["rssi"] = new JSONValue((int)mp->rx_rssi); if (mp->rx_snr != 0) jsonObj["snr"] = new JSONValue((float)mp->rx_snr); + if (mp->hop_start != 0 && mp->hop_limit <= mp->hop_start) + jsonObj["hops_away"] = new JSONValue((uint)(mp->hop_start - mp->hop_limit)); // serialize and write it to the stream JSONValue *value = new JSONValue(jsonObj); From 7167f1e04f3a7f76b4dc476bf36df581d6afeb9c Mon Sep 17 00:00:00 2001 From: Ben Meadors Date: Sat, 9 Mar 2024 15:25:16 -0600 Subject: [PATCH 18/29] Add parens to macro (#3361) --- src/gps/GeoCoord.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/gps/GeoCoord.h b/src/gps/GeoCoord.h index 9f911ed935..e811035db7 100644 --- a/src/gps/GeoCoord.h +++ b/src/gps/GeoCoord.h @@ -11,7 +11,7 @@ #define PI 3.1415926535897932384626433832795 #define OLC_CODE_LEN 11 -#define DEG_CONVERT 180 / PI +#define DEG_CONVERT (180 / PI) // Helper functions // Raises a number to an exponent, handling negative exponents. From dced888492a915e84d74cbd3fc2afa5d07ff54d8 Mon Sep 17 00:00:00 2001 From: Jonathan Bennett Date: Sat, 9 Mar 2024 13:40:03 -0600 Subject: [PATCH 19/29] Add precision_bit sto json --- src/mqtt/MQTT.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/mqtt/MQTT.cpp b/src/mqtt/MQTT.cpp index b250751770..619815e85a 100644 --- a/src/mqtt/MQTT.cpp +++ b/src/mqtt/MQTT.cpp @@ -655,6 +655,9 @@ std::string MQTT::meshPacketToJson(meshtastic_MeshPacket *mp) if ((int)decoded->VDOP) { msgPayload["VDOP"] = new JSONValue((int)decoded->VDOP); } + if ((int)decoded->precision_bits) { + msgPayload["precision_bits"] = new JSONValue((int)decoded->precision_bits); + } jsonObj["payload"] = new JSONValue(msgPayload); } else { LOG_ERROR("Error decoding protobuf for position message!\n"); From 3da1b74a103df8cdead31f671eb46ac2ac0acf2a Mon Sep 17 00:00:00 2001 From: Andre K Date: Sun, 10 Mar 2024 05:39:37 -0300 Subject: [PATCH 20/29] refactor: always send range tests with zero hops --- src/modules/RangeTestModule.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/modules/RangeTestModule.cpp b/src/modules/RangeTestModule.cpp index ecf4b70c79..b45068b45b 100644 --- a/src/modules/RangeTestModule.cpp +++ b/src/modules/RangeTestModule.cpp @@ -113,7 +113,7 @@ void RangeTestModuleRadio::sendPayload(NodeNum dest, bool wantReplies) meshtastic_MeshPacket *p = allocDataPacket(); p->to = dest; p->decoded.want_response = wantReplies; - + p->hop_limit = 0; p->want_ack = true; packetSequence++; @@ -295,4 +295,4 @@ bool RangeTestModuleRadio::appendFile(const meshtastic_MeshPacket &mp) #endif return 1; -} \ No newline at end of file +} From af9d14c370699c8a8cc618763052ecf12eccb461 Mon Sep 17 00:00:00 2001 From: GUVWAF Date: Sun, 10 Mar 2024 14:52:37 +0100 Subject: [PATCH 21/29] Periodic reporting of device information to a map via MQTT --- src/mesh/Channels.cpp | 20 +++++++ src/mesh/Channels.h | 3 + src/mesh/NodeDB.cpp | 7 ++- src/mesh/NodeDB.h | 8 ++- src/mesh/RadioInterface.cpp | 6 ++ src/mesh/RadioInterface.h | 3 + src/mqtt/MQTT.cpp | 107 +++++++++++++++++++++++++++++++----- src/mqtt/MQTT.h | 17 ++++-- 8 files changed, 149 insertions(+), 22 deletions(-) diff --git a/src/mesh/Channels.cpp b/src/mesh/Channels.cpp index 2d27c737de..b50ecf6ca4 100644 --- a/src/mesh/Channels.cpp +++ b/src/mesh/Channels.cpp @@ -2,6 +2,7 @@ #include "CryptoEngine.h" #include "DisplayFormatters.h" #include "NodeDB.h" +#include "RadioInterface.h" #include "configuration.h" #include @@ -254,6 +255,25 @@ const char *Channels::getName(size_t chIndex) return channelName; } +bool Channels::hasDefaultChannel() +{ + // If we don't use a preset or we override the frequency, we don't have a default channel + if (!config.lora.use_preset || config.lora.override_frequency) + return false; + // Check if any of the channels are using the default name and PSK + for (size_t i = 0; i < getNumChannels(); i++) { + const auto &ch = getByIndex(i); + if (ch.settings.psk.size == 1 && ch.settings.psk.bytes[0] == 1) { + const char *name = getName(i); + const char *presetName = DisplayFormatters::getModemPresetDisplayName(config.lora.modem_preset, false); + // Check if the name is the default derived from the modem preset and we use the default frequency slot + if (strcmp(name, presetName) == 0 && RadioInterface::uses_default_frequency_slot) + return true; + } + } + return false; +} + /** * Generate a short suffix used to disambiguate channels that might have the same "name" entered by the human but different PSKs. * The ideas is that the PSK changing should be visible to the user so that they see they probably messed up and that's why they diff --git a/src/mesh/Channels.h b/src/mesh/Channels.h index 87a72e07b1..0e11605c49 100644 --- a/src/mesh/Channels.h +++ b/src/mesh/Channels.h @@ -102,6 +102,9 @@ class Channels */ int16_t setActiveByIndex(ChannelIndex channelIndex); + // Returns true if we can be reached via a channel with the default settings given a region and modem preset + bool hasDefaultChannel(); + private: /** Given a channel index, change to use the crypto key specified by that index * diff --git a/src/mesh/NodeDB.cpp b/src/mesh/NodeDB.cpp index 787c16a797..9d7647138f 100644 --- a/src/mesh/NodeDB.cpp +++ b/src/mesh/NodeDB.cpp @@ -744,14 +744,17 @@ uint32_t sinceReceived(const meshtastic_MeshPacket *p) #define NUM_ONLINE_SECS (60 * 60 * 2) // 2 hrs to consider someone offline -size_t NodeDB::getNumOnlineMeshNodes() +size_t NodeDB::getNumOnlineMeshNodes(bool localOnly) { size_t numseen = 0; // FIXME this implementation is kinda expensive - for (int i = 0; i < *numMeshNodes; i++) + for (int i = 0; i < *numMeshNodes; i++) { + if (localOnly && meshNodes[i].via_mqtt) + continue; if (sinceLastSeen(&meshNodes[i]) < NUM_ONLINE_SECS) numseen++; + } return numseen; } diff --git a/src/mesh/NodeDB.h b/src/mesh/NodeDB.h index e24a971c16..8545b08d67 100644 --- a/src/mesh/NodeDB.h +++ b/src/mesh/NodeDB.h @@ -108,8 +108,10 @@ class NodeDB // get channel channel index we heard a nodeNum on, defaults to 0 if not found uint8_t getMeshNodeChannel(NodeNum n); - /// Return the number of nodes we've heard from recently (within the last 2 hrs?) - size_t getNumOnlineMeshNodes(); + /* Return the number of nodes we've heard from recently (within the last 2 hrs?) + * @param localOnly if true, ignore nodes heard via MQTT + */ + size_t getNumOnlineMeshNodes(bool localOnly = false); void initConfigIntervals(), initModuleConfigIntervals(), resetNodes(), removeNodeByNum(uint nodeNum); @@ -246,4 +248,4 @@ extern uint32_t error_address; #define Module_Config_size \ (ModuleConfig_CannedMessageConfig_size + ModuleConfig_ExternalNotificationConfig_size + ModuleConfig_MQTTConfig_size + \ ModuleConfig_RangeTestConfig_size + ModuleConfig_SerialConfig_size + ModuleConfig_StoreForwardConfig_size + \ - ModuleConfig_TelemetryConfig_size + ModuleConfig_size) + ModuleConfig_TelemetryConfig_size + ModuleConfig_size) \ No newline at end of file diff --git a/src/mesh/RadioInterface.cpp b/src/mesh/RadioInterface.cpp index c10eb26f6a..7a27112519 100644 --- a/src/mesh/RadioInterface.cpp +++ b/src/mesh/RadioInterface.cpp @@ -1,5 +1,6 @@ #include "RadioInterface.h" #include "Channels.h" +#include "DisplayFormatters.h" #include "MeshRadio.h" #include "MeshService.h" #include "NodeDB.h" @@ -143,6 +144,7 @@ const RegionInfo regions[] = { }; const RegionInfo *myRegion; +bool RadioInterface::uses_default_frequency_slot = true; static uint8_t bytes[MAX_RHPACKETLEN]; @@ -486,6 +488,10 @@ void RadioInterface::applyModemConfig() // channel_num is actually (channel_num - 1), since modulus (%) returns values from 0 to (numChannels - 1) int channel_num = (loraConfig.channel_num ? loraConfig.channel_num - 1 : hash(channelName)) % numChannels; + // Check if we use the default frequency slot + RadioInterface::uses_default_frequency_slot = + channel_num == hash(DisplayFormatters::getModemPresetDisplayName(config.lora.modem_preset, false)) % numChannels; + // Old frequency selection formula // float freq = myRegion->freqStart + ((((myRegion->freqEnd - myRegion->freqStart) / numChannels) / 2) * channel_num); diff --git a/src/mesh/RadioInterface.h b/src/mesh/RadioInterface.h index f85b3bfa54..ee4726d745 100644 --- a/src/mesh/RadioInterface.h +++ b/src/mesh/RadioInterface.h @@ -175,6 +175,9 @@ class RadioInterface /// Some boards (1st gen Pinetab Lora module) have broken IRQ wires, so we need to poll via i2c registers virtual bool isIRQPending() { return false; } + // Whether we use the default frequency slot given our LoRa config (region and modem preset) + static bool uses_default_frequency_slot; + protected: int8_t power = 17; // Set by applyModemConfig() diff --git a/src/mqtt/MQTT.cpp b/src/mqtt/MQTT.cpp index 898607ecab..426934be8b 100644 --- a/src/mqtt/MQTT.cpp +++ b/src/mqtt/MQTT.cpp @@ -77,8 +77,7 @@ void MQTT::onReceive(char *topic, byte *payload, size_t length) if (jsonPayloadStr.length() <= sizeof(p->decoded.payload.bytes)) { memcpy(p->decoded.payload.bytes, jsonPayloadStr.c_str(), jsonPayloadStr.length()); p->decoded.payload.size = jsonPayloadStr.length(); - meshtastic_MeshPacket *packet = packetPool.allocCopy(*p); - service.sendToMesh(packet, RX_SRC_LOCAL); + service.sendToMesh(p, RX_SRC_LOCAL); } else { LOG_WARN("Received MQTT json payload too long, dropping\n"); } @@ -192,6 +191,11 @@ MQTT::MQTT() : concurrency::OSThread("mqtt"), mqttQueue(MAX_MQTT_QUEUE) jsonTopic = "msh" + jsonTopic; } + if (moduleConfig.mqtt.map_reporting_enabled && moduleConfig.mqtt.has_map_report_settings) { + map_position_precision = moduleConfig.mqtt.map_report_settings.position_precision; + map_publish_interval_secs = moduleConfig.mqtt.map_report_settings.publish_interval_secs; + } + #ifdef HAS_NETWORKING if (!moduleConfig.mqtt.proxy_to_client_enabled) pubSub.setCallback(mqttCallback); @@ -365,27 +369,30 @@ void MQTT::sendSubscriptions() bool MQTT::wantsLink() const { - bool hasChannel = false; + bool hasChannelorMapReport = false; if (moduleConfig.mqtt.enabled) { - // No need for link if no channel needed it - size_t numChan = channels.getNumChannels(); - for (size_t i = 0; i < numChan; i++) { - const auto &ch = channels.getByIndex(i); - if (ch.settings.uplink_enabled || ch.settings.downlink_enabled) { - hasChannel = true; - break; + hasChannelorMapReport = moduleConfig.mqtt.map_reporting_enabled; + if (!hasChannelorMapReport) { + // No need for link if no channel needed it + size_t numChan = channels.getNumChannels(); + for (size_t i = 0; i < numChan; i++) { + const auto &ch = channels.getByIndex(i); + if (ch.settings.uplink_enabled || ch.settings.downlink_enabled) { + hasChannelorMapReport = true; + break; + } } } } - if (hasChannel && moduleConfig.mqtt.proxy_to_client_enabled) + if (hasChannelorMapReport && moduleConfig.mqtt.proxy_to_client_enabled) return true; #if HAS_WIFI - return hasChannel && WiFi.isConnected(); + return hasChannelorMapReport && WiFi.isConnected(); #endif #if HAS_ETHERNET - return hasChannel && Ethernet.linkStatus() == LinkON; + return hasChannelorMapReport && Ethernet.linkStatus() == LinkON; #endif return false; } @@ -397,6 +404,8 @@ int32_t MQTT::runOnce() bool wantConnection = wantsLink(); + perhapsReportToMap(); + // If connected poll rapidly, otherwise only occasionally check for a wifi connection change and ability to contact server if (moduleConfig.mqtt.proxy_to_client_enabled) { publishQueuedMessages(); @@ -536,6 +545,78 @@ void MQTT::onSend(const meshtastic_MeshPacket &mp, const meshtastic_MeshPacket & } } +void MQTT::perhapsReportToMap() +{ + if (!moduleConfig.mqtt.map_reporting_enabled || !(moduleConfig.mqtt.proxy_to_client_enabled || isConnectedDirectly())) + return; + + if (map_position_precision == 0 || (localPosition.latitude_i == 0 && localPosition.longitude_i == 0)) { + LOG_WARN("MQTT Map reporting is enabled, but precision is 0 or no position available.\n"); + return; + } + + if (millis() - last_report_to_map < map_publish_interval_secs * 1000) { + return; + } else { + // Allocate ServiceEnvelope and fill it + meshtastic_ServiceEnvelope *se = mqttPool.allocZeroed(); + se->channel_id = (char *)channels.getGlobalId(channels.getPrimaryIndex()); // Use primary channel as the channel_id + se->gateway_id = owner.id; + + // Allocate MeshPacket and fill it + meshtastic_MeshPacket *mp = packetPool.allocZeroed(); + mp->which_payload_variant = meshtastic_MeshPacket_decoded_tag; + mp->from = nodeDB.getNodeNum(); + mp->to = NODENUM_BROADCAST; + mp->decoded.portnum = meshtastic_PortNum_MAP_REPORT_APP; + + // Fill MapReport message + meshtastic_MapReport mapReport = meshtastic_MapReport_init_default; + memcpy(mapReport.long_name, owner.long_name, sizeof(owner.long_name)); + memcpy(mapReport.short_name, owner.short_name, sizeof(owner.short_name)); + mapReport.role = config.device.role; + mapReport.hw_model = owner.hw_model; + strncpy(mapReport.firmware_version, optstr(APP_VERSION), sizeof(mapReport.firmware_version)); + mapReport.region = config.lora.region; + mapReport.modem_preset = config.lora.modem_preset; + mapReport.has_default_channel = channels.hasDefaultChannel(); + + // Set position with precision (same as in PositionModule) + if (map_position_precision < 32 && map_position_precision > 0) { + mapReport.latitude_i = localPosition.latitude_i & (UINT32_MAX << (32 - map_position_precision)); + mapReport.longitude_i = localPosition.longitude_i & (UINT32_MAX << (32 - map_position_precision)); + mapReport.latitude_i += (1 << (31 - map_position_precision)); + mapReport.longitude_i += (1 << (31 - map_position_precision)); + } else { + mapReport.latitude_i = localPosition.latitude_i; + mapReport.longitude_i = localPosition.longitude_i; + } + mapReport.altitude = localPosition.altitude; + mapReport.position_precision = map_position_precision; + + mapReport.num_online_local_nodes = nodeDB.getNumOnlineMeshNodes(true); + + // Encode MapReport message and set it to MeshPacket in ServiceEnvelope + mp->decoded.payload.size = pb_encode_to_bytes(mp->decoded.payload.bytes, sizeof(mp->decoded.payload.bytes), + &meshtastic_MapReport_msg, &mapReport); + se->packet = mp; + + // FIXME - this size calculation is super sloppy, but it will go away once we dynamically alloc meshpackets + static uint8_t bytes[meshtastic_MeshPacket_size + 64]; + size_t numBytes = pb_encode_to_bytes(bytes, sizeof(bytes), &meshtastic_ServiceEnvelope_msg, se); + + LOG_INFO("MQTT Publish map report to %s\n", statusTopic.c_str()); + publish(statusTopic.c_str(), bytes, numBytes, false); + + // Release the allocated memory for ServiceEnvelope and MeshPacket + mqttPool.release(se); + packetPool.release(mp); + + // Update the last report time + last_report_to_map = millis(); + } +} + // converts a downstream packet into a json message std::string MQTT::meshPacketToJson(meshtastic_MeshPacket *mp) { diff --git a/src/mqtt/MQTT.h b/src/mqtt/MQTT.h index 2b803e3fcd..1599c7ae89 100644 --- a/src/mqtt/MQTT.h +++ b/src/mqtt/MQTT.h @@ -79,10 +79,16 @@ class MQTT : private concurrency::OSThread virtual int32_t runOnce() override; private: - std::string statusTopic = "/2/stat/"; - std::string cryptTopic = "/2/c/"; // msh/2/c/CHANNELID/NODEID - std::string jsonTopic = "/2/json/"; // msh/2/json/CHANNELID/NODEID - /** return true if we have a channel that wants uplink/downlink + std::string statusTopic = "/2/stat/"; // For "online"/"offline" message and MapReport + std::string cryptTopic = "/2/c/"; // msh/2/c/CHANNELID/NODEID + std::string jsonTopic = "/2/json/"; // msh/2/json/CHANNELID/NODEID + + // For map reporting (only applies when enabled) + uint32_t last_report_to_map = 0; + uint32_t map_position_precision = 32; // default to full precision + uint32_t map_publish_interval_secs = 60 * 15; // default to 15 minutes + + /** return true if we have a channel that wants uplink/downlink or map reporting is enabled */ bool wantsLink() const; @@ -102,6 +108,9 @@ class MQTT : private concurrency::OSThread void publishStatus(); void publishQueuedMessages(); + // Check if we should report unencrypted information about our node for consumption by a map + void perhapsReportToMap(); + // returns true if this is a valid JSON envelope which we accept on downlink bool isValidJsonEnvelope(JSONObject &json); From 69dcc948b9631e798f232c3669e114a8da0ab9a7 Mon Sep 17 00:00:00 2001 From: caveman99 Date: Sun, 10 Mar 2024 14:39:40 +0000 Subject: [PATCH 22/29] [create-pull-request] automated change --- protobufs | 2 +- src/mesh/generated/meshtastic/deviceonly.pb.h | 2 +- src/mesh/generated/meshtastic/localonly.pb.h | 2 +- .../generated/meshtastic/module_config.pb.c | 5 +- .../generated/meshtastic/module_config.pb.h | 42 ++++++++++-- src/mesh/generated/meshtastic/mqtt.pb.c | 3 + src/mesh/generated/meshtastic/mqtt.pb.h | 68 +++++++++++++++++++ src/mesh/generated/meshtastic/portnums.pb.h | 2 + 8 files changed, 117 insertions(+), 9 deletions(-) diff --git a/protobufs b/protobufs index 5a97acb175..00332412b2 160000 --- a/protobufs +++ b/protobufs @@ -1 +1 @@ -Subproject commit 5a97acb17543a10e114675a205e3274a83e721af +Subproject commit 00332412b238fe559175a6e83fdf8d31fa5e209a diff --git a/src/mesh/generated/meshtastic/deviceonly.pb.h b/src/mesh/generated/meshtastic/deviceonly.pb.h index ca4b2176bb..556821e1c6 100644 --- a/src/mesh/generated/meshtastic/deviceonly.pb.h +++ b/src/mesh/generated/meshtastic/deviceonly.pb.h @@ -324,7 +324,7 @@ extern const pb_msgdesc_t meshtastic_NodeRemoteHardwarePin_msg; #define meshtastic_DeviceState_size 17571 #define meshtastic_NodeInfoLite_size 158 #define meshtastic_NodeRemoteHardwarePin_size 29 -#define meshtastic_OEMStore_size 3246 +#define meshtastic_OEMStore_size 3262 #define meshtastic_PositionLite_size 28 #ifdef __cplusplus diff --git a/src/mesh/generated/meshtastic/localonly.pb.h b/src/mesh/generated/meshtastic/localonly.pb.h index 7d39da01f4..2e22cb1e4e 100644 --- a/src/mesh/generated/meshtastic/localonly.pb.h +++ b/src/mesh/generated/meshtastic/localonly.pb.h @@ -181,7 +181,7 @@ extern const pb_msgdesc_t meshtastic_LocalModuleConfig_msg; /* Maximum encoded size of messages (where known) */ #define meshtastic_LocalConfig_size 469 -#define meshtastic_LocalModuleConfig_size 631 +#define meshtastic_LocalModuleConfig_size 647 #ifdef __cplusplus } /* extern "C" */ diff --git a/src/mesh/generated/meshtastic/module_config.pb.c b/src/mesh/generated/meshtastic/module_config.pb.c index 38965f3e25..a75c3fb594 100644 --- a/src/mesh/generated/meshtastic/module_config.pb.c +++ b/src/mesh/generated/meshtastic/module_config.pb.c @@ -6,12 +6,15 @@ #error Regenerate this file with the current version of nanopb generator. #endif -PB_BIND(meshtastic_ModuleConfig, meshtastic_ModuleConfig, AUTO) +PB_BIND(meshtastic_ModuleConfig, meshtastic_ModuleConfig, 2) PB_BIND(meshtastic_ModuleConfig_MQTTConfig, meshtastic_ModuleConfig_MQTTConfig, AUTO) +PB_BIND(meshtastic_ModuleConfig_MapReportSettings, meshtastic_ModuleConfig_MapReportSettings, AUTO) + + PB_BIND(meshtastic_ModuleConfig_RemoteHardwareConfig, meshtastic_ModuleConfig_RemoteHardwareConfig, AUTO) diff --git a/src/mesh/generated/meshtastic/module_config.pb.h b/src/mesh/generated/meshtastic/module_config.pb.h index edfd56e4ca..2e1c25c7ff 100644 --- a/src/mesh/generated/meshtastic/module_config.pb.h +++ b/src/mesh/generated/meshtastic/module_config.pb.h @@ -84,6 +84,14 @@ typedef enum _meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar { } meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar; /* Struct definitions */ +/* Settings for reporting unencrypted information about our node to a map via MQTT */ +typedef struct _meshtastic_ModuleConfig_MapReportSettings { + /* How often we should report our info to the map (in seconds) */ + uint32_t publish_interval_secs; + /* Bits of precision for the location sent (default of 32 is full precision). */ + uint32_t position_precision; +} meshtastic_ModuleConfig_MapReportSettings; + /* MQTT Client Config */ typedef struct _meshtastic_ModuleConfig_MQTTConfig { /* If a meshtastic node is able to reach the internet it will normally attempt to gateway any channels that are marked as @@ -114,6 +122,11 @@ typedef struct _meshtastic_ModuleConfig_MQTTConfig { char root[16]; /* If true, we can use the connected phone / client to proxy messages to MQTT instead of a direct connection */ bool proxy_to_client_enabled; + /* If true, we will periodically report unencrypted information about our node to a map via MQTT */ + bool map_reporting_enabled; + /* Settings for reporting information about our node to a map via MQTT */ + bool has_map_report_settings; + meshtastic_ModuleConfig_MapReportSettings map_report_settings; } meshtastic_ModuleConfig_MQTTConfig; /* NeighborInfoModule Config */ @@ -427,6 +440,7 @@ extern "C" { + #define meshtastic_ModuleConfig_AudioConfig_bitrate_ENUMTYPE meshtastic_ModuleConfig_AudioConfig_Audio_Baud @@ -447,7 +461,8 @@ extern "C" { /* Initializer values for message structs */ #define meshtastic_ModuleConfig_init_default {0, {meshtastic_ModuleConfig_MQTTConfig_init_default}} -#define meshtastic_ModuleConfig_MQTTConfig_init_default {0, "", "", "", 0, 0, 0, "", 0} +#define meshtastic_ModuleConfig_MQTTConfig_init_default {0, "", "", "", 0, 0, 0, "", 0, 0, false, meshtastic_ModuleConfig_MapReportSettings_init_default} +#define meshtastic_ModuleConfig_MapReportSettings_init_default {0, 0} #define meshtastic_ModuleConfig_RemoteHardwareConfig_init_default {0, 0, 0, {meshtastic_RemoteHardwarePin_init_default, meshtastic_RemoteHardwarePin_init_default, meshtastic_RemoteHardwarePin_init_default, meshtastic_RemoteHardwarePin_init_default}} #define meshtastic_ModuleConfig_NeighborInfoConfig_init_default {0, 0} #define meshtastic_ModuleConfig_DetectionSensorConfig_init_default {0, 0, 0, 0, "", 0, 0, 0} @@ -462,7 +477,8 @@ extern "C" { #define meshtastic_ModuleConfig_AmbientLightingConfig_init_default {0, 0, 0, 0, 0} #define meshtastic_RemoteHardwarePin_init_default {0, "", _meshtastic_RemoteHardwarePinType_MIN} #define meshtastic_ModuleConfig_init_zero {0, {meshtastic_ModuleConfig_MQTTConfig_init_zero}} -#define meshtastic_ModuleConfig_MQTTConfig_init_zero {0, "", "", "", 0, 0, 0, "", 0} +#define meshtastic_ModuleConfig_MQTTConfig_init_zero {0, "", "", "", 0, 0, 0, "", 0, 0, false, meshtastic_ModuleConfig_MapReportSettings_init_zero} +#define meshtastic_ModuleConfig_MapReportSettings_init_zero {0, 0} #define meshtastic_ModuleConfig_RemoteHardwareConfig_init_zero {0, 0, 0, {meshtastic_RemoteHardwarePin_init_zero, meshtastic_RemoteHardwarePin_init_zero, meshtastic_RemoteHardwarePin_init_zero, meshtastic_RemoteHardwarePin_init_zero}} #define meshtastic_ModuleConfig_NeighborInfoConfig_init_zero {0, 0} #define meshtastic_ModuleConfig_DetectionSensorConfig_init_zero {0, 0, 0, 0, "", 0, 0, 0} @@ -478,6 +494,8 @@ extern "C" { #define meshtastic_RemoteHardwarePin_init_zero {0, "", _meshtastic_RemoteHardwarePinType_MIN} /* Field tags (for use in manual encoding/decoding) */ +#define meshtastic_ModuleConfig_MapReportSettings_publish_interval_secs_tag 1 +#define meshtastic_ModuleConfig_MapReportSettings_position_precision_tag 2 #define meshtastic_ModuleConfig_MQTTConfig_enabled_tag 1 #define meshtastic_ModuleConfig_MQTTConfig_address_tag 2 #define meshtastic_ModuleConfig_MQTTConfig_username_tag 3 @@ -487,6 +505,8 @@ extern "C" { #define meshtastic_ModuleConfig_MQTTConfig_tls_enabled_tag 7 #define meshtastic_ModuleConfig_MQTTConfig_root_tag 8 #define meshtastic_ModuleConfig_MQTTConfig_proxy_to_client_enabled_tag 9 +#define meshtastic_ModuleConfig_MQTTConfig_map_reporting_enabled_tag 10 +#define meshtastic_ModuleConfig_MQTTConfig_map_report_settings_tag 11 #define meshtastic_ModuleConfig_NeighborInfoConfig_enabled_tag 1 #define meshtastic_ModuleConfig_NeighborInfoConfig_update_interval_tag 2 #define meshtastic_ModuleConfig_DetectionSensorConfig_enabled_tag 1 @@ -623,9 +643,18 @@ X(a, STATIC, SINGULAR, BOOL, encryption_enabled, 5) \ X(a, STATIC, SINGULAR, BOOL, json_enabled, 6) \ X(a, STATIC, SINGULAR, BOOL, tls_enabled, 7) \ X(a, STATIC, SINGULAR, STRING, root, 8) \ -X(a, STATIC, SINGULAR, BOOL, proxy_to_client_enabled, 9) +X(a, STATIC, SINGULAR, BOOL, proxy_to_client_enabled, 9) \ +X(a, STATIC, SINGULAR, BOOL, map_reporting_enabled, 10) \ +X(a, STATIC, OPTIONAL, MESSAGE, map_report_settings, 11) #define meshtastic_ModuleConfig_MQTTConfig_CALLBACK NULL #define meshtastic_ModuleConfig_MQTTConfig_DEFAULT NULL +#define meshtastic_ModuleConfig_MQTTConfig_map_report_settings_MSGTYPE meshtastic_ModuleConfig_MapReportSettings + +#define meshtastic_ModuleConfig_MapReportSettings_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, UINT32, publish_interval_secs, 1) \ +X(a, STATIC, SINGULAR, UINT32, position_precision, 2) +#define meshtastic_ModuleConfig_MapReportSettings_CALLBACK NULL +#define meshtastic_ModuleConfig_MapReportSettings_DEFAULT NULL #define meshtastic_ModuleConfig_RemoteHardwareConfig_FIELDLIST(X, a) \ X(a, STATIC, SINGULAR, BOOL, enabled, 1) \ @@ -764,6 +793,7 @@ X(a, STATIC, SINGULAR, UENUM, type, 3) extern const pb_msgdesc_t meshtastic_ModuleConfig_msg; extern const pb_msgdesc_t meshtastic_ModuleConfig_MQTTConfig_msg; +extern const pb_msgdesc_t meshtastic_ModuleConfig_MapReportSettings_msg; extern const pb_msgdesc_t meshtastic_ModuleConfig_RemoteHardwareConfig_msg; extern const pb_msgdesc_t meshtastic_ModuleConfig_NeighborInfoConfig_msg; extern const pb_msgdesc_t meshtastic_ModuleConfig_DetectionSensorConfig_msg; @@ -781,6 +811,7 @@ extern const pb_msgdesc_t meshtastic_RemoteHardwarePin_msg; /* Defines for backwards compatibility with code written before nanopb-0.4.0 */ #define meshtastic_ModuleConfig_fields &meshtastic_ModuleConfig_msg #define meshtastic_ModuleConfig_MQTTConfig_fields &meshtastic_ModuleConfig_MQTTConfig_msg +#define meshtastic_ModuleConfig_MapReportSettings_fields &meshtastic_ModuleConfig_MapReportSettings_msg #define meshtastic_ModuleConfig_RemoteHardwareConfig_fields &meshtastic_ModuleConfig_RemoteHardwareConfig_msg #define meshtastic_ModuleConfig_NeighborInfoConfig_fields &meshtastic_ModuleConfig_NeighborInfoConfig_msg #define meshtastic_ModuleConfig_DetectionSensorConfig_fields &meshtastic_ModuleConfig_DetectionSensorConfig_msg @@ -801,7 +832,8 @@ extern const pb_msgdesc_t meshtastic_RemoteHardwarePin_msg; #define meshtastic_ModuleConfig_CannedMessageConfig_size 49 #define meshtastic_ModuleConfig_DetectionSensorConfig_size 44 #define meshtastic_ModuleConfig_ExternalNotificationConfig_size 42 -#define meshtastic_ModuleConfig_MQTTConfig_size 222 +#define meshtastic_ModuleConfig_MQTTConfig_size 238 +#define meshtastic_ModuleConfig_MapReportSettings_size 12 #define meshtastic_ModuleConfig_NeighborInfoConfig_size 8 #define meshtastic_ModuleConfig_PaxcounterConfig_size 8 #define meshtastic_ModuleConfig_RangeTestConfig_size 10 @@ -809,7 +841,7 @@ extern const pb_msgdesc_t meshtastic_RemoteHardwarePin_msg; #define meshtastic_ModuleConfig_SerialConfig_size 28 #define meshtastic_ModuleConfig_StoreForwardConfig_size 22 #define meshtastic_ModuleConfig_TelemetryConfig_size 36 -#define meshtastic_ModuleConfig_size 225 +#define meshtastic_ModuleConfig_size 241 #define meshtastic_RemoteHardwarePin_size 21 #ifdef __cplusplus diff --git a/src/mesh/generated/meshtastic/mqtt.pb.c b/src/mesh/generated/meshtastic/mqtt.pb.c index 3046e6109f..a43f364e14 100644 --- a/src/mesh/generated/meshtastic/mqtt.pb.c +++ b/src/mesh/generated/meshtastic/mqtt.pb.c @@ -9,4 +9,7 @@ PB_BIND(meshtastic_ServiceEnvelope, meshtastic_ServiceEnvelope, AUTO) +PB_BIND(meshtastic_MapReport, meshtastic_MapReport, AUTO) + + diff --git a/src/mesh/generated/meshtastic/mqtt.pb.h b/src/mesh/generated/meshtastic/mqtt.pb.h index 12e83c7245..8ca570d786 100644 --- a/src/mesh/generated/meshtastic/mqtt.pb.h +++ b/src/mesh/generated/meshtastic/mqtt.pb.h @@ -5,6 +5,7 @@ #define PB_MESHTASTIC_MESHTASTIC_MQTT_PB_H_INCLUDED #include #include "meshtastic/mesh.pb.h" +#include "meshtastic/config.pb.h" #if PB_PROTO_HEADER_VERSION != 40 #error Regenerate this file with the current version of nanopb generator. @@ -23,6 +24,38 @@ typedef struct _meshtastic_ServiceEnvelope { char *gateway_id; } meshtastic_ServiceEnvelope; +/* Information about a node intended to be reported unencrypted to a map using MQTT. */ +typedef struct _meshtastic_MapReport { + /* A full name for this user, i.e. "Kevin Hester" */ + char long_name[40]; + /* A VERY short name, ideally two characters. + Suitable for a tiny OLED screen */ + char short_name[5]; + /* Role of the node that applies specific settings for a particular use-case */ + meshtastic_Config_DeviceConfig_Role role; + /* Hardware model of the node, i.e. T-Beam, Heltec V3, etc... */ + meshtastic_HardwareModel hw_model; + /* Device firmware version string */ + char firmware_version[18]; + /* The region code for the radio (US, CN, EU433, etc...) */ + meshtastic_Config_LoRaConfig_RegionCode region; + /* Modem preset used by the radio (LongFast, MediumSlow, etc...) */ + meshtastic_Config_LoRaConfig_ModemPreset modem_preset; + /* Whether the node has a channel with default PSK and name (LongFast, MediumSlow, etc...) + and it uses the default frequency slot given the region and modem preset. */ + bool has_default_channel; + /* Latitude: multiply by 1e-7 to get degrees in floating point */ + int32_t latitude_i; + /* Longitude: multiply by 1e-7 to get degrees in floating point */ + int32_t longitude_i; + /* Altitude in meters above MSL */ + int32_t altitude; + /* Indicates the bits of precision for latitude and longitude set by the sending node */ + uint32_t position_precision; + /* Number of online nodes (heard in the last 2 hours) this node has in its list that were received locally (not via MQTT) */ + uint16_t num_online_local_nodes; +} meshtastic_MapReport; + #ifdef __cplusplus extern "C" { @@ -30,12 +63,27 @@ extern "C" { /* Initializer values for message structs */ #define meshtastic_ServiceEnvelope_init_default {NULL, NULL, NULL} +#define meshtastic_MapReport_init_default {"", "", _meshtastic_Config_DeviceConfig_Role_MIN, _meshtastic_HardwareModel_MIN, "", _meshtastic_Config_LoRaConfig_RegionCode_MIN, _meshtastic_Config_LoRaConfig_ModemPreset_MIN, 0, 0, 0, 0, 0, 0} #define meshtastic_ServiceEnvelope_init_zero {NULL, NULL, NULL} +#define meshtastic_MapReport_init_zero {"", "", _meshtastic_Config_DeviceConfig_Role_MIN, _meshtastic_HardwareModel_MIN, "", _meshtastic_Config_LoRaConfig_RegionCode_MIN, _meshtastic_Config_LoRaConfig_ModemPreset_MIN, 0, 0, 0, 0, 0, 0} /* Field tags (for use in manual encoding/decoding) */ #define meshtastic_ServiceEnvelope_packet_tag 1 #define meshtastic_ServiceEnvelope_channel_id_tag 2 #define meshtastic_ServiceEnvelope_gateway_id_tag 3 +#define meshtastic_MapReport_long_name_tag 1 +#define meshtastic_MapReport_short_name_tag 2 +#define meshtastic_MapReport_role_tag 3 +#define meshtastic_MapReport_hw_model_tag 4 +#define meshtastic_MapReport_firmware_version_tag 5 +#define meshtastic_MapReport_region_tag 6 +#define meshtastic_MapReport_modem_preset_tag 7 +#define meshtastic_MapReport_has_default_channel_tag 8 +#define meshtastic_MapReport_latitude_i_tag 9 +#define meshtastic_MapReport_longitude_i_tag 10 +#define meshtastic_MapReport_altitude_tag 11 +#define meshtastic_MapReport_position_precision_tag 12 +#define meshtastic_MapReport_num_online_local_nodes_tag 13 /* Struct field encoding specification for nanopb */ #define meshtastic_ServiceEnvelope_FIELDLIST(X, a) \ @@ -46,13 +94,33 @@ X(a, POINTER, SINGULAR, STRING, gateway_id, 3) #define meshtastic_ServiceEnvelope_DEFAULT NULL #define meshtastic_ServiceEnvelope_packet_MSGTYPE meshtastic_MeshPacket +#define meshtastic_MapReport_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, STRING, long_name, 1) \ +X(a, STATIC, SINGULAR, STRING, short_name, 2) \ +X(a, STATIC, SINGULAR, UENUM, role, 3) \ +X(a, STATIC, SINGULAR, UENUM, hw_model, 4) \ +X(a, STATIC, SINGULAR, STRING, firmware_version, 5) \ +X(a, STATIC, SINGULAR, UENUM, region, 6) \ +X(a, STATIC, SINGULAR, UENUM, modem_preset, 7) \ +X(a, STATIC, SINGULAR, BOOL, has_default_channel, 8) \ +X(a, STATIC, SINGULAR, SFIXED32, latitude_i, 9) \ +X(a, STATIC, SINGULAR, SFIXED32, longitude_i, 10) \ +X(a, STATIC, SINGULAR, INT32, altitude, 11) \ +X(a, STATIC, SINGULAR, UINT32, position_precision, 12) \ +X(a, STATIC, SINGULAR, UINT32, num_online_local_nodes, 13) +#define meshtastic_MapReport_CALLBACK NULL +#define meshtastic_MapReport_DEFAULT NULL + extern const pb_msgdesc_t meshtastic_ServiceEnvelope_msg; +extern const pb_msgdesc_t meshtastic_MapReport_msg; /* Defines for backwards compatibility with code written before nanopb-0.4.0 */ #define meshtastic_ServiceEnvelope_fields &meshtastic_ServiceEnvelope_msg +#define meshtastic_MapReport_fields &meshtastic_MapReport_msg /* Maximum encoded size of messages (where known) */ /* meshtastic_ServiceEnvelope_size depends on runtime parameters */ +#define meshtastic_MapReport_size 108 #ifdef __cplusplus } /* extern "C" */ diff --git a/src/mesh/generated/meshtastic/portnums.pb.h b/src/mesh/generated/meshtastic/portnums.pb.h index 3f3e9aaee8..f576c78939 100644 --- a/src/mesh/generated/meshtastic/portnums.pb.h +++ b/src/mesh/generated/meshtastic/portnums.pb.h @@ -122,6 +122,8 @@ typedef enum _meshtastic_PortNum { /* ATAK Plugin Portnum for payloads from the official Meshtastic ATAK plugin */ meshtastic_PortNum_ATAK_PLUGIN = 72, + /* Provides unencrypted information about a node for consumption by a map via MQTT */ + meshtastic_PortNum_MAP_REPORT_APP = 73, /* 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/firmware/blob/master/bin/regen-protos.sh)) */ From b45a912409c067b9ea6c0c41799a1128800cab4b Mon Sep 17 00:00:00 2001 From: GUVWAF Date: Sun, 10 Mar 2024 15:56:00 +0100 Subject: [PATCH 23/29] Use dedicated `map` topic --- src/mqtt/MQTT.cpp | 6 ++++-- src/mqtt/MQTT.h | 3 ++- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/mqtt/MQTT.cpp b/src/mqtt/MQTT.cpp index b33132aa40..2de35971ad 100644 --- a/src/mqtt/MQTT.cpp +++ b/src/mqtt/MQTT.cpp @@ -185,10 +185,12 @@ MQTT::MQTT() : concurrency::OSThread("mqtt"), mqttQueue(MAX_MQTT_QUEUE) statusTopic = moduleConfig.mqtt.root + statusTopic; cryptTopic = moduleConfig.mqtt.root + cryptTopic; jsonTopic = moduleConfig.mqtt.root + jsonTopic; + mapTopic = moduleConfig.mqtt.root + jsonTopic; } else { statusTopic = "msh" + statusTopic; cryptTopic = "msh" + cryptTopic; jsonTopic = "msh" + jsonTopic; + mapTopic = "msh" + mapTopic; } if (moduleConfig.mqtt.map_reporting_enabled && moduleConfig.mqtt.has_map_report_settings) { @@ -605,8 +607,8 @@ void MQTT::perhapsReportToMap() static uint8_t bytes[meshtastic_MeshPacket_size + 64]; size_t numBytes = pb_encode_to_bytes(bytes, sizeof(bytes), &meshtastic_ServiceEnvelope_msg, se); - LOG_INFO("MQTT Publish map report to %s\n", statusTopic.c_str()); - publish(statusTopic.c_str(), bytes, numBytes, false); + LOG_INFO("MQTT Publish map report to %s\n", mapTopic.c_str()); + publish(mapTopic.c_str(), bytes, numBytes, false); // Release the allocated memory for ServiceEnvelope and MeshPacket mqttPool.release(se); diff --git a/src/mqtt/MQTT.h b/src/mqtt/MQTT.h index b665a6efce..eeeb00d92e 100644 --- a/src/mqtt/MQTT.h +++ b/src/mqtt/MQTT.h @@ -79,9 +79,10 @@ class MQTT : private concurrency::OSThread virtual int32_t runOnce() override; private: - std::string statusTopic = "/2/stat/"; // For "online"/"offline" message and MapReport + std::string statusTopic = "/2/stat/"; // For "online"/"offline" message std::string cryptTopic = "/2/e/"; // msh/2/e/CHANNELID/NODEID std::string jsonTopic = "/2/json/"; // msh/2/json/CHANNELID/NODEID + std::string mapTopic = "/2/map/"; // For protobuf-encoded MapReport messages // For map reporting (only applies when enabled) uint32_t last_report_to_map = 0; From cb7407e06ba88f8c9d1aec45e0ccae2101cc5d99 Mon Sep 17 00:00:00 2001 From: GUVWAF Date: Sun, 10 Mar 2024 16:04:59 +0100 Subject: [PATCH 24/29] Don't need to check all channels if not using default frequency slot --- src/mesh/Channels.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/mesh/Channels.cpp b/src/mesh/Channels.cpp index b50ecf6ca4..3e9c782419 100644 --- a/src/mesh/Channels.cpp +++ b/src/mesh/Channels.cpp @@ -257,8 +257,8 @@ const char *Channels::getName(size_t chIndex) bool Channels::hasDefaultChannel() { - // If we don't use a preset or we override the frequency, we don't have a default channel - if (!config.lora.use_preset || config.lora.override_frequency) + // If we don't use a preset or the default frequency slot, or we override the frequency, we don't have a default channel + if (!config.lora.use_preset || !RadioInterface::uses_default_frequency_slot || config.lora.override_frequency) return false; // Check if any of the channels are using the default name and PSK for (size_t i = 0; i < getNumChannels(); i++) { @@ -266,8 +266,8 @@ bool Channels::hasDefaultChannel() if (ch.settings.psk.size == 1 && ch.settings.psk.bytes[0] == 1) { const char *name = getName(i); const char *presetName = DisplayFormatters::getModemPresetDisplayName(config.lora.modem_preset, false); - // Check if the name is the default derived from the modem preset and we use the default frequency slot - if (strcmp(name, presetName) == 0 && RadioInterface::uses_default_frequency_slot) + // Check if the name is the default derived from the modem preset + if (strcmp(name, presetName) == 0) return true; } } From fb4faf790ba28fd09344c61a713728e647f6c711 Mon Sep 17 00:00:00 2001 From: Thomas Herrmann Date: Tue, 5 Mar 2024 23:39:43 +0100 Subject: [PATCH 25/29] split query of paxcounter data from sending funcionality; don't cummulate (count mode != 1); use flag to signal changed count data --- src/modules/esp32/PaxcounterModule.cpp | 24 ++++++++++++++++++------ src/modules/esp32/PaxcounterModule.h | 3 +++ 2 files changed, 21 insertions(+), 6 deletions(-) diff --git a/src/modules/esp32/PaxcounterModule.cpp b/src/modules/esp32/PaxcounterModule.cpp index 2182ed1241..29edb069d9 100644 --- a/src/modules/esp32/PaxcounterModule.cpp +++ b/src/modules/esp32/PaxcounterModule.cpp @@ -7,8 +7,6 @@ PaxcounterModule *paxcounterModule; -void NullFunc(){}; - // paxcounterModule->sendInfo(NODENUM_BROADCAST); PaxcounterModule::PaxcounterModule() @@ -19,10 +17,14 @@ PaxcounterModule::PaxcounterModule() bool PaxcounterModule::sendInfo(NodeNum dest) { - libpax_counter_count(&count_from_libpax); - LOG_INFO("(Sending): pax: wifi=%d; ble=%d; uptime=%d\n", count_from_libpax.wifi_count, count_from_libpax.ble_count, + if (paxcounterModule->reportedDataSent) + return false; + + LOG_INFO("(Sending): pax: wifi=%d; ble=%d; uptime=%lu\n", count_from_libpax.wifi_count, count_from_libpax.ble_count, millis() / 1000); + paxcounterModule->reportedDataSent = true; + meshtastic_Paxcount pl = meshtastic_Paxcount_init_default; pl.wifi = count_from_libpax.wifi_count; pl.ble = count_from_libpax.ble_count; @@ -55,6 +57,14 @@ meshtastic_MeshPacket *PaxcounterModule::allocReply() return allocDataProtobuf(pl); } +void PaxcounterModule::handlePaxCounterReportRequest() +{ + // libpax_counter_count(&paxcounterModule->count_from_libpax); + LOG_INFO("(Reading): libPax reported new data: wifi=%d; ble=%d; uptime=%lu\n", paxcounterModule->count_from_libpax.wifi_count, + paxcounterModule->count_from_libpax.ble_count, millis() / 1000); + paxcounterModule->reportedDataSent = false; +} + int32_t PaxcounterModule::runOnce() { if (isActive()) { @@ -76,12 +86,14 @@ int32_t PaxcounterModule::runOnce() libpax_update_config(&configuration); // internal processing initialization - libpax_counter_init(NullFunc, &count_from_libpax, UINT16_MAX, 1); + libpax_counter_init(handlePaxCounterReportRequest, &count_from_libpax, + moduleConfig.paxcounter.paxcounter_update_interval, 0); libpax_counter_start(); } else { sendInfo(NODENUM_BROADCAST); } - return getConfiguredOrDefaultMs(moduleConfig.paxcounter.paxcounter_update_interval, default_broadcast_interval_secs); + // we check every second if the counter had new data to send + return 1000; } else { return disable(); } diff --git a/src/modules/esp32/PaxcounterModule.h b/src/modules/esp32/PaxcounterModule.h index e72f874505..67d47be560 100644 --- a/src/modules/esp32/PaxcounterModule.h +++ b/src/modules/esp32/PaxcounterModule.h @@ -13,10 +13,13 @@ class PaxcounterModule : private concurrency::OSThread, public ProtobufModule { bool firstTime = true; + bool reportedDataSent = true; public: PaxcounterModule(); + static void handlePaxCounterReportRequest(); + protected: struct count_payload_t count_from_libpax = {0, 0, 0}; virtual int32_t runOnce() override; From 73c77b663c0bff89aa28817d27faf667201088f6 Mon Sep 17 00:00:00 2001 From: Thomas Herrmann Date: Tue, 5 Mar 2024 23:48:52 +0100 Subject: [PATCH 26/29] fix typo --- src/modules/esp32/PaxcounterModule.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/modules/esp32/PaxcounterModule.cpp b/src/modules/esp32/PaxcounterModule.cpp index 29edb069d9..94fcca36fd 100644 --- a/src/modules/esp32/PaxcounterModule.cpp +++ b/src/modules/esp32/PaxcounterModule.cpp @@ -77,7 +77,7 @@ int32_t PaxcounterModule::runOnce() libpax_default_config(&configuration); configuration.blecounter = 1; - configuration.blescantime = 0; // infinit + configuration.blescantime = 0; // infinite configuration.wificounter = 1; configuration.wifi_channel_map = WIFI_CHANNEL_ALL; configuration.wifi_channel_switch_interval = 50; From 09e08e0091dc2c4ef2dfc2debdf7bda7b4f6f5b2 Mon Sep 17 00:00:00 2001 From: Thomas Herrmann Date: Wed, 6 Mar 2024 19:15:04 +0100 Subject: [PATCH 27/29] add some documentation, cleanup --- src/modules/esp32/PaxcounterModule.cpp | 39 +++++++++++++++++--------- src/modules/esp32/PaxcounterModule.h | 7 +++-- 2 files changed, 29 insertions(+), 17 deletions(-) diff --git a/src/modules/esp32/PaxcounterModule.cpp b/src/modules/esp32/PaxcounterModule.cpp index 94fcca36fd..e718d62613 100644 --- a/src/modules/esp32/PaxcounterModule.cpp +++ b/src/modules/esp32/PaxcounterModule.cpp @@ -7,7 +7,18 @@ PaxcounterModule *paxcounterModule; -// paxcounterModule->sendInfo(NODENUM_BROADCAST); +/** + * Callback function for libpax. + * We only clear our sent flag here, since this function is called from another thread, so we + * cannot send to the mesh directly. + */ +void PaxcounterModule::handlePaxCounterReportRequest() +{ + // The libpax library already updated our data structure, just before invoking this callback. + LOG_INFO("PaxcounterModule: libpax reported new data: wifi=%d; ble=%d; uptime=%lu\n", + paxcounterModule->count_from_libpax.wifi_count, paxcounterModule->count_from_libpax.ble_count, millis() / 1000); + paxcounterModule->reportedDataSent = false; +} PaxcounterModule::PaxcounterModule() : concurrency::OSThread("PaxcounterModule"), @@ -15,15 +26,20 @@ PaxcounterModule::PaxcounterModule() { } +/** + * Send the Pax information to the mesh if we got new data from libpax. + * This is called periodically from our runOnce() method and will actually send the data to the mesh + * if libpax updated it since the last transmission through the callback. + * @param dest - destination node (usually NODENUM_BROADCAST) + * @return false if sending is unnecessary, true if information was sent + */ bool PaxcounterModule::sendInfo(NodeNum dest) { if (paxcounterModule->reportedDataSent) return false; - LOG_INFO("(Sending): pax: wifi=%d; ble=%d; uptime=%lu\n", count_from_libpax.wifi_count, count_from_libpax.ble_count, - millis() / 1000); - - paxcounterModule->reportedDataSent = true; + LOG_INFO("PaxcounterModule: sending pax info wifi=%d; ble=%d; uptime=%lu\n", count_from_libpax.wifi_count, + count_from_libpax.ble_count, millis() / 1000); meshtastic_Paxcount pl = meshtastic_Paxcount_init_default; pl.wifi = count_from_libpax.wifi_count; @@ -33,9 +49,12 @@ bool PaxcounterModule::sendInfo(NodeNum dest) meshtastic_MeshPacket *p = allocDataProtobuf(pl); p->to = dest; p->decoded.want_response = false; - p->priority = meshtastic_MeshPacket_Priority_MIN; + p->priority = meshtastic_MeshPacket_Priority_DEFAULT; service.sendToMesh(p, RX_SRC_LOCAL, true); + + paxcounterModule->reportedDataSent = true; + return true; } @@ -57,14 +76,6 @@ meshtastic_MeshPacket *PaxcounterModule::allocReply() return allocDataProtobuf(pl); } -void PaxcounterModule::handlePaxCounterReportRequest() -{ - // libpax_counter_count(&paxcounterModule->count_from_libpax); - LOG_INFO("(Reading): libPax reported new data: wifi=%d; ble=%d; uptime=%lu\n", paxcounterModule->count_from_libpax.wifi_count, - paxcounterModule->count_from_libpax.ble_count, millis() / 1000); - paxcounterModule->reportedDataSent = false; -} - int32_t PaxcounterModule::runOnce() { if (isActive()) { diff --git a/src/modules/esp32/PaxcounterModule.h b/src/modules/esp32/PaxcounterModule.h index 67d47be560..ebd6e71919 100644 --- a/src/modules/esp32/PaxcounterModule.h +++ b/src/modules/esp32/PaxcounterModule.h @@ -8,18 +8,19 @@ #include /** - * A simple example module that just replies with "Message received" to any message it receives. + * Wrapper module for the estimate passenger (PAX) count library (https://github.com/dbinfrago/libpax) which + * implements the core functionality of the ESP32 Paxcounter project (https://github.com/cyberman54/ESP32-Paxcounter) */ class PaxcounterModule : private concurrency::OSThread, public ProtobufModule { bool firstTime = true; bool reportedDataSent = true; + static void handlePaxCounterReportRequest(); + public: PaxcounterModule(); - static void handlePaxCounterReportRequest(); - protected: struct count_payload_t count_from_libpax = {0, 0, 0}; virtual int32_t runOnce() override; From 26691c0be7145b726eafad236def154cad3122d5 Mon Sep 17 00:00:00 2001 From: Thomas Herrmann Date: Fri, 8 Mar 2024 23:48:56 +0100 Subject: [PATCH 28/29] include requested change and suggestions on PR from @caveman99 --- src/modules/esp32/PaxcounterModule.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/modules/esp32/PaxcounterModule.cpp b/src/modules/esp32/PaxcounterModule.cpp index e718d62613..580fc46e19 100644 --- a/src/modules/esp32/PaxcounterModule.cpp +++ b/src/modules/esp32/PaxcounterModule.cpp @@ -18,6 +18,7 @@ void PaxcounterModule::handlePaxCounterReportRequest() LOG_INFO("PaxcounterModule: libpax reported new data: wifi=%d; ble=%d; uptime=%lu\n", paxcounterModule->count_from_libpax.wifi_count, paxcounterModule->count_from_libpax.ble_count, millis() / 1000); paxcounterModule->reportedDataSent = false; + paxcounterModule->setIntervalFromNow(0); } PaxcounterModule::PaxcounterModule() @@ -49,7 +50,7 @@ bool PaxcounterModule::sendInfo(NodeNum dest) meshtastic_MeshPacket *p = allocDataProtobuf(pl); p->to = dest; p->decoded.want_response = false; - p->priority = meshtastic_MeshPacket_Priority_DEFAULT; + p->priority = meshtastic_MeshPacket_Priority_MIN; service.sendToMesh(p, RX_SRC_LOCAL, true); @@ -103,8 +104,7 @@ int32_t PaxcounterModule::runOnce() } else { sendInfo(NODENUM_BROADCAST); } - // we check every second if the counter had new data to send - return 1000; + return getConfiguredOrDefaultMs(moduleConfig.paxcounter.paxcounter_update_interval, default_broadcast_interval_secs); } else { return disable(); } From 766beefbc5796f700e5d6d94f7f9d7682cc0a4c8 Mon Sep 17 00:00:00 2001 From: Kevin Cai Date: Sun, 10 Mar 2024 18:24:32 -0400 Subject: [PATCH 29/29] Update AccelerometerThread.h to work with T-Watch S3 --- src/AccelerometerThread.h | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/AccelerometerThread.h b/src/AccelerometerThread.h index 744f0ad647..9898f4d49d 100644 --- a/src/AccelerometerThread.h +++ b/src/AccelerometerThread.h @@ -103,12 +103,21 @@ class AccelerometerThread : public concurrency::OSThread #endif struct bma423_axes_remap remap_data; +#ifdef T_WATCH_S3 + remap_data.x_axis = 1; + remap_data.x_axis_sign = 0; + remap_data.y_axis = 0; + remap_data.y_axis_sign = 0; + remap_data.z_axis = 2; + remap_data.z_axis_sign = 1; +#else remap_data.x_axis = 0; remap_data.x_axis_sign = 1; remap_data.y_axis = 1; remap_data.y_axis_sign = 0; remap_data.z_axis = 2; remap_data.z_axis_sign = 1; +#endif // Need to raise the wrist function, need to set the correct axis bmaSensor.setRemapAxes(&remap_data); // sensor.enableFeature(BMA423_STEP_CNTR, true); @@ -171,4 +180,4 @@ class AccelerometerThread : public concurrency::OSThread Adafruit_LIS3DH lis; }; -} // namespace concurrency \ No newline at end of file +} // namespace concurrency