diff --git a/.github/workflows/main_matrix.yml b/.github/workflows/main_matrix.yml index e5ed1a71a..fd4acbdba 100644 --- a/.github/workflows/main_matrix.yml +++ b/.github/workflows/main_matrix.yml @@ -57,12 +57,12 @@ jobs: sudo apt-get install -y cppcheck - name: Setup Python - uses: actions/setup-python@v2 + uses: actions/setup-python@v4 with: python-version: 3.x - name: Cache python libs - uses: actions/cache@v1 + uses: actions/cache@v3 id: cache-pip # needed in if test with: path: ~/.cache/pip @@ -112,12 +112,12 @@ jobs: repository: ${{github.event.pull_request.head.repo.full_name}} - name: Setup Python - uses: actions/setup-python@v2 + uses: actions/setup-python@v4 with: python-version: 3.x - name: Cache python libs - uses: actions/cache@v1 + uses: actions/cache@v3 id: cache-pip # needed in if test with: path: ~/.cache/pip @@ -157,11 +157,11 @@ jobs: token: ${{ secrets.GITHUB_TOKEN }} - name: Get release version string - run: echo "::set-output name=version::$(./bin/buildinfo.py long)" + run: echo "version=$(./bin/buildinfo.py long)" >> $GITHUB_OUTPUT id: version - name: Store binaries as an artifact - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v3 with: name: firmware-${{ matrix.board }}-${{ steps.version.outputs.version }}.zip path: | @@ -190,12 +190,12 @@ jobs: repository: ${{github.event.pull_request.head.repo.full_name}} - name: Setup Python - uses: actions/setup-python@v2 + uses: actions/setup-python@v4 with: python-version: 3.x - name: Cache python libs - uses: actions/cache@v1 + uses: actions/cache@v3 id: cache-pip # needed in if test with: path: ~/.cache/pip @@ -214,16 +214,17 @@ jobs: run: bin/build-nrf52.sh ${{ matrix.board }} - name: Get release version string - run: echo "::set-output name=version::$(./bin/buildinfo.py long)" + run: echo "version=$(./bin/buildinfo.py long)" >> $GITHUB_OUTPUT id: version - name: Store binaries as an artifact - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v3 with: name: firmware-${{ matrix.board }}-${{ steps.version.outputs.version }}.zip path: | release/*.uf2 release/*.elf + release/*.zip retention-days: 90 build-rpi2040: @@ -244,12 +245,12 @@ jobs: repository: ${{github.event.pull_request.head.repo.full_name}} - name: Setup Python - uses: actions/setup-python@v2 + uses: actions/setup-python@v4 with: python-version: 3.x - name: Cache python libs - uses: actions/cache@v1 + uses: actions/cache@v3 id: cache-pip # needed in if test with: path: ~/.cache/pip @@ -268,11 +269,11 @@ jobs: run: ./bin/build-rpi2040.sh ${{ matrix.board }} - name: Get release version string - run: echo "::set-output name=version::$(./bin/buildinfo.py long)" + run: echo "version=$(./bin/buildinfo.py long)" >> $GITHUB_OUTPUT id: version - name: Store binaries as an artifact - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v3 with: name: firmware-${{ matrix.board }}-${{ steps.version.outputs.version }}.zip path: | @@ -291,12 +292,12 @@ jobs: repository: ${{github.event.pull_request.head.repo.full_name}} - name: Setup Python - uses: actions/setup-python@v2 + uses: actions/setup-python@v4 with: python-version: 3.x - name: Cache python libs - uses: actions/cache@v1 + uses: actions/cache@v3 id: cache-pip # needed in if test with: path: ~/.cache/pip @@ -326,11 +327,11 @@ jobs: run: bin/build-native.sh - name: Get release version string - run: echo "::set-output name=version::$(./bin/buildinfo.py long)" + run: echo "version=$(./bin/buildinfo.py long)" >> $GITHUB_OUTPUT id: version - name: Store binaries as an artifact - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v3 with: name: firmware-native-${{ steps.version.outputs.version }}.zip path: | @@ -359,30 +360,31 @@ jobs: ref: ${{github.event.pull_request.head.ref}} repository: ${{github.event.pull_request.head.repo.full_name}} - - uses: actions/download-artifact@v2 + - uses: actions/download-artifact@v3 with: path: ./ - name: Get release version string - run: echo "::set-output name=version::$(./bin/buildinfo.py long)" + run: echo "version=$(./bin/buildinfo.py long)" >> $GITHUB_OUTPUT id: version - name: Move files up - run: mv -b -t ./ ./*tbeam-1*/littlefs*.bin ./*tbeam-1*/bleota.bin ./**/firmware*.bin ./*t-echo*/Meshtastic_nRF52_factory_erase.uf2 ./**/firmware-*.uf2 ./**/*.elf ./**/meshtasticd_linux_amd64 ./*native*/*device-*.sh ./*native*/*device-*.bat + run: mv -b -t ./ ./*tbeam-1*/littlefs*.bin ./*tbeam-1*/bleota.bin ./**/firmware*.bin ./*t-echo*/Meshtastic_nRF52_factory_erase.uf2 ./**/firmware-*.uf2 ./**/firmware-*-ota.zip ./**/*.elf ./**/meshtasticd_linux_amd64 ./*native*/*device-*.sh ./*native*/*device-*.bat - name: Repackage in single firmware zip - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v3 with: name: firmware-${{ steps.version.outputs.version }} path: | ./*.bin ./*.uf2 + ./firmware-*-ota.zip ./meshtasticd_linux_amd64 ./device-*.sh ./device-*.bat retention-days: 90 - - uses: actions/download-artifact@v2 + - uses: actions/download-artifact@v3 with: name: firmware-${{ steps.version.outputs.version }} path: ./output @@ -400,7 +402,7 @@ jobs: run: zip -j -r ./firmware-${{ steps.version.outputs.version }}.zip ./output - name: Repackage in single elfs zip - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v3 with: name: debug-elfs-${{ steps.version.outputs.version }}.zip path: ./*.elf @@ -424,18 +426,18 @@ jobs: needs: [gather-artifacts, after-checks] steps: - name: Checkout - uses: actions/checkout@v2 + uses: actions/checkout@v3 - name: Setup Python - uses: actions/setup-python@v2 + uses: actions/setup-python@v4 with: python-version: 3.x - name: Get release version string - run: echo "::set-output name=version::$(./bin/buildinfo.py long)" + run: echo "version=$(./bin/buildinfo.py long)" >> $GITHUB_OUTPUT id: version - - uses: actions/download-artifact@v2 + - uses: actions/download-artifact@v3 with: name: firmware-${{ steps.version.outputs.version }} path: ./output @@ -448,7 +450,7 @@ jobs: - name: Zip firmware run: zip -j -r ./firmware-${{ steps.version.outputs.version }}.zip ./output - - uses: actions/download-artifact@v2 + - uses: actions/download-artifact@v3 with: name: debug-elfs-${{ steps.version.outputs.version }}.zip path: ./elfs @@ -466,7 +468,7 @@ jobs: with: draft: true prerelease: true - release_name: Meshtastic Device ${{ steps.version.outputs.version }} alpha - Public Preview + release_name: Meshtastic Device ${{ steps.version.outputs.version }} Alpha tag_name: v${{ steps.version.outputs.version }} body: | Autogenerated by github action, developer should edit as required before publishing... diff --git a/arch/esp32/esp32.ini b/arch/esp32/esp32.ini index d2e2fd9c5..798fb3d5a 100644 --- a/arch/esp32/esp32.ini +++ b/arch/esp32/esp32.ini @@ -1,7 +1,7 @@ ; Common settings for ESP targes, mixin with extends = esp32_base [esp32_base] extends = arduino_base -platform = espressif32@^5.2.0 +platform = platformio/espressif32@^5.2.0 build_src_filter = ${arduino_base.build_src_filter} - - - upload_speed = 921600 diff --git a/arch/esp32/esp32s3.ini b/arch/esp32/esp32s3.ini index e19b9aaf7..8dc6d0b62 100644 --- a/arch/esp32/esp32s3.ini +++ b/arch/esp32/esp32s3.ini @@ -1,6 +1,6 @@ [esp32s3_base] extends = arduino_base -platform = espressif32@^5.2.0 +platform = platformio/espressif32@^5.2.0 build_src_filter = ${arduino_base.build_src_filter} - - - upload_speed = 961200 diff --git a/arch/nrf52/nrf52.ini b/arch/nrf52/nrf52.ini index a79ebda48..f4e2af236 100644 --- a/arch/nrf52/nrf52.ini +++ b/arch/nrf52/nrf52.ini @@ -1,11 +1,9 @@ [nrf52_base] ; Instead of the standard nordicnrf52 platform, we use our fork which has our added variant files -; platform = nordicnrf52 ;pending https://github.com/platformio/builder-framework-arduino-nrf5/pull/7 -platform = https://github.com/meshtastic/platform-nordicnrf52.git#merge +platform = platformio/nordicnrf52@^9.4.0 extends = arduino_base build_type = debug ; I'm debugging with ICE a lot now -; note: liboberon provides the AES256 implementation for NRF52 (though not using the hardware acceleration of the NRF52840 - FIXME) build_flags = ${arduino_base.build_flags} -Wno-unused-variable -Isrc/platform/nrf52 diff --git a/arch/nrf52/nrf52840.ini b/arch/nrf52/nrf52840.ini index 29c32e795..14cc1d1e1 100644 --- a/arch/nrf52/nrf52840.ini +++ b/arch/nrf52/nrf52840.ini @@ -4,7 +4,7 @@ build_flags = ${nrf52_base.build_flags} lib_deps = ${arduino_base.lib_deps} ${environmental_base.lib_deps} - https://github.com/Kongduino/Adafruit_nRFCrypto.git#20fc7fdaf086bd70e901c007dd23c6e8856aec25 + https://github.com/Kongduino/Adafruit_nRFCrypto.git#e31a8825ea3300b163a0a3c1ddd5de34e10e1371 ; Note: By default no lora device is created for this build - it uses a simulated interface [env:nrf52840dk] diff --git a/arch/rp2040/rp2040.ini b/arch/rp2040/rp2040.ini index 6660cccd2..9eea340bf 100644 --- a/arch/rp2040/rp2040.ini +++ b/arch/rp2040/rp2040.ini @@ -1,6 +1,6 @@ ; Common settings for rp2040 Processor based targets [rp2040_base] -platform = https://github.com/maxgerhardt/platform-raspberrypi.git +platform = https://github.com/maxgerhardt/platform-raspberrypi.git#5ce1a228e7cae453f366deb8962252b9b7356bbc extends = arduino_base board_build.core = earlephilhower board_build.filesystem_size = 0.5m diff --git a/arch/stm32/stm32wl5e.ini b/arch/stm32/stm32wl5e.ini index fc3f42ba7..d13750fdb 100644 --- a/arch/stm32/stm32wl5e.ini +++ b/arch/stm32/stm32wl5e.ini @@ -1,5 +1,5 @@ [stm32wl5e_base] -platform = ststm32 +platform = platformio/ststm32@^15.4.1 board = generic_wl5e framework = arduino build_type = debug diff --git a/bin/build-nrf52.sh b/bin/build-nrf52.sh index 2202564e7..76972b100 100755 --- a/bin/build-nrf52.sh +++ b/bin/build-nrf52.sh @@ -26,7 +26,9 @@ basename=firmware-$1-$VERSION pio run --environment $1 # -v SRCELF=.pio/build/$1/firmware.elf +DFUPKG=.pio/build/$1/firmware.zip cp $SRCELF $OUTDIR/$basename.elf +cp $DFUPKG $OUTDIR/$basename-ota.zip echo "Generating NRF52 uf2 file" SRCHEX=.pio/build/$1/firmware.hex diff --git a/platformio.ini b/platformio.ini index 36ffc27fe..1f97cfb19 100644 --- a/platformio.ini +++ b/platformio.ini @@ -2,7 +2,8 @@ ; https://docs.platformio.org/page/projectconf.html [platformio] -default_envs = tbeam +;default_envs = tbeam +;default_envs = pico ;default_envs = tbeam-s3-core ;default_envs = tbeam0.7 ;default_envs = heltec-v1 @@ -56,6 +57,9 @@ lib_deps = ; Used for the code analysis in PIO Home / Inspect check_tool = cppcheck check_skip_packages = yes +check_flags = + --common-flag + cppcheck: --enable=--inline-suppr ; Common settings for conventional (non Portduino) Arduino targets [arduino_base] @@ -64,6 +68,7 @@ lib_deps = ${env.lib_deps} ; Portduino is using meshtastic fork for now jgromes/RadioLib@5.4.1 + https://github.com/caveman99/SparkFun_ATECCX08a_Arduino_Library.git#008e7f9d40bad66b2f7a0074aaac05b7c424339d build_flags = ${env.build_flags} -Os -DRADIOLIB_SPI_PARANOID=0 @@ -88,4 +93,5 @@ lib_deps = adafruit/Adafruit MCP9808 Library@^2.0.0 adafruit/Adafruit INA260 Library@^1.5.0 adafruit/Adafruit INA219@^1.2.0 - + adafruit/Adafruit SHTC3 Library@^1.0.0 + adafruit/Adafruit LPS2X@^2.0.4 \ No newline at end of file diff --git a/protobufs b/protobufs index 256f11954..d0559bfa3 160000 --- a/protobufs +++ b/protobufs @@ -1 +1 @@ -Subproject commit 256f11954b7c8fcb9dc4481771c24559925bf32a +Subproject commit d0559bfa3c31023ed2f2aa3807b6a0a1da9a6feb diff --git a/src/BluetoothCommon.cpp b/src/BluetoothCommon.cpp index 0eda2080e..728b6f360 100644 --- a/src/BluetoothCommon.cpp +++ b/src/BluetoothCommon.cpp @@ -7,7 +7,7 @@ const uint8_t MESH_SERVICE_UUID_16[16u] = {0xfd, 0xea, 0x73, 0xe2, 0xca, 0x5d, 0 0x1f, 0x46, 0xa8, 0x15, 0x18, 0xb2, 0xa1, 0x6b}; const uint8_t TORADIO_UUID_16[16u] = {0xe7, 0x01, 0x44, 0x12, 0x66, 0x78, 0xdd, 0xa1, 0xad, 0x4d, 0x9e, 0x12, 0xd2, 0x76, 0x5c, 0xf7}; -const uint8_t FROMRADIO_UUID_16[16u] = {0xd5, 0x54, 0xe4, 0xc5, 0x25, 0xc5, 0x31, 0xa5, - 0x55, 0x4a, 0x02, 0xee, 0xc2, 0xbc, 0xa2, 0x8b}; +const uint8_t FROMRADIO_UUID_16[16u] = {0x02, 0x00, 0x12, 0xac, 0x42, 0x02, 0x78, 0xb8, + 0xed, 0x11, 0x93, 0x49, 0x9e, 0xe6, 0x55, 0x2c}; const uint8_t FROMNUM_UUID_16[16u] = {0x53, 0x44, 0xe3, 0x47, 0x75, 0xaa, 0x70, 0xa6, 0x66, 0x4f, 0x00, 0xa8, 0x8c, 0xa1, 0x9d, 0xed}; \ No newline at end of file diff --git a/src/BluetoothCommon.h b/src/BluetoothCommon.h index 26c7b2cef..4352eba13 100644 --- a/src/BluetoothCommon.h +++ b/src/BluetoothCommon.h @@ -9,7 +9,7 @@ #define MESH_SERVICE_UUID "6ba1b218-15a8-461f-9fa8-5dcae273eafd" #define TORADIO_UUID "f75c76d2-129e-4dad-a1dd-7866124401e7" -#define FROMRADIO_UUID "8ba2bcc2-ee02-4a55-a531-c525c5e454d5" +#define FROMRADIO_UUID "2c55e69e-4993-11ed-b878-0242ac120002" #define FROMNUM_UUID "ed9da18c-a800-4f66-a670-aa7547e34453" // NRF52 wants these constants as byte arrays diff --git a/src/RedirectablePrint.cpp b/src/RedirectablePrint.cpp index d1972d2ad..de9b95027 100644 --- a/src/RedirectablePrint.cpp +++ b/src/RedirectablePrint.cpp @@ -49,6 +49,13 @@ size_t RedirectablePrint::vprintf(const char *format, va_list arg) if (len < 0) return 0; + // If the resulting string is longer than sizeof(printBuf)-1 characters, the remaining characters are still counted for the return value + + if (len > sizeof(printBuf) - 1) { + len = sizeof(printBuf) - 1; + printBuf[sizeof(printBuf) - 2] = '\n'; + } + len = Print::write(printBuf, len); return len; } @@ -103,4 +110,4 @@ size_t RedirectablePrint::logDebug(const char *format, ...) } return r; -} \ No newline at end of file +} diff --git a/src/configuration.h b/src/configuration.h index 524dbec36..9374c29d4 100644 --- a/src/configuration.h +++ b/src/configuration.h @@ -110,6 +110,16 @@ along with this program. If not, see . #define INA_ADDR_ALTERNATE 0x41 #define QMC6310_ADDR 0x1C #define QMI8658_ADDR 0x6B +#define SHTC3_ADDR 0x70 +#define LPS22HB_ADDR 0x5C +#define LPS22HB_ADDR_ALT 0x5D + +// ----------------------------------------------------------------------------- +// Security +// ----------------------------------------------------------------------------- + +#define ATECC608B_ADDR 0x35 + // ----------------------------------------------------------------------------- // GPS // ----------------------------------------------------------------------------- diff --git a/src/detect/i2cScan.h b/src/detect/i2cScan.h index 3adb44277..6fc9d67d3 100644 --- a/src/detect/i2cScan.h +++ b/src/detect/i2cScan.h @@ -9,6 +9,41 @@ #endif #if HAS_WIRE + +void printATECCInfo() +{ +#ifndef ARCH_PORTDUINO + atecc.readConfigZone(false); + + DEBUG_MSG("ATECC608B Serial Number: "); + for (int i = 0 ; i < 9 ; i++) { + DEBUG_MSG("%02x",atecc.serialNumber[i]); + } + + DEBUG_MSG(", Rev Number: "); + for (int i = 0 ; i < 4 ; i++) { + DEBUG_MSG("%02x",atecc.revisionNumber[i]); + } + DEBUG_MSG("\n"); + + DEBUG_MSG("ATECC608B Config %s",atecc.configLockStatus ? "Locked" : "Unlocked"); + DEBUG_MSG(", Data %s",atecc.dataOTPLockStatus ? "Locked" : "Unlocked"); + DEBUG_MSG(", Slot 0 %s\n",atecc.slot0LockStatus ? "Locked" : "Unlocked"); + + if (atecc.configLockStatus && atecc.dataOTPLockStatus && atecc.slot0LockStatus) { + if (atecc.generatePublicKey() == false) { + DEBUG_MSG("ATECC608B Error generating public key\n"); + } else { + DEBUG_MSG("ATECC608B Public Key: "); + for (int i = 0 ; i < 64 ; i++) { + DEBUG_MSG("%02x",atecc.publicKey64Bytes[i]); + } + DEBUG_MSG("\n"); + } + } +#endif +} + uint16_t getRegisterValue(uint8_t address, uint8_t reg, uint8_t length) { uint16_t value = 0x00; Wire.beginTransmission(address); @@ -79,6 +114,17 @@ void scanI2Cdevice(void) DEBUG_MSG("unknown display found\n"); } } +#ifndef ARCH_PORTDUINO + if (addr == ATECC608B_ADDR){ + keystore_found = addr; + if (atecc.begin(keystore_found) == true) { + DEBUG_MSG("ATECC608B initialized\n"); + } else { + DEBUG_MSG("ATECC608B initialization failed\n"); + } + printATECCInfo(); + } +#endif #ifdef RV3028_RTC if (addr == RV3028_RTC){ rtc_found = addr; @@ -145,14 +191,22 @@ void scanI2Cdevice(void) nodeTelemetrySensorsMap[TelemetrySensorType_MCP9808] = addr; DEBUG_MSG("MCP9808 sensor found at address 0x%x\n", (uint8_t)addr); } - if(addr == QMC6310_ADDR){ + if (addr == QMC6310_ADDR) { DEBUG_MSG("QMC6310 3-Axis magnetic sensor found at address 0x%x\n", (uint8_t)addr); nodeTelemetrySensorsMap[TelemetrySensorType_QMC6310] = addr; } - if(addr == QMI8658_ADDR){ + if (addr == QMI8658_ADDR) { DEBUG_MSG("QMI8658 6-Axis inertial measurement sensor found at address 0x%x\n", (uint8_t)addr); nodeTelemetrySensorsMap[TelemetrySensorType_QMI8658] = addr; } + if (addr == SHTC3_ADDR) { + DEBUG_MSG("SHTC3 sensor found at address 0x%x\n", (uint8_t)addr); + nodeTelemetrySensorsMap[TelemetrySensorType_SHTC3] = addr; + } + if (addr == LPS22HB_ADDR || addr == LPS22HB_ADDR_ALT) { + DEBUG_MSG("LPS22HB sensor found at address 0x%x\n", (uint8_t)addr); + nodeTelemetrySensorsMap[TelemetrySensorType_LPS22] = addr; + } } else if (err == 4) { DEBUG_MSG("Unknow error at address 0x%x\n", addr); } diff --git a/src/gps/NMEAWPL.cpp b/src/gps/NMEAWPL.cpp new file mode 100644 index 000000000..95e69343b --- /dev/null +++ b/src/gps/NMEAWPL.cpp @@ -0,0 +1,76 @@ +#include "NMEAWPL.h" + +/* ------------------------------------------- + * 1 2 3 4 5 6 + * | | | | | | + * $--WPL,llll.ll,a,yyyyy.yy,a,c--c*hh + * + * Field Number: + * 1 Latitude + * 2 N or S (North or South) + * 3 Longitude + * 4 E or W (East or West) + * 5 Waypoint name + * 6 Checksum + * ------------------------------------------- + */ + +uint printWPL(char *buf, const Position &pos, const char *name) +{ + uint len = sprintf(buf, "$GNWPL,%07.2f,%c,%08.2f,%c,%s", pos.latitude_i * 1e-5, pos.latitude_i < 0 ? 'S' : 'N', pos.longitude_i * 1e-5, pos.longitude_i < 0 ? 'W' : 'E', name); + uint chk = 0; + for (uint i = 1; i < len; i++) { + chk ^= buf[i]; + } + len += sprintf(buf + len, "*%02X\r\n", chk); + return len; +} + +/* ------------------------------------------- + * 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 + * | | | | | | | | | | | | | | | + * $--GGA,hhmmss.ss,ddmm.mm,a,ddmm.mm,a,x,xx,x.x,x.x,M,x.x,M,x.x,xxxx*hh + * + * Field Number: + * 1 UTC of this position report, hh is hours, mm is minutes, ss.ss is seconds. + * 2 Latitude + * 3 N or S (North or South) + * 4 Longitude + * 5 E or W (East or West) + * 6 GPS Quality Indicator (non null) + * 7 Number of satellites in use, 00 - 12 + * 8 Horizontal Dilution of precision (meters) + * 9 Antenna Altitude above/below mean-sea-level (geoid) (in meters) + * 10 Units of antenna altitude, meters + * 11 Geoidal separation, the difference between the WGS-84 earth ellipsoid and mean-sea-level (geoid), "-" means mean-sea-level below ellipsoid + * 12 Units of geoidal separation, meters + * 13 Age of differential GPS data, time in seconds since last SC104 type 1 or 9 update, null field when DGPS is not used + * 14 Differential reference station ID, 0000-1023 + * 15 Checksum + * ------------------------------------------- + */ + +uint printGGA(char *buf, const Position &pos) +{ + uint len = sprintf(buf, "$GNGGA,%06u.%03u,%07.2f,%c,%08.2f,%c,%u,%02u,%04u,%04d,%c,%04d,%c,%d,%04d", + pos.time / 1000, + pos.time % 1000, + pos.latitude_i * 1e-5, pos.latitude_i < 0 ? 'S' : 'N', + pos.longitude_i * 1e-5, pos.longitude_i < 0 ? 'W' : 'E', + pos.fix_type, + pos.sats_in_view, + pos.HDOP, + pos.altitude, + 'M', + pos.altitude_geoidal_separation, + 'M', + 0, + 0); + + uint chk = 0; + for (uint i = 1; i < len; i++) { + chk ^= buf[i]; + } + len += sprintf(buf + len, "*%02X\r\n", chk); + return len; +} \ No newline at end of file diff --git a/src/gps/NMEAWPL.h b/src/gps/NMEAWPL.h new file mode 100644 index 000000000..853c850eb --- /dev/null +++ b/src/gps/NMEAWPL.h @@ -0,0 +1,7 @@ +#pragma once + +#include +#include "main.h" + +uint printWPL(char *buf, const Position &pos, const char *name); +uint printGGA(char *buf, const Position &pos); diff --git a/src/gps/RTC.h b/src/gps/RTC.h index f2df3c757..527b31f46 100644 --- a/src/gps/RTC.h +++ b/src/gps/RTC.h @@ -16,7 +16,7 @@ enum RTCQuality { RTCQualityFromNet = 2, /// Our time is based on NTP - RTCQualityNTP= 3, + RTCQualityNTP = 3, /// Our time is based on our own GPS RTCQualityGPS = 4 diff --git a/src/graphics/Screen.cpp b/src/graphics/Screen.cpp index 601b83f9c..0f6c96f0f 100644 --- a/src/graphics/Screen.cpp +++ b/src/graphics/Screen.cpp @@ -84,10 +84,6 @@ static char ourId[5]; // GeoCoord object for the screen GeoCoord geoCoord; -// OEM Config File -static const char *oemConfigFile = "/oem/oem.proto"; -OEMStore oemStore; - #ifdef SHOW_REDRAWS static bool heartbeat = false; #endif @@ -928,9 +924,6 @@ void Screen::setup() dispdev.setDetected(screen_model); #endif - // Load OEM config from Proto file if existent - loadProto(oemConfigFile, OEMStore_size, sizeof(oemConfigFile), OEMStore_fields, &oemStore); - // Initialising the UI will init the display too. ui.init(); diff --git a/src/main.cpp b/src/main.cpp index c9ed47818..d4d76cc3b 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -45,6 +45,7 @@ #include "RF95Interface.h" #include "SX1262Interface.h" #include "SX1268Interface.h" +#include "SX1281Interface.h" #if !HAS_RADIO && defined(ARCH_PORTDUINO) #include "platform/portduino/SimRadio.h" #endif @@ -80,6 +81,14 @@ uint8_t kb_model; // The I2C address of the RTC Module (if found) uint8_t rtc_found; +bool rIf_wide_lora = false; + +// Keystore Chips +uint8_t keystore_found; +#ifndef ARCH_PORTDUINO +ATECCX08A atecc; +#endif + bool eink_found = true; uint32_t serialSinceMsec; @@ -279,12 +288,10 @@ void setup() power->setup(); // Must be after status handler is installed, so that handler gets notified of the initial configuration /* - * Repeat the scanning for I2C devices after power initialization. + * Repeat the scanning for I2C devices after power initialization or look for 'latecomers'. * Boards with an PMU need to be powered on to correctly scan to the device address, such as t-beam-s3-core */ - if ((HW_VENDOR == HardwareModel_LILYGO_TBEAM_S3_CORE) || (HW_VENDOR == HardwareModel_TBEAM)) { - scanI2Cdevice(); - } + scanI2Cdevice(); // Init our SPI controller (must be before screen and lora) initSPI(); @@ -364,6 +371,20 @@ void setup() } #endif +#if defined(USE_SX1281) && !defined(ARCH_PORTDUINO) + if (!rIf) { + rIf = new SX1281Interface(SX126X_CS, SX126X_DIO1, SX126X_RESET, SX126X_BUSY, SPI); + if (!rIf->init()) { + DEBUG_MSG("Warning: Failed to find SX1281 radio\n"); + delete rIf; + rIf = NULL; + } else { + DEBUG_MSG("SX1281 Radio init succeeded, using SX1281 radio\n"); + rIf_wide_lora = true; + } + } +#endif + #if defined(USE_SX1262) if (!rIf) { rIf = new SX1262Interface(SX126X_CS, SX126X_DIO1, SX126X_RESET, SX126X_BUSY, SPI); diff --git a/src/main.h b/src/main.h index f3f9c62c9..bd24dec76 100644 --- a/src/main.h +++ b/src/main.h @@ -6,18 +6,27 @@ #include "PowerStatus.h" #include "graphics/Screen.h" #include "mesh/generated/telemetry.pb.h" +#ifndef ARCH_PORTDUINO +#include +#endif extern uint8_t screen_found; extern uint8_t screen_model; extern uint8_t cardkb_found; extern uint8_t kb_model; extern uint8_t rtc_found; +extern uint8_t keystore_found; +extern bool rIf_wide_lora; extern bool eink_found; extern bool pmu_found; extern bool isCharging; extern bool isUSBPowered; +#ifndef ARCH_PORTDUINO +extern ATECCX08A atecc; +#endif + extern uint8_t nodeTelemetrySensorsMap[TelemetrySensorType_QMI8658+1]; extern int TCPPort; // set by Portduino diff --git a/src/mesh/Channels.cpp b/src/mesh/Channels.cpp index a87d66b14..200184720 100644 --- a/src/mesh/Channels.cpp +++ b/src/mesh/Channels.cpp @@ -62,13 +62,6 @@ Channel &Channels::fixupChannel(ChannelIndex chIndex) // Convert the old string "Default" to our new short representation if (strcmp(channelSettings.name, "Default") == 0) *channelSettings.name = '\0'; - - /* Convert any old usage of the defaultpsk into our new short representation. - if (channelSettings.psk.size == sizeof(defaultpsk) && - memcmp(channelSettings.psk.bytes, defaultpsk, sizeof(defaultpsk)) == 0) { - *channelSettings.psk.bytes = 1; - channelSettings.psk.size = 1; - } */ } hashes[chIndex] = generateHash(chIndex); @@ -124,7 +117,22 @@ CryptoKey Channels::getKey(ChannelIndex chIndex) DEBUG_MSG("Expanding short PSK #%d\n", pskIndex); if (pskIndex == 0) k.length = 0; // Turn off encryption - else { + else if (oemStore.oem_aes_key.size > 1) { + // Use the OEM key + DEBUG_MSG("Using OEM Key with %d bytes\n", oemStore.oem_aes_key.size); + memcpy(k.bytes, oemStore.oem_aes_key.bytes , oemStore.oem_aes_key.size); + k.length = oemStore.oem_aes_key.size; + // Bump up the last byte of PSK as needed + uint8_t *last = k.bytes + oemStore.oem_aes_key.size - 1; + *last = *last + pskIndex - 1; // index of 1 means no change vs defaultPSK + if (k.length < 16) { + DEBUG_MSG("Warning: OEM provided a too short AES128 key - padding\n"); + k.length = 16; + } else if (k.length < 32 && k.length != 16) { + DEBUG_MSG("Warning: OEM provided a too short AES256 key - padding\n"); + k.length = 32; + } + } else { memcpy(k.bytes, defaultpsk, sizeof(defaultpsk)); k.length = sizeof(defaultpsk); // Bump up the last byte of PSK as needed diff --git a/src/mesh/InterfacesTemplates.cpp b/src/mesh/InterfacesTemplates.cpp index 2c8d9b290..6707813db 100644 --- a/src/mesh/InterfacesTemplates.cpp +++ b/src/mesh/InterfacesTemplates.cpp @@ -1,7 +1,13 @@ #include "SX126xInterface.h" #include "SX126xInterface.cpp" +#include "SX128xInterface.h" +#include "SX128xInterface.cpp" // We need this declaration for proper linking in derived classes template class SX126xInterface; template class SX126xInterface; -template class SX126xInterface; \ No newline at end of file +template class SX126xInterface; + +#if !defined(ARCH_PORTDUINO) +template class SX128xInterface; +#endif \ No newline at end of file diff --git a/src/mesh/MeshModule.cpp b/src/mesh/MeshModule.cpp index 63f2a4358..7b204ae49 100644 --- a/src/mesh/MeshModule.cpp +++ b/src/mesh/MeshModule.cpp @@ -112,7 +112,7 @@ void MeshModule::callPlugins(const MeshPacket &mp, RxSource src) bool rxChannelOk = !pi.boundChannel || (mp.from == 0) || !ch || strlen(ch->settings.name) > 0 || - (strcmp(ch->settings.name, pi.boundChannel) == 0); + (strcasecmp(ch->settings.name, pi.boundChannel) == 0); if (!rxChannelOk) { // no one should have already replied! diff --git a/src/mesh/MeshRadio.h b/src/mesh/MeshRadio.h index 961d62192..2cb7f4e6d 100644 --- a/src/mesh/MeshRadio.h +++ b/src/mesh/MeshRadio.h @@ -15,6 +15,7 @@ struct RegionInfo { uint8_t powerLimit; // Or zero for not set bool audioPermitted; bool freqSwitching; + bool wideLora; const char *name; // EU433 etc }; diff --git a/src/mesh/MeshService.cpp b/src/mesh/MeshService.cpp index 31d26b625..d5c7c4a19 100644 --- a/src/mesh/MeshService.cpp +++ b/src/mesh/MeshService.cpp @@ -283,3 +283,8 @@ int MeshService::onGPSChanged(const meshtastic::GPSStatus *newStatus) return 0; } + +bool MeshService::isToPhoneQueueEmpty() +{ + return toPhoneQueue.isEmpty(); +} \ No newline at end of file diff --git a/src/mesh/MeshService.h b/src/mesh/MeshService.h index ffa8c06ec..24ec1c0bc 100644 --- a/src/mesh/MeshService.h +++ b/src/mesh/MeshService.h @@ -89,6 +89,8 @@ class MeshService /// Send a packet to the phone void sendToPhone(MeshPacket *p); + bool isToPhoneQueueEmpty(); + private: /// Called when our gps position has changed - updates nodedb and sends Location message out into the mesh /// returns 0 to allow futher processing diff --git a/src/mesh/NodeDB.cpp b/src/mesh/NodeDB.cpp index 92115d127..8a7ee6086 100644 --- a/src/mesh/NodeDB.cpp +++ b/src/mesh/NodeDB.cpp @@ -38,6 +38,7 @@ MyNodeInfo &myNodeInfo = devicestate.my_node; LocalConfig config; LocalModuleConfig moduleConfig; ChannelFile channelFile; +OEMStore oemStore; /** The current change # for radio settings. Starts at 0 on boot and any time the radio settings * might have changed is incremented. Allows others to detect they might now be on a new channel. @@ -129,6 +130,8 @@ bool NodeDB::factoryReset() // second, install default state (this will deal with the duplicate mac address issue) installDefaultDeviceState(); installDefaultConfig(); + installDefaultModuleConfig(); + installDefaultChannels(); // third, write everything to disk saveToDisk(); #ifdef ARCH_ESP32 @@ -354,6 +357,8 @@ static const char *prefFileName = "/prefs/db.proto"; static const char *configFileName = "/prefs/config.proto"; static const char *moduleConfigFileName = "/prefs/module.proto"; static const char *channelFileName = "/prefs/channels.proto"; +static const char *oemConfigFile = "/oem/oem.proto"; + /** Load a protobuf from a file, return true for success */ bool loadProto(const char *filename, size_t protoSize, size_t objSize, const pb_msgdesc_t *fields, void *dest_struct) @@ -433,6 +438,9 @@ void NodeDB::loadFromDisk() DEBUG_MSG("Loaded saved channelFile version %d\n", channelFile.version); } } + + if (loadProto(oemConfigFile, OEMStore_size, sizeof(OEMStore), OEMStore_fields, &oemStore)) + DEBUG_MSG("Loaded OEMStore\n"); } /** Save a protobuf from a file, return true for success */ diff --git a/src/mesh/NodeDB.h b/src/mesh/NodeDB.h index 574d6fe54..6b9744565 100644 --- a/src/mesh/NodeDB.h +++ b/src/mesh/NodeDB.h @@ -26,6 +26,7 @@ extern ChannelFile channelFile; extern MyNodeInfo &myNodeInfo; extern LocalConfig config; extern LocalModuleConfig moduleConfig; +extern OEMStore oemStore; extern User &owner; /// Given a node, return how many seconds in the past (vs now) that we last heard from it diff --git a/src/mesh/PhoneAPI.cpp b/src/mesh/PhoneAPI.cpp index a725e6283..2f060889a 100644 --- a/src/mesh/PhoneAPI.cpp +++ b/src/mesh/PhoneAPI.cpp @@ -79,17 +79,17 @@ bool PhoneAPI::handleToRadio(const uint8_t *buf, size_t bufLength) memset(&toRadioScratch, 0, sizeof(toRadioScratch)); if (pb_decode_from_bytes(buf, bufLength, ToRadio_fields, &toRadioScratch)) { switch (toRadioScratch.which_payload_variant) { - case ToRadio_packet_tag: - return handleToRadioPacket(toRadioScratch.packet); - case ToRadio_want_config_id_tag: - config_nonce = toRadioScratch.want_config_id; - DEBUG_MSG("Client wants config, nonce=%u\n", config_nonce); - handleStartConfig(); - break; - case ToRadio_disconnect_tag: - DEBUG_MSG("Disconnecting from phone\n"); - close(); - break; + case ToRadio_packet_tag: + return handleToRadioPacket(toRadioScratch.packet); + case ToRadio_want_config_id_tag: + config_nonce = toRadioScratch.want_config_id; + DEBUG_MSG("Client wants config, nonce=%u\n", config_nonce); + handleStartConfig(); + break; + case ToRadio_disconnect_tag: + DEBUG_MSG("Disconnecting from phone\n"); + close(); + break; default: // Ignore nop messages // DEBUG_MSG("Error: unexpected ToRadio variant\n"); @@ -109,8 +109,10 @@ bool PhoneAPI::handleToRadio(const uint8_t *buf, size_t bufLength) * * Our sending states progress in the following sequence (the client app ASSUMES THIS SEQUENCE, DO NOT CHANGE IT): * STATE_SEND_MY_INFO, // send our my info record + * STATE_SEND_CHANNELS * STATE_SEND_NODEINFO, // states progress in this order as the device sends to the client STATE_SEND_CONFIG, + STATE_SEND_MODULE_CONFIG, STATE_SEND_COMPLETE_ID, STATE_SEND_PACKETS // send packets or debug strings */ @@ -128,7 +130,7 @@ size_t PhoneAPI::getFromRadio(uint8_t *buf) case STATE_SEND_NOTHING: DEBUG_MSG("getFromRadio=STATE_SEND_NOTHING\n"); break; - + case STATE_SEND_MY_INFO: DEBUG_MSG("getFromRadio=STATE_SEND_MY_INFO\n"); // If the user has specified they don't want our node to share its location, make sure to tell the phone @@ -154,51 +156,63 @@ size_t PhoneAPI::getFromRadio(uint8_t *buf) // Stay in current state until done sending nodeinfos } else { DEBUG_MSG("Done sending nodeinfos\n"); - state = STATE_SEND_CONFIG; + state = STATE_SEND_CHANNELS; // Go ahead and send that ID right now return getFromRadio(buf); } break; } + case STATE_SEND_CHANNELS: + DEBUG_MSG("getFromRadio=STATE_SEND_CHANNELS\n"); + fromRadioScratch.which_payload_variant = FromRadio_channel_tag; + fromRadioScratch.channel = channels.getByIndex(config_state); + config_state++; + // Advance when we have sent all of our Channels + if (config_state >= MAX_NUM_CHANNELS) { + state = STATE_SEND_CONFIG; + config_state = Config_device_tag; + } + break; + case STATE_SEND_CONFIG: DEBUG_MSG("getFromRadio=STATE_SEND_CONFIG\n"); fromRadioScratch.which_payload_variant = FromRadio_config_tag; switch (config_state) { - case Config_device_tag: - fromRadioScratch.config.which_payload_variant = Config_device_tag; - fromRadioScratch.config.payload_variant.device = config.device; - break; - case Config_position_tag: - fromRadioScratch.config.which_payload_variant = Config_position_tag; - fromRadioScratch.config.payload_variant.position = config.position; - break; - case Config_power_tag: - fromRadioScratch.config.which_payload_variant = Config_power_tag; - fromRadioScratch.config.payload_variant.power = config.power; - fromRadioScratch.config.payload_variant.power.ls_secs = default_ls_secs; - break; - case Config_network_tag: - fromRadioScratch.config.which_payload_variant = Config_network_tag; - fromRadioScratch.config.payload_variant.network = config.network; - break; - case Config_display_tag: - fromRadioScratch.config.which_payload_variant = Config_display_tag; - fromRadioScratch.config.payload_variant.display = config.display; - break; - case Config_lora_tag: - fromRadioScratch.config.which_payload_variant = Config_lora_tag; - fromRadioScratch.config.payload_variant.lora = config.lora; - break; - case Config_bluetooth_tag: - fromRadioScratch.config.which_payload_variant = Config_bluetooth_tag; - fromRadioScratch.config.payload_variant.bluetooth = config.bluetooth; - break; + case Config_device_tag: + fromRadioScratch.config.which_payload_variant = Config_device_tag; + fromRadioScratch.config.payload_variant.device = config.device; + break; + case Config_position_tag: + fromRadioScratch.config.which_payload_variant = Config_position_tag; + fromRadioScratch.config.payload_variant.position = config.position; + break; + case Config_power_tag: + fromRadioScratch.config.which_payload_variant = Config_power_tag; + fromRadioScratch.config.payload_variant.power = config.power; + fromRadioScratch.config.payload_variant.power.ls_secs = default_ls_secs; + break; + case Config_network_tag: + fromRadioScratch.config.which_payload_variant = Config_network_tag; + fromRadioScratch.config.payload_variant.network = config.network; + break; + case Config_display_tag: + fromRadioScratch.config.which_payload_variant = Config_display_tag; + fromRadioScratch.config.payload_variant.display = config.display; + break; + case Config_lora_tag: + fromRadioScratch.config.which_payload_variant = Config_lora_tag; + fromRadioScratch.config.payload_variant.lora = config.lora; + break; + case Config_bluetooth_tag: + fromRadioScratch.config.which_payload_variant = Config_bluetooth_tag; + fromRadioScratch.config.payload_variant.bluetooth = config.bluetooth; + break; } // NOTE: The phone app needs to know the ls_secs value so it can properly expect sleep behavior. // So even if we internally use 0 to represent 'use default' we still need to send the value we are // using to the app (so that even old phone apps work with new device loads). - + config_state++; // Advance when we have sent all of our config objects if (config_state > Config_bluetooth_tag) { @@ -211,37 +225,37 @@ size_t PhoneAPI::getFromRadio(uint8_t *buf) DEBUG_MSG("getFromRadio=STATE_SEND_MODULECONFIG\n"); fromRadioScratch.which_payload_variant = FromRadio_moduleConfig_tag; switch (config_state) { - case ModuleConfig_mqtt_tag: - fromRadioScratch.moduleConfig.which_payload_variant = ModuleConfig_mqtt_tag; - fromRadioScratch.moduleConfig.payload_variant.mqtt = moduleConfig.mqtt; - break; - case ModuleConfig_serial_tag: - fromRadioScratch.moduleConfig.which_payload_variant = ModuleConfig_serial_tag; - fromRadioScratch.moduleConfig.payload_variant.serial = moduleConfig.serial; - break; - case ModuleConfig_external_notification_tag: - fromRadioScratch.moduleConfig.which_payload_variant = ModuleConfig_external_notification_tag; - fromRadioScratch.moduleConfig.payload_variant.external_notification = moduleConfig.external_notification; - break; - case ModuleConfig_range_test_tag: - fromRadioScratch.moduleConfig.which_payload_variant = ModuleConfig_range_test_tag; - fromRadioScratch.moduleConfig.payload_variant.range_test = moduleConfig.range_test; - break; - case ModuleConfig_telemetry_tag: - fromRadioScratch.moduleConfig.which_payload_variant = ModuleConfig_telemetry_tag; - fromRadioScratch.moduleConfig.payload_variant.telemetry = moduleConfig.telemetry; - break; - case ModuleConfig_canned_message_tag: - fromRadioScratch.moduleConfig.which_payload_variant = ModuleConfig_canned_message_tag; - fromRadioScratch.moduleConfig.payload_variant.canned_message = moduleConfig.canned_message; - break; + case ModuleConfig_mqtt_tag: + fromRadioScratch.moduleConfig.which_payload_variant = ModuleConfig_mqtt_tag; + fromRadioScratch.moduleConfig.payload_variant.mqtt = moduleConfig.mqtt; + break; + case ModuleConfig_serial_tag: + fromRadioScratch.moduleConfig.which_payload_variant = ModuleConfig_serial_tag; + fromRadioScratch.moduleConfig.payload_variant.serial = moduleConfig.serial; + break; + case ModuleConfig_external_notification_tag: + fromRadioScratch.moduleConfig.which_payload_variant = ModuleConfig_external_notification_tag; + fromRadioScratch.moduleConfig.payload_variant.external_notification = moduleConfig.external_notification; + break; + case ModuleConfig_range_test_tag: + fromRadioScratch.moduleConfig.which_payload_variant = ModuleConfig_range_test_tag; + fromRadioScratch.moduleConfig.payload_variant.range_test = moduleConfig.range_test; + break; + case ModuleConfig_telemetry_tag: + fromRadioScratch.moduleConfig.which_payload_variant = ModuleConfig_telemetry_tag; + fromRadioScratch.moduleConfig.payload_variant.telemetry = moduleConfig.telemetry; + break; + case ModuleConfig_canned_message_tag: + fromRadioScratch.moduleConfig.which_payload_variant = ModuleConfig_canned_message_tag; + fromRadioScratch.moduleConfig.payload_variant.canned_message = moduleConfig.canned_message; + break; } config_state++; // Advance when we have sent all of our ModuleConfig objects if (config_state > ModuleConfig_canned_message_tag) { state = STATE_SEND_COMPLETE_ID; - config_state = Config_device_tag; + config_state = 0; } break; @@ -274,7 +288,8 @@ size_t PhoneAPI::getFromRadio(uint8_t *buf) if (fromRadioScratch.which_payload_variant != 0) { // Encapsulate as a FromRadio packet size_t numbytes = pb_encode_to_bytes(buf, FromRadio_size, FromRadio_fields, &fromRadioScratch); - // DEBUG_MSG("encoding toPhone packet to phone variant=%d, %d bytes\n", fromRadioScratch.which_payloadVariant, numbytes); + + DEBUG_MSG("encoding toPhone packet to phone variant=%d, %d bytes\n", fromRadioScratch.which_payload_variant, numbytes); return numbytes; } @@ -282,7 +297,7 @@ size_t PhoneAPI::getFromRadio(uint8_t *buf) return 0; } -void PhoneAPI::handleDisconnect() +void PhoneAPI::handleDisconnect() { DEBUG_MSG("PhoneAPI disconnect\n"); } @@ -301,26 +316,25 @@ void PhoneAPI::releasePhonePacket() bool PhoneAPI::available() { switch (state) { - case STATE_SEND_NOTHING: - return false; - case STATE_SEND_MY_INFO: - return true; - case STATE_SEND_CONFIG: - return true; - case STATE_SEND_MODULECONFIG: - return true; - case STATE_SEND_NODEINFO: - if (!nodeInfoForPhone) - nodeInfoForPhone = nodeDB.readNextInfo(); - return true; // Always say we have something, because we might need to advance our state machine - case STATE_SEND_COMPLETE_ID: - return true; - case STATE_SEND_PACKETS: { - if (!packetForPhone) - packetForPhone = service.getForPhone(); - bool hasPacket = !!packetForPhone; - // DEBUG_MSG("available hasPacket=%d\n", hasPacket); - return hasPacket; + case STATE_SEND_NOTHING: + return false; + case STATE_SEND_MY_INFO: + case STATE_SEND_CHANNELS: + case STATE_SEND_CONFIG: + case STATE_SEND_MODULECONFIG: + case STATE_SEND_COMPLETE_ID: + return true; + case STATE_SEND_NODEINFO: + if (!nodeInfoForPhone) + nodeInfoForPhone = nodeDB.readNextInfo(); + return true; // Always say we have something, because we might need to advance our state machine + + case STATE_SEND_PACKETS: { + if (!packetForPhone) + packetForPhone = service.getForPhone(); + bool hasPacket = !!packetForPhone; + // DEBUG_MSG("available hasPacket=%d\n", hasPacket); + return hasPacket; } default: assert(0); // unexpected state - FIXME, make an error code and reboot diff --git a/src/mesh/PhoneAPI.h b/src/mesh/PhoneAPI.h index fb372bde1..cbac5f688 100644 --- a/src/mesh/PhoneAPI.h +++ b/src/mesh/PhoneAPI.h @@ -16,13 +16,13 @@ * Eventually there should be once instance of this class for each live connection (because it has a bit of state * for that connection) */ -class PhoneAPI - : public Observer // FIXME, we shouldn't be inheriting from Observer, instead use CallbackObserver as a member +class PhoneAPI : public Observer // FIXME, we shouldn't be inheriting from Observer, instead use CallbackObserver as a member { enum State { STATE_SEND_NOTHING, // Initial state, don't send anything until the client starts asking for config STATE_SEND_MY_INFO, // send our my info record STATE_SEND_NODEINFO, // states progress in this order as the device sends to to the client + STATE_SEND_CHANNELS, // Send all channels STATE_SEND_CONFIG, // Replacement for the old Radioconfig STATE_SEND_MODULECONFIG, // Send Module specific config STATE_SEND_COMPLETE_ID, @@ -31,7 +31,7 @@ class PhoneAPI State state = STATE_SEND_NOTHING; - int8_t config_state = Config_device_tag; + uint8_t config_state = 0; /** * Each packet sent to the phone has an incrementing count diff --git a/src/mesh/RadioInterface.cpp b/src/mesh/RadioInterface.cpp index 1a6ad856d..07bb01108 100644 --- a/src/mesh/RadioInterface.cpp +++ b/src/mesh/RadioInterface.cpp @@ -6,15 +6,16 @@ #include "Router.h" #include "assert.h" #include "configuration.h" +#include "main.h" #include "sleep.h" #include #include #include -#define RDEF(name, freq_start, freq_end, duty_cycle, spacing, power_limit, audio_permitted, frequency_switching) \ +#define RDEF(name, freq_start, freq_end, duty_cycle, spacing, power_limit, audio_permitted, frequency_switching, wide_lora) \ { \ Config_LoRaConfig_RegionCode_##name, freq_start, freq_end, duty_cycle, spacing, power_limit, audio_permitted, \ - frequency_switching, #name \ + frequency_switching, wide_lora, #name \ } const RegionInfo regions[] = { @@ -22,12 +23,12 @@ const RegionInfo regions[] = { https://link.springer.com/content/pdf/bbm%3A978-1-4842-4357-2%2F1.pdf https://www.thethingsnetwork.org/docs/lorawan/regional-parameters/ */ - RDEF(US, 902.0f, 928.0f, 100, 0, 30, true, false), + RDEF(US, 902.0f, 928.0f, 100, 0, 30, true, false, false), /* https://lora-alliance.org/wp-content/uploads/2020/11/lorawan_regional_parameters_v1.0.3reva_0.pdf */ - RDEF(EU_433, 433.0f, 434.0f, 10, 0, 12, true, false), + RDEF(EU_433, 433.0f, 434.0f, 10, 0, 12, true, false, false), /* https://www.thethingsnetwork.org/docs/lorawan/duty-cycle/ @@ -43,23 +44,23 @@ const RegionInfo regions[] = { (Please refer to section 4.21 in the following document) https://ec.europa.eu/growth/tools-databases/tris/index.cfm/ro/search/?trisaction=search.detail&year=2021&num=528&dLang=EN */ - RDEF(EU_868, 869.4f, 869.65f, 10, 0, 27, false, false), + RDEF(EU_868, 869.4f, 869.65f, 10, 0, 27, false, false, false), /* https://lora-alliance.org/wp-content/uploads/2020/11/lorawan_regional_parameters_v1.0.3reva_0.pdf */ - RDEF(CN, 470.0f, 510.0f, 100, 0, 19, true, false), + RDEF(CN, 470.0f, 510.0f, 100, 0, 19, true, false, false), /* https://lora-alliance.org/wp-content/uploads/2020/11/lorawan_regional_parameters_v1.0.3reva_0.pdf */ - RDEF(JP, 920.8f, 927.8f, 100, 0, 16, true, false), + RDEF(JP, 920.8f, 927.8f, 100, 0, 16, true, false, false), /* https://www.iot.org.au/wp/wp-content/uploads/2016/12/IoTSpectrumFactSheet.pdf https://iotalliance.org.nz/wp-content/uploads/sites/4/2019/05/IoT-Spectrum-in-NZ-Briefing-Paper.pdf */ - RDEF(ANZ, 915.0f, 928.0f, 100, 0, 30, true, false), + RDEF(ANZ, 915.0f, 928.0f, 100, 0, 30, true, false, false), /* https://digital.gov.ru/uploaded/files/prilozhenie-12-k-reshenyu-gkrch-18-46-03-1.pdf @@ -67,38 +68,44 @@ const RegionInfo regions[] = { Note: - We do LBT, so 100% is allowed. */ - RDEF(RU, 868.7f, 869.2f, 100, 0, 20, true, false), + RDEF(RU, 868.7f, 869.2f, 100, 0, 20, true, false, false), /* ??? */ - RDEF(KR, 920.0f, 923.0f, 100, 0, 0, true, false), + RDEF(KR, 920.0f, 923.0f, 100, 0, 0, true, false, false), /* ??? */ - RDEF(TW, 920.0f, 925.0f, 100, 0, 0, true, false), + RDEF(TW, 920.0f, 925.0f, 100, 0, 0, true, false, false), /* https://lora-alliance.org/wp-content/uploads/2020/11/lorawan_regional_parameters_v1.0.3reva_0.pdf */ - RDEF(IN, 865.0f, 867.0f, 100, 0, 30, true, false), + RDEF(IN, 865.0f, 867.0f, 100, 0, 30, true, false, false), /* https://rrf.rsm.govt.nz/smart-web/smart/page/-smart/domain/licence/LicenceSummary.wdk?id=219752 https://iotalliance.org.nz/wp-content/uploads/sites/4/2019/05/IoT-Spectrum-in-NZ-Briefing-Paper.pdf */ - RDEF(NZ_865, 864.0f, 868.0f, 100, 0, 0, true, false), + RDEF(NZ_865, 864.0f, 868.0f, 100, 0, 0, true, false, false), /* https://lora-alliance.org/wp-content/uploads/2020/11/lorawan_regional_parameters_v1.0.3reva_0.pdf */ - RDEF(TH, 920.0f, 925.0f, 100, 0, 16, true, false), + RDEF(TH, 920.0f, 925.0f, 100, 0, 16, true, false, false), + + /* + 2.4 GHZ WLAN Band equivalent. Only for SX128x chips. + */ + + RDEF(LORA_24, 2400.0f, 2483.5f, 100, 0, 10, true, false, true), /* This needs to be last. Same as US. */ - RDEF(UNSET, 902.0f, 928.0f, 100, 0, 30, true, false) + RDEF(UNSET, 902.0f, 928.0f, 100, 0, 30, true, false, false) }; @@ -355,39 +362,40 @@ void RadioInterface::applyModemConfig() // No Sync Words in LORA mode Config_LoRaConfig &loraConfig = config.lora; if (loraConfig.spread_factor == 0) { + switch (loraConfig.modem_preset) { case Config_LoRaConfig_ModemPreset_SHORT_FAST: - bw = 250; + bw = (myRegion->wideLora && rIf_wide_lora) ? 800 : 250; cr = 8; sf = 7; break; case Config_LoRaConfig_ModemPreset_SHORT_SLOW: - bw = 250; + bw = (myRegion->wideLora && rIf_wide_lora) ? 800 : 250; cr = 8; sf = 8; break; case Config_LoRaConfig_ModemPreset_MEDIUM_FAST: - bw = 250; + bw = (myRegion->wideLora && rIf_wide_lora) ? 800 : 250; cr = 8; sf = 9; break; case Config_LoRaConfig_ModemPreset_MEDIUM_SLOW: - bw = 250; + bw = (myRegion->wideLora && rIf_wide_lora) ? 800 : 250; cr = 8; sf = 10; break; case Config_LoRaConfig_ModemPreset_LONG_FAST: - bw = 250; + bw = (myRegion->wideLora && rIf_wide_lora) ? 800 : 250; cr = 8; sf = 11; break; case Config_LoRaConfig_ModemPreset_LONG_SLOW: - bw = 125; + bw = (myRegion->wideLora && rIf_wide_lora) ? 400 : 125; cr = 8; sf = 12; break; case Config_LoRaConfig_ModemPreset_VERY_LONG_SLOW: - bw = 31.25; + bw = (myRegion->wideLora && rIf_wide_lora) ? 200 : 31.25; cr = 8; sf = 12; break; @@ -415,14 +423,14 @@ void RadioInterface::applyModemConfig() power = 17; // Default to default power if we don't have a valid power // Set final tx_power back onto config - loraConfig.tx_power = power; + loraConfig.tx_power = (int8_t)power; // cppcheck-suppress assignmentAddressToInteger // Calculate the number of channels uint32_t numChannels = floor((myRegion->freqEnd - myRegion->freqStart) / (myRegion->spacing + (bw / 1000))); // If user has manually specified a channel num, then use that, otherwise generate one by hashing the name const char *channelName = channels.getName(channels.getPrimaryIndex()); - int channel_num = loraConfig.channel_num ? loraConfig.channel_num - 1 : hash(channelName) % numChannels; + int channel_num = (loraConfig.channel_num ? loraConfig.channel_num - 1 : hash(channelName)) % numChannels; // Old frequency selection formula // float freq = myRegion->freqStart + ((((myRegion->freqEnd - myRegion->freqStart) / numChannels) / 2) * channel_num); diff --git a/src/mesh/ReliableRouter.cpp b/src/mesh/ReliableRouter.cpp index f77e2a455..e42f2cdef 100644 --- a/src/mesh/ReliableRouter.cpp +++ b/src/mesh/ReliableRouter.cpp @@ -63,7 +63,7 @@ bool ReliableRouter::shouldFilterReceived(MeshPacket *p) sendAckNak(Routing_Error_NONE, getFrom(p), p->id, p->channel); DEBUG_MSG("acking a repeated want_ack packet\n"); } - } else if (wasSeenRecently(p, false) && p->hop_limit == HOP_RELIABLE && !MeshModule::currentReply) { + } else if (wasSeenRecently(p, false) && p->hop_limit == HOP_RELIABLE && !MeshModule::currentReply && p->to != nodeDB.getNodeNum()) { // retransmission on broadcast has hop_limit still equal to HOP_RELIABLE DEBUG_MSG("Resending implicit ack for a repeated floodmsg\n"); MeshPacket *tosend = packetPool.allocCopy(*p); diff --git a/src/mesh/SX1281Interface.cpp b/src/mesh/SX1281Interface.cpp new file mode 100644 index 000000000..50805cfe0 --- /dev/null +++ b/src/mesh/SX1281Interface.cpp @@ -0,0 +1,13 @@ +#include "configuration.h" +#include "SX1281Interface.h" +#include "error.h" + +#if !defined(ARCH_PORTDUINO) + +SX1281Interface::SX1281Interface(RADIOLIB_PIN_TYPE cs, RADIOLIB_PIN_TYPE irq, RADIOLIB_PIN_TYPE rst, RADIOLIB_PIN_TYPE busy, + SPIClass &spi) + : SX128xInterface(cs, irq, rst, busy, spi) +{ +} + +#endif \ No newline at end of file diff --git a/src/mesh/SX1281Interface.h b/src/mesh/SX1281Interface.h new file mode 100644 index 000000000..3bd65309a --- /dev/null +++ b/src/mesh/SX1281Interface.h @@ -0,0 +1,17 @@ +#pragma once + +#include "SX128xInterface.h" + +/** + * Our adapter for SX1281 radios + */ + +#if !defined(ARCH_PORTDUINO) + +class SX1281Interface : public SX128xInterface +{ + public: + SX1281Interface(RADIOLIB_PIN_TYPE cs, RADIOLIB_PIN_TYPE irq, RADIOLIB_PIN_TYPE rst, RADIOLIB_PIN_TYPE busy, SPIClass &spi); +}; + +#endif \ No newline at end of file diff --git a/src/mesh/SX128xInterface.cpp b/src/mesh/SX128xInterface.cpp new file mode 100644 index 000000000..10d791847 --- /dev/null +++ b/src/mesh/SX128xInterface.cpp @@ -0,0 +1,247 @@ +#include "configuration.h" +#include "SX128xInterface.h" +#include "error.h" + +#if !defined(ARCH_PORTDUINO) + +// Particular boards might define a different max power based on what their hardware can do +#ifndef SX128X_MAX_POWER +#define SX128X_MAX_POWER 22 +#endif + +template +SX128xInterface::SX128xInterface(RADIOLIB_PIN_TYPE cs, RADIOLIB_PIN_TYPE irq, RADIOLIB_PIN_TYPE rst, RADIOLIB_PIN_TYPE busy, + SPIClass &spi) + : RadioLibInterface(cs, irq, rst, busy, spi, &lora), lora(&module) +{ +} + +/// Initialise the Driver transport hardware and software. +/// Make sure the Driver is properly configured before calling init(). +/// \return true if initialisation succeeded. +template +bool SX128xInterface::init() +{ +#ifdef SX128X_POWER_EN + digitalWrite(SX128X_POWER_EN, HIGH); + pinMode(SX128X_POWER_EN, OUTPUT); +#endif + +#ifdef SX128X_RXEN // set not rx or tx mode + digitalWrite(SX128X_RXEN, LOW); // Set low before becoming an output + pinMode(SX128X_RXEN, OUTPUT); +#endif +#ifdef SX128X_TXEN + digitalWrite(SX128X_TXEN, LOW); + pinMode(SX128X_TXEN, OUTPUT); +#endif + + RadioLibInterface::init(); + + if (power == 0) + power = SX128X_MAX_POWER; + + if (power > SX128X_MAX_POWER) // This chip has lower power limits than some + power = SX128X_MAX_POWER; + + limitPower(); + + int res = lora.begin(getFreq(), bw, sf, cr, syncWord, power, preambleLength); + // \todo Display actual typename of the adapter, not just `SX128x` + DEBUG_MSG("SX128x init result %d\n", res); + + DEBUG_MSG("Frequency set to %f\n", getFreq()); + DEBUG_MSG("Bandwidth set to %f\n", bw); + DEBUG_MSG("Power output set to %d\n", power); + +#ifdef SX128X_TXEN + // lora.begin sets Dio2 as RF switch control, which is not true if we are manually controlling RX and TX + if (res == RADIOLIB_ERR_NONE) + res = lora.setDio2AsRfSwitch(true); +#endif + + if (res == RADIOLIB_ERR_NONE) + res = lora.setCRC(RADIOLIB_SX128X_LORA_CRC_ON); + + if (res == RADIOLIB_ERR_NONE) + startReceive(); // start receiving + + return res == RADIOLIB_ERR_NONE; +} + +template +bool SX128xInterface::reconfigure() +{ + RadioLibInterface::reconfigure(); + + // set mode to standby + setStandby(); + + // configure publicly accessible settings + int err = lora.setSpreadingFactor(sf); + if (err != RADIOLIB_ERR_NONE) + RECORD_CRITICALERROR(CriticalErrorCode_INVALID_RADIO_SETTING); + + err = lora.setBandwidth(bw); + if (err != RADIOLIB_ERR_NONE) + RECORD_CRITICALERROR(CriticalErrorCode_INVALID_RADIO_SETTING); + + err = lora.setCodingRate(cr); + if (err != RADIOLIB_ERR_NONE) + RECORD_CRITICALERROR(CriticalErrorCode_INVALID_RADIO_SETTING); + + // Hmm - seems to lower SNR when the signal levels are high. Leaving off for now... + // TODO: Confirm gain registers are okay now + // err = lora.setRxGain(true); + // assert(err == RADIOLIB_ERR_NONE); + + err = lora.setSyncWord(syncWord); + assert(err == RADIOLIB_ERR_NONE); + + err = lora.setPreambleLength(preambleLength); + assert(err == RADIOLIB_ERR_NONE); + + err = lora.setFrequency(getFreq()); + if (err != RADIOLIB_ERR_NONE) + RECORD_CRITICALERROR(CriticalErrorCode_INVALID_RADIO_SETTING); + + if (power > 22) // This chip has lower power limits than some + power = 22; + err = lora.setOutputPower(power); + assert(err == RADIOLIB_ERR_NONE); + + startReceive(); // restart receiving + + return RADIOLIB_ERR_NONE; +} + +template +void INTERRUPT_ATTR SX128xInterface::disableInterrupt() +{ + lora.clearDio1Action(); +} + +template +void SX128xInterface::setStandby() +{ + checkNotification(); // handle any pending interrupts before we force standby + + int err = lora.standby(); + assert(err == RADIOLIB_ERR_NONE); + +#ifdef SX128X_RXEN // we have RXEN/TXEN control - turn off RX and TX power + digitalWrite(SX128X_RXEN, LOW); +#endif +#ifdef SX128X_TXEN + digitalWrite(SX128X_TXEN, LOW); +#endif + + isReceiving = false; // If we were receiving, not any more + disableInterrupt(); + completeSending(); // If we were sending, not anymore +} + +/** + * Add SNR data to received messages + */ +template +void SX128xInterface::addReceiveMetadata(MeshPacket *mp) +{ + // DEBUG_MSG("PacketStatus %x\n", lora.getPacketStatus()); + mp->rx_snr = lora.getSNR(); + mp->rx_rssi = lround(lora.getRSSI()); +} + +/** We override to turn on transmitter power as needed. + */ +template +void SX128xInterface::configHardwareForSend() +{ +#ifdef SX128X_TXEN // we have RXEN/TXEN control - turn on TX power / off RX power + digitalWrite(SX128X_TXEN, HIGH); +#endif +#ifdef SX128X_RXEN + digitalWrite(SX128X_RXEN, LOW); +#endif + + RadioLibInterface::configHardwareForSend(); +} + +// For power draw measurements, helpful to force radio to stay sleeping +// #define SLEEP_ONLY + +template +void SX128xInterface::startReceive() +{ +#ifdef SLEEP_ONLY + sleep(); +#else + + setStandby(); + +#ifdef SX128X_RXEN // we have RXEN/TXEN control - turn on RX power / off TX power + digitalWrite(SX128X_RXEN, HIGH); +#endif +#ifdef SX128X_TXEN + digitalWrite(SX128X_TXEN, LOW); +#endif + + int err = lora.startReceive(); + + assert(err == RADIOLIB_ERR_NONE); + + isReceiving = true; + + // Must be done AFTER, starting transmit, because startTransmit clears (possibly stale) interrupt pending register bits + enableInterrupt(isrRxLevel0); +#endif +} + +/** Could we send right now (i.e. either not actively receving or transmitting)? */ +template +bool SX128xInterface::isChannelActive() +{ + // check if we can detect a LoRa preamble on the current channel + int16_t result; + + setStandby(); + result = lora.scanChannel(); + if (result == RADIOLIB_PREAMBLE_DETECTED) + return true; + + assert(result != RADIOLIB_ERR_WRONG_MODEM); + + return false; +} + +/** Could we send right now (i.e. either not actively receving or transmitting)? */ +template +bool SX128xInterface::isActivelyReceiving() +{ + return isChannelActive(); +} + +template +bool SX128xInterface::sleep() +{ + // Not keeping config is busted - next time nrf52 board boots lora sending fails tcxo related? - see datasheet + // \todo Display actual typename of the adapter, not just `SX128x` + DEBUG_MSG("SX128x entering sleep mode (FIXME, don't keep config)\n"); + setStandby(); // Stop any pending operations + + // turn off TCXO if it was powered + // FIXME - this isn't correct + // lora.setTCXO(0); + + // put chipset into sleep mode (we've already disabled interrupts by now) + bool keepConfig = true; + lora.sleep(keepConfig); // Note: we do not keep the config, full reinit will be needed + +#ifdef SX128X_POWER_EN + digitalWrite(SX128X_POWER_EN, LOW); +#endif + + return true; +} + +#endif \ No newline at end of file diff --git a/src/mesh/SX128xInterface.h b/src/mesh/SX128xInterface.h new file mode 100644 index 000000000..d01dfc510 --- /dev/null +++ b/src/mesh/SX128xInterface.h @@ -0,0 +1,75 @@ +#pragma once + +#if !defined(ARCH_PORTDUINO) + +#include "RadioLibInterface.h" + +/** + * \brief Adapter for SX128x radio family. Implements common logic for child classes. + * \tparam T RadioLib module type for SX128x: SX1281. + */ +template +class SX128xInterface : public RadioLibInterface +{ + public: + SX128xInterface(RADIOLIB_PIN_TYPE cs, RADIOLIB_PIN_TYPE irq, RADIOLIB_PIN_TYPE rst, RADIOLIB_PIN_TYPE busy, SPIClass &spi); + + /// Initialise the Driver transport hardware and software. + /// Make sure the Driver is properly configured before calling init(). + /// \return true if initialisation succeeded. + virtual bool init() override; + + /// Apply any radio provisioning changes + /// Make sure the Driver is properly configured before calling init(). + /// \return true if initialisation succeeded. + virtual bool reconfigure() override; + + /// Prepare hardware for sleep. Call this _only_ for deep sleep, not needed for light sleep. + virtual bool sleep() override; + + protected: + + float currentLimit = 140; // Higher OCP limit for SX128x PA + + /** + * Specific module instance + */ + T lora; + + /** + * Glue functions called from ISR land + */ + virtual void disableInterrupt() override; + + /** + * Enable a particular ISR callback glue function + */ + virtual void enableInterrupt(void (*callback)()) { lora.setDio1Action(callback); } + + /** can we detect a LoRa preamble on the current channel? */ + virtual bool isChannelActive() override; + + /** are we actively receiving a packet (only called during receiving state) */ + virtual bool isActivelyReceiving() override; + + /** + * Start waiting to receive a message + */ + virtual void startReceive() override; + + /** + * We override to turn on transmitter power as needed. + */ + virtual void configHardwareForSend() override; + + /** + * Add SNR data to received messages + */ + virtual void addReceiveMetadata(MeshPacket *mp) override; + + virtual void setStandby() override; + + private: +}; + +#endif \ No newline at end of file diff --git a/src/mesh/SinglePortModule.h b/src/mesh/SinglePortModule.h index 2e587cb89..57db73221 100644 --- a/src/mesh/SinglePortModule.h +++ b/src/mesh/SinglePortModule.h @@ -18,6 +18,9 @@ class SinglePortModule : public MeshModule SinglePortModule(const char *_name, PortNum _ourPortNum) : MeshModule(_name), ourPortNum(_ourPortNum) {} protected: + uint32_t max_channel_util_percent = 40; + uint32_t polite_channel_util_percent = 25; + /** * @return true if you want to receive the specified portnum */ diff --git a/src/mesh/generated/admin.pb.h b/src/mesh/generated/admin.pb.h index 9392c19a0..9b93bfd44 100644 --- a/src/mesh/generated/admin.pb.h +++ b/src/mesh/generated/admin.pb.h @@ -59,8 +59,6 @@ typedef struct _AdminMessage { AdminMessage_ModuleConfigType get_module_config_request; /* Send the current Config in the response to this message. */ ModuleConfig get_module_config_response; - /* Send all channels in the response to this message */ - bool get_all_channel_request; /* Get the Canned Message Module messages in the response to this message. */ bool get_canned_message_module_messages_request; /* Get the Canned Message Module messages in the response to this message. */ @@ -139,7 +137,6 @@ extern "C" { #define AdminMessage_get_config_response_tag 6 #define AdminMessage_get_module_config_request_tag 7 #define AdminMessage_get_module_config_response_tag 8 -#define AdminMessage_get_all_channel_request_tag 9 #define AdminMessage_get_canned_message_module_messages_request_tag 10 #define AdminMessage_get_canned_message_module_messages_response_tag 11 #define AdminMessage_get_device_metadata_request_tag 12 @@ -170,7 +167,6 @@ X(a, STATIC, ONEOF, UENUM, (payload_variant,get_config_request,get_confi X(a, STATIC, ONEOF, MESSAGE, (payload_variant,get_config_response,get_config_response), 6) \ X(a, STATIC, ONEOF, UENUM, (payload_variant,get_module_config_request,get_module_config_request), 7) \ X(a, STATIC, ONEOF, MESSAGE, (payload_variant,get_module_config_response,get_module_config_response), 8) \ -X(a, STATIC, ONEOF, BOOL, (payload_variant,get_all_channel_request,get_all_channel_request), 9) \ X(a, STATIC, ONEOF, BOOL, (payload_variant,get_canned_message_module_messages_request,get_canned_message_module_messages_request), 10) \ X(a, STATIC, ONEOF, STRING, (payload_variant,get_canned_message_module_messages_response,get_canned_message_module_messages_response), 11) \ X(a, STATIC, ONEOF, BOOL, (payload_variant,get_device_metadata_request,get_device_metadata_request), 12) \ diff --git a/src/mesh/generated/apponly.pb.h b/src/mesh/generated/apponly.pb.h index 36bff5ad4..63f3cf3bf 100644 --- a/src/mesh/generated/apponly.pb.h +++ b/src/mesh/generated/apponly.pb.h @@ -54,7 +54,7 @@ extern const pb_msgdesc_t ChannelSet_msg; #define ChannelSet_fields &ChannelSet_msg /* Maximum encoded size of messages (where known) */ -#define ChannelSet_size 581 +#define ChannelSet_size 582 #ifdef __cplusplus } /* extern "C" */ diff --git a/src/mesh/generated/config.pb.h b/src/mesh/generated/config.pb.h index bc06b53ec..940771580 100644 --- a/src/mesh/generated/config.pb.h +++ b/src/mesh/generated/config.pb.h @@ -64,7 +64,8 @@ typedef enum _Config_LoRaConfig_RegionCode { Config_LoRaConfig_RegionCode_RU = 9, Config_LoRaConfig_RegionCode_IN = 10, Config_LoRaConfig_RegionCode_NZ_865 = 11, - Config_LoRaConfig_RegionCode_TH = 12 + Config_LoRaConfig_RegionCode_TH = 12, + Config_LoRaConfig_RegionCode_LORA_24 = 13 } Config_LoRaConfig_RegionCode; typedef enum _Config_LoRaConfig_ModemPreset { @@ -116,7 +117,7 @@ typedef struct _Config_LoRaConfig { uint32_t hop_limit; bool tx_enabled; int8_t tx_power; - uint8_t channel_num; + uint16_t channel_num; pb_size_t ignore_incoming_count; uint32_t ignore_incoming[3]; } Config_LoRaConfig; @@ -186,8 +187,8 @@ typedef struct _Config { #define _Config_DisplayConfig_DisplayUnits_ARRAYSIZE ((Config_DisplayConfig_DisplayUnits)(Config_DisplayConfig_DisplayUnits_IMPERIAL+1)) #define _Config_LoRaConfig_RegionCode_MIN Config_LoRaConfig_RegionCode_UNSET -#define _Config_LoRaConfig_RegionCode_MAX Config_LoRaConfig_RegionCode_TH -#define _Config_LoRaConfig_RegionCode_ARRAYSIZE ((Config_LoRaConfig_RegionCode)(Config_LoRaConfig_RegionCode_TH+1)) +#define _Config_LoRaConfig_RegionCode_MAX Config_LoRaConfig_RegionCode_LORA_24 +#define _Config_LoRaConfig_RegionCode_ARRAYSIZE ((Config_LoRaConfig_RegionCode)(Config_LoRaConfig_RegionCode_LORA_24+1)) #define _Config_LoRaConfig_ModemPreset_MIN Config_LoRaConfig_ModemPreset_LONG_FAST #define _Config_LoRaConfig_ModemPreset_MAX Config_LoRaConfig_ModemPreset_SHORT_FAST @@ -387,7 +388,7 @@ extern const pb_msgdesc_t Config_BluetoothConfig_msg; #define Config_BluetoothConfig_size 10 #define Config_DeviceConfig_size 6 #define Config_DisplayConfig_size 20 -#define Config_LoRaConfig_size 67 +#define Config_LoRaConfig_size 68 #define Config_NetworkConfig_size 137 #define Config_PositionConfig_size 30 #define Config_PowerConfig_size 43 diff --git a/src/mesh/generated/deviceonly.pb.h b/src/mesh/generated/deviceonly.pb.h index e7ee26e8f..87a7c8cec 100644 --- a/src/mesh/generated/deviceonly.pb.h +++ b/src/mesh/generated/deviceonly.pb.h @@ -69,6 +69,7 @@ typedef struct _DeviceState { } DeviceState; typedef PB_BYTES_ARRAY_T(2048) OEMStore_oem_icon_bits_t; +typedef PB_BYTES_ARRAY_T(32) OEMStore_oem_aes_key_t; /* This can be used for customizing the firmware distribution. If populated, show a secondary bootup screen with cuatom logo and text for 2.5 seconds. */ typedef struct _OEMStore { @@ -82,6 +83,8 @@ typedef struct _OEMStore { ScreenFonts oem_font; /* Use this font for the OEM text. */ char oem_text[40]; + /* The default device encryption key, 16 or 32 byte */ + OEMStore_oem_aes_key_t oem_aes_key; } OEMStore; @@ -98,10 +101,10 @@ extern "C" { /* Initializer values for message structs */ #define DeviceState_init_default {false, MyNodeInfo_init_default, false, User_init_default, 0, {NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default}, 0, {MeshPacket_init_default}, false, MeshPacket_init_default, 0, 0, 0} #define ChannelFile_init_default {0, {Channel_init_default, Channel_init_default, Channel_init_default, Channel_init_default, Channel_init_default, Channel_init_default, Channel_init_default, Channel_init_default}, 0} -#define OEMStore_init_default {0, 0, {0, {0}}, _ScreenFonts_MIN, ""} +#define OEMStore_init_default {0, 0, {0, {0}}, _ScreenFonts_MIN, "", {0, {0}}} #define DeviceState_init_zero {false, MyNodeInfo_init_zero, false, User_init_zero, 0, {NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero}, 0, {MeshPacket_init_zero}, false, MeshPacket_init_zero, 0, 0, 0} #define ChannelFile_init_zero {0, {Channel_init_zero, Channel_init_zero, Channel_init_zero, Channel_init_zero, Channel_init_zero, Channel_init_zero, Channel_init_zero, Channel_init_zero}, 0} -#define OEMStore_init_zero {0, 0, {0, {0}}, _ScreenFonts_MIN, ""} +#define OEMStore_init_zero {0, 0, {0, {0}}, _ScreenFonts_MIN, "", {0, {0}}} /* Field tags (for use in manual encoding/decoding) */ #define ChannelFile_channels_tag 1 @@ -119,6 +122,7 @@ extern "C" { #define OEMStore_oem_icon_bits_tag 3 #define OEMStore_oem_font_tag 4 #define OEMStore_oem_text_tag 5 +#define OEMStore_oem_aes_key_tag 6 /* Struct field encoding specification for nanopb */ #define DeviceState_FIELDLIST(X, a) \ @@ -150,7 +154,8 @@ X(a, STATIC, SINGULAR, UINT32, oem_icon_width, 1) \ X(a, STATIC, SINGULAR, UINT32, oem_icon_height, 2) \ X(a, STATIC, SINGULAR, BYTES, oem_icon_bits, 3) \ X(a, STATIC, SINGULAR, UENUM, oem_font, 4) \ -X(a, STATIC, SINGULAR, STRING, oem_text, 5) +X(a, STATIC, SINGULAR, STRING, oem_text, 5) \ +X(a, STATIC, SINGULAR, BYTES, oem_aes_key, 6) #define OEMStore_CALLBACK NULL #define OEMStore_DEFAULT NULL @@ -166,7 +171,7 @@ extern const pb_msgdesc_t OEMStore_msg; /* Maximum encoded size of messages (where known) */ #define ChannelFile_size 638 #define DeviceState_size 21800 -#define OEMStore_size 2106 +#define OEMStore_size 2140 #ifdef __cplusplus } /* extern "C" */ diff --git a/src/mesh/generated/localonly.pb.h b/src/mesh/generated/localonly.pb.h index afbe06afe..d4c5fb012 100644 --- a/src/mesh/generated/localonly.pb.h +++ b/src/mesh/generated/localonly.pb.h @@ -144,7 +144,7 @@ extern const pb_msgdesc_t LocalModuleConfig_msg; #define LocalModuleConfig_fields &LocalModuleConfig_msg /* Maximum encoded size of messages (where known) */ -#define LocalConfig_size 334 +#define LocalConfig_size 335 #define LocalModuleConfig_size 270 #ifdef __cplusplus diff --git a/src/mesh/generated/mesh.pb.h b/src/mesh/generated/mesh.pb.h index 6849fac85..ee72ff504 100644 --- a/src/mesh/generated/mesh.pb.h +++ b/src/mesh/generated/mesh.pb.h @@ -4,6 +4,7 @@ #ifndef PB_MESH_PB_H_INCLUDED #define PB_MESH_PB_H_INCLUDED #include +#include "channel.pb.h" #include "config.pb.h" #include "module_config.pb.h" #include "portnums.pb.h" @@ -652,6 +653,8 @@ typedef struct _FromRadio { bool rebooted; /* Include module config */ ModuleConfig moduleConfig; + /* One packet is sent for each channel */ + Channel channel; }; } FromRadio; @@ -854,6 +857,7 @@ extern "C" { #define FromRadio_config_complete_id_tag 7 #define FromRadio_rebooted_tag 8 #define FromRadio_moduleConfig_tag 9 +#define FromRadio_channel_tag 10 #define ToRadio_packet_tag 1 #define ToRadio_peer_info_tag 2 #define ToRadio_want_config_id_tag 3 @@ -1001,7 +1005,8 @@ X(a, STATIC, ONEOF, MESSAGE, (payload_variant,config,config), 5) \ X(a, STATIC, ONEOF, MESSAGE, (payload_variant,log_record,log_record), 6) \ X(a, STATIC, ONEOF, UINT32, (payload_variant,config_complete_id,config_complete_id), 7) \ X(a, STATIC, ONEOF, BOOL, (payload_variant,rebooted,rebooted), 8) \ -X(a, STATIC, ONEOF, MESSAGE, (payload_variant,moduleConfig,moduleConfig), 9) +X(a, STATIC, ONEOF, MESSAGE, (payload_variant,moduleConfig,moduleConfig), 9) \ +X(a, STATIC, ONEOF, MESSAGE, (payload_variant,channel,channel), 10) #define FromRadio_CALLBACK NULL #define FromRadio_DEFAULT NULL #define FromRadio_payload_variant_packet_MSGTYPE MeshPacket @@ -1010,6 +1015,7 @@ X(a, STATIC, ONEOF, MESSAGE, (payload_variant,moduleConfig,moduleConfig), #define FromRadio_payload_variant_config_MSGTYPE Config #define FromRadio_payload_variant_log_record_MSGTYPE LogRecord #define FromRadio_payload_variant_moduleConfig_MSGTYPE ModuleConfig +#define FromRadio_payload_variant_channel_MSGTYPE Channel #define ToRadio_FIELDLIST(X, a) \ X(a, STATIC, ONEOF, MESSAGE, (payload_variant,packet,packet), 1) \ diff --git a/src/mesh/generated/module_config.pb.h b/src/mesh/generated/module_config.pb.h index 36ac24750..b0f14eea2 100644 --- a/src/mesh/generated/module_config.pb.h +++ b/src/mesh/generated/module_config.pb.h @@ -33,7 +33,8 @@ typedef enum _ModuleConfig_SerialConfig_Serial_Mode { ModuleConfig_SerialConfig_Serial_Mode_DEFAULT = 0, ModuleConfig_SerialConfig_Serial_Mode_SIMPLE = 1, ModuleConfig_SerialConfig_Serial_Mode_PROTO = 2, - ModuleConfig_SerialConfig_Serial_Mode_TEXTMSG = 3 + ModuleConfig_SerialConfig_Serial_Mode_TEXTMSG = 3, + ModuleConfig_SerialConfig_Serial_Mode_NMEA = 4 } ModuleConfig_SerialConfig_Serial_Mode; typedef enum _ModuleConfig_CannedMessageConfig_InputEventChar { @@ -140,8 +141,8 @@ typedef struct _ModuleConfig { #define _ModuleConfig_SerialConfig_Serial_Baud_ARRAYSIZE ((ModuleConfig_SerialConfig_Serial_Baud)(ModuleConfig_SerialConfig_Serial_Baud_BAUD_921600+1)) #define _ModuleConfig_SerialConfig_Serial_Mode_MIN ModuleConfig_SerialConfig_Serial_Mode_DEFAULT -#define _ModuleConfig_SerialConfig_Serial_Mode_MAX ModuleConfig_SerialConfig_Serial_Mode_TEXTMSG -#define _ModuleConfig_SerialConfig_Serial_Mode_ARRAYSIZE ((ModuleConfig_SerialConfig_Serial_Mode)(ModuleConfig_SerialConfig_Serial_Mode_TEXTMSG+1)) +#define _ModuleConfig_SerialConfig_Serial_Mode_MAX ModuleConfig_SerialConfig_Serial_Mode_NMEA +#define _ModuleConfig_SerialConfig_Serial_Mode_ARRAYSIZE ((ModuleConfig_SerialConfig_Serial_Mode)(ModuleConfig_SerialConfig_Serial_Mode_NMEA+1)) #define _ModuleConfig_CannedMessageConfig_InputEventChar_MIN ModuleConfig_CannedMessageConfig_InputEventChar_NONE #define _ModuleConfig_CannedMessageConfig_InputEventChar_MAX ModuleConfig_CannedMessageConfig_InputEventChar_BACK diff --git a/src/mesh/http/ContentHandler.cpp b/src/mesh/http/ContentHandler.cpp index 6763d70fe..91f88dda0 100644 --- a/src/mesh/http/ContentHandler.cpp +++ b/src/mesh/http/ContentHandler.cpp @@ -79,8 +79,8 @@ void registerHandlers(HTTPServer *insecureServer, HTTPSServer *secureServer) ResourceNode *nodeHotspotAndroid = new ResourceNode("/generate_204", "GET", &handleHotspot); ResourceNode *nodeAdmin = new ResourceNode("/admin", "GET", &handleAdmin); - ResourceNode *nodeAdminSettings = new ResourceNode("/admin/settings", "GET", &handleAdminSettings); - ResourceNode *nodeAdminSettingsApply = new ResourceNode("/admin/settings/apply", "POST", &handleAdminSettingsApply); + // ResourceNode *nodeAdminSettings = new ResourceNode("/admin/settings", "GET", &handleAdminSettings); + // ResourceNode *nodeAdminSettingsApply = new ResourceNode("/admin/settings/apply", "POST", &handleAdminSettingsApply); // ResourceNode *nodeAdminFs = new ResourceNode("/admin/fs", "GET", &handleFs); // ResourceNode *nodeUpdateFs = new ResourceNode("/admin/fs/update", "POST", &handleUpdateFs); // ResourceNode *nodeDeleteFs = new ResourceNode("/admin/fs/delete", "GET", &handleDeleteFsContent); @@ -113,8 +113,8 @@ void registerHandlers(HTTPServer *insecureServer, HTTPSServer *secureServer) // secureServer->registerNode(nodeDeleteFs); secureServer->registerNode(nodeAdmin); // secureServer->registerNode(nodeAdminFs); - secureServer->registerNode(nodeAdminSettings); - secureServer->registerNode(nodeAdminSettingsApply); + // secureServer->registerNode(nodeAdminSettings); + // secureServer->registerNode(nodeAdminSettingsApply); secureServer->registerNode(nodeRoot); // This has to be last // Insecure nodes @@ -134,8 +134,8 @@ void registerHandlers(HTTPServer *insecureServer, HTTPSServer *secureServer) // insecureServer->registerNode(nodeDeleteFs); insecureServer->registerNode(nodeAdmin); // insecureServer->registerNode(nodeAdminFs); - insecureServer->registerNode(nodeAdminSettings); - insecureServer->registerNode(nodeAdminSettingsApply); + // insecureServer->registerNode(nodeAdminSettings); + // insecureServer->registerNode(nodeAdminSettingsApply); insecureServer->registerNode(nodeRoot); // This has to be last } @@ -694,8 +694,8 @@ void handleAdmin(HTTPRequest *req, HTTPResponse *res) res->setHeader("Access-Control-Allow-Methods", "GET"); res->println("

Meshtastic

\n"); - res->println("Settings
\n"); - res->println("Manage Web Content
\n"); + // res->println("Settings
\n"); + // res->println("Manage Web Content
\n"); res->println("Device Report
\n"); } diff --git a/src/mesh/http/WiFiAPClient.cpp b/src/mesh/http/WiFiAPClient.cpp index b169f8ca8..4a4ac05a9 100644 --- a/src/mesh/http/WiFiAPClient.cpp +++ b/src/mesh/http/WiFiAPClient.cpp @@ -43,40 +43,25 @@ bool APStartupComplete = 0; static bool needReconnect = true; // If we create our reconnector, run it once at the beginning -// FIXME, veto light sleep if we have a TCP server running -#if 0 -class WifiSleepObserver : public Observer { -protected: - - /// Return 0 if sleep is okay - virtual int onNotify(uint32_t newValue) { - - } -}; - -static WifiSleepObserver wifiSleepObserver; -//preflightSleepObserver.observe(&preflightSleep); -#endif - static int32_t reconnectWiFi() { const char *wifiName = config.network.wifi_ssid; const char *wifiPsw = config.network.wifi_psk; if (config.network.wifi_enabled && needReconnect && !WiFi.isConnected()) { - // if (radioConfig.has_preferences && needReconnect && !WiFi.isConnected()) { - + if (!*wifiPsw) // Treat empty password as no password wifiPsw = NULL; if (*wifiName) { needReconnect = false; + // Make sure we clear old connection credentials + WiFi.disconnect(false, true); + DEBUG_MSG("... Reconnecting to WiFi access point\n"); WiFi.mode(WIFI_MODE_STA); WiFi.begin(wifiName, wifiPsw); - - // Starting timeClient; } } diff --git a/src/modules/AdminModule.cpp b/src/modules/AdminModule.cpp index 883ed0ac3..6a10c618a 100644 --- a/src/modules/AdminModule.cpp +++ b/src/modules/AdminModule.cpp @@ -101,9 +101,7 @@ bool AdminModule::handleReceivedProtobuf(const MeshPacket &mp, AdminMessage *r) * Other */ case AdminMessage_reboot_seconds_tag: { - int32_t s = r->reboot_seconds; - DEBUG_MSG("Rebooting in %d seconds\n", s); - rebootAtMsec = (s < 0) ? 0 : (millis() + s * 1000); + reboot(r->reboot_seconds); break; } case AdminMessage_reboot_ota_seconds_tag: { @@ -135,13 +133,13 @@ bool AdminModule::handleReceivedProtobuf(const MeshPacket &mp, AdminMessage *r) case AdminMessage_factory_reset_tag: { DEBUG_MSG("Initiating factory reset\n"); nodeDB.factoryReset(); - rebootAtMsec = millis() + (5 * 1000); + reboot(5); break; } case AdminMessage_nodedb_reset_tag: { DEBUG_MSG("Initiating node-db reset\n"); nodeDB.resetNodes(); - rebootAtMsec = millis() + (5 * 1000); + reboot(5); break; } #ifdef ARCH_PORTDUINO @@ -196,14 +194,12 @@ void AdminModule::handleSetOwner(const User &o) if (changed) { // If nothing really changed, don't broadcast on the network or write to flash service.reloadOwner(); DEBUG_MSG("Rebooting due to owner changes\n"); - screen->startRebootScreen(); - rebootAtMsec = millis() + (5 * 1000); + reboot(5); } } void AdminModule::handleSetConfig(const Config &c) { - bool requiresReboot = false; bool isRouter = (config.device.role == Config_DeviceConfig_Role_ROUTER); bool isRegionUnset = (config.lora.region == Config_LoRaConfig_RegionCode_UNSET); @@ -218,12 +214,13 @@ void AdminModule::handleSetConfig(const Config &c) nodeDB.initConfigIntervals(); nodeDB.initModuleConfigIntervals(); } - requiresReboot = true; break; case Config_position_tag: DEBUG_MSG("Setting config: Position\n"); config.has_position = true; config.position = c.payload_variant.position; + // Save nodedb as well in case we got a fixed position packet + nodeDB.saveToDisk(SEGMENT_DEVICESTATE); break; case Config_power_tag: DEBUG_MSG("Setting config: Power\n"); @@ -234,7 +231,6 @@ void AdminModule::handleSetConfig(const Config &c) DEBUG_MSG("Setting config: WiFi\n"); config.has_network = true; config.network = c.payload_variant.network; - requiresReboot = true; break; case Config_display_tag: DEBUG_MSG("Setting config: Display\n"); @@ -249,23 +245,16 @@ void AdminModule::handleSetConfig(const Config &c) config.lora.region > Config_LoRaConfig_RegionCode_UNSET) { config.lora.tx_enabled = true; } - requiresReboot = true; break; case Config_bluetooth_tag: DEBUG_MSG("Setting config: Bluetooth\n"); config.has_bluetooth = true; config.bluetooth = c.payload_variant.bluetooth; - requiresReboot = true; break; } service.reloadConfig(SEGMENT_CONFIG); - // Reboot 5 seconds after a config that requires rebooting is set - if (requiresReboot) { - DEBUG_MSG("Rebooting due to config changes\n"); - screen->startRebootScreen(); - rebootAtMsec = millis() + (5 * 1000); - } + reboot(5); } void AdminModule::handleSetModuleConfig(const ModuleConfig &c) @@ -307,8 +296,9 @@ void AdminModule::handleSetModuleConfig(const ModuleConfig &c) moduleConfig.canned_message = c.payload_variant.canned_message; break; } - + service.reloadConfig(SEGMENT_MODULECONFIG); + reboot(5); } void AdminModule::handleSetChannel(const Channel &cc) @@ -468,6 +458,13 @@ void AdminModule::handleGetChannel(const MeshPacket &req, uint32_t channelIndex) } } +void AdminModule::reboot(int32_t seconds) +{ + DEBUG_MSG("Rebooting in %d seconds\n", seconds); + screen->startRebootScreen(); + rebootAtMsec = (seconds < 0) ? 0 : (millis() + seconds * 1000); +} + AdminModule::AdminModule() : ProtobufModule("Admin", PortNum_ADMIN_APP, AdminMessage_fields) { // restrict to the admin channel for rx diff --git a/src/modules/AdminModule.h b/src/modules/AdminModule.h index 9a87c7f9e..dd81c3218 100644 --- a/src/modules/AdminModule.h +++ b/src/modules/AdminModule.h @@ -37,6 +37,7 @@ class AdminModule : public ProtobufModule void handleSetConfig(const Config &c); void handleSetModuleConfig(const ModuleConfig &c); void handleSetChannel(); + void reboot(int32_t seconds); }; extern AdminModule *adminModule; diff --git a/src/modules/CannedMessageModule.cpp b/src/modules/CannedMessageModule.cpp index 29ea20257..b00dfb796 100644 --- a/src/modules/CannedMessageModule.cpp +++ b/src/modules/CannedMessageModule.cpp @@ -225,7 +225,7 @@ int32_t CannedMessageModule::runOnce() this->cursor = 0; this->destSelect = false; 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) { + } else if (((this->runState == CANNED_MESSAGE_RUN_STATE_ACTIVE) || (this->runState == CANNED_MESSAGE_RUN_STATE_FREETEXT)) && ((millis() - this->lastTouchMillis) > INACTIVATE_AFTER_MS)) { // Reset module DEBUG_MSG("Reset due to lack of activity.\n"); e.frameChanged = true; @@ -430,6 +430,8 @@ void CannedMessageModule::drawFrame(OLEDDisplay *display, OLEDDisplayUiState *st { displayedNodeNum = 0; // Not currently showing a node pane + char buffer[50]; + if (cannedMessageModule->runState == CANNED_MESSAGE_RUN_STATE_SENDING_ACTIVE) { display->setTextAlignment(TEXT_ALIGN_CENTER); display->setFont(FONT_MEDIUM); @@ -445,7 +447,6 @@ void CannedMessageModule::drawFrame(OLEDDisplay *display, OLEDDisplayUiState *st display->fillRect(0 + x, 0 + y, x + display->getWidth(), y + FONT_HEIGHT_SMALL); display->setColor(BLACK); } - char buffer[50]; display->drawStringf(0 + x, 0 + y, buffer, "To: %s", cannedMessageModule->getNodeName(this->dest)); // used chars right aligned sprintf(buffer, "%d left", Constants_DATA_PAYLOAD_LEN - this->freetext.length()); @@ -456,11 +457,13 @@ void CannedMessageModule::drawFrame(OLEDDisplay *display, OLEDDisplayUiState *st if (this->messagesCount > 0) { display->setTextAlignment(TEXT_ALIGN_LEFT); display->setFont(FONT_SMALL); - display->drawString(0 + x, 0 + y, cannedMessageModule->getPrevMessage()); - display->setFont(FONT_MEDIUM); - display->drawString(0 + x, 0 + y + FONT_HEIGHT_SMALL, cannedMessageModule->getCurrentMessage()); - display->setFont(FONT_SMALL); - display->drawString(0 + x, 0 + y + FONT_HEIGHT_MEDIUM, cannedMessageModule->getNextMessage()); + display->drawStringf(0 + x, 0 + y, buffer, "To: %s", cannedMessageModule->getNodeName(this->dest)); + display->drawString(0 + x, 0 + y + FONT_HEIGHT_SMALL, cannedMessageModule->getPrevMessage()); + display->fillRect(0 + x, 0 + y + FONT_HEIGHT_SMALL * 2, x + display->getWidth(), y + FONT_HEIGHT_SMALL); + display->setColor(BLACK); + display->drawString(0 + x, 0 + y + FONT_HEIGHT_SMALL * 2, cannedMessageModule->getCurrentMessage()); + display->setColor(WHITE); + display->drawString(0 + x, 0 + y + FONT_HEIGHT_SMALL * 3, cannedMessageModule->getNextMessage()); } } } diff --git a/src/modules/CannedMessageModule.h b/src/modules/CannedMessageModule.h index b9ef5c413..2edef4655 100644 --- a/src/modules/CannedMessageModule.h +++ b/src/modules/CannedMessageModule.h @@ -87,4 +87,4 @@ class CannedMessageModule : }; extern CannedMessageModule *cannedMessageModule; -#endif \ No newline at end of file +#endif diff --git a/src/modules/Modules.cpp b/src/modules/Modules.cpp index 91f3f2edd..986a375df 100644 --- a/src/modules/Modules.cpp +++ b/src/modules/Modules.cpp @@ -60,13 +60,15 @@ void setupModules() new DeviceTelemetryModule(); new EnvironmentTelemetryModule(); #endif +#if (defined(ARCH_ESP32) || defined(ARCH_NRF52)) && !defined(TTGO_T_ECHO) + new SerialModule(); +#endif #ifdef ARCH_ESP32 // Only run on an esp32 based device. /* Maintained by MC Hamster (Jm Casler) jm@casler.org */ - new SerialModule(); new ExternalNotificationModule(); storeForwardModule = new StoreForwardModule(); diff --git a/src/modules/PositionModule.cpp b/src/modules/PositionModule.cpp index 572d7063b..5159de278 100644 --- a/src/modules/PositionModule.cpp +++ b/src/modules/PositionModule.cpp @@ -108,7 +108,7 @@ MeshPacket *PositionModule::allocReply() // Strip out any time information before sending packets to other nodes - to keep the wire size small (and because other // nodes shouldn't trust it anyways) Note: we allow a device with a local GPS to include the time, so that gpsless // devices can get time. - if (getRTCQuality() < RTCQualityGPS) { + if (getRTCQuality() < RTCQualityDevice) { DEBUG_MSG("Stripping time %u from position send\n", p.time); p.time = 0; } else @@ -144,7 +144,7 @@ int32_t PositionModule::runOnce() if (lastGpsSend == 0 || (now - lastGpsSend) >= intervalMs) { // Only send packets if the channel is less than 40% utilized. - if (airTime->channelUtilizationPercent() < 40) { + if (airTime->channelUtilizationPercent() < max_channel_util_percent) { if (node->has_position && (node->position.latitude_i != 0 || node->position.longitude_i != 0)) { lastGpsSend = now; @@ -165,7 +165,7 @@ int32_t PositionModule::runOnce() } else if (config.position.position_broadcast_smart_enabled) { // Only send packets if the channel is less than 25% utilized. - if (airTime->channelUtilizationPercent() < 25) { + if (airTime->channelUtilizationPercent() < polite_channel_util_percent) { NodeInfo *node2 = service.refreshMyNodeInfo(); // should guarantee there is now a position diff --git a/src/modules/SerialModule.cpp b/src/modules/SerialModule.cpp index 59f489d52..05ed44cd7 100644 --- a/src/modules/SerialModule.cpp +++ b/src/modules/SerialModule.cpp @@ -2,6 +2,7 @@ #include "MeshService.h" #include "NodeDB.h" #include "RTC.h" +#include "NMEAWPL.h" #include "Router.h" #include "configuration.h" #include @@ -40,7 +41,7 @@ KNOWN PROBLEMS * Until the module is initilized by the startup sequence, the TX pin is in a floating state. Device connected to that pin may see this as "noise". - * Will not work on NRF and the Linux device targets. + * Will not work on T-Echo and the Linux device targets. */ @@ -62,16 +63,19 @@ char serialStringChar[Constants_DATA_PAYLOAD_LEN]; SerialModuleRadio::SerialModuleRadio() : MeshModule("SerialModuleRadio") { - // restrict to the admin channel for rx - boundChannel = Channels::serialChannel; switch (moduleConfig.serial.mode) { case ModuleConfig_SerialConfig_Serial_Mode_TEXTMSG: ourPortNum = PortNum_TEXT_MESSAGE_APP; break; + case ModuleConfig_SerialConfig_Serial_Mode_NMEA: + ourPortNum = PortNum_POSITION_APP; + break; default: ourPortNum = PortNum_SERIAL_APP; + // restrict to the serial channel for rx + boundChannel = Channels::serialChannel; break; } } @@ -175,15 +179,25 @@ int32_t SerialModule::runOnce() firstTime = 0; } else { - String serialString; - while (Serial2.available()) { - serialString = Serial2.readString(); - serialString.toCharArray(serialStringChar, Constants_DATA_PAYLOAD_LEN); + // in NMEA mode send out GGA every 2 seconds, Don't read from Port + if (moduleConfig.serial.mode == ModuleConfig_SerialConfig_Serial_Mode_NMEA) { + if (millis() - lastNmeaTime > 2000) { + lastNmeaTime = millis(); + printGGA(outbuf, nodeDB.getNode(myNodeInfo.my_node_num)->position); + Serial2.printf("%s", outbuf); + } + } else { + String serialString; - serialModuleRadio->sendPayload(); + while (Serial2.available()) { + serialString = Serial2.readString(); + serialString.toCharArray(serialStringChar, Constants_DATA_PAYLOAD_LEN); - DEBUG_MSG("Received: %s\n", serialStringChar); + serialModuleRadio->sendPayload(); + + DEBUG_MSG("Received: %s\n", serialStringChar); + } } } @@ -191,7 +205,7 @@ int32_t SerialModule::runOnce() } else { DEBUG_MSG("Serial Module Disabled\n"); - return (INT32_MAX); + return INT32_MAX; } #else return INT32_MAX; @@ -255,6 +269,19 @@ ProcessMessage SerialModuleRadio::handleReceived(const MeshPacket &mp) } else if (moduleConfig.serial.mode == ModuleConfig_SerialConfig_Serial_Mode_PROTO) { // TODO this needs to be implemented + } else if (moduleConfig.serial.mode == ModuleConfig_SerialConfig_Serial_Mode_NMEA) { + // Decode the Payload some more + Position scratch; + Position *decoded = NULL; + if (mp.which_payload_variant == MeshPacket_decoded_tag && mp.decoded.portnum == ourPortNum) { + memset(&scratch, 0, sizeof(scratch)); + if (pb_decode_from_bytes(p.payload.bytes, p.payload.size, Position_fields, &scratch)) { + decoded = &scratch; + } + // send position packet as WPL to the serial port + printWPL(outbuf, *decoded, nodeDB.getNode(getFrom(&mp))->user.long_name); + Serial2.printf("%s", outbuf); + } } } diff --git a/src/modules/SerialModule.h b/src/modules/SerialModule.h index e93cb5952..0130bbe2d 100644 --- a/src/modules/SerialModule.h +++ b/src/modules/SerialModule.h @@ -11,6 +11,8 @@ class SerialModule : private concurrency::OSThread { bool firstTime = 1; + unsigned long lastNmeaTime = millis(); + char outbuf[90] = ""; public: SerialModule(); @@ -28,6 +30,7 @@ extern SerialModule *serialModule; class SerialModuleRadio : public MeshModule { uint32_t lastRxID = 0; + char outbuf[90] = ""; public: SerialModuleRadio(); diff --git a/src/modules/Telemetry/DeviceTelemetry.cpp b/src/modules/Telemetry/DeviceTelemetry.cpp index 612cf6c1f..f3d787682 100644 --- a/src/modules/Telemetry/DeviceTelemetry.cpp +++ b/src/modules/Telemetry/DeviceTelemetry.cpp @@ -9,19 +9,23 @@ #include "main.h" #include #include +#include "MeshService.h" int32_t DeviceTelemetryModule::runOnce() { #ifndef ARCH_PORTDUINO - if (firstTime) { - // This is the first time the OSThread library has called this function, so do some setup - firstTime = 0; - DEBUG_MSG("Device Telemetry: Initializing\n"); + uint32_t now = millis(); + if ((lastSentToMesh == 0 || + (now - lastSentToMesh) >= getConfiguredOrDefaultMs(moduleConfig.telemetry.device_update_interval)) && + airTime->channelUtilizationPercent() < max_channel_util_percent) { + sendTelemetry(); + lastSentToMesh = now; + } 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) + sendTelemetry(NODENUM_BROADCAST, true); } - sendOurTelemetry(); - // OSThread library. Multiply the preference value by 1000 to convert seconds to miliseconds - - return getConfiguredOrDefaultMs(moduleConfig.telemetry.device_update_interval); + return sendToPhoneIntervalMs; #endif } @@ -29,14 +33,13 @@ bool DeviceTelemetryModule::handleReceivedProtobuf(const MeshPacket &mp, Telemet { if (t->which_variant == Telemetry_device_metrics_tag) { const char *sender = getSenderShortName(mp); - - DEBUG_MSG("-----------------------------------------\n"); - DEBUG_MSG("Device Telemetry: Received data from %s\n", sender); - DEBUG_MSG("Telemetry->time: %i\n", t->time); - DEBUG_MSG("Telemetry->air_util_tx: %f\n", t->variant.device_metrics.air_util_tx); - DEBUG_MSG("Telemetry->battery_level: %i\n", t->variant.device_metrics.battery_level); - DEBUG_MSG("Telemetry->channel_utilization: %f\n", t->variant.device_metrics.channel_utilization); - DEBUG_MSG("Telemetry->voltage: %f\n", t->variant.device_metrics.voltage); + + DEBUG_MSG("(Received from %s): air_util_tx=%f, channel_utilization=%f, battery_level=%i, voltage=%f\n", + sender, + t->variant.device_metrics.air_util_tx, + t->variant.device_metrics.channel_utilization, + t->variant.device_metrics.battery_level, + t->variant.device_metrics.voltage); lastMeasurementPacket = packetPool.allocCopy(mp); @@ -45,7 +48,7 @@ bool DeviceTelemetryModule::handleReceivedProtobuf(const MeshPacket &mp, Telemet return false; // Let others look at this message also if they want } -bool DeviceTelemetryModule::sendOurTelemetry(NodeNum dest, bool wantReplies) +bool DeviceTelemetryModule::sendTelemetry(NodeNum dest, bool phoneOnly) { Telemetry t; @@ -57,22 +60,25 @@ bool DeviceTelemetryModule::sendOurTelemetry(NodeNum dest, bool wantReplies) t.variant.device_metrics.channel_utilization = myNodeInfo.channel_utilization; t.variant.device_metrics.voltage = powerStatus->getBatteryVoltageMv() / 1000.0; - DEBUG_MSG("-----------------------------------------\n"); - DEBUG_MSG("Device Telemetry: Read data\n"); - - DEBUG_MSG("Telemetry->time: %i\n", t.time); - DEBUG_MSG("Telemetry->air_util_tx: %f\n", t.variant.device_metrics.air_util_tx); - DEBUG_MSG("Telemetry->battery_level: %i\n", t.variant.device_metrics.battery_level); - DEBUG_MSG("Telemetry->channel_utilization: %f\n", t.variant.device_metrics.channel_utilization); - DEBUG_MSG("Telemetry->voltage: %f\n", t.variant.device_metrics.voltage); + DEBUG_MSG("(Sending): air_util_tx=%f, channel_utilization=%f, battery_level=%i, voltage=%f\n", + t.variant.device_metrics.air_util_tx, + t.variant.device_metrics.channel_utilization, + t.variant.device_metrics.battery_level, + t.variant.device_metrics.voltage); MeshPacket *p = allocDataProtobuf(t); p->to = dest; - p->decoded.want_response = wantReplies; + p->decoded.want_response = false; + p->priority = MeshPacket_Priority_MIN; lastMeasurementPacket = packetPool.allocCopy(*p); - DEBUG_MSG("Device Telemetry: Sending packet to mesh\n"); - service.sendToMesh(p, RX_SRC_LOCAL, true); nodeDB.updateTelemetry(nodeDB.getNodeNum(), t, RX_SRC_LOCAL); + if (phoneOnly) { + DEBUG_MSG("Sending packet to phone\n"); + service.sendToPhone(p); + } else { + DEBUG_MSG("Sending packet to mesh\n"); + service.sendToMesh(p, RX_SRC_LOCAL, true); + } return true; } diff --git a/src/modules/Telemetry/DeviceTelemetry.h b/src/modules/Telemetry/DeviceTelemetry.h index 1a10ffcfa..0fb33ceee 100644 --- a/src/modules/Telemetry/DeviceTelemetry.h +++ b/src/modules/Telemetry/DeviceTelemetry.h @@ -11,7 +11,8 @@ class DeviceTelemetryModule : private concurrency::OSThread, public ProtobufModu DeviceTelemetryModule() : concurrency::OSThread("DeviceTelemetryModule"), ProtobufModule("DeviceTelemetry", PortNum_TELEMETRY_APP, &Telemetry_msg) { - lastMeasurementPacket = nullptr; + lastMeasurementPacket = nullptr; + setIntervalFromNow(10 * 1000); } virtual bool wantUIFrame() { return false; } @@ -24,9 +25,10 @@ class DeviceTelemetryModule : private concurrency::OSThread, public ProtobufModu /** * Send our Telemetry into the mesh */ - bool sendOurTelemetry(NodeNum dest = NODENUM_BROADCAST, bool wantReplies = false); + bool sendTelemetry(NodeNum dest = NODENUM_BROADCAST, bool phoneOnly = false); private: - bool firstTime = 1; + uint32_t sendToPhoneIntervalMs = SECONDS_IN_MINUTE * 1000; // Send to phone every minute + uint32_t lastSentToMesh = 0; const MeshPacket *lastMeasurementPacket; }; diff --git a/src/modules/Telemetry/EnvironmentTelemetry.cpp b/src/modules/Telemetry/EnvironmentTelemetry.cpp index 36e6a8176..cd5cd03bc 100644 --- a/src/modules/Telemetry/EnvironmentTelemetry.cpp +++ b/src/modules/Telemetry/EnvironmentTelemetry.cpp @@ -9,6 +9,7 @@ #include "main.h" #include #include +#include "MeshService.h" // Sensors #include "Sensor/BMP280Sensor.h" @@ -17,6 +18,8 @@ #include "Sensor/MCP9808Sensor.h" #include "Sensor/INA260Sensor.h" #include "Sensor/INA219Sensor.h" +#include "Sensor/SHTC3Sensor.h" +#include "Sensor/LPS22HBSensor.h" BMP280Sensor bmp280Sensor; BME280Sensor bme280Sensor; @@ -24,6 +27,8 @@ BME680Sensor bme680Sensor; MCP9808Sensor mcp9808Sensor; INA260Sensor ina260Sensor; INA219Sensor ina219Sensor; +SHTC3Sensor shtc3Sensor; +LPS22HBSensor lps22hbSensor; #define FAILED_STATE_SENSOR_READ_MULTIPLIER 10 #define DISPLAY_RECEIVEID_MEASUREMENTS_ON_SCREEN true @@ -83,21 +88,31 @@ int32_t EnvironmentTelemetryModule::runOnce() result = ina260Sensor.runOnce(); if (ina219Sensor.hasSensor()) result = ina219Sensor.runOnce(); + if (shtc3Sensor.hasSensor()) + result = shtc3Sensor.runOnce(); + if (lps22hbSensor.hasSensor()) { + result = lps22hbSensor.runOnce(); + } } return result; } else { // if we somehow got to a second run of this module with measurement disabled, then just wait forever if (!moduleConfig.telemetry.environment_measurement_enabled) return result; - // this is not the first time OSThread library has called this function - // so just do what we intend to do on the interval - if (!sendOurTelemetry()) { - // if we failed to read the sensor, then try again - // as soon as we can according to the maximum polling frequency - return DEFAULT_SENSOR_MINIMUM_WAIT_TIME_BETWEEN_READS; + + uint32_t now = millis(); + if ((lastSentToMesh == 0 || + (now - lastSentToMesh) >= getConfiguredOrDefaultMs(moduleConfig.telemetry.environment_update_interval)) && + airTime->channelUtilizationPercent() < max_channel_util_percent) { + sendTelemetry(); + lastSentToMesh = now; + } 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) + sendTelemetry(NODENUM_BROADCAST, true); } } - return getConfiguredOrDefaultMs(moduleConfig.telemetry.environment_update_interval); + return sendToPhoneIntervalMs; #endif } @@ -143,7 +158,7 @@ void EnvironmentTelemetryModule::drawFrame(OLEDDisplay *display, OLEDDisplayUiSt if (!pb_decode_from_bytes(p.payload.bytes, p.payload.size, Telemetry_fields, &lastMeasurement)) { display->setFont(FONT_SMALL); display->drawString(x, y += fontHeight(FONT_MEDIUM), "Measurement Error"); - DEBUG_MSG("Environment Telemetry: unable to decode last packet"); + DEBUG_MSG("Unable to decode last packet"); return; } @@ -168,15 +183,14 @@ bool EnvironmentTelemetryModule::handleReceivedProtobuf(const MeshPacket &mp, Te if (t->which_variant == Telemetry_environment_metrics_tag) { const char *sender = getSenderShortName(mp); - DEBUG_MSG("-----------------------------------------\n"); - DEBUG_MSG("Environment Telemetry: Received data from %s\n", sender); - DEBUG_MSG("Telemetry->time: %i\n", t->time); - DEBUG_MSG("Telemetry->barometric_pressure: %f\n", t->variant.environment_metrics.barometric_pressure); - DEBUG_MSG("Telemetry->current: %f\n", t->variant.environment_metrics.current); - DEBUG_MSG("Telemetry->gas_resistance: %f\n", t->variant.environment_metrics.gas_resistance); - DEBUG_MSG("Telemetry->relative_humidity: %f\n", t->variant.environment_metrics.relative_humidity); - DEBUG_MSG("Telemetry->temperature: %f\n", t->variant.environment_metrics.temperature); - DEBUG_MSG("Telemetry->voltage: %f\n", t->variant.environment_metrics.voltage); + DEBUG_MSG("(Received from %s): barometric_pressure=%f, current=%f, gas_resistance=%f, relative_humidity=%f, temperature=%f, voltage=%f\n", + sender, + t->variant.environment_metrics.barometric_pressure, + t->variant.environment_metrics.current, + t->variant.environment_metrics.gas_resistance, + t->variant.environment_metrics.relative_humidity, + t->variant.environment_metrics.temperature, + t->variant.environment_metrics.voltage); lastMeasurementPacket = packetPool.allocCopy(mp); } @@ -184,7 +198,7 @@ bool EnvironmentTelemetryModule::handleReceivedProtobuf(const MeshPacket &mp, Te return false; // Let others look at this message also if they want } -bool EnvironmentTelemetryModule::sendOurTelemetry(NodeNum dest, bool wantReplies) +bool EnvironmentTelemetryModule::sendTelemetry(NodeNum dest, bool phoneOnly) { Telemetry m; m.time = getTime(); @@ -197,9 +211,10 @@ bool EnvironmentTelemetryModule::sendOurTelemetry(NodeNum dest, bool wantReplies m.variant.environment_metrics.temperature = 0; m.variant.environment_metrics.voltage = 0; - DEBUG_MSG("-----------------------------------------\n"); - DEBUG_MSG("Environment Telemetry: Read data\n"); - + if (lps22hbSensor.hasSensor()) + lps22hbSensor.getMetrics(&m); + if (shtc3Sensor.hasSensor()) + shtc3Sensor.getMetrics(&m); if (bmp280Sensor.hasSensor()) bmp280Sensor.getMetrics(&m); if (bme280Sensor.hasSensor()) @@ -213,22 +228,28 @@ bool EnvironmentTelemetryModule::sendOurTelemetry(NodeNum dest, bool wantReplies if (ina260Sensor.hasSensor()) ina260Sensor.getMetrics(&m); - DEBUG_MSG("Telemetry->time: %i\n", m.time); - DEBUG_MSG("Telemetry->barometric_pressure: %f\n", m.variant.environment_metrics.barometric_pressure); - DEBUG_MSG("Telemetry->current: %f\n", m.variant.environment_metrics.current); - DEBUG_MSG("Telemetry->gas_resistance: %f\n", m.variant.environment_metrics.gas_resistance); - DEBUG_MSG("Telemetry->relative_humidity: %f\n", m.variant.environment_metrics.relative_humidity); - DEBUG_MSG("Telemetry->temperature: %f\n", m.variant.environment_metrics.temperature); - DEBUG_MSG("Telemetry->voltage: %f\n", m.variant.environment_metrics.voltage); + DEBUG_MSG("(Sending): barometric_pressure=%f, current=%f, gas_resistance=%f, relative_humidity=%f, temperature=%f, voltage=%f\n", + m.variant.environment_metrics.barometric_pressure, + m.variant.environment_metrics.current, + m.variant.environment_metrics.gas_resistance, + m.variant.environment_metrics.relative_humidity, + m.variant.environment_metrics.temperature, + m.variant.environment_metrics.voltage); sensor_read_error_count = 0; MeshPacket *p = allocDataProtobuf(m); p->to = dest; - p->decoded.want_response = wantReplies; + p->decoded.want_response = false; + p->priority = MeshPacket_Priority_MIN; lastMeasurementPacket = packetPool.allocCopy(*p); - DEBUG_MSG("Environment Telemetry: Sending packet to mesh"); - service.sendToMesh(p, RX_SRC_LOCAL, true); + if (phoneOnly) { + DEBUG_MSG("Sending packet to phone\n"); + service.sendToPhone(p); + } else { + DEBUG_MSG("Sending packet to mesh\n"); + service.sendToMesh(p, RX_SRC_LOCAL, true); + } return true; } diff --git a/src/modules/Telemetry/EnvironmentTelemetry.h b/src/modules/Telemetry/EnvironmentTelemetry.h index e1a612288..c081ead51 100644 --- a/src/modules/Telemetry/EnvironmentTelemetry.h +++ b/src/modules/Telemetry/EnvironmentTelemetry.h @@ -12,7 +12,8 @@ class EnvironmentTelemetryModule : private concurrency::OSThread, public Protobu : concurrency::OSThread("EnvironmentTelemetryModule"), ProtobufModule("EnvironmentTelemetry", PortNum_TELEMETRY_APP, &Telemetry_msg) { - lastMeasurementPacket = nullptr; + lastMeasurementPacket = nullptr; + setIntervalFromNow(10 * 1000); } virtual bool wantUIFrame() override; #if !HAS_SCREEN @@ -30,11 +31,13 @@ class EnvironmentTelemetryModule : private concurrency::OSThread, public Protobu /** * Send our Telemetry into the mesh */ - bool sendOurTelemetry(NodeNum dest = NODENUM_BROADCAST, bool wantReplies = false); + bool sendTelemetry(NodeNum dest = NODENUM_BROADCAST, bool wantReplies = false); private: float CelsiusToFahrenheit(float c); bool firstTime = 1; const MeshPacket *lastMeasurementPacket; + uint32_t sendToPhoneIntervalMs = SECONDS_IN_MINUTE * 1000; // Send to phone every minute + uint32_t lastSentToMesh = 0; uint32_t sensor_read_error_count = 0; }; diff --git a/src/modules/Telemetry/Sensor/LPS22HBSensor.cpp b/src/modules/Telemetry/Sensor/LPS22HBSensor.cpp new file mode 100644 index 000000000..1209c1435 --- /dev/null +++ b/src/modules/Telemetry/Sensor/LPS22HBSensor.cpp @@ -0,0 +1,36 @@ +#include "../mesh/generated/telemetry.pb.h" +#include "configuration.h" +#include "TelemetrySensor.h" +#include "LPS22HBSensor.h" +#include +#include + +LPS22HBSensor::LPS22HBSensor() : + TelemetrySensor(TelemetrySensorType_LPS22, "LPS22HB") +{ +} + +int32_t LPS22HBSensor::runOnce() { + DEBUG_MSG("Init sensor: %s\n", sensorName); + if (!hasSensor()) { + return DEFAULT_SENSOR_MINIMUM_WAIT_TIME_BETWEEN_READS; + } + status = lps22hb.begin_I2C(nodeTelemetrySensorsMap[sensorType]); + return initI2CSensor(); +} + +void LPS22HBSensor::setup() +{ + lps22hb.setDataRate(LPS22_RATE_10_HZ); +} + +bool LPS22HBSensor::getMetrics(Telemetry *measurement) { + sensors_event_t temp; + sensors_event_t pressure; + lps22hb.getEvent(&pressure, &temp); + + measurement->variant.environment_metrics.temperature = temp.temperature; + measurement->variant.environment_metrics.barometric_pressure = pressure.pressure; + + return true; +} \ No newline at end of file diff --git a/src/modules/Telemetry/Sensor/LPS22HBSensor.h b/src/modules/Telemetry/Sensor/LPS22HBSensor.h new file mode 100644 index 000000000..1ded032fb --- /dev/null +++ b/src/modules/Telemetry/Sensor/LPS22HBSensor.h @@ -0,0 +1,17 @@ +#include "../mesh/generated/telemetry.pb.h" +#include "TelemetrySensor.h" +#include +#include + +class LPS22HBSensor : virtual public TelemetrySensor { +private: + Adafruit_LPS22 lps22hb; + +protected: + virtual void setup() override; + +public: + LPS22HBSensor(); + virtual int32_t runOnce() override; + virtual bool getMetrics(Telemetry *measurement) override; +}; \ No newline at end of file diff --git a/src/modules/Telemetry/Sensor/SHTC3Sensor.cpp b/src/modules/Telemetry/Sensor/SHTC3Sensor.cpp new file mode 100644 index 000000000..b3a76ba91 --- /dev/null +++ b/src/modules/Telemetry/Sensor/SHTC3Sensor.cpp @@ -0,0 +1,34 @@ +#include "../mesh/generated/telemetry.pb.h" +#include "configuration.h" +#include "TelemetrySensor.h" +#include "SHTC3Sensor.h" +#include + +SHTC3Sensor::SHTC3Sensor() : + TelemetrySensor(TelemetrySensorType_SHTC3, "SHTC3") +{ +} + +int32_t SHTC3Sensor::runOnce() { + DEBUG_MSG("Init sensor: %s\n", sensorName); + if (!hasSensor()) { + return DEFAULT_SENSOR_MINIMUM_WAIT_TIME_BETWEEN_READS; + } + status = shtc3.begin(); + return initI2CSensor(); +} + +void SHTC3Sensor::setup() +{ + // Set up oversampling and filter initialization +} + +bool SHTC3Sensor::getMetrics(Telemetry *measurement) { + sensors_event_t humidity, temp; + shtc3.getEvent(&humidity, &temp); + + measurement->variant.environment_metrics.temperature = temp.temperature; + measurement->variant.environment_metrics.relative_humidity = humidity.relative_humidity; + + return true; +} \ No newline at end of file diff --git a/src/modules/Telemetry/Sensor/SHTC3Sensor.h b/src/modules/Telemetry/Sensor/SHTC3Sensor.h new file mode 100644 index 000000000..28a1648bb --- /dev/null +++ b/src/modules/Telemetry/Sensor/SHTC3Sensor.h @@ -0,0 +1,16 @@ +#include "../mesh/generated/telemetry.pb.h" +#include "TelemetrySensor.h" +#include + +class SHTC3Sensor : virtual public TelemetrySensor { +private: + Adafruit_SHTC3 shtc3 = Adafruit_SHTC3(); + +protected: + virtual void setup() override; + +public: + SHTC3Sensor(); + virtual int32_t runOnce() override; + virtual bool getMetrics(Telemetry *measurement) override; +}; \ No newline at end of file diff --git a/src/modules/esp32/StoreForwardModule.cpp b/src/modules/esp32/StoreForwardModule.cpp index 95aeef709..e51296824 100644 --- a/src/modules/esp32/StoreForwardModule.cpp +++ b/src/modules/esp32/StoreForwardModule.cpp @@ -27,7 +27,7 @@ int32_t StoreForwardModule::runOnce() if (this->busy) { // Only send packets if the channel is less than 25% utilized. - if (airTime->channelUtilizationPercent() < 25) { + if (airTime->channelUtilizationPercent() < polite_channel_util_percent) { // DEBUG_MSG("--- --- --- In busy loop 1 %d\n", this->packetHistoryTXQueue_index); storeForwardModule->sendPayload(this->busyTo, this->packetHistoryTXQueue_index); diff --git a/src/mqtt/MQTT.cpp b/src/mqtt/MQTT.cpp index 7087c58c8..7deba4668 100644 --- a/src/mqtt/MQTT.cpp +++ b/src/mqtt/MQTT.cpp @@ -27,8 +27,9 @@ void MQTT::onPublish(char *topic, byte *payload, unsigned int length) { // parsing ServiceEnvelope ServiceEnvelope e = ServiceEnvelope_init_default; - if (!pb_decode_from_bytes(payload, length, ServiceEnvelope_fields, &e) && moduleConfig.mqtt.json_enabled) { - // check if this is a json payload message + + if (moduleConfig.mqtt.json_enabled && (strncmp(topic, jsonTopic.c_str(), jsonTopic.length()) == 0)) { + // check if this is a json payload message by comparing the topic start using namespace json11; char payloadStr[length + 1]; memcpy(payloadStr, payload, length); @@ -36,36 +37,37 @@ void MQTT::onPublish(char *topic, byte *payload, unsigned int length) std::string err; auto json = Json::parse(payloadStr, err); if (err.empty()) { - DEBUG_MSG("Received json payload on MQTT, parsing..\n"); + DEBUG_MSG("JSON Received on MQTT, parsing..\n"); // check if it is a valid envelope - if (json.object_items().count("sender") != 0 && json.object_items().count("payload") != 0) { + if (json.object_items().count("sender") != 0 && json.object_items().count("payload") != 0 && json["type"].string_value().compare("sendtext") == 0) { // this is a valid envelope if (json["sender"].string_value().compare(owner.id) != 0) { std::string jsonPayloadStr = json["payload"].dump(); - DEBUG_MSG("Received json payload %s, length %u\n", jsonPayloadStr.c_str(), jsonPayloadStr.length()); - // FIXME Not sure we need to be doing this + DEBUG_MSG("JSON payload %s, length %u\n", jsonPayloadStr.c_str(), jsonPayloadStr.length()); + // construct protobuf data packet using TEXT_MESSAGE, send it to the mesh - // MeshPacket *p = router->allocForSending(); - // p->decoded.portnum = PortNum_TEXT_MESSAGE_APP; - // if (jsonPayloadStr.length() <= sizeof(p->decoded.payload.bytes)) { - // memcpy(p->decoded.payload.bytes, jsonPayloadStr.c_str(), jsonPayloadStr.length()); - // p->decoded.payload.size = jsonPayloadStr.length(); - // MeshPacket *packet = packetPool.allocCopy(*p); - // service.sendToMesh(packet, RX_SRC_LOCAL); - // } else { - // DEBUG_MSG("Received MQTT json payload too long, dropping\n"); - // } + MeshPacket *p = router->allocForSending(); + p->decoded.portnum = PortNum_TEXT_MESSAGE_APP; + if (jsonPayloadStr.length() <= sizeof(p->decoded.payload.bytes)) { + memcpy(p->decoded.payload.bytes, jsonPayloadStr.c_str(), jsonPayloadStr.length()); + p->decoded.payload.size = jsonPayloadStr.length(); + MeshPacket *packet = packetPool.allocCopy(*p); + service.sendToMesh(packet, RX_SRC_LOCAL); + } else { + DEBUG_MSG("Received MQTT json payload too long, dropping\n"); + } } else { - DEBUG_MSG("Ignoring downlink message we originally sent.\n"); + DEBUG_MSG("JSON Ignoring downlink message we originally sent.\n"); } } else { - DEBUG_MSG("Received json payload on MQTT but not a valid envelope\n"); + DEBUG_MSG("JSON Received payload on MQTT but not a valid envelope\n"); } } else { // no json, this is an invalid payload DEBUG_MSG("Invalid MQTT service envelope, topic %s, len %u!\n", topic, length); } } else { + pb_decode_from_bytes(payload, length, ServiceEnvelope_fields, &e); if (strcmp(e.gateway_id, owner.id) == 0) DEBUG_MSG("Ignoring downlink message we originally sent.\n"); else { @@ -243,7 +245,7 @@ void MQTT::onSend(const MeshPacket &mp, ChannelIndex chIndex) auto jsonString = this->downstreamPacketToJson((MeshPacket *)&mp); if (jsonString.length() != 0) { String topicJson = jsonTopic + channelId + "/" + owner.id; - DEBUG_MSG("publish json message to %s, %u bytes: %s\n", topicJson.c_str(), jsonString.length(), jsonString.c_str()); + DEBUG_MSG("JSON publish message to %s, %u bytes: %s\n", topicJson.c_str(), jsonString.length(), jsonString.c_str()); pubSub.publish(topicJson.c_str(), jsonString.c_str(), false); } } @@ -251,7 +253,7 @@ void MQTT::onSend(const MeshPacket &mp, ChannelIndex chIndex) } // converts a downstream packet into a json message -String MQTT::downstreamPacketToJson(MeshPacket *mp) +std::string MQTT::downstreamPacketToJson(MeshPacket *mp) { using namespace json11; @@ -279,7 +281,7 @@ String MQTT::downstreamPacketToJson(MeshPacket *mp) // if it isn't, then we need to create a json object // with the string as the value DEBUG_MSG("text message payload is of type plaintext\n"); - msgPayload = Json::object({{"text", payloadStr}}); + msgPayload = Json::object{{"text", payloadStr}}; } break; } @@ -396,8 +398,8 @@ String MQTT::downstreamPacketToJson(MeshPacket *mp) }; // serialize and return it - static std::string jsonStr = jsonObj.dump(); + std::string jsonStr = jsonObj.dump(); DEBUG_MSG("serialized json message: %s\n", jsonStr.c_str()); - return jsonStr.c_str(); + return jsonStr; } diff --git a/src/mqtt/MQTT.h b/src/mqtt/MQTT.h index cfb68354e..9d80c7d91 100644 --- a/src/mqtt/MQTT.h +++ b/src/mqtt/MQTT.h @@ -58,7 +58,7 @@ class MQTT : private concurrency::OSThread void onPublish(char *topic, byte *payload, unsigned int length); /// Called when a new publish arrives from the MQTT server - String downstreamPacketToJson(MeshPacket *mp); + std::string downstreamPacketToJson(MeshPacket *mp); /// Return 0 if sleep is okay, veto sleep if we are connected to pubsub server // int preflightSleepCb(void *unused = NULL) { return pubSub.connected() ? 1 : 0; } diff --git a/src/platform/esp32/BleOta.cpp b/src/platform/esp32/BleOta.cpp index 8062eede6..ef93a240b 100644 --- a/src/platform/esp32/BleOta.cpp +++ b/src/platform/esp32/BleOta.cpp @@ -5,16 +5,13 @@ static const String MESHTASTIC_OTA_APP_PROJECT_NAME("Meshtastic-OTA"); const esp_partition_t* BleOta::findEspOtaAppPartition() { - const esp_partition_t *part - = esp_partition_find_first(ESP_PARTITION_TYPE_APP, ESP_PARTITION_SUBTYPE_APP_OTA_1, nullptr); + const esp_partition_t *part = esp_partition_find_first(ESP_PARTITION_TYPE_APP, ESP_PARTITION_SUBTYPE_APP_OTA_0, nullptr); esp_app_desc_t app_desc; esp_err_t ret = ESP_ERROR_CHECK_WITHOUT_ABORT(esp_ota_get_partition_description(part, &app_desc)); if (ret != ESP_OK || MESHTASTIC_OTA_APP_PROJECT_NAME != app_desc.project_name) { - part - = esp_partition_find_first(ESP_PARTITION_TYPE_APP, ESP_PARTITION_SUBTYPE_APP_OTA_1, - nullptr); + part = esp_partition_find_first(ESP_PARTITION_TYPE_APP, ESP_PARTITION_SUBTYPE_APP_OTA_1, nullptr); ret = ESP_ERROR_CHECK_WITHOUT_ABORT(esp_ota_get_partition_description(part, &app_desc)); } diff --git a/src/platform/esp32/main-esp32.cpp b/src/platform/esp32/main-esp32.cpp index 134d89332..9e8990d97 100644 --- a/src/platform/esp32/main-esp32.cpp +++ b/src/platform/esp32/main-esp32.cpp @@ -4,6 +4,7 @@ #include "main.h" #include "nimble/NimbleBluetooth.h" +#include "BleOta.h" #include "mesh/http/WiFiAPClient.h" #include "sleep.h" @@ -63,6 +64,12 @@ void esp32Setup() preferences.putUInt("rebootCounter", rebootCounter); preferences.end(); DEBUG_MSG("Number of Device Reboots: %d\n", rebootCounter); + String BLEOTA=BleOta::getOtaAppVersion(); + if (BLEOTA.isEmpty()) { + DEBUG_MSG("No OTA firmware available\n"); + }else{ + DEBUG_MSG("OTA firmware version %s\n", BLEOTA); + } // enableModemSleep(); diff --git a/src/platform/nrf52/NRF52Bluetooth.cpp b/src/platform/nrf52/NRF52Bluetooth.cpp index bfa3887ed..acb26071f 100644 --- a/src/platform/nrf52/NRF52Bluetooth.cpp +++ b/src/platform/nrf52/NRF52Bluetooth.cpp @@ -244,6 +244,9 @@ void NRF52Bluetooth::setup() Bluefruit.Periph.setConnectCallback(onConnect); Bluefruit.Periph.setDisconnectCallback(onDisconnect); + bledfu.setPermission(SECMODE_ENC_WITH_MITM, SECMODE_ENC_WITH_MITM); + bledfu.begin(); // Install the DFU helper + // Configure and Start the Device Information Service DEBUG_MSG("Configuring the Device Information Service\n"); bledis.setModel(optstr(HW_VERSION)); @@ -254,7 +257,7 @@ void NRF52Bluetooth::setup() DEBUG_MSG("Configuring the Battery Service\n"); blebas.begin(); blebas.write(0); // Unknown battery level for now - bledfu.begin(); // Install the DFU helper + // Setup the Heart Rate Monitor service using // BLEService and BLECharacteristic classes diff --git a/variants/rak11200/variant.h b/variants/rak11200/variant.h index 6886a3ad3..0962d4f45 100644 --- a/variants/rak11200/variant.h +++ b/variants/rak11200/variant.h @@ -59,7 +59,7 @@ static const uint8_t SCK = 33; // https://docs.rakwireless.com/Product-Categories/WisBlock/RAK13300/ -#define LORA_DIO0 26 // a No connect on the SX1262/SX1268 module +#define LORA_DIO0 -1 // a No connect on the SX1262/SX1268 module #define LORA_RESET WB_IO4 // RST for SX1276, and for SX1262/SX1268 #define LORA_DIO1 WB_IO6 // IRQ for SX1262/SX1268 #define LORA_DIO2 WB_IO5 // BUSY for SX1262/SX1268 diff --git a/variants/pico/platformio.ini b/variants/rpipico/platformio.ini similarity index 90% rename from variants/pico/platformio.ini rename to variants/rpipico/platformio.ini index 4f238fa14..122fbd42f 100644 --- a/variants/pico/platformio.ini +++ b/variants/rpipico/platformio.ini @@ -1,12 +1,12 @@ [env:pico] extends = rp2040_base -board = pico +board = rpipico upload_protocol = picotool # add our variants files to the include and src paths build_flags = ${rp2040_base.build_flags} -DPRIVATE_HW - -Ivariants/pico + -Ivariants/rpipico -DARDUINO_AVR_NANO_EVERY -DDEBUG_RP2040_WIRE -DDEBUG_RP2040_SPI diff --git a/variants/pico/variant.h b/variants/rpipico/variant.h similarity index 100% rename from variants/pico/variant.h rename to variants/rpipico/variant.h diff --git a/variants/rpipicow/platformio.ini b/variants/rpipicow/platformio.ini new file mode 100644 index 000000000..697ec1caa --- /dev/null +++ b/variants/rpipicow/platformio.ini @@ -0,0 +1,17 @@ +[env:picow] +extends = rp2040_base +board = rpipicow +upload_protocol = picotool + +# add our variants files to the include and src paths +build_flags = ${rp2040_base.build_flags} + -DPRIVATE_HW + -Ivariants/rpipicow + -DARDUINO_AVR_NANO_EVERY + -DDEBUG_RP2040_WIRE + -DDEBUG_RP2040_SPI + -DDEBUG_RP2040_CORE + -DDEBUG_RP2040_PORT=Serial + -DUSE_TINYUSB +lib_deps = + ${rp2040_base.lib_deps} \ No newline at end of file diff --git a/variants/rpipicow/variant.h b/variants/rpipicow/variant.h new file mode 100644 index 000000000..d620a356b --- /dev/null +++ b/variants/rpipicow/variant.h @@ -0,0 +1,52 @@ +// #define RADIOLIB_CUSTOM_ARDUINO 1 +// #define RADIOLIB_TONE_UNSUPPORTED 1 +// #define RADIOLIB_SOFTWARE_SERIAL_UNSUPPORTED 1 + +#define ARDUINO_ARCH_AVR + +#define CBC 0 +#define CTR 1 +#define ECB 0 + +#define NO_GPS 1 +#define USE_SH1106 1 +#undef GPS_SERIAL_NUM + +// #define I2C_SDA 6 +// #define I2C_SCL 7 + +#define BUTTON_PIN 17 +#define EXT_NOTIFY_OUT 4 + +#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 USE_RF95 +#define USE_SX1262 + +#undef RF95_SCK +#undef RF95_MISO +#undef RF95_MOSI +#undef RF95_NSS + +#define RF95_SCK 10 +#define RF95_MISO 12 +#define RF95_MOSI 11 +#define RF95_NSS 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 RF95_NSS +#define SX126X_DIO1 LORA_DIO1 +#define SX126X_BUSY LORA_DIO2 +#define SX126X_RESET LORA_RESET +#define SX126X_E22 +#endif + +#include \ No newline at end of file diff --git a/version.properties b/version.properties index f1a44dd8a..750a9609f 100644 --- a/version.properties +++ b/version.properties @@ -1,4 +1,4 @@ [VERSION] major = 1 minor = 3 -build = 43 +build = 47