From 777e11bad97777d6351c04cf0e09c4983b0faff0 Mon Sep 17 00:00:00 2001 From: Clive Blackledge Date: Sun, 28 Sep 2025 14:42:51 -0700 Subject: [PATCH 01/15] Bug / Send upgraded (duplicate) packets to phone if the queue removal failed. (#8148) * Add seenRecently = true if wasUpgraded is true but unable to remove from queue (i.e. already sent/processed). * Consistent comment between FloodingRouter and HopRouter --- src/mesh/FloodingRouter.cpp | 4 ++++ src/mesh/NextHopRouter.cpp | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/src/mesh/FloodingRouter.cpp b/src/mesh/FloodingRouter.cpp index 3caee78f8..7ce106317 100644 --- a/src/mesh/FloodingRouter.cpp +++ b/src/mesh/FloodingRouter.cpp @@ -54,6 +54,10 @@ bool FloodingRouter::shouldFilterReceived(const meshtastic_MeshPacket *p) // We already enqueued the improved copy, so make sure the incoming packet stops here. return true; } + + // No queue entry was replaced by this upgraded copy, so treat it as a duplicate to avoid + // delivering the same packet to applications/phone twice with different hop limits. + seenRecently = true; } if (seenRecently) { diff --git a/src/mesh/NextHopRouter.cpp b/src/mesh/NextHopRouter.cpp index 4ae0a818e..dce436471 100644 --- a/src/mesh/NextHopRouter.cpp +++ b/src/mesh/NextHopRouter.cpp @@ -64,6 +64,10 @@ bool NextHopRouter::shouldFilterReceived(const meshtastic_MeshPacket *p) // We already enqueued the improved copy, so make sure the incoming packet stops here. return true; } + + // No queue entry was replaced by this upgraded copy, so treat it as a duplicate to avoid + // delivering the same packet to applications/phone twice with different hop limits. + seenRecently = true; } if (seenRecently) { From a3e6f16378bfd89bcae91191dfd734278dd9cc6b Mon Sep 17 00:00:00 2001 From: Tom Fifield Date: Tue, 30 Sep 2025 08:20:39 +1000 Subject: [PATCH 02/15] Introduce non-linear TX_GAIN_LORA (#8107) * Introduce non-linear TX_GAIN_LORA Previously, our TX_GAIN_LORA setting was a single number, intended to represent the signal gain going through a power amp (plus or minus antenna, attenuator, and other parts of the RF chain). It turns out the relationship between the input power (i.e. from an SX1262) and total output power is often non-linear. While we fudged a 1dBm difference here and there with existing chips, the Heltec v4 has a 5dBm difference in gain depending on which end of the input power (and frequency) you are at. To allow people to run their Heltec v4 at max power when legal, and future proof our code, this patch introduced an optional array-based TX_GAIN_LORA. Define NUM_PA_POINTS and set TX_GAIN_LORA to gain values for a given input power in 1dBm increments, and all will work. For linear systems, just continue to define TX_GAIN_LORA as a number. Fixes https://github.com/meshtastic/firmware/issues/8070 * Remove temporary power limit on heltec v4 * Add function RadioLibInterface::checkOutputPower * Ensure SX126x reaches minimum supported power. * Keep it simple, instead. --- src/configuration.h | 6 ++++++ src/mesh/RadioInterface.cpp | 14 ++++++++++++++ src/mesh/SX126xInterface.cpp | 7 +++++-- variants/esp32s3/heltec_v4/platformio.ini | 1 - 4 files changed, 25 insertions(+), 3 deletions(-) diff --git a/src/configuration.h b/src/configuration.h index 1b386ec17..91181890b 100644 --- a/src/configuration.h +++ b/src/configuration.h @@ -117,6 +117,12 @@ along with this program. If not, see . #define SX126X_MAX_POWER 22 #endif +#ifdef HELTEC_V4 +// Power Amps are often non-linear, so we can use an array of values for the power curve +#define NUM_PA_POINTS 22 +#define TX_GAIN_LORA 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 10, 10, 9, 9, 8, 7 +#endif + // Default system gain to 0 if not defined #ifndef TX_GAIN_LORA #define TX_GAIN_LORA 0 diff --git a/src/mesh/RadioInterface.cpp b/src/mesh/RadioInterface.cpp index 31c68c302..b891ec89c 100644 --- a/src/mesh/RadioInterface.cpp +++ b/src/mesh/RadioInterface.cpp @@ -674,11 +674,25 @@ void RadioInterface::limitPower(int8_t loraMaxPower) power = maxPower; } +#ifndef NUM_PA_POINTS if (TX_GAIN_LORA > 0) { LOG_INFO("Requested Tx power: %d dBm; Device LoRa Tx gain: %d dB", power, TX_GAIN_LORA); power -= TX_GAIN_LORA; } +#else + // we have an array of PA gain values. Find the highest power setting that works. + const uint16_t tx_gain[NUM_PA_POINTS] = {TX_GAIN_LORA}; + for (int radio_dbm = 0; radio_dbm < NUM_PA_POINTS; radio_dbm++) { + if (((radio_dbm + tx_gain[radio_dbm]) > power) || + ((radio_dbm == (NUM_PA_POINTS - 1)) && ((radio_dbm + tx_gain[radio_dbm]) <= power))) { + // we've exceeded the power limit, or hit the max we can do + LOG_INFO("Requested Tx power: %d dBm; Device LoRa Tx gain: %d dB", power, tx_gain[radio_dbm]); + power -= tx_gain[radio_dbm]; + break; + } + } +#endif if (power > loraMaxPower) // Clamp power to maximum defined level power = loraMaxPower; diff --git a/src/mesh/SX126xInterface.cpp b/src/mesh/SX126xInterface.cpp index 3fc2562b3..785338483 100644 --- a/src/mesh/SX126xInterface.cpp +++ b/src/mesh/SX126xInterface.cpp @@ -80,6 +80,9 @@ template bool SX126xInterface::init() RadioLibInterface::init(); limitPower(SX126X_MAX_POWER); + // Make sure we reach the minimum power supported to turn the chip on (-9dBm) + if (power < -9) + power = -9; int res = lora.begin(getFreq(), bw, sf, cr, syncWord, power, preambleLength, tcxoVoltage, useRegulatorLDO); // \todo Display actual typename of the adapter, not just `SX126x` @@ -118,8 +121,8 @@ template bool SX126xInterface::init() LOG_DEBUG("Set DIO2 as %sRF switch, result: %d", dio2AsRfSwitch ? "" : "not ", res); } - // If a pin isn't defined, we set it to RADIOLIB_NC, it is safe to always do external RF switching with RADIOLIB_NC as it has - // no effect +// If a pin isn't defined, we set it to RADIOLIB_NC, it is safe to always do external RF switching with RADIOLIB_NC as it has +// no effect #if ARCH_PORTDUINO if (res == RADIOLIB_ERR_NONE) { LOG_DEBUG("Use MCU pin %i as RXEN and pin %i as TXEN to control RF switching", portduino_config.lora_rxen_pin.pin, diff --git a/variants/esp32s3/heltec_v4/platformio.ini b/variants/esp32s3/heltec_v4/platformio.ini index 1a448bc99..d0a250ad3 100644 --- a/variants/esp32s3/heltec_v4/platformio.ini +++ b/variants/esp32s3/heltec_v4/platformio.ini @@ -8,4 +8,3 @@ build_flags = -D HELTEC_V4 -I variants/esp32s3/heltec_v4 -D GPS_POWER_TOGGLE ; comment this line to disable triple press function on the user button to turn off gps entirely. - -D SX126X_MAX_POWER=11 From 4fd568f38429fae506a52daab9009bf6438ab254 Mon Sep 17 00:00:00 2001 From: Jonathan Bennett Date: Tue, 30 Sep 2025 13:00:01 -0500 Subject: [PATCH 03/15] Initial support for T-Rex --- boards/t-rex.json | 52 +++++++++ src/detect/ScanI2C.cpp | 4 +- src/detect/ScanI2C.h | 1 + src/detect/ScanI2CTwoWire.cpp | 3 + src/gps/RTC.cpp | 30 ++++++ src/gps/RTC.h | 4 + src/main.cpp | 6 ++ src/platform/nrf52/NRF52Bluetooth.cpp | 3 +- src/power.h | 2 + variants/t-rex/platformio.ini | 17 +++ variants/t-rex/variant.cpp | 45 ++++++++ variants/t-rex/variant.h | 148 ++++++++++++++++++++++++++ 12 files changed, 312 insertions(+), 3 deletions(-) create mode 100644 boards/t-rex.json create mode 100644 variants/t-rex/platformio.ini create mode 100644 variants/t-rex/variant.cpp create mode 100644 variants/t-rex/variant.h diff --git a/boards/t-rex.json b/boards/t-rex.json new file mode 100644 index 000000000..d827c55dd --- /dev/null +++ b/boards/t-rex.json @@ -0,0 +1,52 @@ +{ + "build": { + "arduino": { + "ldscript": "nrf52840_s140_v6.ld" + }, + "core": "nRF5", + "cpu": "cortex-m4", + "extra_flags": "-DARDUINO_NRF52840_FEATHER -DNRF52840_XXAA", + "f_cpu": "64000000L", + "hwids": [ + ["0x239A", "0x8029"], + ["0x239A", "0x0029"], + ["0x239A", "0x002A"], + ["0x239A", "0x802A"] + ], + "usb_product": "Muzi T-REX", + "mcu": "nrf52840", + "variant": "t-rex", + "bsp": { + "name": "adafruit" + }, + "softdevice": { + "sd_flags": "-DS140", + "sd_name": "s140", + "sd_version": "6.1.1", + "sd_fwid": "0x00B6" + }, + "bootloader": { + "settings_addr": "0xFF000" + } + }, + "connectivity": ["bluetooth"], + "debug": { + "jlink_device": "nRF52840_xxAA", + "svd_path": "nrf52840.svd", + "openocd_target": "nrf52840-mdk-rs" + }, + "frameworks": ["arduino", "freertos"], + "name": "WisCore RAK4631 Board", + "upload": { + "maximum_ram_size": 248832, + "maximum_size": 815104, + "speed": 115200, + "protocol": "nrfutil", + "protocols": ["jlink", "nrfjprog", "nrfutil", "stlink"], + "use_1200bps_touch": true, + "require_upload_port": true, + "wait_for_upload_port": true + }, + "url": "https://muzi.works/", + "vendor": "Muzi Works" +} diff --git a/src/detect/ScanI2C.cpp b/src/detect/ScanI2C.cpp index 170bef3a6..8ac503b83 100644 --- a/src/detect/ScanI2C.cpp +++ b/src/detect/ScanI2C.cpp @@ -25,8 +25,8 @@ ScanI2C::FoundDevice ScanI2C::firstScreen() const ScanI2C::FoundDevice ScanI2C::firstRTC() const { - ScanI2C::DeviceType types[] = {RTC_RV3028, RTC_PCF8563}; - return firstOfOrNONE(2, types); + ScanI2C::DeviceType types[] = {RTC_RV3028, RTC_PCF8563, RTC_RX8130CE}; + return firstOfOrNONE(3, types); } ScanI2C::FoundDevice ScanI2C::firstKeyboard() const diff --git a/src/detect/ScanI2C.h b/src/detect/ScanI2C.h index 470a416c0..2e602338c 100644 --- a/src/detect/ScanI2C.h +++ b/src/detect/ScanI2C.h @@ -14,6 +14,7 @@ class ScanI2C SCREEN_ST7567, RTC_RV3028, RTC_PCF8563, + RTC_RX8130CE, CARDKB, TDECKKB, BBQ10KB, diff --git a/src/detect/ScanI2CTwoWire.cpp b/src/detect/ScanI2CTwoWire.cpp index 01a630b52..6df3f8be1 100644 --- a/src/detect/ScanI2CTwoWire.cpp +++ b/src/detect/ScanI2CTwoWire.cpp @@ -197,6 +197,9 @@ void ScanI2CTwoWire::scanPort(I2CPort port, uint8_t *address, uint8_t asize) #ifdef PCF8563_RTC SCAN_SIMPLE_CASE(PCF8563_RTC, RTC_PCF8563, "PCF8563", (uint8_t)addr.address) #endif +#ifdef RX8130CE_RTC + SCAN_SIMPLE_CASE(RX8130CE_RTC, RTC_RX8130CE, "RX8130CE", (uint8_t)addr.address) +#endif case CARDKB_ADDR: // Do we have the RAK14006 instead? diff --git a/src/gps/RTC.cpp b/src/gps/RTC.cpp index da20e28eb..97590c6a8 100644 --- a/src/gps/RTC.cpp +++ b/src/gps/RTC.cpp @@ -109,6 +109,25 @@ RTCSetResult readFromRTC() } return RTCSetResultSuccess; } +#elif defined(RX8130CE_RTC) + if (rtc_found.address == RX8130CE_RTC) { + uint32_t now = millis(); + ArtronShop_RX8130CE rtc(&Wire); + tm t; + if (rtc.getTime(&t)) { + tv.tv_sec = gm_mktime(&t); + tv.tv_usec = 0; + + uint32_t printableEpoch = tv.tv_sec; // Print lib only supports 32 bit but time_t can be 64 bit on some platforms + LOG_DEBUG("Read RTC time from RX8130CE getDateTime as %02d-%02d-%02d %02d:%02d:%02d (%ld)", t.tm_year + 1900, + t.tm_mon + 1, t.tm_mday, t.tm_hour, t.tm_min, t.tm_sec, printableEpoch); + timeStartMsec = now; + zeroOffsetSecs = tv.tv_sec; + if (currentQuality == RTCQualityNone) { + currentQuality = RTCQualityDevice; + } + } + } #else if (!gettimeofday(&tv, NULL)) { uint32_t now = millis(); @@ -214,6 +233,17 @@ RTCSetResult perhapsSetRTC(RTCQuality q, const struct timeval *tv, bool forceUpd LOG_DEBUG("PCF8563_RTC setDateTime %02d-%02d-%02d %02d:%02d:%02d (%ld)", t->tm_year + 1900, t->tm_mon + 1, t->tm_mday, t->tm_hour, t->tm_min, t->tm_sec, printableEpoch); } +#elif defined(RX8130CE_RTC) + if (rtc_found.address == RX8130CE_RTC) { + ArtronShop_RX8130CE rtc(&Wire); + tm *t = gmtime(&tv->tv_sec); + if (rtc.setTime(*t)) { + LOG_DEBUG("RX8130CE setDateTime %02d-%02d-%02d %02d:%02d:%02d (%ld)", t->tm_year + 1900, t->tm_mon + 1, + t->tm_mday, t->tm_hour, t->tm_min, t->tm_sec, printableEpoch); + } else { + LOG_WARN("Failed to set time for RX8130CE"); + } + } #elif defined(ARCH_ESP32) settimeofday(tv, NULL); #endif diff --git a/src/gps/RTC.h b/src/gps/RTC.h index eca17bf35..06dd34c16 100644 --- a/src/gps/RTC.h +++ b/src/gps/RTC.h @@ -4,6 +4,10 @@ #include "sys/time.h" #include +#ifdef RX8130CE_RTC +#include +#endif + enum RTCQuality { /// We haven't had our RTC set yet diff --git a/src/main.cpp b/src/main.cpp index 29a608dfe..1df4ebe3b 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -297,6 +297,12 @@ void printInfo() #ifndef PIO_UNIT_TESTING void setup() { +#if defined(T_REX) // XXX + pinMode(DCDC_EN_HOLD, OUTPUT); + digitalWrite(DCDC_EN_HOLD, HIGH); + pinMode(NRF_ON, OUTPUT); + digitalWrite(NRF_ON, HIGH); +#endif #if defined(PIN_POWER_EN) pinMode(PIN_POWER_EN, OUTPUT); diff --git a/src/platform/nrf52/NRF52Bluetooth.cpp b/src/platform/nrf52/NRF52Bluetooth.cpp index b457c35b7..79eef8f76 100644 --- a/src/platform/nrf52/NRF52Bluetooth.cpp +++ b/src/platform/nrf52/NRF52Bluetooth.cpp @@ -335,7 +335,8 @@ bool NRF52Bluetooth::onPairingPasskey(uint16_t conn_handle, uint8_t const passke meshtastic::BluetoothStatus newStatus(textkey); bluetoothStatus->updateStatus(&newStatus); -#if !defined(MESHTASTIC_EXCLUDE_SCREEN) // Todo: migrate this display code back into Screen class, and observe bluetoothStatus +#if HAS_SCREEN && \ + !defined(MESHTASTIC_EXCLUDE_SCREEN) // Todo: migrate this display code back into Screen class, and observe bluetoothStatus if (screen) { screen->startAlert([](OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y) -> void { char btPIN[16] = "888888"; diff --git a/src/power.h b/src/power.h index e96f5b022..10ec07a30 100644 --- a/src/power.h +++ b/src/power.h @@ -34,6 +34,8 @@ #define OCV_ARRAY 4200, 3876, 3826, 3763, 3713, 3660, 3573, 3485, 3422, 3359, 3300 #elif defined(SEEED_SOLAR_NODE) #define OCV_ARRAY 4200, 3986, 3922, 3812, 3734, 3645, 3527, 3420, 3281, 3087, 2786 +#elif defined(T_REX) +#define OCV_ARRAY 4330, 4292, 4254, 4216, 4178, 4140, 4102, 4064, 4026, 3988, 3950 #else // LiIon #define OCV_ARRAY 4190, 4050, 3990, 3890, 3800, 3720, 3630, 3530, 3420, 3300, 3100 #endif diff --git a/variants/t-rex/platformio.ini b/variants/t-rex/platformio.ini new file mode 100644 index 000000000..c2b72e7d3 --- /dev/null +++ b/variants/t-rex/platformio.ini @@ -0,0 +1,17 @@ +; The t-rex Muzi board +[env:t-rex] +extends = nrf52840_base +board = t-rex +board_check = true +build_flags = ${nrf52840_base.build_flags} -Ivariants/t-rex -D T_REX + -DGPS_POWER_TOGGLE ; comment this line to disable triple press function on the user button to turn off gps entirely. + -DRADIOLIB_EXCLUDE_SX128X=1 + -DRADIOLIB_EXCLUDE_SX127X=1 + -DRADIOLIB_EXCLUDE_LR11X0=1 +build_src_filter = ${nrf52_base.build_src_filter} +<../variants/t-rex> + + +lib_deps = + ${nrf52840_base.lib_deps} + ${networking_base.lib_deps} + https://github.com/RAKWireless/RAK13800-W5100S/archive/1.0.2.zip + rakwireless/RAKwireless NCP5623 RGB LED library@^1.0.2 + artronshop/ArtronShop_RX8130CE@1.0.0 diff --git a/variants/t-rex/variant.cpp b/variants/t-rex/variant.cpp new file mode 100644 index 000000000..f87c041aa --- /dev/null +++ b/variants/t-rex/variant.cpp @@ -0,0 +1,45 @@ +/* + Copyright (c) 2014-2015 Arduino LLC. All right reserved. + Copyright (c) 2016 Sandeep Mistry All right reserved. + Copyright (c) 2018, Adafruit Industries (adafruit.com) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include "variant.h" +#include "nrf.h" +#include "wiring_constants.h" +#include "wiring_digital.h" + +const uint32_t g_ADigitalPinMap[] = { + // P0 + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, + + // P1 + 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47}; + +void initVariant() +{ + // LED1 & LED2 + pinMode(PIN_LED1, OUTPUT); + ledOff(PIN_LED1); + + pinMode(PIN_LED2, OUTPUT); + ledOff(PIN_LED2); + + // 3V3 Power Rail + // pinMode(PIN_3V3_EN, OUTPUT); + // digitalWrite(PIN_3V3_EN, HIGH); +} diff --git a/variants/t-rex/variant.h b/variants/t-rex/variant.h new file mode 100644 index 000000000..33bf7093a --- /dev/null +++ b/variants/t-rex/variant.h @@ -0,0 +1,148 @@ +/* + Copyright (c) 2014-2015 Arduino LLC. All right reserved. + Copyright (c) 2016 Sandeep Mistry All right reserved. + Copyright (c) 2018, Adafruit Industries (adafruit.com) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the GNU Lesser General Public License for more details. + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef _VARIANT_TREX_ +#define _VARIANT_TREX_ + +#define RAK4630 + +/** Master clock frequency */ +#define VARIANT_MCK (64000000ul) + +#define USE_LFXO // Board uses 32khz crystal for LF +// define USE_LFRC // Board uses RC for LF + +/*---------------------------------------------------------------------------- + * Headers + *----------------------------------------------------------------------------*/ + +#include "WVariant.h" + +#ifdef __cplusplus +extern "C" { +#endif // __cplusplus + +// Number of pins defined in PinDescription array +#define PINS_COUNT (48) // ??? +#define NUM_DIGITAL_PINS (48) // ??? +#define NUM_ANALOG_INPUTS (1) +#define NUM_ANALOG_OUTPUTS (0) + +// LEDs +#define PIN_LED1 (32 + 4) // P1.04 Controls Green LED +#define PIN_LED2 (28) // P0.28 Controls Blue LED + +#define LED_BUILTIN PIN_LED1 +#define LED_CONN PIN_LED2 + +#define LED_GREEN PIN_LED1 +#define LED_BLUE PIN_LED2 + +#define LED_STATE_ON 1 // State when LED is litted + +// Button +#define PIN_BUTTON1 (26) +#define BUTTON_ACTIVE_LOW 0 +#define BUTTON_ACTIVE_PULLUP 0 + +#define ADC_RESOLUTION 14 // ??? + +// Serial for GPS +#define PIN_SERIAL1_RX (25) +#define PIN_SERIAL1_TX (24) + +// Connected to Jlink CDC +#define PIN_SERIAL2_RX (8) // ??? +#define PIN_SERIAL2_TX (6) // ??? + +/* + * SPI Interfaces + */ +#define SPI_INTERFACES_COUNT 1 + +#define PIN_SPI_MISO (45) +#define PIN_SPI_MOSI (44) +#define PIN_SPI_SCK (43) + +static const uint8_t SS = 42; +static const uint8_t MOSI = PIN_SPI_MOSI; +static const uint8_t MISO = PIN_SPI_MISO; +static const uint8_t SCK = PIN_SPI_SCK; + +// T-Rex Extras +#define DCDC_EN_HOLD (13) // P0.13 Keeps DCDC alive after user button is pressed +#define NRF_ON (29) // P0.29 Tells IO controller device is on + +// RAKRGB +#define HAS_NCP5623 // ??? + +#define HAS_SCREEN 0 + +/* + * Wire Interfaces + */ +#define WIRE_INTERFACES_COUNT 1 + +#define PIN_WIRE_SDA (19) // P0.19 RTC_SDA +#define PIN_WIRE_SCL (20) // P0.20 RTC_SDA + +#define PIN_BUZZER (0 + 3) // P0.03 + +#define USE_SX1262 +#define SX126X_CS (42) +#define SX126X_DIO1 (47) +#define SX126X_BUSY (46) +#define SX126X_RESET (38) +#define SX126X_POWER_EN (37) + +// DIO2 controlls an antenna switch and the TCXO voltage is controlled by DIO3 +#define SX126X_DIO2_AS_RF_SWITCH +#define SX126X_DIO3_TCXO_VOLTAGE 1.8 + +// Testing USB detection +#define NRF_APM + +#define PIN_GPS_EN (32 + 1) // P1.01 +#define PIN_GPS_PPS (2) // P0.02 Pulse per second input from the GPS + +#define GPS_RX_PIN PIN_SERIAL1_RX +#define GPS_TX_PIN PIN_SERIAL1_TX + +// Battery +#define BATTERY_PIN (0 + 31) // P0.31 ADC_VBAT +// and has 12 bit resolution +#define BATTERY_SENSE_RESOLUTION_BITS 12 // ??? +#define BATTERY_SENSE_RESOLUTION 4096.0 +#undef AREF_VOLTAGE +#define AREF_VOLTAGE 3.0 +#define VBAT_AR_INTERNAL AR_INTERNAL_3_0 +#define ADC_MULTIPLIER 1.73 // ??? + +#define HAS_RTC 1 + +#define RX8130CE_RTC 0x32 + +#ifdef __cplusplus +} +#endif + +/*---------------------------------------------------------------------------- + * Arduino objects - C++ only + *----------------------------------------------------------------------------*/ + +#endif From f9937967fa9b21eb85f6fdfb2e79bcada012db46 Mon Sep 17 00:00:00 2001 From: Jonathan Bennett Date: Tue, 30 Sep 2025 15:56:01 -0500 Subject: [PATCH 04/15] Add HardwareModel and correct directories --- src/platform/nrf52/architecture.h | 2 ++ variants/{ => nrf52840}/t-rex/platformio.ini | 6 ++++-- variants/{ => nrf52840}/t-rex/variant.cpp | 0 variants/{ => nrf52840}/t-rex/variant.h | 0 4 files changed, 6 insertions(+), 2 deletions(-) rename variants/{ => nrf52840}/t-rex/platformio.ini (83%) rename variants/{ => nrf52840}/t-rex/variant.cpp (100%) rename variants/{ => nrf52840}/t-rex/variant.h (100%) diff --git a/src/platform/nrf52/architecture.h b/src/platform/nrf52/architecture.h index c9938062e..9d0447067 100644 --- a/src/platform/nrf52/architecture.h +++ b/src/platform/nrf52/architecture.h @@ -98,6 +98,8 @@ #define HW_VENDOR meshtastic_HardwareModel_SEEED_WIO_TRACKER_L1_EINK #elif defined(SEEED_WIO_TRACKER_L1) #define HW_VENDOR meshtastic_HardwareModel_SEEED_WIO_TRACKER_L1 +#elif defined(T_REX) +#define HW_VENDOR meshtastic_HardwareModel_MUZI_R1_NEO #elif defined(HELTEC_MESH_SOLAR) #define HW_VENDOR meshtastic_HardwareModel_HELTEC_MESH_SOLAR #else diff --git a/variants/t-rex/platformio.ini b/variants/nrf52840/t-rex/platformio.ini similarity index 83% rename from variants/t-rex/platformio.ini rename to variants/nrf52840/t-rex/platformio.ini index c2b72e7d3..33b8f96f5 100644 --- a/variants/t-rex/platformio.ini +++ b/variants/nrf52840/t-rex/platformio.ini @@ -3,12 +3,14 @@ extends = nrf52840_base board = t-rex board_check = true -build_flags = ${nrf52840_base.build_flags} -Ivariants/t-rex -D T_REX +build_flags = ${nrf52840_base.build_flags} + -Ivariants/nrf52840/t-rex + -D T_REX -DGPS_POWER_TOGGLE ; comment this line to disable triple press function on the user button to turn off gps entirely. -DRADIOLIB_EXCLUDE_SX128X=1 -DRADIOLIB_EXCLUDE_SX127X=1 -DRADIOLIB_EXCLUDE_LR11X0=1 -build_src_filter = ${nrf52_base.build_src_filter} +<../variants/t-rex> + + +build_src_filter = ${nrf52_base.build_src_filter} +<../variants/nrf52840/t-rex> + + lib_deps = ${nrf52840_base.lib_deps} ${networking_base.lib_deps} diff --git a/variants/t-rex/variant.cpp b/variants/nrf52840/t-rex/variant.cpp similarity index 100% rename from variants/t-rex/variant.cpp rename to variants/nrf52840/t-rex/variant.cpp diff --git a/variants/t-rex/variant.h b/variants/nrf52840/t-rex/variant.h similarity index 100% rename from variants/t-rex/variant.h rename to variants/nrf52840/t-rex/variant.h From 8b466b1db3cd9941a328f956308476adc688bb9f Mon Sep 17 00:00:00 2001 From: Jonathan Bennett Date: Tue, 30 Sep 2025 18:00:29 -0500 Subject: [PATCH 05/15] T-rex comment cleanup --- src/main.cpp | 2 +- variants/nrf52840/t-rex/variant.h | 18 +++++++++--------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index 1df4ebe3b..382f35065 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -297,7 +297,7 @@ void printInfo() #ifndef PIO_UNIT_TESTING void setup() { -#if defined(T_REX) // XXX +#if defined(T_REX) pinMode(DCDC_EN_HOLD, OUTPUT); digitalWrite(DCDC_EN_HOLD, HIGH); pinMode(NRF_ON, OUTPUT); diff --git a/variants/nrf52840/t-rex/variant.h b/variants/nrf52840/t-rex/variant.h index 33bf7093a..9ab0cadf8 100644 --- a/variants/nrf52840/t-rex/variant.h +++ b/variants/nrf52840/t-rex/variant.h @@ -38,8 +38,8 @@ extern "C" { #endif // __cplusplus // Number of pins defined in PinDescription array -#define PINS_COUNT (48) // ??? -#define NUM_DIGITAL_PINS (48) // ??? +#define PINS_COUNT (48) +#define NUM_DIGITAL_PINS (48) #define NUM_ANALOG_INPUTS (1) #define NUM_ANALOG_OUTPUTS (0) @@ -60,15 +60,15 @@ extern "C" { #define BUTTON_ACTIVE_LOW 0 #define BUTTON_ACTIVE_PULLUP 0 -#define ADC_RESOLUTION 14 // ??? +#define ADC_RESOLUTION 14 // Serial for GPS #define PIN_SERIAL1_RX (25) #define PIN_SERIAL1_TX (24) // Connected to Jlink CDC -#define PIN_SERIAL2_RX (8) // ??? -#define PIN_SERIAL2_TX (6) // ??? +#define PIN_SERIAL2_RX (8) +#define PIN_SERIAL2_TX (6) /* * SPI Interfaces @@ -89,7 +89,7 @@ static const uint8_t SCK = PIN_SPI_SCK; #define NRF_ON (29) // P0.29 Tells IO controller device is on // RAKRGB -#define HAS_NCP5623 // ??? +#define HAS_NCP5623 #define HAS_SCREEN 0 @@ -99,7 +99,7 @@ static const uint8_t SCK = PIN_SPI_SCK; #define WIRE_INTERFACES_COUNT 1 #define PIN_WIRE_SDA (19) // P0.19 RTC_SDA -#define PIN_WIRE_SCL (20) // P0.20 RTC_SDA +#define PIN_WIRE_SCL (20) // P0.20 RTC_SCL #define PIN_BUZZER (0 + 3) // P0.03 @@ -126,12 +126,12 @@ static const uint8_t SCK = PIN_SPI_SCK; // Battery #define BATTERY_PIN (0 + 31) // P0.31 ADC_VBAT // and has 12 bit resolution -#define BATTERY_SENSE_RESOLUTION_BITS 12 // ??? +#define BATTERY_SENSE_RESOLUTION_BITS 12 #define BATTERY_SENSE_RESOLUTION 4096.0 #undef AREF_VOLTAGE #define AREF_VOLTAGE 3.0 #define VBAT_AR_INTERNAL AR_INTERNAL_3_0 -#define ADC_MULTIPLIER 1.73 // ??? +#define ADC_MULTIPLIER 1.73 #define HAS_RTC 1 From ad44940732b4f7bb1f664e399cf53f405fba4b0a Mon Sep 17 00:00:00 2001 From: Jonathan Bennett Date: Tue, 30 Sep 2025 20:46:38 -0500 Subject: [PATCH 06/15] Put the GPIO in the right state for wake from sleep --- variants/nrf52840/t-rex/variant.h | 1 + 1 file changed, 1 insertion(+) diff --git a/variants/nrf52840/t-rex/variant.h b/variants/nrf52840/t-rex/variant.h index 9ab0cadf8..4735e340d 100644 --- a/variants/nrf52840/t-rex/variant.h +++ b/variants/nrf52840/t-rex/variant.h @@ -59,6 +59,7 @@ extern "C" { #define PIN_BUTTON1 (26) #define BUTTON_ACTIVE_LOW 0 #define BUTTON_ACTIVE_PULLUP 0 +#define BUTTON_SENSE_TYPE INPUT_SENSE_HIGH #define ADC_RESOLUTION 14 From d5164b4fbfe6b75328e90497e73d785faadcf85d Mon Sep 17 00:00:00 2001 From: Jonathan Bennett Date: Tue, 30 Sep 2025 21:38:32 -0500 Subject: [PATCH 07/15] Check the BUILD_EPOCH if defined --- src/gps/RTC.cpp | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/src/gps/RTC.cpp b/src/gps/RTC.cpp index 97590c6a8..665a9aaa3 100644 --- a/src/gps/RTC.cpp +++ b/src/gps/RTC.cpp @@ -121,11 +121,21 @@ RTCSetResult readFromRTC() uint32_t printableEpoch = tv.tv_sec; // Print lib only supports 32 bit but time_t can be 64 bit on some platforms LOG_DEBUG("Read RTC time from RX8130CE getDateTime as %02d-%02d-%02d %02d:%02d:%02d (%ld)", t.tm_year + 1900, t.tm_mon + 1, t.tm_mday, t.tm_hour, t.tm_min, t.tm_sec, printableEpoch); - timeStartMsec = now; - zeroOffsetSecs = tv.tv_sec; +#ifdef BUILD_EPOCH + if (tv.tv_sec < BUILD_EPOCH) { + if (Throttle::isWithinTimespanMs(lastTimeValidationWarning, TIME_VALIDATION_WARNING_INTERVAL_MS) == false) { + LOG_WARN("Ignore time (%ld) before build epoch (%ld)!", printableEpoch, BUILD_EPOCH); + lastTimeValidationWarning = millis(); + } + return RTCSetResultInvalidTime; + } +#endif if (currentQuality == RTCQualityNone) { + timeStartMsec = now; + zeroOffsetSecs = tv.tv_sec; currentQuality = RTCQualityDevice; } + return RTCSetResultSuccess; } } #else From 17afdb9ccf9f89334b5087c99b47c7a469ef53f3 Mon Sep 17 00:00:00 2001 From: rcarteraz Date: Wed, 1 Oct 2025 08:21:20 -0700 Subject: [PATCH 08/15] no more t-rex --- variants/nrf52840/{t-rex => r1-neo}/platformio.ini | 12 ++++++------ variants/nrf52840/{t-rex => r1-neo}/variant.cpp | 0 variants/nrf52840/{t-rex => r1-neo}/variant.h | 6 +++--- 3 files changed, 9 insertions(+), 9 deletions(-) rename variants/nrf52840/{t-rex => r1-neo}/platformio.ini (82%) rename variants/nrf52840/{t-rex => r1-neo}/variant.cpp (100%) rename variants/nrf52840/{t-rex => r1-neo}/variant.h (98%) diff --git a/variants/nrf52840/t-rex/platformio.ini b/variants/nrf52840/r1-neo/platformio.ini similarity index 82% rename from variants/nrf52840/t-rex/platformio.ini rename to variants/nrf52840/r1-neo/platformio.ini index 33b8f96f5..6feb55dc9 100644 --- a/variants/nrf52840/t-rex/platformio.ini +++ b/variants/nrf52840/r1-neo/platformio.ini @@ -1,16 +1,16 @@ -; The t-rex Muzi board -[env:t-rex] +; The R1 Neo board +[env:r1-neo] extends = nrf52840_base -board = t-rex +board = r1-neo board_check = true build_flags = ${nrf52840_base.build_flags} - -Ivariants/nrf52840/t-rex - -D T_REX + -Ivariants/nrf52840/r1-neo + -D R1_NEO -DGPS_POWER_TOGGLE ; comment this line to disable triple press function on the user button to turn off gps entirely. -DRADIOLIB_EXCLUDE_SX128X=1 -DRADIOLIB_EXCLUDE_SX127X=1 -DRADIOLIB_EXCLUDE_LR11X0=1 -build_src_filter = ${nrf52_base.build_src_filter} +<../variants/nrf52840/t-rex> + + +build_src_filter = ${nrf52_base.build_src_filter} +<../variants/nrf52840/r1-neo> + + lib_deps = ${nrf52840_base.lib_deps} ${networking_base.lib_deps} diff --git a/variants/nrf52840/t-rex/variant.cpp b/variants/nrf52840/r1-neo/variant.cpp similarity index 100% rename from variants/nrf52840/t-rex/variant.cpp rename to variants/nrf52840/r1-neo/variant.cpp diff --git a/variants/nrf52840/t-rex/variant.h b/variants/nrf52840/r1-neo/variant.h similarity index 98% rename from variants/nrf52840/t-rex/variant.h rename to variants/nrf52840/r1-neo/variant.h index 4735e340d..901e993e3 100644 --- a/variants/nrf52840/t-rex/variant.h +++ b/variants/nrf52840/r1-neo/variant.h @@ -16,8 +16,8 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ -#ifndef _VARIANT_TREX_ -#define _VARIANT_TREX_ +#ifndef _VARIANT_R1NEO_ +#define _VARIANT_R1NEO_ #define RAK4630 @@ -85,7 +85,7 @@ static const uint8_t MOSI = PIN_SPI_MOSI; static const uint8_t MISO = PIN_SPI_MISO; static const uint8_t SCK = PIN_SPI_SCK; -// T-Rex Extras +// R1 Neo Extras #define DCDC_EN_HOLD (13) // P0.13 Keeps DCDC alive after user button is pressed #define NRF_ON (29) // P0.29 Tells IO controller device is on From b28d09509676c8edd66fae0051562354655119dd Mon Sep 17 00:00:00 2001 From: rcarteraz Date: Wed, 1 Oct 2025 09:30:46 -0700 Subject: [PATCH 09/15] missed t-rexes --- boards/{t-rex.json => r1-neo.json} | 4 ++-- src/main.cpp | 2 +- src/platform/nrf52/architecture.h | 2 +- src/power.h | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) rename boards/{t-rex.json => r1-neo.json} (95%) diff --git a/boards/t-rex.json b/boards/r1-neo.json similarity index 95% rename from boards/t-rex.json rename to boards/r1-neo.json index d827c55dd..0383a2f48 100644 --- a/boards/t-rex.json +++ b/boards/r1-neo.json @@ -13,9 +13,9 @@ ["0x239A", "0x002A"], ["0x239A", "0x802A"] ], - "usb_product": "Muzi T-REX", + "usb_product": "Muzi R1 Neo", "mcu": "nrf52840", - "variant": "t-rex", + "variant": "r1-neo", "bsp": { "name": "adafruit" }, diff --git a/src/main.cpp b/src/main.cpp index 382f35065..d49111414 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -297,7 +297,7 @@ void printInfo() #ifndef PIO_UNIT_TESTING void setup() { -#if defined(T_REX) +#if defined(R1_NEO) pinMode(DCDC_EN_HOLD, OUTPUT); digitalWrite(DCDC_EN_HOLD, HIGH); pinMode(NRF_ON, OUTPUT); diff --git a/src/platform/nrf52/architecture.h b/src/platform/nrf52/architecture.h index 9d0447067..8212bcbc3 100644 --- a/src/platform/nrf52/architecture.h +++ b/src/platform/nrf52/architecture.h @@ -98,7 +98,7 @@ #define HW_VENDOR meshtastic_HardwareModel_SEEED_WIO_TRACKER_L1_EINK #elif defined(SEEED_WIO_TRACKER_L1) #define HW_VENDOR meshtastic_HardwareModel_SEEED_WIO_TRACKER_L1 -#elif defined(T_REX) +#elif defined(R1_NEO) #define HW_VENDOR meshtastic_HardwareModel_MUZI_R1_NEO #elif defined(HELTEC_MESH_SOLAR) #define HW_VENDOR meshtastic_HardwareModel_HELTEC_MESH_SOLAR diff --git a/src/power.h b/src/power.h index 10ec07a30..23eb95064 100644 --- a/src/power.h +++ b/src/power.h @@ -34,7 +34,7 @@ #define OCV_ARRAY 4200, 3876, 3826, 3763, 3713, 3660, 3573, 3485, 3422, 3359, 3300 #elif defined(SEEED_SOLAR_NODE) #define OCV_ARRAY 4200, 3986, 3922, 3812, 3734, 3645, 3527, 3420, 3281, 3087, 2786 -#elif defined(T_REX) +#elif defined(R1_NEO) #define OCV_ARRAY 4330, 4292, 4254, 4216, 4178, 4140, 4102, 4064, 4026, 3988, 3950 #else // LiIon #define OCV_ARRAY 4190, 4050, 3990, 3890, 3800, 3720, 3630, 3530, 3420, 3300, 3100 From 849bbad2798900ab1b1a5f153de8fca1122c7369 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 1 Oct 2025 15:13:28 -0500 Subject: [PATCH 10/15] Automated version bumps (#8177) 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 0d03dd349..3505f1940 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.12 + https://github.com/meshtastic/firmware/releases?q=tag%3Av2.7.11 diff --git a/debian/changelog b/debian/changelog index 15c8604f6..8bd053b25 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,4 +1,4 @@ -meshtasticd (2.7.11.0) UNRELEASED; urgency=medium +meshtasticd (2.7.12.0) unstable; urgency=medium [ Austin Lane ] * Initial packaging @@ -7,4 +7,7 @@ meshtasticd (2.7.11.0) UNRELEASED; urgency=medium [ ] * GitHub Actions Automatic version bump - -- Wed, 24 Sep 2025 11:01:13 +0000 + [ GitHub Actions ] + * Version 2.7.12 + + -- GitHub Actions Wed, 01 Oct 2025 19:51:41 +0000 diff --git a/version.properties b/version.properties index ce1205f2b..5c84a8e65 100644 --- a/version.properties +++ b/version.properties @@ -1,4 +1,4 @@ [VERSION] major = 2 minor = 7 -build = 11 +build = 12 From f7469159cf8c992f9869fc67ca04531edab4479a Mon Sep 17 00:00:00 2001 From: Mike Robbins Date: Wed, 1 Oct 2025 16:31:53 -0400 Subject: [PATCH 11/15] Reliable ACKs for DMs (#8165) * RoutingModule::sendAckNak takes ackWantsAck arg to set want_ack on the ACK itself * Use reliable delivery for traceroute requests (which will be copied to traceroute responses by setReplyTo) * Update ReliableRouter::sniffReceived to use ReliableRouter::shouldSuccessAckWithWantAck * Use isFromUs * Update MockRoutingModule::sendAckNak to include ackWantsAck argument (currently ignored) --------- Co-authored-by: Ben Meadors --- src/mesh/PhoneAPI.cpp | 7 +++++ src/mesh/ReliableRouter.cpp | 44 +++++++++++++++++++++++++++++++- src/mesh/ReliableRouter.h | 6 +++++ src/mesh/Router.cpp | 5 ++-- src/mesh/Router.h | 3 ++- src/modules/RoutingModule.cpp | 6 ++++- src/modules/RoutingModule.h | 4 +-- src/modules/TraceRouteModule.cpp | 6 +++++ test/test_mqtt/MQTT.cpp | 4 +-- 9 files changed, 76 insertions(+), 9 deletions(-) diff --git a/src/mesh/PhoneAPI.cpp b/src/mesh/PhoneAPI.cpp index f6f1bc027..07f314415 100644 --- a/src/mesh/PhoneAPI.cpp +++ b/src/mesh/PhoneAPI.cpp @@ -710,6 +710,13 @@ bool PhoneAPI::handleToRadioPacket(meshtastic_MeshPacket &p) // sendNotification(meshtastic_LogRecord_Level_WARNING, p.id, "Text messages can only be sent once every 2 seconds"); return false; } + + // Upgrade traceroute requests from phone to use reliable delivery, matching TraceRouteModule + if (p.decoded.portnum == meshtastic_PortNum_TRACEROUTE_APP && !isBroadcast(p.to)) { + // Use reliable delivery for traceroute requests (which will be copied to traceroute responses by setReplyTo) + p.want_ack = true; + } + lastPortNumToRadio[p.decoded.portnum] = millis(); service->handleToRadio(p); return true; diff --git a/src/mesh/ReliableRouter.cpp b/src/mesh/ReliableRouter.cpp index cca838ff0..b31c352fe 100644 --- a/src/mesh/ReliableRouter.cpp +++ b/src/mesh/ReliableRouter.cpp @@ -103,10 +103,20 @@ void ReliableRouter::sniffReceived(const meshtastic_MeshPacket *p, const meshtas /* A response may be set to want_ack for retransmissions, but we don't need to ACK a response if it received an implicit ACK already. If we received it directly or via NextHopRouter, only ACK with a hop limit of 0 to make sure the other side stops retransmitting. */ - if (!p->decoded.request_id && !p->decoded.reply_id) { + + if (shouldSuccessAckWithWantAck(p)) { + // If this packet should always be ACKed reliably with want_ack back to the original sender, make sure we + // do that unconditionally. + sendAckNak(meshtastic_Routing_Error_NONE, getFrom(p), p->id, p->channel, + routingModule->getHopLimitForResponse(p->hop_start, p->hop_limit), true); + } else if (!p->decoded.request_id && !p->decoded.reply_id) { + // If it's not an ACK or a reply, send an ACK. sendAckNak(meshtastic_Routing_Error_NONE, getFrom(p), p->id, p->channel, routingModule->getHopLimitForResponse(p->hop_start, p->hop_limit)); } else if ((p->hop_start > 0 && p->hop_start == p->hop_limit) || p->next_hop != NO_NEXT_HOP_PREFERENCE) { + // If we received the packet directly from the original sender, send a 0-hop ACK since the original sender + // won't overhear any implicit ACKs. If we received the packet via NextHopRouter, also send a 0-hop ACK to + // stop the immediate relayer's retransmissions. sendAckNak(meshtastic_Routing_Error_NONE, getFrom(p), p->id, p->channel, 0); } } else if (p->which_payload_variant == meshtastic_MeshPacket_encrypted_tag && p->channel == 0 && @@ -152,4 +162,36 @@ void ReliableRouter::sniffReceived(const meshtastic_MeshPacket *p, const meshtas // handle the packet as normal isBroadcast(p->to) ? FloodingRouter::sniffReceived(p, c) : NextHopRouter::sniffReceived(p, c); +} + +/** + * If we ACK this packet, should we set want_ack=true on the ACK for reliable delivery of the ACK packet? + */ +bool ReliableRouter::shouldSuccessAckWithWantAck(const meshtastic_MeshPacket *p) +{ + // Don't ACK-with-want-ACK outgoing packets + if (isFromUs(p)) + return false; + + // Only ACK-with-want-ACK if the original packet asked for want_ack + if (!p->want_ack) + return false; + + // Only ACK-with-want-ACK packets to us (not broadcast) + if (!isToUs(p)) + return false; + + // Special case for text message DMs: + bool isTextMessage = + (p->which_payload_variant == meshtastic_MeshPacket_decoded_tag) && + IS_ONE_OF(p->decoded.portnum, meshtastic_PortNum_TEXT_MESSAGE_APP, meshtastic_PortNum_TEXT_MESSAGE_COMPRESSED_APP); + + if (isTextMessage) { + // If it's a non-broadcast text message, and the original asked for want_ack, + // let's send an ACK that is itself want_ack to improve reliability of confirming delivery back to the sender. + // This should include all DMs regardless of whether or not reply_id is set. + return true; + } + + return false; } \ No newline at end of file diff --git a/src/mesh/ReliableRouter.h b/src/mesh/ReliableRouter.h index 2cf10fb99..33121de6b 100644 --- a/src/mesh/ReliableRouter.h +++ b/src/mesh/ReliableRouter.h @@ -31,4 +31,10 @@ class ReliableRouter : public NextHopRouter * We hook this method so we can see packets before FloodingRouter says they should be discarded */ virtual bool shouldFilterReceived(const meshtastic_MeshPacket *p) override; + + private: + /** + * Should this packet be ACKed with a want_ack for reliable delivery? + */ + bool shouldSuccessAckWithWantAck(const meshtastic_MeshPacket *p); }; \ No newline at end of file diff --git a/src/mesh/Router.cpp b/src/mesh/Router.cpp index 8f9fb28f9..60637cbd1 100644 --- a/src/mesh/Router.cpp +++ b/src/mesh/Router.cpp @@ -198,9 +198,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, uint8_t hopLimit) +void Router::sendAckNak(meshtastic_Routing_Error err, NodeNum to, PacketId idFrom, ChannelIndex chIndex, uint8_t hopLimit, + bool ackWantsAck) { - routingModule->sendAckNak(err, to, idFrom, chIndex, hopLimit); + routingModule->sendAckNak(err, to, idFrom, chIndex, hopLimit, ackWantsAck); } void Router::abortSendAndNak(meshtastic_Routing_Error err, meshtastic_MeshPacket *p) diff --git a/src/mesh/Router.h b/src/mesh/Router.h index 92a5a06e5..10a3771a7 100644 --- a/src/mesh/Router.h +++ b/src/mesh/Router.h @@ -125,7 +125,8 @@ class Router : protected concurrency::OSThread, protected PacketHistory /** * Send an ack or a nak packet back towards whoever sent idFrom */ - void sendAckNak(meshtastic_Routing_Error err, NodeNum to, PacketId idFrom, ChannelIndex chIndex, uint8_t hopLimit = 0); + void sendAckNak(meshtastic_Routing_Error err, NodeNum to, PacketId idFrom, ChannelIndex chIndex, uint8_t hopLimit = 0, + bool ackWantsAck = false); private: /** diff --git a/src/modules/RoutingModule.cpp b/src/modules/RoutingModule.cpp index fbe3a9cee..05173983c 100644 --- a/src/modules/RoutingModule.cpp +++ b/src/modules/RoutingModule.cpp @@ -47,10 +47,14 @@ meshtastic_MeshPacket *RoutingModule::allocReply() return NULL; } -void RoutingModule::sendAckNak(meshtastic_Routing_Error err, NodeNum to, PacketId idFrom, ChannelIndex chIndex, uint8_t hopLimit) +void RoutingModule::sendAckNak(meshtastic_Routing_Error err, NodeNum to, PacketId idFrom, ChannelIndex chIndex, uint8_t hopLimit, + bool ackWantsAck) { auto p = allocAckNak(err, to, idFrom, chIndex, hopLimit); + // Allow the caller to set want_ack on this ACK packet if it's important that the ACK be delivered reliably + p->want_ack = ackWantsAck; + router->sendLocal(p); // we sometimes send directly to the local node } diff --git a/src/modules/RoutingModule.h b/src/modules/RoutingModule.h index c047f6e29..a4e0679d0 100644 --- a/src/modules/RoutingModule.h +++ b/src/modules/RoutingModule.h @@ -13,8 +13,8 @@ class RoutingModule : public ProtobufModule */ RoutingModule(); - virtual void sendAckNak(meshtastic_Routing_Error err, NodeNum to, PacketId idFrom, ChannelIndex chIndex, - uint8_t hopLimit = 0); + virtual void sendAckNak(meshtastic_Routing_Error err, NodeNum to, PacketId idFrom, ChannelIndex chIndex, uint8_t hopLimit = 0, + bool ackWantsAck = false); // 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); diff --git a/src/modules/TraceRouteModule.cpp b/src/modules/TraceRouteModule.cpp index 8f69c504a..fc2cc232b 100644 --- a/src/modules/TraceRouteModule.cpp +++ b/src/modules/TraceRouteModule.cpp @@ -419,6 +419,9 @@ bool TraceRouteModule::startTraceRoute(NodeNum node) p->decoded.portnum = meshtastic_PortNum_TRACEROUTE_APP; p->decoded.want_response = true; + // Use reliable delivery for traceroute requests (which will be copied to traceroute responses by setReplyTo) + p->want_ack = true; + // Manually encode the RouteDiscovery payload p->decoded.payload.size = pb_encode_to_bytes(p->decoded.payload.bytes, sizeof(p->decoded.payload.bytes), &meshtastic_RouteDiscovery_msg, &req); @@ -532,6 +535,9 @@ void TraceRouteModule::launch(NodeNum node) p->decoded.portnum = meshtastic_PortNum_TRACEROUTE_APP; p->decoded.want_response = true; + // Use reliable delivery for traceroute requests (which will be copied to traceroute responses by setReplyTo) + p->want_ack = true; + p->decoded.payload.size = pb_encode_to_bytes(p->decoded.payload.bytes, sizeof(p->decoded.payload.bytes), &meshtastic_RouteDiscovery_msg, &req); diff --git a/test/test_mqtt/MQTT.cpp b/test/test_mqtt/MQTT.cpp index 32d81f6b4..ede3d22b7 100644 --- a/test/test_mqtt/MQTT.cpp +++ b/test/test_mqtt/MQTT.cpp @@ -83,8 +83,8 @@ class MockNodeDB : public NodeDB class MockRoutingModule : public RoutingModule { public: - void sendAckNak(meshtastic_Routing_Error err, NodeNum to, PacketId idFrom, ChannelIndex chIndex, - uint8_t hopLimit = 0) override + void sendAckNak(meshtastic_Routing_Error err, NodeNum to, PacketId idFrom, ChannelIndex chIndex, uint8_t hopLimit = 0, + bool ackWantsAck = false) override { ackNacks_.emplace_back(err, to, idFrom, chIndex, hopLimit); } From 641a2fc63d1e412ff1326f00e63e7fa5964d17d0 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 1 Oct 2025 15:32:06 -0500 Subject: [PATCH 12/15] Update protobufs (#8178) Co-authored-by: thebentern <9000580+thebentern@users.noreply.github.com> --- protobufs | 2 +- src/mesh/generated/meshtastic/admin.pb.h | 12 ++++++++---- src/mesh/generated/meshtastic/mesh.pb.h | 4 ++++ 3 files changed, 13 insertions(+), 5 deletions(-) diff --git a/protobufs b/protobufs index 5fa4c44d9..60c3e6600 160000 --- a/protobufs +++ b/protobufs @@ -1 +1 @@ -Subproject commit 5fa4c44d914aba67971ba15ace22e4b21af64ae5 +Subproject commit 60c3e6600a2f4e6f49e45aeb47aafd8291a0015c diff --git a/src/mesh/generated/meshtastic/admin.pb.h b/src/mesh/generated/meshtastic/admin.pb.h index bc0b780b9..7cc896292 100644 --- a/src/mesh/generated/meshtastic/admin.pb.h +++ b/src/mesh/generated/meshtastic/admin.pb.h @@ -132,6 +132,8 @@ typedef struct _meshtastic_SharedContact { meshtastic_User user; /* Add this contact to the blocked / ignored list */ bool should_ignore; + /* Set the IS_KEY_MANUALLY_VERIFIED bit */ + bool manually_verified; } meshtastic_SharedContact; /* This message is used by a client to initiate or complete a key verification */ @@ -319,13 +321,13 @@ extern "C" { #define meshtastic_AdminMessage_InputEvent_init_default {0, 0, 0, 0} #define meshtastic_HamParameters_init_default {"", 0, 0, ""} #define meshtastic_NodeRemoteHardwarePinsResponse_init_default {0, {meshtastic_NodeRemoteHardwarePin_init_default, meshtastic_NodeRemoteHardwarePin_init_default, meshtastic_NodeRemoteHardwarePin_init_default, meshtastic_NodeRemoteHardwarePin_init_default, meshtastic_NodeRemoteHardwarePin_init_default, meshtastic_NodeRemoteHardwarePin_init_default, meshtastic_NodeRemoteHardwarePin_init_default, meshtastic_NodeRemoteHardwarePin_init_default, meshtastic_NodeRemoteHardwarePin_init_default, meshtastic_NodeRemoteHardwarePin_init_default, meshtastic_NodeRemoteHardwarePin_init_default, meshtastic_NodeRemoteHardwarePin_init_default, meshtastic_NodeRemoteHardwarePin_init_default, meshtastic_NodeRemoteHardwarePin_init_default, meshtastic_NodeRemoteHardwarePin_init_default, meshtastic_NodeRemoteHardwarePin_init_default}} -#define meshtastic_SharedContact_init_default {0, false, meshtastic_User_init_default, 0} +#define meshtastic_SharedContact_init_default {0, false, meshtastic_User_init_default, 0, 0} #define meshtastic_KeyVerificationAdmin_init_default {_meshtastic_KeyVerificationAdmin_MessageType_MIN, 0, 0, false, 0} #define meshtastic_AdminMessage_init_zero {0, {0}, {0, {0}}} #define meshtastic_AdminMessage_InputEvent_init_zero {0, 0, 0, 0} #define meshtastic_HamParameters_init_zero {"", 0, 0, ""} #define meshtastic_NodeRemoteHardwarePinsResponse_init_zero {0, {meshtastic_NodeRemoteHardwarePin_init_zero, meshtastic_NodeRemoteHardwarePin_init_zero, meshtastic_NodeRemoteHardwarePin_init_zero, meshtastic_NodeRemoteHardwarePin_init_zero, meshtastic_NodeRemoteHardwarePin_init_zero, meshtastic_NodeRemoteHardwarePin_init_zero, meshtastic_NodeRemoteHardwarePin_init_zero, meshtastic_NodeRemoteHardwarePin_init_zero, meshtastic_NodeRemoteHardwarePin_init_zero, meshtastic_NodeRemoteHardwarePin_init_zero, meshtastic_NodeRemoteHardwarePin_init_zero, meshtastic_NodeRemoteHardwarePin_init_zero, meshtastic_NodeRemoteHardwarePin_init_zero, meshtastic_NodeRemoteHardwarePin_init_zero, meshtastic_NodeRemoteHardwarePin_init_zero, meshtastic_NodeRemoteHardwarePin_init_zero}} -#define meshtastic_SharedContact_init_zero {0, false, meshtastic_User_init_zero, 0} +#define meshtastic_SharedContact_init_zero {0, false, meshtastic_User_init_zero, 0, 0} #define meshtastic_KeyVerificationAdmin_init_zero {_meshtastic_KeyVerificationAdmin_MessageType_MIN, 0, 0, false, 0} /* Field tags (for use in manual encoding/decoding) */ @@ -341,6 +343,7 @@ extern "C" { #define meshtastic_SharedContact_node_num_tag 1 #define meshtastic_SharedContact_user_tag 2 #define meshtastic_SharedContact_should_ignore_tag 3 +#define meshtastic_SharedContact_manually_verified_tag 4 #define meshtastic_KeyVerificationAdmin_message_type_tag 1 #define meshtastic_KeyVerificationAdmin_remote_nodenum_tag 2 #define meshtastic_KeyVerificationAdmin_nonce_tag 3 @@ -504,7 +507,8 @@ X(a, STATIC, REPEATED, MESSAGE, node_remote_hardware_pins, 1) #define meshtastic_SharedContact_FIELDLIST(X, a) \ X(a, STATIC, SINGULAR, UINT32, node_num, 1) \ X(a, STATIC, OPTIONAL, MESSAGE, user, 2) \ -X(a, STATIC, SINGULAR, BOOL, should_ignore, 3) +X(a, STATIC, SINGULAR, BOOL, should_ignore, 3) \ +X(a, STATIC, SINGULAR, BOOL, manually_verified, 4) #define meshtastic_SharedContact_CALLBACK NULL #define meshtastic_SharedContact_DEFAULT NULL #define meshtastic_SharedContact_user_MSGTYPE meshtastic_User @@ -539,7 +543,7 @@ extern const pb_msgdesc_t meshtastic_KeyVerificationAdmin_msg; #define meshtastic_HamParameters_size 31 #define meshtastic_KeyVerificationAdmin_size 25 #define meshtastic_NodeRemoteHardwarePinsResponse_size 496 -#define meshtastic_SharedContact_size 125 +#define meshtastic_SharedContact_size 127 #ifdef __cplusplus } /* extern "C" */ diff --git a/src/mesh/generated/meshtastic/mesh.pb.h b/src/mesh/generated/meshtastic/mesh.pb.h index 601d52007..d8d2f2e8a 100644 --- a/src/mesh/generated/meshtastic/mesh.pb.h +++ b/src/mesh/generated/meshtastic/mesh.pb.h @@ -278,6 +278,10 @@ typedef enum _meshtastic_HardwareModel { meshtastic_HardwareModel_M5STACK_C6L = 111, /* M5Stack Cardputer Adv */ meshtastic_HardwareModel_M5STACK_CARDPUTER_ADV = 112, + /* ESP32S3 main controller with GPS and TFT screen. */ + meshtastic_HardwareModel_HELTEC_WIRELESS_TRACKER_V2 = 113, + /* LilyGo T-Watch Ultra */ + meshtastic_HardwareModel_T_WATCH_ULTRA = 114, /* ------------------------------------------------------------------------------------------------------------------------------------------ Reserved ID For developing private Ports. These will show up in live traffic sparsely, so we can use a high number. Keep it within 8 bits. ------------------------------------------------------------------------------------------------------------------------------------------ */ From ec28c383af11cd24afd6b9fd72235789c38c43ad Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 1 Oct 2025 15:32:25 -0500 Subject: [PATCH 13/15] Upgrade trunk (#8159) Co-authored-by: vidplace7 <1779290+vidplace7@users.noreply.github.com> --- .trunk/trunk.yaml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.trunk/trunk.yaml b/.trunk/trunk.yaml index 4e9de6a02..fa6fa6d7d 100644 --- a/.trunk/trunk.yaml +++ b/.trunk/trunk.yaml @@ -8,15 +8,15 @@ plugins: uri: https://github.com/trunk-io/plugins lint: enabled: - - checkov@3.2.471 - - renovate@41.130.1 + - checkov@3.2.473 + - renovate@41.132.2 - prettier@3.6.2 - trufflehog@3.90.8 - yamllint@1.37.1 - bandit@1.8.6 - - trivy@0.66.0 + - trivy@0.67.0 - taplo@0.10.0 - - ruff@0.13.1 + - ruff@0.13.2 - isort@6.0.1 - markdownlint@0.45.0 - oxipng@9.1.5 From 76d480713022f7be1314b8902157fbcad94d601f Mon Sep 17 00:00:00 2001 From: Jonathan Bennett Date: Wed, 1 Oct 2025 21:07:30 -0500 Subject: [PATCH 14/15] Add support for the manually_verified bool in SharedContact (#8180) --- src/mesh/NodeDB.cpp | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/src/mesh/NodeDB.cpp b/src/mesh/NodeDB.cpp index a32ee37f9..5b235485d 100644 --- a/src/mesh/NodeDB.cpp +++ b/src/mesh/NodeDB.cpp @@ -1597,9 +1597,18 @@ void NodeDB::updateTelemetry(uint32_t nodeId, const meshtastic_Telemetry &t, RxS void NodeDB::addFromContact(meshtastic_SharedContact contact) { meshtastic_NodeInfoLite *info = getOrCreateMeshNode(contact.node_num); - if (!info) { + if (!info || !contact.has_user) { return; } + // If the local node has this node marked as manually verified + // and the client does not, do not allow the client to update the + // saved public key. + if ((info->bitfield & NODEINFO_BITFIELD_IS_KEY_MANUALLY_VERIFIED_MASK) && !contact.manually_verified) { + if (contact.user.public_key.size != info->user.public_key.size || + memcmp(contact.user.public_key.bytes, info->user.public_key.bytes, info->user.public_key.size) != 0) { + return; + } + } info->num = contact.node_num; info->has_user = true; info->user = TypeConversions::ConvertToUserLite(contact.user); @@ -1614,10 +1623,12 @@ void NodeDB::addFromContact(meshtastic_SharedContact contact) } else { info->last_heard = getValidTime(RTCQualityNTP); info->is_favorite = true; - info->bitfield |= NODEINFO_BITFIELD_IS_KEY_MANUALLY_VERIFIED_MASK; + // As the clients will begin sending the contact with DMs, we want to strictly check if the node is manually verified + if (contact.manually_verified) { + info->bitfield |= NODEINFO_BITFIELD_IS_KEY_MANUALLY_VERIFIED_MASK; + } // Mark the node's key as manually verified to indicate trustworthiness. updateGUIforNode = info; - // powerFSM.trigger(EVENT_NODEDB_UPDATED); This event has been retired sortMeshDB(); notifyObservers(true); // Force an update whether or not our node counts have changed } From b978c6c86c7d7303de921c01b9d948d067ba6c07 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 2 Oct 2025 05:15:36 -0500 Subject: [PATCH 15/15] Upgrade trunk (#8183) Co-authored-by: vidplace7 <1779290+vidplace7@users.noreply.github.com> --- .trunk/trunk.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.trunk/trunk.yaml b/.trunk/trunk.yaml index fa6fa6d7d..ec1563931 100644 --- a/.trunk/trunk.yaml +++ b/.trunk/trunk.yaml @@ -9,7 +9,7 @@ plugins: lint: enabled: - checkov@3.2.473 - - renovate@41.132.2 + - renovate@41.132.5 - prettier@3.6.2 - trufflehog@3.90.8 - yamllint@1.37.1