From c3b2b474c6f9247b01b815bf181fbe104d121a64 Mon Sep 17 00:00:00 2001 From: Jonathan Bennett Date: Wed, 16 Jul 2025 16:05:34 -0500 Subject: [PATCH 1/6] Drop NodeInfo packets if the is_licensed bit doesn't match owner (#7361) --- src/modules/NodeInfoModule.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/modules/NodeInfoModule.cpp b/src/modules/NodeInfoModule.cpp index cf9940e25..b6fee7703 100644 --- a/src/modules/NodeInfoModule.cpp +++ b/src/modules/NodeInfoModule.cpp @@ -14,6 +14,11 @@ bool NodeInfoModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp, mes { auto p = *pptr; + if (p.is_licensed != owner.is_licensed) { + LOG_WARN("Invalid nodeInfo detected, is_licensed mismatch!"); + return true; + } + // Coerce user.id to be derived from the node number snprintf(p.id, sizeof(p.id), "!%08x", getFrom(&mp)); From 55fc4fcd9024df2f711bc797a467cea274e9ab4a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20G=C3=B6ttgens?= Date: Thu, 17 Jul 2025 00:40:29 +0200 Subject: [PATCH 2/6] clean up double i2c init/scan code (#7359) --- src/main.cpp | 18 ++---------------- 1 file changed, 2 insertions(+), 16 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index 640f0b1fe..c37001307 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -515,25 +515,11 @@ void setup() LOG_INFO("Scan for i2c devices"); #endif -#if defined(I2C_SDA1) && defined(ARCH_RP2040) - Wire1.setSDA(I2C_SDA1); - Wire1.setSCL(I2C_SCL1); - Wire1.begin(); - i2cScanner->scanPort(ScanI2C::I2CPort::WIRE1); -#elif defined(I2C_SDA1) && !defined(ARCH_RP2040) - Wire1.begin(I2C_SDA1, I2C_SCL1); - i2cScanner->scanPort(ScanI2C::I2CPort::WIRE1); -#elif defined(NRF52840_XXAA) && (WIRE_INTERFACES_COUNT == 2) +#if defined(I2C_SDA1) || (defined(NRF52840_XXAA) && (WIRE_INTERFACES_COUNT == 2)) i2cScanner->scanPort(ScanI2C::I2CPort::WIRE1); #endif -#if defined(I2C_SDA) && defined(ARCH_RP2040) - Wire.setSDA(I2C_SDA); - Wire.setSCL(I2C_SCL); - Wire.begin(); - i2cScanner->scanPort(ScanI2C::I2CPort::WIRE); -#elif defined(I2C_SDA) && !defined(ARCH_RP2040) - Wire.begin(I2C_SDA, I2C_SCL); +#if defined(I2C_SDA) i2cScanner->scanPort(ScanI2C::I2CPort::WIRE); #elif defined(ARCH_PORTDUINO) if (settingsStrings[i2cdev] != "") { From 71b6508ad3a248360fe3d409eebdd44d1d3ad7b4 Mon Sep 17 00:00:00 2001 From: Jason P Date: Wed, 16 Jul 2025 19:44:23 -0500 Subject: [PATCH 3/6] BaseUI Updates (#7358) * Calculate the length of the right string and use it * Improve readability of Version Number * Prevent negative message IDs and proactively favorite DM'd nodes * Patch up Remove Favorite functionality * Fix warnings for TFT_MESH_* and hasSupportBrightness * Fix warning around casting variables * Correct Favorite Node Behavior to rebuild favorite nodes when updated. * Resolve bool kb_found issue not working for second discovery keyboards --------- Co-authored-by: Jonathan Bennett --- src/graphics/Screen.cpp | 2 ++ src/graphics/draw/DebugRenderer.cpp | 16 +++++++-- src/graphics/draw/MenuHandler.cpp | 17 ++++----- src/graphics/draw/NotificationRenderer.cpp | 2 +- src/graphics/draw/UIRenderer.cpp | 41 ++++++++++------------ src/graphics/draw/UIRenderer.h | 2 ++ src/input/cardKbI2cImpl.cpp | 1 + src/modules/CannedMessageModule.cpp | 8 ++++- 8 files changed, 52 insertions(+), 37 deletions(-) diff --git a/src/graphics/Screen.cpp b/src/graphics/Screen.cpp index dbd0445f7..b9c9e2fbf 100644 --- a/src/graphics/Screen.cpp +++ b/src/graphics/Screen.cpp @@ -864,6 +864,8 @@ void Screen::setFrames(FrameFocus focus) uint8_t previousFrameCount = framesetInfo.frameCount; FramesetInfo fsi; // Location of specific frames, for applying focus parameter + graphics::UIRenderer::rebuildFavoritedNodes(); + LOG_DEBUG("Show standard frames"); showingNormalScreen = true; diff --git a/src/graphics/draw/DebugRenderer.cpp b/src/graphics/draw/DebugRenderer.cpp index b1a901f99..5420d1b4b 100644 --- a/src/graphics/draw/DebugRenderer.cpp +++ b/src/graphics/draw/DebugRenderer.cpp @@ -483,7 +483,7 @@ void drawLoRaFocused(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, } // **************************** -// * Memory Screen * +// * System Screen * // **************************** void drawMemoryUsage(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y) { @@ -593,7 +593,19 @@ void drawMemoryUsage(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, } line += 1; char appversionstr[35]; - snprintf(appversionstr, sizeof(appversionstr), "Ver.: %s", optstr(APP_VERSION)); + snprintf(appversionstr, sizeof(appversionstr), "Ver: %s", optstr(APP_VERSION)); + char appversionstr_formatted[40]; + char *lastDot = strrchr(appversionstr, '.'); + if (lastDot) { + size_t prefixLen = lastDot - appversionstr; + strncpy(appversionstr_formatted, appversionstr, prefixLen); + appversionstr_formatted[prefixLen] = '\0'; + strncat(appversionstr_formatted, " (", sizeof(appversionstr_formatted) - strlen(appversionstr_formatted) - 1); + strncat(appversionstr_formatted, lastDot + 1, sizeof(appversionstr_formatted) - strlen(appversionstr_formatted) - 1); + strncat(appversionstr_formatted, ")", sizeof(appversionstr_formatted) - strlen(appversionstr_formatted) - 1); + strncpy(appversionstr, appversionstr_formatted, sizeof(appversionstr) - 1); + appversionstr[sizeof(appversionstr) - 1] = '\0'; + } int textWidth = display->getStringWidth(appversionstr); int nameX = (SCREEN_WIDTH - textWidth) / 2; display->drawString(nameX, getTextPositions(display)[line], appversionstr); diff --git a/src/graphics/draw/MenuHandler.cpp b/src/graphics/draw/MenuHandler.cpp index ded492da6..7ed9c4ea1 100644 --- a/src/graphics/draw/MenuHandler.cpp +++ b/src/graphics/draw/MenuHandler.cpp @@ -375,12 +375,6 @@ void menuHandler::textMessageBaseMenu() void menuHandler::systemBaseMenu() { - // Check if brightness is supported - bool hasSupportBrightness = false; -#if defined(ST7789_CS) || defined(USE_OLED) || defined(USE_SSD1306) || defined(USE_SH1106) || defined(USE_SH1107) || HAS_TFT - hasSupportBrightness = true; -#endif - enum optionsNumbers { Back, Notifications, ScreenOptions, PowerMenu, Test, enumEnd }; static const char *optionsArray[enumEnd] = {"Back"}; static int optionsEnumArray[enumEnd] = {Back}; @@ -450,11 +444,11 @@ void menuHandler::favoriteBaseMenu() bannerOptions.optionsEnumPtr = optionsEnumArray; bannerOptions.optionsCount = options; bannerOptions.bannerCallback = [](int selected) -> void { - if (selected == 1) { + if (selected == Preset) { cannedMessageModule->LaunchWithDestination(graphics::UIRenderer::currentFavoriteNodeNum); - } else if (selected == 2 && kb_found) { + } else if (selected == Freetext) { cannedMessageModule->LaunchFreetextWithDestination(graphics::UIRenderer::currentFavoriteNodeNum); - } else if ((!kb_found && selected == 2) || (selected == 3 && kb_found)) { + } else if (selected == Remove) { menuHandler::menuQueue = menuHandler::remove_favorite; screen->runNow(); } @@ -707,6 +701,7 @@ void menuHandler::TFTColorPickerMenu(OLEDDisplay *display) bannerOptions.optionsArrayPtr = optionsArray; bannerOptions.optionsCount = 10; bannerOptions.bannerCallback = [display](int selected) -> void { +#if defined(HELTEC_MESH_NODE_T114) || defined(HELTEC_VISION_MASTER_T190) || defined(T_DECK) || HAS_TFT uint8_t TFT_MESH_r = 0; uint8_t TFT_MESH_g = 0; uint8_t TFT_MESH_b = 0; @@ -758,7 +753,6 @@ void menuHandler::TFTColorPickerMenu(OLEDDisplay *display) screen->runNow(); } -#if defined(HELTEC_MESH_NODE_T114) || defined(HELTEC_VISION_MASTER_T190) || defined(T_DECK) || HAS_TFT if (selected != 0) { display->setColor(BLACK); display->fillRect(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT); @@ -856,8 +850,9 @@ void menuHandler::removeFavoriteMenu() bannerOptions.optionsCount = 2; bannerOptions.bannerCallback = [](int selected) -> void { if (selected == 1) { + LOG_INFO("Removing %x as favorite node", graphics::UIRenderer::currentFavoriteNodeNum); nodeDB->set_favorite(false, graphics::UIRenderer::currentFavoriteNodeNum); - screen->setFrames(graphics::Screen::FOCUS_PRESERVE); + screen->setFrames(graphics::Screen::FOCUS_DEFAULT); } }; screen->showOverlayBanner(bannerOptions); diff --git a/src/graphics/draw/NotificationRenderer.cpp b/src/graphics/draw/NotificationRenderer.cpp index 7350c204f..d9cf280ac 100644 --- a/src/graphics/draw/NotificationRenderer.cpp +++ b/src/graphics/draw/NotificationRenderer.cpp @@ -156,7 +156,7 @@ void NotificationRenderer::drawNumberPicker(OLEDDisplay *display, OLEDDisplayUiS resetBanner(); return; } - if (curSelected == numDigits) { + if (curSelected == static_cast(numDigits)) { alertBannerCallback(currentNumber); resetBanner(); return; diff --git a/src/graphics/draw/UIRenderer.cpp b/src/graphics/draw/UIRenderer.cpp index 9be8b04f4..71d92616f 100644 --- a/src/graphics/draw/UIRenderer.cpp +++ b/src/graphics/draw/UIRenderer.cpp @@ -24,6 +24,23 @@ extern graphics::Screen *screen; namespace graphics { NodeNum UIRenderer::currentFavoriteNodeNum = 0; +std::vector graphics::UIRenderer::favoritedNodes; + +void graphics::UIRenderer::rebuildFavoritedNodes() +{ + favoritedNodes.clear(); + size_t total = nodeDB->getNumMeshNodes(); + for (size_t i = 0; i < total; i++) { + meshtastic_NodeInfoLite *n = nodeDB->getMeshNodeByIndex(i); + if (!n || n->num == nodeDB->getNodeNum()) + continue; + if (n->is_favorite) + favoritedNodes.push_back(n); + } + + std::sort(favoritedNodes.begin(), favoritedNodes.end(), + [](const meshtastic_NodeInfoLite *a, const meshtastic_NodeInfoLite *b) { return a->num < b->num; }); +} #if !MESHTASTIC_EXCLUDE_GPS // GeoCoord object for coordinate conversions @@ -201,27 +218,7 @@ void UIRenderer::drawNodes(OLEDDisplay *display, int16_t x, int16_t y, const mes // ********************** void UIRenderer::drawNodeInfo(OLEDDisplay *display, const OLEDDisplayUiState *state, int16_t x, int16_t y) { - // --- Cache favorite nodes for the current frame only, to save computation --- - static std::vector favoritedNodes; - static int prevFrame = -1; - // --- Only rebuild favorites list if we're on a new frame --- - if (state->currentFrame != prevFrame) { - prevFrame = state->currentFrame; - favoritedNodes.clear(); - size_t total = nodeDB->getNumMeshNodes(); - for (size_t i = 0; i < total; i++) { - meshtastic_NodeInfoLite *n = nodeDB->getMeshNodeByIndex(i); - // Skip nulls and ourself - if (!n || n->num == nodeDB->getNodeNum()) - continue; - if (n->is_favorite) - favoritedNodes.push_back(n); - } - // Keep a stable, consistent display order - std::sort(favoritedNodes.begin(), favoritedNodes.end(), - [](const meshtastic_NodeInfoLite *a, const meshtastic_NodeInfoLite *b) { return a->num < b->num; }); - } if (favoritedNodes.empty()) return; @@ -657,7 +654,7 @@ void UIRenderer::drawDeviceFocused(OLEDDisplay *display, OLEDDisplayUiState *sta char combinedName[50]; snprintf(combinedName, sizeof(combinedName), "%s (%s)", longNameStr.empty() ? "" : longNameStr.c_str(), shortnameble); - if (SCREEN_WIDTH - (display->getStringWidth(longName) + display->getStringWidth(shortnameble)) > 10) { + if (SCREEN_WIDTH - (display->getStringWidth(combinedName)) > 10) { size_t len = strlen(combinedName); if (len >= 3 && strcmp(combinedName + len - 3, " ()") == 0) { combinedName[len - 3] = '\0'; // Remove the last three characters @@ -668,7 +665,7 @@ void UIRenderer::drawDeviceFocused(OLEDDisplay *display, OLEDDisplayUiState *sta nameX, ((rows == 4) ? getTextPositions(display)[line++] : getTextPositions(display)[line++]) + yOffset, combinedName); } else { // === LongName Centered === - textWidth = display->getStringWidth(longName); + textWidth = display->getStringWidth(longNameStr.c_str()); nameX = (SCREEN_WIDTH - textWidth) / 2; display->drawString(nameX, getTextPositions(display)[line++], longNameStr.c_str()); diff --git a/src/graphics/draw/UIRenderer.h b/src/graphics/draw/UIRenderer.h index 9e5e8c4b4..3c8e1dd9d 100644 --- a/src/graphics/draw/UIRenderer.h +++ b/src/graphics/draw/UIRenderer.h @@ -61,6 +61,8 @@ class UIRenderer static void drawCompassAndLocationScreen(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y); static NodeNum currentFavoriteNodeNum; + static std::vector favoritedNodes; + static void rebuildFavoritedNodes(); // OEM screens #ifdef USERPREFS_OEM_TEXT diff --git a/src/input/cardKbI2cImpl.cpp b/src/input/cardKbI2cImpl.cpp index 21ecf381a..fcbdd0a3f 100644 --- a/src/input/cardKbI2cImpl.cpp +++ b/src/input/cardKbI2cImpl.cpp @@ -67,4 +67,5 @@ void CardKbI2cImpl::init() } #endif inputBroker->registerSource(this); + kb_found = true; } \ No newline at end of file diff --git a/src/modules/CannedMessageModule.cpp b/src/modules/CannedMessageModule.cpp index a1b89e0f8..2690c67f0 100644 --- a/src/modules/CannedMessageModule.cpp +++ b/src/modules/CannedMessageModule.cpp @@ -850,7 +850,13 @@ void CannedMessageModule::sendText(NodeNum dest, ChannelIndex channel, const cha this->waitingForAck = true; // Log outgoing message - LOG_INFO("Send message id=%d, dest=%x, msg=%.*s", p->id, p->to, p->decoded.payload.size, p->decoded.payload.bytes); + LOG_INFO("Send message id=%u, dest=%x, msg=%.*s", p->id, p->to, p->decoded.payload.size, p->decoded.payload.bytes); + + if (p->to != 0xffffffff) { + LOG_INFO("Proactively adding %x as favorite node", p->to); + nodeDB->set_favorite(true, p->to); + screen->setFrames(graphics::Screen::FOCUS_PRESERVE); + } // Send to mesh and phone (even if no phone connected, to track ACKs) service->sendToMesh(p, RX_SRC_LOCAL, true); From abe0a34fc0092937364c9045d7514a8ef568af74 Mon Sep 17 00:00:00 2001 From: Tom Fifield Date: Fri, 18 Jul 2025 20:49:19 +1000 Subject: [PATCH 4/6] Add additional Epoch check for time set (#7375) We have two perhapsSetRTC functions, which are called to set the time. The one with 3 parameters had a helpful check to reject an invalid time, by comparing the time from the source against when the firmware was compiled. The one with 2 parameters, which is called from the GPS lookForTime did not. As a result, certain GPS with bad time handling could set a time that was in the past. This patch adds the same epoch check code to the other perhapsSetRTC method. Fixes https://github.com/meshtastic/firmware/issues/7364 --- src/gps/RTC.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/gps/RTC.cpp b/src/gps/RTC.cpp index 5054be3f0..c4d6065ff 100644 --- a/src/gps/RTC.cpp +++ b/src/gps/RTC.cpp @@ -228,6 +228,13 @@ RTCSetResult perhapsSetRTC(RTCQuality q, struct tm &t) tv.tv_sec = res; tv.tv_usec = 0; // time.centisecond() * (10 / 1000); +#ifdef BUILD_EPOCH + if (tv->tv_sec < BUILD_EPOCH) { + LOG_WARN("Ignore time (%ld) before build epoch (%ld)!", printableEpoch, BUILD_EPOCH); + return RTCSetResultInvalidTime; + } +#endif + // LOG_DEBUG("Got time from GPS month=%d, year=%d, unixtime=%ld", t.tm_mon, t.tm_year, tv.tv_sec); if (t.tm_year < 0 || t.tm_year >= 300) { // LOG_DEBUG("Ignore invalid GPS month=%d, year=%d, unixtime=%ld", t.tm_mon, t.tm_year, tv.tv_sec); From cf574c71d8dbf6a28e279286b90deb7658e10bb4 Mon Sep 17 00:00:00 2001 From: Ben Meadors Date: Fri, 18 Jul 2025 09:24:34 -0500 Subject: [PATCH 5/6] Fix build --- src/gps/RTC.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/gps/RTC.cpp b/src/gps/RTC.cpp index c4d6065ff..d574c9ad0 100644 --- a/src/gps/RTC.cpp +++ b/src/gps/RTC.cpp @@ -226,10 +226,10 @@ RTCSetResult perhapsSetRTC(RTCQuality q, struct tm &t) time_t res = gm_mktime(&t); struct timeval tv; tv.tv_sec = res; - tv.tv_usec = 0; // time.centisecond() * (10 / 1000); - + tv.tv_usec = 0; // time.centisecond() * (10 / 1000); + uint32_t printableEpoch = tv.tv_sec; // Print lib only supports 32 bit but time_t can be 64 bit on some platforms #ifdef BUILD_EPOCH - if (tv->tv_sec < BUILD_EPOCH) { + if (tv.tv_sec < BUILD_EPOCH) { LOG_WARN("Ignore time (%ld) before build epoch (%ld)!", printableEpoch, BUILD_EPOCH); return RTCSetResultInvalidTime; } From 3ca45ae99cac60073def445fcbed9787e9f1f5ac Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sat, 19 Jul 2025 10:41:01 -0500 Subject: [PATCH 6/6] automated bumps (#7383) Co-authored-by: thebentern <9000580+thebentern@users.noreply.github.com> --- bin/org.meshtastic.meshtasticd.metainfo.xml | 3 +++ debian/changelog | 7 +++++-- version.properties | 2 +- 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/bin/org.meshtastic.meshtasticd.metainfo.xml b/bin/org.meshtastic.meshtasticd.metainfo.xml index 291fe7a7c..116155807 100644 --- a/bin/org.meshtastic.meshtasticd.metainfo.xml +++ b/bin/org.meshtastic.meshtasticd.metainfo.xml @@ -87,6 +87,9 @@ + + https://github.com/meshtastic/firmware/releases?q=tag%3Av2.7.4 + https://github.com/meshtastic/firmware/releases?q=tag%3Av2.7.3 diff --git a/debian/changelog b/debian/changelog index b5009028a..02a32f2f1 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,4 +1,4 @@ -meshtasticd (2.7.3.0) UNRELEASED; urgency=medium +meshtasticd (2.7.4.0) UNRELEASED; urgency=medium [ Austin Lane ] * Initial packaging @@ -31,4 +31,7 @@ meshtasticd (2.7.3.0) UNRELEASED; urgency=medium [ Ubuntu ] * GitHub Actions Automatic version bump - -- Ubuntu Thu, 10 Jul 2025 16:29:27 +0000 + [ ] + * GitHub Actions Automatic version bump + + -- Sat, 19 Jul 2025 11:36:55 +0000 diff --git a/version.properties b/version.properties index 5de810523..aa959bcac 100644 --- a/version.properties +++ b/version.properties @@ -1,4 +1,4 @@ [VERSION] major = 2 minor = 7 -build = 3 +build = 4