mirror of
https://github.com/meshtastic/firmware.git
synced 2025-10-29 23:54:41 +00:00
Merge branch 'meshtastic:develop' into develop
This commit is contained in:
commit
b4f8e9dcb1
6
.github/workflows/main_matrix.yml
vendored
6
.github/workflows/main_matrix.yml
vendored
@ -27,6 +27,7 @@ on:
|
||||
|
||||
jobs:
|
||||
setup:
|
||||
if: github.repository == 'meshtastic/firmware'
|
||||
strategy:
|
||||
fail-fast: true
|
||||
matrix:
|
||||
@ -41,6 +42,10 @@ jobs:
|
||||
python-version: 3.x
|
||||
cache: pip
|
||||
- run: pip install -U platformio
|
||||
- name: Uncomment build epoch
|
||||
shell: bash
|
||||
run: |
|
||||
sed -i 's/#-DBUILD_EPOCH=$UNIX_TIME/-DBUILD_EPOCH=$UNIX_TIME/' platformio.ini
|
||||
- name: Generate matrix
|
||||
id: jsonStep
|
||||
run: |
|
||||
@ -57,6 +62,7 @@ jobs:
|
||||
check: ${{ steps.jsonStep.outputs.check }}
|
||||
|
||||
version:
|
||||
if: github.repository == 'meshtastic/firmware'
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
|
||||
@ -16,7 +16,7 @@ lint:
|
||||
- bandit@1.8.6
|
||||
- trivy@0.67.0
|
||||
- taplo@0.10.0
|
||||
- ruff@0.13.2
|
||||
- ruff@0.13.3
|
||||
- isort@6.1.0
|
||||
- markdownlint@0.45.0
|
||||
- oxipng@9.1.5
|
||||
|
||||
@ -1 +1 @@
|
||||
2.6.4
|
||||
2.6.6
|
||||
@ -70,7 +70,7 @@ lib_deps =
|
||||
# renovate: datasource=git-refs depName=meshtastic-TinyGPSPlus packageName=https://github.com/meshtastic/TinyGPSPlus gitBranch=master
|
||||
https://github.com/meshtastic/TinyGPSPlus/archive/71a82db35f3b973440044c476d4bcdc673b104f4.zip
|
||||
# renovate: datasource=git-refs depName=meshtastic-ArduinoThread packageName=https://github.com/meshtastic/ArduinoThread gitBranch=master
|
||||
https://github.com/meshtastic/ArduinoThread/archive/7c3ee9e1951551b949763b1f5280f8db1fa4068d.zip
|
||||
https://github.com/meshtastic/ArduinoThread/archive/b841b0415721f1341ea41cccfb4adccfaf951567.zip
|
||||
# renovate: datasource=custom.pio depName=Nanopb packageName=nanopb/library/Nanopb
|
||||
nanopb/Nanopb@0.4.91
|
||||
# renovate: datasource=custom.pio depName=ErriezCRC32 packageName=erriez/library/ErriezCRC32
|
||||
@ -120,7 +120,7 @@ lib_deps =
|
||||
[device-ui_base]
|
||||
lib_deps =
|
||||
# renovate: datasource=git-refs depName=meshtastic/device-ui packageName=https://github.com/meshtastic/device-ui gitBranch=master
|
||||
https://github.com/meshtastic/device-ui/archive/505ffadaa7a931df5dc8153229b719a07bbb028c.zip
|
||||
https://github.com/meshtastic/device-ui/archive/e564d78ae1a7e9a225aaf4a73b1cb84c549f510f.zip
|
||||
|
||||
; Common libs for environmental measurements in telemetry module
|
||||
[environmental_base]
|
||||
|
||||
@ -828,11 +828,8 @@ void Power::readPowerStatus()
|
||||
|
||||
// Notify any status instances that are observing us
|
||||
const PowerStatus powerStatus2 = PowerStatus(hasBattery, usbPowered, isChargingNow, batteryVoltageMv, batteryChargePercent);
|
||||
if (millis() > lastLogTime + 50 * 1000) {
|
||||
LOG_DEBUG("Battery: usbPower=%d, isCharging=%d, batMv=%d, batPct=%d", powerStatus2.getHasUSB(),
|
||||
powerStatus2.getIsCharging(), powerStatus2.getBatteryVoltageMv(), powerStatus2.getBatteryChargePercent());
|
||||
lastLogTime = millis();
|
||||
}
|
||||
LOG_DEBUG("Battery: usbPower=%d, isCharging=%d, batMv=%d, batPct=%d", powerStatus2.getHasUSB(), powerStatus2.getIsCharging(),
|
||||
powerStatus2.getBatteryVoltageMv(), powerStatus2.getBatteryChargePercent());
|
||||
newStatus.notifyObservers(&powerStatus2);
|
||||
#ifdef DEBUG_HEAP
|
||||
if (lastheap != memGet.getFreeHeap()) {
|
||||
|
||||
@ -378,7 +378,7 @@ void ScanI2CTwoWire::scanPort(I2CPort port, uint8_t *address, uint8_t asize)
|
||||
case SHT31_4x_ADDR: // same as OPT3001_ADDR_ALT
|
||||
case SHT31_4x_ADDR_ALT: // same as OPT3001_ADDR
|
||||
registerValue = getRegisterValue(ScanI2CTwoWire::RegisterLocation(addr, 0x89), 2);
|
||||
if (registerValue == 0x11a2 || registerValue == 0x11da || registerValue == 0xe9c || registerValue == 0xc8d) {
|
||||
if (registerValue == 0x11a2 || registerValue == 0x11da || registerValue == 0x11f3 || registerValue == 0xe9c || registerValue == 0xc8d) {
|
||||
type = SHT4X;
|
||||
logFoundDevice("SHT4X", (uint8_t)addr.address);
|
||||
} else if (getRegisterValue(ScanI2CTwoWire::RegisterLocation(addr, 0x7E), 2) == 0x5449) {
|
||||
|
||||
449
src/gps/GPS.cpp
449
src/gps/GPS.cpp
@ -506,10 +506,9 @@ bool GPS::setup()
|
||||
delay(1000);
|
||||
#endif
|
||||
if (probeTries < GPS_PROBETRIES) {
|
||||
LOG_DEBUG("Probe for GPS at %d", serialSpeeds[speedSelect]);
|
||||
gnssModel = probe(serialSpeeds[speedSelect]);
|
||||
if (gnssModel == GNSS_MODEL_UNKNOWN) {
|
||||
if (++speedSelect == array_count(serialSpeeds)) {
|
||||
if (currentStep == 0 && ++speedSelect == array_count(serialSpeeds)) {
|
||||
speedSelect = 0;
|
||||
++probeTries;
|
||||
}
|
||||
@ -518,10 +517,9 @@ bool GPS::setup()
|
||||
// Rare Serial Speeds
|
||||
#ifndef CONFIG_IDF_TARGET_ESP32C6
|
||||
if (probeTries == GPS_PROBETRIES) {
|
||||
LOG_DEBUG("Probe for GPS at %d", rareSerialSpeeds[speedSelect]);
|
||||
gnssModel = probe(rareSerialSpeeds[speedSelect]);
|
||||
if (gnssModel == GNSS_MODEL_UNKNOWN) {
|
||||
if (++speedSelect == array_count(rareSerialSpeeds)) {
|
||||
if (currentStep == 0 && ++speedSelect == array_count(rareSerialSpeeds)) {
|
||||
LOG_WARN("Give up on GPS probe and set to %d", GPS_BAUDRATE);
|
||||
return true;
|
||||
}
|
||||
@ -1033,7 +1031,7 @@ void GPS::down()
|
||||
LOG_DEBUG("%us until next search", sleepTime / 1000);
|
||||
|
||||
// If update interval less than 10 seconds, no attempt to sleep
|
||||
if (updateInterval <= 10 * 1000UL || sleepTime == 0)
|
||||
if (updateInterval <= GPS_UPDATE_ALWAYS_ON_THRESHOLD_MS || sleepTime == 0)
|
||||
setPowerState(GPS_IDLE);
|
||||
|
||||
else {
|
||||
@ -1094,7 +1092,7 @@ int32_t GPS::runOnce()
|
||||
return disable();
|
||||
}
|
||||
if (!setup())
|
||||
return 2000; // Setup failed, re-run in two seconds
|
||||
return currentDelay; // Setup failed, re-run in two seconds
|
||||
|
||||
// We have now loaded our saved preferences from flash
|
||||
if (config.position.gps_mode != meshtastic_Config_PositionConfig_GpsMode_ENABLED) {
|
||||
@ -1104,6 +1102,29 @@ int32_t GPS::runOnce()
|
||||
publishUpdate();
|
||||
}
|
||||
|
||||
// ======================== GPS_ACTIVE state ========================
|
||||
// In GPS_ACTIVE state, GPS is powered on and we're receiving NMEA messages.
|
||||
// We use the following logic to determine when to update the local position
|
||||
// or time by running GPS::publishUpdate.
|
||||
// Note: Local position update is asynchronous to position broadcast. We
|
||||
// generally run this state every gps_update_interval seconds, and in most cases
|
||||
// gps_update_interval is faster than the position broadcast interval so there's a
|
||||
// fresh position ready when the device wants to broadcast one on the mesh.
|
||||
//
|
||||
// 1. Got a time for the first time --> set the time, don't publish.
|
||||
// 2. Got a lock for the first time
|
||||
// --> If gps_update_interval is <= 10s --> publishUpdate
|
||||
// --> Otherwise, hold for MIN(gps_update_interval - GPS_UPDATE_ALWAYS_ON_THRESHOLD_MS, 20s)
|
||||
// 3. Got a lock after turning back on
|
||||
// --> If gps_update_interval is <= 10s --> publishUpdate
|
||||
// --> Otherwise, hold for MIN(gps_update_interval - GPS_UPDATE_ALWAYS_ON_THRESHOLD_MS, 20s)
|
||||
// 4. Hold has expired
|
||||
// --> If we have a time and a location --> publishUpdate
|
||||
// --> down()
|
||||
// 5. Search time has expired
|
||||
// --> If we have a time and a location --> publishUpdate
|
||||
// --> If we had a location before but don't now --> publishUpdate
|
||||
// --> down()
|
||||
if (whileActive()) {
|
||||
// if we have received valid NMEA claim we are connected
|
||||
setConnected();
|
||||
@ -1113,55 +1134,81 @@ int32_t GPS::runOnce()
|
||||
if (!config.position.fixed_position && powerState != GPS_ACTIVE && scheduling.isUpdateDue())
|
||||
up();
|
||||
|
||||
// If we've already set time from the GPS, no need to ask the GPS
|
||||
bool gotTime = (getRTCQuality() >= RTCQualityGPS);
|
||||
if (!gotTime && lookForTime()) { // Note: we count on this && short-circuiting and not resetting the RTC time
|
||||
gotTime = true;
|
||||
shouldPublish = true;
|
||||
}
|
||||
|
||||
// quality of the previous fix. We set it to 0 when we go down, so it's a way
|
||||
// to check if we're getting a lock after being GPS_OFF.
|
||||
uint8_t prev_fixQual = fixQual;
|
||||
bool gotLoc = lookForLocation();
|
||||
if (gotLoc && !hasValidLocation) { // declare that we have location ASAP
|
||||
LOG_DEBUG("hasValidLocation RISING EDGE");
|
||||
hasValidLocation = true;
|
||||
shouldPublish = true;
|
||||
// Hold for 20secs after getting a lock to download ephemeris etc
|
||||
fixHoldEnds = millis() + 20000;
|
||||
}
|
||||
|
||||
if (gotLoc && prev_fixQual == 0) { // just got a lock after turning back on.
|
||||
fixHoldEnds = millis() + 20000;
|
||||
shouldPublish = true; // Publish immediately, since next publish is at end of hold
|
||||
}
|
||||
if (powerState == GPS_ACTIVE) {
|
||||
// if gps_update_interval is <=10s, GPS never goes off, so we treat that differently
|
||||
uint32_t updateInterval = Default::getConfiguredOrDefaultMs(config.position.gps_update_interval);
|
||||
|
||||
bool tooLong = scheduling.searchedTooLong();
|
||||
if (tooLong)
|
||||
LOG_WARN("Couldn't publish a valid location: didn't get a GPS lock in time");
|
||||
// 1. Got a time for the first time
|
||||
bool gotTime = (getRTCQuality() >= RTCQualityGPS);
|
||||
if (!gotTime && lookForTime()) { // Note: we count on this && short-circuiting and not resetting the RTC time
|
||||
gotTime = true;
|
||||
}
|
||||
|
||||
// Once we get a location we no longer desperately want an update
|
||||
if ((gotLoc && gotTime) || tooLong) {
|
||||
// 2. Got a lock for the first time, or 3. Got a lock after turning back on
|
||||
bool gotLoc = lookForLocation();
|
||||
if (gotLoc) {
|
||||
#ifdef GPS_DEBUG
|
||||
if (!hasValidLocation) { // declare that we have location ASAP
|
||||
LOG_DEBUG("hasValidLocation RISING EDGE");
|
||||
}
|
||||
#endif
|
||||
if (updateInterval <= GPS_UPDATE_ALWAYS_ON_THRESHOLD_MS) {
|
||||
hasValidLocation = true;
|
||||
shouldPublish = true;
|
||||
} else if (!hasValidLocation || prev_fixQual == 0 || (fixHoldEnds + GPS_THREAD_INTERVAL) < millis()) {
|
||||
hasValidLocation = true;
|
||||
// Hold for up to 20secs after getting a lock to download ephemeris etc
|
||||
uint32_t holdTime = updateInterval - GPS_UPDATE_ALWAYS_ON_THRESHOLD_MS;
|
||||
if (holdTime > GPS_FIX_HOLD_MAX_MS)
|
||||
holdTime = GPS_FIX_HOLD_MAX_MS;
|
||||
fixHoldEnds = millis() + holdTime;
|
||||
#ifdef GPS_DEBUG
|
||||
LOG_DEBUG("Holding for %ums after lock", holdTime);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
bool tooLong = scheduling.searchedTooLong();
|
||||
if (tooLong && !gotLoc) {
|
||||
LOG_WARN("Couldn't publish a valid location: didn't get a GPS lock in time");
|
||||
// we didn't get a location during this ack window, therefore declare loss of lock
|
||||
if (hasValidLocation) {
|
||||
LOG_DEBUG("hasValidLocation FALLING EDGE");
|
||||
}
|
||||
p = meshtastic_Position_init_default;
|
||||
hasValidLocation = false;
|
||||
}
|
||||
if (millis() > fixHoldEnds) {
|
||||
shouldPublish = true; // publish our update at the end of the lock hold
|
||||
publishUpdate();
|
||||
down();
|
||||
p = meshtastic_Position_init_default;
|
||||
hasValidLocation = false;
|
||||
shouldPublish = true;
|
||||
#ifdef GPS_DEBUG
|
||||
} else {
|
||||
LOG_DEBUG("hasValidLocation FALLING EDGE");
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
// Hold has expired , Search time has expired, we got a time only, or we never needed to hold.
|
||||
bool holdExpired = (fixHoldEnds != 0 && millis() > fixHoldEnds);
|
||||
if (shouldPublish || tooLong || holdExpired) {
|
||||
if (gotTime && hasValidLocation) {
|
||||
shouldPublish = true;
|
||||
}
|
||||
if (shouldPublish) {
|
||||
fixHoldEnds = 0;
|
||||
publishUpdate();
|
||||
}
|
||||
|
||||
// There's a chance we just got a time, so keep going to see if we can get a location too
|
||||
if (tooLong || holdExpired) {
|
||||
down();
|
||||
}
|
||||
|
||||
#ifdef GPS_DEBUG
|
||||
} else if (fixHoldEnds != 0) {
|
||||
LOG_DEBUG("Holding for GPS data download: %d ms (numSats=%d)", fixHoldEnds - millis(), p.sats_in_view);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
// If state has changed do a publish
|
||||
publishUpdate();
|
||||
// ===================== end GPS_ACTIVE state ========================
|
||||
|
||||
if (config.position.fixed_position == true && hasValidLocation)
|
||||
return disable(); // This should trigger when we have a fixed position, and get that first position
|
||||
@ -1218,163 +1265,197 @@ static const char *DETECTED_MESSAGE = "%s detected";
|
||||
|
||||
GnssModel_t GPS::probe(int serialSpeed)
|
||||
{
|
||||
uint8_t buffer[768] = {0};
|
||||
|
||||
switch (currentStep) {
|
||||
case 0: {
|
||||
#if defined(ARCH_NRF52) || defined(ARCH_PORTDUINO) || defined(ARCH_STM32WL)
|
||||
_serial_gps->end();
|
||||
_serial_gps->begin(serialSpeed);
|
||||
_serial_gps->end();
|
||||
_serial_gps->begin(serialSpeed);
|
||||
#elif defined(ARCH_RP2040)
|
||||
_serial_gps->end();
|
||||
_serial_gps->setFIFOSize(256);
|
||||
_serial_gps->begin(serialSpeed);
|
||||
_serial_gps->end();
|
||||
_serial_gps->setFIFOSize(256);
|
||||
_serial_gps->begin(serialSpeed);
|
||||
#else
|
||||
if (_serial_gps->baudRate() != serialSpeed) {
|
||||
LOG_DEBUG("Set Baud to %i", serialSpeed);
|
||||
_serial_gps->updateBaudRate(serialSpeed);
|
||||
}
|
||||
if (_serial_gps->baudRate() != serialSpeed) {
|
||||
LOG_DEBUG("Set GPS Baud to %i", serialSpeed);
|
||||
_serial_gps->updateBaudRate(serialSpeed);
|
||||
}
|
||||
#endif
|
||||
|
||||
memset(&ublox_info, 0, sizeof(ublox_info));
|
||||
uint8_t buffer[768] = {0};
|
||||
delay(100);
|
||||
memset(&ublox_info, 0, sizeof(ublox_info));
|
||||
delay(100);
|
||||
|
||||
// Close all NMEA sentences, valid for L76K, ATGM336H (and likely other AT6558 devices)
|
||||
_serial_gps->write("$PCAS03,0,0,0,0,0,0,0,0,0,0,,,0,0*02\r\n");
|
||||
delay(20);
|
||||
// Close NMEA sequences on Ublox
|
||||
_serial_gps->write("$PUBX,40,GLL,0,0,0,0,0,0*5C\r\n");
|
||||
_serial_gps->write("$PUBX,40,GSV,0,0,0,0,0,0*59\r\n");
|
||||
_serial_gps->write("$PUBX,40,VTG,0,0,0,0,0,0*5E\r\n");
|
||||
delay(20);
|
||||
// Close NMEA sequences on CM121
|
||||
_serial_gps->write("$CFGMSG,0,1,0,1*1B\r\n");
|
||||
_serial_gps->write("$CFGMSG,0,2,0,1*18\r\n");
|
||||
_serial_gps->write("$CFGMSG,0,3,0,1*19\r\n");
|
||||
delay(20);
|
||||
|
||||
// Unicore UFirebirdII Series: UC6580, UM620, UM621, UM670A, UM680A, or UM681A,or CM121
|
||||
std::vector<ChipInfo> unicore = {
|
||||
{"UC6580", "UC6580", GNSS_MODEL_UC6580}, {"UM600", "UM600", GNSS_MODEL_UC6580}, {"CM121", "CM121", GNSS_MODEL_CM121}};
|
||||
PROBE_FAMILY("Unicore Family", "$PDTINFO", unicore, 500);
|
||||
|
||||
std::vector<ChipInfo> atgm = {
|
||||
{"ATGM336H", "$GPTXT,01,01,02,HW=ATGM336H", GNSS_MODEL_ATGM336H},
|
||||
/* ATGM332D series (-11(GPS), -21(BDS), -31(GPS+BDS), -51(GPS+GLONASS), -71-0(GPS+BDS+GLONASS)) based on AT6558 */
|
||||
{"ATGM332D", "$GPTXT,01,01,02,HW=ATGM332D", GNSS_MODEL_ATGM336H}};
|
||||
PROBE_FAMILY("ATGM33xx Family", "$PCAS06,1*1A", atgm, 500);
|
||||
|
||||
/* Airoha (Mediatek) AG3335A/M/S, A3352Q, Quectel L89 2.0, SimCom SIM65M */
|
||||
_serial_gps->write("$PAIR062,2,0*3C\r\n"); // GSA OFF to reduce volume
|
||||
_serial_gps->write("$PAIR062,3,0*3D\r\n"); // GSV OFF to reduce volume
|
||||
_serial_gps->write("$PAIR513*3D\r\n"); // save configuration
|
||||
std::vector<ChipInfo> airoha = {{"AG3335", "$PAIR021,AG3335", GNSS_MODEL_AG3335},
|
||||
{"AG3352", "$PAIR021,AG3352", GNSS_MODEL_AG3352},
|
||||
{"RYS3520", "$PAIR021,REYAX_RYS3520_V2", GNSS_MODEL_AG3352}};
|
||||
PROBE_FAMILY("Airoha Family", "$PAIR021*39", airoha, 1000);
|
||||
|
||||
PROBE_SIMPLE("LC86", "$PQTMVERNO*58", "$PQTMVERNO,LC86", GNSS_MODEL_AG3352, 500);
|
||||
PROBE_SIMPLE("L76K", "$PCAS06,0*1B", "$GPTXT,01,01,02,SW=", GNSS_MODEL_MTK, 500);
|
||||
|
||||
// Close all NMEA sentences, valid for MTK3333 and MTK3339 platforms
|
||||
_serial_gps->write("$PMTK514,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0*2E\r\n");
|
||||
delay(20);
|
||||
std::vector<ChipInfo> mtk = {{"L76B", "Quectel-L76B", GNSS_MODEL_MTK_L76B}, {"PA1010D", "1010D", GNSS_MODEL_MTK_PA1010D},
|
||||
{"PA1616S", "1616S", GNSS_MODEL_MTK_PA1616S}, {"LS20031", "MC-1513", GNSS_MODEL_MTK_L76B},
|
||||
{"L96", "Quectel-L96", GNSS_MODEL_MTK_L76B}, {"L80-R", "_3337_", GNSS_MODEL_MTK_L76B},
|
||||
{"L80", "_3339_", GNSS_MODEL_MTK_L76B}};
|
||||
|
||||
PROBE_FAMILY("MTK Family", "$PMTK605*31", mtk, 500);
|
||||
|
||||
uint8_t cfg_rate[] = {0xB5, 0x62, 0x06, 0x08, 0x00, 0x00, 0x00, 0x00};
|
||||
UBXChecksum(cfg_rate, sizeof(cfg_rate));
|
||||
clearBuffer();
|
||||
_serial_gps->write(cfg_rate, sizeof(cfg_rate));
|
||||
// Check that the returned response class and message ID are correct
|
||||
GPS_RESPONSE response = getACK(0x06, 0x08, 750);
|
||||
if (response == GNSS_RESPONSE_NONE) {
|
||||
LOG_WARN("No GNSS Module (baudrate %d)", serialSpeed);
|
||||
// Close all NMEA sentences, valid for L76K, ATGM336H (and likely other AT6558 devices)
|
||||
_serial_gps->write("$PCAS03,0,0,0,0,0,0,0,0,0,0,,,0,0*02\r\n");
|
||||
delay(20);
|
||||
// Close NMEA sequences on Ublox
|
||||
_serial_gps->write("$PUBX,40,GLL,0,0,0,0,0,0*5C\r\n");
|
||||
_serial_gps->write("$PUBX,40,GSV,0,0,0,0,0,0*59\r\n");
|
||||
_serial_gps->write("$PUBX,40,VTG,0,0,0,0,0,0*5E\r\n");
|
||||
delay(20);
|
||||
// Close NMEA sequences on CM121
|
||||
_serial_gps->write("$CFGMSG,0,1,0,1*1B\r\n");
|
||||
_serial_gps->write("$CFGMSG,0,2,0,1*18\r\n");
|
||||
_serial_gps->write("$CFGMSG,0,3,0,1*19\r\n");
|
||||
currentDelay = 20;
|
||||
currentStep = 1;
|
||||
return GNSS_MODEL_UNKNOWN;
|
||||
} else if (response == GNSS_RESPONSE_FRAME_ERRORS) {
|
||||
LOG_INFO("UBlox Frame Errors (baudrate %d)", serialSpeed);
|
||||
}
|
||||
case 1: {
|
||||
|
||||
memset(buffer, 0, sizeof(buffer));
|
||||
uint8_t _message_MONVER[8] = {
|
||||
0xB5, 0x62, // Sync message for UBX protocol
|
||||
0x0A, 0x04, // Message class and ID (UBX-MON-VER)
|
||||
0x00, 0x00, // Length of payload (we're asking for an answer, so no payload)
|
||||
0x00, 0x00 // Checksum
|
||||
};
|
||||
// Get Ublox gnss module hardware and software info
|
||||
UBXChecksum(_message_MONVER, sizeof(_message_MONVER));
|
||||
clearBuffer();
|
||||
_serial_gps->write(_message_MONVER, sizeof(_message_MONVER));
|
||||
// Unicore UFirebirdII Series: UC6580, UM620, UM621, UM670A, UM680A, or UM681A,or CM121
|
||||
std::vector<ChipInfo> unicore = {
|
||||
{"UC6580", "UC6580", GNSS_MODEL_UC6580}, {"UM600", "UM600", GNSS_MODEL_UC6580}, {"CM121", "CM121", GNSS_MODEL_CM121}};
|
||||
PROBE_FAMILY("Unicore Family", "$PDTINFO", unicore, 500);
|
||||
currentDelay = 20;
|
||||
currentStep = 2;
|
||||
return GNSS_MODEL_UNKNOWN;
|
||||
}
|
||||
case 2: {
|
||||
std::vector<ChipInfo> atgm = {
|
||||
{"ATGM336H", "$GPTXT,01,01,02,HW=ATGM336H", GNSS_MODEL_ATGM336H},
|
||||
/* ATGM332D series (-11(GPS), -21(BDS), -31(GPS+BDS), -51(GPS+GLONASS), -71-0(GPS+BDS+GLONASS)) based on AT6558 */
|
||||
{"ATGM332D", "$GPTXT,01,01,02,HW=ATGM332D", GNSS_MODEL_ATGM336H}};
|
||||
PROBE_FAMILY("ATGM33xx Family", "$PCAS06,1*1A", atgm, 500);
|
||||
currentDelay = 20;
|
||||
currentStep = 3;
|
||||
return GNSS_MODEL_UNKNOWN;
|
||||
}
|
||||
case 3: {
|
||||
/* Airoha (Mediatek) AG3335A/M/S, A3352Q, Quectel L89 2.0, SimCom SIM65M */
|
||||
_serial_gps->write("$PAIR062,2,0*3C\r\n"); // GSA OFF to reduce volume
|
||||
_serial_gps->write("$PAIR062,3,0*3D\r\n"); // GSV OFF to reduce volume
|
||||
_serial_gps->write("$PAIR513*3D\r\n"); // save configuration
|
||||
std::vector<ChipInfo> airoha = {{"AG3335", "$PAIR021,AG3335", GNSS_MODEL_AG3335},
|
||||
{"AG3352", "$PAIR021,AG3352", GNSS_MODEL_AG3352},
|
||||
{"RYS3520", "$PAIR021,REYAX_RYS3520_V2", GNSS_MODEL_AG3352}};
|
||||
PROBE_FAMILY("Airoha Family", "$PAIR021*39", airoha, 1000);
|
||||
currentDelay = 20;
|
||||
currentStep = 4;
|
||||
return GNSS_MODEL_UNKNOWN;
|
||||
}
|
||||
case 4: {
|
||||
PROBE_SIMPLE("LC86", "$PQTMVERNO*58", "$PQTMVERNO,LC86", GNSS_MODEL_AG3352, 500);
|
||||
PROBE_SIMPLE("L76K", "$PCAS06,0*1B", "$GPTXT,01,01,02,SW=", GNSS_MODEL_MTK, 500);
|
||||
currentDelay = 20;
|
||||
currentStep = 5;
|
||||
return GNSS_MODEL_UNKNOWN;
|
||||
}
|
||||
case 5: {
|
||||
|
||||
uint16_t len = getACK(buffer, sizeof(buffer), 0x0A, 0x04, 1200);
|
||||
if (len) {
|
||||
uint16_t position = 0;
|
||||
for (int i = 0; i < 30; i++) {
|
||||
ublox_info.swVersion[i] = buffer[position];
|
||||
position++;
|
||||
}
|
||||
for (int i = 0; i < 10; i++) {
|
||||
ublox_info.hwVersion[i] = buffer[position];
|
||||
position++;
|
||||
}
|
||||
// Close all NMEA sentences, valid for MTK3333 and MTK3339 platforms
|
||||
_serial_gps->write("$PMTK514,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0*2E\r\n");
|
||||
delay(20);
|
||||
std::vector<ChipInfo> mtk = {{"L76B", "Quectel-L76B", GNSS_MODEL_MTK_L76B}, {"PA1010D", "1010D", GNSS_MODEL_MTK_PA1010D},
|
||||
{"PA1616S", "1616S", GNSS_MODEL_MTK_PA1616S}, {"LS20031", "MC-1513", GNSS_MODEL_MTK_L76B},
|
||||
{"L96", "Quectel-L96", GNSS_MODEL_MTK_L76B}, {"L80-R", "_3337_", GNSS_MODEL_MTK_L76B},
|
||||
{"L80", "_3339_", GNSS_MODEL_MTK_L76B}};
|
||||
|
||||
while (len >= position + 30) {
|
||||
for (int i = 0; i < 30; i++) {
|
||||
ublox_info.extension[ublox_info.extensionNo][i] = buffer[position];
|
||||
position++;
|
||||
}
|
||||
ublox_info.extensionNo++;
|
||||
if (ublox_info.extensionNo > 9)
|
||||
break;
|
||||
}
|
||||
|
||||
LOG_DEBUG("Module Info : ");
|
||||
LOG_DEBUG("Soft version: %s", ublox_info.swVersion);
|
||||
LOG_DEBUG("Hard version: %s", ublox_info.hwVersion);
|
||||
LOG_DEBUG("Extensions:%d", ublox_info.extensionNo);
|
||||
for (int i = 0; i < ublox_info.extensionNo; i++) {
|
||||
LOG_DEBUG(" %s", ublox_info.extension[i]);
|
||||
PROBE_FAMILY("MTK Family", "$PMTK605*31", mtk, 500);
|
||||
currentDelay = 20;
|
||||
currentStep = 6;
|
||||
return GNSS_MODEL_UNKNOWN;
|
||||
}
|
||||
case 6: {
|
||||
uint8_t cfg_rate[] = {0xB5, 0x62, 0x06, 0x08, 0x00, 0x00, 0x00, 0x00};
|
||||
UBXChecksum(cfg_rate, sizeof(cfg_rate));
|
||||
clearBuffer();
|
||||
_serial_gps->write(cfg_rate, sizeof(cfg_rate));
|
||||
// Check that the returned response class and message ID are correct
|
||||
GPS_RESPONSE response = getACK(0x06, 0x08, 750);
|
||||
if (response == GNSS_RESPONSE_NONE) {
|
||||
LOG_WARN("No GNSS Module (baudrate %d)", serialSpeed);
|
||||
currentDelay = 2000;
|
||||
currentStep = 0;
|
||||
return GNSS_MODEL_UNKNOWN;
|
||||
} else if (response == GNSS_RESPONSE_FRAME_ERRORS) {
|
||||
LOG_INFO("UBlox Frame Errors (baudrate %d)", serialSpeed);
|
||||
}
|
||||
|
||||
memset(buffer, 0, sizeof(buffer));
|
||||
uint8_t _message_MONVER[8] = {
|
||||
0xB5, 0x62, // Sync message for UBX protocol
|
||||
0x0A, 0x04, // Message class and ID (UBX-MON-VER)
|
||||
0x00, 0x00, // Length of payload (we're asking for an answer, so no payload)
|
||||
0x00, 0x00 // Checksum
|
||||
};
|
||||
// Get Ublox gnss module hardware and software info
|
||||
UBXChecksum(_message_MONVER, sizeof(_message_MONVER));
|
||||
clearBuffer();
|
||||
_serial_gps->write(_message_MONVER, sizeof(_message_MONVER));
|
||||
|
||||
// tips: extensionNo field is 0 on some 6M GNSS modules
|
||||
for (int i = 0; i < ublox_info.extensionNo; ++i) {
|
||||
if (!strncmp(ublox_info.extension[i], "MOD=", 4)) {
|
||||
strncpy((char *)buffer, &(ublox_info.extension[i][4]), sizeof(buffer));
|
||||
} else if (!strncmp(ublox_info.extension[i], "PROTVER", 7)) {
|
||||
char *ptr = nullptr;
|
||||
memset(buffer, 0, sizeof(buffer));
|
||||
strncpy((char *)buffer, &(ublox_info.extension[i][8]), sizeof(buffer));
|
||||
LOG_DEBUG("Protocol Version:%s", (char *)buffer);
|
||||
if (strlen((char *)buffer)) {
|
||||
ublox_info.protocol_version = strtoul((char *)buffer, &ptr, 10);
|
||||
LOG_DEBUG("ProtVer=%d", ublox_info.protocol_version);
|
||||
} else {
|
||||
ublox_info.protocol_version = 0;
|
||||
uint16_t len = getACK(buffer, sizeof(buffer), 0x0A, 0x04, 1200);
|
||||
if (len) {
|
||||
uint16_t position = 0;
|
||||
for (int i = 0; i < 30; i++) {
|
||||
ublox_info.swVersion[i] = buffer[position];
|
||||
position++;
|
||||
}
|
||||
for (int i = 0; i < 10; i++) {
|
||||
ublox_info.hwVersion[i] = buffer[position];
|
||||
position++;
|
||||
}
|
||||
|
||||
while (len >= position + 30) {
|
||||
for (int i = 0; i < 30; i++) {
|
||||
ublox_info.extension[ublox_info.extensionNo][i] = buffer[position];
|
||||
position++;
|
||||
}
|
||||
ublox_info.extensionNo++;
|
||||
if (ublox_info.extensionNo > 9)
|
||||
break;
|
||||
}
|
||||
|
||||
LOG_DEBUG("Module Info : ");
|
||||
LOG_DEBUG("Soft version: %s", ublox_info.swVersion);
|
||||
LOG_DEBUG("Hard version: %s", ublox_info.hwVersion);
|
||||
LOG_DEBUG("Extensions:%d", ublox_info.extensionNo);
|
||||
for (int i = 0; i < ublox_info.extensionNo; i++) {
|
||||
LOG_DEBUG(" %s", ublox_info.extension[i]);
|
||||
}
|
||||
|
||||
memset(buffer, 0, sizeof(buffer));
|
||||
|
||||
// tips: extensionNo field is 0 on some 6M GNSS modules
|
||||
for (int i = 0; i < ublox_info.extensionNo; ++i) {
|
||||
if (!strncmp(ublox_info.extension[i], "MOD=", 4)) {
|
||||
strncpy((char *)buffer, &(ublox_info.extension[i][4]), sizeof(buffer));
|
||||
} else if (!strncmp(ublox_info.extension[i], "PROTVER", 7)) {
|
||||
char *ptr = nullptr;
|
||||
memset(buffer, 0, sizeof(buffer));
|
||||
strncpy((char *)buffer, &(ublox_info.extension[i][8]), sizeof(buffer));
|
||||
LOG_DEBUG("Protocol Version:%s", (char *)buffer);
|
||||
if (strlen((char *)buffer)) {
|
||||
ublox_info.protocol_version = strtoul((char *)buffer, &ptr, 10);
|
||||
LOG_DEBUG("ProtVer=%d", ublox_info.protocol_version);
|
||||
} else {
|
||||
ublox_info.protocol_version = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (strncmp(ublox_info.hwVersion, "00040007", 8) == 0) {
|
||||
LOG_INFO(DETECTED_MESSAGE, "U-blox 6", "6");
|
||||
return GNSS_MODEL_UBLOX6;
|
||||
} else if (strncmp(ublox_info.hwVersion, "00070000", 8) == 0) {
|
||||
LOG_INFO(DETECTED_MESSAGE, "U-blox 7", "7");
|
||||
return GNSS_MODEL_UBLOX7;
|
||||
} else if (strncmp(ublox_info.hwVersion, "00080000", 8) == 0) {
|
||||
LOG_INFO(DETECTED_MESSAGE, "U-blox 8", "8");
|
||||
return GNSS_MODEL_UBLOX8;
|
||||
} else if (strncmp(ublox_info.hwVersion, "00190000", 8) == 0) {
|
||||
LOG_INFO(DETECTED_MESSAGE, "U-blox 9", "9");
|
||||
return GNSS_MODEL_UBLOX9;
|
||||
} else if (strncmp(ublox_info.hwVersion, "000A0000", 8) == 0) {
|
||||
LOG_INFO(DETECTED_MESSAGE, "U-blox 10", "10");
|
||||
return GNSS_MODEL_UBLOX10;
|
||||
if (strncmp(ublox_info.hwVersion, "00040007", 8) == 0) {
|
||||
LOG_INFO(DETECTED_MESSAGE, "U-blox 6", "6");
|
||||
return GNSS_MODEL_UBLOX6;
|
||||
} else if (strncmp(ublox_info.hwVersion, "00070000", 8) == 0) {
|
||||
LOG_INFO(DETECTED_MESSAGE, "U-blox 7", "7");
|
||||
return GNSS_MODEL_UBLOX7;
|
||||
} else if (strncmp(ublox_info.hwVersion, "00080000", 8) == 0) {
|
||||
LOG_INFO(DETECTED_MESSAGE, "U-blox 8", "8");
|
||||
return GNSS_MODEL_UBLOX8;
|
||||
} else if (strncmp(ublox_info.hwVersion, "00190000", 8) == 0) {
|
||||
LOG_INFO(DETECTED_MESSAGE, "U-blox 9", "9");
|
||||
return GNSS_MODEL_UBLOX9;
|
||||
} else if (strncmp(ublox_info.hwVersion, "000A0000", 8) == 0) {
|
||||
LOG_INFO(DETECTED_MESSAGE, "U-blox 10", "10");
|
||||
return GNSS_MODEL_UBLOX10;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
LOG_WARN("No GNSS Module (baudrate %d)", serialSpeed);
|
||||
currentDelay = 2000;
|
||||
currentStep = 0;
|
||||
return GNSS_MODEL_UNKNOWN;
|
||||
}
|
||||
|
||||
@ -1589,12 +1670,8 @@ bool GPS::lookForLocation()
|
||||
|
||||
#ifndef TINYGPS_OPTION_NO_STATISTICS
|
||||
if (reader.failedChecksum() > lastChecksumFailCount) {
|
||||
// In a GPS_DEBUG build we want to log all of these. In production, we only care if there are many of them.
|
||||
#ifndef GPS_DEBUG
|
||||
if (reader.failedChecksum() > 4)
|
||||
#endif
|
||||
LOG_WARN("%u new GPS checksum failures, for a total of %u", reader.failedChecksum() - lastChecksumFailCount,
|
||||
reader.failedChecksum());
|
||||
LOG_WARN("%u new GPS checksum failures, for a total of %u", reader.failedChecksum() - lastChecksumFailCount,
|
||||
reader.failedChecksum());
|
||||
lastChecksumFailCount = reader.failedChecksum();
|
||||
}
|
||||
#endif
|
||||
|
||||
@ -16,6 +16,9 @@
|
||||
#define GPS_EN_ACTIVE 1
|
||||
#endif
|
||||
|
||||
static constexpr uint32_t GPS_UPDATE_ALWAYS_ON_THRESHOLD_MS = 10 * 1000UL;
|
||||
static constexpr uint32_t GPS_FIX_HOLD_MAX_MS = 20000;
|
||||
|
||||
typedef enum {
|
||||
GNSS_MODEL_ATGM336H,
|
||||
GNSS_MODEL_MTK,
|
||||
@ -151,6 +154,8 @@ class GPS : private concurrency::OSThread
|
||||
TinyGPSPlus reader;
|
||||
uint8_t fixQual = 0; // fix quality from GPGGA
|
||||
uint32_t lastChecksumFailCount = 0;
|
||||
uint8_t currentStep = 0;
|
||||
int32_t currentDelay = 2000;
|
||||
|
||||
#ifndef TINYGPS_OPTION_NO_CUSTOM_FIELDS
|
||||
// (20210908) TinyGps++ can only read the GPGSA "FIX TYPE" field
|
||||
@ -173,8 +178,6 @@ class GPS : private concurrency::OSThread
|
||||
*/
|
||||
bool hasValidLocation = false; // default to false, until we complete our first read
|
||||
|
||||
bool isInPowersave = false;
|
||||
|
||||
bool shouldPublish = false; // If we've changed GPS state, this will force a publish the next loop()
|
||||
|
||||
bool hasGPS = false; // Do we have a GPS we are talking to
|
||||
|
||||
@ -1503,7 +1503,7 @@ int Screen::handleTextMessage(const meshtastic_MeshPacket *packet)
|
||||
screen->showSimpleBanner(banner, 1500);
|
||||
if (config.device.buzzer_mode != meshtastic_Config_DeviceConfig_BuzzerMode_DIRECT_MSG_ONLY ||
|
||||
(isAlert && moduleConfig.external_notification.alert_bell_buzzer) ||
|
||||
(!isBroadcast(packet->to) && isToUs(p))) {
|
||||
(!isBroadcast(packet->to) && isToUs(packet))) {
|
||||
// Beep if not in DIRECT_MSG_ONLY mode or if in DIRECT_MSG_ONLY mode and either
|
||||
// - packet contains an alert and alert bell buzzer is enabled
|
||||
// - packet is a non-broadcast that is addressed to this node
|
||||
|
||||
@ -31,8 +31,33 @@ bool FloodingRouter::shouldFilterReceived(const meshtastic_MeshPacket *p)
|
||||
wasSeenRecently(p, true, nullptr, nullptr, &wasUpgraded); // Updates history; returns false when an upgrade is detected
|
||||
|
||||
// Handle hop_limit upgrade scenario for rebroadcasters
|
||||
if (wasUpgraded && perhapsHandleUpgradedPacket(p)) {
|
||||
return true; // we handled it, so stop processing
|
||||
// isRebroadcaster() is duplicated in perhapsRebroadcast(), but this avoids confusing log messages
|
||||
if (wasUpgraded && isRebroadcaster() && iface && p->hop_limit > 0) {
|
||||
// wasSeenRecently() reports false in upgrade cases so we handle replacement before the duplicate short-circuit
|
||||
// If we overhear a duplicate copy of the packet with more hops left than the one we are waiting to
|
||||
// rebroadcast, then remove the packet currently sitting in the TX queue and use this one instead.
|
||||
uint8_t dropThreshold = p->hop_limit; // remove queued packets that have fewer hops remaining
|
||||
if (iface->removePendingTXPacket(getFrom(p), p->id, dropThreshold)) {
|
||||
LOG_DEBUG("Processing upgraded packet 0x%08x for rebroadcast with hop limit %d (dropping queued < %d)", p->id,
|
||||
p->hop_limit, dropThreshold);
|
||||
|
||||
if (nodeDB)
|
||||
nodeDB->updateFrom(*p);
|
||||
#if !MESHTASTIC_EXCLUDE_TRACEROUTE
|
||||
if (traceRouteModule && p->which_payload_variant == meshtastic_MeshPacket_decoded_tag &&
|
||||
p->decoded.portnum == meshtastic_PortNum_TRACEROUTE_APP)
|
||||
traceRouteModule->processUpgradedPacket(*p);
|
||||
#endif
|
||||
|
||||
perhapsRebroadcast(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) {
|
||||
@ -45,10 +70,8 @@ bool FloodingRouter::shouldFilterReceived(const meshtastic_MeshPacket *p)
|
||||
if (isRepeated) {
|
||||
LOG_DEBUG("Repeated reliable tx");
|
||||
// Check if it's still in the Tx queue, if not, we have to relay it again
|
||||
if (!findInTxQueue(p->from, p->id)) {
|
||||
reprocessPacket(p);
|
||||
if (!findInTxQueue(p->from, p->id))
|
||||
perhapsRebroadcast(p);
|
||||
}
|
||||
} else {
|
||||
perhapsCancelDupe(p);
|
||||
}
|
||||
@ -59,40 +82,6 @@ bool FloodingRouter::shouldFilterReceived(const meshtastic_MeshPacket *p)
|
||||
return Router::shouldFilterReceived(p);
|
||||
}
|
||||
|
||||
bool FloodingRouter::perhapsHandleUpgradedPacket(const meshtastic_MeshPacket *p)
|
||||
{
|
||||
// isRebroadcaster() is duplicated in perhapsRebroadcast(), but this avoids confusing log messages
|
||||
if (isRebroadcaster() && iface && p->hop_limit > 0) {
|
||||
// If we overhear a duplicate copy of the packet with more hops left than the one we are waiting to
|
||||
// rebroadcast, then remove the packet currently sitting in the TX queue and use this one instead.
|
||||
uint8_t dropThreshold = p->hop_limit; // remove queued packets that have fewer hops remaining
|
||||
if (iface->removePendingTXPacket(getFrom(p), p->id, dropThreshold)) {
|
||||
LOG_DEBUG("Processing upgraded packet 0x%08x for rebroadcast with hop limit %d (dropping queued < %d)", p->id,
|
||||
p->hop_limit, dropThreshold);
|
||||
|
||||
reprocessPacket(p);
|
||||
perhapsRebroadcast(p);
|
||||
|
||||
rxDupe++;
|
||||
// We already enqueued the improved copy, so make sure the incoming packet stops here.
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void FloodingRouter::reprocessPacket(const meshtastic_MeshPacket *p)
|
||||
{
|
||||
if (nodeDB)
|
||||
nodeDB->updateFrom(*p);
|
||||
#if !MESHTASTIC_EXCLUDE_TRACEROUTE
|
||||
if (traceRouteModule && p->which_payload_variant == meshtastic_MeshPacket_decoded_tag &&
|
||||
p->decoded.portnum == meshtastic_PortNum_TRACEROUTE_APP)
|
||||
traceRouteModule->processUpgradedPacket(*p);
|
||||
#endif
|
||||
}
|
||||
|
||||
bool FloodingRouter::roleAllowsCancelingDupe(const meshtastic_MeshPacket *p)
|
||||
{
|
||||
if (config.device.role == meshtastic_Config_DeviceConfig_Role_ROUTER ||
|
||||
@ -132,6 +121,41 @@ bool FloodingRouter::isRebroadcaster()
|
||||
config.device.rebroadcast_mode != meshtastic_Config_DeviceConfig_RebroadcastMode_NONE;
|
||||
}
|
||||
|
||||
void FloodingRouter::perhapsRebroadcast(const meshtastic_MeshPacket *p)
|
||||
{
|
||||
if (!isToUs(p) && (p->hop_limit > 0) && !isFromUs(p)) {
|
||||
if (p->id != 0) {
|
||||
if (isRebroadcaster()) {
|
||||
meshtastic_MeshPacket *tosend = packetPool.allocCopy(*p); // keep a copy because we will be sending it
|
||||
|
||||
// Use shared logic to determine if hop_limit should be decremented
|
||||
if (shouldDecrementHopLimit(p)) {
|
||||
tosend->hop_limit--; // bump down the hop count
|
||||
} else {
|
||||
LOG_INFO("favorite-ROUTER/CLIENT_BASE-to-ROUTER/CLIENT_BASE flood: preserving hop_limit");
|
||||
}
|
||||
#if USERPREFS_EVENT_MODE
|
||||
if (tosend->hop_limit > 2) {
|
||||
// if we are "correcting" the hop_limit, "correct" the hop_start by the same amount to preserve hops away.
|
||||
tosend->hop_start -= (tosend->hop_limit - 2);
|
||||
tosend->hop_limit = 2;
|
||||
}
|
||||
#endif
|
||||
|
||||
tosend->next_hop = NO_NEXT_HOP_PREFERENCE; // this should already be the case, but just in case
|
||||
|
||||
LOG_INFO("Rebroadcast received floodmsg");
|
||||
// Note: we are careful to resend using the original senders node id
|
||||
send(tosend);
|
||||
} else {
|
||||
LOG_DEBUG("No rebroadcast: Role = CLIENT_MUTE or Rebroadcast Mode = NONE");
|
||||
}
|
||||
} else {
|
||||
LOG_DEBUG("Ignore 0 id broadcast");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void FloodingRouter::sniffReceived(const meshtastic_MeshPacket *p, const meshtastic_Routing *c)
|
||||
{
|
||||
bool isAckorReply = (p->which_payload_variant == meshtastic_MeshPacket_decoded_tag) &&
|
||||
|
||||
@ -27,6 +27,10 @@
|
||||
*/
|
||||
class FloodingRouter : public Router
|
||||
{
|
||||
private:
|
||||
/* Check if we should rebroadcast this packet, and do so if needed */
|
||||
void perhapsRebroadcast(const meshtastic_MeshPacket *p);
|
||||
|
||||
public:
|
||||
/**
|
||||
* Constructor
|
||||
@ -55,17 +59,6 @@ class FloodingRouter : public Router
|
||||
*/
|
||||
virtual void sniffReceived(const meshtastic_MeshPacket *p, const meshtastic_Routing *c) override;
|
||||
|
||||
/* Check if we should rebroadcast this packet, and do so if needed */
|
||||
virtual bool perhapsRebroadcast(const meshtastic_MeshPacket *p) = 0;
|
||||
|
||||
/* Check if we should handle an upgraded packet (with higher hop_limit)
|
||||
* @return true if we handled it (so stop processing)
|
||||
*/
|
||||
bool perhapsHandleUpgradedPacket(const meshtastic_MeshPacket *p);
|
||||
|
||||
/* Call when we receive a packet that needs some reprocessing, but afterwards should be filtered */
|
||||
void reprocessPacket(const meshtastic_MeshPacket *p);
|
||||
|
||||
// Return false for roles like ROUTER which should always rebroadcast even when we've heard another rebroadcast of
|
||||
// the same packet
|
||||
bool roleAllowsCancelingDupe(const meshtastic_MeshPacket *p);
|
||||
|
||||
@ -43,8 +43,31 @@ bool NextHopRouter::shouldFilterReceived(const meshtastic_MeshPacket *p)
|
||||
&wasUpgraded); // Updates history; returns false when an upgrade is detected
|
||||
|
||||
// Handle hop_limit upgrade scenario for rebroadcasters
|
||||
if (wasUpgraded && perhapsHandleUpgradedPacket(p)) {
|
||||
return true; // we handled it, so stop processing
|
||||
// isRebroadcaster() is duplicated in perhapsRelay(), but this avoids confusing log messages
|
||||
if (wasUpgraded && isRebroadcaster() && iface && p->hop_limit > 0) {
|
||||
// Upgrade detection bypasses the duplicate short-circuit so we replace the queued packet before exiting
|
||||
uint8_t dropThreshold = p->hop_limit; // remove queued packets that have fewer hops remaining
|
||||
if (iface->removePendingTXPacket(getFrom(p), p->id, dropThreshold)) {
|
||||
LOG_DEBUG("Processing upgraded packet 0x%08x for relay with hop limit %d (dropping queued < %d)", p->id, p->hop_limit,
|
||||
dropThreshold);
|
||||
|
||||
if (nodeDB)
|
||||
nodeDB->updateFrom(*p);
|
||||
#if !MESHTASTIC_EXCLUDE_TRACEROUTE
|
||||
if (traceRouteModule && p->which_payload_variant == meshtastic_MeshPacket_decoded_tag &&
|
||||
p->decoded.portnum == meshtastic_PortNum_TRACEROUTE_APP)
|
||||
traceRouteModule->processUpgradedPacket(*p);
|
||||
#endif
|
||||
|
||||
perhapsRelay(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) {
|
||||
@ -59,20 +82,14 @@ bool NextHopRouter::shouldFilterReceived(const meshtastic_MeshPacket *p)
|
||||
if (wasFallback) {
|
||||
LOG_INFO("Fallback to flooding from relay_node=0x%x", p->relay_node);
|
||||
// Check if it's still in the Tx queue, if not, we have to relay it again
|
||||
if (!findInTxQueue(p->from, p->id)) {
|
||||
reprocessPacket(p);
|
||||
perhapsRebroadcast(p);
|
||||
}
|
||||
if (!findInTxQueue(p->from, p->id))
|
||||
perhapsRelay(p);
|
||||
} else {
|
||||
bool isRepeated = p->hop_start > 0 && p->hop_start == p->hop_limit;
|
||||
// If repeated and not in Tx queue anymore, try relaying again, or if we are the destination, send the ACK again
|
||||
if (isRepeated) {
|
||||
if (!findInTxQueue(p->from, p->id)) {
|
||||
reprocessPacket(p);
|
||||
if (!perhapsRebroadcast(p) && isToUs(p) && p->want_ack) {
|
||||
sendAckNak(meshtastic_Routing_Error_NONE, getFrom(p), p->id, p->channel, 0);
|
||||
}
|
||||
}
|
||||
if (!findInTxQueue(p->from, p->id) && !perhapsRelay(p) && isToUs(p) && p->want_ack)
|
||||
sendAckNak(meshtastic_Routing_Error_NONE, getFrom(p), p->id, p->channel, 0);
|
||||
} else if (!weWereNextHop) {
|
||||
perhapsCancelDupe(p); // If it's a dupe, cancel relay if we were not explicitly asked to relay
|
||||
}
|
||||
@ -90,14 +107,13 @@ void NextHopRouter::sniffReceived(const meshtastic_MeshPacket *p, const meshtast
|
||||
bool isAckorReply = (p->which_payload_variant == meshtastic_MeshPacket_decoded_tag) &&
|
||||
(p->decoded.request_id != 0 || p->decoded.reply_id != 0);
|
||||
if (isAckorReply) {
|
||||
// Update next-hop for the original transmitter of this successful transmission to the relay node, but ONLY if "from"
|
||||
// is not 0 (means implicit ACK) and original packet was also relayed by this node, or we sent it directly to the
|
||||
// destination
|
||||
// Update next-hop for the original transmitter of this successful transmission to the relay node, but ONLY if "from" is
|
||||
// not 0 (means implicit ACK) and original packet was also relayed by this node, or we sent it directly to the destination
|
||||
if (p->from != 0) {
|
||||
meshtastic_NodeInfoLite *origTx = nodeDB->getMeshNode(p->from);
|
||||
if (origTx) {
|
||||
// Either relayer of ACK was also a relayer of the packet, or we were the *only* relayer and the ACK came
|
||||
// directly from the destination
|
||||
// Either relayer of ACK was also a relayer of the packet, or we were the *only* relayer and the ACK came directly
|
||||
// from the destination
|
||||
bool wasAlreadyRelayer = wasRelayer(p->relay_node, p->decoded.request_id, p->to);
|
||||
bool weWereSoleRelayer = false;
|
||||
bool weWereRelayer = wasRelayer(ourRelayID, p->decoded.request_id, p->to, &weWereSoleRelayer);
|
||||
@ -118,49 +134,34 @@ void NextHopRouter::sniffReceived(const meshtastic_MeshPacket *p, const meshtast
|
||||
}
|
||||
}
|
||||
|
||||
perhapsRebroadcast(p);
|
||||
perhapsRelay(p);
|
||||
|
||||
// handle the packet as normal
|
||||
Router::sniffReceived(p, c);
|
||||
}
|
||||
|
||||
/* Check if we should be rebroadcasting this packet if so, do so. */
|
||||
bool NextHopRouter::perhapsRebroadcast(const meshtastic_MeshPacket *p)
|
||||
/* Check if we should be relaying this packet if so, do so. */
|
||||
bool NextHopRouter::perhapsRelay(const meshtastic_MeshPacket *p)
|
||||
{
|
||||
if (!isToUs(p) && !isFromUs(p) && p->hop_limit > 0) {
|
||||
if (p->id != 0) {
|
||||
if (p->next_hop == NO_NEXT_HOP_PREFERENCE || p->next_hop == nodeDB->getLastByteOfNodeNum(getNodeNum())) {
|
||||
if (isRebroadcaster()) {
|
||||
if (p->next_hop == NO_NEXT_HOP_PREFERENCE || p->next_hop == nodeDB->getLastByteOfNodeNum(getNodeNum())) {
|
||||
meshtastic_MeshPacket *tosend = packetPool.allocCopy(*p); // keep a copy because we will be sending it
|
||||
LOG_INFO("Rebroadcast received message coming from %x", p->relay_node);
|
||||
meshtastic_MeshPacket *tosend = packetPool.allocCopy(*p); // keep a copy because we will be sending it
|
||||
LOG_INFO("Relaying received message coming from %x", p->relay_node);
|
||||
|
||||
// Use shared logic to determine if hop_limit should be decremented
|
||||
if (shouldDecrementHopLimit(p)) {
|
||||
tosend->hop_limit--; // bump down the hop count
|
||||
} else {
|
||||
LOG_INFO("favorite-ROUTER/CLIENT_BASE-to-ROUTER/CLIENT_BASE rebroadcast: preserving hop_limit");
|
||||
}
|
||||
#if USERPREFS_EVENT_MODE
|
||||
if (tosend->hop_limit > 2) {
|
||||
// if we are "correcting" the hop_limit, "correct" the hop_start by the same amount to preserve hops away.
|
||||
tosend->hop_start -= (tosend->hop_limit - 2);
|
||||
tosend->hop_limit = 2;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (p->next_hop == NO_NEXT_HOP_PREFERENCE) {
|
||||
FloodingRouter::send(tosend);
|
||||
} else {
|
||||
NextHopRouter::send(tosend);
|
||||
}
|
||||
|
||||
return true;
|
||||
// Use shared logic to determine if hop_limit should be decremented
|
||||
if (shouldDecrementHopLimit(p)) {
|
||||
tosend->hop_limit--; // bump down the hop count
|
||||
} else {
|
||||
LOG_INFO("Router/CLIENT_BASE-to-favorite-router/CLIENT_BASE relay: preserving hop_limit");
|
||||
}
|
||||
|
||||
NextHopRouter::send(tosend);
|
||||
|
||||
return true;
|
||||
} else {
|
||||
LOG_DEBUG("No rebroadcast: Role = CLIENT_MUTE or Rebroadcast Mode = NONE");
|
||||
LOG_DEBUG("Not rebroadcasting: Role = CLIENT_MUTE or Rebroadcast Mode = NONE");
|
||||
}
|
||||
} else {
|
||||
LOG_DEBUG("Ignore 0 id broadcast");
|
||||
}
|
||||
}
|
||||
|
||||
@ -230,13 +231,13 @@ bool NextHopRouter::stopRetransmission(GlobalPacketId key)
|
||||
}
|
||||
}
|
||||
|
||||
// Regardless of whether or not we canceled this packet from the txQueue, remove it from our pending list so it
|
||||
// doesn't get scheduled again. (This is the core of stopRetransmission.)
|
||||
// Regardless of whether or not we canceled this packet from the txQueue, remove it from our pending list so it doesn't
|
||||
// get scheduled again. (This is the core of stopRetransmission.)
|
||||
auto numErased = pending.erase(key);
|
||||
assert(numErased == 1);
|
||||
|
||||
// When we remove an entry from pending, always be sure to release the copy of the packet that was allocated in the
|
||||
// call to startRetransmission.
|
||||
// When we remove an entry from pending, always be sure to release the copy of the packet that was allocated in the call
|
||||
// to startRetransmission.
|
||||
packetPool.release(p);
|
||||
|
||||
return true;
|
||||
|
||||
@ -148,7 +148,7 @@ class NextHopRouter : public FloodingRouter
|
||||
*/
|
||||
uint8_t getNextHop(NodeNum to, uint8_t relay_node);
|
||||
|
||||
/** Check if we should be rebroadcasting this packet if so, do so.
|
||||
* @return true if we did rebroadcast */
|
||||
bool perhapsRebroadcast(const meshtastic_MeshPacket *p) override;
|
||||
/** Check if we should be relaying this packet if so, do so.
|
||||
* @return true if we did relay */
|
||||
bool perhapsRelay(const meshtastic_MeshPacket *p);
|
||||
};
|
||||
@ -1874,6 +1874,13 @@ uint8_t NodeDB::getMeshNodeChannel(NodeNum n)
|
||||
return info->channel;
|
||||
}
|
||||
|
||||
std::string NodeDB::getNodeId() const
|
||||
{
|
||||
char nodeId[16];
|
||||
snprintf(nodeId, sizeof(nodeId), "!%08x", myNodeInfo.my_node_num);
|
||||
return std::string(nodeId);
|
||||
}
|
||||
|
||||
/// Find a node in our DB, return null for missing
|
||||
/// NOTE: This function might be called from an ISR
|
||||
meshtastic_NodeInfoLite *NodeDB::getMeshNode(NodeNum n)
|
||||
|
||||
@ -5,6 +5,7 @@
|
||||
#include <algorithm>
|
||||
#include <assert.h>
|
||||
#include <pb_encode.h>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "MeshTypes.h"
|
||||
@ -203,6 +204,9 @@ class NodeDB
|
||||
/// @return our node number
|
||||
NodeNum getNodeNum() { return myNodeInfo.my_node_num; }
|
||||
|
||||
/// @return our node ID as a string in the format "!xxxxxxxx"
|
||||
std::string getNodeId() const;
|
||||
|
||||
// @return last byte of a NodeNum, 0xFF if it ended at 0x00
|
||||
uint8_t getLastByteOfNodeNum(NodeNum num) { return (uint8_t)((num & 0xFF) ? (num & 0xFF) : 0xFF); }
|
||||
|
||||
|
||||
@ -94,6 +94,7 @@ bool PacketHistory::wasSeenRecently(const meshtastic_MeshPacket *p, bool withUpd
|
||||
LOG_DEBUG("Packet History - Hop limit upgrade: packet 0x%08x from hop_limit=%d to hop_limit=%d", p->id, found->hop_limit,
|
||||
p->hop_limit);
|
||||
*wasUpgraded = true;
|
||||
seenRecently = false; // Allow router processing but prevent duplicate app delivery
|
||||
} else if (wasUpgraded) {
|
||||
*wasUpgraded = false; // Initialize to false if not an upgrade
|
||||
}
|
||||
|
||||
@ -433,6 +433,8 @@ size_t PhoneAPI::getFromRadio(uint8_t *buf)
|
||||
case STATE_SEND_OTHER_NODEINFOS: {
|
||||
LOG_DEBUG("Send known nodes");
|
||||
if (nodeInfoForPhone.num != 0) {
|
||||
// Just in case we stored a different user.id in the past, but should never happen going forward
|
||||
sprintf(nodeInfoForPhone.user.id, "!%08x", nodeInfoForPhone.num);
|
||||
LOG_INFO("nodeinfo: num=0x%x, lastseen=%u, id=%s, name=%s", nodeInfoForPhone.num, nodeInfoForPhone.last_heard,
|
||||
nodeInfoForPhone.user.id, nodeInfoForPhone.user.long_name);
|
||||
fromRadioScratch.which_payload_variant = meshtastic_FromRadio_node_info_tag;
|
||||
|
||||
@ -148,8 +148,6 @@ void registerHandlers(HTTPServer *insecureServer, HTTPSServer *secureServer)
|
||||
|
||||
void handleAPIv1FromRadio(HTTPRequest *req, HTTPResponse *res)
|
||||
{
|
||||
if (webServerThread)
|
||||
webServerThread->markActivity();
|
||||
|
||||
LOG_DEBUG("webAPI handleAPIv1FromRadio");
|
||||
|
||||
@ -393,9 +391,6 @@ void handleFsDeleteStatic(HTTPRequest *req, HTTPResponse *res)
|
||||
|
||||
void handleStatic(HTTPRequest *req, HTTPResponse *res)
|
||||
{
|
||||
if (webServerThread)
|
||||
webServerThread->markActivity();
|
||||
|
||||
// Get access to the parameters
|
||||
ResourceParameters *params = req->getParams();
|
||||
|
||||
|
||||
@ -49,12 +49,6 @@ Preferences prefs;
|
||||
using namespace httpsserver;
|
||||
#include "mesh/http/ContentHandler.h"
|
||||
|
||||
static const uint32_t ACTIVE_THRESHOLD_MS = 5000;
|
||||
static const uint32_t MEDIUM_THRESHOLD_MS = 30000;
|
||||
static const int32_t ACTIVE_INTERVAL_MS = 50;
|
||||
static const int32_t MEDIUM_INTERVAL_MS = 200;
|
||||
static const int32_t IDLE_INTERVAL_MS = 1000;
|
||||
|
||||
static SSLCert *cert;
|
||||
static HTTPSServer *secureServer;
|
||||
static HTTPServer *insecureServer;
|
||||
@ -181,32 +175,6 @@ WebServerThread::WebServerThread() : concurrency::OSThread("WebServer")
|
||||
if (!config.network.wifi_enabled && !config.network.eth_enabled) {
|
||||
disable();
|
||||
}
|
||||
lastActivityTime = millis();
|
||||
}
|
||||
|
||||
void WebServerThread::markActivity()
|
||||
{
|
||||
lastActivityTime = millis();
|
||||
}
|
||||
|
||||
int32_t WebServerThread::getAdaptiveInterval()
|
||||
{
|
||||
uint32_t currentTime = millis();
|
||||
uint32_t timeSinceActivity;
|
||||
|
||||
if (currentTime >= lastActivityTime) {
|
||||
timeSinceActivity = currentTime - lastActivityTime;
|
||||
} else {
|
||||
timeSinceActivity = (UINT32_MAX - lastActivityTime) + currentTime + 1;
|
||||
}
|
||||
|
||||
if (timeSinceActivity < ACTIVE_THRESHOLD_MS) {
|
||||
return ACTIVE_INTERVAL_MS;
|
||||
} else if (timeSinceActivity < MEDIUM_THRESHOLD_MS) {
|
||||
return MEDIUM_INTERVAL_MS;
|
||||
} else {
|
||||
return IDLE_INTERVAL_MS;
|
||||
}
|
||||
}
|
||||
|
||||
int32_t WebServerThread::runOnce()
|
||||
@ -221,7 +189,8 @@ int32_t WebServerThread::runOnce()
|
||||
ESP.restart();
|
||||
}
|
||||
|
||||
return getAdaptiveInterval();
|
||||
// Loop every 5ms.
|
||||
return (5);
|
||||
}
|
||||
|
||||
void initWebServer()
|
||||
|
||||
@ -10,17 +10,13 @@ void createSSLCert();
|
||||
|
||||
class WebServerThread : private concurrency::OSThread
|
||||
{
|
||||
private:
|
||||
uint32_t lastActivityTime = 0;
|
||||
|
||||
public:
|
||||
WebServerThread();
|
||||
uint32_t requestRestart = 0;
|
||||
void markActivity();
|
||||
|
||||
protected:
|
||||
virtual int32_t runOnce() override;
|
||||
int32_t getAdaptiveInterval();
|
||||
};
|
||||
|
||||
extern WebServerThread *webServerThread;
|
||||
|
||||
@ -94,11 +94,11 @@ static void onNetworkConnected()
|
||||
// ESPmDNS (ESP32) and SimpleMDNS (RP2040) have slightly different APIs for adding TXT records
|
||||
#ifdef ARCH_ESP32
|
||||
MDNS.addServiceTxt("meshtastic", "tcp", "shortname", String(owner.short_name));
|
||||
MDNS.addServiceTxt("meshtastic", "tcp", "id", String(owner.id));
|
||||
MDNS.addServiceTxt("meshtastic", "tcp", "id", String(nodeDB->getNodeId().c_str()));
|
||||
// ESP32 prints obtained IP address in WiFiEvent
|
||||
#elif defined(ARCH_RP2040)
|
||||
MDNS.addServiceTxt("meshtastic", "shortname", owner.short_name);
|
||||
MDNS.addServiceTxt("meshtastic", "id", owner.id);
|
||||
MDNS.addServiceTxt("meshtastic", "id", nodeDB->getNodeId().c_str());
|
||||
LOG_INFO("Obtained IP address: %s", WiFi.localIP().toString().c_str());
|
||||
#endif
|
||||
}
|
||||
|
||||
@ -113,8 +113,12 @@ meshtastic_MeshPacket *NodeInfoModule::allocReply()
|
||||
u.public_key.size = 0;
|
||||
}
|
||||
|
||||
// Clear the user.id field since it should be derived from node number on the receiving end
|
||||
u.id[0] = '\0';
|
||||
// FIXME: Clear the user.id field since it should be derived from node number on the receiving end
|
||||
// u.id[0] = '\0';
|
||||
|
||||
// Ensure our user.id is derived correctly
|
||||
strcpy(u.id, nodeDB->getNodeId().c_str());
|
||||
|
||||
LOG_INFO("Send owner %s/%s/%s", u.id, u.long_name, u.short_name);
|
||||
lastSentToMesh = millis();
|
||||
return allocDataProtobuf(u);
|
||||
|
||||
@ -67,7 +67,7 @@ SerialModuleRadio *serialModuleRadio;
|
||||
defined(ELECROW_ThinkNode_M5) || defined(HELTEC_MESH_SOLAR) || defined(T_ECHO_LITE)
|
||||
SerialModule::SerialModule() : StreamAPI(&Serial), concurrency::OSThread("Serial") {}
|
||||
static Print *serialPrint = &Serial;
|
||||
#elif defined(CONFIG_IDF_TARGET_ESP32C6) || defined(RAK3172)
|
||||
#elif defined(CONFIG_IDF_TARGET_ESP32C6) || defined(RAK3172) || defined(EBYTE_E77_MBL)
|
||||
SerialModule::SerialModule() : StreamAPI(&Serial1), concurrency::OSThread("Serial") {}
|
||||
static Print *serialPrint = &Serial1;
|
||||
#else
|
||||
|
||||
@ -21,11 +21,6 @@ void TraceRouteModule::alterReceivedProtobuf(meshtastic_MeshPacket &p, meshtasti
|
||||
{
|
||||
const meshtastic_Data &incoming = p.decoded;
|
||||
|
||||
// Update next-hops using returned route
|
||||
if (incoming.request_id) {
|
||||
updateNextHops(p, r);
|
||||
}
|
||||
|
||||
// Insert unknown hops if necessary
|
||||
insertUnknownHops(p, r, !incoming.request_id);
|
||||
|
||||
@ -158,65 +153,6 @@ void TraceRouteModule::alterReceivedProtobuf(meshtastic_MeshPacket &p, meshtasti
|
||||
}
|
||||
}
|
||||
|
||||
void TraceRouteModule::updateNextHops(meshtastic_MeshPacket &p, meshtastic_RouteDiscovery *r)
|
||||
{
|
||||
// E.g. if the route is A->B->C->D and we are B, we can set C as next-hop for C and D
|
||||
// Similarly, if we are C, we can set D as next-hop for D
|
||||
// If we are A, we can set B as next-hop for B, C and D
|
||||
|
||||
// First check if we were the original sender or in the original route
|
||||
int8_t nextHopIndex = -1;
|
||||
if (isToUs(&p)) {
|
||||
nextHopIndex = 0; // We are the original sender, next hop is first in route
|
||||
} else {
|
||||
// Check if we are in the original route
|
||||
for (uint8_t i = 0; i < r->route_count; i++) {
|
||||
if (r->route[i] == nodeDB->getNodeNum()) {
|
||||
nextHopIndex = i + 1; // Next hop is the one after us
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If we are in the original route, update the next hops
|
||||
if (nextHopIndex != -1) {
|
||||
// For every node after us, we can set the next-hop to the first node after us
|
||||
NodeNum nextHop;
|
||||
if (nextHopIndex == r->route_count) {
|
||||
nextHop = p.from; // We are the last in the route, next hop is destination
|
||||
} else {
|
||||
nextHop = r->route[nextHopIndex];
|
||||
}
|
||||
|
||||
if (nextHop == NODENUM_BROADCAST) {
|
||||
return;
|
||||
}
|
||||
uint8_t nextHopByte = nodeDB->getLastByteOfNodeNum(nextHop);
|
||||
|
||||
// For the rest of the nodes in the route, set their next-hop
|
||||
// Note: if we are the last in the route, this loop will not run
|
||||
for (int8_t i = nextHopIndex; i < r->route_count; i++) {
|
||||
NodeNum targetNode = r->route[i];
|
||||
maybeSetNextHop(targetNode, nextHopByte);
|
||||
}
|
||||
|
||||
// Also set next-hop for the destination node
|
||||
maybeSetNextHop(p.from, nextHopByte);
|
||||
}
|
||||
}
|
||||
|
||||
void TraceRouteModule::maybeSetNextHop(NodeNum target, uint8_t nextHopByte)
|
||||
{
|
||||
if (target == NODENUM_BROADCAST)
|
||||
return;
|
||||
|
||||
meshtastic_NodeInfoLite *node = nodeDB->getMeshNode(target);
|
||||
if (node && node->next_hop != nextHopByte) {
|
||||
LOG_INFO("Updating next-hop for 0x%08x to 0x%02x based on traceroute", target, nextHopByte);
|
||||
node->next_hop = nextHopByte;
|
||||
}
|
||||
}
|
||||
|
||||
void TraceRouteModule::processUpgradedPacket(const meshtastic_MeshPacket &mp)
|
||||
{
|
||||
if (mp.which_payload_variant != meshtastic_MeshPacket_decoded_tag || mp.decoded.portnum != meshtastic_PortNum_TRACEROUTE_APP)
|
||||
|
||||
@ -55,12 +55,6 @@ class TraceRouteModule : public ProtobufModule<meshtastic_RouteDiscovery>,
|
||||
// Call to add your ID to the route array of a RouteDiscovery message
|
||||
void appendMyIDandSNR(meshtastic_RouteDiscovery *r, float snr, bool isTowardsDestination, bool SNRonly);
|
||||
|
||||
// Update next-hops in the routing table based on the returned route
|
||||
void updateNextHops(meshtastic_MeshPacket &p, meshtastic_RouteDiscovery *r);
|
||||
|
||||
// Helper to update next-hop for a single node
|
||||
void maybeSetNextHop(NodeNum target, uint8_t nextHopByte);
|
||||
|
||||
/* Call to print the route array of a RouteDiscovery message.
|
||||
Set origin to where the request came from.
|
||||
Set dest to the ID of its destination, or NODENUM_BROADCAST if it has not yet arrived there. */
|
||||
|
||||
@ -60,7 +60,9 @@ inline void onReceiveProto(char *topic, byte *payload, size_t length)
|
||||
return;
|
||||
}
|
||||
const meshtastic_Channel &ch = channels.getByName(e.channel_id);
|
||||
if (strcmp(e.gateway_id, owner.id) == 0) {
|
||||
// Generate node ID from nodenum for comparison
|
||||
std::string nodeId = nodeDB->getNodeId();
|
||||
if (strcmp(e.gateway_id, nodeId.c_str()) == 0) {
|
||||
// Generate an implicit ACK towards ourselves (handled and processed only locally!) for this message.
|
||||
// We do this because packets are not rebroadcasted back into MQTT anymore and we assume that at least one node
|
||||
// receives it when we get our own packet back. Then we'll stop our retransmissions.
|
||||
@ -128,8 +130,10 @@ inline void onReceiveProto(char *topic, byte *payload, size_t length)
|
||||
// returns true if this is a valid JSON envelope which we accept on downlink
|
||||
inline bool isValidJsonEnvelope(JSONObject &json)
|
||||
{
|
||||
// Generate node ID from nodenum for comparison
|
||||
std::string nodeId = nodeDB->getNodeId();
|
||||
// if "sender" is provided, avoid processing packets we uplinked
|
||||
return (json.find("sender") != json.end() ? (json["sender"]->AsString().compare(owner.id) != 0) : true) &&
|
||||
return (json.find("sender") != json.end() ? (json["sender"]->AsString().compare(nodeId) != 0) : true) &&
|
||||
(json.find("hopLimit") != json.end() ? json["hopLimit"]->IsNumber() : true) && // hop limit should be a number
|
||||
(json.find("from") != json.end()) && json["from"]->IsNumber() &&
|
||||
(json["from"]->AsNumber() == nodeDB->getNodeNum()) && // only accept message if the "from" is us
|
||||
@ -297,7 +301,9 @@ bool connectPubSub(const PubSubConfig &config, PubSubClient &pubSub, Client &cli
|
||||
LOG_INFO("Connecting directly to MQTT server %s, port: %d, username: %s, password: %s", config.serverAddr.c_str(),
|
||||
config.serverPort, config.mqttUsername, config.mqttPassword);
|
||||
|
||||
const bool connected = pubSub.connect(owner.id, config.mqttUsername, config.mqttPassword);
|
||||
// Generate node ID from nodenum for client identification
|
||||
std::string nodeId = nodeDB->getNodeId();
|
||||
const bool connected = pubSub.connect(nodeId.c_str(), config.mqttUsername, config.mqttPassword);
|
||||
if (connected) {
|
||||
LOG_INFO("MQTT connected");
|
||||
} else {
|
||||
@ -687,11 +693,14 @@ void MQTT::publishQueuedMessages()
|
||||
if (jsonString.length() == 0)
|
||||
return;
|
||||
|
||||
// Generate node ID from nodenum for topic
|
||||
std::string nodeId = nodeDB->getNodeId();
|
||||
|
||||
std::string topicJson;
|
||||
if (env.packet->pki_encrypted) {
|
||||
topicJson = jsonTopic + "PKI/" + owner.id;
|
||||
topicJson = jsonTopic + "PKI/" + nodeId;
|
||||
} else {
|
||||
topicJson = jsonTopic + env.channel_id + "/" + owner.id;
|
||||
topicJson = jsonTopic + env.channel_id + "/" + nodeId;
|
||||
}
|
||||
LOG_INFO("JSON publish message to %s, %u bytes: %s", topicJson.c_str(), jsonString.length(), jsonString.c_str());
|
||||
publish(topicJson.c_str(), jsonString.c_str(), false);
|
||||
@ -749,10 +758,14 @@ void MQTT::onSend(const meshtastic_MeshPacket &mp_encrypted, const meshtastic_Me
|
||||
return; // Don't upload a still-encrypted PKI packet if not encryption_enabled
|
||||
}
|
||||
|
||||
const meshtastic_ServiceEnvelope env = {
|
||||
.packet = const_cast<meshtastic_MeshPacket *>(p), .channel_id = const_cast<char *>(channelId), .gateway_id = owner.id};
|
||||
// Generate node ID from nodenum for service envelope
|
||||
std::string nodeId = nodeDB->getNodeId();
|
||||
|
||||
const meshtastic_ServiceEnvelope env = {.packet = const_cast<meshtastic_MeshPacket *>(p),
|
||||
.channel_id = const_cast<char *>(channelId),
|
||||
.gateway_id = const_cast<char *>(nodeId.c_str())};
|
||||
size_t numBytes = pb_encode_to_bytes(bytes, sizeof(bytes), &meshtastic_ServiceEnvelope_msg, &env);
|
||||
std::string topic = cryptTopic + channelId + "/" + owner.id;
|
||||
std::string topic = cryptTopic + channelId + "/" + nodeId;
|
||||
|
||||
if (moduleConfig.mqtt.proxy_to_client_enabled || this->isConnectedDirectly()) {
|
||||
LOG_DEBUG("MQTT Publish %s, %u bytes", topic.c_str(), numBytes);
|
||||
@ -766,7 +779,9 @@ void MQTT::onSend(const meshtastic_MeshPacket &mp_encrypted, const meshtastic_Me
|
||||
auto jsonString = MeshPacketSerializer::JsonSerialize(&mp_decoded);
|
||||
if (jsonString.length() == 0)
|
||||
return;
|
||||
std::string topicJson = jsonTopic + channelId + "/" + owner.id;
|
||||
// Generate node ID from nodenum for JSON topic
|
||||
std::string nodeIdForJson = nodeDB->getNodeId();
|
||||
std::string topicJson = jsonTopic + channelId + "/" + nodeIdForJson;
|
||||
LOG_INFO("JSON publish message to %s, %u bytes: %s", topicJson.c_str(), jsonString.length(), jsonString.c_str());
|
||||
publish(topicJson.c_str(), jsonString.c_str(), false);
|
||||
#endif // ARCH_NRF52 NRF52_USE_JSON
|
||||
@ -845,11 +860,14 @@ void MQTT::perhapsReportToMap()
|
||||
mp->decoded.payload.size =
|
||||
pb_encode_to_bytes(mp->decoded.payload.bytes, sizeof(mp->decoded.payload.bytes), &meshtastic_MapReport_msg, &mapReport);
|
||||
|
||||
// Generate node ID from nodenum for service envelope
|
||||
std::string nodeId = nodeDB->getNodeId();
|
||||
|
||||
// Encode the MeshPacket into a binary ServiceEnvelope and publish
|
||||
const meshtastic_ServiceEnvelope se = {
|
||||
.packet = mp,
|
||||
.channel_id = (char *)channels.getGlobalId(channels.getPrimaryIndex()), // Use primary channel as the channel_id
|
||||
.gateway_id = owner.id};
|
||||
.gateway_id = const_cast<char *>(nodeId.c_str())};
|
||||
size_t numBytes = pb_encode_to_bytes(bytes, sizeof(bytes), &meshtastic_ServiceEnvelope_msg, &se);
|
||||
|
||||
LOG_INFO("MQTT Publish map report to %s", mapTopic.c_str());
|
||||
|
||||
@ -138,7 +138,6 @@ class Power : private concurrency::OSThread
|
||||
void reboot();
|
||||
// open circuit voltage lookup table
|
||||
uint8_t low_voltage_counter;
|
||||
int32_t lastLogTime = 0;
|
||||
#ifdef DEBUG_HEAP
|
||||
uint32_t lastheap;
|
||||
#endif
|
||||
|
||||
@ -413,7 +413,7 @@ std::string MeshPacketSerializer::JsonSerialize(const meshtastic_MeshPacket *mp,
|
||||
jsonObj["from"] = new JSONValue((unsigned int)mp->from);
|
||||
jsonObj["channel"] = new JSONValue((unsigned int)mp->channel);
|
||||
jsonObj["type"] = new JSONValue(msgType.c_str());
|
||||
jsonObj["sender"] = new JSONValue(owner.id);
|
||||
jsonObj["sender"] = new JSONValue(nodeDB->getNodeId().c_str());
|
||||
if (mp->rx_rssi != 0)
|
||||
jsonObj["rssi"] = new JSONValue((int)mp->rx_rssi);
|
||||
if (mp->rx_snr != 0)
|
||||
|
||||
@ -353,7 +353,7 @@ std::string MeshPacketSerializer::JsonSerialize(const meshtastic_MeshPacket *mp,
|
||||
jsonObj["from"] = (unsigned int)mp->from;
|
||||
jsonObj["channel"] = (unsigned int)mp->channel;
|
||||
jsonObj["type"] = msgType.c_str();
|
||||
jsonObj["sender"] = owner.id;
|
||||
jsonObj["sender"] = nodeDB->getNodeId().c_str();
|
||||
if (mp->rx_rssi != 0)
|
||||
jsonObj["rssi"] = (int)mp->rx_rssi;
|
||||
if (mp->rx_snr != 0)
|
||||
|
||||
@ -332,7 +332,7 @@ void setUp(void)
|
||||
};
|
||||
channelFile.channels_count = 1;
|
||||
owner = meshtastic_User{.id = "!12345678"};
|
||||
myNodeInfo = meshtastic_MyNodeInfo{.my_node_num = 10};
|
||||
myNodeInfo = meshtastic_MyNodeInfo{.my_node_num = 0x12345678}; // Match the expected gateway ID in topic
|
||||
localPosition =
|
||||
meshtastic_Position{.has_latitude_i = true, .latitude_i = 7 * 1e7, .has_longitude_i = true, .longitude_i = 3 * 1e7};
|
||||
|
||||
@ -591,7 +591,7 @@ void test_receiveEncryptedPKITopicToUs(void)
|
||||
// Should ignore messages published to MQTT by this gateway.
|
||||
void test_receiveIgnoresOwnPublishedMessages(void)
|
||||
{
|
||||
unitTest->publish(&decoded, owner.id);
|
||||
unitTest->publish(&decoded, nodeDB->getNodeId().c_str());
|
||||
|
||||
TEST_ASSERT_TRUE(mockRouter->packets_.empty());
|
||||
TEST_ASSERT_TRUE(mockRoutingModule->ackNacks_.empty());
|
||||
@ -603,7 +603,7 @@ void test_receiveAcksOwnSentMessages(void)
|
||||
meshtastic_MeshPacket p = decoded;
|
||||
p.from = myNodeInfo.my_node_num;
|
||||
|
||||
unitTest->publish(&p, owner.id);
|
||||
unitTest->publish(&p, nodeDB->getNodeId().c_str());
|
||||
|
||||
TEST_ASSERT_TRUE(mockRouter->packets_.empty());
|
||||
TEST_ASSERT_EQUAL(1, mockRoutingModule->ackNacks_.size());
|
||||
|
||||
@ -107,3 +107,7 @@ build_src_filter = ${env:native-tft.build_src_filter}
|
||||
[env:coverage]
|
||||
extends = env:native
|
||||
build_flags = -lgcov --coverage -fprofile-abs-path -fsanitize=address ${env:native.build_flags}
|
||||
; https://docs.platformio.org/en/latest/projectconf/sections/env/options/test/test_testing_command.html
|
||||
test_testing_command =
|
||||
${platformio.build_dir}/${this.__env__}/program
|
||||
-s
|
||||
|
||||
@ -6,9 +6,12 @@ board_level = extra
|
||||
build_flags =
|
||||
${stm32_base.build_flags}
|
||||
-Ivariants/stm32/CDEBYTE_E77-MBL
|
||||
-DSERIAL_UART_INSTANCE=1
|
||||
-DSERIAL_UART_INSTANCE=2
|
||||
-DPIN_SERIAL_RX=PA3
|
||||
-DPIN_SERIAL_TX=PA2
|
||||
-DENABLE_HWSERIAL1
|
||||
-DPIN_SERIAL1_RX=PB7
|
||||
-DPIN_SERIAL1_TX=PB6
|
||||
-DMESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR=1
|
||||
-DMESHTASTIC_EXCLUDE_I2C=1
|
||||
-DMESHTASTIC_EXCLUDE_GPS=1
|
||||
|
||||
@ -18,4 +18,6 @@ Do not expect a working Meshtastic device with this target.
|
||||
#define LED_PIN PB4 // LED1
|
||||
// #define LED_PIN PB3 // LED2
|
||||
#define LED_STATE_ON 1
|
||||
|
||||
#define EBYTE_E77_MBL
|
||||
#endif
|
||||
|
||||
@ -6,7 +6,6 @@ board_upload.maximum_size = 233472 ; reserve the last 28KB for filesystem
|
||||
build_flags =
|
||||
${stm32_base.build_flags}
|
||||
-Ivariants/stm32/rak3172
|
||||
-DRAK3172
|
||||
-DENABLE_HWSERIAL1
|
||||
-DPIN_SERIAL1_RX=PB7
|
||||
-DPIN_SERIAL1_TX=PB6
|
||||
|
||||
@ -16,4 +16,6 @@ Do not expect a working Meshtastic device with this target.
|
||||
#define LED_PIN PA0 // Green LED
|
||||
#define LED_STATE_ON 1
|
||||
|
||||
#define RAK3172
|
||||
|
||||
#endif
|
||||
|
||||
Loading…
Reference in New Issue
Block a user