diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 000000000..616c16ce2 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,26 @@ +version: 2 +updates: + - package-ecosystem: docker + directory: devcontainer + schedule: + interval: daily + time: "05:00" # trunk-ignore(yamllint/quoted-strings): required by dependabot syntax check + timezone: US/Pacific + - package-ecosystem: docker + directory: / + schedule: + interval: daily + time: "05:00" # trunk-ignore(yamllint/quoted-strings): required by dependabot syntax check + timezone: US/Pacific + - package-ecosystem: gitsubmodule + directory: / + schedule: + interval: daily + time: "05:00" # trunk-ignore(yamllint/quoted-strings): required by dependabot syntax check + timezone: US/Pacific + - package-ecosystem: github-actions + directory: /.github/workflows + schedule: + interval: daily + time: "05:00" # trunk-ignore(yamllint/quoted-strings): required by dependabot syntax check + timezone: US/Pacific diff --git a/.github/workflows/build_native.yml b/.github/workflows/build_native.yml index 51bef0c13..1fb44a717 100644 --- a/.github/workflows/build_native.yml +++ b/.github/workflows/build_native.yml @@ -67,7 +67,7 @@ jobs: - name: Docker build and push tagged versions if: ${{ github.event_name == 'workflow_dispatch' }} continue-on-error: true # FIXME: Failing docker login auth - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: . file: ./Dockerfile @@ -77,7 +77,7 @@ jobs: - name: Docker build and push if: ${{ github.ref == 'refs/heads/master' && github.event_name != 'pull_request_target' && github.event_name != 'pull_request' }} continue-on-error: true # FIXME: Failing docker login auth - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: . file: ./Dockerfile diff --git a/.github/workflows/main_matrix.yml b/.github/workflows/main_matrix.yml index 9b97dcb2e..19a92d8b8 100644 --- a/.github/workflows/main_matrix.yml +++ b/.github/workflows/main_matrix.yml @@ -251,12 +251,6 @@ jobs: merge-multiple: true path: ./output - - uses: actions/download-artifact@v4 - with: - pattern: meshtasticd_${{ steps.version.outputs.version }}_*.deb - merge-multiple: true - path: ./output - - name: Display structure of downloaded files run: ls -R @@ -314,6 +308,12 @@ jobs: asset_name: debug-elfs-${{ steps.version.outputs.version }}.zip asset_content_type: application/zip + - uses: actions/download-artifact@v4 + with: + pattern: meshtasticd_${{ steps.version.outputs.version }}_*.deb + merge-multiple: true + path: ./output + - name: Add raspbian aarch64 .deb uses: actions/upload-release-asset@v1 env: @@ -349,7 +349,35 @@ jobs: bin/bump_version.py - name: Create version.properties pull request - uses: peter-evans/create-pull-request@v6 + uses: peter-evans/create-pull-request@v7 with: + title: Bump version.properties add-paths: | version.properties + + - name: Checkout meshtastic/meshtastic.github.io + uses: actions/checkout@v4 + with: + repository: meshtastic/meshtastic.github.io + token: ${{ secrets.ARTIFACTS_TOKEN }} + path: meshtastic.github.io + + - name: Display structure of downloaded files + run: ls -R + + - name: Extract firmware.zip + run: | + unzip ./firmware-${{ steps.version.outputs.version }}.zip -d meshtastic.github.io/firmware-${{ steps.version.outputs.version }} + + - name: Display structure of downloaded files + run: ls -R + + - name: Commit and push changes + run: | + cd meshtastic.github.io + find . -type f -name 'meshtasticd_*' -exec rm -f {} + + git config --global user.name "github-actions[bot]" + git config --global user.email "github-actions[bot]@users.noreply.github.com" + git add . + git commit -m "Add firmware version ${{ steps.version.outputs.version }}" + git push diff --git a/.github/workflows/package_amd64.yml b/.github/workflows/package_amd64.yml index ae7bf3242..0b5093f24 100644 --- a/.github/workflows/package_amd64.yml +++ b/.github/workflows/package_amd64.yml @@ -13,7 +13,7 @@ jobs: uses: ./.github/workflows/build_native.yml package-native: - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 needs: build-native steps: - name: Checkout code diff --git a/.github/workflows/package_raspbian.yml b/.github/workflows/package_raspbian.yml index 5471332c5..bcbda53e2 100644 --- a/.github/workflows/package_raspbian.yml +++ b/.github/workflows/package_raspbian.yml @@ -13,7 +13,7 @@ jobs: uses: ./.github/workflows/build_raspbian.yml package-raspbian: - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 needs: build-raspbian steps: - name: Checkout code diff --git a/.github/workflows/package_raspbian_armv7l.yml b/.github/workflows/package_raspbian_armv7l.yml index 5b9c9aa71..1308fe925 100644 --- a/.github/workflows/package_raspbian_armv7l.yml +++ b/.github/workflows/package_raspbian_armv7l.yml @@ -13,7 +13,7 @@ jobs: uses: ./.github/workflows/build_raspbian_armv7l.yml package-raspbian_armv7l: - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 needs: build-raspbian_armv7l steps: - name: Checkout code diff --git a/.github/workflows/sec_sast_semgrep_cron.yml b/.github/workflows/sec_sast_semgrep_cron.yml index 2a0361f5e..54bbbe6d2 100644 --- a/.github/workflows/sec_sast_semgrep_cron.yml +++ b/.github/workflows/sec_sast_semgrep_cron.yml @@ -12,7 +12,7 @@ jobs: semgrep-full: runs-on: ubuntu-latest container: - image: returntocorp/semgrep + image: semgrep/semgrep steps: # step 1 diff --git a/.github/workflows/sec_sast_semgrep_pull.yml b/.github/workflows/sec_sast_semgrep_pull.yml index 2575cbf01..9013f1c74 100644 --- a/.github/workflows/sec_sast_semgrep_pull.yml +++ b/.github/workflows/sec_sast_semgrep_pull.yml @@ -6,7 +6,7 @@ jobs: semgrep-diff: runs-on: ubuntu-22.04 container: - image: returntocorp/semgrep + image: semgrep/semgrep steps: # step 1 diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index f58b38ac9..bf3d15dba 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -77,7 +77,7 @@ jobs: pio upgrade - name: Setup pnpm - uses: pnpm/action-setup@v2 + uses: pnpm/action-setup@v4 with: version: latest diff --git a/.github/workflows/update_protobufs.yml b/.github/workflows/update_protobufs.yml index 4402a280e..7ce767370 100644 --- a/.github/workflows/update_protobufs.yml +++ b/.github/workflows/update_protobufs.yml @@ -1,4 +1,4 @@ -name: "Update protobufs and regenerate classes" +name: Update protobufs and regenerate classes on: workflow_dispatch jobs: @@ -26,8 +26,9 @@ jobs: ./bin/regen-protos.sh - name: Create pull request - uses: peter-evans/create-pull-request@v6 + uses: peter-evans/create-pull-request@v7 with: + title: Update protobufs and classes add-paths: | protobufs src/mesh diff --git a/arch/esp32/esp32.ini b/arch/esp32/esp32.ini index 0dd6cbc1d..13360be78 100644 --- a/arch/esp32/esp32.ini +++ b/arch/esp32/esp32.ini @@ -5,7 +5,7 @@ custom_esp32_kind = esp32 platform = platformio/espressif32@6.7.0 build_src_filter = - ${arduino_base.build_src_filter} - - - - - + ${arduino_base.build_src_filter} - - - - - upload_speed = 921600 debug_init_break = tbreak setup diff --git a/arch/nrf52/nrf52.ini b/arch/nrf52/nrf52.ini index 503da2aab..04880d540 100644 --- a/arch/nrf52/nrf52.ini +++ b/arch/nrf52/nrf52.ini @@ -16,7 +16,7 @@ build_flags = -DLFS_NO_ASSERT ; Disable LFS assertions , see https://github.com/meshtastic/firmware/pull/3818 build_src_filter = - ${arduino_base.build_src_filter} - - - - - - - - - - + ${arduino_base.build_src_filter} - - - - - - - - - - lib_deps= ${arduino_base.lib_deps} diff --git a/arch/portduino/portduino.ini b/arch/portduino/portduino.ini index 482b1f9c5..30cc190d2 100644 --- a/arch/portduino/portduino.ini +++ b/arch/portduino/portduino.ini @@ -1,6 +1,6 @@ ; The Portduino based sim environment on top of any host OS, all hardware will be simulated [portduino_base] -platform = https://github.com/meshtastic/platform-native.git#ad8112adf82ce1f5b917092cf32be07a077801a0 +platform = https://github.com/meshtastic/platform-native.git#6b3796d697481c8f6e3f4aa5c111bd9979f29e64 framework = arduino build_src_filter = @@ -9,7 +9,7 @@ build_src_filter = - - - - - + - - - + diff --git a/arch/rp2040/rp2040.ini b/arch/rp2xx0/rp2040.ini similarity index 97% rename from arch/rp2040/rp2040.ini rename to arch/rp2xx0/rp2040.ini index dd3a4d7ff..d3f27a676 100644 --- a/arch/rp2040/rp2040.ini +++ b/arch/rp2xx0/rp2040.ini @@ -8,7 +8,7 @@ board_build.core = earlephilhower board_build.filesystem_size = 0.5m build_flags = ${arduino_base.build_flags} -Wno-unused-variable - -Isrc/platform/rp2040 + -Isrc/platform/rp2xx0 -D__PLAT_RP2040__ # -D _POSIX_THREADS build_src_filter = diff --git a/arch/rp2xx0/rp2350.ini b/arch/rp2xx0/rp2350.ini new file mode 100644 index 000000000..96ed0cb21 --- /dev/null +++ b/arch/rp2xx0/rp2350.ini @@ -0,0 +1,23 @@ +; Common settings for rp2040 Processor based targets +[rp2350_base] +platform = https://github.com/maxgerhardt/platform-raspberrypi.git#9e55f6db5c56b9867c69fe473f388beea4546672 +extends = arduino_base +platform_packages = framework-arduinopico@https://github.com/earlephilhower/arduino-pico.git#a6ab6e1f95bc1428d667d55ea7173c0744acc03c ; 4.0.2+ + +board_build.core = earlephilhower +board_build.filesystem_size = 0.5m +build_flags = + ${arduino_base.build_flags} -Wno-unused-variable + -Isrc/platform/rp2xx0 + -D__PLAT_RP2040__ +# -D _POSIX_THREADS +build_src_filter = + ${arduino_base.build_src_filter} - - - - - - - - - + +lib_ignore = + BluetoothOTA + +lib_deps = + ${arduino_base.lib_deps} + ${environmental_base.lib_deps} + rweather/Crypto \ No newline at end of file diff --git a/arch/stm32/stm32.ini b/arch/stm32/stm32.ini index 050dbf7f0..4be290015 100644 --- a/arch/stm32/stm32.ini +++ b/arch/stm32/stm32.ini @@ -22,7 +22,7 @@ build_flags = -fdata-sections build_src_filter = - ${arduino_base.build_src_filter} - - - - - - - - - - - - - - + ${arduino_base.build_src_filter} - - - - - - - - - - - - - - board_upload.offset_address = 0x08000000 upload_protocol = stlink diff --git a/meshtestic b/meshtestic index 31ee3d90c..37245b3d6 160000 --- a/meshtestic +++ b/meshtestic @@ -1 +1 @@ -Subproject commit 31ee3d90c8bef61e835c3271be2c7cda8c4a5cc2 +Subproject commit 37245b3d612a9272f546bbb092837bafdad46bc2 diff --git a/protobufs b/protobufs index 5709c0a05..9b8490784 160000 --- a/protobufs +++ b/protobufs @@ -1 +1 @@ -Subproject commit 5709c0a05eaefccbc9cb8ed3917adbf5fd134197 +Subproject commit 9b84907847b67047b72f9792f4b47532b308bbe4 diff --git a/src/Power.cpp b/src/Power.cpp index 61a6c987d..b3a67abd5 100644 --- a/src/Power.cpp +++ b/src/Power.cpp @@ -13,6 +13,7 @@ #include "power.h" #include "NodeDB.h" #include "PowerFSM.h" +#include "Throttle.h" #include "buzz/buzz.h" #include "configuration.h" #include "main.h" @@ -30,6 +31,7 @@ #if HAS_WIFI #include #endif + #endif #ifndef DELAY_FOREVER @@ -244,7 +246,7 @@ class AnalogBatteryLevel : public HasBatteryLevel config.power.adc_multiplier_override > 0 ? config.power.adc_multiplier_override : ADC_MULTIPLIER; // Do not call analogRead() often. const uint32_t min_read_interval = 5000; - if (millis() - last_read_time_ms > min_read_interval) { + if (!Throttle::isWithinTimespanMs(last_read_time_ms, min_read_interval)) { last_read_time_ms = millis(); uint32_t raw = 0; diff --git a/src/SerialConsole.cpp b/src/SerialConsole.cpp index b911e15da..2c1133771 100644 --- a/src/SerialConsole.cpp +++ b/src/SerialConsole.cpp @@ -1,6 +1,8 @@ #include "SerialConsole.h" +#include "Default.h" #include "NodeDB.h" #include "PowerFSM.h" +#include "Throttle.h" #include "configuration.h" #include "time.h" @@ -47,7 +49,7 @@ SerialConsole::SerialConsole() : StreamAPI(&Port), RedirectablePrint(&Port), con #if defined(ARCH_NRF52) || defined(CONFIG_IDF_TARGET_ESP32S2) || defined(CONFIG_IDF_TARGET_ESP32S3) || defined(ARCH_RP2040) time_t timeout = millis(); while (!Port) { - if ((millis() - timeout) < 5000) { + if (Throttle::isWithinTimespanMs(timeout, FIVE_SECONDS_MS)) { delay(100); } else { break; @@ -73,7 +75,7 @@ void SerialConsole::flush() bool SerialConsole::checkIsConnected() { uint32_t now = millis(); - return (now - lastContactMsec) < SERIAL_CONNECTION_TIMEOUT; + return Throttle::isWithinTimespanMs(lastContactMsec, SERIAL_CONNECTION_TIMEOUT); } /** diff --git a/src/configuration.h b/src/configuration.h index 349bd2870..7416e0a3e 100644 --- a/src/configuration.h +++ b/src/configuration.h @@ -210,6 +210,16 @@ along with this program. If not, see . #define MINIMUM_SAFE_FREE_HEAP 1500 #endif +#ifndef WIRE_INTERFACES_COUNT +// Officially an NRF52 macro +// Repurposed cross-platform to identify devices using Wire1 +#if defined(I2C_SDA1) || defined(PIN_WIRE_SDA) +#define WIRE_INTERFACES_COUNT 2 +#elif HAS_WIRE +#define WIRE_INTERFACES_COUNT 1 +#endif +#endif + /* Step #3: mop up with disabled values for HAS_ options not handled by the above two */ #ifndef HAS_WIFI diff --git a/src/detect/ScanI2CTwoWire.cpp b/src/detect/ScanI2CTwoWire.cpp index f09eb3b95..98f40be76 100644 --- a/src/detect/ScanI2CTwoWire.cpp +++ b/src/detect/ScanI2CTwoWire.cpp @@ -162,13 +162,13 @@ void ScanI2CTwoWire::scanPort(I2CPort port, uint8_t *address, uint8_t asize) Melopero_RV3028 rtc; #endif -#ifdef I2C_SDA1 +#if WIRE_INTERFACES_COUNT == 2 if (port == I2CPort::WIRE1) { i2cBus = &Wire1; } else { #endif i2cBus = &Wire; -#ifdef I2C_SDA1 +#if WIRE_INTERFACES_COUNT == 2 } #endif @@ -423,7 +423,7 @@ TwoWire *ScanI2CTwoWire::fetchI2CBus(ScanI2C::DeviceAddress address) const if (address.port == ScanI2C::I2CPort::WIRE) { return &Wire; } else { -#ifdef I2C_SDA1 +#if WIRE_INTERFACES_COUNT == 2 return &Wire1; #else return &Wire; diff --git a/src/gps/GPS.cpp b/src/gps/GPS.cpp index 01fa65816..4fa676913 100644 --- a/src/gps/GPS.cpp +++ b/src/gps/GPS.cpp @@ -6,6 +6,7 @@ #include "NodeDB.h" #include "PowerMon.h" #include "RTC.h" +#include "Throttle.h" #include "main.h" // pmu_found #include "sleep.h" @@ -206,7 +207,7 @@ GPS_RESPONSE GPS::getACKCas(uint8_t class_id, uint8_t msg_id, uint32_t waitMilli // ACK-NACK| 0xBA | 0xCE | 0x04 | 0x00 | 0x05 | 0x00 | 0xXX | 0xXX | 0x00 | 0x00 | 0xXX | 0xXX | 0xXX | 0xXX | // ACK-ACK | 0xBA | 0xCE | 0x04 | 0x00 | 0x05 | 0x01 | 0xXX | 0xXX | 0x00 | 0x00 | 0xXX | 0xXX | 0xXX | 0xXX | - while (millis() - startTime < waitMillis) { + while (Throttle::isWithinTimespanMs(startTime, waitMillis)) { if (_serial_gps->available()) { buffer[bufferPos++] = _serial_gps->read(); @@ -275,7 +276,7 @@ GPS_RESPONSE GPS::getACK(uint8_t class_id, uint8_t msg_id, uint32_t waitMillis) buf[9] += buf[8]; } - while (millis() - startTime < waitMillis) { + while (Throttle::isWithinTimespanMs(startTime, waitMillis)) { if (ack > 9) { #ifdef GPS_DEBUG LOG_DEBUG("\n"); @@ -332,7 +333,7 @@ int GPS::getACK(uint8_t *buffer, uint16_t size, uint8_t requestedClass, uint8_t uint32_t startTime = millis(); uint16_t needRead; - while (millis() - startTime < waitMillis) { + while (Throttle::isWithinTimespanMs(startTime, waitMillis)) { if (_serial_gps->available()) { int c = _serial_gps->read(); switch (ubxFrameCounter) { @@ -525,268 +526,144 @@ bool GPS::setup() delay(250); _serial_gps->write("$PAIR513*3D\r\n"); // save configuration + } else if (gnssModel == GNSS_MODEL_UBLOX6) { + clearBuffer(); + SEND_UBX_PACKET(0x06, 0x02, _message_DISABLE_TXT_INFO, "Unable to disable text info messages.\n", 500); + SEND_UBX_PACKET(0x06, 0x39, _message_JAM_6_7, "Unable to enable interference resistance.\n", 500); + SEND_UBX_PACKET(0x06, 0x23, _message_NAVX5, "Unable to configure NAVX5 settings.\n", 500); - } else if (gnssModel == GNSS_MODEL_UBLOX) { - // Configure GNSS system to GPS+SBAS+GLONASS (Module may restart after this command) - // We need set it because by default it is GPS only, and we want to use GLONASS too - // Also we need SBAS for better accuracy and extra features - // ToDo: Dynamic configure GNSS systems depending of LoRa region + // Turn off unwanted NMEA messages, set update rate + SEND_UBX_PACKET(0x06, 0x08, _message_1HZ, "Unable to set GPS update rate.\n", 500); + SEND_UBX_PACKET(0x06, 0x01, _message_GLL, "Unable to disable NMEA GLL.\n", 500); + SEND_UBX_PACKET(0x06, 0x01, _message_GSA, "Unable to Enable NMEA GSA.\n", 500); + SEND_UBX_PACKET(0x06, 0x01, _message_GSV, "Unable to disable NMEA GSV.\n", 500); + SEND_UBX_PACKET(0x06, 0x01, _message_VTG, "Unable to disable NMEA VTG.\n", 500); + SEND_UBX_PACKET(0x06, 0x01, _message_RMC, "Unable to enable NMEA RMC.\n", 500); + SEND_UBX_PACKET(0x06, 0x01, _message_GGA, "Unable to enable NMEA GGA.\n", 500); - if (strncmp(info.hwVersion, "000A0000", 8) != 0) { - if (strncmp(info.hwVersion, "00040007", 8) != 0) { - // The original ublox Neo-6 is GPS only and doesn't support the UBX-CFG-GNSS message - // Max7 seems to only support GPS *or* GLONASS - // Neo-7 is supposed to support GPS *and* GLONASS but NAKs the CFG-GNSS command to do it - // So treat all the u-blox 7 series as GPS only - // M8 can support 3 constallations at once so turn on GPS, GLONASS and Galileo (or BeiDou) + clearBuffer(); + SEND_UBX_PACKET(0x06, 0x11, _message_CFG_RXM_ECO, "Unable to enable powersaving ECO mode for Neo-6.\n", 500); + SEND_UBX_PACKET(0x06, 0x3B, _message_CFG_PM2, "Unable to enable powersaving details for GPS.\n", 500); + SEND_UBX_PACKET(0x06, 0x01, _message_AID, "Unable to disable UBX-AID.\n", 500); - if (strncmp(info.hwVersion, "00070000", 8) == 0) { - LOG_DEBUG("Setting GPS+SBAS\n"); - msglen = makeUBXPacket(0x06, 0x3e, sizeof(_message_GNSS_7), _message_GNSS_7); - _serial_gps->write(UBXscratch, msglen); - } else { - msglen = makeUBXPacket(0x06, 0x3e, sizeof(_message_GNSS_8), _message_GNSS_8); - _serial_gps->write(UBXscratch, msglen); - } + msglen = makeUBXPacket(0x06, 0x09, sizeof(_message_SAVE), _message_SAVE); + _serial_gps->write(UBXscratch, msglen); + if (getACK(0x06, 0x09, 2000) != GNSS_RESPONSE_OK) { + LOG_WARN("Unable to save GNSS module configuration.\n"); + } else { + LOG_INFO("GNSS module configuration saved!\n"); + } + } else if (gnssModel == GNSS_MODEL_UBLOX7 || gnssModel == GNSS_MODEL_UBLOX8 || gnssModel == GNSS_MODEL_UBLOX9) { + if (gnssModel == GNSS_MODEL_UBLOX7) { + LOG_DEBUG("Setting GPS+SBAS\n"); + msglen = makeUBXPacket(0x06, 0x3e, sizeof(_message_GNSS_7), _message_GNSS_7); + _serial_gps->write(UBXscratch, msglen); + } else { // 8,9 + msglen = makeUBXPacket(0x06, 0x3e, sizeof(_message_GNSS_8), _message_GNSS_8); + _serial_gps->write(UBXscratch, msglen); + } - if (getACK(0x06, 0x3e, 800) == GNSS_RESPONSE_NAK) { - // It's not critical if the module doesn't acknowledge this configuration. - LOG_INFO("Unable to reconfigure GNSS - defaults maintained. Is this module GPS-only?\n"); - } else { - if (strncmp(info.hwVersion, "00070000", 8) == 0) { - LOG_INFO("GNSS configured for GPS+SBAS. Pause for 0.75s before sending next command.\n"); - } else { - LOG_INFO( - "GNSS configured for GPS+SBAS+GLONASS+Galileo. Pause for 0.75s before sending next command.\n"); - } - // Documentation say, we need wait atleast 0.5s after reconfiguration of GNSS module, before sending next - // commands for the M8 it tends to be more... 1 sec should be enough ;>) - delay(1000); - } + if (getACK(0x06, 0x3e, 800) == GNSS_RESPONSE_NAK) { + // It's not critical if the module doesn't acknowledge this configuration. + LOG_INFO("Unable to reconfigure GNSS - defaults maintained. Is this module GPS-only?\n"); + } else { + if (gnssModel == GNSS_MODEL_UBLOX7) { + LOG_INFO("GNSS configured for GPS+SBAS.\n"); + } else { // 8,9 + LOG_INFO("GNSS configured for GPS+SBAS+GLONASS+Galileo.\n"); } - // Disable Text Info messages - msglen = makeUBXPacket(0x06, 0x02, sizeof(_message_DISABLE_TXT_INFO), _message_DISABLE_TXT_INFO); + // Documentation say, we need wait atleast 0.5s after reconfiguration of GNSS module, before sending next + // commands for the M8 it tends to be more... 1 sec should be enough ;>) + delay(1000); + } + + // Disable Text Info messages //6,7,8,9 + clearBuffer(); + SEND_UBX_PACKET(0x06, 0x02, _message_DISABLE_TXT_INFO, "Unable to disable text info messages.\n", 500); + + if (gnssModel == GNSS_MODEL_UBLOX8) { // 8 clearBuffer(); - _serial_gps->write(UBXscratch, msglen); - if (getACK(0x06, 0x02, 500) != GNSS_RESPONSE_OK) { - LOG_WARN("Unable to disable text info messages.\n"); - } - // ToDo add M10 tests for below - if (strncmp(info.hwVersion, "00080000", 8) == 0) { - msglen = makeUBXPacket(0x06, 0x39, sizeof(_message_JAM_8), _message_JAM_8); + SEND_UBX_PACKET(0x06, 0x39, _message_JAM_8, "Unable to enable interference resistance.\n", 500); + + clearBuffer(); + SEND_UBX_PACKET(0x06, 0x23, _message_NAVX5_8, "Unable to configure NAVX5_8 settings.\n", 500); + } else { // 6,7,9 + SEND_UBX_PACKET(0x06, 0x39, _message_JAM_6_7, "Unable to enable interference resistance.\n", 500); + SEND_UBX_PACKET(0x06, 0x23, _message_NAVX5, "Unable to configure NAVX5 settings.\n", 500); + } + // Turn off unwanted NMEA messages, set update rate + SEND_UBX_PACKET(0x06, 0x08, _message_1HZ, "Unable to set GPS update rate.\n", 500); + SEND_UBX_PACKET(0x06, 0x01, _message_GLL, "Unable to disable NMEA GLL.\n", 500); + SEND_UBX_PACKET(0x06, 0x01, _message_GSA, "Unable to Enable NMEA GSA.\n", 500); + SEND_UBX_PACKET(0x06, 0x01, _message_GSV, "Unable to disable NMEA GSV.\n", 500); + SEND_UBX_PACKET(0x06, 0x01, _message_VTG, "Unable to disable NMEA VTG.\n", 500); + SEND_UBX_PACKET(0x06, 0x01, _message_RMC, "Unable to enable NMEA RMC.\n", 500); + SEND_UBX_PACKET(0x06, 0x01, _message_GGA, "Unable to enable NMEA GGA.\n", 500); + + if (uBloxProtocolVersion >= 18) { + clearBuffer(); + SEND_UBX_PACKET(0x06, 0x86, _message_PMS, "Unable to enable powersaving for GPS.\n", 500); + SEND_UBX_PACKET(0x06, 0x3B, _message_CFG_PM2, "Unable to enable powersaving details for GPS.\n", 500); + + // For M8 we want to enable NMEA vserion 4.10 so we can see the additional sats. + if (gnssModel == GNSS_MODEL_UBLOX8) { clearBuffer(); - _serial_gps->write(UBXscratch, msglen); - if (getACK(0x06, 0x39, 500) != GNSS_RESPONSE_OK) { - LOG_WARN("Unable to enable interference resistance.\n"); - } - - msglen = makeUBXPacket(0x06, 0x23, sizeof(_message_NAVX5_8), _message_NAVX5_8); - clearBuffer(); - _serial_gps->write(UBXscratch, msglen); - if (getACK(0x06, 0x23, 500) != GNSS_RESPONSE_OK) { - LOG_WARN("Unable to configure NAVX5_8 settings.\n"); - } - } else { - msglen = makeUBXPacket(0x06, 0x39, sizeof(_message_JAM_6_7), _message_JAM_6_7); - _serial_gps->write(UBXscratch, msglen); - if (getACK(0x06, 0x39, 500) != GNSS_RESPONSE_OK) { - LOG_WARN("Unable to enable interference resistance.\n"); - } - - msglen = makeUBXPacket(0x06, 0x23, sizeof(_message_NAVX5), _message_NAVX5); - _serial_gps->write(UBXscratch, msglen); - if (getACK(0x06, 0x23, 500) != GNSS_RESPONSE_OK) { - LOG_WARN("Unable to configure NAVX5 settings.\n"); - } - } - // Turn off unwanted NMEA messages, set update rate - - msglen = makeUBXPacket(0x06, 0x08, sizeof(_message_1HZ), _message_1HZ); - _serial_gps->write(UBXscratch, msglen); - if (getACK(0x06, 0x08, 500) != GNSS_RESPONSE_OK) { - LOG_WARN("Unable to set GPS update rate.\n"); - } - - msglen = makeUBXPacket(0x06, 0x01, sizeof(_message_GLL), _message_GLL); - _serial_gps->write(UBXscratch, msglen); - if (getACK(0x06, 0x01, 500) != GNSS_RESPONSE_OK) { - LOG_WARN("Unable to disable NMEA GLL.\n"); - } - - msglen = makeUBXPacket(0x06, 0x01, sizeof(_message_GSA), _message_GSA); - _serial_gps->write(UBXscratch, msglen); - if (getACK(0x06, 0x01, 500) != GNSS_RESPONSE_OK) { - LOG_WARN("Unable to Enable NMEA GSA.\n"); - } - - msglen = makeUBXPacket(0x06, 0x01, sizeof(_message_GSV), _message_GSV); - _serial_gps->write(UBXscratch, msglen); - if (getACK(0x06, 0x01, 500) != GNSS_RESPONSE_OK) { - LOG_WARN("Unable to disable NMEA GSV.\n"); - } - - msglen = makeUBXPacket(0x06, 0x01, sizeof(_message_VTG), _message_VTG); - _serial_gps->write(UBXscratch, msglen); - if (getACK(0x06, 0x01, 500) != GNSS_RESPONSE_OK) { - LOG_WARN("Unable to disable NMEA VTG.\n"); - } - - msglen = makeUBXPacket(0x06, 0x01, sizeof(_message_RMC), _message_RMC); - _serial_gps->write(UBXscratch, msglen); - if (getACK(0x06, 0x01, 500) != GNSS_RESPONSE_OK) { - LOG_WARN("Unable to enable NMEA RMC.\n"); - } - - msglen = makeUBXPacket(0x06, 0x01, sizeof(_message_GGA), _message_GGA); - _serial_gps->write(UBXscratch, msglen); - if (getACK(0x06, 0x01, 500) != GNSS_RESPONSE_OK) { - LOG_WARN("Unable to enable NMEA GGA.\n"); - } - - if (uBloxProtocolVersion >= 18) { - msglen = makeUBXPacket(0x06, 0x86, sizeof(_message_PMS), _message_PMS); - clearBuffer(); - _serial_gps->write(UBXscratch, msglen); - if (getACK(0x06, 0x86, 500) != GNSS_RESPONSE_OK) { - LOG_WARN("Unable to enable powersaving for GPS.\n"); - } - msglen = makeUBXPacket(0x06, 0x3B, sizeof(_message_CFG_PM2), _message_CFG_PM2); - _serial_gps->write(UBXscratch, msglen); - if (getACK(0x06, 0x3B, 500) != GNSS_RESPONSE_OK) { - LOG_WARN("Unable to enable powersaving details for GPS.\n"); - } - // For M8 we want to enable NMEA vserion 4.10 so we can see the additional sats. - if (strncmp(info.hwVersion, "00080000", 8) == 0) { - msglen = makeUBXPacket(0x06, 0x17, sizeof(_message_NMEA), _message_NMEA); - clearBuffer(); - _serial_gps->write(UBXscratch, msglen); - if (getACK(0x06, 0x17, 500) != GNSS_RESPONSE_OK) { - LOG_WARN("Unable to enable NMEA 4.10.\n"); - } - } - } else { - if (strncmp(info.hwVersion, "00040007", 8) == 0) { // This PSM mode is only for Neo-6 - msglen = makeUBXPacket(0x06, 0x11, 0x2, _message_CFG_RXM_ECO); - _serial_gps->write(UBXscratch, msglen); - if (getACK(0x06, 0x11, 500) != GNSS_RESPONSE_OK) { - LOG_WARN("Unable to enable powersaving ECO mode for Neo-6.\n"); - } - msglen = makeUBXPacket(0x06, 0x3B, sizeof(_message_CFG_PM2), _message_CFG_PM2); - _serial_gps->write(UBXscratch, msglen); - if (getACK(0x06, 0x3B, 500) != GNSS_RESPONSE_OK) { - LOG_WARN("Unable to enable powersaving details for GPS.\n"); - } - msglen = makeUBXPacket(0x06, 0x01, sizeof(_message_AID), _message_AID); - _serial_gps->write(UBXscratch, msglen); - if (getACK(0x06, 0x01, 500) != GNSS_RESPONSE_OK) { - LOG_WARN("Unable to disable UBX-AID.\n"); - } - } else { - msglen = makeUBXPacket(0x06, 0x11, 0x2, _message_CFG_RXM_PSM); - _serial_gps->write(UBXscratch, msglen); - if (getACK(0x06, 0x11, 500) != GNSS_RESPONSE_OK) { - LOG_WARN("Unable to enable powersaving mode for GPS.\n"); - } - - msglen = makeUBXPacket(0x06, 0x3B, sizeof(_message_CFG_PM2), _message_CFG_PM2); - _serial_gps->write(UBXscratch, msglen); - if (getACK(0x06, 0x3B, 500) != GNSS_RESPONSE_OK) { - LOG_WARN("Unable to enable powersaving details for GPS.\n"); - } - } + SEND_UBX_PACKET(0x06, 0x17, _message_NMEA, "Unable to enable NMEA 4.10.\n", 500); } } else { - // LOG_INFO("u-blox M10 hardware found.\n"); - delay(1000); - // First disable all NMEA messages in RAM layer - msglen = makeUBXPacket(0x06, 0x8A, sizeof(_message_VALSET_DISABLE_NMEA_RAM), _message_VALSET_DISABLE_NMEA_RAM); - clearBuffer(); - _serial_gps->write(UBXscratch, msglen); - if (getACK(0x06, 0x8A, 300) != GNSS_RESPONSE_OK) { - LOG_WARN("Unable to disable NMEA messages for M10 GPS RAM.\n"); - } - delay(250); - // Next disable unwanted NMEA messages in BBR layer - msglen = makeUBXPacket(0x06, 0x8A, sizeof(_message_VALSET_DISABLE_NMEA_BBR), _message_VALSET_DISABLE_NMEA_BBR); - clearBuffer(); - _serial_gps->write(UBXscratch, msglen); - if (getACK(0x06, 0x8A, 300) != GNSS_RESPONSE_OK) { - LOG_WARN("Unable to disable NMEA messages for M10 GPS BBR.\n"); - } - delay(250); - // Disable Info txt messages in RAM layer - msglen = - makeUBXPacket(0x06, 0x8A, sizeof(_message_VALSET_DISABLE_TXT_INFO_RAM), _message_VALSET_DISABLE_TXT_INFO_RAM); - clearBuffer(); - _serial_gps->write(UBXscratch, msglen); - if (getACK(0x06, 0x8A, 300) != GNSS_RESPONSE_OK) { - LOG_WARN("Unable to disable Info messages for M10 GPS RAM.\n"); - } - delay(250); - // Next disable Info txt messages in BBR layer - msglen = - makeUBXPacket(0x06, 0x8A, sizeof(_message_VALSET_DISABLE_TXT_INFO_BBR), _message_VALSET_DISABLE_TXT_INFO_BBR); - clearBuffer(); - _serial_gps->write(UBXscratch, msglen); - if (getACK(0x06, 0x8A, 300) != GNSS_RESPONSE_OK) { - LOG_WARN("Unable to disable Info messages for M10 GPS BBR.\n"); - } - // Do M10 configuration for Power Management. - - msglen = makeUBXPacket(0x06, 0x8A, sizeof(_message_VALSET_PM_RAM), _message_VALSET_PM_RAM); - _serial_gps->write(UBXscratch, msglen); - if (getACK(0x06, 0x8A, 300) != GNSS_RESPONSE_OK) { - LOG_WARN("Unable to enable powersaving for M10 GPS RAM.\n"); - } - msglen = makeUBXPacket(0x06, 0x8A, sizeof(_message_VALSET_PM_BBR), _message_VALSET_PM_BBR); - _serial_gps->write(UBXscratch, msglen); - if (getACK(0x06, 0x8A, 300) != GNSS_RESPONSE_OK) { - LOG_WARN("Unable to enable powersaving for M10 GPS BBR.\n"); - } - - delay(250); - msglen = makeUBXPacket(0x06, 0x8A, sizeof(_message_VALSET_ITFM_RAM), _message_VALSET_ITFM_RAM); - _serial_gps->write(UBXscratch, msglen); - if (getACK(0x06, 0x8A, 300) != GNSS_RESPONSE_OK) { - LOG_WARN("Unable to enable Jamming detection M10 GPS RAM.\n"); - } - msglen = makeUBXPacket(0x06, 0x8A, sizeof(_message_VALSET_ITFM_BBR), _message_VALSET_ITFM_BBR); - _serial_gps->write(UBXscratch, msglen); - if (getACK(0x06, 0x8A, 300) != GNSS_RESPONSE_OK) { - LOG_WARN("Unable to enable Jamming detection M10 GPS BBR.\n"); - } - - // Here is where the init commands should go to do further M10 initialization. - delay(250); - msglen = makeUBXPacket(0x06, 0x8A, sizeof(_message_VALSET_DISABLE_SBAS_RAM), _message_VALSET_DISABLE_SBAS_RAM); - _serial_gps->write(UBXscratch, msglen); - if (getACK(0x06, 0x8A, 300) != GNSS_RESPONSE_OK) { - LOG_WARN("Unable to disable SBAS M10 GPS RAM.\n"); - } - delay(750); // will cause a receiver restart so wait a bit - msglen = makeUBXPacket(0x06, 0x8A, sizeof(_message_VALSET_DISABLE_SBAS_BBR), _message_VALSET_DISABLE_SBAS_BBR); - _serial_gps->write(UBXscratch, msglen); - if (getACK(0x06, 0x8A, 300) != GNSS_RESPONSE_OK) { - LOG_WARN("Unable to disable SBAS M10 GPS BBR.\n"); - } - delay(750); // will cause a receiver restart so wait a bit - // Done with initialization, Now enable wanted NMEA messages in BBR layer so they will survive a periodic sleep. - msglen = makeUBXPacket(0x06, 0x8A, sizeof(_message_VALSET_ENABLE_NMEA_BBR), _message_VALSET_ENABLE_NMEA_BBR); - _serial_gps->write(UBXscratch, msglen); - if (getACK(0x06, 0x8A, 300) != GNSS_RESPONSE_OK) { - LOG_WARN("Unable to enable messages for M10 GPS BBR.\n"); - } - delay(250); - // Next enable wanted NMEA messages in RAM layer - msglen = makeUBXPacket(0x06, 0x8A, sizeof(_message_VALSET_ENABLE_NMEA_RAM), _message_VALSET_ENABLE_NMEA_RAM); - _serial_gps->write(UBXscratch, msglen); - if (getACK(0x06, 0x8A, 300) != GNSS_RESPONSE_OK) { - LOG_WARN("Unable to enable messages for M10 GPS RAM.\n"); - } - // As the M10 has no flash, the best we can do to preserve the config is to set it in RAM and BBR. - // BBR will survive a restart, and power off for a while, but modules with small backup - // batteries or super caps will not retain the config for a long power off time. + SEND_UBX_PACKET(0x06, 0x11, _message_CFG_RXM_PSM, "Unable to enable powersaving mode for GPS.\n", 500); + SEND_UBX_PACKET(0x06, 0x3B, _message_CFG_PM2, "Unable to enable powersaving details for GPS.\n", 500); } + + msglen = makeUBXPacket(0x06, 0x09, sizeof(_message_SAVE), _message_SAVE); + _serial_gps->write(UBXscratch, msglen); + if (getACK(0x06, 0x09, 2000) != GNSS_RESPONSE_OK) { + LOG_WARN("Unable to save GNSS module configuration.\n"); + } else { + LOG_INFO("GNSS module configuration saved!\n"); + } + } else if (gnssModel == GNSS_MODEL_UBLOX10) { + delay(1000); + clearBuffer(); + SEND_UBX_PACKET(0x06, 0x8A, _message_VALSET_DISABLE_NMEA_RAM, "Unable to disable NMEA messages in M10 RAM.\n", 300); + delay(750); + clearBuffer(); + SEND_UBX_PACKET(0x06, 0x8A, _message_VALSET_DISABLE_NMEA_BBR, "Unable to disable NMEA messages in M10 BBR.\n", 300); + delay(750); + clearBuffer(); + SEND_UBX_PACKET(0x06, 0x8A, _message_VALSET_DISABLE_TXT_INFO_RAM, + "Unable to disable Info messages for M10 GPS RAM.\n", 300); + delay(750); + // Next disable Info txt messages in BBR layer + clearBuffer(); + SEND_UBX_PACKET(0x06, 0x8A, _message_VALSET_DISABLE_TXT_INFO_BBR, + "Unable to disable Info messages for M10 GPS BBR.\n", 300); + delay(750); + // Do M10 configuration for Power Management. + SEND_UBX_PACKET(0x06, 0x8A, _message_VALSET_PM_RAM, "Unable to enable powersaving for M10 GPS RAM.\n", 300); + delay(750); + SEND_UBX_PACKET(0x06, 0x8A, _message_VALSET_PM_BBR, "Unable to enable powersaving for M10 GPS BBR.\n", 300); + delay(750); + SEND_UBX_PACKET(0x06, 0x8A, _message_VALSET_ITFM_RAM, "Unable to enable Jamming detection M10 GPS RAM.\n", 300); + delay(750); + SEND_UBX_PACKET(0x06, 0x8A, _message_VALSET_ITFM_BBR, "Unable to enable Jamming detection M10 GPS BBR.\n", 300); + delay(750); + // Here is where the init commands should go to do further M10 initialization. + SEND_UBX_PACKET(0x06, 0x8A, _message_VALSET_DISABLE_SBAS_RAM, "Unable to disable SBAS M10 GPS RAM.\n", 300); + delay(750); // will cause a receiver restart so wait a bit + SEND_UBX_PACKET(0x06, 0x8A, _message_VALSET_DISABLE_SBAS_BBR, "Unable to disable SBAS M10 GPS BBR.\n", 300); + delay(750); // will cause a receiver restart so wait a bit + + // Done with initialization, Now enable wanted NMEA messages in BBR layer so they will survive a periodic sleep. + SEND_UBX_PACKET(0x06, 0x8A, _message_VALSET_ENABLE_NMEA_BBR, "Unable to enable messages for M10 GPS BBR.\n", 300); + delay(750); + // Next enable wanted NMEA messages in RAM layer + SEND_UBX_PACKET(0x06, 0x8A, _message_VALSET_ENABLE_NMEA_RAM, "Unable to enable messages for M10 GPS RAM.\n", 500); + delay(750); + + // As the M10 has no flash, the best we can do to preserve the config is to set it in RAM and BBR. + // BBR will survive a restart, and power off for a while, but modules with small backup + // batteries or super caps will not retain the config for a long power off time. msglen = makeUBXPacket(0x06, 0x09, sizeof(_message_SAVE), _message_SAVE); _serial_gps->write(UBXscratch, msglen); if (getACK(0x06, 0x09, 2000) != GNSS_RESPONSE_OK) { @@ -949,7 +826,8 @@ void GPS::setPowerPMU(bool on) void GPS::setPowerUBLOX(bool on, uint32_t sleepMs) { // Abort: if not UBLOX hardware - if (gnssModel != GNSS_MODEL_UBLOX) + if (!(gnssModel == GNSS_MODEL_UBLOX6 || gnssModel == GNSS_MODEL_UBLOX7 || gnssModel == GNSS_MODEL_UBLOX8 || + gnssModel == GNSS_MODEL_UBLOX9 || gnssModel == GNSS_MODEL_UBLOX10)) return; // If waking @@ -972,7 +850,7 @@ void GPS::setPowerUBLOX(bool on, uint32_t sleepMs) } // Determine hardware version - if (strncmp(info.hwVersion, "000A0000", 8) != 0) { + if (gnssModel == GNSS_MODEL_UBLOX10) { // Encode the sleep time in millis into the packet for (int i = 0; i < 4; i++) gps->_message_PMREQ[0 + i] = sleepMs >> (i * 8); @@ -1031,7 +909,9 @@ void GPS::down() // Check whether the GPS hardware is capable of GPS_SOFTSLEEP // If not, fallback to GPS_HARDSLEEP instead bool softsleepSupported = false; - if (gnssModel == GNSS_MODEL_UBLOX) // U-blox is supported via PMREQ + // U-blox is supported via PMREQ + if (gnssModel == GNSS_MODEL_UBLOX6 || gnssModel == GNSS_MODEL_UBLOX7 || gnssModel == GNSS_MODEL_UBLOX8 || + gnssModel == GNSS_MODEL_UBLOX9 || gnssModel == GNSS_MODEL_UBLOX10) softsleepSupported = true; #ifdef PIN_GPS_STANDBY // L76B, L76K and clones have a standby pin softsleepSupported = true; @@ -1106,7 +986,9 @@ int32_t GPS::runOnce() // if we have received valid NMEA claim we are connected setConnected(); } else { - if ((config.position.gps_mode == meshtastic_Config_PositionConfig_GpsMode_ENABLED) && (gnssModel == GNSS_MODEL_UBLOX)) { + if ((config.position.gps_mode == meshtastic_Config_PositionConfig_GpsMode_ENABLED) && + (gnssModel == GNSS_MODEL_UBLOX6 || gnssModel == GNSS_MODEL_UBLOX7 || gnssModel == GNSS_MODEL_UBLOX8 || + gnssModel == GNSS_MODEL_UBLOX9 || gnssModel == GNSS_MODEL_UBLOX10)) { // reset the GPS on next bootup if (devicestate.did_gps_reset && scheduling.elapsedSearchMs() > 60 * 1000UL && !hasFlow()) { LOG_DEBUG("GPS is not communicating, trying factory reset on next bootup.\n"); @@ -1335,9 +1217,9 @@ GnssModel_t GPS::probe(int serialSpeed) strncpy((char *)buffer, &(info.extension[i][4]), sizeof(buffer)); // LOG_DEBUG("GetModel:%s\n", (char *)buffer); if (strlen((char *)buffer)) { - LOG_INFO("UBlox GNSS probe succeeded, using UBlox %s GNSS Module\n", (char *)buffer); + LOG_INFO("%s detected, using GNSS_MODEL_UBLOX\n", (char *)buffer); } else { - LOG_INFO("UBlox GNSS probe succeeded, using UBlox GNSS Module\n"); + LOG_INFO("Generic Ublox detected, using GNSS_MODEL_UBLOX\n"); } } else if (!strncmp(info.extension[i], "PROTVER", 7)) { char *ptr = nullptr; @@ -1352,9 +1234,20 @@ GnssModel_t GPS::probe(int serialSpeed) } } } + if (strncmp(info.hwVersion, "00040007", 8) == 0) { + return GNSS_MODEL_UBLOX6; + } else if (strncmp(info.hwVersion, "00070000", 8) == 0) { + return GNSS_MODEL_UBLOX7; + } else if (strncmp(info.hwVersion, "00080000", 8) == 0) { + return GNSS_MODEL_UBLOX8; + } else if (strncmp(info.hwVersion, "00190000", 8) == 0) { + return GNSS_MODEL_UBLOX9; + } else if (strncmp(info.hwVersion, "000A0000", 8) == 0) { + return GNSS_MODEL_UBLOX10; + } } - return GNSS_MODEL_UBLOX; + return GNSS_MODEL_UNKNOWN; } GPS *GPS::createGps() @@ -1528,16 +1421,15 @@ bool GPS::lookForTime() #ifdef GNSS_AIROHA uint8_t fix = reader.fixQuality(); - uint32_t now = millis(); if (fix > 0) { if (lastFixStartMsec > 0) { - if ((now - lastFixStartMsec) < GPS_FIX_HOLD_TIME) { + if (Throttle::isWithinTimespanMs(lastFixStartMsec, GPS_FIX_HOLD_TIME)) { return false; } else { clearBuffer(); } } else { - lastFixStartMsec = now; + lastFixStartMsec = millis(); return false; } } else { @@ -1581,16 +1473,15 @@ bool GPS::lookForLocation() #ifdef GNSS_AIROHA if ((config.position.gps_update_interval * 1000) >= (GPS_FIX_HOLD_TIME * 2)) { uint8_t fix = reader.fixQuality(); - uint32_t now = millis(); if (fix > 0) { if (lastFixStartMsec > 0) { - if ((now - lastFixStartMsec) < GPS_FIX_HOLD_TIME) { + if (Throttle::isWithinTimespanMs(lastFixStartMsec, GPS_FIX_HOLD_TIME)) { return false; } else { clearBuffer(); } } else { - lastFixStartMsec = now; + lastFixStartMsec = millis(); return false; } } else { @@ -1820,4 +1711,4 @@ void GPS::toggleGpsMode() enable(); } } -#endif // Exclude GPS +#endif // Exclude GPS \ No newline at end of file diff --git a/src/gps/GPS.h b/src/gps/GPS.h index 3423edb68..48aecc8b9 100644 --- a/src/gps/GPS.h +++ b/src/gps/GPS.h @@ -26,7 +26,11 @@ struct uBloxGnssModelInfo { typedef enum { GNSS_MODEL_ATGM336H, GNSS_MODEL_MTK, - GNSS_MODEL_UBLOX, + GNSS_MODEL_UBLOX6, + GNSS_MODEL_UBLOX7, + GNSS_MODEL_UBLOX8, + GNSS_MODEL_UBLOX9, + GNSS_MODEL_UBLOX10, GNSS_MODEL_UC6580, GNSS_MODEL_UNKNOWN, GNSS_MODEL_MTK_L76B, @@ -310,4 +314,4 @@ class GPS : private concurrency::OSThread }; extern GPS *gps; -#endif // Exclude GPS +#endif // Exclude GPS \ No newline at end of file diff --git a/src/gps/RTC.cpp b/src/gps/RTC.cpp index 728284242..d9ac56b74 100644 --- a/src/gps/RTC.cpp +++ b/src/gps/RTC.cpp @@ -2,6 +2,7 @@ #include "configuration.h" #include "detect/ScanI2C.h" #include "main.h" +#include #include #include @@ -29,7 +30,7 @@ void readFromRTC() if (rtc_found.address == RV3028_RTC) { uint32_t now = millis(); Melopero_RV3028 rtc; -#ifdef I2C_SDA1 +#if WIRE_INTERFACES_COUNT == 2 rtc.initI2C(rtc_found.port == ScanI2C::I2CPort::WIRE1 ? Wire1 : Wire); #else rtc.initI2C(); @@ -58,7 +59,7 @@ void readFromRTC() uint32_t now = millis(); PCF8563_Class rtc; -#ifdef I2C_SDA1 +#if WIRE_INTERFACES_COUNT == 2 rtc.begin(rtc_found.port == ScanI2C::I2CPort::WIRE1 ? Wire1 : Wire); #else rtc.begin(); @@ -127,7 +128,7 @@ bool perhapsSetRTC(RTCQuality q, const struct timeval *tv, bool forceUpdate) } else if (q == RTCQualityGPS) { shouldSet = true; LOG_DEBUG("Reapplying GPS time: %ld secs\n", printableEpoch); - } else if (q == RTCQualityNTP && (now - lastSetMsec) > (12 * 60 * 60 * 1000UL)) { + } else if (q == RTCQualityNTP && !Throttle::isWithinTimespanMs(lastSetMsec, (12 * 60 * 60 * 1000UL))) { // Every 12 hrs we will slam in a new NTP or Phone GPS / NTP time, to correct for local RTC clock drift shouldSet = true; LOG_DEBUG("Reapplying external time to correct clock drift %ld secs\n", printableEpoch); @@ -150,7 +151,7 @@ bool perhapsSetRTC(RTCQuality q, const struct timeval *tv, bool forceUpdate) #ifdef RV3028_RTC if (rtc_found.address == RV3028_RTC) { Melopero_RV3028 rtc; -#ifdef I2C_SDA1 +#if WIRE_INTERFACES_COUNT == 2 rtc.initI2C(rtc_found.port == ScanI2C::I2CPort::WIRE1 ? Wire1 : Wire); #else rtc.initI2C(); @@ -164,7 +165,7 @@ bool perhapsSetRTC(RTCQuality q, const struct timeval *tv, bool forceUpdate) if (rtc_found.address == PCF8563_RTC) { PCF8563_Class rtc; -#ifdef I2C_SDA1 +#if WIRE_INTERFACES_COUNT == 2 rtc.begin(rtc_found.port == ScanI2C::I2CPort::WIRE1 ? Wire1 : Wire); #else rtc.begin(); diff --git a/src/gps/ubx.h b/src/gps/ubx.h index 0852c331d..64e45f160 100644 --- a/src/gps/ubx.h +++ b/src/gps/ubx.h @@ -1,3 +1,10 @@ +#define SEND_UBX_PACKET(TYPE, ID, DATA, ERRMSG, TIMEOUT) \ + msglen = makeUBXPacket(TYPE, ID, sizeof(DATA), DATA); \ + _serial_gps->write(UBXscratch, msglen); \ + if (getACK(TYPE, ID, TIMEOUT) != GNSS_RESPONSE_OK) { \ + LOG_WARN(#ERRMSG); \ + } + // Power Management uint8_t GPS::_message_PMREQ[] PROGMEM = { diff --git a/src/graphics/EInkDynamicDisplay.cpp b/src/graphics/EInkDynamicDisplay.cpp index c31941a60..ca994b2c9 100644 --- a/src/graphics/EInkDynamicDisplay.cpp +++ b/src/graphics/EInkDynamicDisplay.cpp @@ -1,3 +1,4 @@ +#include "Throttle.h" #include "configuration.h" #if defined(USE_EINK) && defined(USE_EINK_DYNAMICDISPLAY) @@ -231,15 +232,13 @@ void EInkDynamicDisplay::checkForPromotion() // Is it too soon for another frame of this type? void EInkDynamicDisplay::checkRateLimiting() { - uint32_t now = millis(); - // Sanity check: millis() overflow - just let the update run.. - if (previousRunMs > now) + if (previousRunMs > millis()) return; // Skip update: too soon for BACKGROUND if (frameFlags == BACKGROUND) { - if (now - previousRunMs < EINK_LIMIT_RATE_BACKGROUND_SEC * 1000) { + if (Throttle::isWithinTimespanMs(previousRunMs, EINK_LIMIT_RATE_BACKGROUND_SEC * 1000)) { refresh = SKIPPED; reason = EXCEEDED_RATELIMIT_FULL; return; @@ -252,7 +251,7 @@ void EInkDynamicDisplay::checkRateLimiting() // Skip update: too soon for RESPONSIVE if (frameFlags & RESPONSIVE) { - if (now - previousRunMs < EINK_LIMIT_RATE_RESPONSIVE_SEC * 1000) { + if (Throttle::isWithinTimespanMs(previousRunMs, EINK_LIMIT_RATE_RESPONSIVE_SEC * 1000)) { refresh = SKIPPED; reason = EXCEEDED_RATELIMIT_FAST; LOG_DEBUG("refresh=SKIPPED, reason=EXCEEDED_RATELIMIT_FAST, frameFlags=0x%x\n", frameFlags); diff --git a/src/graphics/Screen.cpp b/src/graphics/Screen.cpp index ae09ee408..19b20e8dc 100644 --- a/src/graphics/Screen.cpp +++ b/src/graphics/Screen.cpp @@ -22,6 +22,7 @@ along with this program. If not, see . #include "Screen.h" #include "../userPrefs.h" #include "PowerMon.h" +#include "Throttle.h" #include "configuration.h" #if HAS_SCREEN #include @@ -117,6 +118,7 @@ static bool heartbeat = false; #define SCREEN_HEIGHT display->getHeight() #include "graphics/ScreenFonts.h" +#include #define getStringCenteredX(s) ((SCREEN_WIDTH - display->getStringWidth(s)) / 2) @@ -1670,6 +1672,11 @@ void Screen::setup() static_cast(dispdev)->setSubtype(7); #endif +#if defined(USE_ST7789) && defined(TFT_MESH) + // Heltec T114 and T190: honor a custom text color, if defined in variant.h + static_cast(dispdev)->setRGB(TFT_MESH); +#endif + // Initialising the UI will init the display too. ui->init(); @@ -1726,6 +1733,8 @@ void Screen::setup() #if defined(ST7701_CS) || defined(ST7735_CS) || defined(ILI9341_DRIVER) || defined(ST7701_CS) || defined(ST7789_CS) || \ defined(RAK14014) || defined(HX8357_CS) static_cast(dispdev)->flipScreenVertically(); +#elif defined(USE_ST7789) + static_cast(dispdev)->flipScreenVertically(); #else dispdev->flipScreenVertically(); #endif @@ -1942,7 +1951,7 @@ int32_t Screen::runOnce() if (showingNormalScreen) { // standard screen loop handling here if (config.display.auto_screen_carousel_secs > 0 && - (millis() - lastScreenTransition) > (config.display.auto_screen_carousel_secs * 1000)) { + !Throttle::isWithinTimespanMs(lastScreenTransition, config.display.auto_screen_carousel_secs * 1000)) { // If an E-Ink display struggles with fast refresh, force carousel to use full refresh instead // Carousel is potentially a major source of E-Ink display wear @@ -2435,8 +2444,8 @@ void DebugInfo::drawFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16 // Draw our hardware ID to assist with bluetooth pairing. Either prefix with Info or S&F Logo if (moduleConfig.store_forward.enabled) { #ifdef ARCH_ESP32 - if (millis() - storeForwardModule->lastHeartbeat > - (storeForwardModule->heartbeatInterval * 1200)) { // no heartbeat, overlap a bit + if (!Throttle::isWithinTimespanMs(storeForwardModule->lastHeartbeat, + (storeForwardModule->heartbeatInterval * 1200))) { // no heartbeat, overlap a bit #if (defined(USE_EINK) || defined(ILI9341_DRIVER) || defined(ST7701_CS) || defined(ST7735_CS) || defined(ST7789_CS) || \ defined(USE_ST7789) || defined(HX8357_CS)) && \ !defined(DISPLAY_FORCE_SMALL_FONTS) diff --git a/src/input/ExpressLRSFiveWay.cpp b/src/input/ExpressLRSFiveWay.cpp index c444800ba..af4433dae 100644 --- a/src/input/ExpressLRSFiveWay.cpp +++ b/src/input/ExpressLRSFiveWay.cpp @@ -1,5 +1,5 @@ - #include "ExpressLRSFiveWay.h" +#include "Throttle.h" #ifdef INPUTBROKER_EXPRESSLRSFIVEWAY_TYPE @@ -76,11 +76,10 @@ void ExpressLRSFiveWay::update(int *keyValue, bool *keyLongPressed) *keyValue = NO_PRESS; int newKey = readKey(); - uint32_t now = millis(); if (keyInProcess == NO_PRESS) { // New key down if (newKey != NO_PRESS) { - keyDownStart = now; + keyDownStart = millis(); // DBGLN("down=%u", newKey); } } else { @@ -88,7 +87,7 @@ void ExpressLRSFiveWay::update(int *keyValue, bool *keyLongPressed) if (newKey == NO_PRESS) { // DBGLN("up=%u", keyInProcess); if (!isLongPressed) { - if ((now - keyDownStart) > KEY_DEBOUNCE_MS) { + if (!Throttle::isWithinTimespanMs(keyDownStart, KEY_DEBOUNCE_MS)) { *keyValue = keyInProcess; *keyLongPressed = false; } @@ -101,7 +100,7 @@ void ExpressLRSFiveWay::update(int *keyValue, bool *keyLongPressed) } // else still pressing, waiting for long if not already signaled else if (!isLongPressed) { - if ((now - keyDownStart) > KEY_LONG_PRESS_MS) { + if (!Throttle::isWithinTimespanMs(keyDownStart, KEY_LONG_PRESS_MS)) { *keyValue = keyInProcess; *keyLongPressed = true; isLongPressed = true; diff --git a/src/input/ScanAndSelect.cpp b/src/input/ScanAndSelect.cpp index d693d768c..65ca7e332 100644 --- a/src/input/ScanAndSelect.cpp +++ b/src/input/ScanAndSelect.cpp @@ -6,6 +6,7 @@ #include "ScanAndSelect.h" #include "modules/CannedMessageModule.h" +#include // Config static const char name[] = "scanAndSelect"; // should match "allow input source" string @@ -75,7 +76,7 @@ int32_t ScanAndSelectInput::runOnce() else { // Duration enough for long press // Long press not yet fired (prevent repeat firing while held) - if (!longPressFired && now - downSinceMs > durationLongMs) { + if (!longPressFired && Throttle::isWithinTimespanMs(downSinceMs, durationLongMs)) { longPressFired = true; longPress(); } @@ -91,7 +92,7 @@ int32_t ScanAndSelectInput::runOnce() // Long press event didn't already fire if (held && !longPressFired) { // Duration enough for short press - if (now - downSinceMs > durationShortMs) { + if (!Throttle::isWithinTimespanMs(downSinceMs, durationShortMs)) { shortPress(); } } diff --git a/src/input/SerialKeyboard.cpp b/src/input/SerialKeyboard.cpp index 7b7a2f3ec..4827e8995 100644 --- a/src/input/SerialKeyboard.cpp +++ b/src/input/SerialKeyboard.cpp @@ -1,5 +1,6 @@ #include "SerialKeyboard.h" #include "configuration.h" +#include #ifdef INPUTBROKER_SERIAL_TYPE #define CANNED_MESSAGE_MODULE_ENABLE 1 // in case it's not set in the variant file @@ -73,7 +74,7 @@ int32_t SerialKeyboard::runOnce() // Serial.print ("X"); // Serial.println (shiftRegister2, BIN); - if (millis() - lastPressTime > 500) { + if (!Throttle::isWithinTimespanMs(lastPressTime, 500)) { quickPress = 0; } diff --git a/src/input/cardKbI2cImpl.cpp b/src/input/cardKbI2cImpl.cpp index 8aaebcb45..f1df6b137 100644 --- a/src/input/cardKbI2cImpl.cpp +++ b/src/input/cardKbI2cImpl.cpp @@ -16,7 +16,7 @@ void CardKbI2cImpl::init() uint8_t i2caddr_asize = 3; auto i2cScanner = std::unique_ptr(new ScanI2CTwoWire()); -#if defined(I2C_SDA1) +#if WIRE_INTERFACES_COUNT == 2 i2cScanner->scanPort(ScanI2C::I2CPort::WIRE1, i2caddr_scan, i2caddr_asize); #endif i2cScanner->scanPort(ScanI2C::I2CPort::WIRE, i2caddr_scan, i2caddr_asize); diff --git a/src/input/kbI2cBase.cpp b/src/input/kbI2cBase.cpp index 2692fc80d..1d8154bcf 100644 --- a/src/input/kbI2cBase.cpp +++ b/src/input/kbI2cBase.cpp @@ -33,7 +33,7 @@ int32_t KbI2cBase::runOnce() if (!i2cBus) { switch (cardkb_found.port) { case ScanI2C::WIRE1: -#ifdef I2C_SDA1 +#if WIRE_INTERFACES_COUNT == 2 LOG_DEBUG("Using I2C Bus 1 (the second one)\n"); i2cBus = &Wire1; if (cardkb_found.address == BBQ10_KB_ADDR) { diff --git a/src/main.cpp b/src/main.cpp index e24ba68b3..01fccf2b0 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -18,6 +18,7 @@ #include "Led.h" #include "RTC.h" #include "SPILock.h" +#include "Throttle.h" #include "concurrency/OSThread.h" #include "concurrency/Periodic.h" #include "detect/ScanI2C.h" @@ -360,6 +361,8 @@ void setup() Wire1.begin(); #elif defined(I2C_SDA1) && !defined(ARCH_RP2040) Wire1.begin(I2C_SDA1, I2C_SCL1); +#elif WIRE_INTERFACES_COUNT == 2 + Wire1.begin(); #endif #if defined(I2C_SDA) && defined(ARCH_RP2040) @@ -427,6 +430,8 @@ void setup() #elif defined(I2C_SDA1) && !defined(ARCH_RP2040) Wire1.begin(I2C_SDA1, I2C_SCL1); i2cScanner->scanPort(ScanI2C::I2CPort::WIRE1); +#elif defined(NRF52840_XXAA) && (WIRE_INTERFACES_COUNT == 2) + i2cScanner->scanPort(ScanI2C::I2CPort::WIRE1); #endif #if defined(I2C_SDA) && defined(ARCH_RP2040) @@ -1118,7 +1123,7 @@ void loop() #ifdef DEBUG_STACK static uint32_t lastPrint = 0; - if (millis() - lastPrint > 10 * 1000L) { + if (!Throttle::isWithinTimespanMs(lastPrint, 10 * 1000L)) { lastPrint = millis(); meshtastic::printThreadInfo("main"); } diff --git a/src/mesh/LR11x0Interface.cpp b/src/mesh/LR11x0Interface.cpp index c0742f241..e237c354f 100644 --- a/src/mesh/LR11x0Interface.cpp +++ b/src/mesh/LR11x0Interface.cpp @@ -1,7 +1,9 @@ #include "LR11x0Interface.h" +#include "Throttle.h" #include "configuration.h" #include "error.h" #include "mesh/NodeDB.h" + #ifdef ARCH_PORTDUINO #include "PortduinoGlue.h" #endif @@ -275,15 +277,15 @@ template bool LR11x0Interface::isActivelyReceiving() bool detected = (irq & (RADIOLIB_LR11X0_IRQ_SYNC_WORD_HEADER_VALID | RADIOLIB_LR11X0_IRQ_PREAMBLE_DETECTED)); // Handle false detections if (detected) { - uint32_t now = millis(); if (!activeReceiveStart) { - activeReceiveStart = now; - } else if ((now - activeReceiveStart > 2 * preambleTimeMsec) && !(irq & RADIOLIB_LR11X0_IRQ_SYNC_WORD_HEADER_VALID)) { + activeReceiveStart = millis(); + } else if (!Throttle::isWithinTimespanMs(activeReceiveStart, 2 * preambleTimeMsec) && + !(irq & RADIOLIB_LR11X0_IRQ_SYNC_WORD_HEADER_VALID)) { // The HEADER_VALID flag should be set by now if it was really a packet, so ignore PREAMBLE_DETECTED flag activeReceiveStart = 0; LOG_DEBUG("Ignore false preamble detection.\n"); return false; - } else if (now - activeReceiveStart > maxPacketTimeMsec) { + } else if (!Throttle::isWithinTimespanMs(activeReceiveStart, maxPacketTimeMsec)) { // We should have gotten an RX_DONE IRQ by now if it was really a packet, so ignore HEADER_VALID flag activeReceiveStart = 0; LOG_DEBUG("Ignore false header detection.\n"); diff --git a/src/mesh/PacketHistory.cpp b/src/mesh/PacketHistory.cpp index 26a73a3fe..ed1c3c59c 100644 --- a/src/mesh/PacketHistory.cpp +++ b/src/mesh/PacketHistory.cpp @@ -5,6 +5,7 @@ #ifdef ARCH_PORTDUINO #include "platform/portduino/PortduinoGlue.h" #endif +#include "Throttle.h" PacketHistory::PacketHistory() { @@ -22,18 +23,17 @@ bool PacketHistory::wasSeenRecently(const meshtastic_MeshPacket *p, bool withUpd return false; // Not a floodable message ID, so we don't care } - uint32_t now = millis(); - PacketRecord r; r.id = p->id; r.sender = getFrom(p); - r.rxTimeMsec = now; + r.rxTimeMsec = millis(); auto found = recentPackets.find(r); bool seenRecently = (found != recentPackets.end()); // found not equal to .end() means packet was seen recently - if (seenRecently && (now - found->rxTimeMsec) >= FLOOD_EXPIRE_TIME) { // Check whether found packet has already expired - recentPackets.erase(found); // Erase and pretend packet has not been seen recently + if (seenRecently && + !Throttle::isWithinTimespanMs(found->rxTimeMsec, FLOOD_EXPIRE_TIME)) { // Check whether found packet has already expired + recentPackets.erase(found); // Erase and pretend packet has not been seen recently found = recentPackets.end(); seenRecently = false; } @@ -64,12 +64,10 @@ bool PacketHistory::wasSeenRecently(const meshtastic_MeshPacket *p, bool withUpd */ void PacketHistory::clearExpiredRecentPackets() { - uint32_t now = millis(); - LOG_DEBUG("recentPackets size=%ld\n", recentPackets.size()); for (auto it = recentPackets.begin(); it != recentPackets.end();) { - if ((now - it->rxTimeMsec) >= FLOOD_EXPIRE_TIME) { + if (!Throttle::isWithinTimespanMs(it->rxTimeMsec, FLOOD_EXPIRE_TIME)) { it = recentPackets.erase(it); // erase returns iterator pointing to element immediately following the one erased } else { ++it; diff --git a/src/mesh/PhoneAPI.cpp b/src/mesh/PhoneAPI.cpp index 121687c49..2ed7a69db 100644 --- a/src/mesh/PhoneAPI.cpp +++ b/src/mesh/PhoneAPI.cpp @@ -25,6 +25,7 @@ #if !MESHTASTIC_EXCLUDE_MQTT #include "mqtt/MQTT.h" #endif +#include "Throttle.h" #include PhoneAPI::PhoneAPI() @@ -561,12 +562,12 @@ bool PhoneAPI::handleToRadioPacket(meshtastic_MeshPacket &p) { printPacket("PACKET FROM PHONE", &p); if (p.decoded.portnum == meshtastic_PortNum_TRACEROUTE_APP && lastPortNumToRadio[p.decoded.portnum] && - (millis() - lastPortNumToRadio[p.decoded.portnum]) < (THIRTY_SECONDS_MS)) { + Throttle::isWithinTimespanMs(lastPortNumToRadio[p.decoded.portnum], THIRTY_SECONDS_MS)) { LOG_WARN("Rate limiting portnum %d\n", p.decoded.portnum); sendNotification(meshtastic_LogRecord_Level_WARNING, p.id, "TraceRoute can only be sent once every 30 seconds"); return false; } else if (p.decoded.portnum == meshtastic_PortNum_POSITION_APP && lastPortNumToRadio[p.decoded.portnum] && - (millis() - lastPortNumToRadio[p.decoded.portnum]) < (FIVE_SECONDS_MS)) { + Throttle::isWithinTimespanMs(lastPortNumToRadio[p.decoded.portnum], FIVE_SECONDS_MS)) { LOG_WARN("Rate limiting portnum %d\n", p.decoded.portnum); sendNotification(meshtastic_LogRecord_Level_WARNING, p.id, "Position can only be sent once every 5 seconds"); return false; diff --git a/src/mesh/RadioInterface.h b/src/mesh/RadioInterface.h index f1016e3d8..d0d20926c 100644 --- a/src/mesh/RadioInterface.h +++ b/src/mesh/RadioInterface.h @@ -10,6 +10,7 @@ #define MAX_TX_QUEUE 16 // max number of packets which can be waiting for transmission #define MAX_RHPACKETLEN 256 +#define LORA_HEADER_LENGTH 16 #define PACKET_FLAGS_HOP_LIMIT_MASK 0x07 #define PACKET_FLAGS_WANT_ACK_MASK 0x08 diff --git a/src/mesh/RadioLibInterface.cpp b/src/mesh/RadioLibInterface.cpp index f299ebff2..6cdb3b99e 100644 --- a/src/mesh/RadioLibInterface.cpp +++ b/src/mesh/RadioLibInterface.cpp @@ -3,6 +3,7 @@ #include "NodeDB.h" #include "PowerMon.h" #include "SPILock.h" +#include "Throttle.h" #include "configuration.h" #include "error.h" #include "main.h" @@ -41,7 +42,7 @@ void LockingArduinoHal::spiTransfer(uint8_t *out, size_t len, uint8_t *in) uint32_t start = millis(); while (digitalRead(busy)) { - if (millis() - start >= 2000) { + if (!Throttle::isWithinTimespanMs(start, 2000)) { LOG_ERROR("GPIO mid-transfer timeout, is it connected?"); return; } @@ -114,7 +115,7 @@ bool RadioLibInterface::canSendImmediately() } // If we've been trying to send the same packet more than one minute and we haven't gotten a // TX IRQ from the radio, the radio is probably broken. - if (busyTx && (millis() - lastTxStart > 60000)) { + if (busyTx && !Throttle::isWithinTimespanMs(lastTxStart, 60000)) { LOG_ERROR("Hardware Failure! busyTx for more than 60s\n"); RECORD_CRITICALERROR(meshtastic_CriticalErrorCode_TRANSMIT_FAILED); // reboot in 5 seconds when this condition occurs. diff --git a/src/mesh/Router.cpp b/src/mesh/Router.cpp index 3cf30519b..a6b946761 100644 --- a/src/mesh/Router.cpp +++ b/src/mesh/Router.cpp @@ -475,7 +475,7 @@ meshtastic_Routing_Error perhapsEncode(meshtastic_MeshPacket *p) } } */ - if (numbytes > MAX_RHPACKETLEN) + if (numbytes + LORA_HEADER_LENGTH > MAX_RHPACKETLEN) return meshtastic_Routing_Error_TOO_LARGE; // printBytes("plaintext", bytes, numbytes); @@ -499,7 +499,7 @@ meshtastic_Routing_Error perhapsEncode(meshtastic_MeshPacket *p) p->decoded.portnum != meshtastic_PortNum_TRACEROUTE_APP && p->decoded.portnum != meshtastic_PortNum_NODEINFO_APP && p->decoded.portnum != meshtastic_PortNum_ROUTING_APP && p->decoded.portnum != meshtastic_PortNum_POSITION_APP) { LOG_DEBUG("Using PKI!\n"); - if (numbytes + 12 > MAX_RHPACKETLEN) + if (numbytes + LORA_HEADER_LENGTH + 12 > MAX_RHPACKETLEN) return meshtastic_Routing_Error_TOO_LARGE; if (p->pki_encrypted && !memfll(p->public_key.bytes, 0, 32) && memcmp(p->public_key.bytes, node->user.public_key.bytes, 32) != 0) { diff --git a/src/mesh/SX126xInterface.cpp b/src/mesh/SX126xInterface.cpp index 6d23206bd..2c6096062 100644 --- a/src/mesh/SX126xInterface.cpp +++ b/src/mesh/SX126xInterface.cpp @@ -6,6 +6,8 @@ #include "PortduinoGlue.h" #endif +#include "Throttle.h" + // Particular boards might define a different max power based on what their hardware can do, default to max power output if not // specified (may be dangerous if using external PA and SX126x power config forgotten) #ifndef SX126X_MAX_POWER @@ -319,15 +321,15 @@ template bool SX126xInterface::isActivelyReceiving() bool detected = (irq & (RADIOLIB_SX126X_IRQ_HEADER_VALID | RADIOLIB_SX126X_IRQ_PREAMBLE_DETECTED)); // Handle false detections if (detected) { - uint32_t now = millis(); if (!activeReceiveStart) { - activeReceiveStart = now; - } else if ((now - activeReceiveStart > 2 * preambleTimeMsec) && !(irq & RADIOLIB_SX126X_IRQ_HEADER_VALID)) { + activeReceiveStart = millis(); + } else if (!Throttle::isWithinTimespanMs(activeReceiveStart, 2 * preambleTimeMsec) && + !(irq & RADIOLIB_SX126X_IRQ_HEADER_VALID)) { // The HEADER_VALID flag should be set by now if it was really a packet, so ignore PREAMBLE_DETECTED flag activeReceiveStart = 0; LOG_DEBUG("Ignore false preamble detection.\n"); return false; - } else if (now - activeReceiveStart > maxPacketTimeMsec) { + } else if (!Throttle::isWithinTimespanMs(activeReceiveStart, maxPacketTimeMsec)) { // We should have gotten an RX_DONE IRQ by now if it was really a packet, so ignore HEADER_VALID flag activeReceiveStart = 0; LOG_DEBUG("Ignore false header detection.\n"); @@ -359,4 +361,4 @@ template bool SX126xInterface::sleep() #endif return true; -} +} \ No newline at end of file diff --git a/src/mesh/SX128xInterface.cpp b/src/mesh/SX128xInterface.cpp index 9ff9ac2d7..270356e26 100644 --- a/src/mesh/SX128xInterface.cpp +++ b/src/mesh/SX128xInterface.cpp @@ -1,4 +1,5 @@ #include "SX128xInterface.h" +#include "Throttle.h" #include "configuration.h" #include "error.h" #include "mesh/NodeDB.h" @@ -294,15 +295,15 @@ template bool SX128xInterface::isActivelyReceiving() // Handle false detections if (detected) { - uint32_t now = millis(); if (!activeReceiveStart) { - activeReceiveStart = now; - } else if ((now - activeReceiveStart > 2 * preambleTimeMsec) && !(irq & RADIOLIB_SX128X_IRQ_HEADER_VALID)) { + activeReceiveStart = millis(); + } else if (!Throttle::isWithinTimespanMs(activeReceiveStart, 2 * preambleTimeMsec) && + !(irq & RADIOLIB_SX128X_IRQ_HEADER_VALID)) { // The HEADER_VALID flag should be set by now if it was really a packet, so ignore PREAMBLE_DETECTED flag activeReceiveStart = 0; LOG_DEBUG("Ignore false preamble detection.\n"); return false; - } else if (now - activeReceiveStart > maxPacketTimeMsec) { + } else if (Throttle::isWithinTimespanMs(activeReceiveStart, maxPacketTimeMsec)) { // We should have gotten an RX_DONE IRQ by now if it was really a packet, so ignore HEADER_VALID flag activeReceiveStart = 0; LOG_DEBUG("Ignore false header detection.\n"); diff --git a/src/mesh/StreamAPI.cpp b/src/mesh/StreamAPI.cpp index 9f59aa971..c3d85ed33 100644 --- a/src/mesh/StreamAPI.cpp +++ b/src/mesh/StreamAPI.cpp @@ -1,6 +1,7 @@ #include "StreamAPI.h" #include "PowerFSM.h" #include "RTC.h" +#include "Throttle.h" #include "configuration.h" #define START1 0x94 @@ -20,10 +21,9 @@ int32_t StreamAPI::runOncePart() */ int32_t StreamAPI::readStream() { - uint32_t now = millis(); if (!stream->available()) { // Nothing available this time, if the computer has talked to us recently, poll often, otherwise let CPU sleep a long time - bool recentRx = (now - lastRxMsec) < 2000; + bool recentRx = Throttle::isWithinTimespanMs(lastRxMsec, 2000); return recentRx ? 5 : 250; } else { while (stream->available()) { // Currently we never want to block @@ -71,7 +71,7 @@ int32_t StreamAPI::readStream() } // we had bytes available this time, so assume we might have them next time also - lastRxMsec = now; + lastRxMsec = millis(); return 0; } } diff --git a/src/mesh/Throttle.cpp b/src/mesh/Throttle.cpp index d8f23f9dc..f278cc843 100644 --- a/src/mesh/Throttle.cpp +++ b/src/mesh/Throttle.cpp @@ -24,4 +24,12 @@ bool Throttle::execute(uint32_t *lastExecutionMs, uint32_t minumumIntervalMs, vo onDefer(); } return false; +} + +/// @brief Check if the last execution time is within the interval +/// @param lastExecutionMs The last execution time in milliseconds +/// @param timeSpanMs The interval in milliseconds of the timespan +bool Throttle::isWithinTimespanMs(uint32_t lastExecutionMs, uint32_t timeSpanMs) +{ + return (millis() - lastExecutionMs) < timeSpanMs; } \ No newline at end of file diff --git a/src/mesh/Throttle.h b/src/mesh/Throttle.h index 8115595a4..8b4bb5d30 100644 --- a/src/mesh/Throttle.h +++ b/src/mesh/Throttle.h @@ -6,4 +6,5 @@ class Throttle { public: static bool execute(uint32_t *lastExecutionMs, uint32_t minumumIntervalMs, void (*func)(void), void (*onDefer)(void) = NULL); + static bool isWithinTimespanMs(uint32_t lastExecutionMs, uint32_t intervalMs); }; \ No newline at end of file diff --git a/src/mesh/generated/meshtastic/mesh.pb.h b/src/mesh/generated/meshtastic/mesh.pb.h index 921dfa55b..aa83ff27a 100644 --- a/src/mesh/generated/meshtastic/mesh.pb.h +++ b/src/mesh/generated/meshtastic/mesh.pb.h @@ -71,6 +71,8 @@ typedef enum _meshtastic_HardwareModel { meshtastic_HardwareModel_RAK2560 = 22, /* Heltec HRU-3601: https://heltec.org/project/hru-3601/ */ meshtastic_HardwareModel_HELTEC_HRU_3601 = 23, + /* Heltec Wireless Bridge */ + meshtastic_HardwareModel_HELTEC_WIRELESS_BRIDGE = 24, /* B&Q Consulting Station Edition G1: https://uniteng.com/wiki/doku.php?id=meshtastic:station */ meshtastic_HardwareModel_STATION_G1 = 25, /* RAK11310 (RP2040 + SX1262) */ @@ -199,6 +201,8 @@ typedef enum _meshtastic_HardwareModel { /* M5 esp32 based MCU modules with enclosure, TFT and LORA Shields. All Variants (Basic, Core, Fire, Core2, Paper) https://m5stack.com/ */ meshtastic_HardwareModel_M5STACK_COREBASIC = 77, meshtastic_HardwareModel_M5STACK_CORE2 = 78, + /* Pico2 with Waveshare Hat, same as Pico */ + meshtastic_HardwareModel_RPI_PICO2 = 79, /* ------------------------------------------------------------------------------------------------------------------------------------------ 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. ------------------------------------------------------------------------------------------------------------------------------------------ */ diff --git a/src/mesh/wifi/WiFiAPClient.cpp b/src/mesh/wifi/WiFiAPClient.cpp index 07b03222e..d1169dc3b 100644 --- a/src/mesh/wifi/WiFiAPClient.cpp +++ b/src/mesh/wifi/WiFiAPClient.cpp @@ -23,6 +23,7 @@ static void WiFiEvent(WiFiEvent_t event); #endif #ifndef DISABLE_NTP +#include "Throttle.h" #include #endif @@ -142,7 +143,7 @@ static int32_t reconnectWiFi() } #ifndef DISABLE_NTP - if (WiFi.isConnected() && (((millis() - lastrun_ntp) > 43200000) || (lastrun_ntp == 0))) { // every 12 hours + if (WiFi.isConnected() && (!Throttle::isWithinTimespanMs(lastrun_ntp, 43200000) || (lastrun_ntp == 0))) { // every 12 hours LOG_DEBUG("Updating NTP time from %s\n", config.network.ntp_server); if (timeClient.update()) { LOG_DEBUG("NTP Request Success - Setting RTCQualityNTP if needed\n"); @@ -420,4 +421,4 @@ uint8_t getWifiDisconnectReason() { return wifiDisconnectReason; } -#endif +#endif \ No newline at end of file diff --git a/src/modules/CannedMessageModule.cpp b/src/modules/CannedMessageModule.cpp index 87a3e8927..a1b9c4dc0 100644 --- a/src/modules/CannedMessageModule.cpp +++ b/src/modules/CannedMessageModule.cpp @@ -27,6 +27,7 @@ #endif #include "graphics/ScreenFonts.h" +#include // Remove Canned message screen if no action is taken for some milliseconds #define INACTIVATE_AFTER_MS 20000 @@ -422,7 +423,7 @@ int32_t CannedMessageModule::runOnce() this->notifyObservers(&e); } else if (((this->runState == CANNED_MESSAGE_RUN_STATE_ACTIVE) || (this->runState == CANNED_MESSAGE_RUN_STATE_FREETEXT)) && - ((millis() - this->lastTouchMillis) > INACTIVATE_AFTER_MS)) { + !Throttle::isWithinTimespanMs(this->lastTouchMillis, INACTIVATE_AFTER_MS)) { // Reset module e.action = UIFrameEvent::Action::REGENERATE_FRAMESET; // We want to change the list of frames shown on-screen this->currentMessageIndex = -1; diff --git a/src/modules/DetectionSensorModule.cpp b/src/modules/DetectionSensorModule.cpp index 20aa6d8e9..de782b2ba 100644 --- a/src/modules/DetectionSensorModule.cpp +++ b/src/modules/DetectionSensorModule.cpp @@ -5,6 +5,7 @@ #include "PowerFSM.h" #include "configuration.h" #include "main.h" +#include DetectionSensorModule *detectionSensorModule; #define GPIO_POLLING_INTERVAL 100 diff --git a/src/modules/NeighborInfoModule.cpp b/src/modules/NeighborInfoModule.cpp index 218fb8801..a3a3b9bb4 100644 --- a/src/modules/NeighborInfoModule.cpp +++ b/src/modules/NeighborInfoModule.cpp @@ -3,6 +3,7 @@ #include "MeshService.h" #include "NodeDB.h" #include "RTC.h" +#include NeighborInfoModule *neighborInfoModule; @@ -87,7 +88,8 @@ void NeighborInfoModule::cleanUpNeighbors() NodeNum my_node_id = nodeDB->getNodeNum(); for (auto it = neighbors.rbegin(); it != neighbors.rend();) { // We will remove a neighbor if we haven't heard from them in twice the broadcast interval - if ((now - it->last_rx_time > it->node_broadcast_interval_secs * 2) && (it->node_id != my_node_id)) { + if (!Throttle::isWithinTimespanMs(it->last_rx_time, it->node_broadcast_interval_secs * 2) && + (it->node_id != my_node_id)) { LOG_DEBUG("Removing neighbor with node ID 0x%x\n", it->node_id); it = std::vector::reverse_iterator( neighbors.erase(std::next(it).base())); // Erase the element and update the iterator diff --git a/src/modules/NodeInfoModule.cpp b/src/modules/NodeInfoModule.cpp index cb047a4dc..41f008fb0 100644 --- a/src/modules/NodeInfoModule.cpp +++ b/src/modules/NodeInfoModule.cpp @@ -6,6 +6,7 @@ #include "Router.h" #include "configuration.h" #include "main.h" +#include NodeInfoModule *nodeInfoModule; @@ -67,13 +68,12 @@ meshtastic_MeshPacket *NodeInfoModule::allocReply() LOG_DEBUG("Skip sending NodeInfo due to > 40 percent channel util.\n"); return NULL; } - uint32_t now = millis(); // If we sent our NodeInfo less than 5 min. ago, don't send it again as it may be still underway. - if (!shorterTimeout && lastSentToMesh && (now - lastSentToMesh) < (5 * 60 * 1000)) { + if (!shorterTimeout && lastSentToMesh && Throttle::isWithinTimespanMs(lastSentToMesh, 5 * 60 * 1000)) { LOG_DEBUG("Skip sending NodeInfo since we just sent it less than 5 minutes ago.\n"); ignoreRequest = true; // Mark it as ignored for MeshModule return NULL; - } else if (shorterTimeout && lastSentToMesh && (now - lastSentToMesh) < (60 * 1000)) { + } else if (shorterTimeout && lastSentToMesh && Throttle::isWithinTimespanMs(lastSentToMesh, 60 * 1000)) { LOG_DEBUG("Skip sending actively requested NodeInfo since we just sent it less than 60 seconds ago.\n"); ignoreRequest = true; // Mark it as ignored for MeshModule return NULL; @@ -82,7 +82,7 @@ meshtastic_MeshPacket *NodeInfoModule::allocReply() meshtastic_User &u = owner; LOG_INFO("sending owner %s/%s/%s\n", u.id, u.long_name, u.short_name); - lastSentToMesh = now; + lastSentToMesh = millis(); return allocDataProtobuf(u); } } diff --git a/src/modules/PositionModule.cpp b/src/modules/PositionModule.cpp index cb6a58b2e..4ba09385d 100644 --- a/src/modules/PositionModule.cpp +++ b/src/modules/PositionModule.cpp @@ -145,7 +145,7 @@ void PositionModule::trySetRtc(meshtastic_Position p, bool isLocal, bool forceUp bool PositionModule::hasQualityTimesource() { bool setFromPhoneOrNtpToday = - lastSetFromPhoneNtpOrGps == 0 ? false : (millis() - lastSetFromPhoneNtpOrGps) <= (SEC_PER_DAY * 1000UL); + lastSetFromPhoneNtpOrGps == 0 ? false : Throttle::isWithinTimespanMs(lastSetFromPhoneNtpOrGps, SEC_PER_DAY * 1000UL); #if MESHTASTIC_EXCLUDE_GPS bool hasGpsOrRtc = (rtc_found.address != ScanI2C::ADDRESS_NONE.address); #else diff --git a/src/modules/PowerStressModule.cpp b/src/modules/PowerStressModule.cpp index 4c9f0df88..48159ba54 100644 --- a/src/modules/PowerStressModule.cpp +++ b/src/modules/PowerStressModule.cpp @@ -9,6 +9,7 @@ #include "main.h" #include "sleep.h" #include "target_specific.h" +#include extern void printInfo(); @@ -114,7 +115,7 @@ int32_t PowerStressModule::runOnce() break; case meshtastic_PowerStressMessage_Opcode_CPU_FULLON: { uint32_t start_msec = millis(); - while ((millis() - start_msec) < (uint32_t)sleep_msec) + while (Throttle::isWithinTimespanMs(start_msec, sleep_msec)) ; // Don't let CPU idle at all sleep_msec = 0; // we already slept break; diff --git a/src/modules/RangeTestModule.cpp b/src/modules/RangeTestModule.cpp index 8154a661e..b02494ef3 100644 --- a/src/modules/RangeTestModule.cpp +++ b/src/modules/RangeTestModule.cpp @@ -19,6 +19,7 @@ #include "configuration.h" #include "gps/GeoCoord.h" #include +#include RangeTestModule *rangeTestModule; RangeTestModuleRadio *rangeTestModuleRadio; @@ -79,7 +80,7 @@ int32_t RangeTestModule::runOnce() } // If we have been running for more than 8 hours, turn module back off - if (millis() - started > 28800000) { + if (!Throttle::isWithinTimespanMs(started, 28800000)) { LOG_INFO("Range Test Module - Disabling after 8 hours\n"); return disable(); } else { diff --git a/src/modules/RemoteHardwareModule.cpp b/src/modules/RemoteHardwareModule.cpp index 0242b59bc..f6b8b2e90 100644 --- a/src/modules/RemoteHardwareModule.cpp +++ b/src/modules/RemoteHardwareModule.cpp @@ -5,6 +5,7 @@ #include "Router.h" #include "configuration.h" #include "main.h" +#include #define NUM_GPIOS 64 @@ -118,11 +119,10 @@ bool RemoteHardwareModule::handleReceivedProtobuf(const meshtastic_MeshPacket &r int32_t RemoteHardwareModule::runOnce() { if (moduleConfig.remote_hardware.enabled && watchGpios) { - uint32_t now = millis(); - if (now - lastWatchMsec >= WATCH_INTERVAL_MSEC) { + if (!Throttle::isWithinTimespanMs(lastWatchMsec, WATCH_INTERVAL_MSEC)) { uint64_t curVal = digitalReads(watchGpios); - lastWatchMsec = now; + lastWatchMsec = millis(); if (curVal != previousWatch) { previousWatch = curVal; diff --git a/src/modules/SerialModule.cpp b/src/modules/SerialModule.cpp index f0ba64f65..a4dbb072f 100644 --- a/src/modules/SerialModule.cpp +++ b/src/modules/SerialModule.cpp @@ -7,6 +7,7 @@ #include "Router.h" #include "configuration.h" #include +#include /* SerialModule @@ -97,8 +98,7 @@ SerialModuleRadio::SerialModuleRadio() : MeshModule("SerialModuleRadio") */ bool SerialModule::checkIsConnected() { - uint32_t now = millis(); - return (now - lastContactMsec) < SERIAL_CONNECTION_TIMEOUT; + return Throttle::isWithinTimespanMs(lastContactMsec, SERIAL_CONNECTION_TIMEOUT); } int32_t SerialModule::runOnce() @@ -182,13 +182,13 @@ int32_t SerialModule::runOnce() return runOncePart(); } else if ((moduleConfig.serial.mode == meshtastic_ModuleConfig_SerialConfig_Serial_Mode_NMEA) && HAS_GPS) { // in NMEA mode send out GGA every 2 seconds, Don't read from Port - if (millis() - lastNmeaTime > 2000) { + if (!Throttle::isWithinTimespanMs(lastNmeaTime, 2000)) { lastNmeaTime = millis(); printGGA(outbuf, sizeof(outbuf), localPosition); serialPrint->printf("%s", outbuf); } } else if ((moduleConfig.serial.mode == meshtastic_ModuleConfig_SerialConfig_Serial_Mode_CALTOPO) && HAS_GPS) { - if (millis() - lastNmeaTime > 10000) { + if (!Throttle::isWithinTimespanMs(lastNmeaTime, 10000)) { lastNmeaTime = millis(); uint32_t readIndex = 0; const meshtastic_NodeInfoLite *tempNodeInfo = nodeDB->readNextMeshNode(readIndex); @@ -500,7 +500,7 @@ void SerialModule::processWXSerial() LOG_INFO("WS85 : %i %.1fg%.1f %.1fv %.1fv\n", atoi(windDir), strtof(windVel, nullptr), strtof(windGust, nullptr), batVoltageF, capVoltageF); } - if (gotwind && millis() - lastAveraged > averageIntervalMillis) { + if (gotwind && !Throttle::isWithinTimespanMs(lastAveraged, averageIntervalMillis)) { // calulate averages and send to the mesh float velAvg = 1.0 * velSum / velCount; diff --git a/src/modules/Telemetry/AirQualityTelemetry.cpp b/src/modules/Telemetry/AirQualityTelemetry.cpp index 56d308cfa..0b6be1b7e 100644 --- a/src/modules/Telemetry/AirQualityTelemetry.cpp +++ b/src/modules/Telemetry/AirQualityTelemetry.cpp @@ -12,6 +12,7 @@ #include "Router.h" #include "detect/ScanI2CTwoWire.h" #include "main.h" +#include int32_t AirQualityTelemetryModule::runOnce() { @@ -60,15 +61,14 @@ int32_t AirQualityTelemetryModule::runOnce() if (!moduleConfig.telemetry.air_quality_enabled) return disable(); - uint32_t now = millis(); if (((lastSentToMesh == 0) || - ((now - lastSentToMesh) >= Default::getConfiguredOrDefaultMsScaled(moduleConfig.telemetry.air_quality_interval, - default_telemetry_broadcast_interval_secs, - numOnlineNodes))) && + !Throttle::isWithinTimespanMs(lastSentToMesh, Default::getConfiguredOrDefaultMsScaled( + moduleConfig.telemetry.air_quality_interval, + default_telemetry_broadcast_interval_secs, numOnlineNodes))) && airTime->isTxAllowedChannelUtil(config.device.role != meshtastic_Config_DeviceConfig_Role_SENSOR) && airTime->isTxAllowedAirUtil()) { sendTelemetry(); - lastSentToMesh = now; + lastSentToMesh = millis(); } else if (service->isToPhoneQueueEmpty()) { // Just send to phone when it's not our time to send to mesh yet // Only send while queue is empty (phone assumed connected) diff --git a/src/modules/Telemetry/EnvironmentTelemetry.cpp b/src/modules/Telemetry/EnvironmentTelemetry.cpp index 31cb2f838..f94f7956b 100644 --- a/src/modules/Telemetry/EnvironmentTelemetry.cpp +++ b/src/modules/Telemetry/EnvironmentTelemetry.cpp @@ -64,6 +64,7 @@ T1000xSensor t1000xSensor; #define DISPLAY_RECEIVEID_MEASUREMENTS_ON_SCREEN true #include "graphics/ScreenFonts.h" +#include int32_t EnvironmentTelemetryModule::runOnce() { @@ -155,21 +156,20 @@ int32_t EnvironmentTelemetryModule::runOnce() result = bme680Sensor.runTrigger(); } - uint32_t now = millis(); if (((lastSentToMesh == 0) || - ((now - lastSentToMesh) >= - Default::getConfiguredOrDefaultMsScaled(moduleConfig.telemetry.environment_update_interval, - default_telemetry_broadcast_interval_secs, numOnlineNodes))) && + !Throttle::isWithinTimespanMs(lastSentToMesh, Default::getConfiguredOrDefaultMsScaled( + moduleConfig.telemetry.environment_update_interval, + default_telemetry_broadcast_interval_secs, numOnlineNodes))) && airTime->isTxAllowedChannelUtil(config.device.role != meshtastic_Config_DeviceConfig_Role_SENSOR) && airTime->isTxAllowedAirUtil()) { sendTelemetry(); - lastSentToMesh = now; - } else if (((lastSentToPhone == 0) || ((now - lastSentToPhone) >= sendToPhoneIntervalMs)) && + lastSentToMesh = millis(); + } else if (((lastSentToPhone == 0) || !Throttle::isWithinTimespanMs(lastSentToPhone, sendToPhoneIntervalMs)) && (service->isToPhoneQueueEmpty())) { // Just send to phone when it's not our time to send to mesh yet // Only send while queue is empty (phone assumed connected) sendTelemetry(NODENUM_BROADCAST, true); - lastSentToPhone = now; + lastSentToPhone = millis(); } } return min(sendToPhoneIntervalMs, result); diff --git a/src/modules/Telemetry/PowerTelemetry.cpp b/src/modules/Telemetry/PowerTelemetry.cpp index 318acf456..a493042a0 100644 --- a/src/modules/Telemetry/PowerTelemetry.cpp +++ b/src/modules/Telemetry/PowerTelemetry.cpp @@ -19,6 +19,7 @@ #define DISPLAY_RECEIVEID_MEASUREMENTS_ON_SCREEN true #include "graphics/ScreenFonts.h" +#include int32_t PowerTelemetryModule::runOnce() { @@ -69,20 +70,19 @@ int32_t PowerTelemetryModule::runOnce() if (!moduleConfig.telemetry.power_measurement_enabled) return disable(); - uint32_t now = millis(); if (((lastSentToMesh == 0) || - ((now - lastSentToMesh) >= Default::getConfiguredOrDefaultMsScaled(moduleConfig.telemetry.power_update_interval, - default_telemetry_broadcast_interval_secs, - numOnlineNodes))) && + !Throttle::isWithinTimespanMs(lastSentToMesh, Default::getConfiguredOrDefaultMsScaled( + moduleConfig.telemetry.power_update_interval, + default_telemetry_broadcast_interval_secs, numOnlineNodes))) && airTime->isTxAllowedAirUtil()) { sendTelemetry(); - lastSentToMesh = now; - } else if (((lastSentToPhone == 0) || ((now - lastSentToPhone) >= sendToPhoneIntervalMs)) && + lastSentToMesh = millis(); + } else if (((lastSentToPhone == 0) || !Throttle::isWithinTimespanMs(lastSentToPhone, sendToPhoneIntervalMs)) && (service->isToPhoneQueueEmpty())) { // Just send to phone when it's not our time to send to mesh yet // Only send while queue is empty (phone assumed connected) sendTelemetry(NODENUM_BROADCAST, true); - lastSentToPhone = now; + lastSentToPhone = millis(); } } return min(sendToPhoneIntervalMs, result); diff --git a/src/modules/Telemetry/Sensor/NAU7802Sensor.cpp b/src/modules/Telemetry/Sensor/NAU7802Sensor.cpp index d7dcbd09f..59f310a24 100644 --- a/src/modules/Telemetry/Sensor/NAU7802Sensor.cpp +++ b/src/modules/Telemetry/Sensor/NAU7802Sensor.cpp @@ -7,6 +7,7 @@ #include "NAU7802Sensor.h" #include "SafeFile.h" #include "TelemetrySensor.h" +#include #include #include @@ -40,7 +41,7 @@ bool NAU7802Sensor::getMetrics(meshtastic_Telemetry *measurement) uint32_t start = millis(); while (!nau7802.available()) { delay(100); - if (millis() - start > 1000) { + if (!Throttle::isWithinTimespanMs(start, 1000)) { nau7802.powerDown(); return false; } diff --git a/src/modules/esp32/StoreForwardModule.cpp b/src/modules/esp32/StoreForwardModule.cpp index c696d342a..51ec2a942 100644 --- a/src/modules/esp32/StoreForwardModule.cpp +++ b/src/modules/esp32/StoreForwardModule.cpp @@ -17,6 +17,7 @@ #include "NodeDB.h" #include "RTC.h" #include "Router.h" +#include "Throttle.h" #include "airtime.h" #include "configuration.h" #include "memGet.h" @@ -29,6 +30,9 @@ StoreForwardModule *storeForwardModule; +uint32_t lastHeartbeat = 0; +uint32_t heartbeatInterval = 60; // Default to 60 seconds, adjust as needed + int32_t StoreForwardModule::runOnce() { #ifdef ARCH_ESP32 @@ -42,7 +46,7 @@ int32_t StoreForwardModule::runOnce() this->busy = false; } } - } else if (this->heartbeat && (millis() - lastHeartbeat > (heartbeatInterval * 1000)) && + } else if (this->heartbeat && (!Throttle::isWithinTimespanMs(lastHeartbeat, heartbeatInterval * 1000)) && airTime->isTxAllowedChannelUtil(true)) { lastHeartbeat = millis(); LOG_INFO("*** Sending heartbeat\n"); @@ -127,7 +131,7 @@ uint32_t StoreForwardModule::getNumAvailablePackets(NodeNum dest, uint32_t last_ { uint32_t count = 0; if (lastRequest.find(dest) == lastRequest.end()) { - lastRequest[dest] = 0; + lastRequest.emplace(dest, 0); } for (uint32_t i = lastRequest[dest]; i < this->packetHistoryTotalCount; i++) { if (this->packetHistory[i].time && (this->packetHistory[i].time > last_time)) { diff --git a/src/mqtt/MQTT.cpp b/src/mqtt/MQTT.cpp index 0f4c5a8c5..56af9f663 100644 --- a/src/mqtt/MQTT.cpp +++ b/src/mqtt/MQTT.cpp @@ -21,6 +21,7 @@ #include "Default.h" #include "serialization/JSON.h" #include "serialization/MeshPacketSerializer.h" +#include #include const int reconnectMax = 5; @@ -158,7 +159,22 @@ void MQTT::onReceive(char *topic, byte *payload, size_t length) meshtastic_MeshPacket *p = packetPool.allocCopy(*e.packet); p->via_mqtt = true; // Mark that the packet was received via MQTT + if (p->from == 0 || p->from == nodeDB->getNodeNum()) { + LOG_INFO("Ignoring downlink message we originally sent.\n"); + packetPool.release(p); + return; + } if (p->which_payload_variant == meshtastic_MeshPacket_decoded_tag) { + if (moduleConfig.mqtt.encryption_enabled) { + LOG_INFO("Ignoring decoded message on MQTT, encryption is enabled.\n"); + packetPool.release(p); + return; + } + if (p->decoded.portnum == meshtastic_PortNum_ADMIN_APP) { + LOG_INFO("Ignoring decoded admin packet.\n"); + packetPool.release(p); + return; + } p->channel = ch.index; } @@ -595,7 +611,7 @@ void MQTT::perhapsReportToMap() if (!moduleConfig.mqtt.map_reporting_enabled || !(moduleConfig.mqtt.proxy_to_client_enabled || isConnectedDirectly())) return; - if (millis() - last_report_to_map < map_publish_interval_msecs) { + if (Throttle::isWithinTimespanMs(last_report_to_map, map_publish_interval_msecs)) { return; } else { if (map_position_precision == 0 || (localPosition.latitude_i == 0 && localPosition.longitude_i == 0)) { diff --git a/src/platform/esp32/architecture.h b/src/platform/esp32/architecture.h index 93630aa8a..90c4c392d 100644 --- a/src/platform/esp32/architecture.h +++ b/src/platform/esp32/architecture.h @@ -76,6 +76,8 @@ #ifdef HELTEC_V2_1 #define HW_VENDOR meshtastic_HardwareModel_HELTEC_V2_1 #endif +#elif defined(HELTEC_WIRELESS_BRIDGE) +#define HW_VENDOR meshtastic_HardwareModel_HELTEC_WIRELESS_BRIDGE #elif defined(ARDUINO_HELTEC_WIFI_LORA_32) #define HW_VENDOR meshtastic_HardwareModel_HELTEC_V1 #elif defined(TLORA_V1) diff --git a/src/platform/rp2040/architecture.h b/src/platform/rp2xx0/architecture.h similarity index 90% rename from src/platform/rp2040/architecture.h rename to src/platform/rp2xx0/architecture.h index 3f75735d3..8c7dfc0cd 100644 --- a/src/platform/rp2040/architecture.h +++ b/src/platform/rp2xx0/architecture.h @@ -23,6 +23,8 @@ #if defined(RPI_PICO) #define HW_VENDOR meshtastic_HardwareModel_RPI_PICO +#elif defined(RPI_PICO2) +#define HW_VENDOR meshtastic_HardwareModel_RPI_PICO2 #elif defined(RAK11310) #define HW_VENDOR meshtastic_HardwareModel_RAK11310 #elif defined(SENSELORA_RP2040) diff --git a/src/platform/rp2040/main-rp2040.cpp b/src/platform/rp2xx0/main-rp2xx0.cpp similarity index 100% rename from src/platform/rp2040/main-rp2040.cpp rename to src/platform/rp2xx0/main-rp2xx0.cpp diff --git a/src/sleep.cpp b/src/sleep.cpp index 27e81ce54..f32d24caa 100644 --- a/src/sleep.cpp +++ b/src/sleep.cpp @@ -5,6 +5,7 @@ #endif #include "ButtonThread.h" +#include "Default.h" #include "Led.h" #include "MeshRadio.h" #include "MeshService.h" @@ -28,6 +29,7 @@ esp_sleep_source_t wakeCause; // the reason we booted this time #endif +#include "Throttle.h" #ifndef INCLUDE_vTaskSuspend #define INCLUDE_vTaskSuspend 0 @@ -168,7 +170,8 @@ static void waitEnterSleep(bool skipPreflight = false) while (!doPreflightSleep()) { delay(100); // Kinda yucky - wait until radio says say we can shutdown (finished in process sends/receives) - if (millis() - now > 30 * 1000) { // If we wait too long just report an error and go to sleep + if (!Throttle::isWithinTimespanMs(now, + THIRTY_SECONDS_MS)) { // If we wait too long just report an error and go to sleep RECORD_CRITICALERROR(meshtastic_CriticalErrorCode_SLEEP_ENTER_WAIT); assert(0); // FIXME - for now we just restart, need to fix bug #167 break; @@ -271,13 +274,6 @@ void doDeepSleep(uint32_t msecToWake, bool skipPreflight = false) digitalWrite(LORA_CS, HIGH); gpio_hold_en((gpio_num_t)LORA_CS); } - -#if defined(I2C_SDA) - Wire.end(); - pinMode(I2C_SDA, ANALOG); - pinMode(I2C_SCL, ANALOG); -#endif - #endif #ifdef HAS_PMU @@ -315,6 +311,14 @@ void doDeepSleep(uint32_t msecToWake, bool skipPreflight = false) } #endif +#if defined(ARCH_ESP32) && defined(I2C_SDA) + // Added by https://github.com/meshtastic/firmware/pull/4418 + // Possibly to support Heltec Capsule Sensor? + Wire.end(); + pinMode(I2C_SDA, ANALOG); + pinMode(I2C_SCL, ANALOG); +#endif + console->flush(); cpuDeepSleep(msecToWake); } diff --git a/variants/diy/platformio.ini b/variants/diy/platformio.ini index 2a55f7a79..f3c22b7a8 100644 --- a/variants/diy/platformio.ini +++ b/variants/diy/platformio.ini @@ -69,4 +69,22 @@ build_flags = ${nrf52840_base.build_flags} build_src_filter = ${nrf52_base.build_src_filter} +<../variants/diy/nrf52_promicro_diy_tcxo> lib_deps = ${nrf52840_base.lib_deps} -debug_tool = jlink \ No newline at end of file +debug_tool = jlink + +; NanoVHF T-Energy-S3 + E22(0)-xxxM - DIY +[env:t-energy-s3_e22] +extends = esp32s3_base +board = esp32-s3-devkitc-1 +board_level = extra +board_upload.flash_size = 16MB ;Specify the FLASH capacity as 16MB +board_build.arduino.memory_type = qio_opi ;Enable internal PSRAM +build_unflags = + ${esp32s3_base.build_unflags} + -D ARDUINO_USB_MODE=1 +build_flags = + ${esp32s3_base.build_flags} + -D EBYTE_ESP32_S3 + -D BOARD_HAS_PSRAM + -D ARDUINO_USB_MODE=0 + -D ARDUINO_USB_CDC_ON_BOOT=1 + -I variants/diy/t-energy-s3_e22 diff --git a/variants/diy/t-energy-s3_e22/variant.h b/variants/diy/t-energy-s3_e22/variant.h new file mode 100644 index 000000000..6933d7715 --- /dev/null +++ b/variants/diy/t-energy-s3_e22/variant.h @@ -0,0 +1,46 @@ +// NanoVHF T-Energy-S3 + E22(0)-xxxM - DIY +// https://github.com/NanoVHF/Meshtastic-DIY/tree/main/PCB/ESP-32-devkit_EBYTE-E22/Mesh-v1.06-TTGO-T18 + +// Battery +#define BATTERY_PIN 3 +#define ADC_MULTIPLIER 2.0 +#define ADC_CHANNEL ADC1_GPIO3_CHANNEL + +// Button on NanoVHF PCB +#define BUTTON_PIN 39 + +// I2C via connectors on NanoVHF PCB +#define I2C_SCL 2 +#define I2C_SDA 42 + +// Screen (disabled) +#define HAS_SCREEN 0 // Assume no screen present by default to prevent crash... + +// GPS via T-Energy-S3 onboard connector +#define HAS_GPS 1 +#define GPS_TX_PIN 43 +#define GPS_RX_PIN 44 + +// LoRa +#define USE_SX1262 // E22-900M30S, E22-900M22S, and E22-900MM22S (not E220!) use SX1262 +#define USE_SX1268 // E22-400M30S, E22-400M33S, E22-400M22S, and E22-400MM22S use SX1268 + +#define SX126X_MAX_POWER 22 // SX126xInterface.cpp defaults to 22 if not defined, but here we define it for good practice +#define SX126X_DIO3_TCXO_VOLTAGE 1.8 // E22 series TCXO reference voltage is 1.8V + +#define SX126X_CS 5 // EBYTE module's NSS pin // FIXME: rename to SX126X_SS +#define SX126X_SCK 6 // EBYTE module's SCK pin +#define SX126X_MOSI 13 // EBYTE module's MOSI pin +#define SX126X_MISO 4 // EBYTE module's MISO pin +#define SX126X_RESET 1 // EBYTE module's NRST pin +#define SX126X_BUSY 48 // EBYTE module's BUSY pin +#define SX126X_DIO1 47 // EBYTE module's DIO1 pin + +#define SX126X_TXEN 10 // Schematic connects EBYTE module's TXEN pin to MCU +#define SX126X_RXEN 12 // Schematic connects EBYTE module's RXEN pin to MCU + +#define LORA_CS SX126X_CS // Compatibility with variant file configuration structure +#define LORA_SCK SX126X_SCK // Compatibility with variant file configuration structure +#define LORA_MOSI SX126X_MOSI // Compatibility with variant file configuration structure +#define LORA_MISO SX126X_MISO // Compatibility with variant file configuration structure +#define LORA_DIO1 SX126X_DIO1 // Compatibility with variant file configuration structure diff --git a/variants/heltec_mesh_node_t114/platformio.ini b/variants/heltec_mesh_node_t114/platformio.ini index e0d8ca0cc..1b06c7f5e 100644 --- a/variants/heltec_mesh_node_t114/platformio.ini +++ b/variants/heltec_mesh_node_t114/platformio.ini @@ -14,4 +14,4 @@ build_src_filter = ${nrf52_base.build_src_filter} +<../variants/heltec_mesh_node lib_deps = ${nrf52840_base.lib_deps} lewisxhe/PCF8563_Library@^1.0.1 - https://github.com/meshtastic/st7789#7181320e7ed05c7fb5fd2d32f14723bce6088b7b \ No newline at end of file + https://github.com/meshtastic/st7789#bd33ea58ddfe4a5e4a66d53300ccbd38d66ac21f \ No newline at end of file diff --git a/variants/heltec_mesh_node_t114/variant.h b/variants/heltec_mesh_node_t114/variant.h index 454e66931..2cea3ef2f 100644 --- a/variants/heltec_mesh_node_t114/variant.h +++ b/variants/heltec_mesh_node_t114/variant.h @@ -92,13 +92,22 @@ No longer populated on PCB #define PIN_SERIAL2_TX (0 + 10) // #define PIN_SERIAL2_EN (0 + 17) -/** - Wire Interfaces - */ -#define WIRE_INTERFACES_COUNT 1 +/* + * I2C + */ -#define PIN_WIRE_SDA (26) -#define PIN_WIRE_SCL (27) +#define WIRE_INTERFACES_COUNT 2 + +// I2C bus 0 +// Routed to footprint for PCF8563TS RTC +// Not populated on T114 V1, maybe in future? +#define PIN_WIRE_SDA (0 + 26) // P0.26 +#define PIN_WIRE_SCL (0 + 27) // P0.27 + +// I2C bus 1 +// Available on header pins, for general use +#define PIN_WIRE1_SDA (0 + 16) // P0.16 +#define PIN_WIRE1_SCL (0 + 13) // P0.13 // QSPI Pins #define PIN_QSPI_SCK (32 + 14) diff --git a/variants/heltec_vision_master_t190/platformio.ini b/variants/heltec_vision_master_t190/platformio.ini index fd0001439..0c504d62b 100644 --- a/variants/heltec_vision_master_t190/platformio.ini +++ b/variants/heltec_vision_master_t190/platformio.ini @@ -9,5 +9,5 @@ build_flags = lib_deps = ${esp32s3_base.lib_deps} lewisxhe/PCF8563_Library@^1.0.1 - https://github.com/meshtastic/st7789#7181320e7ed05c7fb5fd2d32f14723bce6088b7b + https://github.com/meshtastic/st7789#bd33ea58ddfe4a5e4a66d53300ccbd38d66ac21f upload_speed = 921600 \ No newline at end of file diff --git a/variants/heltec_wireless_bridge/platformio.ini b/variants/heltec_wireless_bridge/platformio.ini new file mode 100644 index 000000000..45c3aba74 --- /dev/null +++ b/variants/heltec_wireless_bridge/platformio.ini @@ -0,0 +1,6 @@ +[env:heltec-wireless-bridge] +;build_type = debug ; to make it possible to step through our jtag debugger +extends = esp32_base +board = heltec_wifi_lora_32 +build_flags = + ${esp32_base.build_flags} -D HELTEC_WIRELESS_BRIDGE -I variants/heltec_wireless_bridge \ No newline at end of file diff --git a/variants/heltec_wireless_bridge/variant.h b/variants/heltec_wireless_bridge/variant.h new file mode 100644 index 000000000..7c4f41660 --- /dev/null +++ b/variants/heltec_wireless_bridge/variant.h @@ -0,0 +1,29 @@ +// the default ESP32 Pin of 15 is the Oled SCL, set to 36 and 37 and works fine. +// Tested on Neo6m module. +#undef GPS_RX_PIN +#undef GPS_TX_PIN +#define GPS_RX_PIN 36 +#define GPS_TX_PIN 33 + +#ifndef USE_JTAG // gpio15 is TDO for JTAG, so no I2C on this board while doing jtag +#define I2C_SDA 4 // I2C pins for this board +#define I2C_SCL 15 +#endif + +#define LED_PIN 25 // If defined we will blink this LED +#define BUTTON_PIN 0 // If defined, this will be used for user button presses + +#define USE_RF95 +#define LORA_DIO0 26 // a No connect on the SX1262 module +#ifndef USE_JTAG +#define LORA_RESET 14 +#endif +#define LORA_DIO1 35 +#define LORA_DIO2 34 // Not really used + +// ratio of voltage divider = 3.20 (R1=100k, R2=220k) +#define ADC_MULTIPLIER 3.2 + +#define BATTERY_PIN 13 // A battery voltage measurement pin, voltage divider connected here to measure battery voltage +#define ADC_CHANNEL ADC2_GPIO13_CHANNEL +#define BAT_MEASURE_ADC_UNIT 2 \ No newline at end of file diff --git a/variants/rpipico2/platformio.ini b/variants/rpipico2/platformio.ini new file mode 100644 index 000000000..a63414418 --- /dev/null +++ b/variants/rpipico2/platformio.ini @@ -0,0 +1,16 @@ +[env:pico2] +extends = rp2350_base +board = rpipico2 +upload_protocol = picotool + +# add our variants files to the include and src paths +build_flags = ${rp2350_base.build_flags} + -DRPI_PICO2 + -Ivariants/rpipico2 + -DDEBUG_RP2040_PORT=Serial + -DHW_SPI1_DEVICE + -L "${platformio.libdeps_dir}/${this.__env__}/bsec2/src/cortex-m0plus" +lib_deps = + ${rp2350_base.lib_deps} +debug_build_flags = ${rp2350_base.build_flags} +debug_tool = cmsis-dap ; for e.g. Picotool \ No newline at end of file diff --git a/variants/rpipico2/variant.h b/variants/rpipico2/variant.h new file mode 100644 index 000000000..7efaeaf7a --- /dev/null +++ b/variants/rpipico2/variant.h @@ -0,0 +1,50 @@ +// #define RADIOLIB_CUSTOM_ARDUINO 1 +// #define RADIOLIB_TONE_UNSUPPORTED 1 +// #define RADIOLIB_SOFTWARE_SERIAL_UNSUPPORTED 1 + +#define ARDUINO_ARCH_AVR + +// default I2C pins: +// SDA = 4 +// SCL = 5 + +// Recommended pins for SerialModule: +// txd = 8 +// rxd = 9 + +#define EXT_NOTIFY_OUT 22 +#define BUTTON_PIN 17 + +#define LED_PIN PIN_LED + +#define BATTERY_PIN 26 +// ratio of voltage divider = 3.0 (R17=200k, R18=100k) +#define ADC_MULTIPLIER 3.1 // 3.0 + a bit for being optimistic +#define BATTERY_SENSE_RESOLUTION_BITS ADC_RESOLUTION + +#define USE_SX1262 + +#undef LORA_SCK +#undef LORA_MISO +#undef LORA_MOSI +#undef LORA_CS + +#define LORA_SCK 10 +#define LORA_MISO 12 +#define LORA_MOSI 11 +#define LORA_CS 3 + +#define LORA_DIO0 RADIOLIB_NC +#define LORA_RESET 15 +#define LORA_DIO1 20 +#define LORA_DIO2 2 +#define LORA_DIO3 RADIOLIB_NC + +#ifdef USE_SX1262 +#define SX126X_CS LORA_CS +#define SX126X_DIO1 LORA_DIO1 +#define SX126X_BUSY LORA_DIO2 +#define SX126X_RESET LORA_RESET +#define SX126X_DIO2_AS_RF_SWITCH +#define SX126X_DIO3_TCXO_VOLTAGE 1.8 +#endif diff --git a/version.properties b/version.properties index 69c478482..b30827191 100644 --- a/version.properties +++ b/version.properties @@ -1,4 +1,4 @@ [VERSION] major = 2 minor = 5 -build = 1 +build = 2