diff --git a/.github/actions/build-variant/action.yml b/.github/actions/build-variant/action.yml index 80d2a56bb..b3303d393 100644 --- a/.github/actions/build-variant/action.yml +++ b/.github/actions/build-variant/action.yml @@ -51,6 +51,7 @@ runs: file: build.tar target: build.tar token: ${{ inputs.github_token }} + version: tags/v2.5.3 - name: Unpack web ui if: inputs.include-web-ui == 'true' diff --git a/.github/workflows/build_esp32.yml b/.github/workflows/build_esp32.yml index 7d069e3db..4fc31f22c 100644 --- a/.github/workflows/build_esp32.yml +++ b/.github/workflows/build_esp32.yml @@ -7,6 +7,8 @@ on: required: true type: string +permissions: read-all + jobs: build-esp32: runs-on: ubuntu-latest @@ -24,6 +26,7 @@ jobs: ./arch/esp32/esp32s2.ini ./arch/esp32/esp32s3.ini ./arch/esp32/esp32c3.ini + ./arch/esp32/esp32c6.ini build-script-path: bin/build-esp32.sh ota-firmware-source: firmware.bin ota-firmware-target: release/bleota.bin diff --git a/.github/workflows/build_esp32_c3.yml b/.github/workflows/build_esp32_c3.yml index 5234dbe81..546762952 100644 --- a/.github/workflows/build_esp32_c3.yml +++ b/.github/workflows/build_esp32_c3.yml @@ -26,10 +26,12 @@ jobs: ./arch/esp32/esp32s2.ini ./arch/esp32/esp32s3.ini ./arch/esp32/esp32c3.ini + ./arch/esp32/esp32c6.ini build-script-path: bin/build-esp32.sh ota-firmware-source: firmware-c3.bin ota-firmware-target: release/bleota-c3.bin artifact-paths: | release/*.bin release/*.elf + include-web-ui: true arch: esp32c3 diff --git a/.github/workflows/build_esp32_c6.yml b/.github/workflows/build_esp32_c6.yml index 66f2764a6..56d4d806d 100644 --- a/.github/workflows/build_esp32_c6.yml +++ b/.github/workflows/build_esp32_c6.yml @@ -33,4 +33,5 @@ jobs: artifact-paths: | release/*.bin release/*.elf + include-web-ui: true arch: esp32c6 diff --git a/.github/workflows/build_esp32_s3.yml b/.github/workflows/build_esp32_s3.yml index 554b37cef..a9c067ee1 100644 --- a/.github/workflows/build_esp32_s3.yml +++ b/.github/workflows/build_esp32_s3.yml @@ -7,6 +7,8 @@ on: required: true type: string +permissions: read-all + jobs: build-esp32-s3: runs-on: ubuntu-latest @@ -24,6 +26,7 @@ jobs: ./arch/esp32/esp32s2.ini ./arch/esp32/esp32s3.ini ./arch/esp32/esp32c3.ini + ./arch/esp32/esp32c6.ini build-script-path: bin/build-esp32.sh ota-firmware-source: firmware-s3.bin ota-firmware-target: release/bleota-s3.bin diff --git a/.github/workflows/build_raspbian.yml b/.github/workflows/build_raspbian.yml index 1fd8fad30..e857ae635 100644 --- a/.github/workflows/build_raspbian.yml +++ b/.github/workflows/build_raspbian.yml @@ -13,8 +13,8 @@ jobs: - name: Install libbluetooth shell: bash run: | - apt-get update -y --fix-missing - apt-get install -y libbluetooth-dev libgpiod-dev libyaml-cpp-dev openssl libssl-dev libulfius-dev liborcania-dev + sudo apt-get update -y --fix-missing + sudo apt-get install -y libbluetooth-dev libgpiod-dev libyaml-cpp-dev openssl libssl-dev libulfius-dev liborcania-dev - name: Checkout code uses: actions/checkout@v4 diff --git a/.github/workflows/build_raspbian_armv7l.yml b/.github/workflows/build_raspbian_armv7l.yml index 39b297d1b..f7fddd038 100644 --- a/.github/workflows/build_raspbian_armv7l.yml +++ b/.github/workflows/build_raspbian_armv7l.yml @@ -13,8 +13,8 @@ jobs: - name: Install libbluetooth shell: bash run: | - apt-get update -y --fix-missing - apt-get install -y libbluetooth-dev libgpiod-dev libyaml-cpp-dev openssl libssl-dev libulfius-dev liborcania-dev + sudo apt-get update -y --fix-missing + sudo apt-get install -y libbluetooth-dev libgpiod-dev libyaml-cpp-dev openssl libssl-dev libulfius-dev liborcania-dev - name: Checkout code uses: actions/checkout@v4 diff --git a/.github/workflows/main_matrix.yml b/.github/workflows/main_matrix.yml index 0853df19f..37164b758 100644 --- a/.github/workflows/main_matrix.yml +++ b/.github/workflows/main_matrix.yml @@ -203,6 +203,7 @@ jobs: ./device-*.sh ./device-*.bat ./littlefs-*.bin + ./littlefswebui-*.bin ./bleota*bin ./Meshtastic_nRF52_factory_erase*.uf2 retention-days: 30 @@ -245,7 +246,8 @@ jobs: if: ${{ github.event_name == 'workflow_dispatch' }} outputs: upload_url: ${{ steps.create_release.outputs.upload_url }} - needs: [ + needs: + [ gather-artifacts, package-raspbian, package-raspbian-armv7l, diff --git a/.github/workflows/package_amd64.yml b/.github/workflows/package_amd64.yml index 0b5093f24..c11566deb 100644 --- a/.github/workflows/package_amd64.yml +++ b/.github/workflows/package_amd64.yml @@ -50,11 +50,18 @@ jobs: mkdir -p .debpkg/usr/share/doc/meshtasticd/web mkdir -p .debpkg/usr/sbin mkdir -p .debpkg/etc/meshtasticd + mkdir -p .debpkg/etc/meshtasticd/config.d + mkdir -p .debpkg/etc/meshtasticd/available.d mkdir -p .debpkg/usr/lib/systemd/system/ tar -xf build.tar -C .debpkg/usr/share/doc/meshtasticd/web - gunzip .debpkg/usr/share/doc/meshtasticd/web/*.gz + shopt -s dotglob nullglob + if [ -d .debpkg/usr/share/doc/meshtasticd/web/build ]; then mv .debpkg/usr/share/doc/meshtasticd/web/build/* .debpkg/usr/share/doc/meshtasticd/web/; fi + if [ -d .debpkg/usr/share/doc/meshtasticd/web/build ]; then rmdir .debpkg/usr/share/doc/meshtasticd/web/build; fi + if [ -d .debpkg/usr/share/doc/meshtasticd/web/.DS_Store]; then rm -f .debpkg/usr/share/doc/meshtasticd/web/.DS_Store; fi + gunzip .debpkg/usr/share/doc/meshtasticd/web/ -r cp release/meshtasticd_linux_x86_64 .debpkg/usr/sbin/meshtasticd cp bin/config-dist.yaml .debpkg/etc/meshtasticd/config.yaml + cp bin/config.d/* .debpkg/etc/meshtasticd/available.d/ chmod +x .debpkg/usr/sbin/meshtasticd cp bin/meshtasticd.service .debpkg/usr/lib/systemd/system/meshtasticd.service echo "/etc/meshtasticd/config.yaml" > .debpkg/DEBIAN/conffiles diff --git a/.github/workflows/package_raspbian.yml b/.github/workflows/package_raspbian.yml index bcbda53e2..56b683fdd 100644 --- a/.github/workflows/package_raspbian.yml +++ b/.github/workflows/package_raspbian.yml @@ -50,11 +50,18 @@ jobs: mkdir -p .debpkg/usr/share/doc/meshtasticd/web mkdir -p .debpkg/usr/sbin mkdir -p .debpkg/etc/meshtasticd + mkdir -p .debpkg/etc/meshtasticd/config.d + mkdir -p .debpkg/etc/meshtasticd/available.d mkdir -p .debpkg/usr/lib/systemd/system/ tar -xf build.tar -C .debpkg/usr/share/doc/meshtasticd/web - gunzip .debpkg/usr/share/doc/meshtasticd/web/*.gz + shopt -s dotglob nullglob + if [ -d .debpkg/usr/share/doc/meshtasticd/web/build ]; then mv .debpkg/usr/share/doc/meshtasticd/web/build/* .debpkg/usr/share/doc/meshtasticd/web/; fi + if [ -d .debpkg/usr/share/doc/meshtasticd/web/build ]; then rmdir .debpkg/usr/share/doc/meshtasticd/web/build; fi + if [ -d .debpkg/usr/share/doc/meshtasticd/web/.DS_Store]; then rm -f .debpkg/usr/share/doc/meshtasticd/web/.DS_Store; fi + gunzip .debpkg/usr/share/doc/meshtasticd/web/ -r cp release/meshtasticd_linux_aarch64 .debpkg/usr/sbin/meshtasticd cp bin/config-dist.yaml .debpkg/etc/meshtasticd/config.yaml + cp bin/config.d/* .debpkg/etc/meshtasticd/available.d/ chmod +x .debpkg/usr/sbin/meshtasticd cp bin/meshtasticd.service .debpkg/usr/lib/systemd/system/meshtasticd.service echo "/etc/meshtasticd/config.yaml" > .debpkg/DEBIAN/conffiles diff --git a/.github/workflows/package_raspbian_armv7l.yml b/.github/workflows/package_raspbian_armv7l.yml index 1308fe925..663903e10 100644 --- a/.github/workflows/package_raspbian_armv7l.yml +++ b/.github/workflows/package_raspbian_armv7l.yml @@ -50,11 +50,18 @@ jobs: mkdir -p .debpkg/usr/share/doc/meshtasticd/web mkdir -p .debpkg/usr/sbin mkdir -p .debpkg/etc/meshtasticd + mkdir -p .debpkg/etc/meshtasticd/config.d + mkdir -p .debpkg/etc/meshtasticd/available.d mkdir -p .debpkg/usr/lib/systemd/system/ tar -xf build.tar -C .debpkg/usr/share/doc/meshtasticd/web - gunzip .debpkg/usr/share/doc/meshtasticd/web/*.gz + shopt -s dotglob nullglob + if [ -d .debpkg/usr/share/doc/meshtasticd/web/build ]; then mv .debpkg/usr/share/doc/meshtasticd/web/build/* .debpkg/usr/share/doc/meshtasticd/web/; fi + if [ -d .debpkg/usr/share/doc/meshtasticd/web/build ]; then rmdir .debpkg/usr/share/doc/meshtasticd/web/build; fi + if [ -d .debpkg/usr/share/doc/meshtasticd/web/.DS_Store]; then rm -f .debpkg/usr/share/doc/meshtasticd/web/.DS_Store; fi + gunzip .debpkg/usr/share/doc/meshtasticd/web/ -r cp release/meshtasticd_linux_armv7l .debpkg/usr/sbin/meshtasticd cp bin/config-dist.yaml .debpkg/etc/meshtasticd/config.yaml + cp bin/config.d/* .debpkg/etc/meshtasticd/available.d/ chmod +x .debpkg/usr/sbin/meshtasticd cp bin/meshtasticd.service .debpkg/usr/lib/systemd/system/meshtasticd.service echo "/etc/meshtasticd/config.yaml" > .debpkg/DEBIAN/conffiles diff --git a/.github/workflows/stale_bot.yml b/.github/workflows/stale_bot.yml new file mode 100644 index 000000000..0ce0579de --- /dev/null +++ b/.github/workflows/stale_bot.yml @@ -0,0 +1,22 @@ +name: process stale Issues and PR's +on: + schedule: + - cron: 0 6 * * * + workflow_dispatch: {} + +permissions: + issues: write + pull-requests: write + actions: write + +jobs: + stale_issues: + name: Close Stale Issues + runs-on: ubuntu-latest + + steps: + - name: Stale PR+Issues + uses: actions/stale@v9.0.0 + with: + exempt-issue-labels: pinned,3.0 + exempt-pr-labels: pinned,3.0 diff --git a/.github/workflows/trunk_format_pr.yml b/.github/workflows/trunk_format_pr.yml new file mode 100644 index 000000000..c5c20b465 --- /dev/null +++ b/.github/workflows/trunk_format_pr.yml @@ -0,0 +1,43 @@ +name: Run Trunk Fmt on PR Comment + +on: + issue_comment: + types: [created] + +jobs: + trunk-fmt: + if: github.event.issue.pull_request != null && contains(github.event.comment.body, 'trunk fmt') + runs-on: ubuntu-latest + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + with: + ref: ${{github.event.pull_request.head.ref}} + repository: ${{github.event.pull_request.head.repo.full_name}} + + - name: Install trunk + run: curl https://get.trunk.io -fsSL | bash + + - name: Run Trunk Fmt + run: trunk fmt + + - name: Commit and push changes + run: | + git config --global user.name "github-actions[bot]" + git config --global user.email "github-actions[bot]@users.noreply.github.com" + git add . + git commit -m "Add firmware version ${{ steps.version.outputs.version }}" + git push + + - name: Comment on PR + uses: actions/github-script@v7 + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + script: | + github.issues.createComment({ + issue_number: context.issue.number, + owner: context.repo.owner, + repo: context.repo.repo, + body: '`trunk fmt` has been run on this PR.' + }) diff --git a/.trunk/trunk.yaml b/.trunk/trunk.yaml index ea4045a16..040712e1d 100644 --- a/.trunk/trunk.yaml +++ b/.trunk/trunk.yaml @@ -1,34 +1,34 @@ version: 0.1 cli: - version: 1.22.6 + version: 1.22.8 plugins: sources: - id: trunk - ref: v1.6.3 + ref: v1.6.4 uri: https://github.com/trunk-io/plugins lint: enabled: - - trufflehog@3.82.6 + - trufflehog@3.83.6 - yamllint@1.35.1 - bandit@1.7.10 - - checkov@3.2.256 - - terrascan@1.19.1 - - trivy@0.55.2 + - checkov@3.2.287 + - terrascan@1.19.9 + - trivy@0.56.2 #- trufflehog@3.63.2-rc0 - taplo@0.9.3 - - ruff@0.6.8 + - ruff@0.7.3 - isort@5.13.2 - markdownlint@0.42.0 - oxipng@9.1.2 - svgo@3.3.2 - - actionlint@1.7.3 + - actionlint@1.7.4 - flake8@7.1.1 - hadolint@2.12.0 - shfmt@3.6.0 - shellcheck@0.10.0 - - black@24.8.0 + - black@24.10.0 - git-diff-check - - gitleaks@8.20.0 + - gitleaks@8.21.1 - clang-format@16.0.3 - prettier@3.3.3 ignore: diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 000000000..6843fc85d --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1,4 @@ +# Contributor Covenant Code of Conduct + +The Meshtastic Firmware project is subject to the code of conduct for the parent project, which can be found here: +https://meshtastic.org/docs/legal/conduct/ diff --git a/arch/esp32/esp32.ini b/arch/esp32/esp32.ini index 36d8b9542..d6a756bec 100644 --- a/arch/esp32/esp32.ini +++ b/arch/esp32/esp32.ini @@ -31,7 +31,7 @@ build_flags = -DCONFIG_BT_NIMBLE_ENABLED -DCONFIG_NIMBLE_CPP_LOG_LEVEL=2 -DCONFIG_BT_NIMBLE_MAX_CCCDS=20 - -DCONFIG_BT_NIMBLE_HOST_TASK_STACK_SIZE=5120 + -DCONFIG_BT_NIMBLE_HOST_TASK_STACK_SIZE=8192 -DESP_OPENSSL_SUPPRESS_LEGACY_WARNING -DSERIAL_BUFFER_SIZE=4096 -DLIBPAX_ARDUINO @@ -43,9 +43,10 @@ lib_deps = ${arduino_base.lib_deps} ${networking_base.lib_deps} ${environmental_base.lib_deps} + ${radiolib_base.lib_deps} https://github.com/meshtastic/esp32_https_server.git#23665b3adc080a311dcbb586ed5941b5f94d6ea2 h2zero/NimBLE-Arduino@^1.4.2 - https://github.com/dbSuS/libpax.git#7bcd3fcab75037505be9b122ab2b24cc5176b587 + https://github.com/dbinfrago/libpax.git#3cdc0371c375676a97967547f4065607d4c53fd1 lewisxhe/XPowersLib@^0.2.6 https://github.com/meshtastic/ESP32_Codec2.git#633326c78ac251c059ab3a8c430fcdf25b41672f rweather/Crypto@^0.4.0 diff --git a/arch/esp32/esp32c6.ini b/arch/esp32/esp32c6.ini index 53d7f92ec..3f8b1bdbe 100644 --- a/arch/esp32/esp32c6.ini +++ b/arch/esp32/esp32c6.ini @@ -23,6 +23,7 @@ lib_deps = ${arduino_base.lib_deps} ${networking_base.lib_deps} ${environmental_base.lib_deps} + ${radiolib_base.lib_deps} lewisxhe/XPowersLib@^0.2.6 https://github.com/meshtastic/ESP32_Codec2.git#633326c78ac251c059ab3a8c430fcdf25b41672f rweather/Crypto@^0.4.0 diff --git a/arch/nrf52/nrf52.ini b/arch/nrf52/nrf52.ini index 04880d540..d75f86306 100644 --- a/arch/nrf52/nrf52.ini +++ b/arch/nrf52/nrf52.ini @@ -14,13 +14,18 @@ build_flags = -Wno-unused-variable -Isrc/platform/nrf52 -DLFS_NO_ASSERT ; Disable LFS assertions , see https://github.com/meshtastic/firmware/pull/3818 + -DMESHTASTIC_EXCLUDE_AUDIO=1 + -DMESHTASTIC_EXCLUDE_PAXCOUNTER=1 + -DMAX_NUM_NODES=80 build_src_filter = ${arduino_base.build_src_filter} - - - - - - - - - - lib_deps= ${arduino_base.lib_deps} + ${radiolib_base.lib_deps} rweather/Crypto@^0.4.0 lib_ignore = - BluetoothOTA \ No newline at end of file + BluetoothOTA + lvgl diff --git a/arch/portduino/portduino.ini b/arch/portduino/portduino.ini index 8778c32a0..04fd6db09 100644 --- a/arch/portduino/portduino.ini +++ b/arch/portduino/portduino.ini @@ -1,6 +1,6 @@ ; The Portduino based sim environment on top of any host OS, all hardware will be simulated [portduino_base] -platform = https://github.com/meshtastic/platform-native.git#6b3796d697481c8f6e3f4aa5c111bd9979f29e64 +platform = https://github.com/meshtastic/platform-native.git#bcd02436cfca91f7d28ad0f7dab977c6aaa781af framework = arduino build_src_filter = @@ -23,8 +23,9 @@ build_src_filter = lib_deps = ${env.lib_deps} ${networking_base.lib_deps} + ${radiolib_base.lib_deps} rweather/Crypto@^0.4.0 - lovyan03/LovyanGFX@^1.1.16 + https://github.com/lovyan03/LovyanGFX.git#1401c28a47646fe00538d487adcb2eb3c72de805 build_flags = ${arduino_base.build_flags} @@ -32,6 +33,7 @@ build_flags = -Isrc/platform/portduino -DRADIOLIB_EEPROM_UNSUPPORTED -DPORTDUINO_LINUX_HARDWARE + -lstdc++fs -lbluetooth -lgpiod -lyaml-cpp diff --git a/arch/rp2xx0/rp2040.ini b/arch/rp2xx0/rp2040.ini index c004a3bec..85efa583c 100644 --- a/arch/rp2xx0/rp2040.ini +++ b/arch/rp2xx0/rp2040.ini @@ -1,8 +1,8 @@ ; Common settings for rp2040 Processor based targets [rp2040_base] -platform = https://github.com/maxgerhardt/platform-raspberrypi.git#v1.2.0-gcc12 +platform = https://github.com/maxgerhardt/platform-raspberrypi.git#19e30129fb1428b823be585c787dcb4ac0d9014c ; For arduino-pico 4.2.1 extends = arduino_base -platform_packages = framework-arduinopico@https://github.com/earlephilhower/arduino-pico.git#4.0.3 +platform_packages = framework-arduinopico@https://github.com/earlephilhower/arduino-pico.git#996c3bfab9758f12c07aa20cc6d352e630c16987 ; 4.2.1 with fix for sporadic hangs board_build.core = earlephilhower board_build.filesystem_size = 0.5m @@ -22,4 +22,5 @@ lib_ignore = lib_deps = ${arduino_base.lib_deps} ${environmental_base.lib_deps} + ${radiolib_base.lib_deps} rweather/Crypto \ No newline at end of file diff --git a/arch/rp2xx0/rp2350.ini b/arch/rp2xx0/rp2350.ini index 96ed0cb21..6daf59bdf 100644 --- a/arch/rp2xx0/rp2350.ini +++ b/arch/rp2xx0/rp2350.ini @@ -1,8 +1,8 @@ ; Common settings for rp2040 Processor based targets [rp2350_base] -platform = https://github.com/maxgerhardt/platform-raspberrypi.git#9e55f6db5c56b9867c69fe473f388beea4546672 +platform = https://github.com/maxgerhardt/platform-raspberrypi.git#19e30129fb1428b823be585c787dcb4ac0d9014c ; For arduino-pico 4.2.1 extends = arduino_base -platform_packages = framework-arduinopico@https://github.com/earlephilhower/arduino-pico.git#a6ab6e1f95bc1428d667d55ea7173c0744acc03c ; 4.0.2+ +platform_packages = framework-arduinopico@https://github.com/earlephilhower/arduino-pico.git#96c3bfab9758f12c07aa20cc6d352e630c16987 ; 4.2.1 with fix for sporadic hangs board_build.core = earlephilhower board_build.filesystem_size = 0.5m @@ -16,8 +16,10 @@ build_src_filter = lib_ignore = BluetoothOTA + lvgl lib_deps = ${arduino_base.lib_deps} ${environmental_base.lib_deps} + ${radiolib_base.lib_deps} rweather/Crypto \ No newline at end of file diff --git a/arch/stm32/stm32.ini b/arch/stm32/stm32.ini index 715e8aa73..7e211496d 100644 --- a/arch/stm32/stm32.ini +++ b/arch/stm32/stm32.ini @@ -30,8 +30,9 @@ upload_protocol = stlink lib_deps = ${env.lib_deps} charlesbaynham/OSFS@^1.2.3 + jgromes/RadioLib@7.0.2 https://github.com/caveman99/Crypto.git#f61ae26a53f7a2d0ba5511625b8bf8eff3a35d5e lib_ignore = - mathertel/OneButton@~2.6.1 + mathertel/OneButton@2.6.1 Wire \ No newline at end of file diff --git a/bin/base64_to_hex.py b/bin/base64_to_hex.py new file mode 100644 index 000000000..07c559b9e --- /dev/null +++ b/bin/base64_to_hex.py @@ -0,0 +1,33 @@ +import sys +import base64 + +def base64_to_hex_string(b64_string): + try: + # Decode the Base64 string to raw bytes + decoded_bytes = base64.b64decode(b64_string) + except Exception as e: + raise ValueError(f"Invalid Base64 input: {e}") + + # Check if the decoded result is exactly 32 bytes + if len(decoded_bytes) != 32: + raise ValueError("Decoded Base64 input must be exactly 32 bytes.") + + # Convert each byte to its hex representation + hex_values = [f"0x{byte:02x}" for byte in decoded_bytes] + + # Join the formatted hex values with commas + formatted_output = "{ " + ", ".join(hex_values) + " };" + return formatted_output + +if __name__ == "__main__": + # Check if a Base64 string was provided in command line arguments + if len(sys.argv) != 2: + print("Usage: python script.py ") + sys.exit(1) + + b64_string = sys.argv[1] + try: + formatted_hex = base64_to_hex_string(b64_string) + print(formatted_hex) + except ValueError as e: + print(e) diff --git a/bin/build-esp32.sh b/bin/build-esp32.sh index adb6ab12a..f8d808ced 100755 --- a/bin/build-esp32.sh +++ b/bin/build-esp32.sh @@ -35,6 +35,11 @@ cp $SRCBIN $OUTDIR/$basename-update.bin echo "Building Filesystem for ESP32 targets" pio run --environment $1 -t buildfs +cp .pio/build/$1/littlefs.bin $OUTDIR/littlefswebui-$VERSION.bin +# Remove webserver files from the filesystem and rebuild +ls -l data/static # Diagnostic list of files +rm -rf data/static +pio run --environment $1 -t buildfs cp .pio/build/$1/littlefs.bin $OUTDIR/littlefs-$VERSION.bin cp bin/device-install.* $OUTDIR cp bin/device-update.* $OUTDIR diff --git a/bin/build-native.sh b/bin/build-native.sh index e8ed61bcf..cda77b064 100755 --- a/bin/build-native.sh +++ b/bin/build-native.sh @@ -27,5 +27,4 @@ rm -r $OUTDIR/* || true platformio pkg update --environment native || platformioFailed pio run --environment native || platformioFailed cp .pio/build/native/program "$OUTDIR/meshtasticd_linux_$(uname -m)" -cp bin/device-install.* $OUTDIR -cp bin/device-update.* $OUTDIR +cp bin/native-install.* $OUTDIR diff --git a/bin/config-dist.yaml b/bin/config-dist.yaml index 3a470b7bb..77680cc63 100644 --- a/bin/config-dist.yaml +++ b/bin/config-dist.yaml @@ -1,15 +1,11 @@ +### Many device configs have been moved to /etc/meshtasticd/available.d +### To activate, simply copy or link the appropriate file into /etc/meshtasticd/config.d + ### Define your devices here using Broadcom pin numbering ### Uncomment the block that corresponds to your hardware ### Including the "Module:" line! --- Lora: -# Module: sx1262 # Waveshare SX126X XXXM -# DIO2_AS_RF_SWITCH: true -# CS: 21 -# IRQ: 16 -# Busy: 20 -# Reset: 18 -# SX126X_ANT_SW: 6 # Module: sx1262 # Waveshare SX1302 LISTEN ONLY AT THIS TIME! # CS: 7 @@ -20,6 +16,7 @@ Lora: # CS: 0 # IRQ: 10 # Busy: 11 +# DIO2_AS_RF_SWITCH: true # spidev: spidev0.1 # Module: RF95 # Adafruit RFM9x @@ -84,17 +81,6 @@ I2C: Display: -### Waveshare 2.8inch RPi LCD -# Panel: ST7789 -# CS: 8 -# DC: 22 # Data/Command pin -# Backlight: 18 -# Width: 240 -# Height: 320 -# Reset: 27 -# Rotate: true -# Invert: true - ### Waveshare 1.44inch LCD HAT # Panel: ST7735S # CS: 8 #Chip Select @@ -114,6 +100,29 @@ Display: # Height: 320 # Rotate: true +### SHCHV 3.5 RPi TFT+Touchscreen +# Panel: ILI9486 +# spidev: spidev0.0 +# BusFrequency: 30000000 +# DC: 24 +# Reset: 25 +# Width: 320 +# Height: 480 +# OffsetRotate: 2 + +### TZT 2.0 Inch TFT Display ST7789V 240RGBx320 +# Panel: ST7789 +# spidev: spidev0.0 +# # CS: 8 # can be freely chosen +# BusFrequency: 80000000 +# DC: 24 # can be freely chosen +# Width: 320 +# Height: 240 +# Reset: 25 # can be freely chosen +# Rotate: true +# OffsetRotate: 1 +# Invert: true + ### You can also specify the spi device for the display to use # spidev: spidev0.0 @@ -128,10 +137,6 @@ Touchscreen: # IRQ: 24 # I2CAddr: 0x38 -# Module: XPT2046 # Waveshare 2.8inch -# CS: 7 -# IRQ: 17 - ### You can also specify the spi device for the touchscreen to use # spidev: spidev0.0 @@ -154,4 +159,5 @@ Webserver: General: MaxNodes: 200 - MaxMessageQueue: 100 \ No newline at end of file + MaxMessageQueue: 100 + ConfigDirectory: /etc/meshtasticd/config.d/ \ No newline at end of file diff --git a/bin/config.d/display-waveshare-2.8.yaml b/bin/config.d/display-waveshare-2.8.yaml new file mode 100644 index 000000000..2e28276d8 --- /dev/null +++ b/bin/config.d/display-waveshare-2.8.yaml @@ -0,0 +1,18 @@ +Display: + +### Waveshare 2.8inch RPi LCD + Panel: ST7789 + CS: 8 + DC: 22 # Data/Command pin + Backlight: 18 + Width: 240 + Height: 320 + Reset: 27 + Rotate: true + Invert: true + +Touchscreen: +### Note, at least for now, the touchscreen must have a CS pin defined, even if you let Linux manage the CS switching. + Module: XPT2046 # Waveshare 2.8inch + CS: 7 + IRQ: 17 \ No newline at end of file diff --git a/bin/config.d/lora-MeshAdv-900M30S.yaml b/bin/config.d/lora-MeshAdv-900M30S.yaml new file mode 100644 index 000000000..07dada620 --- /dev/null +++ b/bin/config.d/lora-MeshAdv-900M30S.yaml @@ -0,0 +1,9 @@ +Lora: + Module: sx1262 + CS: 21 + IRQ: 16 + Busy: 20 + Reset: 18 + TXen: 13 + RXen: 12 + DIO3_TCXO_VOLTAGE: true diff --git a/bin/config.d/lora-waveshare-sxxx.yaml b/bin/config.d/lora-waveshare-sxxx.yaml new file mode 100644 index 000000000..a9ff13653 --- /dev/null +++ b/bin/config.d/lora-waveshare-sxxx.yaml @@ -0,0 +1,8 @@ +Lora: + Module: sx1262 # Waveshare SX126X XXXM + DIO2_AS_RF_SWITCH: true + CS: 21 + IRQ: 16 + Busy: 20 + Reset: 18 + SX126X_ANT_SW: 6 diff --git a/bin/device-install.bat b/bin/device-install.bat index 6c880185e..c18be89a8 100755 --- a/bin/device-install.bat +++ b/bin/device-install.bat @@ -1,16 +1,26 @@ @ECHO OFF set PYTHON=python +set WEB_APP=0 + +:: Determine the correct esptool command to use +where esptool >nul 2>&1 +if %ERRORLEVEL% EQU 0 ( + set "ESPTOOL_CMD=esptool" +) else ( + set "ESPTOOL_CMD=%PYTHON% -m esptool" +) goto GETOPTS :HELP -echo Usage: %~nx0 [-h] [-p ESPTOOL_PORT] [-P PYTHON] [-f FILENAME^|FILENAME] +echo Usage: %~nx0 [-h] [-p ESPTOOL_PORT] [-P PYTHON] [-f FILENAME^|FILENAME] [--web] echo Flash image file to device, but first erasing and writing system information echo. echo -h Display this help and exit echo -p ESPTOOL_PORT Set the environment variable for ESPTOOL_PORT. If not set, ESPTOOL iterates all ports (Dangerrous). echo -P PYTHON Specify alternate python interpreter to use to invoke esptool. (Default: %PYTHON%) echo -f FILENAME The .bin file to flash. Custom to your device type and region. +echo --web Flash WEB APP. goto EOF :GETOPTS @@ -19,37 +29,44 @@ if /I "%1"=="--help" goto HELP if /I "%1"=="-F" set "FILENAME=%2" & SHIFT if /I "%1"=="-p" set ESPTOOL_PORT=%2 & SHIFT if /I "%1"=="-P" set PYTHON=%2 & SHIFT +if /I "%1"=="--web" set WEB_APP=1 & SHIFT SHIFT IF NOT "__%1__"=="____" goto GETOPTS IF "__%FILENAME%__" == "____" ( echo "Missing FILENAME" - goto HELP + goto HELP ) IF EXIST %FILENAME% IF x%FILENAME:update=%==x%FILENAME% ( echo Trying to flash update %FILENAME%, but first erasing and writing system information" - %PYTHON% -m esptool --baud 115200 erase_flash - %PYTHON% -m esptool --baud 115200 write_flash 0x00 %FILENAME% + %ESPTOOL_CMD% --baud 115200 erase_flash + %ESPTOOL_CMD% --baud 115200 write_flash 0x00 %FILENAME% @REM Account for S3 and C3 board's different OTA partition IF x%FILENAME:s3=%==x%FILENAME% IF x%FILENAME:v3=%==x%FILENAME% IF x%FILENAME:t-deck=%==x%FILENAME% IF x%FILENAME:wireless-paper=%==x%FILENAME% IF x%FILENAME:wireless-tracker=%==x%FILENAME% IF x%FILENAME:station-g2=%==x%FILENAME% IF x%FILENAME:unphone=%==x%FILENAME% ( IF x%FILENAME:esp32c3=%==x%FILENAME% ( - %PYTHON% -m esptool --baud 115200 write_flash 0x260000 bleota.bin + %ESPTOOL_CMD% --baud 115200 write_flash 0x260000 bleota.bin ) else ( - %PYTHON% -m esptool --baud 115200 write_flash 0x260000 bleota-c3.bin + %ESPTOOL_CMD% --baud 115200 write_flash 0x260000 bleota-c3.bin ) - ) else ( - %PYTHON% -m esptool --baud 115200 write_flash 0x260000 bleota-s3.bin + ) else ( + %ESPTOOL_CMD% --baud 115200 write_flash 0x260000 bleota-s3.bin ) - for %%f in (littlefs-*.bin) do ( - %PYTHON% -m esptool --baud 115200 write_flash 0x300000 %%f + IF %WEB_APP%==1 ( + for %%f in (littlefswebui-*.bin) do ( + %ESPTOOL_CMD% --baud 115200 write_flash 0x300000 %%f + ) + ) else ( + for %%f in (littlefs-*.bin) do ( + %ESPTOOL_CMD% --baud 115200 write_flash 0x300000 %%f + ) ) ) else ( echo "Invalid file: %FILENAME%" - goto HELP + goto HELP ) else ( echo "Invalid file: %FILENAME%" - goto HELP + goto HELP ) :EOF diff --git a/bin/device-install.sh b/bin/device-install.sh index 563a87af4..e09c61ba6 100755 --- a/bin/device-install.sh +++ b/bin/device-install.sh @@ -1,22 +1,45 @@ #!/bin/sh PYTHON=${PYTHON:-$(which python3 python | head -n 1)} +WEB_APP=false + +# Determine the correct esptool command to use +if "$PYTHON" -m esptool version >/dev/null 2>&1; then + ESPTOOL_CMD="$PYTHON -m esptool" +elif command -v esptool >/dev/null 2>&1; then + ESPTOOL_CMD="esptool" +elif command -v esptool.py >/dev/null 2>&1; then + ESPTOOL_CMD="esptool.py" +else + echo "Error: esptool not found" + exit 1 +fi set -e # Usage info show_help() { cat <nul 2>&1 +if %ERRORLEVEL% EQU 0 ( + set "ESPTOOL_CMD=esptool" +) else ( + set "ESPTOOL_CMD=%PYTHON% -m esptool" +) + goto GETOPTS :HELP echo Usage: %~nx0 [-h] [-p ESPTOOL_PORT] [-P PYTHON] [-f FILENAME^|FILENAME] @@ -24,17 +32,17 @@ IF NOT "__%1__"=="____" goto GETOPTS IF "__%FILENAME%__" == "____" ( echo "Missing FILENAME" - goto HELP + goto HELP ) IF EXIST %FILENAME% IF NOT x%FILENAME:update=%==x%FILENAME% ( echo Trying to flash update %FILENAME% - %PYTHON% -m esptool --baud 115200 write_flash 0x10000 %FILENAME% + %ESPTOOL_CMD% --baud 115200 write_flash 0x10000 %FILENAME% ) else ( echo "Invalid file: %FILENAME%" - goto HELP + goto HELP ) else ( echo "Invalid file: %FILENAME%" - goto HELP + goto HELP ) :EOF diff --git a/bin/device-update.sh b/bin/device-update.sh index 7233f61f6..67281dc4f 100755 --- a/bin/device-update.sh +++ b/bin/device-update.sh @@ -2,6 +2,18 @@ PYTHON=${PYTHON:-$(which python3 python|head -n 1)} +# Determine the correct esptool command to use +if "$PYTHON" -m esptool version >/dev/null 2>&1; then + ESPTOOL_CMD="$PYTHON -m esptool" +elif command -v esptool >/dev/null 2>&1; then + ESPTOOL_CMD="esptool" +elif command -v esptool.py >/dev/null 2>&1; then + ESPTOOL_CMD="esptool.py" +else + echo "Error: esptool not found" + exit 1 +fi + # Usage info show_help() { cat << EOF @@ -9,7 +21,7 @@ Usage: $(basename $0) [-h] [-p ESPTOOL_PORT] [-P PYTHON] [-f FILENAME|FILENAME] Flash image file to device, leave existing system intact." -h Display this help and exit - -p ESPTOOL_PORT Set the environment variable for ESPTOOL_PORT. If not set, ESPTOOL iterates all ports (Dangerrous). + -p ESPTOOL_PORT Set the environment variable for ESPTOOL_PORT. If not set, ESPTOOL iterates all ports (Dangerous). -P PYTHON Specify alternate python interpreter to use to invoke esptool. (Default: "$PYTHON") -f FILENAME The *update.bin file to flash. Custom to your device type. @@ -30,7 +42,7 @@ while getopts ":hp:P:f:" opt; do f) FILENAME=${OPTARG} ;; *) - echo "Invalid flag." + echo "Invalid flag." show_help >&2 exit 1 ;; @@ -45,7 +57,7 @@ shift "$((OPTIND-1))" if [ -f "${FILENAME}" ] && [ -z "${FILENAME##*"update"*}" ]; then printf "Trying to flash update ${FILENAME}" - $PYTHON -m esptool --baud 115200 write_flash 0x10000 ${FILENAME} + $ESPTOOL_CMD --baud 115200 write_flash 0x10000 ${FILENAME} else show_help echo "Invalid file: ${FILENAME}" diff --git a/bin/meshtasticd.service b/bin/meshtasticd.service index f15fdc871..1e8ee98b8 100644 --- a/bin/meshtasticd.service +++ b/bin/meshtasticd.service @@ -1,12 +1,16 @@ [Unit] Description=Meshtastic Native Daemon After=network-online.target +StartLimitInterval=200 +StartLimitBurst=5 [Service] User=root Group=root Type=simple ExecStart=/usr/sbin/meshtasticd +Restart=always +RestartSec=3 [Install] -WantedBy=multi-user.target \ No newline at end of file +WantedBy=multi-user.target diff --git a/bin/native-install.sh b/bin/native-install.sh index ba71c4f46..a8fcc29a6 100755 --- a/bin/native-install.sh +++ b/bin/native-install.sh @@ -1,7 +1,7 @@ #!/usr/bin/env bash cp "release/meshtasticd_linux_$(uname -m)" /usr/sbin/meshtasticd -mkdir /etc/meshtasticd +mkdir -p /etc/meshtasticd if [[ -f "/etc/meshtasticd/config.yaml" ]]; then cp bin/config-dist.yaml /etc/meshtasticd/config-upgrade.yaml else diff --git a/bin/platformio-custom.py b/bin/platformio-custom.py index 0f099c09a..701f6b5d8 100644 --- a/bin/platformio-custom.py +++ b/bin/platformio-custom.py @@ -88,12 +88,13 @@ Import("projenv") prefsLoc = projenv["PROJECT_DIR"] + "/version.properties" verObj = readProps(prefsLoc) -print("Using meshtastic platformio-custom.py, firmware version " + verObj["long"]) +print("Using meshtastic platformio-custom.py, firmware version " + verObj["long"] + " on " + env.get("PIOENV")) # General options that are passed to the C and C++ compilers projenv.Append( CCFLAGS=[ "-DAPP_VERSION=" + verObj["long"], "-DAPP_VERSION_SHORT=" + verObj["short"], + "-DAPP_ENV=" + env.get("PIOENV"), ] ) diff --git a/boards/bpi_picow_esp32_s3.json b/boards/bpi_picow_esp32_s3.json index 9a20dd57f..75983d845 100644 --- a/boards/bpi_picow_esp32_s3.json +++ b/boards/bpi_picow_esp32_s3.json @@ -28,6 +28,8 @@ "flash_size": "8MB", "maximum_ram_size": 327680, "maximum_size": 8388608, + "use_1200bps_touch": true, + "wait_for_upload_port": true, "require_upload_port": true, "speed": 921600 }, diff --git a/boards/icarus.json b/boards/icarus.json new file mode 100644 index 000000000..03da4682f --- /dev/null +++ b/boards/icarus.json @@ -0,0 +1,41 @@ +{ + "build": { + "arduino": { + "ldscript": "esp32s3_out.ld", + "memory_type": "qio_opi" + }, + "core": "esp32", + "extra_flags": [ + "-DBOARD_HAS_PSRAM", + "-DARDUINO_USB_CDC_ON_BOOT=1", + "-DARDUINO_USB_MODE=0", + "-DARDUINO_RUNNING_CORE=1", + "-DARDUINO_EVENT_RUNNING_CORE=0" + ], + "f_cpu": "240000000L", + "f_flash": "80000000L", + "flash_mode": "qio", + "hwids": [["0x2886", "0x0059"]], + "mcu": "esp32s3", + "variant": "icarus" + }, + "connectivity": ["wifi", "bluetooth", "lora"], + "debug": { + "default_tool": "esp-builtin", + "onboard_tools": ["esp-builtin"], + "openocd_target": "esp32s3.cfg" + }, + "frameworks": ["arduino", "espidf"], + "name": "icarus", + "upload": { + "flash_size": "8MB", + "maximum_ram_size": 8388608, + "maximum_size": 8388608, + "use_1200bps_touch": true, + "wait_for_upload_port": true, + "require_upload_port": true, + "speed": 921600 + }, + "url": "https://icarus.azlan.works", + "vendor": "Muhammad Shah" +} diff --git a/boards/unphone.json b/boards/unphone.json new file mode 100644 index 000000000..bf711993c --- /dev/null +++ b/boards/unphone.json @@ -0,0 +1,46 @@ +{ + "build": { + "arduino": { + "ldscript": "esp32s3_out.ld", + "memory_type": "qio_opi", + "partitions": "default_8MB.csv" + }, + "core": "esp32", + "extra_flags": [ + "-DBOARD_HAS_PSRAM", + "-DUNPHONE_SPIN=9", + "-DARDUINO_USB_CDC_ON_BOOT=1", + "-DARDUINO_USB_MODE=0", + "-DARDUINO_RUNNING_CORE=1", + "-DARDUINO_EVENT_RUNNING_CORE=1" + ], + "f_cpu": "240000000L", + "f_flash": "80000000L", + "flash_mode": "qio", + "hwids": [ + ["0x16D0", "0x1178"], + ["0x303a", "0x1001"] + ], + "mcu": "esp32s3", + "variant": "unphone" + }, + "connectivity": ["wifi", "bluetooth", "lora"], + "debug": { + "default_tool": "esp-builtin", + "onboard_tools": ["esp-builtin"], + "openocd_target": "esp32s3.cfg" + }, + "frameworks": ["arduino", "espidf"], + "name": "unPhone", + "upload": { + "flash_size": "8MB", + "maximum_ram_size": 327680, + "maximum_size": 8323072, + "use_1200bps_touch": true, + "wait_for_upload_port": true, + "require_upload_port": true, + "speed": 921600 + }, + "url": "https://unphone.net/", + "vendor": "University of Sheffield" +} diff --git a/platformio.ini b/platformio.ini index e437b572e..cc08b33a0 100644 --- a/platformio.ini +++ b/platformio.ini @@ -29,138 +29,134 @@ default_envs = tbeam ;default_envs = rak4631 ;default_envs = rak4631_eth_gw ;default_envs = rak2560 -;default_envs = rak10701 +;default_envs = rak_wismeshtap ;default_envs = wio-e5 ;default_envs = radiomaster_900_bandit_nano ;default_envs = radiomaster_900_bandit_micro ;default_envs = radiomaster_900_bandit -;default_envs = heltec_capsule_sensor_v3 ;default_envs = heltec_vision_master_t190 ;default_envs = heltec_vision_master_e213 ;default_envs = heltec_vision_master_e290 ;default_envs = heltec_mesh_node_t114 - extra_configs = - arch/*/*.ini - variants/*/platformio.ini + arch/*/*.ini + variants/*/platformio.ini +description = Meshtastic [env] test_build_src = true extra_scripts = bin/platformio-custom.py - ; note: we add src to our include search path so that lmic_project_config can override ; note: TINYGPS_OPTION_NO_CUSTOM_FIELDS is VERY important. We don't use custom fields and somewhere in that pile ; of code is a heap corruption bug! ; FIXME: fix lib/BluetoothOTA dependency back on src/ so we can remove -Isrc ; The Radiolib stuff will speed up building considerably. Exclud all the stuff we dont need. build_flags = -Wno-missing-field-initializers - -Wno-format - -Isrc -Isrc/mesh -Isrc/mesh/generated -Isrc/gps -Isrc/buzz -Wl,-Map,.pio/build/output.map - -DUSE_THREAD_NAMES - -DTINYGPS_OPTION_NO_CUSTOM_FIELDS - -DPB_ENABLE_MALLOC=1 - -DRADIOLIB_EXCLUDE_CC1101=1 - -DRADIOLIB_EXCLUDE_NRF24=1 - -DRADIOLIB_EXCLUDE_RF69=1 - -DRADIOLIB_EXCLUDE_SX1231=1 - -DRADIOLIB_EXCLUDE_SX1233=1 - -DRADIOLIB_EXCLUDE_SI443X=1 - -DRADIOLIB_EXCLUDE_RFM2X=1 - -DRADIOLIB_EXCLUDE_AFSK=1 - -DRADIOLIB_EXCLUDE_BELL=1 - -DRADIOLIB_EXCLUDE_HELLSCHREIBER=1 - -DRADIOLIB_EXCLUDE_MORSE=1 - -DRADIOLIB_EXCLUDE_RTTY=1 - -DRADIOLIB_EXCLUDE_SSTV=1 - -DRADIOLIB_EXCLUDE_AX25=1 - -DRADIOLIB_EXCLUDE_DIRECT_RECEIVE=1 - -DRADIOLIB_EXCLUDE_BELL=1 - -DRADIOLIB_EXCLUDE_PAGER=1 - -DRADIOLIB_EXCLUDE_FSK4=1 - -DRADIOLIB_EXCLUDE_APRS=1 - -DRADIOLIB_EXCLUDE_LORAWAN=1 - -DMESHTASTIC_EXCLUDE_DROPZONE=1 - -DMESHTASTIC_EXCLUDE_REMOTEHARDWARE=1 - #-DBUILD_EPOCH=$UNIX_TIME - ;-D OLED_PL + + -Wno-format + -Isrc -Isrc/mesh -Isrc/mesh/generated -Isrc/gps -Isrc/buzz -Wl,-Map,.pio/build/output.map + -DUSE_THREAD_NAMES + -DTINYGPS_OPTION_NO_CUSTOM_FIELDS + -DPB_ENABLE_MALLOC=1 + -DRADIOLIB_EXCLUDE_CC1101=1 + -DRADIOLIB_EXCLUDE_NRF24=1 + -DRADIOLIB_EXCLUDE_RF69=1 + -DRADIOLIB_EXCLUDE_SX1231=1 + -DRADIOLIB_EXCLUDE_SX1233=1 + -DRADIOLIB_EXCLUDE_SI443X=1 + -DRADIOLIB_EXCLUDE_RFM2X=1 + -DRADIOLIB_EXCLUDE_AFSK=1 + -DRADIOLIB_EXCLUDE_BELL=1 + -DRADIOLIB_EXCLUDE_HELLSCHREIBER=1 + -DRADIOLIB_EXCLUDE_MORSE=1 + -DRADIOLIB_EXCLUDE_RTTY=1 + -DRADIOLIB_EXCLUDE_SSTV=1 + -DRADIOLIB_EXCLUDE_AX25=1 + -DRADIOLIB_EXCLUDE_DIRECT_RECEIVE=1 + -DRADIOLIB_EXCLUDE_BELL=1 + -DRADIOLIB_EXCLUDE_PAGER=1 + -DRADIOLIB_EXCLUDE_FSK4=1 + -DRADIOLIB_EXCLUDE_APRS=1 + -DRADIOLIB_EXCLUDE_LORAWAN=1 + -DMESHTASTIC_EXCLUDE_DROPZONE=1 + -DMESHTASTIC_EXCLUDE_REMOTEHARDWARE=1 + -DMESHTASTIC_EXCLUDE_POWERSTRESS=1 ; exclude power stress test module from main firmware + #-DBUILD_EPOCH=$UNIX_TIME + ;-D OLED_PL monitor_speed = 115200 monitor_filters = direct - lib_deps = - jgromes/RadioLib@~7.0.2 - https://github.com/meshtastic/esp8266-oled-ssd1306.git#e16cee124fe26490cb14880c679321ad8ac89c95 ; ESP8266_SSD1306 - mathertel/OneButton@~2.6.1 ; OneButton library for non-blocking button debounce - https://github.com/meshtastic/arduino-fsm.git#7db3702bf0cfe97b783d6c72595e3f38e0b19159 - https://github.com/meshtastic/TinyGPSPlus.git#71a82db35f3b973440044c476d4bcdc673b104f4 - https://github.com/meshtastic/ArduinoThread.git#1ae8778c85d0a2a729f989e0b1e7d7c4dc84eef0 - nanopb/Nanopb@^0.4.9 - erriez/ErriezCRC32@^1.0.1 + https://github.com/meshtastic/esp8266-oled-ssd1306.git#e16cee124fe26490cb14880c679321ad8ac89c95 + mathertel/OneButton@2.6.1 + https://github.com/meshtastic/arduino-fsm.git#7db3702bf0cfe97b783d6c72595e3f38e0b19159 + https://github.com/meshtastic/TinyGPSPlus.git#71a82db35f3b973440044c476d4bcdc673b104f4 + https://github.com/meshtastic/ArduinoThread.git#1ae8778c85d0a2a729f989e0b1e7d7c4dc84eef0 + nanopb/Nanopb@0.4.9 + erriez/ErriezCRC32@1.0.1 ; Used for the code analysis in PIO Home / Inspect check_tool = cppcheck check_skip_packages = yes check_flags = - -DAPP_VERSION=1.0.0 - --suppressions-list=suppressions.txt - --inline-suppr + -DAPP_VERSION=1.0.0 + --suppressions-list=suppressions.txt + --inline-suppr ; Common settings for conventional (non Portduino) Arduino targets [arduino_base] framework = arduino lib_deps = - ${env.lib_deps} - end2endzone/NonBlockingRTTTL@^1.3.0 - https://github.com/meshtastic/SparkFun_ATECCX08a_Arduino_Library.git#5cf62b36c6f30bc72a07bdb2c11fc9a22d1e31da - + ${env.lib_deps} + end2endzone/NonBlockingRTTTL@1.3.0 build_flags = ${env.build_flags} -Os build_src_filter = ${env.build_src_filter} - ; Common libs for communicating over TCP/IP networks such as MQTT [networking_base] lib_deps = - knolleary/PubSubClient@^2.8 - arduino-libraries/NTPClient@^3.1.0 - arcao/Syslog@^2.0.0 + knolleary/PubSubClient@2.8 + arduino-libraries/NTPClient@3.1.0 + arcao/Syslog@2.0.0 + +[radiolib_base] +lib_deps = + jgromes/RadioLib@7.1.0 ; Common libs for environmental measurements in telemetry module ; (not included in native / portduino) [environmental_base] lib_deps = - adafruit/Adafruit BusIO@^1.16.1 - adafruit/Adafruit Unified Sensor@^1.1.11 - adafruit/Adafruit BMP280 Library@^2.6.8 - adafruit/Adafruit BMP085 Library@^1.2.4 - adafruit/Adafruit BME280 Library@^2.2.2 - adafruit/Adafruit BMP3XX Library@^2.1.5 - adafruit/Adafruit MCP9808 Library@^2.0.0 - adafruit/Adafruit INA260 Library@^1.5.0 - adafruit/Adafruit INA219@^1.2.0 - adafruit/Adafruit MAX1704X@^1.0.3 - adafruit/Adafruit SHTC3 Library@^1.0.0 - adafruit/Adafruit LPS2X@^2.0.4 - adafruit/Adafruit SHT31 Library@^2.2.2 - adafruit/Adafruit PM25 AQI Sensor@^1.1.1 - adafruit/Adafruit MPU6050@^2.2.4 - adafruit/Adafruit LIS3DH@^1.3.0 - adafruit/Adafruit AHTX0@^2.0.5 - adafruit/Adafruit LSM6DS@^4.7.2 - adafruit/Adafruit VEML7700 Library@^2.1.6 - adafruit/Adafruit SHT4x Library@^1.0.4 - adafruit/Adafruit TSL2591 Library@^1.4.5 - sparkfun/SparkFun Qwiic Scale NAU7802 Arduino Library@^1.0.5 - sparkfun/SparkFun 9DoF IMU Breakout - ICM 20948 - Arduino Library@^1.2.13 - ClosedCube OPT3001@^1.1.2 - emotibit/EmotiBit MLX90632@^1.0.8 - dfrobot/DFRobot_RTU@^1.0.3 - sparkfun/SparkFun MAX3010x Pulse and Proximity Sensor Library@^1.1.2 - adafruit/Adafruit MLX90614 Library@^2.1.5 - - https://github.com/boschsensortec/Bosch-BSEC2-Library#v1.7.2502 - boschsensortec/BME68x Sensor Library@^1.1.40407 - https://github.com/KodinLanewave/INA3221@^1.0.1 - lewisxhe/SensorLib@0.2.0 - mprograms/QMC5883LCompass@^1.2.0 - - https://github.com/meshtastic/DFRobot_LarkWeatherStation#4de3a9cadef0f6a5220a8a906cf9775b02b0040d - https://github.com/gjelsoe/STK8xxx-Accelerometer.git#v0.1.1 + adafruit/Adafruit BusIO@1.16.2 + adafruit/Adafruit Unified Sensor@1.1.14 + adafruit/Adafruit BMP280 Library@2.6.8 + adafruit/Adafruit BMP085 Library@1.2.4 + adafruit/Adafruit BME280 Library@2.2.4 + adafruit/Adafruit BMP3XX Library@2.1.5 + adafruit/Adafruit MCP9808 Library@2.0.2 + adafruit/Adafruit INA260 Library@1.5.2 + adafruit/Adafruit INA219@1.2.3 + adafruit/Adafruit MAX1704X@1.0.3 + adafruit/Adafruit SHTC3 Library@1.0.1 + adafruit/Adafruit LPS2X@2.0.6 + adafruit/Adafruit SHT31 Library@2.2.2 + adafruit/Adafruit PM25 AQI Sensor@1.1.1 + adafruit/Adafruit MPU6050@2.2.6 + adafruit/Adafruit LIS3DH@1.3.0 + adafruit/Adafruit AHTX0@2.0.5 + adafruit/Adafruit LSM6DS@4.7.3 + adafruit/Adafruit VEML7700 Library@2.1.6 + adafruit/Adafruit SHT4x Library@1.0.5 + adafruit/Adafruit TSL2591 Library@1.4.5 + sparkfun/SparkFun Qwiic Scale NAU7802 Arduino Library@1.0.6 + sparkfun/SparkFun 9DoF IMU Breakout - ICM 20948 - Arduino Library@1.2.13 + ClosedCube OPT3001@1.1.2 + emotibit/EmotiBit MLX90632@1.0.8 + sparkfun/SparkFun MAX3010x Pulse and Proximity Sensor Library@1.1.2 + adafruit/Adafruit MLX90614 Library@2.1.5 + https://github.com/boschsensortec/Bosch-BSEC2-Library#v1.7.2502 + boschsensortec/BME68x Sensor Library@1.1.40407 + https://github.com/KodinLanewave/INA3221@1.0.1 + mprograms/QMC5883LCompass@1.2.3 + dfrobot/DFRobot_RTU@1.0.3 + https://github.com/meshtastic/DFRobot_LarkWeatherStation#4de3a9cadef0f6a5220a8a906cf9775b02b0040d \ No newline at end of file diff --git a/protobufs b/protobufs index 49ebc4783..02e6576ef 160000 --- a/protobufs +++ b/protobufs @@ -1 +1 @@ -Subproject commit 49ebc4783275f108a9f8723ca52a6edf0a954c55 +Subproject commit 02e6576efaa2f691be9504b8c1c6261703f7a277 diff --git a/release/latest/.gitignore b/release/latest/.gitignore deleted file mode 100644 index fff416667..000000000 --- a/release/latest/.gitignore +++ /dev/null @@ -1 +0,0 @@ -curfirmwareversion.xml diff --git a/src/AmbientLightingThread.h b/src/AmbientLightingThread.h index fdd4b53fa..600583348 100644 --- a/src/AmbientLightingThread.h +++ b/src/AmbientLightingThread.h @@ -21,7 +21,7 @@ namespace concurrency class AmbientLightingThread : public concurrency::OSThread { public: - explicit AmbientLightingThread(ScanI2C::DeviceType type) : OSThread("AmbientLightingThread") + explicit AmbientLightingThread(ScanI2C::DeviceType type) : OSThread("AmbientLighting") { notifyDeepSleepObserver.observe(¬ifyDeepSleep); // Let us know when shutdown() is issued. @@ -42,18 +42,18 @@ class AmbientLightingThread : public concurrency::OSThread #ifdef HAS_NCP5623 _type = type; if (_type == ScanI2C::DeviceType::NONE) { - LOG_DEBUG("AmbientLightingThread disabling due to no RGB leds found on I2C bus\n"); + LOG_DEBUG("AmbientLighting Disable due to no RGB leds found on I2C bus"); disable(); return; } #endif #if defined(HAS_NCP5623) || defined(RGBLED_RED) || defined(HAS_NEOPIXEL) || defined(UNPHONE) if (!moduleConfig.ambient_lighting.led_state) { - LOG_DEBUG("AmbientLightingThread disabling due to moduleConfig.ambient_lighting.led_state OFF\n"); + LOG_DEBUG("AmbientLighting Disable due to moduleConfig.ambient_lighting.led_state OFF"); disable(); return; } - LOG_DEBUG("AmbientLightingThread initializing\n"); + LOG_DEBUG("AmbientLighting init"); #ifdef HAS_NCP5623 if (_type == ScanI2C::NCP5623) { rgb.begin(); @@ -106,27 +106,27 @@ class AmbientLightingThread : public concurrency::OSThread rgb.setRed(0); rgb.setGreen(0); rgb.setBlue(0); - LOG_INFO("Turn Off NCP5623 Ambient lighting.\n"); + LOG_INFO("OFF: NCP5623 Ambient lighting"); #endif #ifdef HAS_NEOPIXEL pixels.clear(); pixels.show(); - LOG_INFO("Turn Off NeoPixel Ambient lighting.\n"); + LOG_INFO("OFF: NeoPixel Ambient lighting"); #endif #ifdef RGBLED_CA analogWrite(RGBLED_RED, 255 - 0); analogWrite(RGBLED_GREEN, 255 - 0); analogWrite(RGBLED_BLUE, 255 - 0); - LOG_INFO("Turn Off Ambient lighting RGB Common Anode.\n"); + LOG_INFO("OFF: Ambient light RGB Common Anode"); #elif defined(RGBLED_RED) analogWrite(RGBLED_RED, 0); analogWrite(RGBLED_GREEN, 0); analogWrite(RGBLED_BLUE, 0); - LOG_INFO("Turn Off Ambient lighting RGB Common Cathode.\n"); + LOG_INFO("OFF: Ambient light RGB Common Cathode"); #endif #ifdef UNPHONE unphone.rgb(0, 0, 0); - LOG_INFO("Turn Off unPhone Ambient lighting.\n"); + LOG_INFO("OFF: unPhone Ambient lighting"); #endif return 0; } @@ -138,9 +138,8 @@ class AmbientLightingThread : public concurrency::OSThread rgb.setRed(moduleConfig.ambient_lighting.red); rgb.setGreen(moduleConfig.ambient_lighting.green); rgb.setBlue(moduleConfig.ambient_lighting.blue); - LOG_DEBUG("Initializing NCP5623 Ambient lighting w/ current=%d, red=%d, green=%d, blue=%d\n", - moduleConfig.ambient_lighting.current, moduleConfig.ambient_lighting.red, moduleConfig.ambient_lighting.green, - moduleConfig.ambient_lighting.blue); + LOG_DEBUG("Init NCP5623 Ambient light w/ current=%d, red=%d, green=%d, blue=%d", moduleConfig.ambient_lighting.current, + moduleConfig.ambient_lighting.red, moduleConfig.ambient_lighting.green, moduleConfig.ambient_lighting.blue); #endif #ifdef HAS_NEOPIXEL pixels.fill(pixels.Color(moduleConfig.ambient_lighting.red, moduleConfig.ambient_lighting.green, @@ -158,7 +157,7 @@ class AmbientLightingThread : public concurrency::OSThread #endif #endif pixels.show(); - LOG_DEBUG("Initializing NeoPixel Ambient lighting w/ brightness(current)=%d, red=%d, green=%d, blue=%d\n", + LOG_DEBUG("Init NeoPixel Ambient light w/ brightness(current)=%d, red=%d, green=%d, blue=%d", moduleConfig.ambient_lighting.current, moduleConfig.ambient_lighting.red, moduleConfig.ambient_lighting.green, moduleConfig.ambient_lighting.blue); #endif @@ -166,21 +165,21 @@ class AmbientLightingThread : public concurrency::OSThread analogWrite(RGBLED_RED, 255 - moduleConfig.ambient_lighting.red); analogWrite(RGBLED_GREEN, 255 - moduleConfig.ambient_lighting.green); analogWrite(RGBLED_BLUE, 255 - moduleConfig.ambient_lighting.blue); - LOG_DEBUG("Initializing Ambient lighting RGB Common Anode w/ red=%d, green=%d, blue=%d\n", - moduleConfig.ambient_lighting.red, moduleConfig.ambient_lighting.green, moduleConfig.ambient_lighting.blue); + LOG_DEBUG("Init Ambient light RGB Common Anode w/ red=%d, green=%d, blue=%d", moduleConfig.ambient_lighting.red, + moduleConfig.ambient_lighting.green, moduleConfig.ambient_lighting.blue); #elif defined(RGBLED_RED) analogWrite(RGBLED_RED, moduleConfig.ambient_lighting.red); analogWrite(RGBLED_GREEN, moduleConfig.ambient_lighting.green); analogWrite(RGBLED_BLUE, moduleConfig.ambient_lighting.blue); - LOG_DEBUG("Initializing Ambient lighting RGB Common Cathode w/ red=%d, green=%d, blue=%d\n", - moduleConfig.ambient_lighting.red, moduleConfig.ambient_lighting.green, moduleConfig.ambient_lighting.blue); + LOG_DEBUG("Init Ambient light RGB Common Cathode w/ red=%d, green=%d, blue=%d", moduleConfig.ambient_lighting.red, + moduleConfig.ambient_lighting.green, moduleConfig.ambient_lighting.blue); #endif #ifdef UNPHONE unphone.rgb(moduleConfig.ambient_lighting.red, moduleConfig.ambient_lighting.green, moduleConfig.ambient_lighting.blue); - LOG_DEBUG("Initializing unPhone Ambient lighting w/ red=%d, green=%d, blue=%d\n", moduleConfig.ambient_lighting.red, + LOG_DEBUG("Init unPhone Ambient light w/ red=%d, green=%d, blue=%d", moduleConfig.ambient_lighting.red, moduleConfig.ambient_lighting.green, moduleConfig.ambient_lighting.blue); #endif } }; -} // namespace concurrency \ No newline at end of file +} // namespace concurrency diff --git a/src/AudioThread.h b/src/AudioThread.h index c9f253440..6d560ec55 100644 --- a/src/AudioThread.h +++ b/src/AudioThread.h @@ -16,7 +16,7 @@ class AudioThread : public concurrency::OSThread { public: - AudioThread() : OSThread("AudioThread") { initOutput(); } + AudioThread() : OSThread("Audio") { initOutput(); } void beginRttl(const void *data, uint32_t len) { @@ -64,7 +64,7 @@ class AudioThread : public concurrency::OSThread void initOutput() { audioOut = new AudioOutputI2S(1, AudioOutputI2S::EXTERNAL_I2S); - audioOut->SetPinout(DAC_I2S_BCK, DAC_I2S_WS, DAC_I2S_DOUT); + audioOut->SetPinout(DAC_I2S_BCK, DAC_I2S_WS, DAC_I2S_DOUT, DAC_I2S_MCLK); audioOut->SetGain(0.2); }; diff --git a/src/ButtonThread.cpp b/src/ButtonThread.cpp index 9e6ef55f5..238359952 100644 --- a/src/ButtonThread.cpp +++ b/src/ButtonThread.cpp @@ -1,4 +1,5 @@ #include "ButtonThread.h" +#include "../userPrefs.h" #include "configuration.h" #if !MESHTASTIC_EXCLUDE_GPS #include "GPS.h" @@ -26,20 +27,25 @@ using namespace concurrency; ButtonThread *buttonThread; // Declared extern in header volatile ButtonThread::ButtonEventType ButtonThread::btnEvent = ButtonThread::BUTTON_EVENT_NONE; -#if defined(BUTTON_PIN) || defined(ARCH_PORTDUINO) +#if defined(BUTTON_PIN) || defined(ARCH_PORTDUINO) || defined(USERPREFS_BUTTON_PIN) OneButton ButtonThread::userButton; // Get reference to static member #endif ButtonThread::ButtonThread() : OSThread("Button") { -#if defined(BUTTON_PIN) || defined(ARCH_PORTDUINO) +#if defined(BUTTON_PIN) || defined(ARCH_PORTDUINO) || defined(USERPREFS_BUTTON_PIN) #if defined(ARCH_PORTDUINO) if (settingsMap.count(user) != 0 && settingsMap[user] != RADIOLIB_NC) { this->userButton = OneButton(settingsMap[user], true, true); - LOG_DEBUG("Using GPIO%02d for button\n", settingsMap[user]); + LOG_DEBUG("Use GPIO%02d for button", settingsMap[user]); } #elif defined(BUTTON_PIN) - int pin = config.device.button_gpio ? config.device.button_gpio : BUTTON_PIN; // Resolved button pin +#if !defined(USERPREFS_BUTTON_PIN) + int pin = config.device.button_gpio ? config.device.button_gpio : BUTTON_PIN; // Resolved button pin +#endif +#ifdef USERPREFS_BUTTON_PIN + int pin = config.device.button_gpio ? config.device.button_gpio : USERPREFS_BUTTON_PIN; // Resolved button pin +#endif #if defined(HELTEC_CAPSULE_SENSOR_V3) this->userButton = OneButton(pin, false, false); #elif defined(BUTTON_ACTIVE_LOW) @@ -47,7 +53,7 @@ ButtonThread::ButtonThread() : OSThread("Button") #else this->userButton = OneButton(pin, true, true); #endif - LOG_DEBUG("Using GPIO%02d for button\n", pin); + LOG_DEBUG("Use GPIO%02d for button", pin); #endif #ifdef INPUT_PULLUP_SENSE @@ -59,7 +65,7 @@ ButtonThread::ButtonThread() : OSThread("Button") #endif #endif -#if defined(BUTTON_PIN) || defined(ARCH_PORTDUINO) +#if defined(BUTTON_PIN) || defined(ARCH_PORTDUINO) || defined(USERPREFS_BUTTON_PIN) userButton.attachClick(userButtonPressed); userButton.setClickMs(BUTTON_CLICK_MS); userButton.setPressMs(BUTTON_LONGPRESS_MS); @@ -102,7 +108,7 @@ int32_t ButtonThread::runOnce() // If the button is pressed we suppress CPU sleep until release canSleep = true; // Assume we should not keep the board awake -#if defined(BUTTON_PIN) +#if defined(BUTTON_PIN) || defined(USERPREFS_BUTTON_PIN) userButton.tick(); canSleep &= userButton.isIdle(); #elif defined(ARCH_PORTDUINO) @@ -123,14 +129,19 @@ int32_t ButtonThread::runOnce() if (btnEvent != BUTTON_EVENT_NONE) { switch (btnEvent) { case BUTTON_EVENT_PRESSED: { - LOG_BUTTON("press!\n"); + LOG_BUTTON("press!"); // If a nag notification is running, stop it and prevent other actions if (moduleConfig.external_notification.enabled && (externalNotificationModule->nagCycleCutoff != UINT32_MAX)) { externalNotificationModule->stopNow(); return 50; } #ifdef BUTTON_PIN +#if !defined(USERPREFS_BUTTON_PIN) if (((config.device.button_gpio ? config.device.button_gpio : BUTTON_PIN) != +#endif +#if defined(USERPREFS_BUTTON_PIN) + if (((config.device.button_gpio ? config.device.button_gpio : USERPREFS_BUTTON_PIN) != +#endif moduleConfig.canned_message.inputbroker_pin_press) || !(moduleConfig.canned_message.updown1_enabled || moduleConfig.canned_message.rotary1_enabled) || !moduleConfig.canned_message.enabled) { @@ -148,7 +159,7 @@ int32_t ButtonThread::runOnce() } case BUTTON_EVENT_DOUBLE_PRESSED: { - LOG_BUTTON("Double press!\n"); + LOG_BUTTON("Double press!"); service->refreshLocalMeshNode(); auto sentPosition = service->trySendPosition(NODENUM_BROADCAST, true); if (screen) { @@ -162,7 +173,7 @@ int32_t ButtonThread::runOnce() } case BUTTON_EVENT_MULTI_PRESSED: { - LOG_BUTTON("Mulitipress! %hux\n", multipressClickCount); + LOG_BUTTON("Mulitipress! %hux", multipressClickCount); switch (multipressClickCount) { #if HAS_GPS // 3 clicks: toggle GPS @@ -189,7 +200,7 @@ int32_t ButtonThread::runOnce() } // end multipress event case BUTTON_EVENT_LONG_PRESSED: { - LOG_BUTTON("Long press!\n"); + LOG_BUTTON("Long press!"); powerFSM.trigger(EVENT_PRESS); if (screen) { screen->startAlert("Shutting down..."); @@ -201,7 +212,7 @@ int32_t ButtonThread::runOnce() // Do actual shutdown when button released, otherwise the button release // may wake the board immediatedly. case BUTTON_EVENT_LONG_RELEASED: { - LOG_INFO("Shutdown from long press\n"); + LOG_INFO("Shutdown from long press"); playShutdownMelody(); delay(3000); power->shutdown(); @@ -210,7 +221,7 @@ int32_t ButtonThread::runOnce() #ifdef BUTTON_PIN_TOUCH case BUTTON_EVENT_TOUCH_LONG_PRESSED: { - LOG_BUTTON("Touch press!\n"); + LOG_BUTTON("Touch press!"); if (screen) { // Wake if asleep if (powerFSM.getState() == &stateDARK) @@ -244,7 +255,12 @@ void ButtonThread::attachButtonInterrupts() #elif defined(BUTTON_PIN) // Interrupt for user button, during normal use. Improves responsiveness. attachInterrupt( +#if !defined(USERPREFS_BUTTON_PIN) config.device.button_gpio ? config.device.button_gpio : BUTTON_PIN, +#endif +#if defined(USERPREFS_BUTTON_PIN) + config.device.button_gpio ? config.device.button_gpio : USERPREFS_BUTTON_PIN, +#endif []() { ButtonThread::userButton.tick(); runASAP = true; @@ -273,8 +289,13 @@ void ButtonThread::detachButtonInterrupts() if (settingsMap.count(user) != 0 && settingsMap[user] != RADIOLIB_NC) detachInterrupt(settingsMap[user]); #elif defined(BUTTON_PIN) +#if !defined(USERPREFS_BUTTON_PIN) detachInterrupt(config.device.button_gpio ? config.device.button_gpio : BUTTON_PIN); #endif +#if defined(USERPREFS_BUTTON_PIN) + detachInterrupt(config.device.button_gpio ? config.device.button_gpio : USERPREFS_BUTTON_PIN); +#endif +#endif #ifdef BUTTON_PIN_ALT detachInterrupt(BUTTON_PIN_ALT); @@ -315,7 +336,7 @@ void ButtonThread::userButtonMultiPressed(void *callerThread) // Non-static method, runs during callback. Grabs info while still valid void ButtonThread::storeClickCount() { -#ifdef BUTTON_PIN +#if defined(BUTTON_PIN) || defined(USERPREFS_BUTTON_PIN) multipressClickCount = userButton.getNumberClicks(); #endif } diff --git a/src/ButtonThread.h b/src/ButtonThread.h index 9cd7b3dac..a01a1718f 100644 --- a/src/ButtonThread.h +++ b/src/ButtonThread.h @@ -38,7 +38,7 @@ class ButtonThread : public concurrency::OSThread void storeClickCount(); private: -#if defined(BUTTON_PIN) || defined(ARCH_PORTDUINO) +#if defined(BUTTON_PIN) || defined(ARCH_PORTDUINO) || defined(USERPREFS_BUTTON_PIN) static OneButton userButton; // Static - accessed from an interrupt #endif #ifdef BUTTON_PIN_ALT diff --git a/src/FSCommon.cpp b/src/FSCommon.cpp index 24d3f477e..6cd17dac8 100644 --- a/src/FSCommon.cpp +++ b/src/FSCommon.cpp @@ -53,7 +53,7 @@ bool lfs_assert_failed = extern "C" void lfs_assert(const char *reason) { - LOG_ERROR("LFS assert: %s\n", reason); + LOG_ERROR("LFS assert: %s", reason); lfs_assert_failed = true; } @@ -75,19 +75,19 @@ bool copyFile(const char *from, const char *to) r = OSFS::getFile(from, cbuffer); if (r == notfound) { - LOG_ERROR("Failed to open source file %s\n", from); + LOG_ERROR("Failed to open source file %s", from); return false; } else if (r == noerr) { r = OSFS::newFile(to, cbuffer, true); if (r == noerr) { return true; } else { - LOG_ERROR("OSFS Error %d\n", r); + LOG_ERROR("OSFS Error %d", r); return false; } } else { - LOG_ERROR("OSFS Error %d\n", r); + LOG_ERROR("OSFS Error %d", r); return false; } return true; @@ -97,13 +97,13 @@ bool copyFile(const char *from, const char *to) File f1 = FSCom.open(from, FILE_O_READ); if (!f1) { - LOG_ERROR("Failed to open source file %s\n", from); + LOG_ERROR("Failed to open source file %s", from); return false; } File f2 = FSCom.open(to, FILE_O_WRITE); if (!f2) { - LOG_ERROR("Failed to open destination file %s\n", to); + LOG_ERROR("Failed to open destination file %s", to); return false; } @@ -231,7 +231,7 @@ void listDir(const char *dirname, uint8_t levels, bool del) #ifdef ARCH_ESP32 listDir(file.path(), levels - 1, del); if (del) { - LOG_DEBUG("Removing %s\n", file.path()); + LOG_DEBUG("Remove %s", file.path()); strncpy(buffer, file.path(), sizeof(buffer)); file.close(); FSCom.rmdir(buffer); @@ -241,7 +241,7 @@ void listDir(const char *dirname, uint8_t levels, bool del) #elif (defined(ARCH_RP2040) || defined(ARCH_PORTDUINO)) listDir(file.name(), levels - 1, del); if (del) { - LOG_DEBUG("Removing %s\n", file.name()); + LOG_DEBUG("Remove %s", file.name()); strncpy(buffer, file.name(), sizeof(buffer)); file.close(); FSCom.rmdir(buffer); @@ -249,7 +249,7 @@ void listDir(const char *dirname, uint8_t levels, bool del) file.close(); } #else - LOG_DEBUG(" %s (directory)\n", file.name()); + LOG_DEBUG(" %s (directory)", file.name()); listDir(file.name(), levels - 1, del); file.close(); #endif @@ -257,26 +257,26 @@ void listDir(const char *dirname, uint8_t levels, bool del) } else { #ifdef ARCH_ESP32 if (del) { - LOG_DEBUG("Deleting %s\n", file.path()); + LOG_DEBUG("Delete %s", file.path()); strncpy(buffer, file.path(), sizeof(buffer)); file.close(); FSCom.remove(buffer); } else { - LOG_DEBUG(" %s (%i Bytes)\n", file.path(), file.size()); + LOG_DEBUG(" %s (%i Bytes)", file.path(), file.size()); file.close(); } #elif (defined(ARCH_RP2040) || defined(ARCH_PORTDUINO)) if (del) { - LOG_DEBUG("Deleting %s\n", file.name()); + LOG_DEBUG("Delete %s", file.name()); strncpy(buffer, file.name(), sizeof(buffer)); file.close(); FSCom.remove(buffer); } else { - LOG_DEBUG(" %s (%i Bytes)\n", file.name(), file.size()); + LOG_DEBUG(" %s (%i Bytes)", file.name(), file.size()); file.close(); } #else - LOG_DEBUG(" %s (%i Bytes)\n", file.name(), file.size()); + LOG_DEBUG(" %s (%i Bytes)", file.name(), file.size()); file.close(); #endif } @@ -284,7 +284,7 @@ void listDir(const char *dirname, uint8_t levels, bool del) } #ifdef ARCH_ESP32 if (del) { - LOG_DEBUG("Removing %s\n", root.path()); + LOG_DEBUG("Remove %s", root.path()); strncpy(buffer, root.path(), sizeof(buffer)); root.close(); FSCom.rmdir(buffer); @@ -293,7 +293,7 @@ void listDir(const char *dirname, uint8_t levels, bool del) } #elif (defined(ARCH_RP2040) || defined(ARCH_PORTDUINO)) if (del) { - LOG_DEBUG("Removing %s\n", root.name()); + LOG_DEBUG("Remove %s", root.name()); strncpy(buffer, root.name(), sizeof(buffer)); root.close(); FSCom.rmdir(buffer); @@ -329,13 +329,13 @@ void fsInit() { #ifdef FSCom if (!FSBegin()) { - LOG_ERROR("Filesystem mount Failed.\n"); + LOG_ERROR("Filesystem mount failed"); // assert(0); This auto-formats the partition, so no need to fail here. } #if defined(ARCH_ESP32) - LOG_DEBUG("Filesystem files (%d/%d Bytes):\n", FSCom.usedBytes(), FSCom.totalBytes()); + LOG_DEBUG("Filesystem files (%d/%d Bytes):", FSCom.usedBytes(), FSCom.totalBytes()); #else - LOG_DEBUG("Filesystem files:\n"); + LOG_DEBUG("Filesystem files:"); #endif listDir("/", 10); #endif @@ -350,28 +350,28 @@ void setupSDCard() SDHandler.begin(SPI_SCK, SPI_MISO, SPI_MOSI); if (!SD.begin(SDCARD_CS, SDHandler)) { - LOG_DEBUG("No SD_MMC card detected\n"); + LOG_DEBUG("No SD_MMC card detected"); return; } uint8_t cardType = SD.cardType(); if (cardType == CARD_NONE) { - LOG_DEBUG("No SD_MMC card attached\n"); + LOG_DEBUG("No SD_MMC card attached"); return; } LOG_DEBUG("SD_MMC Card Type: "); if (cardType == CARD_MMC) { - LOG_DEBUG("MMC\n"); + LOG_DEBUG("MMC"); } else if (cardType == CARD_SD) { - LOG_DEBUG("SDSC\n"); + LOG_DEBUG("SDSC"); } else if (cardType == CARD_SDHC) { - LOG_DEBUG("SDHC\n"); + LOG_DEBUG("SDHC"); } else { - LOG_DEBUG("UNKNOWN\n"); + LOG_DEBUG("UNKNOWN"); } uint64_t cardSize = SD.cardSize() / (1024 * 1024); - LOG_DEBUG("SD Card Size: %lu MB\n", (uint32_t)cardSize); - LOG_DEBUG("Total space: %lu MB\n", (uint32_t)(SD.totalBytes() / (1024 * 1024))); - LOG_DEBUG("Used space: %lu MB\n", (uint32_t)(SD.usedBytes() / (1024 * 1024))); + LOG_DEBUG("SD Card Size: %lu MB", (uint32_t)cardSize); + LOG_DEBUG("Total space: %lu MB", (uint32_t)(SD.totalBytes() / (1024 * 1024))); + LOG_DEBUG("Used space: %lu MB", (uint32_t)(SD.usedBytes() / (1024 * 1024))); #endif } \ No newline at end of file diff --git a/src/GPSStatus.h b/src/GPSStatus.h index c2ab16c86..4b7997935 100644 --- a/src/GPSStatus.h +++ b/src/GPSStatus.h @@ -50,9 +50,6 @@ class GPSStatus : public Status int32_t getLatitude() const { if (config.position.fixed_position) { -#ifdef GPS_EXTRAVERBOSE - LOG_WARN("Using fixed latitude\n"); -#endif meshtastic_NodeInfoLite *node = nodeDB->getMeshNode(nodeDB->getNodeNum()); return node->position.latitude_i; } else { @@ -63,9 +60,6 @@ class GPSStatus : public Status int32_t getLongitude() const { if (config.position.fixed_position) { -#ifdef GPS_EXTRAVERBOSE - LOG_WARN("Using fixed longitude\n"); -#endif meshtastic_NodeInfoLite *node = nodeDB->getMeshNode(nodeDB->getNodeNum()); return node->position.longitude_i; } else { @@ -76,9 +70,6 @@ class GPSStatus : public Status int32_t getAltitude() const { if (config.position.fixed_position) { -#ifdef GPS_EXTRAVERBOSE - LOG_WARN("Using fixed altitude\n"); -#endif meshtastic_NodeInfoLite *node = nodeDB->getMeshNode(nodeDB->getNodeNum()); return node->position.altitude; } else { @@ -94,8 +85,8 @@ class GPSStatus : public Status bool matches(const GPSStatus *newStatus) const { -#ifdef GPS_EXTRAVERBOSE - LOG_DEBUG("GPSStatus.match() new pos@%x to old pos@%x\n", newStatus->p.timestamp, p.timestamp); +#ifdef GPS_DEBUG + LOG_DEBUG("GPSStatus.match() new pos@%x to old pos@%x", newStatus->p.timestamp, p.timestamp); #endif return (newStatus->hasLock != hasLock || newStatus->isConnected != isConnected || newStatus->isPowerSaving != isPowerSaving || newStatus->p.latitude_i != p.latitude_i || @@ -112,7 +103,7 @@ class GPSStatus : public Status if (isDirty && p.timestamp && (newStatus->p.timestamp == p.timestamp)) { // We can NEVER be in two locations at the same time! (also PR #886) - LOG_ERROR("BUG: Positional timestamp unchanged from prev solution\n"); + LOG_ERROR("BUG: Positional timestamp unchanged from prev solution"); } initialized = true; @@ -124,11 +115,11 @@ class GPSStatus : public Status if (isDirty) { if (hasLock) { // In debug logs, identify position by @timestamp:stage (stage 3 = notify) - LOG_DEBUG("New GPS pos@%x:3 lat=%f lon=%f alt=%d pdop=%.2f track=%.2f speed=%.2f sats=%d\n", p.timestamp, + LOG_DEBUG("New GPS pos@%x:3 lat=%f lon=%f alt=%d pdop=%.2f track=%.2f speed=%.2f sats=%d", p.timestamp, p.latitude_i * 1e-7, p.longitude_i * 1e-7, p.altitude, p.PDOP * 1e-2, p.ground_track * 1e-5, p.ground_speed * 1e-2, p.sats_in_view); } else { - LOG_DEBUG("No GPS lock\n"); + LOG_DEBUG("No GPS lock"); } onNewStatus.notifyObservers(this); } diff --git a/src/GpioLogic.cpp b/src/GpioLogic.cpp index cbe26fc60..ecdf514e4 100644 --- a/src/GpioLogic.cpp +++ b/src/GpioLogic.cpp @@ -12,7 +12,6 @@ void GpioVirtPin::set(bool value) void GpioHwPin::set(bool value) { - // if (num == 3) LOG_DEBUG("Setting pin %d to %d\n", num, value); pinMode(num, OUTPUT); digitalWrite(num, value); } @@ -66,7 +65,7 @@ GpioBinaryTransformer::GpioBinaryTransformer(GpioVirtPin *inPin1, GpioVirtPin *i assert(!inPin2->dependentPin); // We only allow one dependent pin inPin2->dependentPin = this; - // Don't update at construction time, because various GpioPins might be global constructor based not yet initied because + // Don't update at construction time, because various GpioPins might be global constructor based not yet initiated because // order of operations for global constructors is not defined. // update(); } @@ -88,7 +87,6 @@ void GpioBinaryTransformer::update() newValue = (GpioVirtPin::PinState)(p1 && p2); break; case Or: - // LOG_DEBUG("Doing GPIO OR\n"); newValue = (GpioVirtPin::PinState)(p1 || p2); break; case Xor: @@ -101,4 +99,4 @@ void GpioBinaryTransformer::update() set(newValue); } -GpioSplitter::GpioSplitter(GpioPin *outPin1, GpioPin *outPin2) : outPin1(outPin1), outPin2(outPin2) {} \ No newline at end of file +GpioSplitter::GpioSplitter(GpioPin *outPin1, GpioPin *outPin2) : outPin1(outPin1), outPin2(outPin2) {} diff --git a/src/NodeStatus.h b/src/NodeStatus.h index e6bf31aed..60d1bdd98 100644 --- a/src/NodeStatus.h +++ b/src/NodeStatus.h @@ -56,7 +56,7 @@ class NodeStatus : public Status numTotal = newStatus->getNumTotal(); } if (isDirty || newStatus->forceUpdate) { - LOG_DEBUG("Node status update: %d online, %d total\n", numOnline, numTotal); + LOG_DEBUG("Node status update: %d online, %d total", numOnline, numTotal); onNewStatus.notifyObservers(this); } return 0; diff --git a/src/Power.cpp b/src/Power.cpp index 2fe28633a..a354b74e2 100644 --- a/src/Power.cpp +++ b/src/Power.cpp @@ -154,9 +154,16 @@ static void adcEnable() #ifdef ADC_CTRL // enable adc voltage divider when we need to read #ifdef ADC_USE_PULLUP pinMode(ADC_CTRL, INPUT_PULLUP); +#else +#ifdef HELTEC_V3 + pinMode(ADC_CTRL, INPUT); + uint8_t adc_ctl_enable_value = !(digitalRead(ADC_CTRL)); + pinMode(ADC_CTRL, OUTPUT); + digitalWrite(ADC_CTRL, adc_ctl_enable_value); #else pinMode(ADC_CTRL, OUTPUT); digitalWrite(ADC_CTRL, ADC_CTRL_ENABLED); +#endif #endif delay(10); #endif @@ -167,10 +174,14 @@ static void adcDisable() #ifdef ADC_CTRL // disable adc voltage divider when we need to read #ifdef ADC_USE_PULLUP pinMode(ADC_CTRL, INPUT_PULLDOWN); +#else +#ifdef HELTEC_V3 + pinMode(ADC_CTRL, ANALOG); #else digitalWrite(ADC_CTRL, !ADC_CTRL_ENABLED); #endif #endif +#endif } #endif @@ -240,7 +251,6 @@ class AnalogBatteryLevel : public HasBatteryLevel #if HAS_TELEMETRY && !defined(ARCH_PORTDUINO) && !defined(ARCH_STM32WL) && !defined(HAS_PMU) && \ !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR if (hasINA()) { - LOG_DEBUG("Using INA on I2C addr 0x%x for device battery voltage\n", config.power.device_battery_ina_address); return getINAVoltage(); } #endif @@ -260,7 +270,7 @@ class AnalogBatteryLevel : public HasBatteryLevel config.power.adc_multiplier_override > 0 ? config.power.adc_multiplier_override : ADC_MULTIPLIER; // Do not call analogRead() often. const uint32_t min_read_interval = 5000; - if (!Throttle::isWithinTimespanMs(last_read_time_ms, min_read_interval)) { + if (!initial_read_done || !Throttle::isWithinTimespanMs(last_read_time_ms, min_read_interval)) { last_read_time_ms = millis(); uint32_t raw = 0; @@ -290,7 +300,7 @@ class AnalogBatteryLevel : public HasBatteryLevel last_read_value += (scaled - last_read_value) * 0.5; // Virtual LPF } - // LOG_DEBUG("battery gpio %d raw val=%u scaled=%u filtered=%u\n", BATTERY_PIN, raw, (uint32_t)(scaled), (uint32_t) + // LOG_DEBUG("battery gpio %d raw val=%u scaled=%u filtered=%u", BATTERY_PIN, raw, (uint32_t)(scaled), (uint32_t) // (last_read_value)); } return last_read_value; @@ -335,7 +345,7 @@ class AnalogBatteryLevel : public HasBatteryLevel raw += adc_buf; raw_c++; // Count valid samples } else { - LOG_DEBUG("An attempt to sample ADC2 failed\n"); + LOG_DEBUG("An attempt to sample ADC2 failed"); } } @@ -360,7 +370,12 @@ class AnalogBatteryLevel : public HasBatteryLevel /** * return true if there is a battery installed in this unit */ + // if we have a integrated device with a battery, we can assume that the battery is always connected +#ifdef BATTERY_IMMUTABLE + virtual bool isBatteryConnect() override { return true; } +#else virtual bool isBatteryConnect() override { return getBatteryPercent() != -1; } +#endif /// If we see a battery voltage higher than physics allows - assume charger is pumping /// in power @@ -495,7 +510,7 @@ bool Power::analogInit() #endif #ifdef BATTERY_PIN - LOG_DEBUG("Using analog input %d for battery level\n", BATTERY_PIN); + LOG_DEBUG("Use analog input %d for battery level", BATTERY_PIN); // disable any internal pullups pinMode(BATTERY_PIN, INPUT); @@ -526,18 +541,18 @@ bool Power::analogInit() esp_adc_cal_value_t val_type = esp_adc_cal_characterize(unit, atten, width, DEFAULT_VREF, adc_characs); // show ADC characterization base if (val_type == ESP_ADC_CAL_VAL_EFUSE_TP) { - LOG_INFO("ADCmod: ADC characterization based on Two Point values stored in eFuse\n"); + LOG_INFO("ADC config based on Two Point values stored in eFuse"); } else if (val_type == ESP_ADC_CAL_VAL_EFUSE_VREF) { - LOG_INFO("ADCmod: ADC characterization based on reference voltage stored in eFuse\n"); + LOG_INFO("ADC config based on reference voltage stored in eFuse"); } #ifdef CONFIG_IDF_TARGET_ESP32S3 // ESP32S3 else if (val_type == ESP_ADC_CAL_VAL_EFUSE_TP_FIT) { - LOG_INFO("ADCmod: ADC Characterization based on Two Point values and fitting curve coefficients stored in eFuse\n"); + LOG_INFO("ADC config based on Two Point values and fitting curve coefficients stored in eFuse"); } #endif else { - LOG_INFO("ADCmod: ADC characterization based on default reference voltage\n"); + LOG_INFO("ADC config based on default reference voltage"); } #endif // ARCH_ESP32 @@ -586,7 +601,7 @@ bool Power::setup() void Power::shutdown() { - LOG_INFO("Shutting down\n"); + LOG_INFO("Shutting down"); #if defined(ARCH_NRF52) || defined(ARCH_ESP32) || defined(ARCH_RP2040) #ifdef PIN_LED1 @@ -598,7 +613,7 @@ void Power::shutdown() #ifdef PIN_LED3 ledOff(PIN_LED3); #endif - doDeepSleep(DELAY_FOREVER, false); + doDeepSleep(DELAY_FOREVER, false, false); #endif } @@ -624,7 +639,7 @@ void Power::readPowerStatus() batteryChargePercent = batteryLevel->getBatteryPercent(); } else { // If the AXP192 returns a percentage less than 0, the feature is either not supported or there is an error - // In that case, we compute an estimate of the charge percent based on open circuite voltage table defined + // In that case, we compute an estimate of the charge percent based on open circuit voltage table defined // in power.h batteryChargePercent = clamp((int)(((batteryVoltageMv - (OCV[NUM_OCV_POINTS - 1] * NUM_CELLS)) * 1e2) / ((OCV[0] * NUM_CELLS) - (OCV[NUM_OCV_POINTS - 1] * NUM_CELLS))), @@ -641,7 +656,7 @@ void Power::readPowerStatus() // changes. nrfx_power_usb_state_t nrf_usb_state = nrfx_power_usbstatus_get(); - // LOG_DEBUG("NRF Power %d\n", nrf_usb_state); + // LOG_DEBUG("NRF Power %d", nrf_usb_state); // If changed to DISCONNECTED if (nrf_usb_state == NRFX_POWER_USB_STATE_DISCONNECTED) @@ -654,22 +669,22 @@ void Power::readPowerStatus() // Notify any status instances that are observing us const PowerStatus powerStatus2 = PowerStatus(hasBattery, usbPowered, isCharging, batteryVoltageMv, batteryChargePercent); - LOG_DEBUG("Battery: usbPower=%d, isCharging=%d, batMv=%d, batPct=%d\n", powerStatus2.getHasUSB(), - powerStatus2.getIsCharging(), powerStatus2.getBatteryVoltageMv(), powerStatus2.getBatteryChargePercent()); + LOG_DEBUG("Battery: usbPower=%d, isCharging=%d, batMv=%d, batPct=%d", powerStatus2.getHasUSB(), powerStatus2.getIsCharging(), + powerStatus2.getBatteryVoltageMv(), powerStatus2.getBatteryChargePercent()); newStatus.notifyObservers(&powerStatus2); #ifdef DEBUG_HEAP if (lastheap != memGet.getFreeHeap()) { - LOG_DEBUG("Threads running:"); + std::string threadlist = "Threads running:"; int running = 0; for (int i = 0; i < MAX_THREADS; i++) { auto thread = concurrency::mainController.get(i); if ((thread != nullptr) && (thread->enabled)) { - LOG_DEBUG(" %s", thread->ThreadName.c_str()); + threadlist += vformat(" %s", thread->ThreadName.c_str()); running++; } } - LOG_DEBUG("\n"); - LOG_DEBUG("Heap status: %d/%d bytes free (%d), running %d/%d threads\n", memGet.getFreeHeap(), memGet.getHeapSize(), + LOG_DEBUG(threadlist.c_str()); + LOG_DEBUG("Heap status: %d/%d bytes free (%d), running %d/%d threads", memGet.getFreeHeap(), memGet.getHeapSize(), memGet.getFreeHeap() - lastheap, running, concurrency::mainController.size(false)); lastheap = memGet.getFreeHeap(); } @@ -702,13 +717,13 @@ void Power::readPowerStatus() if (batteryLevel && powerStatus2.getHasBattery() && !powerStatus2.getHasUSB()) { if (batteryLevel->getBattVoltage() < OCV[NUM_OCV_POINTS - 1]) { low_voltage_counter++; - LOG_DEBUG("Low voltage counter: %d/10\n", low_voltage_counter); + LOG_DEBUG("Low voltage counter: %d/10", low_voltage_counter); if (low_voltage_counter > 10) { #ifdef ARCH_NRF52 // We can't trigger deep sleep on NRF52, it's freezing the board - LOG_DEBUG("Low voltage detected, but not triggering deep sleep\n"); + LOG_DEBUG("Low voltage detected, but not trigger deep sleep"); #else - LOG_INFO("Low voltage detected, triggering deep sleep\n"); + LOG_INFO("Low voltage detected, trigger deep sleep"); powerFSM.trigger(EVENT_LOW_BATTERY); #endif } @@ -730,12 +745,12 @@ int32_t Power::runOnce() PMU->getIrqStatus(); if (PMU->isVbusRemoveIrq()) { - LOG_INFO("USB unplugged\n"); + LOG_INFO("USB unplugged"); powerFSM.trigger(EVENT_POWER_DISCONNECTED); } if (PMU->isVbusInsertIrq()) { - LOG_INFO("USB plugged In\n"); + LOG_INFO("USB plugged In"); powerFSM.trigger(EVENT_POWER_CONNECTED); } @@ -743,21 +758,21 @@ int32_t Power::runOnce() Other things we could check if we cared... if (PMU->isBatChagerStartIrq()) { - LOG_DEBUG("Battery start charging\n"); + LOG_DEBUG("Battery start charging"); } if (PMU->isBatChagerDoneIrq()) { - LOG_DEBUG("Battery fully charged\n"); + LOG_DEBUG("Battery fully charged"); } if (PMU->isBatInsertIrq()) { - LOG_DEBUG("Battery inserted\n"); + LOG_DEBUG("Battery inserted"); } if (PMU->isBatRemoveIrq()) { - LOG_DEBUG("Battery removed\n"); + LOG_DEBUG("Battery removed"); } */ #ifndef T_WATCH_S3 // FIXME - why is this triggering on the T-Watch S3? if (PMU->isPekeyLongPressIrq()) { - LOG_DEBUG("PEK long button press\n"); + LOG_DEBUG("PEK long button press"); screen->setOn(false); } #endif @@ -800,22 +815,22 @@ bool Power::axpChipInit() if (!PMU) { PMU = new XPowersAXP2101(*w); if (!PMU->init()) { - LOG_WARN("Failed to find AXP2101 power management\n"); + LOG_WARN("No AXP2101 power management"); delete PMU; PMU = NULL; } else { - LOG_INFO("AXP2101 PMU init succeeded, using AXP2101 PMU\n"); + LOG_INFO("AXP2101 PMU init succeeded"); } } if (!PMU) { PMU = new XPowersAXP192(*w); if (!PMU->init()) { - LOG_WARN("Failed to find AXP192 power management\n"); + LOG_WARN("No AXP192 power management"); delete PMU; PMU = NULL; } else { - LOG_INFO("AXP192 PMU init succeeded, using AXP192 PMU\n"); + LOG_INFO("AXP192 PMU init succeeded"); } } @@ -972,51 +987,51 @@ bool Power::axpChipInit() PMU->enableBattVoltageMeasure(); if (PMU->isChannelAvailable(XPOWERS_DCDC1)) { - LOG_DEBUG("DC1 : %s Voltage:%u mV \n", PMU->isPowerChannelEnable(XPOWERS_DCDC1) ? "+" : "-", + LOG_DEBUG("DC1 : %s Voltage:%u mV ", PMU->isPowerChannelEnable(XPOWERS_DCDC1) ? "+" : "-", PMU->getPowerChannelVoltage(XPOWERS_DCDC1)); } if (PMU->isChannelAvailable(XPOWERS_DCDC2)) { - LOG_DEBUG("DC2 : %s Voltage:%u mV \n", PMU->isPowerChannelEnable(XPOWERS_DCDC2) ? "+" : "-", + LOG_DEBUG("DC2 : %s Voltage:%u mV ", PMU->isPowerChannelEnable(XPOWERS_DCDC2) ? "+" : "-", PMU->getPowerChannelVoltage(XPOWERS_DCDC2)); } if (PMU->isChannelAvailable(XPOWERS_DCDC3)) { - LOG_DEBUG("DC3 : %s Voltage:%u mV \n", PMU->isPowerChannelEnable(XPOWERS_DCDC3) ? "+" : "-", + LOG_DEBUG("DC3 : %s Voltage:%u mV ", PMU->isPowerChannelEnable(XPOWERS_DCDC3) ? "+" : "-", PMU->getPowerChannelVoltage(XPOWERS_DCDC3)); } if (PMU->isChannelAvailable(XPOWERS_DCDC4)) { - LOG_DEBUG("DC4 : %s Voltage:%u mV \n", PMU->isPowerChannelEnable(XPOWERS_DCDC4) ? "+" : "-", + LOG_DEBUG("DC4 : %s Voltage:%u mV ", PMU->isPowerChannelEnable(XPOWERS_DCDC4) ? "+" : "-", PMU->getPowerChannelVoltage(XPOWERS_DCDC4)); } if (PMU->isChannelAvailable(XPOWERS_LDO2)) { - LOG_DEBUG("LDO2 : %s Voltage:%u mV \n", PMU->isPowerChannelEnable(XPOWERS_LDO2) ? "+" : "-", + LOG_DEBUG("LDO2 : %s Voltage:%u mV ", PMU->isPowerChannelEnable(XPOWERS_LDO2) ? "+" : "-", PMU->getPowerChannelVoltage(XPOWERS_LDO2)); } if (PMU->isChannelAvailable(XPOWERS_LDO3)) { - LOG_DEBUG("LDO3 : %s Voltage:%u mV \n", PMU->isPowerChannelEnable(XPOWERS_LDO3) ? "+" : "-", + LOG_DEBUG("LDO3 : %s Voltage:%u mV ", PMU->isPowerChannelEnable(XPOWERS_LDO3) ? "+" : "-", PMU->getPowerChannelVoltage(XPOWERS_LDO3)); } if (PMU->isChannelAvailable(XPOWERS_ALDO1)) { - LOG_DEBUG("ALDO1: %s Voltage:%u mV \n", PMU->isPowerChannelEnable(XPOWERS_ALDO1) ? "+" : "-", + LOG_DEBUG("ALDO1: %s Voltage:%u mV ", PMU->isPowerChannelEnable(XPOWERS_ALDO1) ? "+" : "-", PMU->getPowerChannelVoltage(XPOWERS_ALDO1)); } if (PMU->isChannelAvailable(XPOWERS_ALDO2)) { - LOG_DEBUG("ALDO2: %s Voltage:%u mV \n", PMU->isPowerChannelEnable(XPOWERS_ALDO2) ? "+" : "-", + LOG_DEBUG("ALDO2: %s Voltage:%u mV ", PMU->isPowerChannelEnable(XPOWERS_ALDO2) ? "+" : "-", PMU->getPowerChannelVoltage(XPOWERS_ALDO2)); } if (PMU->isChannelAvailable(XPOWERS_ALDO3)) { - LOG_DEBUG("ALDO3: %s Voltage:%u mV \n", PMU->isPowerChannelEnable(XPOWERS_ALDO3) ? "+" : "-", + LOG_DEBUG("ALDO3: %s Voltage:%u mV ", PMU->isPowerChannelEnable(XPOWERS_ALDO3) ? "+" : "-", PMU->getPowerChannelVoltage(XPOWERS_ALDO3)); } if (PMU->isChannelAvailable(XPOWERS_ALDO4)) { - LOG_DEBUG("ALDO4: %s Voltage:%u mV \n", PMU->isPowerChannelEnable(XPOWERS_ALDO4) ? "+" : "-", + LOG_DEBUG("ALDO4: %s Voltage:%u mV ", PMU->isPowerChannelEnable(XPOWERS_ALDO4) ? "+" : "-", PMU->getPowerChannelVoltage(XPOWERS_ALDO4)); } if (PMU->isChannelAvailable(XPOWERS_BLDO1)) { - LOG_DEBUG("BLDO1: %s Voltage:%u mV \n", PMU->isPowerChannelEnable(XPOWERS_BLDO1) ? "+" : "-", + LOG_DEBUG("BLDO1: %s Voltage:%u mV ", PMU->isPowerChannelEnable(XPOWERS_BLDO1) ? "+" : "-", PMU->getPowerChannelVoltage(XPOWERS_BLDO1)); } if (PMU->isChannelAvailable(XPOWERS_BLDO2)) { - LOG_DEBUG("BLDO2: %s Voltage:%u mV \n", PMU->isPowerChannelEnable(XPOWERS_BLDO2) ? "+" : "-", + LOG_DEBUG("BLDO2: %s Voltage:%u mV ", PMU->isPowerChannelEnable(XPOWERS_BLDO2) ? "+" : "-", PMU->getPowerChannelVoltage(XPOWERS_BLDO2)); } @@ -1124,7 +1139,7 @@ LipoBatteryLevel lipoLevel; bool Power::lipoInit() { bool result = lipoLevel.runOnce(); - LOG_DEBUG("Power::lipoInit lipo sensor is %s\n", result ? "ready" : "not ready yet"); + LOG_DEBUG("Power::lipoInit lipo sensor is %s", result ? "ready" : "not ready yet"); if (!result) return false; batteryLevel = &lipoLevel; diff --git a/src/PowerFSM.cpp b/src/PowerFSM.cpp index 8bf7d3218..4c4d203c2 100644 --- a/src/PowerFSM.cpp +++ b/src/PowerFSM.cpp @@ -19,6 +19,10 @@ #include "sleep.h" #include "target_specific.h" +#if HAS_WIFI && !defined(ARCH_PORTDUINO) +#include "mesh/wifi/WiFiAPClient.h" +#endif + #ifndef SLEEP_TIME #define SLEEP_TIME 30 #endif @@ -53,16 +57,21 @@ static bool isPowered() static void sdsEnter() { - LOG_DEBUG("Enter state: SDS\n"); + LOG_DEBUG("State: SDS"); // FIXME - make sure GPS and LORA radio are off first - because we want close to zero current draw - doDeepSleep(Default::getConfiguredOrDefaultMs(config.power.sds_secs), false); + doDeepSleep(Default::getConfiguredOrDefaultMs(config.power.sds_secs), false, false); } +static void lowBattSDSEnter() +{ + LOG_DEBUG("State: Lower batt SDS"); + doDeepSleep(Default::getConfiguredOrDefaultMs(config.power.sds_secs), false, true); +} extern Power *power; static void shutdownEnter() { - LOG_DEBUG("Enter state: SHUTDOWN\n"); + LOG_DEBUG("State: SHUTDOWN"); power->shutdown(); } @@ -72,16 +81,16 @@ static uint32_t secsSlept; static void lsEnter() { - LOG_INFO("lsEnter begin, ls_secs=%u\n", config.power.ls_secs); + LOG_INFO("lsEnter begin, ls_secs=%u", config.power.ls_secs); screen->setOn(false); secsSlept = 0; // How long have we been sleeping this time - // LOG_INFO("lsEnter end\n"); + // LOG_INFO("lsEnter end"); } static void lsIdle() { - // LOG_INFO("lsIdle begin ls_secs=%u\n", getPref_ls_secs()); + // LOG_INFO("lsIdle begin ls_secs=%u", getPref_ls_secs()); #ifdef ARCH_ESP32 @@ -105,7 +114,7 @@ static void lsIdle() wakeCause2 = doLightSleep(100); // leave led on for 1ms secsSlept += sleepTime; - // LOG_INFO("sleeping, flash led!\n"); + // LOG_INFO("Sleep, flash led!"); break; case ESP_SLEEP_WAKEUP_UART: @@ -137,7 +146,7 @@ static void lsIdle() } else { // Time to stop sleeping! ledBlink.set(false); - LOG_INFO("Reached ls_secs, servicing loop()\n"); + LOG_INFO("Reached ls_secs, service loop()"); powerFSM.trigger(EVENT_WAKE_TIMER); } #endif @@ -145,12 +154,12 @@ static void lsIdle() static void lsExit() { - LOG_INFO("Exit state: LS\n"); + LOG_INFO("Exit state: LS"); } static void nbEnter() { - LOG_DEBUG("Enter state: NB\n"); + LOG_DEBUG("State: NB"); screen->setOn(false); #ifdef ARCH_ESP32 // Only ESP32 should turn off bluetooth @@ -168,7 +177,7 @@ static void darkEnter() static void serialEnter() { - LOG_DEBUG("Enter state: SERIAL\n"); + LOG_DEBUG("State: SERIAL"); setBluetoothEnable(false); screen->setOn(true); screen->print("Serial connected\n"); @@ -183,10 +192,10 @@ static void serialExit() static void powerEnter() { - // LOG_DEBUG("Enter state: POWER\n"); + // LOG_DEBUG("State: POWER"); if (!isPowered()) { - // If we got here, we are in the wrong state - we should be in powered, let that state ahndle things - LOG_INFO("Loss of power in Powered\n"); + // If we got here, we are in the wrong state - we should be in powered, let that state handle things + LOG_INFO("Loss of power in Powered"); powerFSM.trigger(EVENT_POWER_DISCONNECTED); } else { screen->setOn(true); @@ -205,7 +214,7 @@ static void powerIdle() { if (!isPowered()) { // If we got here, we are in the wrong state - LOG_INFO("Loss of power in Powered\n"); + LOG_INFO("Loss of power in Powered"); powerFSM.trigger(EVENT_POWER_DISCONNECTED); } } @@ -222,7 +231,7 @@ static void powerExit() static void onEnter() { - LOG_DEBUG("Enter state: ON\n"); + LOG_DEBUG("State: ON"); screen->setOn(true); setBluetoothEnable(true); } @@ -230,7 +239,7 @@ static void onEnter() static void onIdle() { if (isPowered()) { - // If we got here, we are in the wrong state - we should be in powered, let that state ahndle things + // If we got here, we are in the wrong state - we should be in powered, let that state handle things powerFSM.trigger(EVENT_POWER_CONNECTED); } } @@ -242,11 +251,12 @@ static void screenPress() static void bootEnter() { - LOG_DEBUG("Enter state: BOOT\n"); + LOG_DEBUG("State: BOOT"); } State stateSHUTDOWN(shutdownEnter, NULL, NULL, "SHUTDOWN"); State stateSDS(sdsEnter, NULL, NULL, "SDS"); +State stateLowBattSDS(lowBattSDSEnter, NULL, NULL, "SDS"); State stateLS(lsEnter, lsIdle, lsExit, "LS"); State stateNB(nbEnter, NULL, NULL, "NB"); State stateDARK(darkEnter, NULL, NULL, "DARK"); @@ -264,7 +274,7 @@ void PowerFSM_setup() config.device.role == meshtastic_Config_DeviceConfig_Role_SENSOR; bool hasPower = isPowered(); - LOG_INFO("PowerFSM init, USB power=%d\n", hasPower ? 1 : 0); + LOG_INFO("PowerFSM init, USB power=%d", hasPower ? 1 : 0); powerFSM.add_timed_transition(&stateBOOT, hasPower ? &statePOWER : &stateON, 3 * 1000, NULL, "boot timeout"); // wake timer expired or a packet arrived @@ -291,12 +301,12 @@ void PowerFSM_setup() "Press"); // Allow button to work while in serial API // Handle critically low power battery by forcing deep sleep - powerFSM.add_transition(&stateBOOT, &stateSDS, EVENT_LOW_BATTERY, NULL, "LowBat"); - powerFSM.add_transition(&stateLS, &stateSDS, EVENT_LOW_BATTERY, NULL, "LowBat"); - powerFSM.add_transition(&stateNB, &stateSDS, EVENT_LOW_BATTERY, NULL, "LowBat"); - powerFSM.add_transition(&stateDARK, &stateSDS, EVENT_LOW_BATTERY, NULL, "LowBat"); - powerFSM.add_transition(&stateON, &stateSDS, EVENT_LOW_BATTERY, NULL, "LowBat"); - powerFSM.add_transition(&stateSERIAL, &stateSDS, EVENT_LOW_BATTERY, NULL, "LowBat"); + powerFSM.add_transition(&stateBOOT, &stateLowBattSDS, EVENT_LOW_BATTERY, NULL, "LowBat"); + powerFSM.add_transition(&stateLS, &stateLowBattSDS, EVENT_LOW_BATTERY, NULL, "LowBat"); + powerFSM.add_transition(&stateNB, &stateLowBattSDS, EVENT_LOW_BATTERY, NULL, "LowBat"); + powerFSM.add_transition(&stateDARK, &stateLowBattSDS, EVENT_LOW_BATTERY, NULL, "LowBat"); + powerFSM.add_transition(&stateON, &stateLowBattSDS, EVENT_LOW_BATTERY, NULL, "LowBat"); + powerFSM.add_transition(&stateSERIAL, &stateLowBattSDS, EVENT_LOW_BATTERY, NULL, "LowBat"); // Handle being told to power off powerFSM.add_transition(&stateBOOT, &stateSHUTDOWN, EVENT_SHUTDOWN, NULL, "Shutdown"); @@ -371,9 +381,9 @@ void PowerFSM_setup() // We never enter light-sleep or NB states on NRF52 (because the CPU uses so little power normally) #ifdef ARCH_ESP32 // See: https://github.com/meshtastic/firmware/issues/1071 - // Don't add power saving transitions if we are a power saving tracker or sensor. Sleep will be initiatiated through the - // modules - if ((isRouter || config.power.is_power_saving) && !isTrackerOrSensor) { + // Don't add power saving transitions if we are a power saving tracker or sensor or have Wifi enabled. Sleep will be initiated + // through the modules + if ((isRouter || config.power.is_power_saving) && !isWifiAvailable() && !isTrackerOrSensor) { powerFSM.add_timed_transition(&stateNB, &stateLS, Default::getConfiguredOrDefaultMs(config.power.min_wake_secs, default_min_wake_secs), NULL, "Min wake timeout"); diff --git a/src/PowerMon.cpp b/src/PowerMon.cpp index 16909262d..38740b6ae 100644 --- a/src/PowerMon.cpp +++ b/src/PowerMon.cpp @@ -35,7 +35,7 @@ void PowerMon::emitLog(const char *reason) { #ifdef USE_POWERMON // The nrf52 printf doesn't understand 64 bit ints, so if we ever reach that point this function will need to change. - LOG_INFO("S:PM:0x%08lx,%s\n", (uint32_t)states, reason); + LOG_INFO("S:PM:0x%08lx,%s", (uint32_t)states, reason); #endif } diff --git a/src/PowerStatus.h b/src/PowerStatus.h index 592a03328..fe4543e08 100644 --- a/src/PowerStatus.h +++ b/src/PowerStatus.h @@ -91,7 +91,7 @@ class PowerStatus : public Status isCharging = newStatus->isCharging; } if (isDirty) { - // LOG_DEBUG("Battery %dmV %d%%\n", batteryVoltageMv, batteryChargePercent); + // LOG_DEBUG("Battery %dmV %d%%", batteryVoltageMv, batteryChargePercent); onNewStatus.notifyObservers(this); } return 0; diff --git a/src/RedirectablePrint.cpp b/src/RedirectablePrint.cpp index 6eb6f8319..57f53019d 100644 --- a/src/RedirectablePrint.cpp +++ b/src/RedirectablePrint.cpp @@ -98,81 +98,75 @@ void RedirectablePrint::log_to_serial(const char *logLevel, const char *format, { size_t r = 0; - // Cope with 0 len format strings, but look for new line terminator - bool hasNewline = *format && format[strlen(format) - 1] == '\n'; #ifdef ARCH_PORTDUINO bool color = !settingsMap[ascii_logs]; #else bool color = true; #endif - // If we are the first message on a report, include the header - if (!isContinuationMessage) { + // include the header + if (color) { + if (strcmp(logLevel, MESHTASTIC_LOG_LEVEL_DEBUG) == 0) + Print::write("\u001b[34m", 6); + if (strcmp(logLevel, MESHTASTIC_LOG_LEVEL_INFO) == 0) + Print::write("\u001b[32m", 6); + if (strcmp(logLevel, MESHTASTIC_LOG_LEVEL_WARN) == 0) + Print::write("\u001b[33m", 6); + if (strcmp(logLevel, MESHTASTIC_LOG_LEVEL_ERROR) == 0) + Print::write("\u001b[31m", 6); + if (strcmp(logLevel, MESHTASTIC_LOG_LEVEL_TRACE) == 0) + Print::write("\u001b[35m", 6); + } + + uint32_t rtc_sec = getValidTime(RTCQuality::RTCQualityDevice, true); // display local time on logfile + if (rtc_sec > 0) { + long hms = rtc_sec % SEC_PER_DAY; + // hms += tz.tz_dsttime * SEC_PER_HOUR; + // hms -= tz.tz_minuteswest * SEC_PER_MIN; + // mod `hms` to ensure in positive range of [0...SEC_PER_DAY) + hms = (hms + SEC_PER_DAY) % SEC_PER_DAY; + + // Tear apart hms into h:m:s + int hour = hms / SEC_PER_HOUR; + int min = (hms % SEC_PER_HOUR) / SEC_PER_MIN; + int sec = (hms % SEC_PER_HOUR) % SEC_PER_MIN; // or hms % SEC_PER_MIN +#ifdef ARCH_PORTDUINO + ::printf("%s ", logLevel); if (color) { - if (strcmp(logLevel, MESHTASTIC_LOG_LEVEL_DEBUG) == 0) - Print::write("\u001b[34m", 6); - if (strcmp(logLevel, MESHTASTIC_LOG_LEVEL_INFO) == 0) - Print::write("\u001b[32m", 6); - if (strcmp(logLevel, MESHTASTIC_LOG_LEVEL_WARN) == 0) - Print::write("\u001b[33m", 6); - if (strcmp(logLevel, MESHTASTIC_LOG_LEVEL_ERROR) == 0) - Print::write("\u001b[31m", 6); - if (strcmp(logLevel, MESHTASTIC_LOG_LEVEL_TRACE) == 0) - Print::write("\u001b[35m", 6); + ::printf("\u001b[0m"); } - - uint32_t rtc_sec = getValidTime(RTCQuality::RTCQualityDevice, true); // display local time on logfile - if (rtc_sec > 0) { - long hms = rtc_sec % SEC_PER_DAY; - // hms += tz.tz_dsttime * SEC_PER_HOUR; - // hms -= tz.tz_minuteswest * SEC_PER_MIN; - // mod `hms` to ensure in positive range of [0...SEC_PER_DAY) - hms = (hms + SEC_PER_DAY) % SEC_PER_DAY; - - // Tear apart hms into h:m:s - int hour = hms / SEC_PER_HOUR; - int min = (hms % SEC_PER_HOUR) / SEC_PER_MIN; - int sec = (hms % SEC_PER_HOUR) % SEC_PER_MIN; // or hms % SEC_PER_MIN -#ifdef ARCH_PORTDUINO - ::printf("%s ", logLevel); - if (color) { - ::printf("\u001b[0m"); - } - ::printf("| %02d:%02d:%02d %u ", hour, min, sec, millis() / 1000); + ::printf("| %02d:%02d:%02d %u ", hour, min, sec, millis() / 1000); #else - printf("%s ", logLevel); - if (color) { - printf("\u001b[0m"); - } - printf("| %02d:%02d:%02d %u ", hour, min, sec, millis() / 1000); + printf("%s ", logLevel); + if (color) { + printf("\u001b[0m"); + } + printf("| %02d:%02d:%02d %u ", hour, min, sec, millis() / 1000); #endif - } else { + } else { #ifdef ARCH_PORTDUINO - ::printf("%s ", logLevel); - if (color) { - ::printf("\u001b[0m"); - } - ::printf("| ??:??:?? %u ", millis() / 1000); + ::printf("%s ", logLevel); + if (color) { + ::printf("\u001b[0m"); + } + ::printf("| ??:??:?? %u ", millis() / 1000); #else - printf("%s ", logLevel); - if (color) { - printf("\u001b[0m"); - } - printf("| ??:??:?? %u ", millis() / 1000); + printf("%s ", logLevel); + if (color) { + printf("\u001b[0m"); + } + printf("| ??:??:?? %u ", millis() / 1000); #endif - } - auto thread = concurrency::OSThread::currentThread; - if (thread) { - print("["); - // printf("%p ", thread); - // assert(thread->ThreadName.length()); - print(thread->ThreadName); - print("] "); - } + } + auto thread = concurrency::OSThread::currentThread; + if (thread) { + print("["); + // printf("%p ", thread); + // assert(thread->ThreadName.length()); + print(thread->ThreadName); + print("] "); } r += vprintf(logLevel, format, arg); - - isContinuationMessage = !hasNewline; } void RedirectablePrint::log_to_syslog(const char *logLevel, const char *format, va_list arg) @@ -283,6 +277,14 @@ meshtastic_LogRecord_Level RedirectablePrint::getLogLevel(const char *logLevel) void RedirectablePrint::log(const char *logLevel, const char *format, ...) { + + // append \n to format + size_t len = strlen(format); + char *newFormat = new char[len + 2]; + strcpy(newFormat, format); + newFormat[len] = '\n'; + newFormat[len + 1] = '\0'; + #if ARCH_PORTDUINO // level trace is special, two possible ways to handle it. if (strcmp(logLevel, MESHTASTIC_LOG_LEVEL_TRACE) == 0) { @@ -295,17 +297,24 @@ void RedirectablePrint::log(const char *logLevel, const char *format, ...) } va_end(arg); } - if (settingsMap[logoutputlevel] < level_trace && strcmp(logLevel, MESHTASTIC_LOG_LEVEL_TRACE) == 0) + if (settingsMap[logoutputlevel] < level_trace && strcmp(logLevel, MESHTASTIC_LOG_LEVEL_TRACE) == 0) { + delete[] newFormat; return; + } } - if (settingsMap[logoutputlevel] < level_debug && strcmp(logLevel, MESHTASTIC_LOG_LEVEL_DEBUG) == 0) + if (settingsMap[logoutputlevel] < level_debug && strcmp(logLevel, MESHTASTIC_LOG_LEVEL_DEBUG) == 0) { + delete[] newFormat; return; - else if (settingsMap[logoutputlevel] < level_info && strcmp(logLevel, MESHTASTIC_LOG_LEVEL_INFO) == 0) + } else if (settingsMap[logoutputlevel] < level_info && strcmp(logLevel, MESHTASTIC_LOG_LEVEL_INFO) == 0) { + delete[] newFormat; return; - else if (settingsMap[logoutputlevel] < level_warn && strcmp(logLevel, MESHTASTIC_LOG_LEVEL_WARN) == 0) + } else if (settingsMap[logoutputlevel] < level_warn && strcmp(logLevel, MESHTASTIC_LOG_LEVEL_WARN) == 0) { + delete[] newFormat; return; + } #endif if (moduleConfig.serial.override_console_serial_port && strcmp(logLevel, MESHTASTIC_LOG_LEVEL_DEBUG) == 0) { + delete[] newFormat; return; } @@ -319,9 +328,9 @@ void RedirectablePrint::log(const char *logLevel, const char *format, ...) va_list arg; va_start(arg, format); - log_to_serial(logLevel, format, arg); - log_to_syslog(logLevel, format, arg); - log_to_ble(logLevel, format, arg); + log_to_serial(logLevel, newFormat, arg); + log_to_syslog(logLevel, newFormat, arg); + log_to_ble(logLevel, newFormat, arg); va_end(arg); #ifdef HAS_FREE_RTOS @@ -331,17 +340,18 @@ void RedirectablePrint::log(const char *logLevel, const char *format, ...) #endif } + delete[] newFormat; return; } void RedirectablePrint::hexDump(const char *logLevel, unsigned char *buf, uint16_t len) { const char alphabet[17] = "0123456789abcdef"; - log(logLevel, " +------------------------------------------------+ +----------------+\n"); - log(logLevel, " |.0 .1 .2 .3 .4 .5 .6 .7 .8 .9 .a .b .c .d .e .f | | ASCII |\n"); + log(logLevel, " +------------------------------------------------+ +----------------+"); + log(logLevel, " |.0 .1 .2 .3 .4 .5 .6 .7 .8 .9 .a .b .c .d .e .f | | ASCII |"); for (uint16_t i = 0; i < len; i += 16) { if (i % 128 == 0) - log(logLevel, " +------------------------------------------------+ +----------------+\n"); + log(logLevel, " +------------------------------------------------+ +----------------+"); char s[] = "| | | |\n"; uint8_t ix = 1, iy = 52; for (uint8_t j = 0; j < 16; j++) { @@ -363,7 +373,7 @@ void RedirectablePrint::hexDump(const char *logLevel, unsigned char *buf, uint16 log(logLevel, "."); log(logLevel, s); } - log(logLevel, " +------------------------------------------------+ +----------------+\n"); + log(logLevel, " +------------------------------------------------+ +----------------+"); } std::string RedirectablePrint::mt_sprintf(const std::string fmt_str, ...) diff --git a/src/RedirectablePrint.h b/src/RedirectablePrint.h index 23ae3c44d..45b62b7af 100644 --- a/src/RedirectablePrint.h +++ b/src/RedirectablePrint.h @@ -15,9 +15,6 @@ class RedirectablePrint : public Print { Print *dest; - /// Used to allow multiple logDebug messages to appear on a single log line - bool isContinuationMessage = false; - #ifdef HAS_FREE_RTOS SemaphoreHandle_t inDebugPrint = nullptr; StaticSemaphore_t _MutexStorageSpace; @@ -54,9 +51,9 @@ class RedirectablePrint : public Print protected: /// Subclasses can override if they need to change how we format over the serial port virtual void log_to_serial(const char *logLevel, const char *format, va_list arg); + meshtastic_LogRecord_Level getLogLevel(const char *logLevel); private: void log_to_syslog(const char *logLevel, const char *format, va_list arg); void log_to_ble(const char *logLevel, const char *format, va_list arg); - meshtastic_LogRecord_Level getLogLevel(const char *logLevel); }; \ No newline at end of file diff --git a/src/SafeFile.cpp b/src/SafeFile.cpp index c17d422bd..c76ff8054 100644 --- a/src/SafeFile.cpp +++ b/src/SafeFile.cpp @@ -59,14 +59,14 @@ bool SafeFile::close() // brief window of risk here ;-) if (fullAtomic && FSCom.exists(filename.c_str()) && !FSCom.remove(filename.c_str())) { - LOG_ERROR("Can't remove old pref file\n"); + LOG_ERROR("Can't remove old pref file"); return false; } String filenameTmp = filename; filenameTmp += ".tmp"; if (!renameFile(filenameTmp.c_str(), filename.c_str())) { - LOG_ERROR("Error: can't rename new pref file\n"); + LOG_ERROR("Error: can't rename new pref file"); return false; } @@ -83,7 +83,7 @@ bool SafeFile::testReadback() filenameTmp += ".tmp"; auto f2 = FSCom.open(filenameTmp.c_str(), FILE_O_READ); if (!f2) { - LOG_ERROR("Can't open tmp file for readback\n"); + LOG_ERROR("Can't open tmp file for readback"); return false; } @@ -95,7 +95,7 @@ bool SafeFile::testReadback() f2.close(); if (test_hash != hash) { - LOG_ERROR("Readback failed hash mismatch\n"); + LOG_ERROR("Readback failed hash mismatch"); return false; } diff --git a/src/SerialConsole.cpp b/src/SerialConsole.cpp index fe6ccdefe..68c41980d 100644 --- a/src/SerialConsole.cpp +++ b/src/SerialConsole.cpp @@ -99,25 +99,7 @@ bool SerialConsole::handleToRadio(const uint8_t *buf, size_t len) void SerialConsole::log_to_serial(const char *logLevel, const char *format, va_list arg) { if (usingProtobufs && config.security.debug_log_api_enabled) { - meshtastic_LogRecord_Level ll = meshtastic_LogRecord_Level_UNSET; // default to unset - switch (logLevel[0]) { - case 'D': - ll = meshtastic_LogRecord_Level_DEBUG; - break; - case 'I': - ll = meshtastic_LogRecord_Level_INFO; - break; - case 'W': - ll = meshtastic_LogRecord_Level_WARNING; - break; - case 'E': - ll = meshtastic_LogRecord_Level_ERROR; - break; - case 'C': - ll = meshtastic_LogRecord_Level_CRITICAL; - break; - } - + meshtastic_LogRecord_Level ll = RedirectablePrint::getLogLevel(logLevel); auto thread = concurrency::OSThread::currentThread; emitLogRecord(ll, thread ? thread->ThreadName.c_str() : "", format, arg); } else diff --git a/src/airtime.cpp b/src/airtime.cpp index fcda05468..a7736d667 100644 --- a/src/airtime.cpp +++ b/src/airtime.cpp @@ -13,17 +13,17 @@ void AirTime::logAirtime(reportTypes reportType, uint32_t airtime_ms) { if (reportType == TX_LOG) { - LOG_DEBUG("Packet transmitted : %ums\n", airtime_ms); + LOG_DEBUG("Packet TX: %ums", airtime_ms); this->airtimes.periodTX[0] = this->airtimes.periodTX[0] + airtime_ms; air_period_tx[0] = air_period_tx[0] + airtime_ms; this->utilizationTX[this->getPeriodUtilHour()] = this->utilizationTX[this->getPeriodUtilHour()] + airtime_ms; } else if (reportType == RX_LOG) { - LOG_DEBUG("Packet received : %ums\n", airtime_ms); + LOG_DEBUG("Packet RX: %ums", airtime_ms); this->airtimes.periodRX[0] = this->airtimes.periodRX[0] + airtime_ms; air_period_rx[0] = air_period_rx[0] + airtime_ms; } else if (reportType == RX_ALL_LOG) { - LOG_DEBUG("Packet received (noise?) : %ums\n", airtime_ms); + LOG_DEBUG("Packet RX (noise?) : %ums", airtime_ms); this->airtimes.periodRX_ALL[0] = this->airtimes.periodRX_ALL[0] + airtime_ms; } @@ -50,7 +50,7 @@ void AirTime::airtimeRotatePeriod() { if (this->airtimes.lastPeriodIndex != this->currentPeriodIndex()) { - LOG_DEBUG("Rotating airtimes to a new period = %u\n", this->currentPeriodIndex()); + LOG_DEBUG("Rotate airtimes to a new period = %u", this->currentPeriodIndex()); for (int i = PERIODS_TO_LOG - 2; i >= 0; --i) { this->airtimes.periodTX[i + 1] = this->airtimes.periodTX[i]; @@ -105,7 +105,6 @@ float AirTime::channelUtilizationPercent() uint32_t sum = 0; for (uint32_t i = 0; i < CHANNEL_UTILIZATION_PERIODS; i++) { sum += this->channelUtilization[i]; - // LOG_DEBUG("ChanUtilArray %u %u\n", i, this->channelUtilization[i]); } return (float(sum) / float(CHANNEL_UTILIZATION_PERIODS * 10 * 1000)) * 100; @@ -127,7 +126,7 @@ bool AirTime::isTxAllowedChannelUtil(bool polite) if (channelUtilizationPercent() < percentage) { return true; } else { - LOG_WARN("Channel utilization is >%d percent. Skipping this opportunity to send.\n", percentage); + LOG_WARN("Ch. util >%d%%. Skip send", percentage); return false; } } @@ -138,8 +137,7 @@ bool AirTime::isTxAllowedAirUtil() if (utilizationTXPercent() < myRegion->dutyCycle * polite_duty_cycle_percent / 100) { return true; } else { - LOG_WARN("Tx air utilization is >%f percent. Skipping this opportunity to send.\n", - myRegion->dutyCycle * polite_duty_cycle_percent / 100); + LOG_WARN("TX air util. >%f%%. Skip send", myRegion->dutyCycle * polite_duty_cycle_percent / 100); return false; } } @@ -208,14 +206,5 @@ int32_t AirTime::runOnce() this->utilizationTX[utilPeriodTX] = 0; } } - /* - LOG_DEBUG("utilPeriodTX %d TX Airtime %3.2f%\n", utilPeriodTX, airTime->utilizationTXPercent()); - for (uint32_t i = 0; i < MINUTES_IN_HOUR; i++) { - LOG_DEBUG( - "%d,", this->utilizationTX[i] - ); - } - LOG_DEBUG("\n"); - */ return (1000 * 1); -} \ No newline at end of file +} diff --git a/src/buzz/buzz.cpp b/src/buzz/buzz.cpp index e42a9c203..8db9602bc 100644 --- a/src/buzz/buzz.cpp +++ b/src/buzz/buzz.cpp @@ -55,6 +55,18 @@ void playBeep() playTones(melody, sizeof(melody) / sizeof(ToneDuration)); } +void playGPSEnableBeep() +{ + ToneDuration melody[] = {{NOTE_C3, DURATION_1_8}, {NOTE_FS3, DURATION_1_4}, {NOTE_CS4, DURATION_1_4}}; + playTones(melody, sizeof(melody) / sizeof(ToneDuration)); +} + +void playGPSDisableBeep() +{ + ToneDuration melody[] = {{NOTE_CS4, DURATION_1_8}, {NOTE_FS3, DURATION_1_4}, {NOTE_C3, DURATION_1_4}}; + playTones(melody, sizeof(melody) / sizeof(ToneDuration)); +} + void playStartMelody() { ToneDuration melody[] = {{NOTE_FS3, DURATION_1_8}, {NOTE_AS3, DURATION_1_8}, {NOTE_CS4, DURATION_1_4}}; diff --git a/src/buzz/buzz.h b/src/buzz/buzz.h index 3883bd057..c52c3020c 100644 --- a/src/buzz/buzz.h +++ b/src/buzz/buzz.h @@ -3,3 +3,5 @@ void playBeep(); void playStartMelody(); void playShutdownMelody(); +void playGPSEnableBeep(); +void playGPSDisableBeep(); \ No newline at end of file diff --git a/src/concurrency/InterruptableDelay.cpp b/src/concurrency/InterruptableDelay.cpp index b9606e23a..8bc85436b 100644 --- a/src/concurrency/InterruptableDelay.cpp +++ b/src/concurrency/InterruptableDelay.cpp @@ -18,7 +18,7 @@ bool InterruptableDelay::delay(uint32_t msec) // sem take will return false if we timed out (i.e. were not interrupted) bool r = semaphore.take(msec); - // LOG_DEBUG("interrupt=%d\n", r); + // LOG_DEBUG("interrupt=%d", r); return !r; } diff --git a/src/concurrency/NotifiedWorkerThread.cpp b/src/concurrency/NotifiedWorkerThread.cpp index 271e3e60d..0e4e31d9b 100644 --- a/src/concurrency/NotifiedWorkerThread.cpp +++ b/src/concurrency/NotifiedWorkerThread.cpp @@ -32,12 +32,12 @@ IRAM_ATTR bool NotifiedWorkerThread::notifyCommon(uint32_t v, bool overwrite) notification = v; if (debugNotification) { - LOG_DEBUG("setting notification %d\n", v); + LOG_DEBUG("Set notification %d", v); } return true; } else { if (debugNotification) { - LOG_DEBUG("dropping notification %d\n", v); + LOG_DEBUG("Drop notification %d", v); } return false; } @@ -67,7 +67,7 @@ bool NotifiedWorkerThread::notifyLater(uint32_t delay, uint32_t v, bool overwrit if (didIt) { // If we didn't already have something queued, override the delay to be larger setIntervalFromNow(delay); // a new version of setInterval relative to the current time if (debugNotification) { - LOG_DEBUG("delaying notification %u\n", delay); + LOG_DEBUG("Delay notification %u", delay); } } diff --git a/src/concurrency/OSThread.cpp b/src/concurrency/OSThread.cpp index f23cbe1dc..d9bb901b2 100644 --- a/src/concurrency/OSThread.cpp +++ b/src/concurrency/OSThread.cpp @@ -62,15 +62,15 @@ bool OSThread::shouldRun(unsigned long time) bool r = Thread::shouldRun(time); if (showRun && r) { - LOG_DEBUG("Thread %s: run\n", ThreadName.c_str()); + LOG_DEBUG("Thread %s: run", ThreadName.c_str()); } if (showWaiting && enabled && !r) { - LOG_DEBUG("Thread %s: wait %lu\n", ThreadName.c_str(), interval); + LOG_DEBUG("Thread %s: wait %lu", ThreadName.c_str(), interval); } if (showDisabled && !enabled) { - LOG_DEBUG("Thread %s: disabled\n", ThreadName.c_str()); + LOG_DEBUG("Thread %s: disabled", ThreadName.c_str()); } return r; @@ -86,9 +86,9 @@ void OSThread::run() #ifdef DEBUG_HEAP auto newHeap = memGet.getFreeHeap(); if (newHeap < heap) - LOG_DEBUG("------ Thread %s leaked heap %d -> %d (%d) ------\n", ThreadName.c_str(), heap, newHeap, newHeap - heap); + LOG_DEBUG("------ Thread %s leaked heap %d -> %d (%d) ------", ThreadName.c_str(), heap, newHeap, newHeap - heap); if (heap < newHeap) - LOG_DEBUG("++++++ Thread %s freed heap %d -> %d (%d) ++++++\n", ThreadName.c_str(), heap, newHeap, newHeap - heap); + LOG_DEBUG("++++++ Thread %s freed heap %d -> %d (%d) ++++++", ThreadName.c_str(), heap, newHeap, newHeap - heap); #endif runned(); diff --git a/src/configuration.h b/src/configuration.h index 10a4e0a99..b5727508d 100644 --- a/src/configuration.h +++ b/src/configuration.h @@ -114,6 +114,7 @@ along with this program. If not, see . #define CARDKB_ADDR 0x5F #define TDECK_KB_ADDR 0x55 #define BBQ10_KB_ADDR 0x1F +#define MPR121_KB_ADDR 0x5A // ----------------------------------------------------------------------------- // SENSOR @@ -135,6 +136,7 @@ along with this program. If not, see . #define LPS22HB_ADDR_ALT 0x5D #define SHT31_4x_ADDR 0x44 #define PMSA0031_ADDR 0x12 +#define QMA6100P_ADDR 0x12 #define AHT10_ADDR 0x38 #define RCWL9620_ADDR 0x57 #define VEML7700_ADDR 0x10 @@ -146,13 +148,15 @@ along with this program. If not, see . #define NAU7802_ADDR 0x2A #define MAX30102_ADDR 0x57 #define MLX90614_ADDR_DEF 0x5A +#define CGRADSENS_ADDR 0x66 // ----------------------------------------------------------------------------- // ACCELEROMETER // ----------------------------------------------------------------------------- #define MPU6050_ADDR 0x68 -#define STK8BXX_ADR 0x18 -#define LIS3DH_ADR 0x18 +#define STK8BXX_ADDR 0x18 +#define LIS3DH_ADDR 0x18 +#define LIS3DH_ADDR_ALT 0x19 #define BMA423_ADDR 0x19 #define LSM6DS3_ADDR 0x6A #define BMX160_ADDR 0x69 @@ -167,7 +171,6 @@ along with this program. If not, see . // ----------------------------------------------------------------------------- // Security // ----------------------------------------------------------------------------- -#define ATECC608B_ADDR 0x35 // ----------------------------------------------------------------------------- // IO Expander @@ -205,6 +208,9 @@ along with this program. If not, see . #ifndef GPS_BAUDRATE #define GPS_BAUDRATE 9600 +#define GPS_BAUDRATE_FIXED 0 +#else +#define GPS_BAUDRATE_FIXED 1 #endif /* Step #2: follow with defines common to the architecture; @@ -355,4 +361,4 @@ along with this program. If not, see . #endif #include "DebugConfiguration.h" -#include "RF95Configuration.h" +#include "RF95Configuration.h" \ No newline at end of file diff --git a/src/detect/ScanI2C.cpp b/src/detect/ScanI2C.cpp index a9d70edaa..4caa0f730 100644 --- a/src/detect/ScanI2C.cpp +++ b/src/detect/ScanI2C.cpp @@ -31,14 +31,14 @@ ScanI2C::FoundDevice ScanI2C::firstRTC() const ScanI2C::FoundDevice ScanI2C::firstKeyboard() const { - ScanI2C::DeviceType types[] = {CARDKB, TDECKKB, BBQ10KB, RAK14004}; - return firstOfOrNONE(4, types); + ScanI2C::DeviceType types[] = {CARDKB, TDECKKB, BBQ10KB, RAK14004, MPR121KB}; + return firstOfOrNONE(5, types); } ScanI2C::FoundDevice ScanI2C::firstAccelerometer() const { - ScanI2C::DeviceType types[] = {MPU6050, LIS3DH, BMA423, LSM6DS3, BMX160, STK8BAXX, ICM20948}; - return firstOfOrNONE(7, types); + ScanI2C::DeviceType types[] = {MPU6050, LIS3DH, BMA423, LSM6DS3, BMX160, STK8BAXX, ICM20948, QMA6100P}; + return firstOfOrNONE(8, types); } ScanI2C::FoundDevice ScanI2C::find(ScanI2C::DeviceType) const diff --git a/src/detect/ScanI2C.h b/src/detect/ScanI2C.h index 07db3fd57..7fe3aac89 100644 --- a/src/detect/ScanI2C.h +++ b/src/detect/ScanI2C.h @@ -12,7 +12,6 @@ class ScanI2C SCREEN_SH1106, SCREEN_UNKNOWN, // has the same address as the two above but does not respond to the same commands SCREEN_ST7567, - ATECC608B, RTC_RV3028, RTC_PCF8563, CARDKB, @@ -39,6 +38,7 @@ class ScanI2C QMC5883L, HMC5883L, PMSA0031, + QMA6100P, MPU6050, LIS3DH, BMA423, @@ -61,7 +61,9 @@ class ScanI2C STK8BAXX, ICM20948, MAX30102, - TPS65233 + TPS65233, + MPR121KB, + CGRADSENS } DeviceType; // typedef uint8_t DeviceAddress; diff --git a/src/detect/ScanI2CTwoWire.cpp b/src/detect/ScanI2CTwoWire.cpp index af94290d2..d29e5be8e 100644 --- a/src/detect/ScanI2CTwoWire.cpp +++ b/src/detect/ScanI2CTwoWire.cpp @@ -7,7 +7,7 @@ #include "linux/LinuxHardwareI2C.h" #endif #if !defined(ARCH_PORTDUINO) && !defined(ARCH_STM32WL) -#include "main.h" // atecc +#include "meshUtils.h" // vformat #endif // AXP192 and AXP2101 have the same device address, we just need to identify it in Power.cpp @@ -71,52 +71,18 @@ ScanI2C::DeviceType ScanI2CTwoWire::probeOLED(ScanI2C::DeviceAddress addr) const r &= 0x0f; if (r == 0x08 || r == 0x00) { - LOG_INFO("sh1106 display found\n"); + logFoundDevice("SH1106", (uint8_t)addr.address); o_probe = SCREEN_SH1106; // SH1106 } else if (r == 0x03 || r == 0x04 || r == 0x06 || r == 0x07) { - LOG_INFO("ssd1306 display found\n"); + logFoundDevice("SSD1306", (uint8_t)addr.address); o_probe = SCREEN_SSD1306; // SSD1306 } c++; } while ((r != r_prev) && (c < 4)); - LOG_DEBUG("0x%x subtype probed in %i tries \n", r, c); + LOG_DEBUG("0x%x subtype probed in %i tries ", r, c); return o_probe; } -void ScanI2CTwoWire::printATECCInfo() const -{ -#if !defined(ARCH_PORTDUINO) && !defined(ARCH_STM32WL) - atecc.readConfigZone(false); - - LOG_DEBUG("ATECC608B Serial Number: "); - for (int i = 0; i < 9; i++) { - LOG_DEBUG("%02x", atecc.serialNumber[i]); - } - - LOG_DEBUG(", Rev Number: "); - for (int i = 0; i < 4; i++) { - LOG_DEBUG("%02x", atecc.revisionNumber[i]); - } - LOG_DEBUG("\n"); - - LOG_DEBUG("ATECC608B Config %s", atecc.configLockStatus ? "Locked" : "Unlocked"); - LOG_DEBUG(", Data %s", atecc.dataOTPLockStatus ? "Locked" : "Unlocked"); - LOG_DEBUG(", Slot 0 %s\n", atecc.slot0LockStatus ? "Locked" : "Unlocked"); - - if (atecc.configLockStatus && atecc.dataOTPLockStatus && atecc.slot0LockStatus) { - if (atecc.generatePublicKey() == false) { - LOG_DEBUG("ATECC608B Error generating public key\n"); - } else { - LOG_DEBUG("ATECC608B Public Key: "); - for (int i = 0; i < 64; i++) { - LOG_DEBUG("%02x", atecc.publicKey64Bytes[i]); - } - LOG_DEBUG("\n"); - } - } -#endif -} - uint16_t ScanI2CTwoWire::getRegisterValue(const ScanI2CTwoWire::RegisterLocation ®isterLocation, ScanI2CTwoWire::ResponseWidth responseWidth) const { @@ -128,7 +94,6 @@ uint16_t ScanI2CTwoWire::getRegisterValue(const ScanI2CTwoWire::RegisterLocation i2cBus->endTransmission(); delay(20); i2cBus->requestFrom(registerLocation.i2cAddress.address, responseWidth); - LOG_DEBUG("Wire.available() = %d\n", i2cBus->available()); if (i2cBus->available() == 2) { // Read MSB, then LSB value = (uint16_t)i2cBus->read() << 8; @@ -141,7 +106,7 @@ uint16_t ScanI2CTwoWire::getRegisterValue(const ScanI2CTwoWire::RegisterLocation #define SCAN_SIMPLE_CASE(ADDR, T, ...) \ case ADDR: \ - LOG_INFO(__VA_ARGS__); \ + logFoundDevice(__VA_ARGS__); \ type = T; \ break; @@ -149,7 +114,7 @@ void ScanI2CTwoWire::scanPort(I2CPort port, uint8_t *address, uint8_t asize) { concurrency::LockGuard guard((concurrency::Lock *)&lock); - LOG_DEBUG("Scanning for I2C devices on port %d\n", port); + LOG_DEBUG("Scan for I2C devices on port %d", port); uint8_t err; @@ -172,11 +137,20 @@ void ScanI2CTwoWire::scanPort(I2CPort port, uint8_t *address, uint8_t asize) } #endif - for (addr.address = 1; addr.address < 127; addr.address++) { + // We only need to scan 112 addresses, the rest is reserved for special purposes + // 0x00 General Call + // 0x01 CBUS addresses + // 0x02 Reserved for different bus formats + // 0x03 Reserved for future purposes + // 0x04-0x07 High Speed Master Code + // 0x78-0x7B 10-bit slave addressing + // 0x7C-0x7F Reserved for future purposes + + for (addr.address = 8; addr.address < 120; addr.address++) { if (asize != 0) { - if (!in_array(address, asize, addr.address)) + if (!in_array(address, asize, (uint8_t)addr.address)) continue; - LOG_DEBUG("Scanning address 0x%x\n", addr.address); + LOG_DEBUG("Scan address 0x%x", (uint8_t)addr.address); } i2cBus->beginTransmission(addr.address); #ifdef ARCH_PORTDUINO @@ -189,35 +163,16 @@ void ScanI2CTwoWire::scanPort(I2CPort port, uint8_t *address, uint8_t asize) #endif type = NONE; if (err == 0) { - LOG_DEBUG("I2C device found at address 0x%x\n", addr.address); - switch (addr.address) { case SSD1306_ADDRESS: type = probeOLED(addr); break; -#if !defined(ARCH_PORTDUINO) && !defined(ARCH_STM32WL) - case ATECC608B_ADDR: -#ifdef RP2040_SLOW_CLOCK - if (atecc.begin(addr.address, Wire, Serial2) == true) -#else - if (atecc.begin(addr.address) == true) -#endif - - { - LOG_INFO("ATECC608B initialized\n"); - } else { - LOG_WARN("ATECC608B initialization failed\n"); - } - printATECCInfo(); - break; -#endif - #ifdef RV3028_RTC case RV3028_RTC: // foundDevices[addr] = RTC_RV3028; type = RTC_RV3028; - LOG_INFO("RV3028 RTC found\n"); + logFoundDevice("RV3028", (uint8_t)addr.address); rtc.initI2C(*i2cBus); rtc.writeToRegister(0x35, 0x07); // no Clkout rtc.writeToRegister(0x37, 0xB4); @@ -225,7 +180,7 @@ void ScanI2CTwoWire::scanPort(I2CPort port, uint8_t *address, uint8_t asize) #endif #ifdef PCF8563_RTC - SCAN_SIMPLE_CASE(PCF8563_RTC, RTC_PCF8563, "PCF8563 RTC found\n") + SCAN_SIMPLE_CASE(PCF8563_RTC, RTC_PCF8563, "PCF8563", (uint8_t)addr.address) #endif case CARDKB_ADDR: @@ -233,49 +188,50 @@ void ScanI2CTwoWire::scanPort(I2CPort port, uint8_t *address, uint8_t asize) registerValue = getRegisterValue(ScanI2CTwoWire::RegisterLocation(addr, 0x04), 1); if (registerValue == 0x02) { // KEYPAD_VERSION - LOG_INFO("RAK14004 found\n"); + logFoundDevice("RAK14004", (uint8_t)addr.address); type = RAK14004; } else { - LOG_INFO("m5 cardKB found\n"); + logFoundDevice("M5 cardKB", (uint8_t)addr.address); type = CARDKB; } break; - SCAN_SIMPLE_CASE(TDECK_KB_ADDR, TDECKKB, "T-Deck keyboard found\n"); - SCAN_SIMPLE_CASE(BBQ10_KB_ADDR, BBQ10KB, "BB Q10 keyboard found\n"); - SCAN_SIMPLE_CASE(ST7567_ADDRESS, SCREEN_ST7567, "st7567 display found\n"); + SCAN_SIMPLE_CASE(TDECK_KB_ADDR, TDECKKB, "T-Deck keyboard", (uint8_t)addr.address); + SCAN_SIMPLE_CASE(BBQ10_KB_ADDR, BBQ10KB, "BB Q10", (uint8_t)addr.address); + + SCAN_SIMPLE_CASE(ST7567_ADDRESS, SCREEN_ST7567, "ST7567", (uint8_t)addr.address); #ifdef HAS_NCP5623 - SCAN_SIMPLE_CASE(NCP5623_ADDR, NCP5623, "NCP5623 RGB LED found\n"); + SCAN_SIMPLE_CASE(NCP5623_ADDR, NCP5623, "NCP5623", (uint8_t)addr.address); #endif #ifdef HAS_PMU - SCAN_SIMPLE_CASE(XPOWERS_AXP192_AXP2101_ADDRESS, PMU_AXP192_AXP2101, "axp192/axp2101 PMU found\n") + SCAN_SIMPLE_CASE(XPOWERS_AXP192_AXP2101_ADDRESS, PMU_AXP192_AXP2101, "AXP192/AXP2101", (uint8_t)addr.address) #endif case BME_ADDR: case BME_ADDR_ALTERNATE: registerValue = getRegisterValue(ScanI2CTwoWire::RegisterLocation(addr, 0xD0), 1); // GET_ID switch (registerValue) { case 0x61: - LOG_INFO("BME-680 sensor found at address 0x%x\n", (uint8_t)addr.address); + logFoundDevice("BME680", (uint8_t)addr.address); type = BME_680; break; case 0x60: - LOG_INFO("BME-280 sensor found at address 0x%x\n", (uint8_t)addr.address); + logFoundDevice("BME280", (uint8_t)addr.address); type = BME_280; break; case 0x55: - LOG_INFO("BMP-085 or BMP-180 sensor found at address 0x%x\n", (uint8_t)addr.address); + logFoundDevice("BMP085/BMP180", (uint8_t)addr.address); type = BMP_085; break; default: registerValue = getRegisterValue(ScanI2CTwoWire::RegisterLocation(addr, 0x00), 1); // GET_ID switch (registerValue) { case 0x50: // BMP-388 should be 0x50 - LOG_INFO("BMP-388 sensor found at address 0x%x\n", (uint8_t)addr.address); + logFoundDevice("BMP-388", (uint8_t)addr.address); type = BMP_3XX; break; case 0x58: // BMP-280 should be 0x58 default: - LOG_INFO("BMP-280 sensor found at address 0x%x\n", (uint8_t)addr.address); + logFoundDevice("BMP-280", (uint8_t)addr.address); type = BMP_280; break; } @@ -284,7 +240,7 @@ void ScanI2CTwoWire::scanPort(I2CPort port, uint8_t *address, uint8_t asize) break; #ifndef HAS_NCP5623 case AHT10_ADDR: - LOG_INFO("AHT10 sensor found at address 0x%x\n", (uint8_t)addr.address); + logFoundDevice("AHT10", (uint8_t)addr.address); type = AHT10; break; #endif @@ -292,43 +248,59 @@ void ScanI2CTwoWire::scanPort(I2CPort port, uint8_t *address, uint8_t asize) case INA_ADDR_ALTERNATE: case INA_ADDR_WAVESHARE_UPS: registerValue = getRegisterValue(ScanI2CTwoWire::RegisterLocation(addr, 0xFE), 2); - LOG_DEBUG("Register MFG_UID: 0x%x\n", registerValue); + LOG_DEBUG("Register MFG_UID: 0x%x", registerValue); if (registerValue == 0x5449) { - LOG_INFO("INA260 sensor found at address 0x%x\n", (uint8_t)addr.address); + logFoundDevice("INA260", (uint8_t)addr.address); type = INA260; } else { // Assume INA219 if INA260 ID is not found - LOG_INFO("INA219 sensor found at address 0x%x\n", (uint8_t)addr.address); + logFoundDevice("INA219", (uint8_t)addr.address); type = INA219; } break; case INA3221_ADDR: registerValue = getRegisterValue(ScanI2CTwoWire::RegisterLocation(addr, 0xFE), 2); - LOG_DEBUG("Register MFG_UID: 0x%x\n", registerValue); + LOG_DEBUG("Register MFG_UID FE: 0x%x", registerValue); if (registerValue == 0x5449) { - LOG_INFO("INA3221 sensor found at address 0x%x\n", (uint8_t)addr.address); + logFoundDevice("INA3221", (uint8_t)addr.address); type = INA3221; } else { - LOG_INFO("DFRobot Lark weather station found at address 0x%x\n", (uint8_t)addr.address); - type = DFROBOT_LARK; + /* check the first 2 bytes of the 6 byte response register + LARK FW 1.0 should return: + RESPONSE_STATUS STATUS_SUCCESS (0x53) + RESPONSE_CMD CMD_GET_VERSION (0x05) + RESPONSE_LEN_L 0x02 + RESPONSE_LEN_H 0x00 + RESPONSE_PAYLOAD 0x01 + RESPONSE_PAYLOAD+1 0x00 + */ + registerValue = getRegisterValue(ScanI2CTwoWire::RegisterLocation(addr, 0x05), 2); + LOG_DEBUG("Register MFG_UID 05: 0x%x", registerValue); + if (registerValue == 0x5305) { + logFoundDevice("DFRobot Lark", (uint8_t)addr.address); + type = DFROBOT_LARK; + } + // else: probably a RAK12500/UBLOX GPS on I2C } break; case MCP9808_ADDR: // We need to check for STK8BAXX first, since register 0x07 is new data flag for the z-axis and can produce some // weird result. and register 0x00 doesn't seems to be colliding with MCP9808 and LIS3DH chips. { +#ifdef HAS_STK8XXX // Check register 0x00 for 0x8700 response to ID STK8BA53 chip. registerValue = getRegisterValue(ScanI2CTwoWire::RegisterLocation(addr, 0x00), 2); if (registerValue == 0x8700) { type = STK8BAXX; - LOG_INFO("STK8BAXX accelerometer found\n"); + logFoundDevice("STK8BAXX", (uint8_t)addr.address); break; } +#endif // Check register 0x07 for 0x0400 response to ID MCP9808 chip. registerValue = getRegisterValue(ScanI2CTwoWire::RegisterLocation(addr, 0x07), 2); if (registerValue == 0x0400) { type = MCP9808; - LOG_INFO("MCP9808 sensor found\n"); + logFoundDevice("MCP9808", (uint8_t)addr.address); break; } @@ -336,7 +308,7 @@ void ScanI2CTwoWire::scanPort(I2CPort port, uint8_t *address, uint8_t asize) registerValue = getRegisterValue(ScanI2CTwoWire::RegisterLocation(addr, 0x0F), 2); if (registerValue == 0x3300 || registerValue == 0x3333) { // RAK4631 WisBlock has LIS3DH register at 0x3333 type = LIS3DH; - LOG_INFO("LIS3DH accelerometer found\n"); + logFoundDevice("LIS3DH", (uint8_t)addr.address); } break; } @@ -344,95 +316,128 @@ void ScanI2CTwoWire::scanPort(I2CPort port, uint8_t *address, uint8_t asize) registerValue = getRegisterValue(ScanI2CTwoWire::RegisterLocation(addr, 0x89), 2); if (registerValue == 0x11a2 || registerValue == 0x11da || registerValue == 0xe9c) { type = SHT4X; - LOG_INFO("SHT4X sensor found\n"); + logFoundDevice("SHT4X", (uint8_t)addr.address); } else if (getRegisterValue(ScanI2CTwoWire::RegisterLocation(addr, 0x7E), 2) == 0x5449) { type = OPT3001; - LOG_INFO("OPT3001 light sensor found\n"); + logFoundDevice("OPT3001", (uint8_t)addr.address); } else { type = SHT31; - LOG_INFO("SHT31 sensor found\n"); + logFoundDevice("SHT31", (uint8_t)addr.address); } break; - SCAN_SIMPLE_CASE(SHTC3_ADDR, SHTC3, "SHTC3 sensor found\n") + SCAN_SIMPLE_CASE(SHTC3_ADDR, SHTC3, "SHTC3", (uint8_t)addr.address) case RCWL9620_ADDR: // get MAX30102 PARTID registerValue = getRegisterValue(ScanI2CTwoWire::RegisterLocation(addr, 0xFF), 1); if (registerValue == 0x15) { type = MAX30102; - LOG_INFO("MAX30102 Health sensor found\n"); + logFoundDevice("MAX30102", (uint8_t)addr.address); break; } else { type = RCWL9620; - LOG_INFO("RCWL9620 sensor found\n"); + logFoundDevice("RCWL9620", (uint8_t)addr.address); } break; case LPS22HB_ADDR_ALT: - SCAN_SIMPLE_CASE(LPS22HB_ADDR, LPS22HB, "LPS22HB sensor found\n") - - SCAN_SIMPLE_CASE(QMC6310_ADDR, QMC6310, "QMC6310 Highrate 3-Axis magnetic sensor found\n") + SCAN_SIMPLE_CASE(LPS22HB_ADDR, LPS22HB, "LPS22HB", (uint8_t)addr.address) + SCAN_SIMPLE_CASE(QMC6310_ADDR, QMC6310, "QMC6310", (uint8_t)addr.address) case QMI8658_ADDR: registerValue = getRegisterValue(ScanI2CTwoWire::RegisterLocation(addr, 0x0A), 1); // get ID if (registerValue == 0xC0) { type = BQ24295; - LOG_INFO("BQ24295 PMU found\n"); + logFoundDevice("BQ24295", (uint8_t)addr.address); break; } registerValue = getRegisterValue(ScanI2CTwoWire::RegisterLocation(addr, 0x0F), 1); // get ID if (registerValue == 0x6A) { type = LSM6DS3; - LOG_INFO("LSM6DS3 accelerometer found at address 0x%x\n", (uint8_t)addr.address); + logFoundDevice("LSM6DS3", (uint8_t)addr.address); } else { type = QMI8658; - LOG_INFO("QMI8658 Highrate 6-Axis inertial measurement sensor found\n"); + logFoundDevice("QMI8658", (uint8_t)addr.address); } break; - SCAN_SIMPLE_CASE(QMC5883L_ADDR, QMC5883L, "QMC5883L Highrate 3-Axis magnetic sensor found\n") - SCAN_SIMPLE_CASE(HMC5883L_ADDR, HMC5883L, "HMC5883L 3-Axis digital compass found\n") - SCAN_SIMPLE_CASE(PMSA0031_ADDR, PMSA0031, "PMSA0031 air quality sensor found\n") - SCAN_SIMPLE_CASE(BMA423_ADDR, BMA423, "BMA423 accelerometer found\n"); - SCAN_SIMPLE_CASE(LSM6DS3_ADDR, LSM6DS3, "LSM6DS3 accelerometer found at address 0x%x\n", (uint8_t)addr.address); - SCAN_SIMPLE_CASE(TCA9535_ADDR, TCA9535, "TCA9535 I2C expander found\n"); - SCAN_SIMPLE_CASE(TCA9555_ADDR, TCA9555, "TCA9555 I2C expander found\n"); - SCAN_SIMPLE_CASE(VEML7700_ADDR, VEML7700, "VEML7700 light sensor found\n"); - SCAN_SIMPLE_CASE(TSL25911_ADDR, TSL2591, "TSL2591 light sensor found\n"); - SCAN_SIMPLE_CASE(OPT3001_ADDR, OPT3001, "OPT3001 light sensor found\n"); - SCAN_SIMPLE_CASE(MLX90632_ADDR, MLX90632, "MLX90632 IR temp sensor found\n"); - SCAN_SIMPLE_CASE(NAU7802_ADDR, NAU7802, "NAU7802 based scale found\n"); - SCAN_SIMPLE_CASE(FT6336U_ADDR, FT6336U, "FT6336U touchscreen found\n"); - SCAN_SIMPLE_CASE(MAX1704X_ADDR, MAX17048, "MAX17048 lipo fuel gauge found\n"); -#ifdef HAS_TPS65233 - SCAN_SIMPLE_CASE(TPS65233_ADDR, TPS65233, "TPS65233 BIAS-T found\n"); + SCAN_SIMPLE_CASE(QMC5883L_ADDR, QMC5883L, "QMC5883L", (uint8_t)addr.address) + SCAN_SIMPLE_CASE(HMC5883L_ADDR, HMC5883L, "HMC5883L", (uint8_t)addr.address) +#ifdef HAS_QMA6100P + SCAN_SIMPLE_CASE(QMA6100P_ADDR, QMA6100P, "QMA6100P", (uint8_t)addr.address) +#else + SCAN_SIMPLE_CASE(PMSA0031_ADDR, PMSA0031, "PMSA0031", (uint8_t)addr.address) #endif - SCAN_SIMPLE_CASE(MLX90614_ADDR_DEF, MLX90614, "MLX90614 IR temp sensor found\n"); + case BMA423_ADDR: // this can also be LIS3DH_ADDR_ALT + registerValue = getRegisterValue(ScanI2CTwoWire::RegisterLocation(addr, 0x0F), 2); + if (registerValue == 0x3300 || registerValue == 0x3333) { // RAK4631 WisBlock has LIS3DH register at 0x3333 + type = LIS3DH; + logFoundDevice("LIS3DH", (uint8_t)addr.address); + } else { + type = BMA423; + logFoundDevice("BMA423", (uint8_t)addr.address); + } + break; + + SCAN_SIMPLE_CASE(LSM6DS3_ADDR, LSM6DS3, "LSM6DS3", (uint8_t)addr.address); + SCAN_SIMPLE_CASE(TCA9535_ADDR, TCA9535, "TCA9535", (uint8_t)addr.address); + SCAN_SIMPLE_CASE(TCA9555_ADDR, TCA9555, "TCA9555", (uint8_t)addr.address); + SCAN_SIMPLE_CASE(VEML7700_ADDR, VEML7700, "VEML7700", (uint8_t)addr.address); + SCAN_SIMPLE_CASE(TSL25911_ADDR, TSL2591, "TSL2591", (uint8_t)addr.address); + SCAN_SIMPLE_CASE(OPT3001_ADDR, OPT3001, "OPT3001", (uint8_t)addr.address); + SCAN_SIMPLE_CASE(MLX90632_ADDR, MLX90632, "MLX90632", (uint8_t)addr.address); + SCAN_SIMPLE_CASE(NAU7802_ADDR, NAU7802, "NAU7802", (uint8_t)addr.address); + SCAN_SIMPLE_CASE(FT6336U_ADDR, FT6336U, "FT6336U", (uint8_t)addr.address); + SCAN_SIMPLE_CASE(MAX1704X_ADDR, MAX17048, "MAX17048", (uint8_t)addr.address); +#ifdef HAS_TPS65233 + SCAN_SIMPLE_CASE(TPS65233_ADDR, TPS65233, "TPS65233", (uint8_t)addr.address); +#endif + + case MLX90614_ADDR_DEF: + registerValue = getRegisterValue(ScanI2CTwoWire::RegisterLocation(addr, 0x0e), 1); + if (registerValue == 0x5a) { + type = MLX90614; + logFoundDevice("MLX90614", (uint8_t)addr.address); + } else { + type = MPR121KB; + logFoundDevice("MPR121KB", (uint8_t)addr.address); + } + break; case ICM20948_ADDR: // same as BMX160_ADDR case ICM20948_ADDR_ALT: // same as MPU6050_ADDR registerValue = getRegisterValue(ScanI2CTwoWire::RegisterLocation(addr, 0x00), 1); if (registerValue == 0xEA) { type = ICM20948; - LOG_INFO("ICM20948 9-dof motion processor found\n"); + logFoundDevice("ICM20948", (uint8_t)addr.address); break; } else if (addr.address == BMX160_ADDR) { type = BMX160; - LOG_INFO("BMX160 accelerometer found\n"); + logFoundDevice("BMX160", (uint8_t)addr.address); break; } else { type = MPU6050; - LOG_INFO("MPU6050 accelerometer found\n"); + logFoundDevice("MPU6050", (uint8_t)addr.address); + break; + } + break; + + case CGRADSENS_ADDR: + // Register 0x00 of the RadSens sensor contains is product identifier 0x7D + registerValue = getRegisterValue(ScanI2CTwoWire::RegisterLocation(addr, 0x00), 1); + if (registerValue == 0x7D) { + type = CGRADSENS; + logFoundDevice("ClimateGuard RadSens", (uint8_t)addr.address); break; } break; default: - LOG_INFO("Device found at address 0x%x was not able to be enumerated\n", addr.address); + LOG_INFO("Device found at address 0x%x was not able to be enumerated", (uint8_t)addr.address); } } else if (err == 4) { - LOG_ERROR("Unknown error at address 0x%x\n", addr.address); + LOG_ERROR("Unknown error at address 0x%x", (uint8_t)addr.address); } // Check if a type was found for the enumerated device - save, if so @@ -465,4 +470,9 @@ size_t ScanI2CTwoWire::countDevices() const { return foundDevices.size(); } + +void ScanI2CTwoWire::logFoundDevice(const char *device, uint8_t address) +{ + LOG_INFO("%s found at address 0x%x", device, address); +} #endif \ No newline at end of file diff --git a/src/detect/ScanI2CTwoWire.h b/src/detect/ScanI2CTwoWire.h index c8dd96469..d0af7cde6 100644 --- a/src/detect/ScanI2CTwoWire.h +++ b/src/detect/ScanI2CTwoWire.h @@ -53,10 +53,10 @@ class ScanI2CTwoWire : public ScanI2C concurrency::Lock lock; - void printATECCInfo() const; - uint16_t getRegisterValue(const RegisterLocation &, ResponseWidth) const; DeviceType probeOLED(ScanI2C::DeviceAddress) const; + + static void logFoundDevice(const char *device, uint8_t address); }; #endif \ No newline at end of file diff --git a/src/detect/axpDebug.h b/src/detect/axpDebug.h deleted file mode 100644 index fc95447aa..000000000 --- a/src/detect/axpDebug.h +++ /dev/null @@ -1,19 +0,0 @@ -#if 0 -// Turn off for now -uint32_t axpDebugRead() -{ - axp.debugCharging(); - LOG_DEBUG("vbus current %f\n", axp.getVbusCurrent()); - LOG_DEBUG("charge current %f\n", axp.getBattChargeCurrent()); - LOG_DEBUG("bat voltage %f\n", axp.getBattVoltage()); - LOG_DEBUG("batt pct %d\n", axp.getBattPercentage()); - LOG_DEBUG("is battery connected %d\n", axp.isBatteryConnect()); - LOG_DEBUG("is USB connected %d\n", axp.isVBUSPlug()); - LOG_DEBUG("is charging %d\n", axp.isChargeing()); - - return 30 * 1000; -} - -Periodic axpDebugOutput(axpDebugRead); -axpDebugOutput.setup(); -#endif \ No newline at end of file diff --git a/src/detect/einkScan.h b/src/detect/einkScan.h index 6915709de..d20c7b6e5 100644 --- a/src/detect/einkScan.h +++ b/src/detect/einkScan.h @@ -59,9 +59,9 @@ void scanEInkDevice(void) d_writeCommand(0x20); eink_found = (d_waitWhileBusy(150) > 0) ? true : false; if (eink_found) - LOG_DEBUG("EInk display found\n"); + LOG_DEBUG("EInk display found"); else - LOG_DEBUG("EInk display not found\n"); + LOG_DEBUG("EInk display not found"); SPI1.end(); } #endif \ No newline at end of file diff --git a/src/gps/GPS.cpp b/src/gps/GPS.cpp index 5863fc343..dcece305a 100644 --- a/src/gps/GPS.cpp +++ b/src/gps/GPS.cpp @@ -7,6 +7,7 @@ #include "PowerMon.h" #include "RTC.h" #include "Throttle.h" +#include "buzz.h" #include "meshUtils.h" #include "main.h" // pmu_found @@ -19,6 +20,7 @@ #ifdef ARCH_PORTDUINO #include "PortduinoGlue.h" #include "meshUtils.h" +#include #include #endif @@ -26,29 +28,43 @@ #define GPS_RESET_MODE HIGH #endif +// Not all platforms have std::size(). +template std::size_t array_count(const T (&)[N]) +{ + return N; +} + #if defined(NRF52840_XXAA) || defined(NRF52833_XXAA) || defined(ARCH_ESP32) || defined(ARCH_PORTDUINO) HardwareSerial *GPS::_serial_gps = &Serial1; #elif defined(ARCH_RP2040) SerialUART *GPS::_serial_gps = &Serial1; #else -HardwareSerial *GPS::_serial_gps = NULL; +HardwareSerial *GPS::_serial_gps = nullptr; #endif GPS *gps = nullptr; -GPSUpdateScheduling scheduling; +static const char *ACK_SUCCESS_MESSAGE = "Get ack success!"; + +static GPSUpdateScheduling scheduling; /// Multiple GPS instances might use the same serial port (in sequence), but we can /// only init that port once. static bool didSerialInit; -struct uBloxGnssModelInfo info; -uint8_t uBloxProtocolVersion; +static struct uBloxGnssModelInfo { + char swVersion[30]; + char hwVersion[10]; + uint8_t extensionNo; + char extension[10][30]; + uint8_t protocol_version; +} ublox_info; + #define GPS_SOL_EXPIRY_MS 5000 // in millis. give 1 second time to combine different sentences. NMEA Frequency isn't higher anyway #define NMEA_MSG_GXGSA "GNGSA" // GSA message (GPGSA, GNGSA etc) // For logging -const char *getGPSPowerStateString(GPSPowerState state) +static const char *getGPSPowerStateString(GPSPowerState state) { switch (state) { case GPS_ACTIVE: @@ -67,7 +83,7 @@ const char *getGPSPowerStateString(GPSPowerState state) } } -void GPS::UBXChecksum(uint8_t *message, size_t length) +static void UBXChecksum(uint8_t *message, size_t length) { uint8_t CK_A = 0, CK_B = 0; @@ -83,7 +99,7 @@ void GPS::UBXChecksum(uint8_t *message, size_t length) } // Calculate the checksum for a CAS packet -void GPS::CASChecksum(uint8_t *message, size_t length) +static void CASChecksum(uint8_t *message, size_t length) { uint32_t cksum = ((uint32_t)message[5] << 24); // Message ID cksum += ((uint32_t)message[4]) << 16; // Class @@ -154,7 +170,7 @@ uint8_t GPS::makeCASPacket(uint8_t class_id, uint8_t msg_id, uint8_t payload_siz CASChecksum(UBXscratch, (payload_size + 10)); #if defined(GPS_DEBUG) && defined(DEBUG_PORT) - LOG_DEBUG("Constructed CAS packet: \n"); + LOG_DEBUG("CAS packet: "); DEBUG_PORT.hexDump(MESHTASTIC_LOG_LEVEL_DEBUG, UBXscratch, payload_size + 10); #endif return (payload_size + 10); @@ -166,33 +182,33 @@ GPS_RESPONSE GPS::getACK(const char *message, uint32_t waitMillis) uint8_t b; int bytesRead = 0; uint32_t startTimeout = millis() + waitMillis; +#ifdef GPS_DEBUG + std::string debugmsg = ""; +#endif while (millis() < startTimeout) { if (_serial_gps->available()) { b = _serial_gps->read(); #ifdef GPS_DEBUG - LOG_DEBUG("%c", (b >= 32 && b <= 126) ? b : '.'); + debugmsg += vformat("%c", (b >= 32 && b <= 126) ? b : '.'); #endif buffer[bytesRead] = b; bytesRead++; if ((bytesRead == 767) || (b == '\r')) { if (strnstr((char *)buffer, message, bytesRead) != nullptr) { #ifdef GPS_DEBUG - LOG_DEBUG("\r\nFound: %s\r\n", message); // Log the found message + LOG_DEBUG("Found: %s", message); // Log the found message #endif return GNSS_RESPONSE_OK; } else { bytesRead = 0; #ifdef GPS_DEBUG - LOG_DEBUG("\r\n"); + LOG_DEBUG(debugmsg.c_str()); #endif } } } } -#ifdef GPS_DEBUG - LOG_DEBUG("\n"); -#endif return GNSS_RESPONSE_NONE; } @@ -235,7 +251,7 @@ GPS_RESPONSE GPS::getACKCas(uint8_t class_id, uint8_t msg_id, uint32_t waitMilli // Check for an ACK-ACK for the specified class and message id if ((msg_cls == 0x05) && (msg_msg_id == 0x01) && payload_cls == class_id && payload_msg == msg_id) { #ifdef GPS_DEBUG - LOG_INFO("Got ACK for class %02X message %02X in %d millis.\n", class_id, msg_id, millis() - startTime); + LOG_INFO("Got ACK for class %02X message %02X in %dms", class_id, msg_id, millis() - startTime); #endif return GNSS_RESPONSE_OK; } @@ -243,7 +259,7 @@ GPS_RESPONSE GPS::getACKCas(uint8_t class_id, uint8_t msg_id, uint32_t waitMilli // Check for an ACK-NACK for the specified class and message id if ((msg_cls == 0x05) && (msg_msg_id == 0x00) && payload_cls == class_id && payload_msg == msg_id) { #ifdef GPS_DEBUG - LOG_WARN("Got NACK for class %02X message %02X in %d millis.\n", class_id, msg_id, millis() - startTime); + LOG_WARN("Got NACK for class %02X message %02X in %dms", class_id, msg_id, millis() - startTime); #endif return GNSS_RESPONSE_NAK; } @@ -266,6 +282,9 @@ GPS_RESPONSE GPS::getACK(uint8_t class_id, uint8_t msg_id, uint32_t waitMillis) uint32_t startTime = millis(); const char frame_errors[] = "More than 100 frame errors"; int sCounter = 0; +#ifdef GPS_DEBUG + std::string debugmsg = ""; +#endif for (int j = 2; j < 6; j++) { buf[8] += buf[j]; @@ -281,8 +300,7 @@ GPS_RESPONSE GPS::getACK(uint8_t class_id, uint8_t msg_id, uint32_t waitMillis) while (Throttle::isWithinTimespanMs(startTime, waitMillis)) { if (ack > 9) { #ifdef GPS_DEBUG - LOG_DEBUG("\n"); - LOG_INFO("Got ACK for class %02X message %02X in %d millis.\n", class_id, msg_id, millis() - startTime); + LOG_INFO("Got ACK for class %02X message %02X in %dms", class_id, msg_id, millis() - startTime); #endif return GNSS_RESPONSE_OK; // ACK received } @@ -291,22 +309,26 @@ GPS_RESPONSE GPS::getACK(uint8_t class_id, uint8_t msg_id, uint32_t waitMillis) if (b == frame_errors[sCounter]) { sCounter++; if (sCounter == 26) { +#ifdef GPS_DEBUG + + LOG_DEBUG(debugmsg.c_str()); +#endif return GNSS_RESPONSE_FRAME_ERRORS; } } else { sCounter = 0; } #ifdef GPS_DEBUG - LOG_DEBUG("%02X", b); + debugmsg += vformat("%02X", b); #endif if (b == buf[ack]) { ack++; } else { if (ack == 3 && b == 0x00) { // UBX-ACK-NAK message #ifdef GPS_DEBUG - LOG_DEBUG("\n"); + LOG_DEBUG(debugmsg.c_str()); #endif - LOG_WARN("Got NAK for class %02X message %02X\n", class_id, msg_id); + LOG_WARN("Got NAK for class %02X message %02X", class_id, msg_id); return GNSS_RESPONSE_NAK; // NAK received } ack = 0; // Reset the acknowledgement counter @@ -314,8 +336,8 @@ GPS_RESPONSE GPS::getACK(uint8_t class_id, uint8_t msg_id, uint32_t waitMillis) } } #ifdef GPS_DEBUG - LOG_DEBUG("\n"); - LOG_WARN("No response for class %02X message %02X\n", class_id, msg_id); + LOG_DEBUG(debugmsg.c_str()); + LOG_WARN("No response for class %02X message %02X", class_id, msg_id); #endif return GNSS_RESPONSE_NONE; // No response received within timeout } @@ -388,8 +410,7 @@ int GPS::getACK(uint8_t *buffer, uint16_t size, uint8_t requestedClass, uint8_t } else { // return payload length #ifdef GPS_DEBUG - LOG_INFO("Got ACK for class %02X message %02X in %d millis.\n", requestedClass, requestedID, - millis() - startTime); + LOG_INFO("Got ACK for class %02X message %02X in %dms", requestedClass, requestedID, millis() - startTime); #endif return needRead; } @@ -400,36 +421,57 @@ int GPS::getACK(uint8_t *buffer, uint16_t size, uint8_t requestedClass, uint8_t } } } - // LOG_WARN("No response for class %02X message %02X\n", requestedClass, requestedID); return 0; } +#if GPS_BAUDRATE_FIXED +// if GPS_BAUDRATE is specified in variant, only try that. +static const int serialSpeeds[1] = {GPS_BAUDRATE}; +static const int rareSerialSpeeds[1] = {GPS_BAUDRATE}; +#else +static const int serialSpeeds[3] = {9600, 115200, 38400}; +static const int rareSerialSpeeds[3] = {4800, 57600, GPS_BAUDRATE}; +#endif + +/** + * @brief Setup the GPS based on the model detected. + * We detect the GPS by cycling through a set of baud rates, first common then rare. + * For each baud rate, we run GPS::Probe to send commands and match the responses + * to known GPS responses. + * @retval Whether setup reached the end of its potential to configure the GPS. + */ bool GPS::setup() { - if (!didSerialInit) { int msglen = 0; if (tx_gpio && gnssModel == GNSS_MODEL_UNKNOWN) { - - // if GPS_BAUDRATE is specified in variant (i.e. not 9600), skip to the specified rate. - if (speedSelect == 0 && GPS_BAUDRATE != serialSpeeds[speedSelect]) { - speedSelect = std::find(serialSpeeds, std::end(serialSpeeds), GPS_BAUDRATE) - serialSpeeds; + if (probeTries < 2) { + LOG_DEBUG("Probe for GPS at %d", serialSpeeds[speedSelect]); + gnssModel = probe(serialSpeeds[speedSelect]); + if (gnssModel == GNSS_MODEL_UNKNOWN) { + if (++speedSelect == array_count(serialSpeeds)) { + speedSelect = 0; + ++probeTries; + } + } } - - LOG_DEBUG("Probing for GPS at %d \n", serialSpeeds[speedSelect]); - gnssModel = probe(serialSpeeds[speedSelect]); - if (gnssModel == GNSS_MODEL_UNKNOWN) { - if (++speedSelect == sizeof(serialSpeeds) / sizeof(int)) { - speedSelect = 0; - if (--probeTries == 0) { - LOG_WARN("Giving up on GPS probe and setting to 9600.\n"); + // Rare Serial Speeds + if (probeTries == 2) { + LOG_DEBUG("Probe for GPS at %d", rareSerialSpeeds[speedSelect]); + gnssModel = probe(rareSerialSpeeds[speedSelect]); + if (gnssModel == GNSS_MODEL_UNKNOWN) { + if (++speedSelect == array_count(rareSerialSpeeds)) { + LOG_WARN("Give up on GPS probe and set to %d", GPS_BAUDRATE); return true; } } - return false; } + } + + if (gnssModel != GNSS_MODEL_UNKNOWN) { + setConnected(); } else { - gnssModel = GNSS_MODEL_UNKNOWN; + return false; } if (gnssModel == GNSS_MODEL_MTK) { @@ -467,19 +509,31 @@ bool GPS::setup() // Switch to Fitness Mode, for running and walking purpose with low speed (<5 m/s) _serial_gps->write("$PMTK886,1*29\r\n"); delay(250); + } else if (gnssModel == GNSS_MODEL_MTK_PA1616S) { + // PA1616S is used in some GPS breakout boards from Adafruit + // PA1616S does not have GLONASS capability. PA1616D does, but is not implemented here. + _serial_gps->write("$PMTK353,1,0,0,0,0*2A\r\n"); + // Above command will reset the GPS and takes longer before it will accept new commands + delay(1000); + // Only ask for RMC and GGA (GNRMC and GNGGA) + _serial_gps->write("$PMTK314,0,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0*28\r\n"); + delay(250); + // Enable SBAS / WAAS + _serial_gps->write("$PMTK301,2*2E\r\n"); + delay(250); } else if (gnssModel == GNSS_MODEL_ATGM336H) { // Set the intial configuration of the device - these _should_ work for most AT6558 devices msglen = makeCASPacket(0x06, 0x07, sizeof(_message_CAS_CFG_NAVX_CONF), _message_CAS_CFG_NAVX_CONF); _serial_gps->write(UBXscratch, msglen); if (getACKCas(0x06, 0x07, 250) != GNSS_RESPONSE_OK) { - LOG_WARN("ATGM336H - Could not set Configuration"); + LOG_WARN("ATGM336H: Could not set Config"); } // Set the update frequence to 1Hz msglen = makeCASPacket(0x06, 0x04, sizeof(_message_CAS_CFG_RATE_1HZ), _message_CAS_CFG_RATE_1HZ); _serial_gps->write(UBXscratch, msglen); if (getACKCas(0x06, 0x04, 250) != GNSS_RESPONSE_OK) { - LOG_WARN("ATGM336H - Could not set Update Frequency"); + LOG_WARN("ATGM336H: Could not set Update Frequency"); } // Set the NEMA output messages @@ -491,7 +545,7 @@ bool GPS::setup() msglen = makeCASPacket(0x06, 0x01, sizeof(cas_cfg_msg_packet), cas_cfg_msg_packet); _serial_gps->write(UBXscratch, msglen); if (getACKCas(0x06, 0x01, 250) != GNSS_RESPONSE_OK) { - LOG_WARN("ATGM336H - Could not enable NMEA MSG: %d\n", fields[i]); + LOG_WARN("ATGM336H: Could not enable NMEA MSG: %d", fields[i]); } } } else if (gnssModel == GNSS_MODEL_UC6580) { @@ -544,20 +598,20 @@ bool GPS::setup() SEND_UBX_PACKET(0x06, 0x01, _message_GGA, "enable NMEA GGA", 500); clearBuffer(); - SEND_UBX_PACKET(0x06, 0x11, _message_CFG_RXM_ECO, "enable powersaving ECO mode for Neo-6", 500); - SEND_UBX_PACKET(0x06, 0x3B, _message_CFG_PM2, "enable powersaving details for GPS", 500); + SEND_UBX_PACKET(0x06, 0x11, _message_CFG_RXM_ECO, "enable powersave ECO mode for Neo-6", 500); + SEND_UBX_PACKET(0x06, 0x3B, _message_CFG_PM2, "enable powersave details for GPS", 500); SEND_UBX_PACKET(0x06, 0x01, _message_AID, "disable UBX-AID", 500); msglen = makeUBXPacket(0x06, 0x09, sizeof(_message_SAVE), _message_SAVE); _serial_gps->write(UBXscratch, msglen); if (getACK(0x06, 0x09, 2000) != GNSS_RESPONSE_OK) { - LOG_WARN("Unable to save GNSS module configuration.\n"); + LOG_WARN("Unable to save GNSS module config"); } else { - LOG_INFO("GNSS module configuration saved!\n"); + LOG_INFO("GNSS module config saved!"); } } else if (IS_ONE_OF(gnssModel, GNSS_MODEL_UBLOX7, GNSS_MODEL_UBLOX8, GNSS_MODEL_UBLOX9)) { if (gnssModel == GNSS_MODEL_UBLOX7) { - LOG_DEBUG("Setting GPS+SBAS\n"); + LOG_DEBUG("Set GPS+SBAS"); msglen = makeUBXPacket(0x06, 0x3e, sizeof(_message_GNSS_7), _message_GNSS_7); _serial_gps->write(UBXscratch, msglen); } else { // 8,9 @@ -567,12 +621,12 @@ bool GPS::setup() if (getACK(0x06, 0x3e, 800) == GNSS_RESPONSE_NAK) { // It's not critical if the module doesn't acknowledge this configuration. - LOG_INFO("reconfigure GNSS - defaults maintained. Is this module GPS-only?\n"); + LOG_DEBUG("reconfigure GNSS - defaults maintained. Is this module GPS-only?"); } else { if (gnssModel == GNSS_MODEL_UBLOX7) { - LOG_INFO("GNSS configured for GPS+SBAS.\n"); + LOG_INFO("GPS+SBAS configured"); } else { // 8,9 - LOG_INFO("GNSS configured for GPS+SBAS+GLONASS+Galileo.\n"); + LOG_INFO("GPS+SBAS+GLONASS+Galileo configured"); } // Documentation say, we need wait atleast 0.5s after reconfiguration of GNSS module, before sending next // commands for the M8 it tends to be more... 1 sec should be enough ;>) @@ -602,10 +656,10 @@ bool GPS::setup() SEND_UBX_PACKET(0x06, 0x01, _message_RMC, "enable NMEA RMC", 500); SEND_UBX_PACKET(0x06, 0x01, _message_GGA, "enable NMEA GGA", 500); - if (uBloxProtocolVersion >= 18) { + if (ublox_info.protocol_version >= 18) { clearBuffer(); - SEND_UBX_PACKET(0x06, 0x86, _message_PMS, "enable powersaving for GPS", 500); - SEND_UBX_PACKET(0x06, 0x3B, _message_CFG_PM2, "enable powersaving details for GPS", 500); + SEND_UBX_PACKET(0x06, 0x86, _message_PMS, "enable powersave for GPS", 500); + SEND_UBX_PACKET(0x06, 0x3B, _message_CFG_PM2, "enable powersave details for GPS", 500); // For M8 we want to enable NMEA vserion 4.10 so we can see the additional sats. if (gnssModel == GNSS_MODEL_UBLOX8) { @@ -613,16 +667,16 @@ bool GPS::setup() SEND_UBX_PACKET(0x06, 0x17, _message_NMEA, "enable NMEA 4.10", 500); } } else { - SEND_UBX_PACKET(0x06, 0x11, _message_CFG_RXM_PSM, "enable powersaving mode for GPS", 500); - SEND_UBX_PACKET(0x06, 0x3B, _message_CFG_PM2, "enable powersaving details for GPS", 500); + SEND_UBX_PACKET(0x06, 0x11, _message_CFG_RXM_PSM, "enable powersave mode for GPS", 500); + SEND_UBX_PACKET(0x06, 0x3B, _message_CFG_PM2, "enable powersave details for GPS", 500); } msglen = makeUBXPacket(0x06, 0x09, sizeof(_message_SAVE), _message_SAVE); _serial_gps->write(UBXscratch, msglen); if (getACK(0x06, 0x09, 2000) != GNSS_RESPONSE_OK) { - LOG_WARN("Unable to save GNSS module configuration.\n"); + LOG_WARN("Unable to save GNSS module config"); } else { - LOG_INFO("GNSS module configuration saved!\n"); + LOG_INFO("GNSS module configuration saved!"); } } else if (gnssModel == GNSS_MODEL_UBLOX10) { delay(1000); @@ -640,13 +694,13 @@ bool GPS::setup() SEND_UBX_PACKET(0x06, 0x8A, _message_VALSET_DISABLE_TXT_INFO_BBR, "disable Info messages for M10 GPS BBR", 300); delay(750); // Do M10 configuration for Power Management. - SEND_UBX_PACKET(0x06, 0x8A, _message_VALSET_PM_RAM, "enable powersaving for M10 GPS RAM", 300); + SEND_UBX_PACKET(0x06, 0x8A, _message_VALSET_PM_RAM, "enable powersave for M10 GPS RAM", 300); delay(750); - SEND_UBX_PACKET(0x06, 0x8A, _message_VALSET_PM_BBR, "enable powersaving for M10 GPS BBR", 300); + SEND_UBX_PACKET(0x06, 0x8A, _message_VALSET_PM_BBR, "enable powersave for M10 GPS BBR", 300); delay(750); - SEND_UBX_PACKET(0x06, 0x8A, _message_VALSET_ITFM_RAM, "enable Jamming detection M10 GPS RAM", 300); + SEND_UBX_PACKET(0x06, 0x8A, _message_VALSET_ITFM_RAM, "enable jam detection M10 GPS RAM", 300); delay(750); - SEND_UBX_PACKET(0x06, 0x8A, _message_VALSET_ITFM_BBR, "enable Jamming detection M10 GPS BBR", 300); + SEND_UBX_PACKET(0x06, 0x8A, _message_VALSET_ITFM_BBR, "enable jam detection M10 GPS BBR", 300); delay(750); // Here is where the init commands should go to do further M10 initialization. SEND_UBX_PACKET(0x06, 0x8A, _message_VALSET_DISABLE_SBAS_RAM, "disable SBAS M10 GPS RAM", 300); @@ -654,7 +708,8 @@ bool GPS::setup() SEND_UBX_PACKET(0x06, 0x8A, _message_VALSET_DISABLE_SBAS_BBR, "disable SBAS M10 GPS BBR", 300); delay(750); // will cause a receiver restart so wait a bit - // Done with initialization, Now enable wanted NMEA messages in BBR layer so they will survive a periodic sleep. + // Done with initialization, Now enable wanted NMEA messages in BBR layer so they will survive a periodic + // sleep. SEND_UBX_PACKET(0x06, 0x8A, _message_VALSET_ENABLE_NMEA_BBR, "enable messages for M10 GPS BBR", 300); delay(750); // Next enable wanted NMEA messages in RAM layer @@ -667,9 +722,9 @@ bool GPS::setup() msglen = makeUBXPacket(0x06, 0x09, sizeof(_message_SAVE_10), _message_SAVE_10); _serial_gps->write(UBXscratch, msglen); if (getACK(0x06, 0x09, 2000) != GNSS_RESPONSE_OK) { - LOG_WARN("Unable to save GNSS module configuration.\n"); + LOG_WARN("Unable to save GNSS module config"); } else { - LOG_INFO("GNSS module configuration saved!\n"); + LOG_INFO("GNSS module configuration saved!"); } } didSerialInit = true; @@ -685,13 +740,14 @@ GPS::~GPS() // we really should unregister our sleep observer notifyDeepSleepObserver.unobserve(¬ifyDeepSleep); } + // Put the GPS hardware into a specified state void GPS::setPowerState(GPSPowerState newState, uint32_t sleepTime) { // Update the stored GPSPowerstate, and create local copies GPSPowerState oldState = powerState; powerState = newState; - LOG_INFO("GPS power state moving from %s to %s\n", getGPSPowerStateString(oldState), getGPSPowerStateString(newState)); + LOG_INFO("GPS power state move from %s to %s", getGPSPowerStateString(oldState), getGPSPowerStateString(newState)); #ifdef HELTEC_MESH_NODE_T114 if ((oldState == GPS_OFF || oldState == GPS_HARDSLEEP) && (newState != GPS_OFF && newState != GPS_HARDSLEEP)) { @@ -755,13 +811,14 @@ void GPS::setPowerState(GPSPowerState newState, uint32_t sleepTime) void GPS::writePinEN(bool on) { // Abort: if conflict with Canned Messages when using Wisblock(?) - if (HW_VENDOR == meshtastic_HardwareModel_RAK4631 && (rotaryEncoderInterruptImpl1 || upDownInterruptImpl1)) + if ((HW_VENDOR == meshtastic_HardwareModel_RAK4631 || HW_VENDOR == meshtastic_HardwareModel_WISMESH_TAP) && + (rotaryEncoderInterruptImpl1 || upDownInterruptImpl1)) return; // Write and log enablePin->set(on); -#ifdef GPS_EXTRAVERBOSE - LOG_DEBUG("Pin EN %s\n", val == HIGH ? "HIGH" : "LOW"); +#ifdef GPS_DEBUG + LOG_DEBUG("Pin EN %s", on == HIGH ? "HI" : "LOW"); #endif } @@ -782,8 +839,8 @@ void GPS::writePinStandby(bool standby) // Write and log pinMode(PIN_GPS_STANDBY, OUTPUT); digitalWrite(PIN_GPS_STANDBY, val); -#ifdef GPS_EXTRAVERBOSE - LOG_DEBUG("Pin STANDBY %s\n", val == HIGH ? "HIGH" : "LOW"); +#ifdef GPS_DEBUG + LOG_DEBUG("Pin STANDBY %s", val == HIGH ? "HI" : "LOW"); #endif #endif } @@ -815,9 +872,8 @@ void GPS::setPowerPMU(bool on) // t-beam v1.1 GNSS power channel on ? PMU->enablePowerOutput(XPOWERS_LDO3) : PMU->disablePowerOutput(XPOWERS_LDO3); } - -#ifdef GPS_EXTRAVERBOSE - LOG_DEBUG("PMU %s\n", on ? "on" : "off"); +#ifdef GPS_DEBUG + LOG_DEBUG("PMU %s", on ? "on" : "off"); #endif #endif } @@ -833,9 +889,6 @@ void GPS::setPowerUBLOX(bool on, uint32_t sleepMs) if (on) { gps->_serial_gps->write(0xFF); clearBuffer(); // This often returns old data, so drop it -#ifdef GPS_EXTRAVERBOSE - LOG_DEBUG("UBLOX: wake\n"); -#endif } // If putting to sleep @@ -852,24 +905,23 @@ void GPS::setPowerUBLOX(bool on, uint32_t sleepMs) if (gnssModel != GNSS_MODEL_UBLOX10) { // Encode the sleep time in millis into the packet for (int i = 0; i < 4; i++) - gps->_message_PMREQ[0 + i] = sleepMs >> (i * 8); + _message_PMREQ[0 + i] = sleepMs >> (i * 8); // Record the message length - msglen = gps->makeUBXPacket(0x02, 0x41, sizeof(_message_PMREQ), gps->_message_PMREQ); + msglen = gps->makeUBXPacket(0x02, 0x41, sizeof(_message_PMREQ), _message_PMREQ); } else { // Encode the sleep time in millis into the packet for (int i = 0; i < 4; i++) - gps->_message_PMREQ_10[4 + i] = sleepMs >> (i * 8); + _message_PMREQ_10[4 + i] = sleepMs >> (i * 8); // Record the message length - msglen = gps->makeUBXPacket(0x02, 0x41, sizeof(_message_PMREQ_10), gps->_message_PMREQ_10); + msglen = gps->makeUBXPacket(0x02, 0x41, sizeof(_message_PMREQ_10), _message_PMREQ_10); } // Send the UBX packet gps->_serial_gps->write(gps->UBXscratch, msglen); - -#ifdef GPS_EXTRAVERBOSE - LOG_DEBUG("UBLOX: sleep for %dmS\n", sleepMs); +#ifdef GPS_DEBUG + LOG_DEBUG("UBLOX: sleep for %dmS", sleepMs); #endif } } @@ -898,7 +950,7 @@ void GPS::down() uint32_t sleepTime = scheduling.msUntilNextSearch(); uint32_t updateInterval = Default::getConfiguredOrDefaultMs(config.position.gps_update_interval); - LOG_DEBUG("%us until next search\n", sleepTime / 1000); + LOG_DEBUG("%us until next search", sleepTime / 1000); // If update interval less than 10 seconds, no attempt to sleep if (updateInterval <= 10 * 1000UL || sleepTime == 0) @@ -916,12 +968,12 @@ void GPS::down() #endif if (softsleepSupported) { - // How long does gps_update_interval need to be, for GPS_HARDSLEEP to become more efficient than GPS_SOFTSLEEP? - // Heuristic equation. A compromise manually fitted to power observations from U-blox NEO-6M and M10050 - // https://www.desmos.com/calculator/6gvjghoumr - // This is not particularly accurate, but probably an impromevement over a single, fixed threshold + // How long does gps_update_interval need to be, for GPS_HARDSLEEP to become more efficient than + // GPS_SOFTSLEEP? Heuristic equation. A compromise manually fitted to power observations from U-blox NEO-6M + // and M10050 https://www.desmos.com/calculator/6gvjghoumr This is not particularly accurate, but probably an + // improvement over a single, fixed threshold uint32_t hardsleepThreshold = (2750 * pow(predictedSearchDuration / 1000, 1.22)); - LOG_DEBUG("gps_update_interval >= %us needed to justify hardsleep\n", hardsleepThreshold / 1000); + LOG_DEBUG("gps_update_interval >= %us needed to justify hardsleep", hardsleepThreshold / 1000); // If update interval too short: softsleep (if supported by hardware) if (updateInterval < hardsleepThreshold) { @@ -940,8 +992,7 @@ void GPS::publishUpdate() shouldPublish = false; // In debug logs, identify position by @timestamp:stage (stage 2 = publish) - LOG_DEBUG("publishing pos@%x:2, hasVal=%d, Sats=%d, GPSlock=%d\n", p.timestamp, hasValidLocation, p.sats_in_view, - hasLock()); + LOG_DEBUG("Publish pos@%x:2, hasVal=%d, Sats=%d, GPSlock=%d", p.timestamp, hasValidLocation, p.sats_in_view, hasLock()); // Notify any status instances that are observing us const meshtastic::GPSStatus status = meshtastic::GPSStatus(hasValidLocation, isConnected(), isPowerSaving(), p); @@ -956,7 +1007,7 @@ int32_t GPS::runOnce() { if (!GPSInitFinished) { if (!_serial_gps || config.position.gps_mode == meshtastic_Config_PositionConfig_GpsMode_NOT_PRESENT) { - LOG_INFO("GPS set to not-present. Skipping probe.\n"); + LOG_INFO("GPS set to not-present. Skip probe"); return disable(); } if (!setup()) @@ -968,13 +1019,14 @@ int32_t GPS::runOnce() } // ONCE we will factory reset the GPS for bug #327 if (!devicestate.did_gps_reset) { - LOG_WARN("GPS FactoryReset requested\n"); + LOG_WARN("GPS FactoryReset requested"); if (gps->factoryReset()) { // If we don't succeed try again next time devicestate.did_gps_reset = true; nodeDB->saveToDisk(SEGMENT_DEVICESTATE); } } GPSInitFinished = true; + publishUpdate(); } // Repeaters have no need for GPS @@ -991,7 +1043,7 @@ int32_t GPS::runOnce() GNSS_MODEL_UBLOX10)) { // reset the GPS on next bootup if (devicestate.did_gps_reset && scheduling.elapsedSearchMs() > 60 * 1000UL && !hasFlow()) { - LOG_DEBUG("GPS is not communicating, trying factory reset on next bootup.\n"); + LOG_DEBUG("GPS is not found, try factory reset on next boot"); devicestate.did_gps_reset = false; nodeDB->saveToDisk(SEGMENT_DEVICESTATE); return disable(); // Stop the GPS thread as it can do nothing useful until next reboot. @@ -1001,7 +1053,7 @@ int32_t GPS::runOnce() // At least one GPS has a bad habit of losing its mind from time to time if (rebootsSeen > 2) { rebootsSeen = 0; - LOG_DEBUG("Would normally factoryReset()\n"); + LOG_DEBUG("Would normally factoryReset()"); // gps->factoryReset(); } @@ -1018,23 +1070,22 @@ int32_t GPS::runOnce() bool gotLoc = lookForLocation(); if (gotLoc && !hasValidLocation) { // declare that we have location ASAP - LOG_DEBUG("hasValidLocation RISING EDGE\n"); + LOG_DEBUG("hasValidLocation RISING EDGE"); hasValidLocation = true; shouldPublish = true; } bool tooLong = scheduling.searchedTooLong(); if (tooLong) - LOG_WARN("Couldn't publish a valid location: didn't get a GPS lock in time.\n"); + LOG_WARN("Couldn't publish a valid location: didn't get a GPS lock in time"); // Once we get a location we no longer desperately want an update - // LOG_DEBUG("gotLoc %d, tooLong %d, gotTime %d\n", gotLoc, tooLong, gotTime); if ((gotLoc && gotTime) || tooLong) { if (tooLong) { // we didn't get a location during this ack window, therefore declare loss of lock if (hasValidLocation) { - LOG_DEBUG("hasValidLocation FALLING EDGE\n"); + LOG_DEBUG("hasValidLocation FALLING EDGE"); } p = meshtastic_Position_init_default; hasValidLocation = false; @@ -1066,22 +1117,24 @@ void GPS::clearBuffer() /// Prepare the GPS for the cpu entering deep or light sleep, expect to be gone for at least 100s of msecs int GPS::prepareDeepSleep(void *unused) { - LOG_INFO("GPS deep sleep!\n"); + LOG_INFO("GPS deep sleep!"); disable(); return 0; } -const char *PROBE_MESSAGE = "Trying %s (%s)...\n"; -const char *DETECTED_MESSAGE = "%s detected, using %s Module\n"; +static const char *PROBE_MESSAGE = "Trying %s (%s)..."; +static const char *DETECTED_MESSAGE = "%s detected, using %s Module"; #define PROBE_SIMPLE(CHIP, TOWRITE, RESPONSE, DRIVER, TIMEOUT, ...) \ - LOG_DEBUG(PROBE_MESSAGE, TOWRITE, CHIP); \ - clearBuffer(); \ - _serial_gps->write(TOWRITE "\r\n"); \ - if (getACK(RESPONSE, TIMEOUT) == GNSS_RESPONSE_OK) { \ - LOG_INFO(DETECTED_MESSAGE, CHIP, #DRIVER); \ - return DRIVER; \ - } + do { \ + LOG_DEBUG(PROBE_MESSAGE, TOWRITE, CHIP); \ + clearBuffer(); \ + _serial_gps->write(TOWRITE "\r\n"); \ + if (getACK(RESPONSE, TIMEOUT) == GNSS_RESPONSE_OK) { \ + LOG_INFO(DETECTED_MESSAGE, CHIP, #DRIVER); \ + return DRIVER; \ + } \ + } while (0) GnssModel_t GPS::probe(int serialSpeed) { @@ -1094,12 +1147,12 @@ GnssModel_t GPS::probe(int serialSpeed) _serial_gps->begin(serialSpeed); #else if (_serial_gps->baudRate() != serialSpeed) { - LOG_DEBUG("Setting Baud to %i\n", serialSpeed); + LOG_DEBUG("Set Baud to %i", serialSpeed); _serial_gps->updateBaudRate(serialSpeed); } #endif - memset(&info, 0, sizeof(struct uBloxGnssModelInfo)); + memset(&ublox_info, 0, sizeof(ublox_info)); uint8_t buffer[768] = {0}; delay(100); @@ -1135,6 +1188,7 @@ GnssModel_t GPS::probe(int serialSpeed) delay(20); PROBE_SIMPLE("L76B", "$PMTK605*31", "Quectel-L76B", GNSS_MODEL_MTK_L76B, 500); + PROBE_SIMPLE("PA1616S", "$PMTK605*31", "1616S", GNSS_MODEL_MTK_PA1616S, 500); uint8_t cfg_rate[] = {0xB5, 0x62, 0x06, 0x08, 0x00, 0x00, 0x00, 0x00}; UBXChecksum(cfg_rate, sizeof(cfg_rate)); @@ -1143,10 +1197,10 @@ GnssModel_t GPS::probe(int serialSpeed) // Check that the returned response class and message ID are correct GPS_RESPONSE response = getACK(0x06, 0x08, 750); if (response == GNSS_RESPONSE_NONE) { - LOG_WARN("Failed to find GNSS Module (baudrate %d)\n", serialSpeed); + LOG_WARN("No GNSS Module (baudrate %d)", serialSpeed); return GNSS_MODEL_UNKNOWN; } else if (response == GNSS_RESPONSE_FRAME_ERRORS) { - LOG_INFO("UBlox Frame Errors (baudrate %d)\n", serialSpeed); + LOG_INFO("UBlox Frame Errors (baudrate %d)", serialSpeed); } memset(buffer, 0, sizeof(buffer)); @@ -1163,72 +1217,71 @@ GnssModel_t GPS::probe(int serialSpeed) uint16_t len = getACK(buffer, sizeof(buffer), 0x0A, 0x04, 1200); if (len) { - // LOG_DEBUG("monver reply size = %d\n", len); uint16_t position = 0; for (int i = 0; i < 30; i++) { - info.swVersion[i] = buffer[position]; + ublox_info.swVersion[i] = buffer[position]; position++; } for (int i = 0; i < 10; i++) { - info.hwVersion[i] = buffer[position]; + ublox_info.hwVersion[i] = buffer[position]; position++; } while (len >= position + 30) { for (int i = 0; i < 30; i++) { - info.extension[info.extensionNo][i] = buffer[position]; + ublox_info.extension[ublox_info.extensionNo][i] = buffer[position]; position++; } - info.extensionNo++; - if (info.extensionNo > 9) + ublox_info.extensionNo++; + if (ublox_info.extensionNo > 9) break; } - LOG_DEBUG("Module Info : \n"); - LOG_DEBUG("Soft version: %s\n", info.swVersion); - LOG_DEBUG("Hard version: %s\n", info.hwVersion); - LOG_DEBUG("Extensions:%d\n", info.extensionNo); - for (int i = 0; i < info.extensionNo; i++) { - LOG_DEBUG(" %s\n", info.extension[i]); + LOG_DEBUG("Module Info : "); + LOG_DEBUG("Soft version: %s", ublox_info.swVersion); + LOG_DEBUG("Hard version: %s", ublox_info.hwVersion); + LOG_DEBUG("Extensions:%d", ublox_info.extensionNo); + for (int i = 0; i < ublox_info.extensionNo; i++) { + LOG_DEBUG(" %s", ublox_info.extension[i]); } memset(buffer, 0, sizeof(buffer)); // tips: extensionNo field is 0 on some 6M GNSS modules - for (int i = 0; i < info.extensionNo; ++i) { - if (!strncmp(info.extension[i], "MOD=", 4)) { - strncpy((char *)buffer, &(info.extension[i][4]), sizeof(buffer)); - } else if (!strncmp(info.extension[i], "PROTVER", 7)) { + for (int i = 0; i < ublox_info.extensionNo; ++i) { + if (!strncmp(ublox_info.extension[i], "MOD=", 4)) { + strncpy((char *)buffer, &(ublox_info.extension[i][4]), sizeof(buffer)); + } else if (!strncmp(ublox_info.extension[i], "PROTVER", 7)) { char *ptr = nullptr; memset(buffer, 0, sizeof(buffer)); - strncpy((char *)buffer, &(info.extension[i][8]), sizeof(buffer)); - LOG_DEBUG("Protocol Version:%s\n", (char *)buffer); + strncpy((char *)buffer, &(ublox_info.extension[i][8]), sizeof(buffer)); + LOG_DEBUG("Protocol Version:%s", (char *)buffer); if (strlen((char *)buffer)) { - uBloxProtocolVersion = strtoul((char *)buffer, &ptr, 10); - LOG_DEBUG("ProtVer=%d\n", uBloxProtocolVersion); + ublox_info.protocol_version = strtoul((char *)buffer, &ptr, 10); + LOG_DEBUG("ProtVer=%d", ublox_info.protocol_version); } else { - uBloxProtocolVersion = 0; + ublox_info.protocol_version = 0; } } } - if (strncmp(info.hwVersion, "00040007", 8) == 0) { + if (strncmp(ublox_info.hwVersion, "00040007", 8) == 0) { LOG_INFO(DETECTED_MESSAGE, "U-blox 6", "6"); return GNSS_MODEL_UBLOX6; - } else if (strncmp(info.hwVersion, "00070000", 8) == 0) { + } else if (strncmp(ublox_info.hwVersion, "00070000", 8) == 0) { LOG_INFO(DETECTED_MESSAGE, "U-blox 7", "7"); return GNSS_MODEL_UBLOX7; - } else if (strncmp(info.hwVersion, "00080000", 8) == 0) { + } else if (strncmp(ublox_info.hwVersion, "00080000", 8) == 0) { LOG_INFO(DETECTED_MESSAGE, "U-blox 8", "8"); return GNSS_MODEL_UBLOX8; - } else if (strncmp(info.hwVersion, "00190000", 8) == 0) { + } else if (strncmp(ublox_info.hwVersion, "00190000", 8) == 0) { LOG_INFO(DETECTED_MESSAGE, "U-blox 9", "9"); return GNSS_MODEL_UBLOX9; - } else if (strncmp(info.hwVersion, "000A0000", 8) == 0) { + } else if (strncmp(ublox_info.hwVersion, "000A0000", 8) == 0) { LOG_INFO(DETECTED_MESSAGE, "U-blox 10", "10"); return GNSS_MODEL_UBLOX10; } } - LOG_WARN("Failed to find GNSS Module (baudrate %d)\n", serialSpeed); + LOG_WARN("No GNSS Module (baudrate %d)", serialSpeed); return GNSS_MODEL_UNKNOWN; } @@ -1271,10 +1324,12 @@ GPS *GPS::createGps() if (!GPS_EN_ACTIVE) { // Need to invert the pin before hardware new GpioNotTransformer( - virtPin, p); // We just leave this created object on the heap so it can stay watching virtPin and driving en_gpio + virtPin, + p); // We just leave this created object on the heap so it can stay watching virtPin and driving en_gpio } else { new GpioUnaryTransformer( - virtPin, p); // We just leave this created object on the heap so it can stay watching virtPin and driving en_gpio + virtPin, + p); // We just leave this created object on the heap so it can stay watching virtPin and driving en_gpio } } @@ -1289,7 +1344,7 @@ GPS *GPS::createGps() // see NMEAGPS.h gsafixtype.begin(reader, NMEA_MSG_GXGSA, 2); gsapdop.begin(reader, NMEA_MSG_GXGSA, 15); - LOG_DEBUG("Using " NMEA_MSG_GXGSA " for 3DFIX and PDOP\n"); + LOG_DEBUG("Use " NMEA_MSG_GXGSA " for 3DFIX and PDOP"); #endif // Make sure the GPS is awake before performing any init. @@ -1310,8 +1365,8 @@ GPS *GPS::createGps() // ESP32 has a special set of parameters vs other arduino ports #if defined(ARCH_ESP32) - LOG_DEBUG("Using GPIO%d for GPS RX\n", new_gps->rx_gpio); - LOG_DEBUG("Using GPIO%d for GPS TX\n", new_gps->tx_gpio); + LOG_DEBUG("Use GPIO%d for GPS RX", new_gps->rx_gpio); + LOG_DEBUG("Use GPIO%d for GPS TX", new_gps->tx_gpio); _serial_gps->begin(GPS_BAUDRATE, SERIAL_8N1, new_gps->rx_gpio, new_gps->tx_gpio); #elif defined(ARCH_RP2040) _serial_gps->setFIFOSize(256); @@ -1347,33 +1402,29 @@ bool GPS::factoryReset() 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x1C, 0xA2}; _serial_gps->write(_message_reset1, sizeof(_message_reset1)); if (getACK(0x05, 0x01, 10000)) { - LOG_INFO(ACK_SUCCESS_MESSAGE); + LOG_DEBUG(ACK_SUCCESS_MESSAGE); } delay(100); byte _message_reset2[] = {0xB5, 0x62, 0x06, 0x09, 0x0D, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x1B, 0xA1}; _serial_gps->write(_message_reset2, sizeof(_message_reset2)); if (getACK(0x05, 0x01, 10000)) { - LOG_INFO(ACK_SUCCESS_MESSAGE); + LOG_DEBUG(ACK_SUCCESS_MESSAGE); } delay(100); byte _message_reset3[] = {0xB5, 0x62, 0x06, 0x09, 0x0D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x03, 0x1D, 0xB3}; _serial_gps->write(_message_reset3, sizeof(_message_reset3)); if (getACK(0x05, 0x01, 10000)) { - LOG_INFO(ACK_SUCCESS_MESSAGE); + LOG_DEBUG(ACK_SUCCESS_MESSAGE); } - // Reset device ram to COLDSTART state - // byte _message_CFG_RST_COLDSTART[] = {0xB5, 0x62, 0x06, 0x04, 0x04, 0x00, 0xFF, 0xB9, 0x00, 0x00, 0xC6, 0x8B}; - // _serial_gps->write(_message_CFG_RST_COLDSTART, sizeof(_message_CFG_RST_COLDSTART)); - // delay(1000); } else if (gnssModel == GNSS_MODEL_MTK) { // send the CAS10 to perform a factory restart of the device (and other device that support PCAS statements) - LOG_INFO("GNSS Factory Reset via PCAS10,3\n"); + LOG_INFO("GNSS Factory Reset via PCAS10,3"); _serial_gps->write("$PCAS10,3*1F\r\n"); delay(100); } else if (gnssModel == GNSS_MODEL_ATGM336H) { - LOG_INFO("Factory Reset via CAS-CFG-RST\n"); + LOG_INFO("Factory Reset via CAS-CFG-RST"); uint8_t msglen = makeCASPacket(0x06, 0x02, sizeof(_message_CAS_CFG_RST_FACTORY), _message_CAS_CFG_RST_FACTORY); _serial_gps->write(UBXscratch, msglen); delay(100); @@ -1382,8 +1433,8 @@ bool GPS::factoryReset() _serial_gps->write("$PMTK104*37\r\n"); // No PMTK_ACK for this command. delay(100); - // send the UBLOX Factory Reset Command regardless of detect state, something is very wrong, just assume it's UBLOX. - // Factory Reset + // send the UBLOX Factory Reset Command regardless of detect state, something is very wrong, just assume it's + // UBLOX. Factory Reset byte _message_reset[] = {0xB5, 0x62, 0x06, 0x09, 0x0D, 0x00, 0xFF, 0xFB, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x17, 0x2B, 0x7E}; _serial_gps->write(_message_reset, sizeof(_message_reset)); @@ -1422,8 +1473,8 @@ bool GPS::lookForTime() auto d = reader.date; if (ti.isValid() && d.isValid()) { // Note: we don't check for updated, because we'll only be called if needed /* Convert to unix time -The Unix epoch (or Unix time or POSIX time or Unix timestamp) is the number of seconds that have elapsed since January 1, 1970 -(midnight UTC/GMT), not counting leap seconds (in ISO 8601: 1970-01-01T00:00:00Z). +The Unix epoch (or Unix time or POSIX time or Unix timestamp) is the number of seconds that have elapsed since January 1, +1970 (midnight UTC/GMT), not counting leap seconds (in ISO 8601: 1970-01-01T00:00:00Z). */ struct tm t; t.tm_sec = ti.second() + round(ti.age() / 1000); @@ -1434,7 +1485,7 @@ The Unix epoch (or Unix time or POSIX time or Unix timestamp) is the number of s t.tm_year = d.year() - 1900; t.tm_isdst = false; if (t.tm_mon > -1) { - LOG_DEBUG("NMEA GPS time %02d-%02d-%02d %02d:%02d:%02d age %d\n", d.year(), d.month(), t.tm_mday, t.tm_hour, t.tm_min, + LOG_DEBUG("NMEA GPS time %02d-%02d-%02d %02d:%02d:%02d age %d", d.year(), d.month(), t.tm_mday, t.tm_hour, t.tm_min, t.tm_sec, ti.age()); perhapsSetRTC(RTCQualityGPS, t); return true; @@ -1478,7 +1529,7 @@ bool GPS::lookForLocation() #ifndef TINYGPS_OPTION_NO_STATISTICS if (reader.failedChecksum() > lastChecksumFailCount) { - LOG_WARN("%u new GPS checksum failures, for a total of %u.\n", reader.failedChecksum() - lastChecksumFailCount, + LOG_WARN("%u new GPS checksum failures, for a total of %u", reader.failedChecksum() - lastChecksumFailCount, reader.failedChecksum()); lastChecksumFailCount = reader.failedChecksum(); } @@ -1486,22 +1537,21 @@ bool GPS::lookForLocation() #ifndef TINYGPS_OPTION_NO_CUSTOM_FIELDS fixType = atoi(gsafixtype.value()); // will set to zero if no data - // LOG_DEBUG("FIX QUAL=%d, TYPE=%d\n", fixQual, fixType); #endif // check if GPS has an acceptable lock if (!hasLock()) return false; -#ifdef GPS_EXTRAVERBOSE - LOG_DEBUG("AGE: LOC=%d FIX=%d DATE=%d TIME=%d\n", reader.location.age(), +#ifdef GPS_DEBUG + LOG_DEBUG("AGE: LOC=%d FIX=%d DATE=%d TIME=%d", reader.location.age(), #ifndef TINYGPS_OPTION_NO_CUSTOM_FIELDS gsafixtype.age(), #else 0, #endif reader.date.age(), reader.time.age()); -#endif // GPS_EXTRAVERBOSE +#endif // GPS_DEBUG // Is this a new point or are we re-reading the previous one? if (!reader.location.isUpdated() && !reader.altitude.isUpdated()) @@ -1515,7 +1565,7 @@ bool GPS::lookForLocation() (gsafixtype.age() < GPS_SOL_EXPIRY_MS) && #endif (reader.time.age() < GPS_SOL_EXPIRY_MS) && (reader.date.age() < GPS_SOL_EXPIRY_MS))) { - LOG_WARN("SOME data is TOO OLD: LOC %u, TIME %u, DATE %u\n", reader.location.age(), reader.time.age(), reader.date.age()); + LOG_WARN("SOME data is TOO OLD: LOC %u, TIME %u, DATE %u", reader.location.age(), reader.time.age(), reader.date.age()); return false; } @@ -1524,14 +1574,14 @@ bool GPS::lookForLocation() // Bail out EARLY to avoid overwriting previous good data (like #857) if (toDegInt(loc.lat) > 900000000) { -#ifdef GPS_EXTRAVERBOSE - LOG_DEBUG("Bail out EARLY on LAT %i\n", toDegInt(loc.lat)); +#ifdef GPS_DEBUG + LOG_DEBUG("Bail out EARLY on LAT %i", toDegInt(loc.lat)); #endif return false; } if (toDegInt(loc.lng) > 1800000000) { -#ifdef GPS_EXTRAVERBOSE - LOG_DEBUG("Bail out EARLY on LNG %i\n", toDegInt(loc.lng)); +#ifdef GPS_DEBUG + LOG_DEBUG("Bail out EARLY on LNG %i", toDegInt(loc.lng)); #endif return false; } @@ -1542,7 +1592,6 @@ bool GPS::lookForLocation() #ifndef TINYGPS_OPTION_NO_CUSTOM_FIELDS p.HDOP = reader.hdop.value(); p.PDOP = TinyGPSPlus::parseDecimal(gsapdop.value()); - // LOG_DEBUG("PDOP=%d, HDOP=%d\n", p.PDOP, p.HDOP); #else // FIXME! naive PDOP emulation (assumes VDOP==HDOP) // correct formula is PDOP = SQRT(HDOP^2 + VDOP^2) @@ -1552,7 +1601,7 @@ bool GPS::lookForLocation() // Discard incomplete or erroneous readings if (reader.hdop.value() == 0) { - LOG_WARN("BOGUS hdop.value() REJECTED: %d\n", reader.hdop.value()); + LOG_WARN("BOGUS hdop.value() REJECTED: %d", reader.hdop.value()); return false; } @@ -1589,7 +1638,7 @@ bool GPS::lookForLocation() p.ground_track = reader.course.value() * 1e3; // Scale the heading (in degrees * 10^-2) to match the expected degrees * 10^-5 } else { - LOG_WARN("BOGUS course.value() REJECTED: %d\n", reader.course.value()); + LOG_WARN("BOGUS course.value() REJECTED: %d", reader.course.value()); } } @@ -1623,24 +1672,25 @@ bool GPS::whileActive() { unsigned int charsInBuf = 0; bool isValid = false; +#ifdef GPS_DEBUG + std::string debugmsg = ""; +#endif if (powerState != GPS_ACTIVE) { clearBuffer(); return false; } #ifdef SERIAL_BUFFER_SIZE if (_serial_gps->available() >= SERIAL_BUFFER_SIZE - 1) { - LOG_WARN("GPS Buffer full with %u bytes waiting. Flushing to avoid corruption.\n", _serial_gps->available()); + LOG_WARN("GPS Buffer full with %u bytes waiting. Flush to avoid corruption", _serial_gps->available()); clearBuffer(); } #endif - // if (_serial_gps->available() > 0) - // LOG_DEBUG("GPS Bytes Waiting: %u\n", _serial_gps->available()); // First consume any chars that have piled up at the receiver while (_serial_gps->available() > 0) { int c = _serial_gps->read(); UBXscratch[charsInBuf] = c; #ifdef GPS_DEBUG - LOG_DEBUG("%c", c); + debugmsg += vformat("%c", (c >= 32 && c <= 126) ? c : '.'); #endif isValid |= reader.encode(c); if (charsInBuf > sizeof(UBXscratch) - 10 || c == '\r') { @@ -1652,6 +1702,11 @@ bool GPS::whileActive() charsInBuf++; } } +#ifdef GPS_DEBUG + if (debugmsg != "") { + LOG_DEBUG(debugmsg.c_str()); + } +#endif return isValid; } void GPS::enable() @@ -1679,17 +1734,19 @@ void GPS::toggleGpsMode() { if (config.position.gps_mode == meshtastic_Config_PositionConfig_GpsMode_ENABLED) { config.position.gps_mode = meshtastic_Config_PositionConfig_GpsMode_DISABLED; - LOG_INFO("User toggled GpsMode. Now DISABLED.\n"); + LOG_INFO("User toggled GpsMode. Now DISABLED"); + playGPSDisableBeep(); #ifdef GNSS_AIROHA if (powerState == GPS_ACTIVE) { - LOG_DEBUG("User power Off GPS\n"); + LOG_DEBUG("User power Off GPS"); digitalWrite(PIN_GPS_EN, LOW); } #endif disable(); } else if (config.position.gps_mode == meshtastic_Config_PositionConfig_GpsMode_DISABLED) { config.position.gps_mode = meshtastic_Config_PositionConfig_GpsMode_ENABLED; - LOG_INFO("User toggled GpsMode. Now ENABLED\n"); + LOG_INFO("User toggled GpsMode. Now ENABLED"); + playGPSEnableBeep(); enable(); } } diff --git a/src/gps/GPS.h b/src/gps/GPS.h index 6222881bc..15fc50fe7 100644 --- a/src/gps/GPS.h +++ b/src/gps/GPS.h @@ -16,13 +16,6 @@ #define GPS_EN_ACTIVE 1 #endif -struct uBloxGnssModelInfo { - char swVersion[30]; - char hwVersion[10]; - uint8_t extensionNo; - char extension[10][30]; -}; - typedef enum { GNSS_MODEL_ATGM336H, GNSS_MODEL_MTK, @@ -34,6 +27,7 @@ typedef enum { GNSS_MODEL_UC6580, GNSS_MODEL_UNKNOWN, GNSS_MODEL_MTK_L76B, + GNSS_MODEL_MTK_PA1616S, GNSS_MODEL_AG3335, GNSS_MODEL_AG3352 } GnssModel_t; @@ -53,9 +47,6 @@ enum GPSPowerState : uint8_t { GPS_OFF // Powered off indefinitely }; -// Generate a string representation of DOP -const char *getDOPString(uint32_t dop); - /** * A gps class that only reads from the GPS periodically and keeps the gps powered down except when reading * @@ -63,101 +54,7 @@ const char *getDOPString(uint32_t dop); */ class GPS : private concurrency::OSThread { - TinyGPSPlus reader; - uint8_t fixQual = 0; // fix quality from GPGGA - uint32_t lastChecksumFailCount = 0; - -#ifndef TINYGPS_OPTION_NO_CUSTOM_FIELDS - // (20210908) TinyGps++ can only read the GPGSA "FIX TYPE" field - // via optional feature "custom fields", currently disabled (bug #525) - TinyGPSCustom gsafixtype; // custom extract fix type from GPGSA - TinyGPSCustom gsapdop; // custom extract PDOP from GPGSA - uint8_t fixType = 0; // fix type from GPGSA -#endif - private: - const int serialSpeeds[6] = {9600, 4800, 38400, 57600, 115200, 9600}; - uint32_t lastWakeStartMsec = 0, lastSleepStartMsec = 0, lastFixStartMsec = 0; - uint32_t rx_gpio = 0; - uint32_t tx_gpio = 0; - - int speedSelect = 0; - int probeTries = 2; - - /** - * hasValidLocation - indicates that the position variables contain a complete - * GPS location, valid and fresh (< gps_update_interval + position_broadcast_secs) - */ - bool hasValidLocation = false; // default to false, until we complete our first read - - bool isInPowersave = false; - - bool shouldPublish = false; // If we've changed GPS state, this will force a publish the next loop() - - bool hasGPS = false; // Do we have a GPS we are talking to - - bool GPSInitFinished = false; // Init thread finished? - bool GPSInitStarted = false; // Init thread finished? - - GPSPowerState powerState = GPS_OFF; // GPS_ACTIVE if we want a location right now - - uint8_t numSatellites = 0; - - CallbackObserver notifyDeepSleepObserver = CallbackObserver(this, &GPS::prepareDeepSleep); - public: - /** If !NULL we will use this serial port to construct our GPS */ -#if defined(ARCH_RP2040) - static SerialUART *_serial_gps; -#else - static HardwareSerial *_serial_gps; -#endif - static uint8_t _message_PMREQ[]; - static uint8_t _message_PMREQ_10[]; - static const uint8_t _message_CFG_RXM_PSM[]; - static const uint8_t _message_CFG_RXM_ECO[]; - static const uint8_t _message_CFG_PM2[]; - static const uint8_t _message_GNSS_7[]; - static const uint8_t _message_GNSS_8[]; - static const uint8_t _message_JAM_6_7[]; - static const uint8_t _message_JAM_8[]; - static const uint8_t _message_NAVX5[]; - static const uint8_t _message_NAVX5_8[]; - static const uint8_t _message_NMEA[]; - static const uint8_t _message_DISABLE_TXT_INFO[]; - static const uint8_t _message_1HZ[]; - static const uint8_t _message_GLL[]; - static const uint8_t _message_GSA[]; - static const uint8_t _message_GSV[]; - static const uint8_t _message_VTG[]; - static const uint8_t _message_RMC[]; - static const uint8_t _message_AID[]; - static const uint8_t _message_GGA[]; - static const uint8_t _message_PMS[]; - static const uint8_t _message_SAVE[]; - static const uint8_t _message_SAVE_10[]; - - // VALSET Commands for M10 - static const uint8_t _message_VALSET_PM[]; - static const uint8_t _message_VALSET_PM_RAM[]; - static const uint8_t _message_VALSET_PM_BBR[]; - static const uint8_t _message_VALSET_ITFM_RAM[]; - static const uint8_t _message_VALSET_ITFM_BBR[]; - static const uint8_t _message_VALSET_DISABLE_NMEA_RAM[]; - static const uint8_t _message_VALSET_DISABLE_NMEA_BBR[]; - static const uint8_t _message_VALSET_DISABLE_TXT_INFO_RAM[]; - static const uint8_t _message_VALSET_DISABLE_TXT_INFO_BBR[]; - static const uint8_t _message_VALSET_ENABLE_NMEA_RAM[]; - static const uint8_t _message_VALSET_ENABLE_NMEA_BBR[]; - static const uint8_t _message_VALSET_DISABLE_SBAS_RAM[]; - static const uint8_t _message_VALSET_DISABLE_SBAS_BBR[]; - - // CASIC commands for ATGM336H - static const uint8_t _message_CAS_CFG_RST_FACTORY[]; - static const uint8_t _message_CAS_CFG_NAVX_CONF[]; - static const uint8_t _message_CAS_CFG_RATE_1HZ[]; - - const char *ACK_SUCCESS_MESSAGE = "Get ack success!\n"; - meshtastic_Position p = meshtastic_Position_init_default; /** This is normally bound to config.position.gps_en_gpio but some rare boards (like heltec tracker) need more advanced @@ -167,8 +64,6 @@ class GPS : private concurrency::OSThread */ GpioVirtPin *enablePin = NULL; - GPS() : concurrency::OSThread("GPS") {} - virtual ~GPS(); /** We will notify this observable anytime GPS state has changed meaningfully */ @@ -205,21 +100,6 @@ class GPS : private concurrency::OSThread // Empty the input buffer as quickly as possible void clearBuffer(); - // Create a ublox packet for editing in memory - uint8_t makeUBXPacket(uint8_t class_id, uint8_t msg_id, uint8_t payload_size, const uint8_t *msg); - uint8_t makeCASPacket(uint8_t class_id, uint8_t msg_id, uint8_t payload_size, const uint8_t *msg); - - // scratch space for creating ublox packets - uint8_t UBXscratch[250] = {0}; - - int rebootsSeen = 0; - - int getACK(uint8_t *buffer, uint16_t size, uint8_t requestedClass, uint8_t requestedID, uint32_t waitMillis); - GPS_RESPONSE getACK(uint8_t c, uint8_t i, uint32_t waitMillis); - GPS_RESPONSE getACK(const char *message, uint32_t waitMillis); - - GPS_RESPONSE getACKCas(uint8_t class_id, uint8_t msg_id, uint32_t waitMillis); - virtual bool factoryReset(); // Creates an instance of the GPS class. @@ -232,20 +112,8 @@ class GPS : private concurrency::OSThread // Let the GPS hardware save power between updates void down(); - protected: - /** - * Perform any processing that should be done only while the GPS is awake and looking for a fix. - * Override this method to check for new locations - * - * @return true if we've acquired a time - */ - - /** - * Perform any processing that should be done only while the GPS is awake and looking for a fix. - * Override this method to check for new locations - * - * @return true if we've acquired a new location - */ + private: + GPS() : concurrency::OSThread("GPS") {} /// Record that we have a GPS void setConnected(); @@ -272,15 +140,74 @@ class GPS : private concurrency::OSThread */ virtual bool lookForLocation(); - private: + GnssModel_t gnssModel = GNSS_MODEL_UNKNOWN; + + TinyGPSPlus reader; + uint8_t fixQual = 0; // fix quality from GPGGA + uint32_t lastChecksumFailCount = 0; + +#ifndef TINYGPS_OPTION_NO_CUSTOM_FIELDS + // (20210908) TinyGps++ can only read the GPGSA "FIX TYPE" field + // via optional feature "custom fields", currently disabled (bug #525) + TinyGPSCustom gsafixtype; // custom extract fix type from GPGSA + TinyGPSCustom gsapdop; // custom extract PDOP from GPGSA + uint8_t fixType = 0; // fix type from GPGSA +#endif + + uint32_t lastWakeStartMsec = 0, lastSleepStartMsec = 0, lastFixStartMsec = 0; + uint32_t rx_gpio = 0; + uint32_t tx_gpio = 0; + + uint8_t speedSelect = 0; + uint8_t probeTries = 0; + + /** + * hasValidLocation - indicates that the position variables contain a complete + * GPS location, valid and fresh (< gps_update_interval + position_broadcast_secs) + */ + bool hasValidLocation = false; // default to false, until we complete our first read + + bool isInPowersave = false; + + bool shouldPublish = false; // If we've changed GPS state, this will force a publish the next loop() + + bool hasGPS = false; // Do we have a GPS we are talking to + + bool GPSInitFinished = false; // Init thread finished? + bool GPSInitStarted = false; // Init thread finished? + + GPSPowerState powerState = GPS_OFF; // GPS_ACTIVE if we want a location right now + + uint8_t numSatellites = 0; + + CallbackObserver notifyDeepSleepObserver = CallbackObserver(this, &GPS::prepareDeepSleep); + + /** If !NULL we will use this serial port to construct our GPS */ +#if defined(ARCH_RP2040) + static SerialUART *_serial_gps; +#else + static HardwareSerial *_serial_gps; +#endif + + // Create a ublox packet for editing in memory + uint8_t makeUBXPacket(uint8_t class_id, uint8_t msg_id, uint8_t payload_size, const uint8_t *msg); + uint8_t makeCASPacket(uint8_t class_id, uint8_t msg_id, uint8_t payload_size, const uint8_t *msg); + + // scratch space for creating ublox packets + uint8_t UBXscratch[250] = {0}; + + int rebootsSeen = 0; + + int getACK(uint8_t *buffer, uint16_t size, uint8_t requestedClass, uint8_t requestedID, uint32_t waitMillis); + GPS_RESPONSE getACK(uint8_t c, uint8_t i, uint32_t waitMillis); + GPS_RESPONSE getACK(const char *message, uint32_t waitMillis); + + GPS_RESPONSE getACKCas(uint8_t class_id, uint8_t msg_id, uint32_t waitMillis); + /// Prepare the GPS for the cpu entering deep sleep, expect to be gone for at least 100s of msecs /// always returns 0 to indicate okay to sleep int prepareDeepSleep(void *unused); - // Calculate checksum - void UBXChecksum(uint8_t *message, size_t length); - void CASChecksum(uint8_t *message, size_t length); - /** Set power with EN pin, if relevant */ void writePinEN(bool on); @@ -309,11 +236,6 @@ class GPS : private concurrency::OSThread // delay counter to allow more sats before fixed position stops GPS thread uint8_t fixeddelayCtr = 0; - - const char *powerStateToString(); - - protected: - GnssModel_t gnssModel = GNSS_MODEL_UNKNOWN; }; extern GPS *gps; diff --git a/src/gps/GPSUpdateScheduling.cpp b/src/gps/GPSUpdateScheduling.cpp index 949ef6039..5eaf7a8ba 100644 --- a/src/gps/GPSUpdateScheduling.cpp +++ b/src/gps/GPSUpdateScheduling.cpp @@ -13,7 +13,7 @@ void GPSUpdateScheduling::informSearching() void GPSUpdateScheduling::informGotLock() { searchEndedMs = millis(); - LOG_DEBUG("Took %us to get lock\n", (searchEndedMs - searchStartedMs) / 1000); + LOG_DEBUG("Took %us to get lock", (searchEndedMs - searchStartedMs) / 1000); updateLockTimePrediction(); } @@ -49,7 +49,7 @@ uint32_t GPSUpdateScheduling::msUntilNextSearch() } // How long have we already been searching? -// Used to abort a search in progress, if it runs unnaceptably long +// Used to abort a search in progress, if it runs unacceptably long uint32_t GPSUpdateScheduling::elapsedSearchMs() { // If searching @@ -70,9 +70,9 @@ bool GPSUpdateScheduling::isUpdateDue() // Have we been searching for a GPS position for too long? bool GPSUpdateScheduling::searchedTooLong() { - uint32_t maxSearchMs = - Default::getConfiguredOrDefaultMs(config.position.position_broadcast_secs, default_broadcast_interval_secs); - + uint32_t minimumOrConfiguredSecs = + Default::getConfiguredOrMinimumValue(config.position.position_broadcast_secs, default_broadcast_interval_secs); + uint32_t maxSearchMs = Default::getConfiguredOrDefaultMs(minimumOrConfiguredSecs, default_broadcast_interval_secs); // If broadcast interval set to max, no such thing as "too long" if (maxSearchMs == UINT32_MAX) return false; @@ -98,7 +98,7 @@ void GPSUpdateScheduling::updateLockTimePrediction() // Ignore the first lock-time: likely to be long, will skew data - // Second locktime: likely stable. Use to intialize the smoothing filter + // Second locktime: likely stable. Use to initialize the smoothing filter if (searchCount == 1) predictedMsToGetLock = lockTime; @@ -106,13 +106,13 @@ void GPSUpdateScheduling::updateLockTimePrediction() else if (searchCount > 1) predictedMsToGetLock = (lockTime * weighting) + (predictedMsToGetLock * (1 - weighting)); - searchCount++; // Only tracked so we can diregard initial lock-times + searchCount++; // Only tracked so we can disregard initial lock-times - LOG_DEBUG("Predicting %us to get next lock\n", predictedMsToGetLock / 1000); + LOG_DEBUG("Predict %us to get next lock", predictedMsToGetLock / 1000); } // How long do we expect to spend searching for a lock? uint32_t GPSUpdateScheduling::predictedSearchDurationMs() { return GPSUpdateScheduling::predictedMsToGetLock; -} \ No newline at end of file +} diff --git a/src/gps/GeoCoord.cpp b/src/gps/GeoCoord.cpp index 5abb25a06..6d1f2da6d 100644 --- a/src/gps/GeoCoord.cpp +++ b/src/gps/GeoCoord.cpp @@ -574,3 +574,23 @@ const char *GeoCoord::degreesToBearing(unsigned int degrees) else return "N"; } + +double GeoCoord::pow_neg(double base, double exponent) +{ + if (exponent == 0) { + return 1; + } else if (exponent > 0) { + return pow(base, exponent); + } + return 1 / pow(base, -exponent); +} + +double GeoCoord::toRadians(double deg) +{ + return deg * PI / 180; +} + +double GeoCoord::toDegrees(double r) +{ + return r * 180 / PI; +} \ No newline at end of file diff --git a/src/gps/GeoCoord.h b/src/gps/GeoCoord.h index ecdaf0ec7..658c177b3 100644 --- a/src/gps/GeoCoord.h +++ b/src/gps/GeoCoord.h @@ -13,28 +13,6 @@ #define OLC_CODE_LEN 11 #define DEG_CONVERT (180 / PI) -// Helper functions -// Raises a number to an exponent, handling negative exponents. -static inline double pow_neg(double base, double exponent) -{ - if (exponent == 0) { - return 1; - } else if (exponent > 0) { - return pow(base, exponent); - } - return 1 / pow(base, -exponent); -} - -static inline double toRadians(double deg) -{ - return deg * PI / 180; -} - -static inline double toDegrees(double r) -{ - return r * 180 / PI; -} - // GeoCoord structs/classes // A struct to hold the data for a DMS coordinate. struct DMS { @@ -120,6 +98,11 @@ class GeoCoord static unsigned int bearingToDegrees(const char *bearing); static const char *degreesToBearing(unsigned int degrees); + // Raises a number to an exponent, handling negative exponents. + static double pow_neg(double base, double exponent); + static double toRadians(double deg); + static double toDegrees(double r); + // Point to point conversions int32_t distanceTo(const GeoCoord &pointB); int32_t bearingTo(const GeoCoord &pointB); @@ -162,4 +145,4 @@ class GeoCoord // OLC getter void getOLCCode(char *code) { strncpy(code, _olc.code, OLC_CODE_LEN + 1); } // +1 for null termination -}; +}; \ No newline at end of file diff --git a/src/gps/NMEAWPL.cpp b/src/gps/NMEAWPL.cpp index f528c4607..f4249ca62 100644 --- a/src/gps/NMEAWPL.cpp +++ b/src/gps/NMEAWPL.cpp @@ -23,7 +23,7 @@ uint32_t printWPL(char *buf, size_t bufsz, const meshtastic_PositionLite &pos, c { GeoCoord geoCoord(pos.latitude_i, pos.longitude_i, pos.altitude); char type = isCaltopoMode ? 'P' : 'N'; - uint32_t len = snprintf(buf, bufsz, "$G%cWPL,%02d%07.4f,%c,%03d%07.4f,%c,%s", type, geoCoord.getDMSLatDeg(), + uint32_t len = snprintf(buf, bufsz, "\r\n$G%cWPL,%02d%07.4f,%c,%03d%07.4f,%c,%s", type, geoCoord.getDMSLatDeg(), (abs(geoCoord.getLatitude()) - geoCoord.getDMSLatDeg() * 1e+7) * 6e-6, geoCoord.getDMSLatCP(), geoCoord.getDMSLonDeg(), (abs(geoCoord.getLongitude()) - geoCoord.getDMSLonDeg() * 1e+7) * 6e-6, geoCoord.getDMSLonCP(), name); diff --git a/src/gps/RTC.cpp b/src/gps/RTC.cpp index d9ac56b74..af964eab5 100644 --- a/src/gps/RTC.cpp +++ b/src/gps/RTC.cpp @@ -46,7 +46,7 @@ void readFromRTC() tv.tv_usec = 0; uint32_t printableEpoch = tv.tv_sec; // Print lib only supports 32 bit but time_t can be 64 bit on some platforms - LOG_DEBUG("Read RTC time from RV3028 getTime as %02d-%02d-%02d %02d:%02d:%02d (%ld)\n", t.tm_year + 1900, t.tm_mon + 1, + LOG_DEBUG("Read RTC time from RV3028 getTime as %02d-%02d-%02d %02d:%02d:%02d (%ld)", t.tm_year + 1900, t.tm_mon + 1, t.tm_mday, t.tm_hour, t.tm_min, t.tm_sec, printableEpoch); timeStartMsec = now; zeroOffsetSecs = tv.tv_sec; @@ -77,8 +77,8 @@ void readFromRTC() tv.tv_usec = 0; uint32_t printableEpoch = tv.tv_sec; // Print lib only supports 32 bit but time_t can be 64 bit on some platforms - LOG_DEBUG("Read RTC time from PCF8563 getDateTime as %02d-%02d-%02d %02d:%02d:%02d (%ld)\n", t.tm_year + 1900, - t.tm_mon + 1, t.tm_mday, t.tm_hour, t.tm_min, t.tm_sec, printableEpoch); + LOG_DEBUG("Read RTC time from PCF8563 getDateTime as %02d-%02d-%02d %02d:%02d:%02d (%ld)", t.tm_year + 1900, t.tm_mon + 1, + t.tm_mday, t.tm_hour, t.tm_min, t.tm_sec, printableEpoch); timeStartMsec = now; zeroOffsetSecs = tv.tv_sec; if (currentQuality == RTCQualityNone) { @@ -89,7 +89,7 @@ void readFromRTC() if (!gettimeofday(&tv, NULL)) { uint32_t now = millis(); uint32_t printableEpoch = tv.tv_sec; // Print lib only supports 32 bit but time_t can be 64 bit on some platforms - LOG_DEBUG("Read RTC time as %ld\n", printableEpoch); + LOG_DEBUG("Read RTC time as %ld", printableEpoch); timeStartMsec = now; zeroOffsetSecs = tv.tv_sec; } @@ -112,7 +112,7 @@ bool perhapsSetRTC(RTCQuality q, const struct timeval *tv, bool forceUpdate) uint32_t printableEpoch = tv->tv_sec; // Print lib only supports 32 bit but time_t can be 64 bit on some platforms #ifdef BUILD_EPOCH if (tv->tv_sec < BUILD_EPOCH) { - LOG_WARN("Ignoring time (%ld) before build epoch (%ld)!\n", printableEpoch, BUILD_EPOCH); + LOG_WARN("Ignore time (%ld) before build epoch (%ld)!", printableEpoch, BUILD_EPOCH); return false; } #endif @@ -120,21 +120,21 @@ bool perhapsSetRTC(RTCQuality q, const struct timeval *tv, bool forceUpdate) bool shouldSet; if (forceUpdate) { shouldSet = true; - LOG_DEBUG("Overriding current RTC quality (%s) with incoming time of RTC quality of %s\n", RtcName(currentQuality), + LOG_DEBUG("Override current RTC quality (%s) with incoming time of RTC quality of %s", RtcName(currentQuality), RtcName(q)); } else if (q > currentQuality) { shouldSet = true; - LOG_DEBUG("Upgrading time to quality %s\n", RtcName(q)); + LOG_DEBUG("Upgrade time to quality %s", RtcName(q)); } else if (q == RTCQualityGPS) { shouldSet = true; - LOG_DEBUG("Reapplying GPS time: %ld secs\n", printableEpoch); + LOG_DEBUG("Reapply GPS time: %ld secs", printableEpoch); } else if (q == RTCQualityNTP && !Throttle::isWithinTimespanMs(lastSetMsec, (12 * 60 * 60 * 1000UL))) { // Every 12 hrs we will slam in a new NTP or Phone GPS / NTP time, to correct for local RTC clock drift shouldSet = true; - LOG_DEBUG("Reapplying external time to correct clock drift %ld secs\n", printableEpoch); + LOG_DEBUG("Reapply external time to correct clock drift %ld secs", printableEpoch); } else { shouldSet = false; - LOG_DEBUG("Current RTC quality: %s. Ignoring time of RTC quality of %s\n", RtcName(currentQuality), RtcName(q)); + LOG_DEBUG("Current RTC quality: %s. Ignore time of RTC quality of %s", RtcName(currentQuality), RtcName(q)); } if (shouldSet) { @@ -158,7 +158,7 @@ bool perhapsSetRTC(RTCQuality q, const struct timeval *tv, bool forceUpdate) #endif tm *t = gmtime(&tv->tv_sec); rtc.setTime(t->tm_year + 1900, t->tm_mon + 1, t->tm_wday, t->tm_mday, t->tm_hour, t->tm_min, t->tm_sec); - LOG_DEBUG("RV3028_RTC setTime %02d-%02d-%02d %02d:%02d:%02d (%ld)\n", t->tm_year + 1900, t->tm_mon + 1, t->tm_mday, + LOG_DEBUG("RV3028_RTC setTime %02d-%02d-%02d %02d:%02d:%02d (%ld)", t->tm_year + 1900, t->tm_mon + 1, t->tm_mday, t->tm_hour, t->tm_min, t->tm_sec, printableEpoch); } #elif defined(PCF8563_RTC) @@ -172,8 +172,8 @@ bool perhapsSetRTC(RTCQuality q, const struct timeval *tv, bool forceUpdate) #endif tm *t = gmtime(&tv->tv_sec); rtc.setDateTime(t->tm_year + 1900, t->tm_mon + 1, t->tm_mday, t->tm_hour, t->tm_min, t->tm_sec); - LOG_DEBUG("PCF8563_RTC setDateTime %02d-%02d-%02d %02d:%02d:%02d (%ld)\n", t->tm_year + 1900, t->tm_mon + 1, - t->tm_mday, t->tm_hour, t->tm_min, t->tm_sec, printableEpoch); + LOG_DEBUG("PCF8563_RTC setDateTime %02d-%02d-%02d %02d:%02d:%02d (%ld)", t->tm_year + 1900, t->tm_mon + 1, t->tm_mday, + t->tm_hour, t->tm_min, t->tm_sec, printableEpoch); } #elif defined(ARCH_ESP32) settimeofday(tv, NULL); @@ -228,9 +228,9 @@ bool perhapsSetRTC(RTCQuality q, struct tm &t) tv.tv_sec = res; tv.tv_usec = 0; // time.centisecond() * (10 / 1000); - // LOG_DEBUG("Got time from GPS month=%d, year=%d, unixtime=%ld\n", t.tm_mon, t.tm_year, tv.tv_sec); + // LOG_DEBUG("Got time from GPS month=%d, year=%d, unixtime=%ld", t.tm_mon, t.tm_year, tv.tv_sec); if (t.tm_year < 0 || t.tm_year >= 300) { - // LOG_DEBUG("Ignoring invalid GPS month=%d, year=%d, unixtime=%ld\n", t.tm_mon, t.tm_year, tv.tv_sec); + // LOG_DEBUG("Ignore invalid GPS month=%d, year=%d, unixtime=%ld", t.tm_mon, t.tm_year, tv.tv_sec); return false; } else { return perhapsSetRTC(q, &tv); diff --git a/src/gps/cas.h b/src/gps/cas.h index 53d75cda9..725fd07b3 100644 --- a/src/gps/cas.h +++ b/src/gps/cas.h @@ -21,7 +21,7 @@ // CFG-RST (0x06, 0x02) // Factory reset -const uint8_t GPS::_message_CAS_CFG_RST_FACTORY[] = { +static const uint8_t _message_CAS_CFG_RST_FACTORY[] = { 0xFF, 0x03, // Fields to clear 0x01, // Reset Mode: Controlled Software reset 0x03 // Startup Mode: Factory @@ -30,7 +30,7 @@ const uint8_t GPS::_message_CAS_CFG_RST_FACTORY[] = { // CFG_RATE (0x06, 0x01) // 1HZ update rate, this should always be the case after // factory reset but update it regardless -const uint8_t GPS::_message_CAS_CFG_RATE_1HZ[] = { +static const uint8_t _message_CAS_CFG_RATE_1HZ[] = { 0xE8, 0x03, // Update Rate: 0x03E8 = 1000ms 0x00, 0x00 // Reserved }; @@ -39,7 +39,7 @@ const uint8_t GPS::_message_CAS_CFG_RATE_1HZ[] = { // Initial ATGM33H-5N configuration, Updates for Dynamic Mode, Fix Mode, and SV system // Qwirk: The ATGM33H-5N-31 should only support GPS+BDS, however it will happily enable // and use GPS+BDS+GLONASS iff the correct CFG_NAVX command is used. -const uint8_t GPS::_message_CAS_CFG_NAVX_CONF[] = { +static const uint8_t _message_CAS_CFG_NAVX_CONF[] = { 0x03, 0x01, 0x00, 0x00, // Update Mask: Dynamic Mode, Fix Mode, Nav Settings 0x03, // Dynamic Mode: Automotive 0x03, // Fix Mode: Auto 2D/3D @@ -60,4 +60,4 @@ const uint8_t GPS::_message_CAS_CFG_NAVX_CONF[] = { 0x00, 0x00, 0x00, 0x00, // Position Accuracy Max 0x00, 0x00, 0x00, 0x00, // Time Accuracy Max 0x00, 0x00, 0x00, 0x00 // Static Hold Threshold -}; \ No newline at end of file +}; diff --git a/src/gps/ubx.h b/src/gps/ubx.h index b137d3349..d674bed51 100644 --- a/src/gps/ubx.h +++ b/src/gps/ubx.h @@ -1,20 +1,22 @@ -const char *failMessage = "Unable to %s\n"; +static const char *failMessage = "Unable to %s"; #define SEND_UBX_PACKET(TYPE, ID, DATA, ERRMSG, TIMEOUT) \ - msglen = makeUBXPacket(TYPE, ID, sizeof(DATA), DATA); \ - _serial_gps->write(UBXscratch, msglen); \ - if (getACK(TYPE, ID, TIMEOUT) != GNSS_RESPONSE_OK) { \ - LOG_WARN(failMessage, #ERRMSG); \ - } + do { \ + msglen = makeUBXPacket(TYPE, ID, sizeof(DATA), DATA); \ + _serial_gps->write(UBXscratch, msglen); \ + if (getACK(TYPE, ID, TIMEOUT) != GNSS_RESPONSE_OK) { \ + LOG_WARN(failMessage, #ERRMSG); \ + } \ + } while (0) // Power Management -uint8_t GPS::_message_PMREQ[] PROGMEM = { +static uint8_t _message_PMREQ[] PROGMEM = { 0x00, 0x00, 0x00, 0x00, // 4 bytes duration of request task (milliseconds) 0x02, 0x00, 0x00, 0x00 // Bitfield, set backup = 1 }; -uint8_t GPS::_message_PMREQ_10[] PROGMEM = { +static uint8_t _message_PMREQ_10[] PROGMEM = { 0x00, // version (0 for this version) 0x00, 0x00, 0x00, // Reserved 1 0x00, 0x00, 0x00, 0x00, // 4 bytes duration of request task (milliseconds) @@ -22,18 +24,18 @@ uint8_t GPS::_message_PMREQ_10[] PROGMEM = { 0x08, 0x00, 0x00, 0x00 // wakeupSources Wake on uartrx }; -const uint8_t GPS::_message_CFG_RXM_PSM[] PROGMEM = { +static const uint8_t _message_CFG_RXM_PSM[] PROGMEM = { 0x08, // Reserved 0x01 // Power save mode }; // only for Neo-6 -const uint8_t GPS::_message_CFG_RXM_ECO[] PROGMEM = { +static const uint8_t _message_CFG_RXM_ECO[] PROGMEM = { 0x08, // Reserved 0x04 // eco mode }; -const uint8_t GPS::_message_CFG_PM2[] PROGMEM = { +static const uint8_t _message_CFG_PM2[] PROGMEM = { 0x01, // version 0x00, // Reserved 1, set to 0x06 by u-Center 0x00, // Reserved 2 @@ -58,7 +60,7 @@ const uint8_t GPS::_message_CFG_PM2[] PROGMEM = { // Constallation setup, none required for Neo-6 // For Neo-7 GPS & SBAS -const uint8_t GPS::_message_GNSS_7[] = { +static const uint8_t _message_GNSS_7[] = { 0x00, // msgVer (0 for this version) 0x00, // numTrkChHw (max number of hardware channels, read only, so it's always 0) 0xff, // numTrkChUse (max number of channels to use, 0xff = max available) @@ -76,7 +78,7 @@ const uint8_t GPS::_message_GNSS_7[] = { // There is also a possibility that the module may be GPS-only. // For M8 GPS, GLONASS, Galileo, SBAS, QZSS -const uint8_t GPS::_message_GNSS_8[] = { +static const uint8_t _message_GNSS_8[] = { 0x00, // msgVer (0 for this version) 0x00, // numTrkChHw (max number of hardware channels, read only, so it's always 0) 0xff, // numTrkChUse (max number of channels to use, 0xff = max available) @@ -90,7 +92,7 @@ const uint8_t GPS::_message_GNSS_8[] = { }; /* // For M8 GPS, GLONASS, BeiDou, SBAS, QZSS -const uint8_t GPS::_message_GNSS_8_B[] = { +static const uint8_t _message_GNSS_8_B[] = { 0x00, // msgVer (0 for this version) 0x00, // numTrkChHw (max number of hardware channels, read only, so it's always 0) 0xff, // numTrkChUse (max number of channels to use, 0xff = max available) read only for protocol >23 @@ -105,7 +107,7 @@ const uint8_t GPS::_message_GNSS_8_B[] = { */ // For M8 we want to enable NMEA version 4.10 messages to allow for Galileo and or BeiDou -const uint8_t GPS::_message_NMEA[]{ +static const uint8_t _message_NMEA[]{ 0x00, // filter flags 0x41, // NMEA Version 0x00, // Max number of SVs to report per TaklerId @@ -121,13 +123,13 @@ const uint8_t GPS::_message_NMEA[]{ // Enable jamming/interference monitor // For Neo-6, Max-7 and Neo-7 -const uint8_t GPS::_message_JAM_6_7[] = { +static const uint8_t _message_JAM_6_7[] = { 0xf3, 0xac, 0x62, 0xad, // config1 bbThreshold = 3, cwThreshold = 15, enable = 1, reserved bits 0x16B156 0x1e, 0x03, 0x00, 0x00 // config2 antennaSetting Unknown = 0, reserved 3, = 0x00,0x00, reserved 2 = 0x31E }; // For M8 -const uint8_t GPS::_message_JAM_8[] = { +static const uint8_t _message_JAM_8[] = { 0xf3, 0xac, 0x62, 0xad, // config1 bbThreshold = 3, cwThreshold = 15, enable1 = 1, reserved bits 0x16B156 0x1e, 0x43, 0x00, 0x00 // config2 antennaSetting Unknown = 0, enable2 = 1, generalBits = 0x31E }; @@ -137,7 +139,7 @@ const uint8_t GPS::_message_JAM_8[] = { // ToDo: check UBX-MON-VER for module type and protocol version // For the Neo-6 -const uint8_t GPS::_message_NAVX5[] = { +static const uint8_t _message_NAVX5[] = { 0x00, 0x00, // msgVer (0 for this version) 0x4c, 0x66, // mask1 0x00, 0x00, 0x00, 0x00, // Reserved 0 @@ -166,7 +168,7 @@ const uint8_t GPS::_message_NAVX5[] = { 0x00, 0x00, 0x00, 0x00 // Reserved 4 }; // For the M8 -const uint8_t GPS::_message_NAVX5_8[] = { +static const uint8_t _message_NAVX5_8[] = { 0x02, 0x00, // msgVer (2 for this version) 0x4c, 0x66, // mask1 0x00, 0x00, 0x00, 0x00, // mask2 @@ -197,7 +199,7 @@ const uint8_t GPS::_message_NAVX5_8[] = { // Additionally, for some new modules like the M9/M10, an update rate lower than 5Hz // is recommended to avoid a known issue with satellites disappearing. // The module defaults for M8, M9, M10 are the same as we use here so no update is necessary -const uint8_t GPS::_message_1HZ[] = { +static const uint8_t _message_1HZ[] = { 0xE8, 0x03, // Measurement Rate (1000ms for 1Hz) 0x01, 0x00, // Navigation rate, always 1 in GPS mode 0x01, 0x00 // Time reference @@ -205,7 +207,7 @@ const uint8_t GPS::_message_1HZ[] = { // Disable GLL. GLL - Geographic position (latitude and longitude), which provides the current geographical // coordinates. -const uint8_t GPS::_message_GLL[] = { +static const uint8_t _message_GLL[] = { 0xF0, 0x01, // NMEA ID for GLL 0x00, // Rate for DDC 0x00, // Rate for UART1 @@ -217,7 +219,7 @@ const uint8_t GPS::_message_GLL[] = { // Disable GSA. GSA - GPS DOP and active satellites, used for detailing the satellites used in the positioning and // the DOP (Dilution of Precision) -const uint8_t GPS::_message_GSA[] = { +static const uint8_t _message_GSA[] = { 0xF0, 0x02, // NMEA ID for GSA 0x00, // Rate for DDC 0x00, // Rate for UART1 @@ -228,7 +230,7 @@ const uint8_t GPS::_message_GSA[] = { }; // Disable GSV. GSV - Satellites in view, details the number and location of satellites in view. -const uint8_t GPS::_message_GSV[] = { +static const uint8_t _message_GSV[] = { 0xF0, 0x03, // NMEA ID for GSV 0x00, // Rate for DDC 0x00, // Rate for UART1 @@ -240,7 +242,7 @@ const uint8_t GPS::_message_GSV[] = { // Disable VTG. VTG - Track made good and ground speed, which provides course and speed information relative to // the ground. -const uint8_t GPS::_message_VTG[] = { +static const uint8_t _message_VTG[] = { 0xF0, 0x05, // NMEA ID for VTG 0x00, // Rate for DDC 0x00, // Rate for UART1 @@ -251,7 +253,7 @@ const uint8_t GPS::_message_VTG[] = { }; // Enable RMC. RMC - Recommended Minimum data, the essential gps pvt (position, velocity, time) data. -const uint8_t GPS::_message_RMC[] = { +static const uint8_t _message_RMC[] = { 0xF0, 0x04, // NMEA ID for RMC 0x00, // Rate for DDC 0x01, // Rate for UART1 @@ -262,7 +264,7 @@ const uint8_t GPS::_message_RMC[] = { }; // Enable GGA. GGA - Global Positioning System Fix Data, which provides 3D location and accuracy data. -const uint8_t GPS::_message_GGA[] = { +static const uint8_t _message_GGA[] = { 0xF0, 0x00, // NMEA ID for GGA 0x00, // Rate for DDC 0x01, // Rate for UART1 @@ -274,7 +276,7 @@ const uint8_t GPS::_message_GGA[] = { // Disable UBX-AID-ALPSRV as it may confuse TinyGPS. The Neo-6 seems to send this message // whether the AID Autonomous is enabled or not -const uint8_t GPS::_message_AID[] = { +static const uint8_t _message_AID[] = { 0x0B, 0x32, // NMEA ID for UBX-AID-ALPSRV 0x00, // Rate for DDC 0x00, // Rate for UART1 @@ -287,7 +289,7 @@ const uint8_t GPS::_message_AID[] = { // Turn off TEXT INFO Messages for all but M10 series // B5 62 06 02 0A 00 01 00 00 00 03 03 00 03 03 00 1F 20 -const uint8_t GPS::_message_DISABLE_TXT_INFO[] = { +static const uint8_t _message_DISABLE_TXT_INFO[] = { 0x01, // Protocol ID for NMEA 0x00, 0x00, 0x00, // Reserved 0x03, // I2C @@ -310,7 +312,7 @@ const uint8_t GPS::_message_DISABLE_TXT_INFO[] = { // and must be smaller than the period. It is only valid when the powerSetupValue is set to Interval; otherwise, // it must be set to '0'. // This command applies to M8 products -const uint8_t GPS::_message_PMS[] = { +static const uint8_t _message_PMS[] = { 0x00, // Version (0) 0x03, // Power setup value 3 = Agresssive 1Hz 0x00, 0x00, // period: not applicable, set to 0 @@ -318,14 +320,14 @@ const uint8_t GPS::_message_PMS[] = { 0x00, 0x00 // reserved, generated by u-center }; -const uint8_t GPS::_message_SAVE[] = { +static const uint8_t _message_SAVE[] = { 0x00, 0x00, 0x00, 0x00, // clearMask: no sections cleared 0xFF, 0xFF, 0x00, 0x00, // saveMask: save all sections 0x00, 0x00, 0x00, 0x00, // loadMask: no sections loaded 0x17 // deviceMask: BBR, Flash, EEPROM, and SPI Flash }; -const uint8_t GPS::_message_SAVE_10[] = { +static const uint8_t _message_SAVE_10[] = { 0x00, 0x00, 0x00, 0x00, // clearMask: no sections cleared 0xFF, 0xFF, 0x00, 0x00, // saveMask: save all sections 0x00, 0x00, 0x00, 0x00, // loadMask: no sections loaded @@ -375,12 +377,12 @@ LIMITPEAKCURRENT L 1 // b5 62 06 8a 26 00 00 02 00 00 01 00 d0 20 02 02 00 d0 40 05 00 00 00 05 00 d0 30 01 00 08 00 d0 10 01 09 00 d0 10 01 10 00 d0 // 10 01 8c 03 */ -const uint8_t GPS::_message_VALSET_PM_RAM[] = {0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0xd0, 0x20, 0x02, 0x02, 0x00, 0xd0, 0x40, - 0x05, 0x00, 0x00, 0x00, 0x05, 0x00, 0xd0, 0x30, 0x01, 0x00, 0x08, 0x00, 0xd0, - 0x10, 0x01, 0x09, 0x00, 0xd0, 0x10, 0x01, 0x10, 0x00, 0xd0, 0x10, 0x01}; -const uint8_t GPS::_message_VALSET_PM_BBR[] = {0x00, 0x02, 0x00, 0x00, 0x01, 0x00, 0xd0, 0x20, 0x02, 0x02, 0x00, 0xd0, 0x40, - 0x05, 0x00, 0x00, 0x00, 0x05, 0x00, 0xd0, 0x30, 0x01, 0x00, 0x08, 0x00, 0xd0, - 0x10, 0x01, 0x09, 0x00, 0xd0, 0x10, 0x01, 0x10, 0x00, 0xd0, 0x10, 0x01}; +static const uint8_t _message_VALSET_PM_RAM[] = {0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0xd0, 0x20, 0x02, 0x02, 0x00, 0xd0, 0x40, + 0x05, 0x00, 0x00, 0x00, 0x05, 0x00, 0xd0, 0x30, 0x01, 0x00, 0x08, 0x00, 0xd0, + 0x10, 0x01, 0x09, 0x00, 0xd0, 0x10, 0x01, 0x10, 0x00, 0xd0, 0x10, 0x01}; +static const uint8_t _message_VALSET_PM_BBR[] = {0x00, 0x02, 0x00, 0x00, 0x01, 0x00, 0xd0, 0x20, 0x02, 0x02, 0x00, 0xd0, 0x40, + 0x05, 0x00, 0x00, 0x00, 0x05, 0x00, 0xd0, 0x30, 0x01, 0x00, 0x08, 0x00, 0xd0, + 0x10, 0x01, 0x09, 0x00, 0xd0, 0x10, 0x01, 0x10, 0x00, 0xd0, 0x10, 0x01}; /* CFG-ITFM replaced by 5 valset messages which can be combined into one for RAM and one for BBR @@ -394,10 +396,10 @@ CFG-ITFM replaced by 5 valset messages which can be combined into one for RAM an b5 62 06 8a 0e 00 00 01 00 00 0d 00 41 10 01 13 00 41 10 01 63 c6 */ -const uint8_t GPS::_message_VALSET_ITFM_RAM[] = {0x00, 0x01, 0x00, 0x00, 0x0d, 0x00, 0x41, - 0x10, 0x01, 0x13, 0x00, 0x41, 0x10, 0x01}; -const uint8_t GPS::_message_VALSET_ITFM_BBR[] = {0x00, 0x02, 0x00, 0x00, 0x0d, 0x00, 0x41, - 0x10, 0x01, 0x13, 0x00, 0x41, 0x10, 0x01}; +static const uint8_t _message_VALSET_ITFM_RAM[] = {0x00, 0x01, 0x00, 0x00, 0x0d, 0x00, 0x41, + 0x10, 0x01, 0x13, 0x00, 0x41, 0x10, 0x01}; +static const uint8_t _message_VALSET_ITFM_BBR[] = {0x00, 0x02, 0x00, 0x00, 0x0d, 0x00, 0x41, + 0x10, 0x01, 0x13, 0x00, 0x41, 0x10, 0x01}; // Turn off all NMEA messages: // Ram layer config message: @@ -407,13 +409,13 @@ const uint8_t GPS::_message_VALSET_ITFM_BBR[] = {0x00, 0x02, 0x00, 0x00, 0x0d, 0 // BBR layer config message: // b5 62 06 8a 13 00 00 02 00 00 ca 00 91 20 00 c5 00 91 20 00 b1 00 91 20 00 f8 4e -const uint8_t GPS::_message_VALSET_DISABLE_NMEA_RAM[] = { +static const uint8_t _message_VALSET_DISABLE_NMEA_RAM[] = { /*0x00, 0x01, 0x00, 0x00, 0xca, 0x00, 0x91, 0x20, 0x00, 0xc5, 0x00, 0x91, 0x20, 0x00, 0xb1, 0x00, 0x91, 0x20, 0x00 */ 0x00, 0x01, 0x00, 0x00, 0xc0, 0x00, 0x91, 0x20, 0x00, 0xca, 0x00, 0x91, 0x20, 0x00, 0xc5, 0x00, 0x91, 0x20, 0x00, 0xac, 0x00, 0x91, 0x20, 0x00, 0xb1, 0x00, 0x91, 0x20, 0x00, 0xbb, 0x00, 0x91, 0x20, 0x00}; -const uint8_t GPS::_message_VALSET_DISABLE_NMEA_BBR[] = {0x00, 0x02, 0x00, 0x00, 0xca, 0x00, 0x91, 0x20, 0x00, 0xc5, - 0x00, 0x91, 0x20, 0x00, 0xb1, 0x00, 0x91, 0x20, 0x00}; +static const uint8_t _message_VALSET_DISABLE_NMEA_BBR[] = {0x00, 0x02, 0x00, 0x00, 0xca, 0x00, 0x91, 0x20, 0x00, 0xc5, + 0x00, 0x91, 0x20, 0x00, 0xb1, 0x00, 0x91, 0x20, 0x00}; // Turn off text info messages: // Ram layer config message: @@ -432,17 +434,17 @@ const uint8_t GPS::_message_VALSET_DISABLE_NMEA_BBR[] = {0x00, 0x02, 0x00, 0x00, // b5 62 06 8a 0e 00 00 04 00 00 bb 00 91 20 01 ac 00 91 20 01 6d b6 // Doing this for the FLASH layer isn't really required since we save the config to flash later -const uint8_t GPS::_message_VALSET_DISABLE_TXT_INFO_RAM[] = {0x00, 0x01, 0x00, 0x00, 0x07, 0x00, 0x92, 0x20, 0x03}; -const uint8_t GPS::_message_VALSET_DISABLE_TXT_INFO_BBR[] = {0x00, 0x02, 0x00, 0x00, 0x07, 0x00, 0x92, 0x20, 0x03}; +static const uint8_t _message_VALSET_DISABLE_TXT_INFO_RAM[] = {0x00, 0x01, 0x00, 0x00, 0x07, 0x00, 0x92, 0x20, 0x03}; +static const uint8_t _message_VALSET_DISABLE_TXT_INFO_BBR[] = {0x00, 0x02, 0x00, 0x00, 0x07, 0x00, 0x92, 0x20, 0x03}; -const uint8_t GPS::_message_VALSET_ENABLE_NMEA_RAM[] = {0x00, 0x01, 0x00, 0x00, 0xbb, 0x00, 0x91, - 0x20, 0x01, 0xac, 0x00, 0x91, 0x20, 0x01}; -const uint8_t GPS::_message_VALSET_ENABLE_NMEA_BBR[] = {0x00, 0x02, 0x00, 0x00, 0xbb, 0x00, 0x91, - 0x20, 0x01, 0xac, 0x00, 0x91, 0x20, 0x01}; -const uint8_t GPS::_message_VALSET_DISABLE_SBAS_RAM[] = {0x00, 0x01, 0x00, 0x00, 0x20, 0x00, 0x31, - 0x10, 0x00, 0x05, 0x00, 0x31, 0x10, 0x00}; -const uint8_t GPS::_message_VALSET_DISABLE_SBAS_BBR[] = {0x00, 0x02, 0x00, 0x00, 0x20, 0x00, 0x31, - 0x10, 0x00, 0x05, 0x00, 0x31, 0x10, 0x00}; +static const uint8_t _message_VALSET_ENABLE_NMEA_RAM[] = {0x00, 0x01, 0x00, 0x00, 0xbb, 0x00, 0x91, + 0x20, 0x01, 0xac, 0x00, 0x91, 0x20, 0x01}; +static const uint8_t _message_VALSET_ENABLE_NMEA_BBR[] = {0x00, 0x02, 0x00, 0x00, 0xbb, 0x00, 0x91, + 0x20, 0x01, 0xac, 0x00, 0x91, 0x20, 0x01}; +static const uint8_t _message_VALSET_DISABLE_SBAS_RAM[] = {0x00, 0x01, 0x00, 0x00, 0x20, 0x00, 0x31, + 0x10, 0x00, 0x05, 0x00, 0x31, 0x10, 0x00}; +static const uint8_t _message_VALSET_DISABLE_SBAS_BBR[] = {0x00, 0x02, 0x00, 0x00, 0x20, 0x00, 0x31, + 0x10, 0x00, 0x05, 0x00, 0x31, 0x10, 0x00}; /* Operational issues with the M10: @@ -462,7 +464,7 @@ the PM config. Lets try without it. PMREQ sort of works with SBAS, but the awake time is too short to re-acquire any SBAS sats. The defination of "Got Fix" doesn't seem to include SBAS. Much more too this... Even if it was, it can take minutes (up to 12.5), -even under good sat visability conditions to re-acquire the SBAS data. +even under good sat visibility conditions to re-acquire the SBAS data. Another effect fo the quick transition to sleep is that no other sats will be acquired so the sat count will tend to remain at what the initial fix was. @@ -475,4 +477,4 @@ b5 62 06 8a 0e 00 00 01 00 00 20 00 31 10 00 05 00 31 10 00 46 87 BBR layer config message: b5 62 06 8a 0e 00 00 02 00 00 20 00 31 10 00 05 00 31 10 00 47 94 -*/ \ No newline at end of file +*/ diff --git a/src/graphics/EInkDisplay2.cpp b/src/graphics/EInkDisplay2.cpp index c4cb10f82..6c85582c0 100644 --- a/src/graphics/EInkDisplay2.cpp +++ b/src/graphics/EInkDisplay2.cpp @@ -79,13 +79,13 @@ bool EInkDisplay::forceDisplay(uint32_t msecLimit) } // Trigger the refresh in GxEPD2 - LOG_DEBUG("Updating E-Paper... "); + LOG_DEBUG("Update E-Paper"); adafruitDisplay->nextPage(); // End the update process endUpdate(); - LOG_DEBUG("done\n"); + LOG_DEBUG("done"); return true; } @@ -123,7 +123,7 @@ void EInkDisplay::setDetected(uint8_t detected) // Connect to the display - variant specific bool EInkDisplay::connect() { - LOG_INFO("Doing EInk init\n"); + LOG_INFO("Do EInk init"); #ifdef PIN_EINK_EN // backlight power, HIGH is backlight on, LOW is off diff --git a/src/graphics/EInkDynamicDisplay.cpp b/src/graphics/EInkDynamicDisplay.cpp index ca994b2c9..6664646b9 100644 --- a/src/graphics/EInkDynamicDisplay.cpp +++ b/src/graphics/EInkDynamicDisplay.cpp @@ -119,7 +119,7 @@ void EInkDynamicDisplay::endOrDetach() awaitRefresh(); else { // Async begins - LOG_DEBUG("Async full-refresh begins (dropping frames)\n"); + LOG_DEBUG("Async full-refresh begins (drop frames)"); notifyLater(intervalPollAsyncRefresh, DUE_POLL_ASYNCREFRESH, true); // Hand-off to NotifiedWorkerThread } } @@ -133,7 +133,7 @@ void EInkDynamicDisplay::endOrDetach() if (previousRefresh == FULL || previousRefresh == FAST) { // If refresh wasn't skipped (on unspecified..) LOG_WARN( "GxEPD2 version has not been modified to support async refresh; using fallback behavior. Please update lib_deps in " - "variant's platformio.ini file\n"); + "variant's platformio.ini file"); EInkDisplay::endUpdate(); } #endif @@ -170,7 +170,7 @@ bool EInkDynamicDisplay::determineMode() checkFastRequested(); if (refresh == UNSPECIFIED) - LOG_WARN("There was a flaw in the determineMode() logic.\n"); + LOG_WARN("There was a flaw in the determineMode() logic"); // -- Decision has been reached -- applyRefreshMode(); @@ -254,7 +254,7 @@ void EInkDynamicDisplay::checkRateLimiting() if (Throttle::isWithinTimespanMs(previousRunMs, EINK_LIMIT_RATE_RESPONSIVE_SEC * 1000)) { refresh = SKIPPED; reason = EXCEEDED_RATELIMIT_FAST; - LOG_DEBUG("refresh=SKIPPED, reason=EXCEEDED_RATELIMIT_FAST, frameFlags=0x%x\n", frameFlags); + LOG_DEBUG("refresh=SKIPPED, reason=EXCEEDED_RATELIMIT_FAST, frameFlags=0x%x", frameFlags); return; } } @@ -271,7 +271,7 @@ void EInkDynamicDisplay::checkCosmetic() if (frameFlags & COSMETIC) { refresh = FULL; reason = FLAGGED_COSMETIC; - LOG_DEBUG("refresh=FULL, reason=FLAGGED_COSMETIC, frameFlags=0x%x\n", frameFlags); + LOG_DEBUG("refresh=FULL, reason=FLAGGED_COSMETIC, frameFlags=0x%x", frameFlags); } } @@ -286,7 +286,7 @@ void EInkDynamicDisplay::checkDemandingFast() if (frameFlags & DEMAND_FAST) { refresh = FAST; reason = FLAGGED_DEMAND_FAST; - LOG_DEBUG("refresh=FAST, reason=FLAGGED_DEMAND_FAST, frameFlags=0x%x\n", frameFlags); + LOG_DEBUG("refresh=FAST, reason=FLAGGED_DEMAND_FAST, frameFlags=0x%x", frameFlags); } } @@ -306,7 +306,7 @@ void EInkDynamicDisplay::checkFrameMatchesPrevious() if (frameFlags == BACKGROUND && fastRefreshCount > 0) { refresh = FULL; reason = REDRAW_WITH_FULL; - LOG_DEBUG("refresh=FULL, reason=REDRAW_WITH_FULL, frameFlags=0x%x\n", frameFlags); + LOG_DEBUG("refresh=FULL, reason=REDRAW_WITH_FULL, frameFlags=0x%x", frameFlags); return; } #endif @@ -314,7 +314,7 @@ void EInkDynamicDisplay::checkFrameMatchesPrevious() // Not redrawn, not COSMETIC, not DEMAND_FAST refresh = SKIPPED; reason = FRAME_MATCHED_PREVIOUS; - LOG_DEBUG("refresh=SKIPPED, reason=FRAME_MATCHED_PREVIOUS, frameFlags=0x%x\n", frameFlags); + LOG_DEBUG("refresh=SKIPPED, reason=FRAME_MATCHED_PREVIOUS, frameFlags=0x%x", frameFlags); } // Have too many fast-refreshes occured consecutively, since last full refresh? @@ -328,7 +328,7 @@ void EInkDynamicDisplay::checkConsecutiveFastRefreshes() if (fastRefreshCount >= EINK_LIMIT_FASTREFRESH) { refresh = FULL; reason = EXCEEDED_LIMIT_FASTREFRESH; - LOG_DEBUG("refresh=FULL, reason=EXCEEDED_LIMIT_FASTREFRESH, frameFlags=0x%x\n", frameFlags); + LOG_DEBUG("refresh=FULL, reason=EXCEEDED_LIMIT_FASTREFRESH, frameFlags=0x%x", frameFlags); } } @@ -343,13 +343,13 @@ void EInkDynamicDisplay::checkFastRequested() // If we want BACKGROUND to use fast. (FULL only when a limit is hit) refresh = FAST; reason = BACKGROUND_USES_FAST; - LOG_DEBUG("refresh=FAST, reason=BACKGROUND_USES_FAST, fastRefreshCount=%lu, frameFlags=0x%x\n", fastRefreshCount, + LOG_DEBUG("refresh=FAST, reason=BACKGROUND_USES_FAST, fastRefreshCount=%lu, frameFlags=0x%x", fastRefreshCount, frameFlags); #else // If we do want to use FULL for BACKGROUND updates refresh = FULL; reason = FLAGGED_BACKGROUND; - LOG_DEBUG("refresh=FULL, reason=FLAGGED_BACKGROUND\n"); + LOG_DEBUG("refresh=FULL, reason=FLAGGED_BACKGROUND"); #endif } @@ -357,7 +357,7 @@ void EInkDynamicDisplay::checkFastRequested() if (frameFlags & RESPONSIVE) { refresh = FAST; reason = NO_OBJECTIONS; - LOG_DEBUG("refresh=FAST, reason=NO_OBJECTIONS, fastRefreshCount=%lu, frameFlags=0x%x\n", fastRefreshCount, frameFlags); + LOG_DEBUG("refresh=FAST, reason=NO_OBJECTIONS, fastRefreshCount=%lu, frameFlags=0x%x", fastRefreshCount, frameFlags); } } @@ -438,7 +438,7 @@ void EInkDynamicDisplay::checkExcessiveGhosting() if (ghostPixelCount > EINK_LIMIT_GHOSTING_PX) { refresh = FULL; reason = EXCEEDED_GHOSTINGLIMIT; - LOG_DEBUG("refresh=FULL, reason=EXCEEDED_GHOSTINGLIMIT, frameFlags=0x%x\n", frameFlags); + LOG_DEBUG("refresh=FULL, reason=EXCEEDED_GHOSTINGLIMIT, frameFlags=0x%x", frameFlags); } } @@ -469,7 +469,7 @@ void EInkDynamicDisplay::joinAsyncRefresh() if (!asyncRefreshRunning) return; - LOG_DEBUG("Joining an async refresh in progress\n"); + LOG_DEBUG("Join an async refresh in progress"); // Continually poll the BUSY pin while (adafruitDisplay->epd2.isBusy()) @@ -479,7 +479,7 @@ void EInkDynamicDisplay::joinAsyncRefresh() adafruitDisplay->endAsyncFull(); // Run the end of nextPage() code EInkDisplay::endUpdate(); // Run base-class code to finish off update (NOT our derived class override) asyncRefreshRunning = false; // Unset the flag - LOG_DEBUG("Refresh complete\n"); + LOG_DEBUG("Refresh complete"); // Note: this code only works because of a modification to meshtastic/GxEPD2. // It is only equipped to intercept calls to nextPage() @@ -503,7 +503,7 @@ void EInkDynamicDisplay::pollAsyncRefresh() adafruitDisplay->endAsyncFull(); // Run the end of nextPage() code EInkDisplay::endUpdate(); // Run base-class code to finish off update (NOT our derived class override) asyncRefreshRunning = false; // Unset the flag - LOG_DEBUG("Async full-refresh complete\n"); + LOG_DEBUG("Async full-refresh complete"); // Note: this code only works because of a modification to meshtastic/GxEPD2. // It is only equipped to intercept calls to nextPage() diff --git a/src/graphics/Screen.cpp b/src/graphics/Screen.cpp index 3c0230330..2ab413bc5 100644 --- a/src/graphics/Screen.cpp +++ b/src/graphics/Screen.cpp @@ -20,7 +20,6 @@ along with this program. If not, see . */ #include "Screen.h" -#include "../userPrefs.h" #include "PowerMon.h" #include "Throttle.h" #include "configuration.h" @@ -101,9 +100,9 @@ std::vector moduleFrames; static char ourId[5]; // vector where symbols (string) are displayed in bottom corner of display. -std::vector functionSymbals; -// string displayed in bottom right corner of display. Created from elements in functionSymbals vector -std::string functionSymbalString = ""; +std::vector functionSymbol; +// string displayed in bottom right corner of display. Created from elements in functionSymbol vector +std::string functionSymbolString = ""; #if HAS_GPS // GeoCoord object for the screen @@ -144,7 +143,7 @@ static bool haveGlyphs(const char *str) } } - LOG_DEBUG("haveGlyphs=%d\n", have); + LOG_DEBUG("haveGlyphs=%d", have); return have; } @@ -186,56 +185,6 @@ static void drawIconScreen(const char *upperMsg, OLEDDisplay *display, OLEDDispl display->setTextAlignment(TEXT_ALIGN_LEFT); // Restore left align, just to be kind to any other unsuspecting code } -static void drawOEMIconScreen(const char *upperMsg, OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y) -{ - // draw an xbm image. - // Please note that everything that should be transitioned - // needs to be drawn relative to x and y - - // draw centered icon left to right and centered above the one line of app text - display->drawXbm(x + (SCREEN_WIDTH - oemStore.oem_icon_width) / 2, - y + (SCREEN_HEIGHT - FONT_HEIGHT_MEDIUM - oemStore.oem_icon_height) / 2 + 2, oemStore.oem_icon_width, - oemStore.oem_icon_height, (const uint8_t *)oemStore.oem_icon_bits.bytes); - - switch (oemStore.oem_font) { - case 0: - display->setFont(FONT_SMALL); - break; - case 2: - display->setFont(FONT_LARGE); - break; - default: - display->setFont(FONT_MEDIUM); - break; - } - - display->setTextAlignment(TEXT_ALIGN_LEFT); - const char *title = oemStore.oem_text; - display->drawString(x + getStringCenteredX(title), y + SCREEN_HEIGHT - FONT_HEIGHT_MEDIUM, title); - display->setFont(FONT_SMALL); - - // Draw region in upper left - if (upperMsg) - display->drawString(x + 0, y + 0, upperMsg); - - // Draw version and shortname in upper right - char buf[25]; - snprintf(buf, sizeof(buf), "%s\n%s", xstr(APP_VERSION_SHORT), haveGlyphs(owner.short_name) ? owner.short_name : ""); - - display->setTextAlignment(TEXT_ALIGN_RIGHT); - display->drawString(x + SCREEN_WIDTH, y + 0, buf); - screen->forceDisplay(); - - display->setTextAlignment(TEXT_ALIGN_LEFT); // Restore left align, just to be kind to any other unsuspecting code -} - -static void drawOEMBootScreen(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y) -{ - // Draw region in upper left - const char *region = myRegion ? myRegion->name : NULL; - drawOEMIconScreen(region, display, state, x, y); -} - void Screen::drawFrameText(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y, const char *message) { uint16_t x_offset = display->width() / 2; @@ -292,11 +241,11 @@ static void drawWelcomeScreen(OLEDDisplay *display, OLEDDisplayUiState *state, i // draw overlay in bottom right corner of screen to show when notifications are muted or modifier key is active static void drawFunctionOverlay(OLEDDisplay *display, OLEDDisplayUiState *state) { - // LOG_DEBUG("Drawing function overlay\n"); - if (functionSymbals.begin() != functionSymbals.end()) { + // LOG_DEBUG("Draw function overlay"); + if (functionSymbol.begin() != functionSymbol.end()) { char buf[64]; display->setFont(FONT_SMALL); - snprintf(buf, sizeof(buf), "%s", functionSymbalString.c_str()); + snprintf(buf, sizeof(buf), "%s", functionSymbolString.c_str()); display->drawString(SCREEN_WIDTH - display->getStringWidth(buf), SCREEN_HEIGHT - FONT_HEIGHT_SMALL, buf); } } @@ -310,7 +259,7 @@ static void drawDeepSleepScreen(OLEDDisplay *display, OLEDDisplayUiState *state, EINK_ADD_FRAMEFLAG(display, COSMETIC); EINK_ADD_FRAMEFLAG(display, BLOCKING); - LOG_DEBUG("Drawing deep sleep screen\n"); + LOG_DEBUG("Draw deep sleep screen"); // Display displayStr on the screen drawIconScreen("Sleeping", display, state, x, y); @@ -319,7 +268,7 @@ static void drawDeepSleepScreen(OLEDDisplay *display, OLEDDisplayUiState *state, /// Used on eink displays when screen updates are paused static void drawScreensaverOverlay(OLEDDisplay *display, OLEDDisplayUiState *state) { - LOG_DEBUG("Drawing screensaver overlay\n"); + LOG_DEBUG("Draw screensaver overlay"); EINK_ADD_FRAMEFLAG(display, COSMETIC); // Take the opportunity for a full-refresh @@ -385,9 +334,9 @@ static void drawModuleFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int } else { // otherwise, just display the module frame that's aligned with the current frame module_frame = state->currentFrame; - // LOG_DEBUG("Screen is not in transition. Frame: %d\n\n", module_frame); + // LOG_DEBUG("Screen is not in transition. Frame: %d", module_frame); } - // LOG_DEBUG("Drawing Module Frame %d\n\n", module_frame); + // LOG_DEBUG("Draw Module Frame %d", module_frame); MeshModule &pi = *moduleFrames.at(module_frame); pi.drawFrame(display, state, x, y); } @@ -446,7 +395,7 @@ static void drawBattery(OLEDDisplay *display, int16_t x, int16_t y, uint8_t *img display->drawFastImage(x, y, 16, 8, imgBuffer); } -#ifdef T_WATCH_S3 +#if defined(DISPLAY_CLOCK_FRAME) void Screen::drawWatchFaceToggleButton(OLEDDisplay *display, int16_t x, int16_t y, bool digitalMode, float scale) { @@ -962,7 +911,7 @@ static void drawTextMessageFrame(OLEDDisplay *display, OLEDDisplayUiState *state const meshtastic_MeshPacket &mp = devicestate.rx_text_message; meshtastic_NodeInfoLite *node = nodeDB->getMeshNode(getFrom(&mp)); - // LOG_DEBUG("drawing text message from 0x%x: %s\n", mp.from, + // LOG_DEBUG("Draw text message from 0x%x: %s", mp.from, // mp.decoded.variant.data.decoded.bytes); // Demo for drawStringMaxWidth: @@ -1008,55 +957,65 @@ static void drawTextMessageFrame(OLEDDisplay *display, OLEDDisplayUiState *state display->setColor(WHITE); #ifndef EXCLUDE_EMOJI - if (strcmp(reinterpret_cast(mp.decoded.payload.bytes), "\U0001F44D") == 0) { + const char *msg = reinterpret_cast(mp.decoded.payload.bytes); + if (strcmp(msg, "\U0001F44D") == 0) { display->drawXbm(x + (SCREEN_WIDTH - thumbs_width) / 2, y + (SCREEN_HEIGHT - FONT_HEIGHT_MEDIUM - thumbs_height) / 2 + 2 + 5, thumbs_width, thumbs_height, thumbup); - } else if (strcmp(reinterpret_cast(mp.decoded.payload.bytes), "\U0001F44E") == 0) { + } else if (strcmp(msg, "\U0001F44E") == 0) { display->drawXbm(x + (SCREEN_WIDTH - thumbs_width) / 2, y + (SCREEN_HEIGHT - FONT_HEIGHT_MEDIUM - thumbs_height) / 2 + 2 + 5, thumbs_width, thumbs_height, thumbdown); - } else if (strcmp(reinterpret_cast(mp.decoded.payload.bytes), "❓") == 0) { + } else if (strcmp(msg, "\U0001F60A") == 0 || strcmp(msg, "\U0001F600") == 0 || strcmp(msg, "\U0001F642") == 0 || + strcmp(msg, "\U0001F609") == 0 || + strcmp(msg, "\U0001F601") == 0) { // matches 5 different common smileys, so that the phone user doesn't have to + // remember which one is compatible + display->drawXbm(x + (SCREEN_WIDTH - smiley_width) / 2, + y + (SCREEN_HEIGHT - FONT_HEIGHT_MEDIUM - smiley_height) / 2 + 2 + 5, smiley_width, smiley_height, + smiley); + } else if (strcmp(msg, "❓") == 0) { display->drawXbm(x + (SCREEN_WIDTH - question_width) / 2, y + (SCREEN_HEIGHT - FONT_HEIGHT_MEDIUM - question_height) / 2 + 2 + 5, question_width, question_height, question); - } else if (strcmp(reinterpret_cast(mp.decoded.payload.bytes), "‼️") == 0) { + } else if (strcmp(msg, "‼️") == 0) { display->drawXbm(x + (SCREEN_WIDTH - bang_width) / 2, y + (SCREEN_HEIGHT - FONT_HEIGHT_MEDIUM - bang_height) / 2 + 2 + 5, bang_width, bang_height, bang); - } else if (strcmp(reinterpret_cast(mp.decoded.payload.bytes), "\U0001F4A9") == 0) { + } else if (strcmp(msg, "\U0001F4A9") == 0) { display->drawXbm(x + (SCREEN_WIDTH - poo_width) / 2, y + (SCREEN_HEIGHT - FONT_HEIGHT_MEDIUM - poo_height) / 2 + 2 + 5, poo_width, poo_height, poo); - } else if (strcmp(reinterpret_cast(mp.decoded.payload.bytes), "\xf0\x9f\xa4\xa3") == 0) { + } else if (strcmp(msg, "\U0001F923") == 0) { display->drawXbm(x + (SCREEN_WIDTH - haha_width) / 2, y + (SCREEN_HEIGHT - FONT_HEIGHT_MEDIUM - haha_height) / 2 + 2 + 5, haha_width, haha_height, haha); - } else if (strcmp(reinterpret_cast(mp.decoded.payload.bytes), "\U0001F44B") == 0) { + } else if (strcmp(msg, "\U0001F44B") == 0) { display->drawXbm(x + (SCREEN_WIDTH - wave_icon_width) / 2, y + (SCREEN_HEIGHT - FONT_HEIGHT_MEDIUM - wave_icon_height) / 2 + 2 + 5, wave_icon_width, wave_icon_height, wave_icon); - } else if (strcmp(reinterpret_cast(mp.decoded.payload.bytes), "\U0001F920") == 0) { + } else if (strcmp(msg, "\U0001F920") == 0) { display->drawXbm(x + (SCREEN_WIDTH - cowboy_width) / 2, y + (SCREEN_HEIGHT - FONT_HEIGHT_MEDIUM - cowboy_height) / 2 + 2 + 5, cowboy_width, cowboy_height, cowboy); - } else if (strcmp(reinterpret_cast(mp.decoded.payload.bytes), "\U0001F42D") == 0) { + } else if (strcmp(msg, "\U0001F42D") == 0) { display->drawXbm(x + (SCREEN_WIDTH - deadmau5_width) / 2, y + (SCREEN_HEIGHT - FONT_HEIGHT_MEDIUM - deadmau5_height) / 2 + 2 + 5, deadmau5_width, deadmau5_height, deadmau5); - } else if (strcmp(reinterpret_cast(mp.decoded.payload.bytes), "\xE2\x98\x80\xEF\xB8\x8F") == 0) { + } else if (strcmp(msg, "\xE2\x98\x80\xEF\xB8\x8F") == 0) { display->drawXbm(x + (SCREEN_WIDTH - sun_width) / 2, y + (SCREEN_HEIGHT - FONT_HEIGHT_MEDIUM - sun_height) / 2 + 2 + 5, sun_width, sun_height, sun); - } else if (strcmp(reinterpret_cast(mp.decoded.payload.bytes), "\u2614") == 0) { + } else if (strcmp(msg, "\u2614") == 0) { display->drawXbm(x + (SCREEN_WIDTH - rain_width) / 2, y + (SCREEN_HEIGHT - FONT_HEIGHT_MEDIUM - rain_height) / 2 + 2 + 10, rain_width, rain_height, rain); - } else if (strcmp(reinterpret_cast(mp.decoded.payload.bytes), "☁️") == 0) { + } else if (strcmp(msg, "☁️") == 0) { display->drawXbm(x + (SCREEN_WIDTH - cloud_width) / 2, y + (SCREEN_HEIGHT - FONT_HEIGHT_MEDIUM - cloud_height) / 2 + 2 + 5, cloud_width, cloud_height, cloud); - } else if (strcmp(reinterpret_cast(mp.decoded.payload.bytes), "🌫️") == 0) { + } else if (strcmp(msg, "🌫️") == 0) { display->drawXbm(x + (SCREEN_WIDTH - fog_width) / 2, y + (SCREEN_HEIGHT - FONT_HEIGHT_MEDIUM - fog_height) / 2 + 2 + 5, fog_width, fog_height, fog); - } else if (strcmp(reinterpret_cast(mp.decoded.payload.bytes), "\xf0\x9f\x98\x88") == 0) { + } else if (strcmp(msg, "\U0001F608") == 0) { display->drawXbm(x + (SCREEN_WIDTH - devil_width) / 2, y + (SCREEN_HEIGHT - FONT_HEIGHT_MEDIUM - devil_height) / 2 + 2 + 5, devil_width, devil_height, devil); - } else if (strcmp(reinterpret_cast(mp.decoded.payload.bytes), "♥️") == 0) { + } else if (strcmp(msg, "♥️") == 0 || strcmp(msg, "\U0001F9E1") == 0 || strcmp(msg, "\U00002763") == 0 || + strcmp(msg, "\U00002764") == 0 || strcmp(msg, "\U0001F495") == 0 || strcmp(msg, "\U0001F496") == 0 || + strcmp(msg, "\U0001F497") == 0 || strcmp(msg, "\U0001F496") == 0) { display->drawXbm(x + (SCREEN_WIDTH - heart_width) / 2, y + (SCREEN_HEIGHT - FONT_HEIGHT_MEDIUM - heart_height) / 2 + 2 + 5, heart_width, heart_height, heart); } else { @@ -1460,7 +1419,7 @@ static void drawNodeInfo(OLEDDisplay *display, OLEDDisplayUiState *state, int16_ } bool hasNodeHeading = false; - if (ourNode && (hasValidPosition(ourNode) || screen->hasHeading())) { + if (ourNode && (nodeDB->hasValidPosition(ourNode) || screen->hasHeading())) { const meshtastic_PositionLite &op = ourNode->position; float myHeading; if (screen->hasHeading()) @@ -1469,7 +1428,7 @@ static void drawNodeInfo(OLEDDisplay *display, OLEDDisplayUiState *state, int16_ myHeading = screen->estimatedHeading(DegD(op.latitude_i), DegD(op.longitude_i)); screen->drawCompassNorth(display, compassX, compassY, myHeading); - if (hasValidPosition(node)) { + if (nodeDB->hasValidPosition(node)) { // display direction toward node hasNodeHeading = true; const meshtastic_PositionLite &p = node->position; @@ -1500,7 +1459,7 @@ static void drawNodeInfo(OLEDDisplay *display, OLEDDisplayUiState *state, int16_ if (!hasNodeHeading) { // direction to node is unknown so display question mark // Debug info for gps lock errors - // LOG_DEBUG("ourNode %d, ourPos %d, theirPos %d\n", !!ourNode, ourNode && hasValidPosition(ourNode), + // LOG_DEBUG("ourNode %d, ourPos %d, theirPos %d", !!ourNode, ourNode && hasValidPosition(ourNode), // hasValidPosition(node)); display->drawString(compassX - FONT_HEIGHT_SMALL / 4, compassY - FONT_HEIGHT_SMALL / 2, "?"); } @@ -1549,7 +1508,7 @@ Screen::Screen(ScanI2C::DeviceAddress address, meshtastic_Config_DisplayConfig_O (address.port == ScanI2C::I2CPort::WIRE1) ? HW_I2C::I2C_TWO : HW_I2C::I2C_ONE); #elif ARCH_PORTDUINO if (settingsMap[displayPanel] != no_screen) { - LOG_DEBUG("Making TFTDisplay!\n"); + LOG_DEBUG("Make TFTDisplay!"); dispdev = new TFTDisplay(address.address, -1, -1, geometry, (address.port == ScanI2C::I2CPort::WIRE1) ? HW_I2C::I2C_TWO : HW_I2C::I2C_ONE); } else { @@ -1596,7 +1555,7 @@ void Screen::handleSetOn(bool on, FrameCallback einkScreensaver) if (on != screenOn) { if (on) { - LOG_INFO("Turning on screen\n"); + LOG_INFO("Turn on screen"); powerMon->setState(meshtastic_PowerMon_State_Screen_On); #ifdef T_WATCH_S3 PMU->enablePowerOutput(XPOWERS_ALDO2); @@ -1631,7 +1590,7 @@ void Screen::handleSetOn(bool on, FrameCallback einkScreensaver) // eInkScreensaver parameter is usually NULL (default argument), default frame used instead setScreensaverFrames(einkScreensaver); #endif - LOG_INFO("Turning off screen\n"); + LOG_INFO("Turn off screen"); dispdev->displayOff(); #ifdef USE_ST7789 SPI1.end(); @@ -1699,9 +1658,6 @@ void Screen::setup() // Set the utf8 conversion function dispdev->setFontTableLookupFunction(customFontTableLookup); - if (strlen(oemStore.oem_text) > 0) - logo_timeout *= 2; - // Add frames. EINK_ADD_FRAMEFLAG(dispdev, DEMAND_FAST); alertFrames[0] = [this](OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y) -> void { @@ -1842,28 +1798,11 @@ int32_t Screen::runOnce() // serialSinceMsec adjusts for additional serial wait time during nRF52 bootup static bool showingBootScreen = true; if (showingBootScreen && (millis() > (logo_timeout + serialSinceMsec))) { - LOG_INFO("Done with boot screen...\n"); + LOG_INFO("Done with boot screen"); stopBootScreen(); showingBootScreen = false; } - // If we have an OEM Boot screen, toggle after logo_timeout seconds - if (strlen(oemStore.oem_text) > 0) { - static bool showingOEMBootScreen = true; - if (showingOEMBootScreen && (millis() > ((logo_timeout / 2) + serialSinceMsec))) { - LOG_INFO("Switch to OEM screen...\n"); - // Change frames. - static FrameCallback bootOEMFrames[] = {drawOEMBootScreen}; - static const int bootOEMFrameCount = sizeof(bootOEMFrames) / sizeof(bootOEMFrames[0]); - ui->setFrames(bootOEMFrames, bootOEMFrameCount); - ui->update(); -#ifndef USE_EINK - ui->update(); -#endif - showingOEMBootScreen = false; - } - } - #ifndef DISABLE_WELCOME_UNSET if (showingNormalScreen && config.lora.region == meshtastic_Config_LoRaConfig_RegionCode_UNSET) { setWelcomeFrames(); @@ -1917,7 +1856,7 @@ int32_t Screen::runOnce() free(cmd.print_text); break; default: - LOG_ERROR("Invalid screen cmd\n"); + LOG_ERROR("Invalid screen cmd"); } } @@ -1955,12 +1894,12 @@ int32_t Screen::runOnce() EINK_ADD_FRAMEFLAG(dispdev, COSMETIC); #endif - LOG_DEBUG("LastScreenTransition exceeded %ums transitioning to next frame\n", (millis() - lastScreenTransition)); + LOG_DEBUG("LastScreenTransition exceeded %ums transition to next frame", (millis() - lastScreenTransition)); handleOnPress(); } } - // LOG_DEBUG("want fps %d, fixed=%d\n", targetFramerate, + // LOG_DEBUG("want fps %d, fixed=%d", targetFramerate, // ui->getUiState()->frameState); If we are scrolling we need to be called // soon, otherwise just 1 fps (to save CPU) We also ask to be called twice // as fast as we really need so that any rounding errors still result with @@ -1991,7 +1930,7 @@ void Screen::drawDebugInfoWiFiTrampoline(OLEDDisplay *display, OLEDDisplayUiStat void Screen::setSSLFrames() { if (address_found.address) { - // LOG_DEBUG("showing SSL frames\n"); + // LOG_DEBUG("Show SSL frames"); static FrameCallback sslFrames[] = {drawSSLScreen}; ui->setFrames(sslFrames, 1); ui->update(); @@ -2003,7 +1942,7 @@ void Screen::setSSLFrames() void Screen::setWelcomeFrames() { if (address_found.address) { - // LOG_DEBUG("showing Welcome frames\n"); + // LOG_DEBUG("Show Welcome frames"); static FrameCallback frames[] = {drawWelcomeScreen}; setFrameImmediateDraw(frames); } @@ -2069,7 +2008,7 @@ void Screen::setFrames(FrameFocus focus) uint8_t originalPosition = ui->getUiState()->currentFrame; FramesetInfo fsi; // Location of specific frames, for applying focus parameter - LOG_DEBUG("showing standard frames\n"); + LOG_DEBUG("Show standard frames"); showingNormalScreen = true; #ifdef USE_EINK @@ -2082,10 +2021,10 @@ void Screen::setFrames(FrameFocus focus) #endif moduleFrames = MeshModule::GetMeshModulesWithUIFrames(); - LOG_DEBUG("Showing %d module frames\n", moduleFrames.size()); + LOG_DEBUG("Show %d module frames", moduleFrames.size()); #ifdef DEBUG_PORT int totalFrameCount = MAX_NUM_NODES + NUM_EXTRA_FRAMES + moduleFrames.size(); - LOG_DEBUG("Total frame count: %d\n", totalFrameCount); + LOG_DEBUG("Total frame count: %d", totalFrameCount); #endif // We don't show the node info of our node (if we have it yet - we should) @@ -2119,7 +2058,7 @@ void Screen::setFrames(FrameFocus focus) numframes++; } - LOG_DEBUG("Added modules. numframes: %d\n", numframes); + LOG_DEBUG("Added modules. numframes: %d", numframes); // If we have a critical fault, show it first fsi.positions.fault = numframes; @@ -2128,7 +2067,7 @@ void Screen::setFrames(FrameFocus focus) focus = FOCUS_FAULT; // Change our "focus" parameter, to ensure we show the fault frame } -#ifdef T_WATCH_S3 +#if defined(DISPLAY_CLOCK_FRAME) normalFrames[numframes++] = screen->digitalWatchFace ? &Screen::drawDigitalClockFrame : &Screen::drawAnalogClockFrame; #endif @@ -2164,7 +2103,7 @@ void Screen::setFrames(FrameFocus focus) #endif fsi.frameCount = numframes; // Total framecount is used to apply FOCUS_PRESERVE - LOG_DEBUG("Finished building frames. numframes: %d\n", numframes); + LOG_DEBUG("Finished build frames. numframes: %d", numframes); ui->setFrames(normalFrames, numframes); ui->enableAllIndicators(); @@ -2245,13 +2184,13 @@ void Screen::dismissCurrentFrame() bool dismissed = false; if (currentFrame == framesetInfo.positions.textMessage && devicestate.has_rx_text_message) { - LOG_INFO("Dismissing Text Message\n"); + LOG_INFO("Dismiss Text Message"); devicestate.has_rx_text_message = false; dismissed = true; } else if (currentFrame == framesetInfo.positions.waypoint && devicestate.has_rx_waypoint) { - LOG_DEBUG("Dismissing Waypoint\n"); + LOG_DEBUG("Dismiss Waypoint"); devicestate.has_rx_waypoint = false; dismissed = true; } @@ -2263,7 +2202,7 @@ void Screen::dismissCurrentFrame() void Screen::handleStartFirmwareUpdateScreen() { - LOG_DEBUG("showing firmware screen\n"); + LOG_DEBUG("Show firmware screen"); showingNormalScreen = false; EINK_ADD_FRAMEFLAG(dispdev, DEMAND_FAST); // E-Ink: Explicitly use fast-refresh for next frame @@ -2312,24 +2251,24 @@ void Screen::decreaseBrightness() /* TO DO: add little popup in center of screen saying what brightness level it is set to*/ } -void Screen::setFunctionSymbal(std::string sym) +void Screen::setFunctionSymbol(std::string sym) { - if (std::find(functionSymbals.begin(), functionSymbals.end(), sym) == functionSymbals.end()) { - functionSymbals.push_back(sym); - functionSymbalString = ""; - for (auto symbol : functionSymbals) { - functionSymbalString = symbol + " " + functionSymbalString; + if (std::find(functionSymbol.begin(), functionSymbol.end(), sym) == functionSymbol.end()) { + functionSymbol.push_back(sym); + functionSymbolString = ""; + for (auto symbol : functionSymbol) { + functionSymbolString = symbol + " " + functionSymbolString; } setFastFramerate(); } } -void Screen::removeFunctionSymbal(std::string sym) +void Screen::removeFunctionSymbol(std::string sym) { - functionSymbals.erase(std::remove(functionSymbals.begin(), functionSymbals.end(), sym), functionSymbals.end()); - functionSymbalString = ""; - for (auto symbol : functionSymbals) { - functionSymbalString = symbol + " " + functionSymbalString; + functionSymbol.erase(std::remove(functionSymbol.begin(), functionSymbol.end(), sym), functionSymbol.end()); + functionSymbolString = ""; + for (auto symbol : functionSymbol) { + functionSymbolString = symbol + " " + functionSymbolString; } setFastFramerate(); } @@ -2355,7 +2294,7 @@ void Screen::handlePrint(const char *text) { // the string passed into us probably has a newline, but that would confuse the logging system // so strip it - LOG_DEBUG("Screen: %.*s\n", strlen(text) - 1, text); + LOG_DEBUG("Screen: %.*s", strlen(text) - 1, text); if (!useDisplay || !showingNormalScreen) return; @@ -2708,7 +2647,7 @@ void DebugInfo::drawFrameSettings(OLEDDisplay *display, OLEDDisplayUiState *stat int Screen::handleStatusUpdate(const meshtastic::Status *arg) { - // LOG_DEBUG("Screen got status update %d\n", arg->getStatusType()); + // LOG_DEBUG("Screen got status update %d", arg->getStatusType()); switch (arg->getStatusType()) { case STATUS_TYPE_NODE: if (showingNormalScreen && nodeStatus->getLastNumTotal() != nodeStatus->getNumTotal()) { @@ -2744,7 +2683,7 @@ int Screen::handleUIFrameEvent(const UIFrameEvent *event) if (event->action == UIFrameEvent::Action::REGENERATE_FRAMESET) setFrames(FOCUS_MODULE); - // Regenerate the frameset, while attempting to maintain focus on the current frame + // Regenerate the frameset, while Attempt to maintain focus on the current frame else if (event->action == UIFrameEvent::Action::REGENERATE_FRAMESET_BACKGROUND) setFrames(FOCUS_PRESERVE); @@ -2759,7 +2698,7 @@ int Screen::handleUIFrameEvent(const UIFrameEvent *event) int Screen::handleInputEvent(const InputEvent *event) { -#ifdef T_WATCH_S3 +#if defined(DISPLAY_CLOCK_FRAME) // For the T-Watch, intercept touches to the 'toggle digital/analog watch face' button uint8_t watchFaceFrame = error_code ? 1 : 0; @@ -2817,4 +2756,4 @@ int Screen::handleAdminMessage(const meshtastic_AdminMessage *arg) } // namespace graphics #else graphics::Screen::Screen(ScanI2C::DeviceAddress, meshtastic_Config_DisplayConfig_OledType, OLEDDISPLAY_GEOMETRY) {} -#endif // HAS_SCREEN \ No newline at end of file +#endif // HAS_SCREEN diff --git a/src/graphics/Screen.h b/src/graphics/Screen.h index b2e6e905b..41c90ca9a 100644 --- a/src/graphics/Screen.h +++ b/src/graphics/Screen.h @@ -1,5 +1,6 @@ #pragma once +#include "../userPrefs.h" #include "configuration.h" #include "detect/ScanI2C.h" @@ -24,8 +25,8 @@ class Screen void startFirmwareUpdateScreen() {} void increaseBrightness() {} void decreaseBrightness() {} - void setFunctionSymbal(std::string) {} - void removeFunctionSymbal(std::string) {} + void setFunctionSymbol(std::string) {} + void removeFunctionSymbol(std::string) {} void startAlert(const char *) {} void endAlert() {} }; @@ -282,8 +283,8 @@ class Screen : public concurrency::OSThread void increaseBrightness(); void decreaseBrightness(); - void setFunctionSymbal(std::string sym); - void removeFunctionSymbal(std::string sym); + void setFunctionSymbol(std::string sym); + void removeFunctionSymbol(std::string sym); /// Stops showing the boot screen. void stopBootScreen() { enqueueCmd(ScreenCmd{.cmd = Cmd::STOP_BOOT_SCREEN}); } @@ -327,10 +328,15 @@ class Screen : public concurrency::OSThread SKIPREST = false; return (uint8_t)ch; } + + case 0xC3: { + SKIPREST = false; + return (uint8_t)(ch | 0xC0); + } } // We want to strip out prefix chars for two-byte char formats - if (ch == 0xC2) + if (ch == 0xC2 || ch == 0xC3) return (uint8_t)0; #if defined(OLED_PL) @@ -549,7 +555,7 @@ class Screen : public concurrency::OSThread static void drawDebugInfoWiFiTrampoline(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y); -#ifdef T_WATCH_S3 +#if defined(DISPLAY_CLOCK_FRAME) static void drawAnalogClockFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y); static void drawDigitalClockFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y); @@ -600,4 +606,4 @@ class Screen : public concurrency::OSThread } // namespace graphics -#endif \ No newline at end of file +#endif diff --git a/src/graphics/ScreenFonts.h b/src/graphics/ScreenFonts.h index c9ce961fa..032348d54 100644 --- a/src/graphics/ScreenFonts.h +++ b/src/graphics/ScreenFonts.h @@ -27,14 +27,22 @@ #define FONT_SMALL ArialMT_Plain_10_RU #else #ifdef OLED_UA -#define FONT_SMALL ArialMT_Plain_10_UA +#define FONT_SMALL ArialMT_Plain_10_UA // Height: 13 #else #define FONT_SMALL ArialMT_Plain_10 // Height: 13 #endif #endif #endif +#ifdef OLED_UA +#define FONT_MEDIUM ArialMT_Plain_16_UA // Height: 19 +#else #define FONT_MEDIUM ArialMT_Plain_16 // Height: 19 -#define FONT_LARGE ArialMT_Plain_24 // Height: 28 +#endif +#ifdef OLED_UA +#define FONT_LARGE ArialMT_Plain_24_UA // Height: 28 +#else +#define FONT_LARGE ArialMT_Plain_24 // Height: 28 +#endif #endif #define _fontHeight(font) ((font)[1] + 1) // height is position 1 diff --git a/src/graphics/TFTDisplay.cpp b/src/graphics/TFTDisplay.cpp index 0c32a7c32..87c3f7de9 100644 --- a/src/graphics/TFTDisplay.cpp +++ b/src/graphics/TFTDisplay.cpp @@ -380,7 +380,7 @@ class LGFX : public lgfx::LGFX_Device _panel_instance->setBus(&_bus_instance); // set the bus on the panel. auto cfg = _panel_instance->config(); // Gets a structure for display panel settings. - LOG_DEBUG("Height: %d, Width: %d \n", settingsMap[displayHeight], settingsMap[displayWidth]); + LOG_DEBUG("Height: %d, Width: %d ", settingsMap[displayHeight], settingsMap[displayWidth]); cfg.pin_cs = settingsMap[displayCS]; // Pin number where CS is connected (-1 = disable) cfg.pin_rst = settingsMap[displayReset]; cfg.panel_width = settingsMap[displayWidth]; // actual displayable width @@ -643,7 +643,7 @@ GpioPin *TFTDisplay::backlightEnable = NULL; TFTDisplay::TFTDisplay(uint8_t address, int sda, int scl, OLEDDISPLAY_GEOMETRY geometry, HW_I2C i2cBus) { - LOG_DEBUG("TFTDisplay!\n"); + LOG_DEBUG("TFTDisplay!"); #ifdef TFT_BL GpioPin *p = new GpioHwPin(TFT_BL); @@ -712,7 +712,7 @@ void TFTDisplay::sendCommand(uint8_t com) // handle display on/off directly switch (com) { case DISPLAYON: { - // LOG_DEBUG("Display on\n"); + // LOG_DEBUG("Display on"); backlightEnable->set(true); #if ARCH_PORTDUINO display(true); @@ -736,7 +736,7 @@ void TFTDisplay::sendCommand(uint8_t com) break; } case DISPLAYOFF: { - // LOG_DEBUG("Display off\n"); + // LOG_DEBUG("Display off"); backlightEnable->set(false); #if ARCH_PORTDUINO tft->clear(); @@ -772,14 +772,14 @@ void TFTDisplay::setDisplayBrightness(uint8_t _brightness) // todo #else tft->setBrightness(_brightness); - LOG_DEBUG("Brightness is set to value: %i \n", _brightness); + LOG_DEBUG("Brightness is set to value: %i ", _brightness); #endif } void TFTDisplay::flipScreenVertically() { #if defined(T_WATCH_S3) - LOG_DEBUG("Flip TFT vertically\n"); // T-Watch S3 right-handed orientation + LOG_DEBUG("Flip TFT vertically"); // T-Watch S3 right-handed orientation tft->setRotation(0); #endif } @@ -823,7 +823,7 @@ void TFTDisplay::setDetected(uint8_t detected) bool TFTDisplay::connect() { concurrency::LockGuard g(spiLock); - LOG_INFO("Doing TFT init\n"); + LOG_INFO("Do TFT init"); #ifdef RAK14014 tft = new TFT_eSPI; #else @@ -831,7 +831,7 @@ bool TFTDisplay::connect() #endif backlightEnable->set(true); - LOG_INFO("Power to TFT Backlight\n"); + LOG_INFO("Power to TFT Backlight"); #ifdef UNPHONE unphone.backlight(true); // using unPhone library diff --git a/src/graphics/fonts/OLEDDisplayFontsUA.cpp b/src/graphics/fonts/OLEDDisplayFontsUA.cpp index 0295ee6ba..2a97526ef 100644 --- a/src/graphics/fonts/OLEDDisplayFontsUA.cpp +++ b/src/graphics/fonts/OLEDDisplayFontsUA.cpp @@ -166,71 +166,71 @@ const uint8_t ArialMT_Plain_10_UA[] PROGMEM = { 0x04, 0x6F, 0x10, 0x09, // 188 0x04, 0x7F, 0x10, 0x09, // 189 0x04, 0x8F, 0x10, 0x09, // 190 - 0x04, 0x9F, 0x0A, 0x06, // 191 - 0x04, 0xA9, 0x0C, 0x07, // 192 - 0x04, 0xB5, 0x0C, 0x07, // 193 - 0x04, 0xC1, 0x0C, 0x07, // 194 - 0x04, 0xCD, 0x0B, 0x07, // 195 - 0x04, 0xD8, 0x0C, 0x07, // 196 - 0x04, 0xE4, 0x0C, 0x07, // 197 - 0x04, 0xF0, 0x0C, 0x07, // 198 - 0x04, 0xFC, 0x0C, 0x07, // 199 - 0x05, 0x08, 0x0C, 0x07, // 200 - 0x05, 0x14, 0x0C, 0x07, // 201 - 0x05, 0x20, 0x0C, 0x07, // 202 - 0x05, 0x2C, 0x0C, 0x07, // 203 - 0x05, 0x38, 0x0C, 0x07, // 204 - 0x05, 0x44, 0x0C, 0x07, // 205 - 0x05, 0x50, 0x0C, 0x07, // 206 - 0x05, 0x5C, 0x0C, 0x07, // 207 - 0x05, 0x68, 0x0B, 0x07, // 208 - 0x05, 0x73, 0x0C, 0x07, // 209 - 0x05, 0x7F, 0x0B, 0x07, // 210 - 0x05, 0x8A, 0x0C, 0x07, // 211 - 0x05, 0x96, 0x0B, 0x07, // 212 - 0x05, 0xA1, 0x0C, 0x07, // 213 - 0x05, 0xAD, 0x0C, 0x07, // 214 - 0x05, 0xB9, 0x0C, 0x07, // 215 - 0x05, 0xC5, 0x0C, 0x07, // 216 - 0x05, 0xD1, 0x0E, 0x08, // 217 - 0x05, 0xDF, 0x0C, 0x07, // 218 - 0x05, 0xEB, 0x0C, 0x07, // 219 - 0x05, 0xF7, 0x0C, 0x07, // 220 - 0x06, 0x03, 0x0C, 0x07, // 221 - 0x06, 0x0F, 0x0C, 0x07, // 222 - 0x06, 0x1B, 0x0C, 0x07, // 223 - 0x06, 0x27, 0x0C, 0x07, // 224 - 0x06, 0x33, 0x0C, 0x07, // 225 - 0x06, 0x3F, 0x0C, 0x07, // 226 - 0x06, 0x4B, 0x0B, 0x07, // 227 - 0x06, 0x56, 0x0C, 0x07, // 228 - 0x06, 0x62, 0x0B, 0x07, // 229 - 0x06, 0x6D, 0x0C, 0x07, // 230 - 0x06, 0x79, 0x0C, 0x07, // 231 - 0x06, 0x85, 0x0C, 0x07, // 232 - 0x06, 0x91, 0x0C, 0x07, // 233 - 0x06, 0x9D, 0x0C, 0x07, // 234 - 0x06, 0xA9, 0x0C, 0x07, // 235 - 0x06, 0xB5, 0x0C, 0x07, // 236 - 0x06, 0xC1, 0x0C, 0x07, // 237 - 0x06, 0xCD, 0x0C, 0x07, // 238 - 0x06, 0xD9, 0x0C, 0x07, // 239 - 0x06, 0xE5, 0x0B, 0x07, // 240 - 0x06, 0xF0, 0x0C, 0x07, // 241 - 0x06, 0xFC, 0x0B, 0x07, // 242 - 0x07, 0x07, 0x0C, 0x07, // 243 - 0x07, 0x13, 0x0B, 0x07, // 244 - 0x07, 0x1E, 0x0C, 0x07, // 245 - 0x07, 0x2A, 0x0C, 0x07, // 246 - 0x07, 0x36, 0x0C, 0x07, // 247 - 0x07, 0x42, 0x0C, 0x07, // 248 - 0x07, 0x4E, 0x0E, 0x08, // 249 - 0x07, 0x5C, 0x0C, 0x07, // 250 - 0x07, 0x68, 0x0C, 0x07, // 251 - 0x07, 0x74, 0x0C, 0x07, // 252 - 0x07, 0x80, 0x0C, 0x07, // 253 - 0x07, 0x8C, 0x0C, 0x07, // 254 - 0x07, 0x98, 0x0C, 0x07, // 255 + 0x04, 0x9F, 0x06, 0x04, // 191 + 0x04, 0xA5, 0x0A, 0x06, // 192 + 0x04, 0xAF, 0x0A, 0x06, // 193 + 0x04, 0xB9, 0x0A, 0x06, // 194 + 0x04, 0xC3, 0x09, 0x06, // 195 + 0x04, 0xCC, 0x0A, 0x06, // 196 + 0x04, 0xD6, 0x0A, 0x06, // 197 + 0x04, 0xE0, 0x0A, 0x06, // 198 + 0x04, 0xEA, 0x08, 0x05, // 199 + 0x04, 0xF2, 0x0A, 0x06, // 200 + 0x04, 0xFC, 0x0A, 0x06, // 201 + 0x05, 0x06, 0x0A, 0x06, // 202 + 0x05, 0x10, 0x0A, 0x06, // 203 + 0x05, 0x1A, 0x0A, 0x06, // 204 + 0x05, 0x24, 0x0A, 0x06, // 205 + 0x05, 0x2E, 0x0A, 0x06, // 206 + 0x05, 0x38, 0x0A, 0x06, // 207 + 0x05, 0x42, 0x09, 0x06, // 208 + 0x05, 0x4B, 0x0A, 0x06, // 209 + 0x05, 0x55, 0x09, 0x06, // 210 + 0x05, 0x5E, 0x0A, 0x06, // 211 + 0x05, 0x68, 0x09, 0x06, // 212 + 0x05, 0x71, 0x0A, 0x06, // 213 + 0x05, 0x7B, 0x0A, 0x06, // 214 + 0x05, 0x85, 0x08, 0x05, // 215 + 0x05, 0x8D, 0x0A, 0x06, // 216 + 0x05, 0x97, 0x0C, 0x07, // 217 + 0x05, 0xA3, 0x0A, 0x06, // 218 + 0x05, 0xAD, 0x0A, 0x06, // 219 + 0x05, 0xB7, 0x08, 0x05, // 220 + 0x05, 0xBF, 0x0A, 0x06, // 221 + 0x05, 0xC9, 0x0A, 0x06, // 222 + 0x05, 0xD3, 0x0A, 0x06, // 223 + 0x05, 0xDD, 0x08, 0x05, // 224 + 0x05, 0xE5, 0x08, 0x05, // 225 + 0x05, 0xED, 0x08, 0x05, // 226 + 0x05, 0xF5, 0x07, 0x05, // 227 + 0x05, 0xFC, 0x0A, 0x06, // 228 + 0x06, 0x06, 0x09, 0x06, // 229 + 0x06, 0x0F, 0x0A, 0x06, // 230 + 0x06, 0x19, 0x08, 0x05, // 231 + 0x06, 0x21, 0x0A, 0x06, // 232 + 0x06, 0x2B, 0x0A, 0x06, // 233 + 0x06, 0x35, 0x06, 0x04, // 234 + 0x06, 0x3B, 0x08, 0x05, // 235 + 0x06, 0x43, 0x0A, 0x06, // 236 + 0x06, 0x4D, 0x08, 0x05, // 237 + 0x06, 0x55, 0x08, 0x05, // 238 + 0x06, 0x5D, 0x08, 0x05, // 239 + 0x06, 0x65, 0x05, 0x04, // 240 + 0x06, 0x6A, 0x08, 0x05, // 241 + 0x06, 0x72, 0x05, 0x04, // 242 + 0x06, 0x77, 0x08, 0x05, // 243 + 0x06, 0x7F, 0x09, 0x06, // 244 + 0x06, 0x88, 0x0A, 0x06, // 245 + 0x06, 0x92, 0x08, 0x05, // 246 + 0x06, 0x9A, 0x08, 0x05, // 247 + 0x06, 0xA2, 0x0A, 0x06, // 248 + 0x06, 0xAC, 0x0C, 0x07, // 249 + 0x06, 0xB8, 0x08, 0x05, // 250 + 0x06, 0xC0, 0x08, 0x05, // 251 + 0x06, 0xC8, 0x08, 0x05, // 252 + 0x06, 0xD0, 0x08, 0x05, // 253 + 0x06, 0xD8, 0x0A, 0x06, // 254 + 0x06, 0xE2, 0x08, 0x05, // 255 // Font Data: 0x00, 0x00, 0xF8, 0x02, // 33 0x38, 0x00, 0x00, 0x00, 0x38, // 34 @@ -356,69 +356,1568 @@ const uint8_t ArialMT_Plain_10_UA[] PROGMEM = { 0x00, 0x00, 0x10, 0x02, 0x78, 0x01, 0xC0, 0x00, 0x20, 0x01, 0x90, 0x01, 0xC8, 0x03, 0x00, 0x01, // 188 0x00, 0x00, 0x10, 0x02, 0x78, 0x01, 0x80, 0x00, 0x60, 0x00, 0x50, 0x02, 0x48, 0x03, 0xC0, 0x02, // 189 0x48, 0x00, 0x58, 0x00, 0x68, 0x03, 0x80, 0x00, 0x60, 0x01, 0x90, 0x01, 0xC8, 0x03, 0x00, 0x01, // 190 - 0x00, 0x00, 0x00, 0x00, 0x28, 0x02, 0xE0, 0x03, 0x28, 0x02, // 191 - 0x00, 0x00, 0xF0, 0x03, 0x88, 0x00, 0x88, 0x00, 0x88, 0x00, 0xF0, 0x03, // 192 - 0x00, 0x00, 0xF8, 0x03, 0x48, 0x02, 0x48, 0x02, 0x48, 0x02, 0x88, 0x01, // 193 - 0x00, 0x00, 0xF8, 0x03, 0x48, 0x02, 0x48, 0x02, 0x48, 0x02, 0xB0, 0x01, // 194 - 0x00, 0x00, 0xF8, 0x03, 0x08, 0x00, 0x08, 0x00, 0x08, 0x00, 0x18, // 195 - 0x00, 0x00, 0x00, 0x02, 0xFC, 0x03, 0x04, 0x02, 0xFC, 0x03, 0x00, 0x02, // 196 - 0x00, 0x00, 0xF8, 0x03, 0x48, 0x02, 0x48, 0x02, 0x48, 0x02, 0x08, 0x02, // 197 - 0x00, 0x00, 0xB8, 0x03, 0x40, 0x00, 0xF8, 0x03, 0x40, 0x00, 0xB8, 0x03, // 198 - 0x00, 0x00, 0x08, 0x02, 0x48, 0x02, 0x48, 0x02, 0x48, 0x02, 0xB0, 0x01, // 199 - 0x00, 0x00, 0xF8, 0x03, 0x80, 0x00, 0x40, 0x00, 0x20, 0x00, 0xF8, 0x03, // 200 - 0x00, 0x00, 0xE0, 0x03, 0x08, 0x01, 0x90, 0x00, 0x48, 0x00, 0xE0, 0x03, // 201 - 0x00, 0x00, 0xF8, 0x03, 0x40, 0x00, 0xA0, 0x00, 0x10, 0x01, 0x08, 0x02, // 202 - 0x00, 0x00, 0x00, 0x02, 0xF0, 0x01, 0x08, 0x00, 0x08, 0x00, 0xF8, 0x03, // 203 - 0x00, 0x00, 0xF8, 0x03, 0x10, 0x00, 0x60, 0x00, 0x10, 0x00, 0xF8, 0x03, // 204 - 0x00, 0x00, 0xF8, 0x03, 0x40, 0x00, 0x40, 0x00, 0x40, 0x00, 0xF8, 0x03, // 205 - 0x00, 0x00, 0xF0, 0x01, 0x08, 0x02, 0x08, 0x02, 0x08, 0x02, 0xF0, 0x01, // 206 - 0x00, 0x00, 0xF8, 0x03, 0x08, 0x00, 0x08, 0x00, 0x08, 0x00, 0xF8, 0x03, // 207 - 0x00, 0x00, 0xF8, 0x03, 0x48, 0x00, 0x48, 0x00, 0x48, 0x00, 0x30, // 208 - 0x00, 0x00, 0xF0, 0x01, 0x08, 0x02, 0x08, 0x02, 0x08, 0x02, 0x10, 0x01, // 209 - 0x00, 0x00, 0x08, 0x00, 0x08, 0x00, 0xF8, 0x03, 0x08, 0x00, 0x08, // 210 - 0x00, 0x00, 0x38, 0x00, 0x40, 0x02, 0x40, 0x02, 0x40, 0x02, 0xF8, 0x01, // 211 - 0x00, 0x00, 0x70, 0x00, 0x88, 0x00, 0xF8, 0x03, 0x88, 0x00, 0x70, // 212 - 0x00, 0x00, 0x18, 0x03, 0xA0, 0x00, 0x40, 0x00, 0xA0, 0x00, 0x18, 0x03, // 213 - 0x00, 0x00, 0xF8, 0x03, 0x00, 0x02, 0x00, 0x02, 0xF8, 0x03, 0x00, 0x02, // 214 - 0x00, 0x00, 0x38, 0x00, 0x40, 0x00, 0x40, 0x00, 0x40, 0x00, 0xF8, 0x03, // 215 - 0x00, 0x00, 0xF8, 0x03, 0x00, 0x02, 0xF8, 0x03, 0x00, 0x02, 0xF8, 0x03, // 216 - 0x00, 0x00, 0xF8, 0x03, 0x00, 0x02, 0xF8, 0x03, 0x00, 0x02, 0xF8, 0x03, 0x00, 0x06, // 217 - 0x00, 0x00, 0x08, 0x00, 0xF8, 0x03, 0x40, 0x02, 0x40, 0x02, 0x80, 0x01, // 218 - 0x00, 0x00, 0xF8, 0x03, 0x40, 0x02, 0x40, 0x02, 0x80, 0x01, 0xF8, 0x03, // 219 - 0x00, 0x00, 0xF8, 0x03, 0x40, 0x02, 0x40, 0x02, 0x40, 0x02, 0x80, 0x01, // 220 - 0x00, 0x00, 0x10, 0x01, 0x08, 0x02, 0x48, 0x02, 0x48, 0x02, 0xF0, 0x01, // 221 - 0x00, 0x00, 0xF8, 0x03, 0x40, 0x00, 0xF0, 0x01, 0x08, 0x02, 0xF0, 0x01, // 222 - 0x00, 0x00, 0x30, 0x02, 0x48, 0x01, 0xC8, 0x00, 0x48, 0x00, 0xF8, 0x03, // 223 - 0x00, 0x00, 0x00, 0x01, 0xA0, 0x02, 0xA0, 0x02, 0xA0, 0x02, 0xC0, 0x03, // 224 - 0x00, 0x00, 0xE0, 0x01, 0x50, 0x02, 0x50, 0x02, 0x48, 0x02, 0x88, 0x01, // 225 - 0x00, 0x00, 0xE0, 0x03, 0xA0, 0x02, 0xA0, 0x02, 0xA0, 0x02, 0x40, 0x01, // 226 - 0x00, 0x00, 0xE0, 0x03, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x60, // 227 - 0x00, 0x00, 0x00, 0x02, 0xC0, 0x03, 0x20, 0x02, 0xE0, 0x03, 0x00, 0x02, // 228 - 0x00, 0x00, 0xC0, 0x01, 0xA0, 0x02, 0xA0, 0x02, 0xA0, 0x02, 0xC0, // 229 - 0x00, 0x00, 0x60, 0x03, 0x80, 0x00, 0xE0, 0x03, 0x80, 0x00, 0x60, 0x03, // 230 - 0x00, 0x00, 0x20, 0x02, 0xA0, 0x02, 0xA0, 0x02, 0xA0, 0x02, 0x40, 0x01, // 231 - 0x00, 0x00, 0xE0, 0x03, 0x00, 0x01, 0x80, 0x00, 0x40, 0x00, 0xE0, 0x03, // 232 - 0x00, 0x00, 0xE0, 0x03, 0x00, 0x01, 0x98, 0x00, 0x40, 0x00, 0xE0, 0x03, // 233 - 0x00, 0x00, 0xE0, 0x03, 0x80, 0x00, 0x80, 0x00, 0x40, 0x01, 0x20, 0x02, // 234 - 0x00, 0x00, 0x00, 0x02, 0xC0, 0x01, 0x20, 0x00, 0x20, 0x00, 0xE0, 0x03, // 235 - 0x00, 0x00, 0xE0, 0x03, 0x40, 0x00, 0x80, 0x00, 0x40, 0x00, 0xE0, 0x03, // 236 - 0x00, 0x00, 0xE0, 0x03, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, 0xE0, 0x03, // 237 - 0x00, 0x00, 0xC0, 0x01, 0x20, 0x02, 0x20, 0x02, 0x20, 0x02, 0xC0, 0x01, // 238 - 0x00, 0x00, 0xE0, 0x03, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0xE0, 0x03, // 239 - 0x00, 0x00, 0xE0, 0x03, 0xA0, 0x00, 0xA0, 0x00, 0xA0, 0x00, 0x40, // 240 - 0x00, 0x00, 0xC0, 0x01, 0x20, 0x02, 0x20, 0x02, 0x20, 0x02, 0x40, 0x02, // 241 - 0x00, 0x00, 0x20, 0x00, 0x20, 0x00, 0xE0, 0x03, 0x20, 0x00, 0x20, // 242 - 0x00, 0x00, 0x60, 0x00, 0x80, 0x02, 0x80, 0x02, 0x80, 0x02, 0xE0, 0x01, // 243 - 0x00, 0x00, 0xC0, 0x00, 0x20, 0x01, 0xE0, 0x03, 0x20, 0x01, 0xC0, // 244 - 0x00, 0x00, 0x20, 0x02, 0x40, 0x01, 0x80, 0x00, 0x40, 0x01, 0x20, 0x02, // 245 - 0x00, 0x00, 0xE0, 0x03, 0x00, 0x02, 0x00, 0x02, 0xE0, 0x03, 0x00, 0x02, // 246 - 0x00, 0x00, 0x60, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, 0xE0, 0x03, // 247 - 0x00, 0x00, 0xE0, 0x03, 0x00, 0x02, 0xE0, 0x03, 0x00, 0x02, 0xE0, 0x03, // 248 - 0x00, 0x00, 0xE0, 0x03, 0x00, 0x02, 0xE0, 0x03, 0x00, 0x02, 0xE0, 0x03, 0x00, 0x06, // 249 - 0x00, 0x00, 0x20, 0x00, 0xE0, 0x03, 0x80, 0x02, 0x80, 0x02, 0x00, 0x01, // 250 - 0x00, 0x00, 0xE0, 0x03, 0x80, 0x02, 0x80, 0x02, 0x00, 0x01, 0xE0, 0x03, // 251 - 0x00, 0x00, 0xE0, 0x03, 0x80, 0x02, 0x80, 0x02, 0x80, 0x02, 0x00, 0x01, // 252 - 0x00, 0x00, 0x40, 0x01, 0x20, 0x02, 0xA0, 0x02, 0xA0, 0x02, 0xC0, 0x01, // 253 - 0x00, 0x00, 0xE0, 0x03, 0x80, 0x00, 0xC0, 0x01, 0x20, 0x02, 0xC0, 0x01, // 254 - 0x00, 0x00, 0x40, 0x02, 0xA0, 0x01, 0xA0, 0x00, 0xA0, 0x00, 0xE0, 0x03, // 255 + 0x28, 0x02, 0xE0, 0x03, 0x28, 0x02, // 191 + 0xF0, 0x03, 0x88, 0x00, 0x88, 0x00, 0x88, 0x00, 0xF0, 0x03, // 192 + 0xF8, 0x03, 0x48, 0x02, 0x48, 0x02, 0x48, 0x02, 0x88, 0x01, // 193 + 0xF8, 0x03, 0x48, 0x02, 0x48, 0x02, 0x48, 0x02, 0xB0, 0x01, // 194 + 0xF8, 0x03, 0x08, 0x00, 0x08, 0x00, 0x08, 0x00, 0x18, // 195 + 0x00, 0x02, 0xFC, 0x03, 0x04, 0x02, 0xFC, 0x03, 0x00, 0x02, // 196 + 0xF8, 0x03, 0x48, 0x02, 0x48, 0x02, 0x48, 0x02, 0x08, 0x02, // 197 + 0xB8, 0x03, 0x40, 0x00, 0xF8, 0x03, 0x40, 0x00, 0xB8, 0x03, // 198 + 0x08, 0x02, 0x48, 0x02, 0x48, 0x02, 0xB0, 0x01, // 199 + 0xF8, 0x03, 0x80, 0x00, 0x40, 0x00, 0x20, 0x00, 0xF8, 0x03, // 200 + 0xE0, 0x03, 0x08, 0x01, 0x90, 0x00, 0x48, 0x00, 0xE0, 0x03, // 201 + 0xF8, 0x03, 0x40, 0x00, 0xA0, 0x00, 0x10, 0x01, 0x08, 0x02, // 202 + 0x00, 0x02, 0xF0, 0x01, 0x08, 0x00, 0x08, 0x00, 0xF8, 0x03, // 203 + 0xF8, 0x03, 0x10, 0x00, 0x60, 0x00, 0x10, 0x00, 0xF8, 0x03, // 204 + 0xF8, 0x03, 0x40, 0x00, 0x40, 0x00, 0x40, 0x00, 0xF8, 0x03, // 205 + 0xF0, 0x01, 0x08, 0x02, 0x08, 0x02, 0x08, 0x02, 0xF0, 0x01, // 206 + 0xF8, 0x03, 0x08, 0x00, 0x08, 0x00, 0x08, 0x00, 0xF8, 0x03, // 207 + 0xF8, 0x03, 0x48, 0x00, 0x48, 0x00, 0x48, 0x00, 0x30, // 208 + 0xF0, 0x01, 0x08, 0x02, 0x08, 0x02, 0x08, 0x02, 0x10, 0x01, // 209 + 0x08, 0x00, 0x08, 0x00, 0xF8, 0x03, 0x08, 0x00, 0x08, // 210 + 0x38, 0x00, 0x40, 0x02, 0x40, 0x02, 0x40, 0x02, 0xF8, 0x01, // 211 + 0x70, 0x00, 0x88, 0x00, 0xF8, 0x03, 0x88, 0x00, 0x70, // 212 + 0x18, 0x03, 0xA0, 0x00, 0x40, 0x00, 0xA0, 0x00, 0x18, 0x03, // 213 + 0xF8, 0x03, 0x00, 0x02, 0x00, 0x02, 0xF8, 0x03, 0x00, 0x02, // 214 + 0x38, 0x00, 0x40, 0x00, 0x40, 0x00, 0xF8, 0x03, // 215 + 0xF8, 0x03, 0x00, 0x02, 0xF8, 0x03, 0x00, 0x02, 0xF8, 0x03, // 216 + 0xF8, 0x03, 0x00, 0x02, 0xF8, 0x03, 0x00, 0x02, 0xF8, 0x03, 0x00, 0x06, // 217 + 0x08, 0x00, 0xF8, 0x03, 0x40, 0x02, 0x40, 0x02, 0x80, 0x01, // 218 + 0xF8, 0x03, 0x40, 0x02, 0x40, 0x02, 0x80, 0x01, 0xF8, 0x03, // 219 + 0xF8, 0x03, 0x40, 0x02, 0x40, 0x02, 0x80, 0x01, // 220 + 0x10, 0x01, 0x08, 0x02, 0x48, 0x02, 0x48, 0x02, 0xF0, 0x01, // 221 + 0xF8, 0x03, 0x40, 0x00, 0xF0, 0x01, 0x08, 0x02, 0xF0, 0x01, // 222 + 0x30, 0x02, 0x48, 0x01, 0xC8, 0x00, 0x48, 0x00, 0xF8, 0x03, // 223 + 0x00, 0x01, 0xA0, 0x02, 0xA0, 0x02, 0xE0, 0x01, // 224 + 0xE0, 0x01, 0x50, 0x02, 0x48, 0x02, 0x88, 0x01, // 225 + 0xE0, 0x03, 0xA0, 0x02, 0xA0, 0x02, 0x40, 0x01, // 226 + 0xE0, 0x03, 0x20, 0x00, 0x20, 0x00, 0x60, // 227 + 0x00, 0x02, 0xC0, 0x03, 0x20, 0x02, 0xE0, 0x03, 0x00, 0x02, // 228 + 0xC0, 0x01, 0xA0, 0x02, 0xA0, 0x02, 0xA0, 0x02, 0xC0, // 229 + 0x60, 0x03, 0x80, 0x00, 0xE0, 0x03, 0x80, 0x00, 0x60, 0x03, // 230 + 0x20, 0x02, 0xA0, 0x02, 0xA0, 0x02, 0x40, 0x01, // 231 + 0xE0, 0x03, 0x00, 0x01, 0x80, 0x00, 0x40, 0x00, 0xE0, 0x03, // 232 + 0xE0, 0x03, 0x08, 0x01, 0x88, 0x00, 0x48, 0x00, 0xE0, 0x03, // 233 + 0xE0, 0x03, 0x80, 0x00, 0x60, 0x03, // 234 + 0x00, 0x02, 0xC0, 0x01, 0x20, 0x00, 0xE0, 0x03, // 235 + 0xE0, 0x03, 0x40, 0x00, 0x80, 0x00, 0x40, 0x00, 0xE0, 0x03, // 236 + 0xE0, 0x03, 0x80, 0x00, 0x80, 0x00, 0xE0, 0x03, // 237 + 0xC0, 0x01, 0x20, 0x02, 0x20, 0x02, 0xC0, 0x01, // 238 + 0xE0, 0x03, 0x20, 0x00, 0x20, 0x00, 0xE0, 0x03, // 239 + 0xE0, 0x03, 0xA0, 0x00, 0x40, // 240 + 0xC0, 0x01, 0x20, 0x02, 0x20, 0x02, 0x40, 0x01, // 241 + 0x20, 0x00, 0xE0, 0x03, 0x20, // 242 + 0x60, 0x00, 0x80, 0x02, 0x80, 0x02, 0xE0, 0x01, // 243 + 0xC0, 0x00, 0x20, 0x01, 0xE0, 0x03, 0x20, 0x01, 0xC0, // 244 + 0x20, 0x02, 0x40, 0x01, 0x80, 0x00, 0x40, 0x01, 0x20, 0x02, // 245 + 0xE0, 0x03, 0x00, 0x02, 0xE0, 0x03, 0x00, 0x06, // 246 + 0x60, 0x00, 0x80, 0x00, 0x80, 0x00, 0xE0, 0x03, // 247 + 0xE0, 0x03, 0x00, 0x02, 0xE0, 0x03, 0x00, 0x02, 0xE0, 0x03, // 248 + 0xE0, 0x03, 0x00, 0x02, 0xE0, 0x03, 0x00, 0x02, 0xE0, 0x03, 0x00, 0x06, // 249 + 0x20, 0x00, 0xE0, 0x03, 0x80, 0x02, 0x00, 0x01, // 250 + 0xE0, 0x03, 0x80, 0x02, 0x00, 0x01, 0xE0, 0x03, // 251 + 0xE0, 0x03, 0x80, 0x02, 0x80, 0x02, 0x00, 0x01, // 252 + 0x40, 0x01, 0x20, 0x02, 0xA0, 0x02, 0xC0, 0x01, // 253 + 0xE0, 0x03, 0x80, 0x00, 0xC0, 0x01, 0x20, 0x02, 0xC0, 0x01, // 254 + 0x40, 0x02, 0xA0, 0x01, 0xA0, 0x00, 0xE0, 0x03, // 255 +}; + +const uint8_t ArialMT_Plain_16_UA[] PROGMEM = { + 0x10, // Width: 16 + 0x13, // Height: 19 + 0x20, // First Char: 32 + 0xE0, // Numbers of Chars: 224 + // Jump Table: + 0xFF, 0xFF, 0x00, 0x04, // 32= :65535 + 0x00, 0x00, 0x08, 0x05, // 33=!:0 + 0x00, 0x08, 0x0D, 0x06, // 34=":8 + 0x00, 0x15, 0x1A, 0x09, // 35=#:21 + 0x00, 0x2F, 0x17, 0x09, // 36=$:47 + 0x00, 0x46, 0x26, 0x0E, // 37=%:70 + 0x00, 0x6C, 0x1D, 0x0B, // 38=&:108 + 0x00, 0x89, 0x04, 0x03, // 39=':137 + 0x00, 0x8D, 0x0C, 0x05, // 40=(:141 + 0x00, 0x99, 0x0B, 0x05, // 41=):153 + 0x00, 0xA4, 0x0D, 0x06, // 42=*:164 + 0x00, 0xB1, 0x17, 0x09, // 43=+:177 + 0x00, 0xC8, 0x09, 0x04, // 44=,:200 + 0x00, 0xD1, 0x0B, 0x05, // 45=-:209 + 0x00, 0xDC, 0x08, 0x04, // 46=.:220 + 0x00, 0xE4, 0x0A, 0x04, // 47=/:228 + 0x00, 0xEE, 0x17, 0x09, // 48=0:238 + 0x01, 0x05, 0x11, 0x09, // 49=1:261 + 0x01, 0x16, 0x17, 0x09, // 50=2:278 + 0x01, 0x2D, 0x17, 0x09, // 51=3:301 + 0x01, 0x44, 0x17, 0x09, // 52=4:324 + 0x01, 0x5B, 0x17, 0x09, // 53=5:347 + 0x01, 0x72, 0x17, 0x09, // 54=6:370 + 0x01, 0x89, 0x16, 0x09, // 55=7:393 + 0x01, 0x9F, 0x17, 0x09, // 56=8:415 + 0x01, 0xB6, 0x17, 0x09, // 57=9:438 + 0x01, 0xCD, 0x05, 0x04, // 58=::461 + 0x01, 0xD2, 0x06, 0x04, // 59=;:466 + 0x01, 0xD8, 0x17, 0x09, // 60=<:472 + 0x01, 0xEF, 0x17, 0x09, // 61==:495 + 0x02, 0x06, 0x17, 0x09, // 62=>:518 + 0x02, 0x1D, 0x16, 0x09, // 63=?:541 + 0x02, 0x33, 0x2F, 0x10, // 64=@:563 + 0x02, 0x62, 0x1D, 0x0B, // 65=A:610 + 0x02, 0x7F, 0x1D, 0x0B, // 66=B:639 + 0x02, 0x9C, 0x20, 0x0C, // 67=C:668 + 0x02, 0xBC, 0x20, 0x0C, // 68=D:700 + 0x02, 0xDC, 0x1D, 0x0B, // 69=E:732 + 0x02, 0xF9, 0x19, 0x0A, // 70=F:761 + 0x03, 0x12, 0x20, 0x0C, // 71=G:786 + 0x03, 0x32, 0x1D, 0x0B, // 72=H:818 + 0x03, 0x4F, 0x05, 0x03, // 73=I:847 + 0x03, 0x54, 0x14, 0x08, // 74=J:852 + 0x03, 0x68, 0x1D, 0x0B, // 75=K:872 + 0x03, 0x85, 0x17, 0x09, // 76=L:901 + 0x03, 0x9C, 0x23, 0x0D, // 77=M:924 + 0x03, 0xBF, 0x1D, 0x0B, // 78=N:959 + 0x03, 0xDC, 0x20, 0x0C, // 79=O:988 + 0x03, 0xFC, 0x1C, 0x0B, // 80=P:1020 + 0x04, 0x18, 0x20, 0x0C, // 81=Q:1048 + 0x04, 0x38, 0x1D, 0x0B, // 82=R:1080 + 0x04, 0x55, 0x1D, 0x0B, // 83=S:1109 + 0x04, 0x72, 0x19, 0x09, // 84=T:1138 + 0x04, 0x8B, 0x1D, 0x0B, // 85=U:1163 + 0x04, 0xA8, 0x1C, 0x0B, // 86=V:1192 + 0x04, 0xC4, 0x2B, 0x0F, // 87=W:1220 + 0x04, 0xEF, 0x20, 0x0B, // 88=X:1263 + 0x05, 0x0F, 0x19, 0x09, // 89=Y:1295 + 0x05, 0x28, 0x1A, 0x09, // 90=Z:1320 + 0x05, 0x42, 0x0C, 0x04, // 91=[:1346 + 0x05, 0x4E, 0x0B, 0x04, // 92=\:1358 + 0x05, 0x59, 0x09, 0x04, // 93=]:1369 + 0x05, 0x62, 0x14, 0x07, // 94=^:1378 + 0x05, 0x76, 0x1B, 0x09, // 95=_:1398 + 0x05, 0x91, 0x07, 0x05, // 96=`:1425 + 0x05, 0x98, 0x17, 0x09, // 97=a:1432 + 0x05, 0xAF, 0x17, 0x09, // 98=b:1455 + 0x05, 0xC6, 0x14, 0x08, // 99=c:1478 + 0x05, 0xDA, 0x17, 0x09, // 100=d:1498 + 0x05, 0xF1, 0x17, 0x09, // 101=e:1521 + 0x06, 0x08, 0x0A, 0x04, // 102=f:1544 + 0x06, 0x12, 0x17, 0x09, // 103=g:1554 + 0x06, 0x29, 0x14, 0x08, // 104=h:1577 + 0x06, 0x3D, 0x05, 0x04, // 105=i:1597 + 0x06, 0x42, 0x06, 0x03, // 106=j:1602 + 0x06, 0x48, 0x17, 0x08, // 107=k:1608 + 0x06, 0x5F, 0x05, 0x03, // 108=l:1631 + 0x06, 0x64, 0x23, 0x0D, // 109=m:1636 + 0x06, 0x87, 0x14, 0x08, // 110=n:1671 + 0x06, 0x9B, 0x17, 0x09, // 111=o:1691 + 0x06, 0xB2, 0x17, 0x09, // 112=p:1714 + 0x06, 0xC9, 0x18, 0x09, // 113=q:1737 + 0x06, 0xE1, 0x0D, 0x05, // 114=r:1761 + 0x06, 0xEE, 0x14, 0x08, // 115=s:1774 + 0x07, 0x02, 0x0B, 0x04, // 116=t:1794 + 0x07, 0x0D, 0x14, 0x08, // 117=u:1805 + 0x07, 0x21, 0x13, 0x07, // 118=v:1825 + 0x07, 0x34, 0x1F, 0x0B, // 119=w:1844 + 0x07, 0x53, 0x14, 0x07, // 120=x:1875 + 0x07, 0x67, 0x13, 0x07, // 121=y:1895 + 0x07, 0x7A, 0x14, 0x07, // 122=z:1914 + 0x07, 0x8E, 0x0F, 0x05, // 123={:1934 + 0x07, 0x9D, 0x06, 0x03, // 124=|:1949 + 0x07, 0xA3, 0x0E, 0x05, // 125=}:1955 + 0x07, 0xB1, 0x17, 0x09, // 126=~:1969 + 0x07, 0xC8, 0x1D, 0x0C, // 127=:1992 + 0x07, 0xE5, 0x26, 0x0E, // 1026=Ђ:2021 + 0x08, 0x0B, 0x19, 0x09, // 1027=Ѓ:2059 + 0x08, 0x24, 0x06, 0x04, // 8218=‚:2084 + 0x08, 0x2A, 0x10, 0x06, // 1107=ѓ:2090 + 0x08, 0x3A, 0x09, 0x05, // 8222=„:2106 + 0x08, 0x43, 0x26, 0x10, // 8230=…:2115 + 0x08, 0x69, 0x16, 0x09, // 8224=†:2153 + 0x08, 0x7F, 0x17, 0x09, // 8225=‡:2175 + 0x08, 0x96, 0x17, 0x09, // 8364=€:2198 + 0x08, 0xAD, 0x32, 0x11, // 8240=‰:2221 + 0x08, 0xDF, 0x2F, 0x11, // 1033=Љ:2271 + 0x09, 0x0E, 0x0B, 0x05, // 8249=‹:2318 + 0x09, 0x19, 0x2C, 0x10, // 1034=Њ:2329 + 0x09, 0x45, 0x1A, 0x09, // 1036=Ќ:2373 + 0x09, 0x5F, 0x23, 0x0D, // 1035=Ћ:2399 + 0x09, 0x82, 0x1D, 0x0C, // 1039=Џ:2434 + 0x09, 0x9F, 0x15, 0x08, // 1106=ђ:2463 + 0x09, 0xB4, 0x04, 0x04, // 8216=‘:2484 + 0x09, 0xB8, 0x04, 0x04, // 8217=’:2488 + 0x09, 0xBC, 0x07, 0x05, // 8220=“:2492 + 0x09, 0xC3, 0x0A, 0x05, // 8221=”:2499 + 0x09, 0xCD, 0x0E, 0x06, // 8226=•:2509 + 0x09, 0xDB, 0x1A, 0x09, // 8211=–:2523 + 0x09, 0xF5, 0x2F, 0x10, // 8212=—:2549 + 0x0A, 0x24, 0x1D, 0x0C, // 65533=�:2596 + 0x0A, 0x41, 0x2C, 0x10, // 8482=™:2625 + 0x0A, 0x6D, 0x29, 0x0F, // 1113=љ:2669 + 0x0A, 0x96, 0x0B, 0x05, // 8250=›:2710 + 0x0A, 0xA1, 0x23, 0x0D, // 1114=њ:2721 + 0x0A, 0xC4, 0x14, 0x07, // 1116=ќ:2756 + 0x0A, 0xD8, 0x14, 0x08, // 1115=ћ:2776 + 0x0A, 0xEC, 0x14, 0x08, // 1119=џ:2796 + 0xFF, 0xFF, 0x00, 0x04, // 160= :65535 + 0x0B, 0x00, 0x1C, 0x0A, // 1038=Ў:2816 + 0x0B, 0x1C, 0x13, 0x07, // 1118=ў:2844 + 0x0B, 0x2F, 0x14, 0x08, // 1032=Ј:2863 + 0x0B, 0x43, 0x14, 0x09, // 164=¤:2883 + 0x0B, 0x57, 0x16, 0x08, // 1168=Ґ:2903 + 0x0B, 0x6D, 0x06, 0x03, // 166=¦:2925 + 0x0B, 0x73, 0x17, 0x09, // 167=§:2931 + 0x0B, 0x8A, 0x1D, 0x0B, // 1025=Ё:2954 + 0x0B, 0xA7, 0x23, 0x0C, // 169=©:2983 + 0x0B, 0xCA, 0x20, 0x0C, // 1028=Є:3018 + 0x0B, 0xEA, 0x14, 0x09, // 171=«:3050 + 0x0B, 0xFE, 0x17, 0x09, // 172=¬:3070 + 0x0C, 0x15, 0x0B, 0x05, // 173=­:3093 + 0x0C, 0x20, 0x23, 0x0C, // 174=®:3104 + 0x0C, 0x43, 0x07, 0x03, // 1031=Ї:3139 + 0x0C, 0x4A, 0x0D, 0x06, // 176=°:3146 + 0x0C, 0x57, 0x17, 0x09, // 177=±:3159 + 0x0C, 0x6E, 0x05, 0x03, // 1030=І:3182 + 0x0C, 0x73, 0x05, 0x04, // 1110=і:3187 + 0x0C, 0x78, 0x10, 0x07, // 1169=ґ:3192 + 0x0C, 0x88, 0x17, 0x09, // 181=µ:3208 + 0x0C, 0x9F, 0x19, 0x09, // 182=¶:3231 + 0x0C, 0xB8, 0x08, 0x05, // 183=·:3256 + 0x0C, 0xC0, 0x17, 0x09, // 1105=ё:3264 + 0x0C, 0xD7, 0x2F, 0x11, // 8470=№:3287 + 0x0D, 0x06, 0x14, 0x08, // 1108=є:3334 + 0x0D, 0x1A, 0x17, 0x09, // 187=»:3354 + 0x0D, 0x31, 0x06, 0x03, // 1112=ј:3377 + 0x0D, 0x37, 0x1D, 0x0B, // 1029=Ѕ:3383 + 0x0D, 0x54, 0x14, 0x08, // 1109=ѕ:3412 + 0x0D, 0x68, 0x07, 0x03, // 1111=ї:3432 + 0x0D, 0x6F, 0x1D, 0x0B, // 1040=А:3439 + 0x0D, 0x8C, 0x1D, 0x0B, // 1041=Б:3468 + 0x0D, 0xA9, 0x1D, 0x0B, // 1042=В:3497 + 0x0D, 0xC6, 0x19, 0x09, // 1043=Г:3526 + 0x0D, 0xDF, 0x1E, 0x0B, // 1044=Д:3551 + 0x0D, 0xFD, 0x1D, 0x0B, // 1045=Е:3581 + 0x0E, 0x1A, 0x29, 0x0E, // 1046=Ж:3610 + 0x0E, 0x43, 0x1A, 0x0A, // 1047=З:3651 + 0x0E, 0x5D, 0x20, 0x0C, // 1048=И:3677 + 0x0E, 0x7D, 0x20, 0x0C, // 1049=Й:3709 + 0x0E, 0x9D, 0x1A, 0x09, // 1050=К:3741 + 0x0E, 0xB7, 0x1D, 0x0B, // 1051=Л:3767 + 0x0E, 0xD4, 0x23, 0x0D, // 1052=М:3796 + 0x0E, 0xF7, 0x1D, 0x0B, // 1053=Н:3831 + 0x0F, 0x14, 0x20, 0x0C, // 1054=О:3860 + 0x0F, 0x34, 0x1D, 0x0B, // 1055=П:3892 + 0x0F, 0x51, 0x1C, 0x0B, // 1056=Р:3921 + 0x0F, 0x6D, 0x20, 0x0C, // 1057=С:3949 + 0x0F, 0x8D, 0x19, 0x09, // 1058=Т:3981 + 0x0F, 0xA6, 0x1C, 0x0A, // 1059=У:4006 + 0x0F, 0xC2, 0x1D, 0x0B, // 1060=Ф:4034 + 0x0F, 0xDF, 0x20, 0x0B, // 1061=Х:4063 + 0x0F, 0xFF, 0x21, 0x0C, // 1062=Ц:4095 + 0x10, 0x20, 0x1A, 0x0A, // 1063=Ч:4128 + 0x10, 0x3A, 0x23, 0x0D, // 1064=Ш:4154 + 0x10, 0x5D, 0x27, 0x0E, // 1065=Щ:4189 + 0x10, 0x84, 0x23, 0x0D, // 1066=Ъ:4228 + 0x10, 0xA7, 0x26, 0x0E, // 1067=Ы:4263 + 0x10, 0xCD, 0x1D, 0x0B, // 1068=Ь:4301 + 0x10, 0xEA, 0x20, 0x0C, // 1069=Э:4330 + 0x11, 0x0A, 0x2C, 0x10, // 1070=Ю:4362 + 0x11, 0x36, 0x20, 0x0C, // 1071=Я:4406 + 0x11, 0x56, 0x17, 0x09, // 1072=а:4438 + 0x11, 0x6D, 0x17, 0x09, // 1073=б:4461 + 0x11, 0x84, 0x17, 0x09, // 1074=в:4484 + 0x11, 0x9B, 0x10, 0x06, // 1075=г:4507 + 0x11, 0xAB, 0x18, 0x09, // 1076=д:4523 + 0x11, 0xC3, 0x17, 0x09, // 1077=е:4547 + 0x11, 0xDA, 0x1D, 0x0A, // 1078=ж:4570 + 0x11, 0xF7, 0x11, 0x07, // 1079=з:4599 + 0x12, 0x08, 0x14, 0x08, // 1080=и:4616 + 0x12, 0x1C, 0x14, 0x08, // 1081=й:4636 + 0x12, 0x30, 0x14, 0x07, // 1082=к:4656 + 0x12, 0x44, 0x14, 0x08, // 1083=л:4676 + 0x12, 0x58, 0x1D, 0x0B, // 1084=м:4696 + 0x12, 0x75, 0x14, 0x08, // 1085=н:4725 + 0x12, 0x89, 0x17, 0x09, // 1086=о:4745 + 0x12, 0xA0, 0x14, 0x08, // 1087=п:4768 + 0x12, 0xB4, 0x17, 0x09, // 1088=р:4788 + 0x12, 0xCB, 0x14, 0x08, // 1089=с:4811 + 0x12, 0xDF, 0x13, 0x07, // 1090=т:4831 + 0x12, 0xF2, 0x13, 0x07, // 1091=у:4850 + 0x13, 0x05, 0x23, 0x0D, // 1092=ф:4869 + 0x13, 0x28, 0x14, 0x07, // 1093=х:4904 + 0x13, 0x3C, 0x1B, 0x09, // 1094=ц:4924 + 0x13, 0x57, 0x14, 0x08, // 1095=ч:4951 + 0x13, 0x6B, 0x1D, 0x0B, // 1096=ш:4971 + 0x13, 0x88, 0x21, 0x0B, // 1097=щ:5000 + 0x13, 0xA9, 0x1A, 0x0A, // 1098=ъ:5033 + 0x13, 0xC3, 0x20, 0x0C, // 1099=ы:5059 + 0x13, 0xE3, 0x17, 0x09, // 1100=ь:5091 + 0x13, 0xFA, 0x14, 0x08, // 1101=э:5114 + 0x14, 0x0E, 0x20, 0x0C, // 1102=ю:5134 + 0x14, 0x2E, 0x17, 0x09, // 1103=я:5166 + // Font Data: + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF8, 0x5F, // 33 + 0x00, 0x00, 0x00, 0x78, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, // 34 + 0x80, 0x08, 0x00, 0x80, 0x78, 0x00, 0xC0, 0x0F, 0x00, 0xB8, 0x08, 0x00, 0x80, 0x08, 0x00, 0x80, 0x78, 0x00, 0xC0, 0x0F, 0x00, + 0xB8, 0x08, 0x00, 0x80, 0x08, // 35 + 0x00, 0x00, 0x00, 0xE0, 0x10, 0x00, 0x10, 0x21, 0x00, 0x08, 0x43, 0x00, 0xFC, 0xFF, 0x00, 0x08, 0x42, 0x00, 0x18, 0x22, 0x00, + 0x20, 0x1C, // 36 + 0x00, 0x00, 0x00, 0xF0, 0x00, 0x00, 0x08, 0x01, 0x00, 0x08, 0x01, 0x00, 0x08, 0x61, 0x00, 0xF0, 0x18, 0x00, 0x00, 0x06, 0x00, + 0xC0, 0x01, 0x00, 0x30, 0x3C, 0x00, 0x08, 0x42, 0x00, 0x00, 0x42, 0x00, 0x00, 0x42, 0x00, 0x00, 0x3C, // 37 + 0x00, 0x00, 0x00, 0x00, 0x1C, 0x00, 0x70, 0x22, 0x00, 0x88, 0x41, 0x00, 0x08, 0x43, 0x00, 0x88, 0x44, 0x00, 0x70, 0x28, 0x00, + 0x00, 0x10, 0x00, 0x00, 0x28, 0x00, 0x00, 0x44, // 38 + 0x00, 0x00, 0x00, 0x78, // 39 + 0x00, 0x00, 0x00, 0x80, 0x3F, 0x00, 0x70, 0xC0, 0x01, 0x08, 0x00, 0x02, // 40 + 0x00, 0x00, 0x00, 0x08, 0x00, 0x02, 0x70, 0xC0, 0x01, 0x80, 0x3F, // 41 + 0x10, 0x00, 0x00, 0xD0, 0x00, 0x00, 0x38, 0x00, 0x00, 0xD0, 0x00, 0x00, 0x10, // 42 + 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0xC0, 0x1F, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x02, // 43 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0x01, // 44 + 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, // 45 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, // 46 + 0x00, 0x60, 0x00, 0x00, 0x1E, 0x00, 0xE0, 0x01, 0x00, 0x18, // 47 + 0x00, 0x00, 0x00, 0xE0, 0x1F, 0x00, 0x10, 0x20, 0x00, 0x08, 0x40, 0x00, 0x08, 0x40, 0x00, 0x08, 0x40, 0x00, 0x10, 0x20, 0x00, + 0xE0, 0x1F, // 48 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x20, 0x00, 0x00, 0x10, 0x00, 0x00, 0xF8, 0x7F, // 49 + 0x00, 0x00, 0x00, 0x20, 0x40, 0x00, 0x10, 0x60, 0x00, 0x08, 0x50, 0x00, 0x08, 0x48, 0x00, 0x08, 0x44, 0x00, 0x10, 0x43, 0x00, + 0xE0, 0x40, // 50 + 0x00, 0x00, 0x00, 0x20, 0x10, 0x00, 0x10, 0x20, 0x00, 0x08, 0x41, 0x00, 0x08, 0x41, 0x00, 0x88, 0x41, 0x00, 0xF0, 0x22, 0x00, + 0x00, 0x1C, // 51 + 0x00, 0x0C, 0x00, 0x00, 0x0A, 0x00, 0x00, 0x09, 0x00, 0xC0, 0x08, 0x00, 0x20, 0x08, 0x00, 0x10, 0x08, 0x00, 0xF8, 0x7F, 0x00, + 0x00, 0x08, // 52 + 0x00, 0x00, 0x00, 0xC0, 0x11, 0x00, 0xB8, 0x20, 0x00, 0x88, 0x40, 0x00, 0x88, 0x40, 0x00, 0x88, 0x40, 0x00, 0x08, 0x21, 0x00, + 0x08, 0x1E, // 53 + 0x00, 0x00, 0x00, 0xE0, 0x1F, 0x00, 0x10, 0x21, 0x00, 0x88, 0x40, 0x00, 0x88, 0x40, 0x00, 0x88, 0x40, 0x00, 0x10, 0x21, 0x00, + 0x20, 0x1E, // 54 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x78, 0x00, 0x88, 0x07, 0x00, 0x68, 0x00, 0x00, + 0x18, // 55 + 0x00, 0x00, 0x00, 0x60, 0x1C, 0x00, 0x90, 0x22, 0x00, 0x08, 0x41, 0x00, 0x08, 0x41, 0x00, 0x08, 0x41, 0x00, 0x90, 0x22, 0x00, + 0x60, 0x1C, // 56 + 0x00, 0x00, 0x00, 0xE0, 0x11, 0x00, 0x10, 0x22, 0x00, 0x08, 0x44, 0x00, 0x08, 0x44, 0x00, 0x08, 0x44, 0x00, 0x10, 0x22, 0x00, + 0xE0, 0x1F, // 57 + 0x00, 0x00, 0x00, 0x40, 0x40, // 58 + 0x00, 0x00, 0x00, 0x40, 0xC0, 0x01, // 59 + 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x05, 0x00, 0x00, 0x05, 0x00, 0x80, 0x08, 0x00, 0x80, 0x08, 0x00, 0x80, 0x08, 0x00, + 0x40, 0x10, // 60 + 0x00, 0x00, 0x00, 0x80, 0x08, 0x00, 0x80, 0x08, 0x00, 0x80, 0x08, 0x00, 0x80, 0x08, 0x00, 0x80, 0x08, 0x00, 0x80, 0x08, 0x00, + 0x80, 0x08, // 61 + 0x00, 0x00, 0x00, 0x40, 0x10, 0x00, 0x80, 0x08, 0x00, 0x80, 0x08, 0x00, 0x80, 0x08, 0x00, 0x00, 0x05, 0x00, 0x00, 0x05, 0x00, + 0x00, 0x02, // 62 + 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x10, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x5C, 0x00, 0x08, 0x02, 0x00, 0x10, 0x01, 0x00, + 0xE0, // 63 + 0x00, 0x00, 0x00, 0x00, 0x3F, 0x00, 0xC0, 0x40, 0x00, 0x20, 0x80, 0x00, 0x10, 0x1E, 0x01, 0x10, 0x21, 0x01, 0x88, 0x40, 0x02, + 0x48, 0x40, 0x02, 0x48, 0x40, 0x02, 0x48, 0x20, 0x02, 0x88, 0x7C, 0x02, 0xC8, 0x43, 0x02, 0x10, 0x40, 0x02, 0x10, 0x20, 0x01, + 0x60, 0x10, 0x01, 0x80, 0x8F, // 64 + 0x00, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x1C, 0x00, 0x80, 0x07, 0x00, 0x70, 0x04, 0x00, 0x08, 0x04, 0x00, 0x70, 0x04, 0x00, + 0x80, 0x07, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x60, // 65 + 0x00, 0x00, 0x00, 0xF8, 0x7F, 0x00, 0x08, 0x41, 0x00, 0x08, 0x41, 0x00, 0x08, 0x41, 0x00, 0x08, 0x41, 0x00, 0x08, 0x41, 0x00, + 0x08, 0x41, 0x00, 0x90, 0x22, 0x00, 0x60, 0x1C, // 66 + 0x00, 0x00, 0x00, 0xC0, 0x0F, 0x00, 0x20, 0x10, 0x00, 0x10, 0x20, 0x00, 0x08, 0x40, 0x00, 0x08, 0x40, 0x00, 0x08, 0x40, 0x00, + 0x08, 0x40, 0x00, 0x08, 0x40, 0x00, 0x10, 0x20, 0x00, 0x20, 0x10, // 67 + 0x00, 0x00, 0x00, 0xF8, 0x7F, 0x00, 0x08, 0x40, 0x00, 0x08, 0x40, 0x00, 0x08, 0x40, 0x00, 0x08, 0x40, 0x00, 0x08, 0x40, 0x00, + 0x08, 0x40, 0x00, 0x10, 0x20, 0x00, 0x20, 0x10, 0x00, 0xC0, 0x0F, // 68 + 0x00, 0x00, 0x00, 0xF8, 0x7F, 0x00, 0x08, 0x41, 0x00, 0x08, 0x41, 0x00, 0x08, 0x41, 0x00, 0x08, 0x41, 0x00, 0x08, 0x41, 0x00, + 0x08, 0x41, 0x00, 0x08, 0x41, 0x00, 0x08, 0x40, // 69 + 0x00, 0x00, 0x00, 0xF8, 0x7F, 0x00, 0x08, 0x02, 0x00, 0x08, 0x02, 0x00, 0x08, 0x02, 0x00, 0x08, 0x02, 0x00, 0x08, 0x02, 0x00, + 0x08, 0x02, 0x00, 0x08, // 70 + 0x00, 0x00, 0x00, 0xC0, 0x0F, 0x00, 0x20, 0x10, 0x00, 0x10, 0x20, 0x00, 0x08, 0x40, 0x00, 0x08, 0x40, 0x00, 0x08, 0x42, 0x00, + 0x08, 0x42, 0x00, 0x10, 0x22, 0x00, 0x20, 0x12, 0x00, 0x00, 0x0E, // 71 + 0x00, 0x00, 0x00, 0xF8, 0x7F, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, + 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0xF8, 0x7F, // 72 + 0x00, 0x00, 0x00, 0xF8, 0x7F, // 73 + 0x00, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, 0xF8, 0x3F, // 74 + 0x00, 0x00, 0x00, 0xF8, 0x7F, 0x00, 0x00, 0x04, 0x00, 0x00, 0x02, 0x00, 0x00, 0x01, 0x00, 0x80, 0x03, 0x00, 0x40, 0x04, 0x00, + 0x20, 0x18, 0x00, 0x10, 0x20, 0x00, 0x08, 0x40, // 75 + 0x00, 0x00, 0x00, 0xF8, 0x7F, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, + 0x00, 0x40, // 76 + 0x00, 0x00, 0x00, 0xF8, 0x7F, 0x00, 0x30, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x60, 0x00, + 0x00, 0x1C, 0x00, 0x00, 0x03, 0x00, 0xC0, 0x00, 0x00, 0x30, 0x00, 0x00, 0xF8, 0x7F, // 77 + 0x00, 0x00, 0x00, 0xF8, 0x7F, 0x00, 0x10, 0x00, 0x00, 0x60, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x04, 0x00, + 0x00, 0x18, 0x00, 0x00, 0x20, 0x00, 0xF8, 0x7F, // 78 + 0x00, 0x00, 0x00, 0xC0, 0x0F, 0x00, 0x20, 0x10, 0x00, 0x10, 0x20, 0x00, 0x08, 0x40, 0x00, 0x08, 0x40, 0x00, 0x08, 0x40, 0x00, + 0x08, 0x40, 0x00, 0x10, 0x20, 0x00, 0x20, 0x10, 0x00, 0xC0, 0x0F, // 79 + 0x00, 0x00, 0x00, 0xF8, 0x7F, 0x00, 0x08, 0x02, 0x00, 0x08, 0x02, 0x00, 0x08, 0x02, 0x00, 0x08, 0x02, 0x00, 0x08, 0x02, 0x00, + 0x08, 0x02, 0x00, 0x10, 0x01, 0x00, 0xE0, // 80 + 0x00, 0x00, 0x00, 0xC0, 0x0F, 0x00, 0x20, 0x10, 0x00, 0x10, 0x20, 0x00, 0x08, 0x40, 0x00, 0x08, 0x40, 0x00, 0x08, 0x50, 0x00, + 0x08, 0x50, 0x00, 0x10, 0x20, 0x00, 0x20, 0x70, 0x00, 0xC0, 0x4F, // 81 + 0x00, 0x00, 0x00, 0xF8, 0x7F, 0x00, 0x08, 0x02, 0x00, 0x08, 0x02, 0x00, 0x08, 0x02, 0x00, 0x08, 0x02, 0x00, 0x08, 0x0E, 0x00, + 0x08, 0x1A, 0x00, 0x10, 0x21, 0x00, 0xE0, 0x40, // 82 + 0x00, 0x00, 0x00, 0x60, 0x10, 0x00, 0x90, 0x20, 0x00, 0x08, 0x41, 0x00, 0x08, 0x41, 0x00, 0x08, 0x41, 0x00, 0x08, 0x42, 0x00, + 0x08, 0x42, 0x00, 0x10, 0x22, 0x00, 0x20, 0x1C, // 83 + 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0xF8, 0x7F, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, + 0x08, 0x00, 0x00, 0x08, // 84 + 0x00, 0x00, 0x00, 0xF8, 0x1F, 0x00, 0x00, 0x20, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, + 0x00, 0x40, 0x00, 0x00, 0x20, 0x00, 0xF8, 0x1F, // 85 + 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0xE0, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x18, 0x00, 0x00, 0x60, 0x00, 0x00, 0x18, 0x00, + 0x00, 0x07, 0x00, 0xE0, 0x00, 0x00, 0x18, // 86 + 0x18, 0x00, 0x00, 0xE0, 0x01, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x60, 0x00, 0x00, 0x1C, 0x00, 0x80, 0x03, 0x00, 0x70, 0x00, 0x00, + 0x08, 0x00, 0x00, 0x70, 0x00, 0x00, 0x80, 0x03, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x60, 0x00, 0x00, 0x1E, 0x00, 0xE0, 0x01, 0x00, + 0x18, // 87 + 0x00, 0x40, 0x00, 0x08, 0x20, 0x00, 0x10, 0x18, 0x00, 0x60, 0x04, 0x00, 0x80, 0x02, 0x00, 0x00, 0x01, 0x00, 0x80, 0x02, 0x00, + 0x60, 0x0C, 0x00, 0x10, 0x10, 0x00, 0x08, 0x20, 0x00, 0x00, 0x40, // 88 + 0x08, 0x00, 0x00, 0x30, 0x00, 0x00, 0x40, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x7E, 0x00, 0x80, 0x01, 0x00, 0x40, 0x00, 0x00, + 0x30, 0x00, 0x00, 0x08, // 89 + 0x00, 0x40, 0x00, 0x08, 0x60, 0x00, 0x08, 0x58, 0x00, 0x08, 0x44, 0x00, 0x08, 0x43, 0x00, 0x88, 0x40, 0x00, 0x68, 0x40, 0x00, + 0x18, 0x40, 0x00, 0x08, 0x40, // 90 + 0x00, 0x00, 0x00, 0xF8, 0xFF, 0x03, 0x08, 0x00, 0x02, 0x08, 0x00, 0x02, // 91 + 0x18, 0x00, 0x00, 0xE0, 0x01, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x60, // 92 + 0x08, 0x00, 0x02, 0x08, 0x00, 0x02, 0xF8, 0xFF, 0x03, // 93 + 0x00, 0x01, 0x00, 0xC0, 0x00, 0x00, 0x30, 0x00, 0x00, 0x08, 0x00, 0x00, 0x30, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0x01, // 94 + 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, // 95 + 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x10, // 96 + 0x00, 0x00, 0x00, 0x00, 0x39, 0x00, 0x80, 0x44, 0x00, 0x40, 0x44, 0x00, 0x40, 0x44, 0x00, 0x40, 0x42, 0x00, 0x40, 0x22, 0x00, + 0x80, 0x7F, // 97 + 0x00, 0x00, 0x00, 0xF8, 0x7F, 0x00, 0x80, 0x20, 0x00, 0x40, 0x40, 0x00, 0x40, 0x40, 0x00, 0x40, 0x40, 0x00, 0x80, 0x20, 0x00, + 0x00, 0x1F, // 98 + 0x00, 0x00, 0x00, 0x00, 0x1F, 0x00, 0x80, 0x20, 0x00, 0x40, 0x40, 0x00, 0x40, 0x40, 0x00, 0x40, 0x40, 0x00, 0x80, 0x20, // 99 + 0x00, 0x00, 0x00, 0x00, 0x1F, 0x00, 0x80, 0x20, 0x00, 0x40, 0x40, 0x00, 0x40, 0x40, 0x00, 0x40, 0x40, 0x00, 0x80, 0x20, 0x00, + 0xF8, 0x7F, // 100 + 0x00, 0x00, 0x00, 0x00, 0x1F, 0x00, 0x80, 0x24, 0x00, 0x40, 0x44, 0x00, 0x40, 0x44, 0x00, 0x40, 0x44, 0x00, 0x80, 0x24, 0x00, + 0x00, 0x17, // 101 + 0x40, 0x00, 0x00, 0xF0, 0x7F, 0x00, 0x48, 0x00, 0x00, 0x48, // 102 + 0x00, 0x00, 0x00, 0x00, 0x1F, 0x01, 0x80, 0x20, 0x02, 0x40, 0x40, 0x02, 0x40, 0x40, 0x02, 0x40, 0x40, 0x02, 0x80, 0x20, 0x01, + 0xC0, 0xFF, // 103 + 0x00, 0x00, 0x00, 0xF8, 0x7F, 0x00, 0x80, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, 0x00, 0x80, 0x7F, // 104 + 0x00, 0x00, 0x00, 0xC8, 0x7F, // 105 + 0x00, 0x00, 0x02, 0xC8, 0xFF, 0x01, // 106 + 0x00, 0x00, 0x00, 0xF8, 0x7F, 0x00, 0x00, 0x08, 0x00, 0x00, 0x04, 0x00, 0x00, 0x06, 0x00, 0x00, 0x19, 0x00, 0x80, 0x20, 0x00, + 0x40, 0x40, // 107 + 0x00, 0x00, 0x00, 0xF8, 0x7F, // 108 + 0x00, 0x00, 0x00, 0xC0, 0x7F, 0x00, 0x80, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, 0x00, 0x80, 0x7F, 0x00, + 0x80, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, 0x00, 0x80, 0x7F, // 109 + 0x00, 0x00, 0x00, 0xC0, 0x7F, 0x00, 0x80, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, 0x00, 0x80, 0x7F, // 110 + 0x00, 0x00, 0x00, 0x00, 0x1F, 0x00, 0x80, 0x20, 0x00, 0x40, 0x40, 0x00, 0x40, 0x40, 0x00, 0x40, 0x40, 0x00, 0x80, 0x20, 0x00, + 0x00, 0x1F, // 111 + 0x00, 0x00, 0x00, 0xC0, 0xFF, 0x03, 0x80, 0x20, 0x00, 0x40, 0x40, 0x00, 0x40, 0x40, 0x00, 0x40, 0x40, 0x00, 0x80, 0x20, 0x00, + 0x00, 0x1F, // 112 + 0x00, 0x00, 0x00, 0x00, 0x1F, 0x00, 0x80, 0x20, 0x00, 0x40, 0x40, 0x00, 0x40, 0x40, 0x00, 0x40, 0x40, 0x00, 0x80, 0x20, 0x00, + 0xC0, 0xFF, 0x03, // 113 + 0x00, 0x00, 0x00, 0xC0, 0x7F, 0x00, 0x80, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, // 114 + 0x00, 0x00, 0x00, 0x80, 0x23, 0x00, 0x40, 0x44, 0x00, 0x40, 0x44, 0x00, 0x40, 0x44, 0x00, 0x40, 0x44, 0x00, 0x80, 0x38, // 115 + 0x40, 0x00, 0x00, 0xF0, 0x7F, 0x00, 0x40, 0x40, 0x00, 0x40, 0x40, // 116 + 0x00, 0x00, 0x00, 0xC0, 0x3F, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, 0x00, 0x20, 0x00, 0xC0, 0x7F, // 117 + 0xC0, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x60, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x03, 0x00, 0xC0, // 118 + 0xC0, 0x00, 0x00, 0x00, 0x1F, 0x00, 0x00, 0x60, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x03, 0x00, 0xC0, 0x00, 0x00, 0x00, 0x03, 0x00, + 0x00, 0x1C, 0x00, 0x00, 0x60, 0x00, 0x00, 0x1F, 0x00, 0xC0, // 119 + 0x40, 0x40, 0x00, 0x80, 0x20, 0x00, 0x00, 0x1B, 0x00, 0x00, 0x04, 0x00, 0x00, 0x1B, 0x00, 0x80, 0x20, 0x00, 0x40, 0x40, // 120 + 0xC0, 0x01, 0x00, 0x00, 0x06, 0x02, 0x00, 0x38, 0x02, 0x00, 0xC0, 0x01, 0x00, 0x38, 0x00, 0x00, 0x07, 0x00, 0xC0, // 121 + 0x40, 0x40, 0x00, 0x40, 0x60, 0x00, 0x40, 0x58, 0x00, 0x40, 0x44, 0x00, 0x40, 0x43, 0x00, 0xC0, 0x40, 0x00, 0x40, 0x40, // 122 + 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0xF0, 0xFB, 0x01, 0x08, 0x00, 0x02, 0x08, 0x00, 0x02, // 123 + 0x00, 0x00, 0x00, 0xF8, 0xFF, 0x03, // 124 + 0x08, 0x00, 0x02, 0x08, 0x00, 0x02, 0xF0, 0xFB, 0x01, 0x00, 0x04, 0x00, 0x00, 0x04, // 125 + 0x00, 0x02, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x01, // 126 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0x7F, 0x00, 0x20, 0x40, 0x00, 0x20, 0x40, 0x00, 0x20, 0x40, 0x00, 0x20, 0x40, 0x00, + 0x20, 0x40, 0x00, 0x20, 0x40, 0x00, 0xE0, 0x7F, // 127 + 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0xF8, 0x7F, 0x00, 0x08, 0x02, 0x00, 0x08, 0x01, 0x00, + 0x08, 0x41, 0x00, 0x08, 0x41, 0x00, 0x00, 0x41, 0x00, 0x00, 0x41, 0x00, 0x00, 0x41, 0x00, 0x00, 0x3E, // 1026 + 0x00, 0x00, 0x00, 0xF8, 0x7F, 0x00, 0x08, 0x00, 0x00, 0x0A, 0x00, 0x00, 0x09, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, + 0x08, 0x00, 0x00, 0x08, // 1027 + 0x00, 0x00, 0x00, 0x00, 0xC0, 0x01, // 8218 + 0x00, 0x00, 0x00, 0xC0, 0x7F, 0x00, 0x40, 0x00, 0x00, 0x50, 0x00, 0x00, 0x48, 0x00, 0x00, 0x40, // 1107 + 0x00, 0xC0, 0x01, 0x00, 0x00, 0x00, 0x00, 0xC0, 0x01, // 8222 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, // 8230 + 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, 0x00, 0xF8, 0xFF, 0x03, 0x40, 0x00, 0x00, 0x40, 0x00, 0x00, + 0x40, // 8224 + 0x00, 0x00, 0x00, 0x40, 0x40, 0x00, 0x40, 0x40, 0x00, 0x40, 0x40, 0x00, 0xF8, 0xFF, 0x03, 0x40, 0x40, 0x00, 0x40, 0x40, 0x00, + 0x40, 0x40, // 8225 + 0x00, 0x05, 0x00, 0xC0, 0x1F, 0x00, 0x20, 0x25, 0x00, 0x10, 0x45, 0x00, 0x08, 0x45, 0x00, 0x08, 0x45, 0x00, 0x08, 0x45, 0x00, + 0x10, 0x20, // 8364 + 0xF0, 0x00, 0x00, 0x08, 0x01, 0x00, 0x08, 0x01, 0x00, 0x08, 0x71, 0x00, 0xF0, 0x0C, 0x00, 0x00, 0x03, 0x00, 0xE0, 0x3C, 0x00, + 0x18, 0x42, 0x00, 0x00, 0x42, 0x00, 0x00, 0x42, 0x00, 0x00, 0x3C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3C, 0x00, 0x00, 0x42, 0x00, + 0x00, 0x42, 0x00, 0x00, 0x42, 0x00, 0x00, 0x3C, // 8240 + 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, 0xF8, 0x3F, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, + 0x08, 0x00, 0x00, 0xF8, 0x7F, 0x00, 0x00, 0x41, 0x00, 0x00, 0x41, 0x00, 0x00, 0x41, 0x00, 0x00, 0x41, 0x00, 0x00, 0x41, 0x00, + 0x00, 0x41, 0x00, 0x00, 0x3E, // 1033 + 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x1B, 0x00, 0x80, 0x20, // 8249 + 0x00, 0x00, 0x00, 0xF8, 0x7F, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, + 0xF8, 0x7F, 0x00, 0x00, 0x41, 0x00, 0x00, 0x41, 0x00, 0x00, 0x41, 0x00, 0x00, 0x41, 0x00, 0x00, 0x41, 0x00, 0x00, 0x41, 0x00, + 0x00, 0x3E, // 1034 + 0x00, 0x00, 0x00, 0xF8, 0x7F, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x82, 0x02, 0x00, 0x61, 0x04, 0x00, 0x10, 0x08, 0x00, + 0x08, 0x30, 0x00, 0x08, 0x40, // 1036 + 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0xF8, 0x7F, 0x00, 0x08, 0x02, 0x00, 0x08, 0x01, 0x00, + 0x08, 0x01, 0x00, 0x08, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x7E, // 1035 + 0x00, 0x00, 0x00, 0xF8, 0x7F, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, 0x00, 0xC0, 0x03, 0x00, 0x40, 0x00, + 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, 0xF8, 0x7F, // 1039 + 0x10, 0x00, 0x00, 0xF8, 0x7F, 0x00, 0x90, 0x00, 0x00, 0x50, 0x00, 0x00, 0x50, 0x00, 0x00, 0x40, 0x00, 0x02, 0x80, 0xFF, + 0x03, // 1106 + 0x00, 0x00, 0x00, 0x38, // 8216 + 0x00, 0x00, 0x00, 0x38, // 8217 + 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, // 8220 + 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, // 8221 + 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x80, 0x07, 0x00, 0x80, 0x07, 0x00, 0x00, 0x03, // 8226 + 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, + 0x00, 0x04, 0x00, 0x00, 0x04, // 8211 + 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, + 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, + 0x00, 0x04, 0x00, 0x00, 0x04, // 8212 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0x7F, 0x00, 0x20, 0x40, 0x00, 0x20, 0x40, 0x00, 0x20, 0x40, 0x00, 0x20, 0x40, 0x00, + 0x20, 0x40, 0x00, 0x20, 0x40, 0x00, 0xE0, 0x7F, // 65533 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0xF8, 0x01, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xF8, 0x01, 0x00, 0x18, 0x00, 0x00, 0xE0, 0x00, 0x00, 0x00, 0x01, 0x00, 0xE0, 0x00, 0x00, 0x18, 0x00, 0x00, + 0xF8, 0x01, // 8482 + 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, 0xC0, 0x3F, 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, 0x00, + 0xC0, 0x7F, 0x00, 0x00, 0x44, 0x00, 0x00, 0x44, 0x00, 0x00, 0x44, 0x00, 0x00, 0x44, 0x00, 0x00, 0x64, 0x00, 0x00, + 0x38, // 1113 + 0x00, 0x00, 0x00, 0x80, 0x20, 0x00, 0x00, 0x1B, 0x00, 0x00, 0x04, // 8250 + 0x00, 0x00, 0x00, 0xC0, 0x7F, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0xC0, 0x7F, 0x00, + 0x00, 0x44, 0x00, 0x00, 0x44, 0x00, 0x00, 0x44, 0x00, 0x00, 0x44, 0x00, 0x00, 0x38, // 1114 + 0x00, 0x00, 0x00, 0xC0, 0x7F, 0x00, 0x00, 0x04, 0x00, 0x10, 0x04, 0x00, 0x08, 0x1B, 0x00, 0xC0, 0x20, 0x00, 0x40, + 0x40, // 1116 + 0x10, 0x00, 0x00, 0xF8, 0x7F, 0x00, 0x90, 0x00, 0x00, 0x50, 0x00, 0x00, 0x50, 0x00, 0x00, 0x40, 0x00, 0x00, 0x80, + 0x7F, // 1115 + 0x00, 0x00, 0x00, 0xC0, 0x7F, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, 0x00, 0xC0, 0x03, 0x00, 0x40, 0x00, 0xC0, + 0x7F, // 1119 + 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x60, 0x40, 0x00, 0x81, 0x41, 0x00, 0x02, 0x66, 0x00, 0x02, 0x18, 0x00, 0x02, 0x06, 0x00, + 0x81, 0x01, 0x00, 0x60, 0x00, 0x00, 0x18, // 1038 + 0xC0, 0x01, 0x00, 0x08, 0x06, 0x02, 0x10, 0x38, 0x02, 0x10, 0xC0, 0x01, 0x10, 0x38, 0x00, 0x08, 0x07, 0x00, 0xC0, // 1118 + 0x00, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, 0xF8, + 0x3F, // 1032 + 0x00, 0x00, 0x00, 0x40, 0x0B, 0x00, 0x80, 0x04, 0x00, 0x40, 0x08, 0x00, 0x40, 0x08, 0x00, 0x80, 0x04, 0x00, 0x40, 0x0B, // 164 + 0x00, 0x00, 0x00, 0xF8, 0x7F, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, + 0x0F, // 1168 + 0x00, 0x00, 0x00, 0xF8, 0xF1, 0x03, // 166 + 0x00, 0x86, 0x00, 0x70, 0x09, 0x01, 0xC8, 0x10, 0x02, 0x88, 0x10, 0x02, 0x08, 0x21, 0x02, 0x08, 0x61, 0x02, 0x30, 0xD2, 0x01, + 0x00, 0x0C, // 167 + 0x00, 0x00, 0x00, 0xF8, 0x7F, 0x00, 0x08, 0x41, 0x00, 0x0A, 0x41, 0x00, 0x08, 0x41, 0x00, 0x0A, 0x41, 0x00, 0x08, 0x41, 0x00, + 0x08, 0x41, 0x00, 0x08, 0x41, 0x00, 0x08, 0x40, // 1025 + 0xC0, 0x0F, 0x00, 0x20, 0x10, 0x00, 0x10, 0x20, 0x00, 0xC8, 0x47, 0x00, 0x28, 0x48, 0x00, 0x28, 0x48, 0x00, 0x28, 0x48, 0x00, + 0x28, 0x48, 0x00, 0x48, 0x44, 0x00, 0x10, 0x20, 0x00, 0x20, 0x10, 0x00, 0xC0, 0x0F, // 169 + 0x00, 0x00, 0x00, 0xC0, 0x0F, 0x00, 0x20, 0x11, 0x00, 0x10, 0x21, 0x00, 0x08, 0x41, 0x00, 0x08, 0x41, 0x00, 0x08, 0x41, 0x00, + 0x08, 0x40, 0x00, 0x08, 0x40, 0x00, 0x10, 0x20, 0x00, 0x20, 0x10, // 1028 + 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x1B, 0x00, 0x80, 0x20, 0x00, 0x00, 0x04, 0x00, 0x00, 0x1B, 0x00, 0x80, 0x20, // 171 + 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x80, 0x00, 0x00, 0x80, 0x00, 0x00, 0x80, 0x00, 0x00, 0x80, 0x00, 0x00, 0x80, 0x00, 0x00, + 0x80, 0x0F, // 172 + 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, // 173 + 0xC0, 0x0F, 0x00, 0x20, 0x10, 0x00, 0x10, 0x20, 0x00, 0xE8, 0x4F, 0x00, 0x28, 0x41, 0x00, 0x28, 0x41, 0x00, 0x28, 0x43, 0x00, + 0x28, 0x45, 0x00, 0xC8, 0x48, 0x00, 0x10, 0x20, 0x00, 0x20, 0x10, 0x00, 0xC0, 0x0F, // 174 + 0x02, 0x00, 0x00, 0xF8, 0x7F, 0x00, 0x02, // 1031 + 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x48, 0x00, 0x00, 0x48, 0x00, 0x00, 0x30, // 176 + 0x00, 0x00, 0x00, 0x00, 0x41, 0x00, 0x00, 0x41, 0x00, 0x00, 0x41, 0x00, 0xE0, 0x4F, 0x00, 0x00, 0x41, 0x00, 0x00, 0x41, 0x00, + 0x00, 0x41, // 177 + 0x00, 0x00, 0x00, 0xF8, 0x7F, // 1030 + 0x00, 0x00, 0x00, 0xC8, 0x7F, // 1110 + 0x00, 0x00, 0x00, 0xC0, 0x7F, 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, 0x00, 0x78, // 1169 + 0x00, 0x00, 0x00, 0xC0, 0xFF, 0x03, 0x00, 0x20, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, 0x00, 0x20, 0x00, + 0xC0, 0x7F, // 181 + 0xF0, 0x00, 0x00, 0xF8, 0x00, 0x00, 0xF8, 0x01, 0x00, 0xF8, 0x01, 0x00, 0xF8, 0xFF, 0x03, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, + 0xF8, 0xFF, 0x03, 0x08, // 182 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, // 183 + 0x00, 0x00, 0x00, 0x00, 0x1F, 0x00, 0x80, 0x24, 0x00, 0x50, 0x44, 0x00, 0x40, 0x44, 0x00, 0x50, 0x44, 0x00, 0x80, 0x24, 0x00, + 0x00, 0x17, // 1105 + 0x00, 0x00, 0x00, 0xF8, 0x7F, 0x00, 0x20, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x06, 0x00, 0x00, 0x08, 0x00, + 0x00, 0x10, 0x00, 0xF8, 0x7F, 0x00, 0x00, 0x00, 0x00, 0x80, 0x27, 0x00, 0x40, 0x28, 0x00, 0x40, 0x28, 0x00, 0x40, 0x28, 0x00, + 0x40, 0x28, 0x00, 0x80, 0x27, // 8470 + 0x00, 0x00, 0x00, 0x00, 0x1F, 0x00, 0x80, 0x24, 0x00, 0x40, 0x44, 0x00, 0x40, 0x44, 0x00, 0x80, 0x20, 0x00, 0x00, + 0x11, // 1108 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x20, 0x00, 0x00, 0x1B, 0x00, 0x00, 0x04, 0x00, 0x80, 0x20, 0x00, 0x00, 0x1B, 0x00, + 0x00, 0x04, // 187 + 0x00, 0x00, 0x02, 0xC8, 0xFF, 0x01, // 1112 + 0x00, 0x00, 0x00, 0x60, 0x10, 0x00, 0x90, 0x20, 0x00, 0x08, 0x41, 0x00, 0x08, 0x41, 0x00, 0x08, 0x41, 0x00, 0x08, 0x42, 0x00, + 0x08, 0x42, 0x00, 0x10, 0x22, 0x00, 0x20, 0x1C, // 1029 + 0x00, 0x00, 0x00, 0x80, 0x23, 0x00, 0x40, 0x44, 0x00, 0x40, 0x44, 0x00, 0x40, 0x44, 0x00, 0x40, 0x44, 0x00, 0x80, + 0x38, // 1109 + 0x10, 0x00, 0x00, 0xC0, 0x7F, 0x00, 0x10, // 1111 + 0x00, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x1C, 0x00, 0x80, 0x07, 0x00, 0x70, 0x04, 0x00, 0x08, 0x04, 0x00, 0x70, 0x04, 0x00, + 0x80, 0x07, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x60, // 1040 + 0x00, 0x00, 0x00, 0xF8, 0x7F, 0x00, 0x08, 0x41, 0x00, 0x08, 0x41, 0x00, 0x08, 0x41, 0x00, 0x08, 0x41, 0x00, 0x08, 0x41, 0x00, + 0x08, 0x41, 0x00, 0x08, 0x41, 0x00, 0x00, 0x3E, // 1041 + 0x00, 0x00, 0x00, 0xF8, 0x7F, 0x00, 0x08, 0x41, 0x00, 0x08, 0x41, 0x00, 0x08, 0x41, 0x00, 0x08, 0x41, 0x00, 0x08, 0x41, 0x00, + 0x08, 0x41, 0x00, 0x90, 0x22, 0x00, 0x60, 0x1C, // 1042 + 0x00, 0x00, 0x00, 0xF8, 0x7F, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, + 0x08, 0x00, 0x00, 0x08, // 1043 + 0x00, 0xC0, 0x03, 0x00, 0x60, 0x00, 0x00, 0x5C, 0x00, 0xF8, 0x43, 0x00, 0x08, 0x40, 0x00, 0x08, 0x40, 0x00, 0x08, 0x40, 0x00, + 0x08, 0x40, 0x00, 0xF8, 0x7F, 0x00, 0x00, 0xC0, 0x03, // 1044 + 0x00, 0x00, 0x00, 0xF8, 0x7F, 0x00, 0x08, 0x41, 0x00, 0x08, 0x41, 0x00, 0x08, 0x41, 0x00, 0x08, 0x41, 0x00, 0x08, 0x41, 0x00, + 0x08, 0x41, 0x00, 0x08, 0x41, 0x00, 0x08, 0x40, // 1045 + 0x08, 0x30, 0x00, 0x10, 0x08, 0x00, 0x60, 0x04, 0x00, 0x80, 0x02, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0xF8, 0x7F, 0x00, + 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x80, 0x02, 0x00, 0x60, 0x04, 0x00, 0x10, 0x08, 0x00, 0x08, 0x30, 0x00, 0x08, + 0x40, // 1046 + 0x00, 0x00, 0x00, 0x20, 0x10, 0x00, 0x10, 0x20, 0x00, 0x08, 0x40, 0x00, 0x08, 0x41, 0x00, 0x08, 0x41, 0x00, 0x88, 0x42, 0x00, + 0x70, 0x42, 0x00, 0x00, 0x3C, // 1047 + 0x00, 0x00, 0x00, 0xF8, 0x7F, 0x00, 0x00, 0x30, 0x00, 0x00, 0x08, 0x00, 0x00, 0x04, 0x00, 0x00, 0x02, 0x00, 0x00, 0x01, 0x00, + 0x80, 0x00, 0x00, 0x40, 0x00, 0x00, 0x30, 0x00, 0x00, 0xF8, 0x7F, // 1048 + 0x00, 0x00, 0x00, 0xF8, 0x7F, 0x00, 0x00, 0x30, 0x00, 0x00, 0x08, 0x00, 0x01, 0x04, 0x00, 0x02, 0x02, 0x00, 0x02, 0x01, 0x00, + 0x82, 0x00, 0x00, 0x41, 0x00, 0x00, 0x30, 0x00, 0x00, 0xF8, 0x7F, // 1049 + 0x00, 0x00, 0x00, 0xF8, 0x7F, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x80, 0x02, 0x00, 0x60, 0x04, 0x00, 0x10, 0x08, 0x00, + 0x08, 0x30, 0x00, 0x08, 0x40, // 1050 + 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, 0xF8, 0x3F, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, + 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0xF8, 0x7F, // 1051 + 0x00, 0x00, 0x00, 0xF8, 0x7F, 0x00, 0x30, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x60, 0x00, + 0x00, 0x1C, 0x00, 0x00, 0x03, 0x00, 0xC0, 0x00, 0x00, 0x30, 0x00, 0x00, 0xF8, 0x7F, // 1052 + 0x00, 0x00, 0x00, 0xF8, 0x7F, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, + 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0xF8, 0x7F, // 1053 + 0x00, 0x00, 0x00, 0xC0, 0x0F, 0x00, 0x20, 0x10, 0x00, 0x10, 0x20, 0x00, 0x08, 0x40, 0x00, 0x08, 0x40, 0x00, 0x08, 0x40, 0x00, + 0x08, 0x40, 0x00, 0x10, 0x20, 0x00, 0x20, 0x10, 0x00, 0xC0, 0x0F, // 1054 + 0x00, 0x00, 0x00, 0xF8, 0x7F, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, + 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0xF8, 0x7F, // 1055 + 0x00, 0x00, 0x00, 0xF8, 0x7F, 0x00, 0x08, 0x02, 0x00, 0x08, 0x02, 0x00, 0x08, 0x02, 0x00, 0x08, 0x02, 0x00, 0x08, 0x02, 0x00, + 0x08, 0x02, 0x00, 0x10, 0x01, 0x00, 0xE0, // 1056 + 0x00, 0x00, 0x00, 0xC0, 0x0F, 0x00, 0x20, 0x10, 0x00, 0x10, 0x20, 0x00, 0x08, 0x40, 0x00, 0x08, 0x40, 0x00, 0x08, 0x40, 0x00, + 0x08, 0x40, 0x00, 0x08, 0x40, 0x00, 0x10, 0x20, 0x00, 0x20, 0x10, // 1057 + 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0xF8, 0x7F, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, + 0x08, 0x00, 0x00, 0x08, // 1058 + 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x60, 0x40, 0x00, 0x80, 0x41, 0x00, 0x00, 0x66, 0x00, 0x00, 0x18, 0x00, 0x00, 0x06, 0x00, + 0x80, 0x01, 0x00, 0x60, 0x00, 0x00, 0x18, // 1059 + 0x00, 0x00, 0x00, 0x80, 0x07, 0x00, 0x40, 0x08, 0x00, 0x20, 0x10, 0x00, 0x20, 0x10, 0x00, 0xF8, 0x7F, 0x00, 0x20, 0x10, 0x00, + 0x20, 0x10, 0x00, 0x40, 0x08, 0x00, 0x80, 0x07, // 1060 + 0x00, 0x40, 0x00, 0x08, 0x20, 0x00, 0x10, 0x18, 0x00, 0x60, 0x04, 0x00, 0x80, 0x02, 0x00, 0x00, 0x01, 0x00, 0x80, 0x02, 0x00, + 0x60, 0x0C, 0x00, 0x10, 0x10, 0x00, 0x08, 0x20, 0x00, 0x00, 0x40, // 1061 + 0x00, 0x00, 0x00, 0xF8, 0x7F, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, + 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, 0xF8, 0x7F, 0x00, 0x00, 0xC0, 0x03, // 1062 + 0x00, 0x00, 0x00, 0xF8, 0x03, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, + 0x00, 0x02, 0x00, 0xF8, 0x7F, // 1063 + 0x00, 0x00, 0x00, 0xF8, 0x7F, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, 0xF8, 0x7F, 0x00, + 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, 0xF8, 0x7F, // 1064 + 0x00, 0x00, 0x00, 0xF8, 0x7F, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, 0xF8, 0x7F, 0x00, + 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, 0xF8, 0x7F, 0x00, 0x00, 0xC0, 0x03, // 1065 + 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0xF8, 0x7F, 0x00, 0x00, 0x41, 0x00, 0x00, 0x41, 0x00, 0x00, 0x41, 0x00, + 0x00, 0x41, 0x00, 0x00, 0x41, 0x00, 0x00, 0x41, 0x00, 0x00, 0x41, 0x00, 0x00, 0x3E, // 1066 + 0x00, 0x00, 0x00, 0xF8, 0x7F, 0x00, 0x00, 0x41, 0x00, 0x00, 0x41, 0x00, 0x00, 0x41, 0x00, 0x00, 0x41, 0x00, 0x00, 0x41, 0x00, + 0x00, 0x41, 0x00, 0x00, 0x41, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF8, 0x7F, // 1067 + 0x00, 0x00, 0x00, 0xF8, 0x7F, 0x00, 0x00, 0x41, 0x00, 0x00, 0x41, 0x00, 0x00, 0x41, 0x00, 0x00, 0x41, 0x00, 0x00, 0x41, 0x00, + 0x00, 0x41, 0x00, 0x00, 0x41, 0x00, 0x00, 0x3E, // 1068 + 0x00, 0x00, 0x00, 0x20, 0x10, 0x00, 0x10, 0x20, 0x00, 0x08, 0x40, 0x00, 0x08, 0x40, 0x00, 0x08, 0x41, 0x00, 0x08, 0x41, 0x00, + 0x08, 0x41, 0x00, 0x10, 0x21, 0x00, 0x20, 0x11, 0x00, 0xC0, 0x0F, // 1069 + 0x00, 0x00, 0x00, 0xF8, 0x7F, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0xC0, 0x0F, 0x00, 0x20, 0x10, 0x00, + 0x10, 0x20, 0x00, 0x08, 0x40, 0x00, 0x08, 0x40, 0x00, 0x08, 0x40, 0x00, 0x08, 0x40, 0x00, 0x10, 0x20, 0x00, 0x20, 0x10, 0x00, + 0xC0, 0x0F, // 1070 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0x40, 0x00, 0x10, 0x21, 0x00, 0x08, 0x1A, 0x00, 0x08, 0x0E, 0x00, 0x08, 0x02, 0x00, + 0x08, 0x02, 0x00, 0x08, 0x02, 0x00, 0x08, 0x02, 0x00, 0xF8, 0x7F, // 1071 + 0x00, 0x00, 0x00, 0x00, 0x39, 0x00, 0x80, 0x44, 0x00, 0x40, 0x44, 0x00, 0x40, 0x44, 0x00, 0x40, 0x42, 0x00, 0x40, 0x22, 0x00, + 0x80, 0x7F, // 1072 + 0x00, 0x00, 0x00, 0xE0, 0x1F, 0x00, 0x90, 0x20, 0x00, 0x48, 0x40, 0x00, 0x48, 0x40, 0x00, 0x48, 0x40, 0x00, 0x88, 0x20, 0x00, + 0x08, 0x1F, // 1073 + 0x00, 0x00, 0x00, 0xC0, 0x7F, 0x00, 0x40, 0x44, 0x00, 0x40, 0x44, 0x00, 0x40, 0x44, 0x00, 0x40, 0x44, 0x00, 0x40, 0x44, 0x00, + 0x80, 0x3B, // 1074 + 0x00, 0x00, 0x00, 0xC0, 0x7F, 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, // 1075 + 0x00, 0xC0, 0x01, 0x00, 0x70, 0x00, 0xC0, 0x4F, 0x00, 0x40, 0x40, 0x00, 0x40, 0x40, 0x00, 0x40, 0x40, 0x00, 0xC0, 0x7F, 0x00, + 0x00, 0xC0, 0x01, // 1076 + 0x00, 0x00, 0x00, 0x00, 0x1F, 0x00, 0x80, 0x24, 0x00, 0x40, 0x44, 0x00, 0x40, 0x44, 0x00, 0x40, 0x44, 0x00, 0x80, 0x24, 0x00, + 0x00, 0x17, // 1077 + 0xC0, 0x20, 0x00, 0x00, 0x1B, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0xC0, 0x7F, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, + 0x00, 0x1B, 0x00, 0xC0, 0x20, 0x00, 0x40, 0x40, // 1078 + 0x80, 0x20, 0x00, 0x40, 0x40, 0x00, 0x40, 0x44, 0x00, 0x40, 0x44, 0x00, 0x40, 0x44, 0x00, 0x80, 0x3B, // 1079 + 0x00, 0x00, 0x00, 0xC0, 0x7F, 0x00, 0x00, 0x30, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x02, 0x00, 0x80, 0x01, 0x00, 0xC0, + 0x7F, // 1080 + 0x00, 0x00, 0x00, 0xC0, 0x7F, 0x00, 0x08, 0x30, 0x00, 0x10, 0x0C, 0x00, 0x10, 0x02, 0x00, 0x90, 0x01, 0x00, 0xC8, + 0x7F, // 1081 + 0x00, 0x00, 0x00, 0xC0, 0x7F, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x1B, 0x00, 0xC0, 0x20, 0x00, 0x40, + 0x40, // 1082 + 0x00, 0x40, 0x00, 0xC0, 0x3F, 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, 0x00, 0xC0, + 0x7F, // 1083 + 0x00, 0x00, 0x00, 0xC0, 0x7F, 0x00, 0xC0, 0x01, 0x00, 0x00, 0x06, 0x00, 0x00, 0x18, 0x00, 0x00, 0x60, 0x00, 0x00, 0x18, 0x00, + 0x00, 0x06, 0x00, 0xC0, 0x01, 0x00, 0xC0, 0x7F, // 1084 + 0x00, 0x00, 0x00, 0xC0, 0x7F, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0xC0, + 0x7F, // 1085 + 0x00, 0x00, 0x00, 0x00, 0x1F, 0x00, 0x80, 0x20, 0x00, 0x40, 0x40, 0x00, 0x40, 0x40, 0x00, 0x40, 0x40, 0x00, 0x80, 0x20, 0x00, + 0x00, 0x1F, // 1086 + 0x00, 0x00, 0x00, 0xC0, 0x7F, 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, 0x00, 0xC0, + 0x7F, // 1087 + 0x00, 0x00, 0x00, 0xC0, 0xFF, 0x03, 0x80, 0x20, 0x00, 0x40, 0x40, 0x00, 0x40, 0x40, 0x00, 0x40, 0x40, 0x00, 0x80, 0x20, 0x00, + 0x00, 0x1F, // 1088 + 0x00, 0x00, 0x00, 0x00, 0x1F, 0x00, 0x80, 0x20, 0x00, 0x40, 0x40, 0x00, 0x40, 0x40, 0x00, 0x40, 0x40, 0x00, 0x80, + 0x20, // 1089 + 0x40, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, 0x00, 0xC0, 0x7F, 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, // 1090 + 0xC0, 0x01, 0x00, 0x00, 0x06, 0x02, 0x00, 0x38, 0x02, 0x00, 0xC0, 0x01, 0x00, 0x38, 0x00, 0x00, 0x07, 0x00, 0xC0, // 1091 + 0x00, 0x00, 0x00, 0x00, 0x1F, 0x00, 0x80, 0x20, 0x00, 0x40, 0x40, 0x00, 0x40, 0x40, 0x00, 0x80, 0x20, 0x00, 0xF8, 0xFF, 0x03, + 0x80, 0x20, 0x00, 0x40, 0x40, 0x00, 0x40, 0x40, 0x00, 0x80, 0x20, 0x00, 0x00, 0x1F, // 1092 + 0x40, 0x40, 0x00, 0x80, 0x20, 0x00, 0x00, 0x1B, 0x00, 0x00, 0x04, 0x00, 0x00, 0x1B, 0x00, 0x80, 0x20, 0x00, 0x40, + 0x40, // 1093 + 0x00, 0x00, 0x00, 0xC0, 0x7F, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, + 0xC0, 0x7F, 0x00, 0x00, 0xC0, 0x03, // 1094 + 0x00, 0x00, 0x00, 0xC0, 0x03, 0x00, 0x00, 0x04, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0xC0, + 0x7F, // 1095 + 0x00, 0x00, 0x00, 0xC0, 0x7F, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, 0xC0, 0x7F, 0x00, 0x00, 0x40, 0x00, + 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, 0xC0, 0x7F, // 1096 + 0x00, 0x00, 0x00, 0xC0, 0x7F, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, 0xC0, 0x7F, 0x00, 0x00, 0x40, 0x00, + 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, 0xC0, 0x7F, 0x00, 0x00, 0xC0, 0x03, // 1097 + 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, 0x00, 0xC0, 0x7F, 0x00, 0x00, 0x44, 0x00, 0x00, 0x44, 0x00, 0x00, 0x44, 0x00, + 0x00, 0x44, 0x00, 0x00, 0x38, // 1098 + 0x00, 0x00, 0x00, 0xC0, 0x7F, 0x00, 0x00, 0x44, 0x00, 0x00, 0x44, 0x00, 0x00, 0x44, 0x00, 0x00, 0x44, 0x00, 0x00, 0x44, 0x00, + 0x00, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0x7F, // 1099 + 0x00, 0x00, 0x00, 0xC0, 0x7F, 0x00, 0x00, 0x44, 0x00, 0x00, 0x44, 0x00, 0x00, 0x44, 0x00, 0x00, 0x44, 0x00, 0x00, 0x44, 0x00, + 0x00, 0x38, // 1100 + 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x80, 0x20, 0x00, 0x40, 0x44, 0x00, 0x40, 0x44, 0x00, 0x80, 0x24, 0x00, 0x00, + 0x1F, // 1101 + 0x00, 0x00, 0x00, 0xC0, 0x7F, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x1F, 0x00, 0x80, 0x20, 0x00, 0x40, 0x40, 0x00, + 0x40, 0x40, 0x00, 0x40, 0x40, 0x00, 0x80, 0x20, 0x00, 0x00, 0x1F, // 1102 + 0x00, 0x00, 0x00, 0x80, 0x63, 0x00, 0x40, 0x14, 0x00, 0x40, 0x0C, 0x00, 0x40, 0x04, 0x00, 0x40, 0x04, 0x00, 0x40, 0x04, 0x00, + 0xC0, 0x7F, // 1103 +}; + +const uint8_t ArialMT_Plain_24_UA[] PROGMEM = { + 0x18, // Width: 24 + 0x1C, // Height: 28 + 0x20, // First Char: 32 + 0xE0, // Numbers of Chars: 224 + // Jump Table: + 0xFF, 0xFF, 0x00, 0x07, // 32= :65535 + 0x00, 0x00, 0x13, 0x08, // 33=!:0 + 0x00, 0x13, 0x1A, 0x09, // 34=":19 + 0x00, 0x2D, 0x33, 0x0D, // 35=#:45 + 0x00, 0x60, 0x2F, 0x0D, // 36=$:96 + 0x00, 0x8F, 0x4F, 0x15, // 37=%:143 + 0x00, 0xDE, 0x3B, 0x10, // 38=&:222 + 0x01, 0x19, 0x0A, 0x05, // 39=':281 + 0x01, 0x23, 0x1C, 0x08, // 40=(:291 + 0x01, 0x3F, 0x1B, 0x08, // 41=):319 + 0x01, 0x5A, 0x22, 0x09, // 42=*:346 + 0x01, 0x7C, 0x33, 0x0E, // 43=+:380 + 0x01, 0xAF, 0x10, 0x07, // 44=,:431 + 0x01, 0xBF, 0x1B, 0x08, // 45=-:447 + 0x01, 0xDA, 0x0F, 0x07, // 46=.:474 + 0x01, 0xE9, 0x1A, 0x07, // 47=/:489 + 0x02, 0x03, 0x2F, 0x0D, // 48=0:515 + 0x02, 0x32, 0x23, 0x0D, // 49=1:562 + 0x02, 0x55, 0x2F, 0x0D, // 50=2:597 + 0x02, 0x84, 0x2F, 0x0D, // 51=3:644 + 0x02, 0xB3, 0x2F, 0x0D, // 52=4:691 + 0x02, 0xE2, 0x2F, 0x0D, // 53=5:738 + 0x03, 0x11, 0x2F, 0x0D, // 54=6:785 + 0x03, 0x40, 0x2E, 0x0D, // 55=7:832 + 0x03, 0x6E, 0x2F, 0x0D, // 56=8:878 + 0x03, 0x9D, 0x2F, 0x0D, // 57=9:925 + 0x03, 0xCC, 0x0F, 0x07, // 58=::972 + 0x03, 0xDB, 0x10, 0x07, // 59=;:987 + 0x03, 0xEB, 0x2F, 0x0E, // 60=<:1003 + 0x04, 0x1A, 0x2F, 0x0E, // 61==:1050 + 0x04, 0x49, 0x2E, 0x0E, // 62=>:1097 + 0x04, 0x77, 0x2E, 0x0D, // 63=?:1143 + 0x04, 0xA5, 0x5C, 0x18, // 64=@:1189 + 0x05, 0x01, 0x3B, 0x0F, // 65=A:1281 + 0x05, 0x3C, 0x3B, 0x10, // 66=B:1340 + 0x05, 0x77, 0x3F, 0x11, // 67=C:1399 + 0x05, 0xB6, 0x3F, 0x11, // 68=D:1462 + 0x05, 0xF5, 0x3B, 0x10, // 69=E:1525 + 0x06, 0x30, 0x36, 0x0F, // 70=F:1584 + 0x06, 0x66, 0x43, 0x13, // 71=G:1638 + 0x06, 0xA9, 0x3B, 0x11, // 72=H:1705 + 0x06, 0xE4, 0x0F, 0x06, // 73=I:1764 + 0x06, 0xF3, 0x27, 0x0C, // 74=J:1779 + 0x07, 0x1A, 0x3F, 0x10, // 75=K:1818 + 0x07, 0x59, 0x2F, 0x0D, // 76=L:1881 + 0x07, 0x88, 0x43, 0x13, // 77=M:1928 + 0x07, 0xCB, 0x3B, 0x11, // 78=N:1995 + 0x08, 0x06, 0x47, 0x13, // 79=O:2054 + 0x08, 0x4D, 0x3A, 0x10, // 80=P:2125 + 0x08, 0x87, 0x48, 0x13, // 81=Q:2183 + 0x08, 0xCF, 0x3F, 0x11, // 82=R:2255 + 0x09, 0x0E, 0x3B, 0x10, // 83=S:2318 + 0x09, 0x49, 0x36, 0x0E, // 84=T:2377 + 0x09, 0x7F, 0x3B, 0x11, // 85=U:2431 + 0x09, 0xBA, 0x39, 0x0F, // 86=V:2490 + 0x09, 0xF3, 0x5A, 0x17, // 87=W:2547 + 0x0A, 0x4D, 0x3B, 0x0F, // 88=X:2637 + 0x0A, 0x88, 0x3D, 0x10, // 89=Y:2696 + 0x0A, 0xC5, 0x37, 0x0F, // 90=Z:2757 + 0x0A, 0xFC, 0x14, 0x07, // 91=[:2812 + 0x0B, 0x10, 0x1B, 0x07, // 92=\:2832 + 0x0B, 0x2B, 0x18, 0x07, // 93=]:2859 + 0x0B, 0x43, 0x2A, 0x0C, // 94=^:2883 + 0x0B, 0x6D, 0x34, 0x0D, // 95=_:2925 + 0x0B, 0xA1, 0x12, 0x08, // 96=`:2977 + 0x0B, 0xB3, 0x2F, 0x0D, // 97=a:2995 + 0x0B, 0xE2, 0x33, 0x0E, // 98=b:3042 + 0x0C, 0x15, 0x2B, 0x0C, // 99=c:3093 + 0x0C, 0x40, 0x2F, 0x0E, // 100=d:3136 + 0x0C, 0x6F, 0x2F, 0x0D, // 101=e:3183 + 0x0C, 0x9E, 0x1A, 0x07, // 102=f:3230 + 0x0C, 0xB8, 0x30, 0x0E, // 103=g:3256 + 0x0C, 0xE8, 0x2F, 0x0E, // 104=h:3304 + 0x0D, 0x17, 0x0F, 0x05, // 105=i:3351 + 0x0D, 0x26, 0x10, 0x06, // 106=j:3366 + 0x0D, 0x36, 0x2F, 0x0C, // 107=k:3382 + 0x0D, 0x65, 0x0F, 0x06, // 108=l:3429 + 0x0D, 0x74, 0x47, 0x14, // 109=m:3444 + 0x0D, 0xBB, 0x2F, 0x0E, // 110=n:3515 + 0x0D, 0xEA, 0x2F, 0x0D, // 111=o:3562 + 0x0E, 0x19, 0x33, 0x0E, // 112=p:3609 + 0x0E, 0x4C, 0x30, 0x0E, // 113=q:3660 + 0x0E, 0x7C, 0x1E, 0x08, // 114=r:3708 + 0x0E, 0x9A, 0x2B, 0x0C, // 115=s:3738 + 0x0E, 0xC5, 0x1B, 0x07, // 116=t:3781 + 0x0E, 0xE0, 0x2F, 0x0E, // 117=u:3808 + 0x0F, 0x0F, 0x2A, 0x0B, // 118=v:3855 + 0x0F, 0x39, 0x42, 0x11, // 119=w:3897 + 0x0F, 0x7B, 0x2B, 0x0B, // 120=x:3963 + 0x0F, 0xA6, 0x2A, 0x0C, // 121=y:4006 + 0x0F, 0xD0, 0x2B, 0x0C, // 122=z:4048 + 0x0F, 0xFB, 0x1C, 0x08, // 123={:4091 + 0x10, 0x17, 0x10, 0x06, // 124=|:4119 + 0x10, 0x27, 0x1B, 0x08, // 125=}:4135 + 0x10, 0x42, 0x33, 0x0E, // 126=~:4162 + 0xFF, 0xFF, 0x00, 0x12, // 127:65535 + 0x10, 0x75, 0x4F, 0x15, // 1026=� �..:4213 + 0x10, 0xC4, 0x32, 0x0D, // 1027=� �.:4292 + 0x10, 0xF6, 0x0C, 0x05, // 8218=��.�.:4342 + 0x11, 0x02, 0x22, 0x09, // 1107=�.�..:4354 + 0x11, 0x24, 0x1C, 0x08, // 8222=��.�.:4388 + 0x11, 0x40, 0x4B, 0x18, // 8230=��.�.:4416 + 0x11, 0x8B, 0x32, 0x0D, // 8224=��.� :4491 + 0x11, 0xBD, 0x33, 0x0D, // 8225=��.�.:4541 + 0x11, 0xF0, 0x2F, 0x0D, // 8364=��..�.:4592 + 0x12, 0x1F, 0x63, 0x1A, // 8240=��.��:4639 + 0x12, 0x82, 0x5F, 0x19, // 1033=� �.�:4738 + 0x12, 0xE1, 0x17, 0x08, // 8249=��.�..:4833 + 0x12, 0xF8, 0x5B, 0x18, // 1034=� �.:4856 + 0x13, 0x53, 0x37, 0x0E, // 1036=� �.:4947 + 0x13, 0x8A, 0x4F, 0x16, // 1035=� �..:5002 + 0x13, 0xD9, 0x3B, 0x11, // 1039=� �.:5081 + 0x14, 0x14, 0x30, 0x0E, // 1106=�.�..:5140 + 0x14, 0x44, 0x0A, 0x05, // 8216=��.Ч.:5188 + 0x14, 0x4E, 0x0A, 0x05, // 8217=��.�..:5198 + 0x14, 0x58, 0x1A, 0x08, // 8220=��.�.:5208 + 0x14, 0x72, 0x1A, 0x08, // 8221=��.�.:5234 + 0x14, 0x8C, 0x1B, 0x08, // 8226=��.�.:5260 + 0x14, 0xA7, 0x33, 0x0D, // 8211=��.�..:5287 + 0x14, 0xDA, 0x5F, 0x18, // 8212=��.�..:5338 + 0xFF, 0xFF, 0x00, 0x12, // 65533=��.�.:65535 + 0x15, 0x39, 0x5B, 0x18, // 8482=��..�.:5433 + 0x15, 0x94, 0x53, 0x16, // 1113=�.�..:5524 + 0x15, 0xE7, 0x1B, 0x08, // 8250=��.�.:5607 + 0x16, 0x02, 0x4B, 0x14, // 1114=�.�.:5634 + 0x16, 0x4D, 0x2B, 0x0B, // 1116=�.�.:5709 + 0x16, 0x78, 0x2F, 0x0E, // 1115=�.�.�:5752 + 0x16, 0xA7, 0x2F, 0x0D, // 1119=�.�.:5799 + 0xFF, 0xFF, 0x00, 0x07, // 160=�.� :65535 + 0x16, 0xD6, 0x36, 0x0F, // 1038=� �.:5846 + 0x17, 0x0C, 0x2A, 0x0C, // 1118=�.�.:5900 + 0x17, 0x36, 0x27, 0x0C, // 1032=� �..:5942 + 0x17, 0x5D, 0x33, 0x0D, // 164=�.�.:5981 + 0x17, 0x90, 0x2A, 0x0C, // 1168=�.�.:6032 + 0x17, 0xBA, 0x10, 0x06, // 166=�.�.:6074 + 0x17, 0xCA, 0x2F, 0x0D, // 167=�.�.:6090 + 0x17, 0xF9, 0x3B, 0x10, // 1025=� �.:6137 + 0x18, 0x34, 0x47, 0x12, // 169=�.��:6196 + 0x18, 0x7B, 0x3F, 0x11, // 1028=� �..:6267 + 0x18, 0xBA, 0x27, 0x0D, // 171=�.�.:6330 + 0x18, 0xE1, 0x2F, 0x0E, // 172=�.�.:6369 + 0x19, 0x10, 0x1B, 0x08, // 173=�.�:6416 + 0x19, 0x2B, 0x47, 0x12, // 174=�.�.:6443 + 0x19, 0x72, 0x15, 0x06, // 1031=� �..:6514 + 0x19, 0x87, 0x1E, 0x0A, // 176=�.��:6535 + 0x19, 0xA5, 0x33, 0x0D, // 177=�.�.:6565 + 0x19, 0xD8, 0x0F, 0x06, // 1030=� �. :6616 + 0x19, 0xE7, 0x0F, 0x05, // 1110=�.�..:6631 + 0x19, 0xF6, 0x22, 0x0A, // 1169=�.�.:6646 + 0x1A, 0x18, 0x2F, 0x0E, // 181=�.�.:6680 + 0x1A, 0x47, 0x32, 0x0D, // 182=�.�.:6727 + 0x1A, 0x79, 0x13, 0x08, // 183=�.��:6777 + 0x1A, 0x8C, 0x2F, 0x0D, // 1105=�.�.:6796 + 0x1A, 0xBB, 0x63, 0x1A, // 8470=��..�..:6843 + 0x1B, 0x1E, 0x2B, 0x0C, // 1108=�.�..:6942 + 0x1B, 0x49, 0x2F, 0x0D, // 187=�.�.:6985 + 0x1B, 0x78, 0x10, 0x06, // 1112=�.Ч.:7032 + 0x1B, 0x88, 0x3B, 0x10, // 1029=� �..:7048 + 0x1B, 0xC3, 0x2B, 0x0C, // 1109=�.�..:7107 + 0x1B, 0xEE, 0x16, 0x06, // 1111=�.�..:7150 + 0x1C, 0x04, 0x3B, 0x0F, // 1040=� �.:7172 + 0x1C, 0x3F, 0x3B, 0x10, // 1041=� �.:7231 + 0x1C, 0x7A, 0x3B, 0x10, // 1042=� �..:7290 + 0x1C, 0xB5, 0x32, 0x0D, // 1043=� �..:7349 + 0x1C, 0xE7, 0x40, 0x10, // 1044=� �..:7399 + 0x1D, 0x27, 0x3B, 0x10, // 1045=� �..:7463 + 0x1D, 0x62, 0x57, 0x16, // 1046=� �..:7522 + 0x1D, 0xB9, 0x37, 0x0F, // 1047=� �..:7609 + 0x1D, 0xF0, 0x3B, 0x11, // 1048=� Ч.:7664 + 0x1E, 0x2B, 0x3B, 0x11, // 1049=� �..:7723 + 0x1E, 0x66, 0x37, 0x0E, // 1050=� �.:7782 + 0x1E, 0x9D, 0x37, 0x10, // 1051=� �.�:7837 + 0x1E, 0xD4, 0x43, 0x13, // 1052=� �.:7892 + 0x1F, 0x17, 0x3B, 0x11, // 1053=� �.:7959 + 0x1F, 0x52, 0x47, 0x13, // 1054=� �.:8018 + 0x1F, 0x99, 0x3B, 0x11, // 1055=� �.:8089 + 0x1F, 0xD4, 0x3A, 0x10, // 1056=� � :8148 + 0x20, 0x0E, 0x3F, 0x11, // 1057=� �.:8206 + 0x20, 0x4D, 0x36, 0x0E, // 1058=� �.:8269 + 0x20, 0x83, 0x36, 0x0F, // 1059=� �.:8323 + 0x20, 0xB9, 0x43, 0x12, // 1060=� �.:8377 + 0x20, 0xFC, 0x3B, 0x0F, // 1061=� �.:8444 + 0x21, 0x37, 0x44, 0x12, // 1062=� �.:8503 + 0x21, 0x7B, 0x37, 0x10, // 1063=� �.:8571 + 0x21, 0xB2, 0x53, 0x16, // 1064=� �.:8626 + 0x22, 0x05, 0x5C, 0x17, // 1065=� ��:8709 + 0x22, 0x61, 0x47, 0x13, // 1066=� �.:8801 + 0x22, 0xA8, 0x4B, 0x15, // 1067=� �.:8872 + 0x22, 0xF3, 0x3B, 0x10, // 1068=� �.:8947 + 0x23, 0x2E, 0x3F, 0x11, // 1069=� �:9006 + 0x23, 0x6D, 0x5B, 0x18, // 1070=� �.:9069 + 0x23, 0xC8, 0x3B, 0x11, // 1071=� �.:9160 + 0x24, 0x03, 0x2F, 0x0D, // 1072=� ��:9219 + 0x24, 0x32, 0x33, 0x0E, // 1073=� �.:9266 + 0x24, 0x65, 0x2F, 0x0D, // 1074=� �.:9317 + 0x24, 0x94, 0x22, 0x09, // 1075=� �.:9364 + 0x24, 0xB6, 0x34, 0x0E, // 1076=� �.:9398 + 0x24, 0xEA, 0x2F, 0x0D, // 1077=� �.:9450 + 0x25, 0x19, 0x3B, 0x10, // 1078=� �.:9497 + 0x25, 0x54, 0x27, 0x0B, // 1079=� ��:9556 + 0x25, 0x7B, 0x2F, 0x0D, // 1080=� �.:9595 + 0x25, 0xAA, 0x2F, 0x0D, // 1081=� �..:9642 + 0x25, 0xD9, 0x2B, 0x0B, // 1082=� �.:9689 + 0x26, 0x04, 0x2F, 0x0E, // 1083=� �.:9732 + 0x26, 0x33, 0x3B, 0x11, // 1084=� �:9779 + 0x26, 0x6E, 0x2F, 0x0D, // 1085=� �.:9838 + 0x26, 0x9D, 0x2F, 0x0D, // 1086=� �.:9885 + 0x26, 0xCC, 0x2F, 0x0D, // 1087=� �.:9932 + 0x26, 0xFB, 0x33, 0x0E, // 1088=�.�.:9979 + 0x27, 0x2E, 0x2B, 0x0C, // 1089=�.�.:10030 + 0x27, 0x59, 0x26, 0x0B, // 1090=�.�..:10073 + 0x27, 0x7F, 0x2A, 0x0C, // 1091=�.�.:10111 + 0x27, 0xA9, 0x4B, 0x14, // 1092=�.�..:10153 + 0x27, 0xF4, 0x2B, 0x0B, // 1093=�.�..:10228 + 0x28, 0x1F, 0x34, 0x0E, // 1094=�.�. :10271 + 0x28, 0x53, 0x2B, 0x0D, // 1095=�.�..:10323 + 0x28, 0x7E, 0x47, 0x13, // 1096=�.�..:10366 + 0x28, 0xC5, 0x4C, 0x14, // 1097=�.�.�:10437 + 0x29, 0x11, 0x37, 0x0F, // 1098=�.�.:10513 + 0x29, 0x48, 0x3B, 0x11, // 1099=�.�..:10568 + 0x29, 0x83, 0x2F, 0x0D, // 1100=�.�.:10627 + 0x29, 0xB2, 0x2B, 0x0C, // 1101=�.�.:10674 + 0x29, 0xDD, 0x43, 0x12, // 1102=�.�.:10717 + 0x2A, 0x20, 0x2B, 0x0D, // 1103=�.�.:10784 + // Font Data: + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xFF, 0xCF, 0x00, 0x80, 0xFF, 0xCF, // 33 + 0x00, 0x00, 0x00, 0x00, 0x80, 0x1F, 0x00, 0x00, 0x80, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, + 0x1F, 0x00, 0x00, 0x80, 0x1F, // 34 + 0x00, 0x30, 0x0C, 0x00, 0x00, 0x30, 0xCC, 0x00, 0x00, 0x30, 0xFE, 0x00, 0x00, 0xF0, 0x3F, 0x00, 0x00, 0xFE, 0x0F, 0x00, 0x80, + 0x3F, 0x0C, 0x00, 0x80, 0x31, 0xCC, 0x00, 0x00, 0x30, 0xFE, 0x00, 0x00, 0xF0, 0x3F, 0x00, 0x00, 0xFE, 0x0D, 0x00, 0x80, 0x3F, + 0x0C, 0x00, 0x80, 0x31, 0x0C, 0x00, 0x00, 0x30, 0x0C, // 35 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x1E, 0x18, 0x00, 0x00, 0x3F, 0x78, 0x00, 0x00, 0x63, 0x70, 0x00, 0x80, 0x61, 0xE0, 0x00, 0x80, + 0xC1, 0xC0, 0x00, 0xC0, 0xFF, 0xFF, 0x03, 0x80, 0xC1, 0xC0, 0x00, 0x80, 0x81, 0xC1, 0x00, 0x00, 0x83, 0x61, 0x00, 0x00, 0x07, + 0x7F, 0x00, 0x00, 0x04, 0x1E, // 36 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x00, 0x7F, 0x00, 0x00, 0x80, 0xC1, 0x00, 0x00, 0x80, 0x80, 0x00, 0x00, 0x80, + 0x80, 0x80, 0x00, 0x80, 0xC1, 0xE0, 0x00, 0x00, 0x7F, 0x78, 0x00, 0x00, 0x3E, 0x3E, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0xC0, + 0x03, 0x00, 0x00, 0xF0, 0x00, 0x00, 0x00, 0x3C, 0x3E, 0x00, 0x00, 0x0F, 0x7F, 0x00, 0x80, 0x83, 0xC1, 0x00, 0x80, 0x80, 0x80, + 0x00, 0x00, 0x80, 0x80, 0x00, 0x00, 0x80, 0xC1, 0x00, 0x00, 0x00, 0x7F, 0x00, 0x00, 0x00, 0x3E, // 37 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x00, 0x7F, 0x00, 0x00, 0x8E, 0x73, 0x00, 0x00, 0xDF, 0xE1, 0x00, 0x80, + 0xF3, 0xC0, 0x00, 0x80, 0xE1, 0xC0, 0x00, 0x80, 0xE1, 0xC1, 0x00, 0x80, 0xB3, 0xE3, 0x00, 0x00, 0x3F, 0x6E, 0x00, 0x00, 0x0E, + 0x7C, 0x00, 0x00, 0x00, 0x3C, 0x00, 0x00, 0x00, 0x7F, 0x00, 0x00, 0x00, 0xE2, 0x00, 0x00, 0x00, 0x40, // 38 + 0x00, 0x00, 0x00, 0x00, 0x80, 0x1F, 0x00, 0x00, 0x80, 0x1F, // 39 + 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0x3F, 0x00, 0x00, 0xF8, 0xFF, 0x01, 0x00, 0x3E, 0xC0, 0x07, 0x00, 0x07, 0x00, 0x0E, 0x80, + 0x01, 0x00, 0x18, 0x80, 0x00, 0x00, 0x10, // 40 + 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x10, 0x80, 0x01, 0x00, 0x18, 0x00, 0x07, 0x00, 0x0E, 0x00, 0x3E, 0xC0, 0x07, 0x00, + 0xF8, 0xFF, 0x01, 0x00, 0xC0, 0x3F, // 41 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x80, 0x0F, 0x00, 0x00, 0x80, + 0x0F, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x02, // 42 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, + 0x80, 0x01, 0x00, 0x00, 0xFC, 0x3F, 0x00, 0x00, 0xFC, 0x3F, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, + 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, // 43 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0x0C, 0x00, 0x00, 0xC0, 0x07, // 44 + 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, + 0x00, 0x06, 0x00, 0x00, 0x00, 0x06, // 45 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0xC0, // 46 + 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x00, 0x80, 0x3F, 0x00, 0x00, 0xF0, 0x07, 0x00, 0x00, 0xFE, 0x00, 0x00, 0x80, + 0x0F, 0x00, 0x00, 0x80, 0x01, // 47 + 0x00, 0x00, 0x00, 0x00, 0x00, 0xF8, 0x0F, 0x00, 0x00, 0xFE, 0x3F, 0x00, 0x00, 0x07, 0x70, 0x00, 0x80, 0x03, 0xE0, 0x00, 0x80, + 0x01, 0xC0, 0x00, 0x80, 0x01, 0xC0, 0x00, 0x80, 0x01, 0xC0, 0x00, 0x80, 0x03, 0xE0, 0x00, 0x00, 0x07, 0x70, 0x00, 0x00, 0xFE, + 0x3F, 0x00, 0x00, 0xF8, 0x0F, // 48 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x00, + 0x0C, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x80, 0xFF, 0xFF, 0x00, 0x80, 0xFF, 0xFF, // 49 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x0C, 0xC0, 0x00, 0x00, 0x0F, 0xE0, 0x00, 0x00, 0x03, 0xF0, 0x00, 0x80, 0x01, 0xD8, 0x00, 0x80, + 0x01, 0xCC, 0x00, 0x80, 0x01, 0xC6, 0x00, 0x80, 0x01, 0xC3, 0x00, 0x80, 0x81, 0xC1, 0x00, 0x00, 0xC3, 0xC0, 0x00, 0x00, 0x7F, + 0xC0, 0x00, 0x00, 0x3C, 0xC0, // 50 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x18, 0x00, 0x00, 0x07, 0x38, 0x00, 0x00, 0x03, 0x70, 0x00, 0x80, 0x01, 0xC0, 0x00, 0x80, + 0xC1, 0xC0, 0x00, 0x80, 0xC1, 0xC0, 0x00, 0x80, 0xC1, 0xC0, 0x00, 0x00, 0xE3, 0xC0, 0x00, 0x00, 0xBF, 0x61, 0x00, 0x00, 0x1E, + 0x3F, 0x00, 0x00, 0x00, 0x1E, // 51 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0xC0, 0x0F, 0x00, 0x00, 0xF0, 0x0C, 0x00, 0x00, + 0x38, 0x0C, 0x00, 0x00, 0x1E, 0x0C, 0x00, 0x00, 0x07, 0x0C, 0x00, 0x80, 0xFF, 0xFF, 0x00, 0x80, 0xFF, 0xFF, 0x00, 0x00, 0x00, + 0x0C, 0x00, 0x00, 0x00, 0x0C, // 52 + 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0x18, 0x00, 0x00, 0xFE, 0x38, 0x00, 0x80, 0x7F, 0x60, 0x00, 0x80, 0x21, 0xC0, 0x00, 0x80, + 0x31, 0xC0, 0x00, 0x80, 0x31, 0xC0, 0x00, 0x80, 0x31, 0xC0, 0x00, 0x80, 0x31, 0xC0, 0x00, 0x80, 0x61, 0x70, 0x00, 0x80, 0xC1, + 0x3F, 0x00, 0x00, 0x80, 0x0F, // 53 + 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0x0F, 0x00, 0x00, 0xFE, 0x3F, 0x00, 0x00, 0x8F, 0x71, 0x00, 0x00, 0xC3, 0xE0, 0x00, 0x80, + 0x61, 0xC0, 0x00, 0x80, 0x61, 0xC0, 0x00, 0x80, 0x61, 0xC0, 0x00, 0x80, 0x61, 0xC0, 0x00, 0x80, 0xC3, 0x60, 0x00, 0x00, 0xC7, + 0x3F, 0x00, 0x00, 0x06, 0x1F, // 54 + 0x00, 0x00, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0xF0, 0x00, 0x80, + 0x01, 0xFE, 0x00, 0x80, 0xC1, 0x0F, 0x00, 0x80, 0xE1, 0x01, 0x00, 0x80, 0x39, 0x00, 0x00, 0x80, 0x0D, 0x00, 0x00, 0x80, 0x07, + 0x00, 0x00, 0x80, 0x01, // 55 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x1E, 0x7F, 0x00, 0x00, 0xBF, 0x61, 0x00, 0x80, 0xE3, 0xC0, 0x00, 0x80, + 0xC1, 0xC0, 0x00, 0x80, 0xC1, 0xC0, 0x00, 0x80, 0xC1, 0xC0, 0x00, 0x80, 0xE3, 0xC0, 0x00, 0x00, 0xBF, 0x61, 0x00, 0x00, 0x1E, + 0x7F, 0x00, 0x00, 0x00, 0x1E, // 56 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x7C, 0x30, 0x00, 0x00, 0xFE, 0x71, 0x00, 0x00, 0x87, 0xE1, 0x00, 0x80, 0x01, 0xC3, 0x00, 0x80, + 0x01, 0xC3, 0x00, 0x80, 0x01, 0xC3, 0x00, 0x80, 0x01, 0xC3, 0x00, 0x80, 0x81, 0x61, 0x00, 0x00, 0xC7, 0x78, 0x00, 0x00, 0xFE, + 0x3F, 0x00, 0x00, 0xF8, 0x07, // 57 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0xC0, 0x00, 0x00, 0x18, 0xC0, // 58 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0xC0, 0x0C, 0x00, 0x18, 0xC0, 0x07, // 59 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0xC0, 0x01, 0x00, 0x00, 0x40, 0x01, 0x00, 0x00, 0x60, 0x03, 0x00, 0x00, + 0x60, 0x03, 0x00, 0x00, 0x30, 0x06, 0x00, 0x00, 0x30, 0x06, 0x00, 0x00, 0x10, 0x04, 0x00, 0x00, 0x18, 0x0C, 0x00, 0x00, 0x18, + 0x0C, 0x00, 0x00, 0x0C, 0x18, // 60 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x06, 0x00, 0x00, 0x30, 0x06, 0x00, 0x00, 0x30, 0x06, 0x00, 0x00, 0x30, 0x06, 0x00, 0x00, + 0x30, 0x06, 0x00, 0x00, 0x30, 0x06, 0x00, 0x00, 0x30, 0x06, 0x00, 0x00, 0x30, 0x06, 0x00, 0x00, 0x30, 0x06, 0x00, 0x00, 0x30, + 0x06, 0x00, 0x00, 0x30, 0x06, // 61 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x0C, 0x18, 0x00, 0x00, 0x18, 0x0C, 0x00, 0x00, 0x18, 0x0C, 0x00, 0x00, 0x10, 0x04, 0x00, 0x00, + 0x30, 0x06, 0x00, 0x00, 0x30, 0x06, 0x00, 0x00, 0x60, 0x03, 0x00, 0x00, 0x60, 0x03, 0x00, 0x00, 0x40, 0x01, 0x00, 0x00, 0xC0, + 0x01, 0x00, 0x00, 0x80, // 62 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x80, 0x03, 0x00, 0x00, 0x80, + 0x01, 0xCE, 0x00, 0x80, 0x01, 0xCF, 0x00, 0x80, 0x81, 0x01, 0x00, 0x80, 0xC1, 0x00, 0x00, 0x00, 0xE3, 0x00, 0x00, 0x00, 0x7F, + 0x00, 0x00, 0x00, 0x1C, // 63 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x3F, 0x00, 0x00, 0xE0, 0xFF, 0x00, 0x00, 0x78, 0xC0, 0x03, 0x00, 0x1C, 0x00, 0x07, 0x00, + 0x0E, 0x1F, 0x06, 0x00, 0xC7, 0x7F, 0x0E, 0x00, 0xE3, 0x60, 0x0C, 0x00, 0x33, 0xC0, 0x0C, 0x80, 0x39, 0xC0, 0x18, 0x80, 0x19, + 0xC0, 0x18, 0x80, 0x19, 0x60, 0x18, 0x80, 0x19, 0x30, 0x18, 0x80, 0x31, 0x78, 0x18, 0x80, 0xE1, 0xFF, 0x18, 0x80, 0xFB, 0xC7, + 0x18, 0x00, 0x3B, 0xC0, 0x18, 0x00, 0x07, 0x60, 0x0C, 0x00, 0x0E, 0x70, 0x0C, 0x00, 0x1C, 0x3C, 0x06, 0x00, 0xF8, 0x1F, 0x06, + 0x00, 0xE0, 0x07, 0x03, 0x00, 0x00, 0x00, 0x01, // 64 + 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x00, 0xC0, 0x0F, 0x00, 0x00, 0xF8, 0x07, 0x00, 0x00, + 0x3E, 0x06, 0x00, 0x80, 0x0F, 0x06, 0x00, 0x80, 0x01, 0x06, 0x00, 0x80, 0x0F, 0x06, 0x00, 0x00, 0x3E, 0x06, 0x00, 0x00, 0xF8, + 0x07, 0x00, 0x00, 0xC0, 0x0F, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x00, 0x00, 0xC0, // 65 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xFF, 0xFF, 0x00, 0x80, 0xFF, 0xFF, 0x00, 0x80, 0xC1, 0xC0, 0x00, 0x80, + 0xC1, 0xC0, 0x00, 0x80, 0xC1, 0xC0, 0x00, 0x80, 0xC1, 0xC0, 0x00, 0x80, 0xC1, 0xC0, 0x00, 0x80, 0xC1, 0xC0, 0x00, 0x80, 0xC1, + 0xC0, 0x00, 0x00, 0xE3, 0xC1, 0x00, 0x00, 0xFF, 0x61, 0x00, 0x00, 0x1E, 0x7F, 0x00, 0x00, 0x00, 0x1E, // 66 + 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0x07, 0x00, 0x00, 0xFC, 0x1F, 0x00, 0x00, 0x1E, 0x3C, 0x00, 0x00, 0x07, 0x70, 0x00, 0x00, + 0x03, 0x60, 0x00, 0x80, 0x01, 0xC0, 0x00, 0x80, 0x01, 0xC0, 0x00, 0x80, 0x01, 0xC0, 0x00, 0x80, 0x01, 0xC0, 0x00, 0x80, 0x01, + 0xC0, 0x00, 0x80, 0x01, 0xC0, 0x00, 0x00, 0x03, 0x60, 0x00, 0x00, 0x07, 0x70, 0x00, 0x00, 0x0E, 0x3C, 0x00, 0x00, 0x08, + 0x0C, // 67 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xFF, 0xFF, 0x00, 0x80, 0xFF, 0xFF, 0x00, 0x80, 0x01, 0xC0, 0x00, 0x80, + 0x01, 0xC0, 0x00, 0x80, 0x01, 0xC0, 0x00, 0x80, 0x01, 0xC0, 0x00, 0x80, 0x01, 0xC0, 0x00, 0x80, 0x01, 0xC0, 0x00, 0x80, 0x01, + 0xC0, 0x00, 0x80, 0x03, 0x60, 0x00, 0x00, 0x07, 0x70, 0x00, 0x00, 0x0E, 0x38, 0x00, 0x00, 0xFC, 0x1F, 0x00, 0x00, 0xF0, + 0x07, // 68 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xFF, 0xFF, 0x00, 0x80, 0xFF, 0xFF, 0x00, 0x80, 0xC1, 0xC0, 0x00, 0x80, + 0xC1, 0xC0, 0x00, 0x80, 0xC1, 0xC0, 0x00, 0x80, 0xC1, 0xC0, 0x00, 0x80, 0xC1, 0xC0, 0x00, 0x80, 0xC1, 0xC0, 0x00, 0x80, 0xC1, + 0xC0, 0x00, 0x80, 0xC1, 0xC0, 0x00, 0x80, 0xC1, 0xC0, 0x00, 0x80, 0xC1, 0xC0, 0x00, 0x80, 0x01, 0xC0, // 69 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xFF, 0xFF, 0x00, 0x80, 0xFF, 0xFF, 0x00, 0x80, 0xC1, 0x00, 0x00, 0x80, + 0xC1, 0x00, 0x00, 0x80, 0xC1, 0x00, 0x00, 0x80, 0xC1, 0x00, 0x00, 0x80, 0xC1, 0x00, 0x00, 0x80, 0xC1, 0x00, 0x00, 0x80, 0xC1, + 0x00, 0x00, 0x80, 0xC1, 0x00, 0x00, 0x80, 0xC1, 0x00, 0x00, 0x80, 0x01, // 70 + 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0x07, 0x00, 0x00, 0xFC, 0x1F, 0x00, 0x00, 0x1E, 0x3C, 0x00, 0x00, 0x07, 0x70, 0x00, 0x00, + 0x03, 0x60, 0x00, 0x80, 0x03, 0x60, 0x00, 0x80, 0x01, 0xC0, 0x00, 0x80, 0x01, 0xC0, 0x00, 0x80, 0x01, 0xC0, 0x00, 0x80, 0x81, + 0xC1, 0x00, 0x80, 0x81, 0xC1, 0x00, 0x80, 0x83, 0xC1, 0x00, 0x00, 0x83, 0x61, 0x00, 0x00, 0x87, 0x61, 0x00, 0x00, 0x8E, 0x3F, + 0x00, 0x00, 0x88, 0x3F, // 71 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xFF, 0xFF, 0x00, 0x80, 0xFF, 0xFF, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, + 0xC0, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0xC0, + 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x80, 0xFF, 0xFF, 0x00, 0x80, 0xFF, 0xFF, // 72 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xFF, 0xFF, 0x00, 0x80, 0xFF, 0xFF, // 73 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0x78, 0x00, 0x00, 0x00, 0xE0, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, + 0x00, 0xC0, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0xE0, 0x00, 0x80, 0xFF, 0x7F, 0x00, 0x80, 0xFF, 0x3F, // 74 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xFF, 0xFF, 0x00, 0x80, 0xFF, 0xFF, 0x00, 0x00, 0x80, 0x03, 0x00, 0x00, + 0xC0, 0x01, 0x00, 0x00, 0xE0, 0x00, 0x00, 0x00, 0xF0, 0x01, 0x00, 0x00, 0xF8, 0x03, 0x00, 0x00, 0x9C, 0x07, 0x00, 0x00, 0x0E, + 0x1E, 0x00, 0x00, 0x07, 0x3C, 0x00, 0x80, 0x03, 0x78, 0x00, 0x80, 0x01, 0xE0, 0x00, 0x80, 0x00, 0xC0, 0x00, 0x00, 0x00, + 0x80, // 75 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xFF, 0xFF, 0x00, 0x80, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, + 0x00, 0xC0, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, + 0xC0, 0x00, 0x00, 0x00, 0xC0, // 76 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xFF, 0xFF, 0x00, 0x80, 0xFF, 0xFF, 0x00, 0x80, 0x07, 0x00, 0x00, 0x00, + 0x3F, 0x00, 0x00, 0x00, 0xF8, 0x03, 0x00, 0x00, 0x80, 0x1F, 0x00, 0x00, 0x00, 0xFC, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, + 0xFC, 0x00, 0x00, 0x80, 0x1F, 0x00, 0x00, 0xF8, 0x03, 0x00, 0x00, 0x3F, 0x00, 0x00, 0x80, 0x07, 0x00, 0x00, 0x80, 0xFF, 0xFF, + 0x00, 0x80, 0xFF, 0xFF, // 77 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xFF, 0xFF, 0x00, 0x80, 0xFF, 0xFF, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x0E, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0xF0, 0x00, 0x00, 0x00, 0xC0, 0x01, 0x00, 0x00, 0x80, 0x07, 0x00, 0x00, 0x00, + 0x0E, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0x70, 0x00, 0x80, 0xFF, 0xFF, 0x00, 0x80, 0xFF, 0xFF, // 78 + 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0x07, 0x00, 0x00, 0xFC, 0x1F, 0x00, 0x00, 0x1E, 0x3C, 0x00, 0x00, 0x07, 0x70, 0x00, 0x00, + 0x03, 0x60, 0x00, 0x80, 0x03, 0xE0, 0x00, 0x80, 0x01, 0xC0, 0x00, 0x80, 0x01, 0xC0, 0x00, 0x80, 0x01, 0xC0, 0x00, 0x80, 0x01, + 0xC0, 0x00, 0x80, 0x01, 0xC0, 0x00, 0x80, 0x03, 0xE0, 0x00, 0x00, 0x03, 0x60, 0x00, 0x00, 0x07, 0x70, 0x00, 0x00, 0x1E, 0x3C, + 0x00, 0x00, 0xFC, 0x1F, 0x00, 0x00, 0xF0, 0x07, // 79 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xFF, 0xFF, 0x00, 0x80, 0xFF, 0xFF, 0x00, 0x80, 0x81, 0x01, 0x00, 0x80, + 0x81, 0x01, 0x00, 0x80, 0x81, 0x01, 0x00, 0x80, 0x81, 0x01, 0x00, 0x80, 0x81, 0x01, 0x00, 0x80, 0x81, 0x01, 0x00, 0x80, 0x81, + 0x01, 0x00, 0x80, 0x81, 0x01, 0x00, 0x00, 0xC3, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x3C, // 80 + 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0x07, 0x00, 0x00, 0xFC, 0x1F, 0x00, 0x00, 0x1E, 0x3C, 0x00, 0x00, 0x07, 0x30, 0x00, 0x00, + 0x03, 0x60, 0x00, 0x80, 0x03, 0x60, 0x00, 0x80, 0x01, 0xC0, 0x00, 0x80, 0x01, 0xC0, 0x00, 0x80, 0x01, 0xC0, 0x00, 0x80, 0x01, + 0xD8, 0x00, 0x80, 0x01, 0xD8, 0x00, 0x80, 0x03, 0xF0, 0x00, 0x00, 0x03, 0x70, 0x00, 0x00, 0x07, 0x70, 0x00, 0x00, 0x1E, 0xFC, + 0x00, 0x00, 0xFC, 0xDF, 0x01, 0x00, 0xF0, 0x87, 0x01, // 81 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xFF, 0xFF, 0x00, 0x80, 0xFF, 0xFF, 0x00, 0x80, 0xC1, 0x00, 0x00, 0x80, + 0xC1, 0x00, 0x00, 0x80, 0xC1, 0x00, 0x00, 0x80, 0xC1, 0x00, 0x00, 0x80, 0xC1, 0x01, 0x00, 0x80, 0xC1, 0x03, 0x00, 0x80, 0xC1, + 0x0F, 0x00, 0x80, 0xC1, 0x1E, 0x00, 0x80, 0x63, 0x7C, 0x00, 0x00, 0x7F, 0xF0, 0x00, 0x00, 0x3E, 0xC0, 0x00, 0x00, 0x00, + 0x80, // 82 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x1C, 0x3C, 0x00, 0x00, 0x7F, 0x70, 0x00, 0x00, 0x63, 0x60, 0x00, 0x80, + 0xE1, 0xE0, 0x00, 0x80, 0xC1, 0xC0, 0x00, 0x80, 0xC1, 0xC0, 0x00, 0x80, 0xC1, 0xC0, 0x00, 0x80, 0xC1, 0xC0, 0x00, 0x80, 0xC1, + 0xC1, 0x00, 0x80, 0x83, 0xE1, 0x00, 0x00, 0x87, 0x63, 0x00, 0x00, 0x0E, 0x3F, 0x00, 0x00, 0x0C, 0x1E, // 83 + 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, + 0x01, 0x00, 0x00, 0x80, 0xFF, 0xFF, 0x00, 0x80, 0xFF, 0xFF, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, + 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, // 84 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xFF, 0x0F, 0x00, 0x80, 0xFF, 0x3F, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, + 0x00, 0xE0, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, + 0xC0, 0x00, 0x00, 0x00, 0xE0, 0x00, 0x00, 0x00, 0x70, 0x00, 0x80, 0xFF, 0x3F, 0x00, 0x80, 0xFF, 0x0F, // 85 + 0x80, 0x00, 0x00, 0x00, 0x80, 0x07, 0x00, 0x00, 0x00, 0x3F, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x00, 0x00, 0xE0, 0x07, 0x00, 0x00, + 0x00, 0x3F, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x00, 0x00, 0xE0, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x00, 0x00, 0x3F, 0x00, 0x00, 0xE0, + 0x07, 0x00, 0x00, 0xF8, 0x00, 0x00, 0x00, 0x3F, 0x00, 0x00, 0x80, 0x07, 0x00, 0x00, 0x80, // 86 + 0x80, 0x01, 0x00, 0x00, 0x80, 0x1F, 0x00, 0x00, 0x00, 0xFE, 0x03, 0x00, 0x00, 0xE0, 0x3F, 0x00, 0x00, 0x00, 0xFE, 0x00, 0x00, + 0x00, 0xC0, 0x00, 0x00, 0x00, 0xFC, 0x00, 0x00, 0x80, 0x3F, 0x00, 0x00, 0xF0, 0x07, 0x00, 0x00, 0x7E, 0x00, 0x00, 0x80, 0x0F, + 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x0F, 0x00, 0x00, 0x00, 0x7E, 0x00, 0x00, 0x00, 0xF0, 0x07, 0x00, 0x00, 0x80, 0x3F, + 0x00, 0x00, 0x00, 0xFC, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0xFE, 0x00, 0x00, 0xE0, 0x3F, 0x00, 0x00, 0xFE, 0x03, 0x00, + 0x80, 0x1F, 0x00, 0x00, 0x80, 0x01, // 87 + 0x00, 0x00, 0x80, 0x00, 0x80, 0x00, 0xC0, 0x00, 0x80, 0x01, 0xF0, 0x00, 0x80, 0x07, 0x78, 0x00, 0x00, 0x0F, 0x1E, 0x00, 0x00, + 0x3C, 0x0F, 0x00, 0x00, 0xF8, 0x07, 0x00, 0x00, 0xE0, 0x01, 0x00, 0x00, 0xF8, 0x07, 0x00, 0x00, 0x1E, 0x0F, 0x00, 0x00, 0x0F, + 0x1C, 0x00, 0x80, 0x07, 0x78, 0x00, 0x80, 0x01, 0xF0, 0x00, 0x80, 0x00, 0xC0, 0x00, 0x00, 0x00, 0x80, // 88 + 0x80, 0x00, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x00, + 0x78, 0x00, 0x00, 0x00, 0xF0, 0x00, 0x00, 0x00, 0xC0, 0xFF, 0x00, 0x00, 0xC0, 0xFF, 0x00, 0x00, 0xF0, 0x00, 0x00, 0x00, 0x78, + 0x00, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x80, 0x03, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, // 89 + 0x00, 0x00, 0xC0, 0x00, 0x80, 0x01, 0xE0, 0x00, 0x80, 0x01, 0xF0, 0x00, 0x80, 0x01, 0xDC, 0x00, 0x80, 0x01, 0xCE, 0x00, 0x80, + 0x01, 0xC7, 0x00, 0x80, 0x81, 0xC3, 0x00, 0x80, 0xE1, 0xC0, 0x00, 0x80, 0x71, 0xC0, 0x00, 0x80, 0x39, 0xC0, 0x00, 0x80, 0x1D, + 0xC0, 0x00, 0x80, 0x07, 0xC0, 0x00, 0x80, 0x03, 0xC0, 0x00, 0x80, 0x01, 0xC0, // 90 + 0x00, 0x00, 0x00, 0x00, 0x80, 0xFF, 0xFF, 0x1F, 0x80, 0xFF, 0xFF, 0x1F, 0x80, 0x01, 0x00, 0x18, 0x80, 0x01, 0x00, 0x18, // 91 + 0x80, 0x01, 0x00, 0x00, 0x80, 0x0F, 0x00, 0x00, 0x00, 0xFE, 0x00, 0x00, 0x00, 0xF0, 0x07, 0x00, 0x00, 0x80, 0x3F, 0x00, 0x00, + 0x00, 0xF8, 0x00, 0x00, 0x00, 0xC0, // 92 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x01, 0x00, 0x18, 0x80, 0x01, 0x00, 0x18, 0x80, 0xFF, 0xFF, 0x1F, 0x80, + 0xFF, 0xFF, 0x1F, // 93 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0xE0, 0x00, 0x00, 0x00, 0x7C, 0x00, 0x00, 0x00, 0x1F, 0x00, 0x00, 0x80, + 0x03, 0x00, 0x00, 0x80, 0x03, 0x00, 0x00, 0x00, 0x1F, 0x00, 0x00, 0x00, 0x7C, 0x00, 0x00, 0x00, 0xE0, 0x00, 0x00, 0x00, + 0x80, // 94 + 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x18, 0x00, + 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, + 0x00, 0x18, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x18, // 95 + 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x03, 0x00, 0x00, 0x00, 0x02, // 96 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x38, 0x00, 0x00, 0x70, 0x7C, 0x00, 0x00, 0x30, 0xE6, 0x00, 0x00, 0x18, 0xC6, 0x00, 0x00, + 0x18, 0xC6, 0x00, 0x00, 0x18, 0xC3, 0x00, 0x00, 0x18, 0x63, 0x00, 0x00, 0x38, 0x63, 0x00, 0x00, 0xF0, 0x7F, 0x00, 0x00, 0xE0, + 0xFF, 0x00, 0x00, 0x00, 0x80, // 97 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xFF, 0xFF, 0x00, 0x80, 0xFF, 0xFF, 0x00, 0x00, 0x60, 0x30, 0x00, 0x00, + 0x30, 0x60, 0x00, 0x00, 0x18, 0xC0, 0x00, 0x00, 0x18, 0xC0, 0x00, 0x00, 0x18, 0xC0, 0x00, 0x00, 0x38, 0xE0, 0x00, 0x00, 0x70, + 0x70, 0x00, 0x00, 0xE0, 0x3F, 0x00, 0x00, 0x80, 0x0F, // 98 + 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0x1F, 0x00, 0x00, 0xE0, 0x3F, 0x00, 0x00, 0x70, 0x70, 0x00, 0x00, 0x38, 0xE0, 0x00, 0x00, + 0x18, 0xC0, 0x00, 0x00, 0x18, 0xC0, 0x00, 0x00, 0x18, 0xC0, 0x00, 0x00, 0x38, 0xE0, 0x00, 0x00, 0x70, 0x70, 0x00, 0x00, 0x60, + 0x30, // 99 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x0F, 0x00, 0x00, 0xE0, 0x3F, 0x00, 0x00, 0x70, 0x70, 0x00, 0x00, 0x38, 0xE0, 0x00, 0x00, + 0x18, 0xC0, 0x00, 0x00, 0x18, 0xC0, 0x00, 0x00, 0x18, 0xC0, 0x00, 0x00, 0x30, 0x60, 0x00, 0x00, 0x70, 0x30, 0x00, 0x80, 0xFF, + 0xFF, 0x00, 0x80, 0xFF, 0xFF, // 100 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x1F, 0x00, 0x00, 0xE0, 0x3F, 0x00, 0x00, 0x70, 0x73, 0x00, 0x00, 0x38, 0xE3, 0x00, 0x00, + 0x18, 0xC3, 0x00, 0x00, 0x18, 0xC3, 0x00, 0x00, 0x18, 0xC3, 0x00, 0x00, 0x38, 0xE3, 0x00, 0x00, 0x70, 0x63, 0x00, 0x00, 0xE0, + 0x33, 0x00, 0x00, 0xC0, 0x13, // 101 + 0x00, 0x18, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x80, 0xFF, 0xFF, 0x00, 0x80, 0x19, 0x00, 0x00, 0x80, + 0x19, 0x00, 0x00, 0x80, 0x19, // 102 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x0F, 0x06, 0x00, 0xE0, 0x3F, 0x0E, 0x00, 0x70, 0x70, 0x1C, 0x00, 0x38, 0xE0, 0x18, 0x00, + 0x18, 0xC0, 0x18, 0x00, 0x18, 0xC0, 0x18, 0x00, 0x18, 0xC0, 0x18, 0x00, 0x30, 0x60, 0x1C, 0x00, 0x60, 0x30, 0x0E, 0x00, 0xF8, + 0xFF, 0x07, 0x00, 0xF8, 0xFF, 0x03, // 103 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xFF, 0xFF, 0x00, 0x80, 0xFF, 0xFF, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, + 0x30, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0xF0, + 0xFF, 0x00, 0x00, 0xE0, 0xFF, // 104 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xF9, 0xFF, 0x00, 0x80, 0xF9, 0xFF, // 105 + 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x18, 0x80, 0xF9, 0xFF, 0x1F, 0x80, 0xF9, 0xFF, 0x0F, // 106 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xFF, 0xFF, 0x00, 0x80, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, + 0x80, 0x03, 0x00, 0x00, 0xC0, 0x07, 0x00, 0x00, 0x60, 0x1E, 0x00, 0x00, 0x30, 0x38, 0x00, 0x00, 0x18, 0xF0, 0x00, 0x00, 0x08, + 0xC0, 0x00, 0x00, 0x00, 0x80, // 107 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xFF, 0xFF, 0x00, 0x80, 0xFF, 0xFF, // 108 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF8, 0xFF, 0x00, 0x00, 0xF8, 0xFF, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0xF0, 0xFF, 0x00, 0x00, 0xE0, + 0xFF, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x38, 0x00, + 0x00, 0x00, 0xF0, 0xFF, 0x00, 0x00, 0xE0, 0xFF, // 109 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF8, 0xFF, 0x00, 0x00, 0xF8, 0xFF, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, + 0x30, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0xF0, + 0xFF, 0x00, 0x00, 0xE0, 0xFF, // 110 + 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0x1F, 0x00, 0x00, 0xE0, 0x3F, 0x00, 0x00, 0x70, 0x70, 0x00, 0x00, 0x38, 0xE0, 0x00, 0x00, + 0x18, 0xC0, 0x00, 0x00, 0x18, 0xC0, 0x00, 0x00, 0x18, 0xC0, 0x00, 0x00, 0x38, 0xE0, 0x00, 0x00, 0x70, 0x70, 0x00, 0x00, 0xE0, + 0x3F, 0x00, 0x00, 0xC0, 0x1F, // 111 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF8, 0xFF, 0x1F, 0x00, 0xF8, 0xFF, 0x1F, 0x00, 0x60, 0x30, 0x00, 0x00, + 0x30, 0x60, 0x00, 0x00, 0x18, 0xC0, 0x00, 0x00, 0x18, 0xC0, 0x00, 0x00, 0x18, 0xC0, 0x00, 0x00, 0x38, 0xE0, 0x00, 0x00, 0x70, + 0x70, 0x00, 0x00, 0xE0, 0x3F, 0x00, 0x00, 0xC0, 0x0F, // 112 + 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0x0F, 0x00, 0x00, 0xE0, 0x3F, 0x00, 0x00, 0x70, 0x70, 0x00, 0x00, 0x38, 0xE0, 0x00, 0x00, + 0x18, 0xC0, 0x00, 0x00, 0x18, 0xC0, 0x00, 0x00, 0x18, 0xC0, 0x00, 0x00, 0x30, 0x60, 0x00, 0x00, 0x60, 0x30, 0x00, 0x00, 0xF8, + 0xFF, 0x1F, 0x00, 0xF8, 0xFF, 0x1F, // 113 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF8, 0xFF, 0x00, 0x00, 0xF8, 0xFF, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, + 0x18, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x18, // 114 + 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0x30, 0x00, 0x00, 0xF0, 0x71, 0x00, 0x00, 0xB8, 0xE3, 0x00, 0x00, 0x18, 0xC3, 0x00, 0x00, + 0x18, 0xC3, 0x00, 0x00, 0x18, 0xC7, 0x00, 0x00, 0x18, 0xC7, 0x00, 0x00, 0x38, 0xE6, 0x00, 0x00, 0x70, 0x7E, 0x00, 0x00, 0x60, + 0x3C, // 115 + 0x00, 0x18, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0xFF, 0x7F, 0x00, 0x80, 0xFF, 0xFF, 0x00, 0x00, 0x18, 0xC0, 0x00, 0x00, + 0x18, 0xC0, 0x00, 0x00, 0x18, 0xC0, // 116 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF8, 0x3F, 0x00, 0x00, 0xF8, 0x7F, 0x00, 0x00, 0x00, 0xE0, 0x00, 0x00, + 0x00, 0xC0, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0xF8, + 0xFF, 0x00, 0x00, 0xF8, 0xFF, // 117 + 0x00, 0x18, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x00, 0x00, 0xE0, 0x03, 0x00, 0x00, 0x00, 0x1F, 0x00, 0x00, 0x00, 0x7C, 0x00, 0x00, + 0x00, 0xE0, 0x00, 0x00, 0x00, 0x7C, 0x00, 0x00, 0x00, 0x1F, 0x00, 0x00, 0xE0, 0x03, 0x00, 0x00, 0xF8, 0x00, 0x00, 0x00, + 0x18, // 118 + 0x00, 0x38, 0x00, 0x00, 0x00, 0xF8, 0x01, 0x00, 0x00, 0xC0, 0x0F, 0x00, 0x00, 0x00, 0x7E, 0x00, 0x00, 0x00, 0xE0, 0x00, 0x00, + 0x00, 0x7E, 0x00, 0x00, 0x80, 0x0F, 0x00, 0x00, 0xF0, 0x01, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0xF0, 0x01, 0x00, 0x00, 0x80, + 0x0F, 0x00, 0x00, 0x00, 0x7E, 0x00, 0x00, 0x00, 0xE0, 0x00, 0x00, 0x00, 0x7E, 0x00, 0x00, 0xC0, 0x0F, 0x00, 0x00, 0xF8, 0x01, + 0x00, 0x00, 0x38, // 119 + 0x00, 0x08, 0x80, 0x00, 0x00, 0x18, 0xC0, 0x00, 0x00, 0x78, 0xF0, 0x00, 0x00, 0xE0, 0x38, 0x00, 0x00, 0xC0, 0x1F, 0x00, 0x00, + 0x00, 0x07, 0x00, 0x00, 0x80, 0x1F, 0x00, 0x00, 0xE0, 0x38, 0x00, 0x00, 0x70, 0xF0, 0x00, 0x00, 0x18, 0xC0, 0x00, 0x00, 0x08, + 0x80, // 120 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0xF8, 0x01, 0x18, 0x00, 0xC0, 0x07, 0x18, 0x00, 0x00, 0x3E, 0x1C, 0x00, + 0x00, 0xF8, 0x0F, 0x00, 0x00, 0xF0, 0x03, 0x00, 0x00, 0x7F, 0x00, 0x00, 0xE0, 0x0F, 0x00, 0x00, 0xF8, 0x00, 0x00, 0x00, + 0x18, // 121 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x18, 0xF0, 0x00, 0x00, 0x18, 0xF8, 0x00, 0x00, 0x18, 0xDC, 0x00, 0x00, + 0x18, 0xCF, 0x00, 0x00, 0x98, 0xC3, 0x00, 0x00, 0xD8, 0xC1, 0x00, 0x00, 0xF8, 0xC0, 0x00, 0x00, 0x78, 0xC0, 0x00, 0x00, 0x18, + 0xC0, // 122 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0xFF, 0xF9, 0x0F, 0x80, 0xFF, 0xF0, 0x1F, 0x80, + 0x01, 0x00, 0x18, 0x80, 0x01, 0x00, 0x18, // 123 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xFF, 0xFF, 0x3F, 0x80, 0xFF, 0xFF, 0x3F, // 124 + 0x00, 0x00, 0x00, 0x00, 0x80, 0x01, 0x00, 0x18, 0x80, 0x01, 0x00, 0x18, 0x80, 0xFF, 0xF0, 0x1F, 0x00, 0xFF, 0xFD, 0x0F, 0x00, + 0x00, 0x0F, 0x00, 0x00, 0x00, 0x06, // 125 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0xC0, 0x01, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, + 0xC0, 0x00, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x80, 0x03, 0x00, 0x00, 0x80, 0x01, // 126 + 0x00, 0x00, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, + 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0xFF, 0xFF, 0x00, 0x80, 0xFF, 0xFF, 0x00, 0x80, 0xC1, 0x00, 0x00, 0x80, 0xC1, + 0x00, 0x00, 0x80, 0x61, 0x00, 0x00, 0x80, 0x61, 0x60, 0x00, 0x80, 0x61, 0xC0, 0x00, 0x80, 0x61, 0xC0, 0x00, 0x00, 0x60, 0xC0, + 0x00, 0x00, 0xE0, 0xE0, 0x00, 0x00, 0xC0, 0x71, 0x00, 0x00, 0x80, 0x3F, 0x00, 0x00, 0x00, 0x1F, // 1026 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xFF, 0xFF, 0x00, 0x80, 0xFF, 0xFF, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, + 0x01, 0x00, 0x00, 0xA0, 0x01, 0x00, 0x00, 0xB8, 0x01, 0x00, 0x00, 0x98, 0x01, 0x00, 0x00, 0x88, 0x01, 0x00, 0x00, 0x80, 0x01, + 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, // 1027 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0x0C, 0x00, 0x00, 0xC0, 0x07, // 8218 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF8, 0xFF, 0x00, 0x00, 0xF8, 0xFF, 0x00, 0x00, 0x1A, 0x00, 0x00, 0x80, + 0x1B, 0x00, 0x00, 0x80, 0x19, 0x00, 0x00, 0x80, 0x18, 0x00, 0x00, 0x00, 0x18, // 1107 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0x0C, 0x00, 0x00, 0xC0, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xC0, 0x0C, 0x00, 0x00, 0xC0, 0x07, // 8222 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xC0, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0xC0, // 8230 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, + 0x30, 0x00, 0x00, 0x80, 0xFF, 0xFF, 0x0F, 0x80, 0xFF, 0xFF, 0x0F, 0x00, 0x30, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x30, + 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x30, // 8224 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0xC0, 0x00, 0x00, 0x18, 0xC0, 0x00, 0x00, 0x18, 0xC0, 0x00, 0x00, 0x18, 0xC0, 0x00, 0x00, + 0x18, 0xC0, 0x00, 0x80, 0xFF, 0xFF, 0x0F, 0x80, 0xFF, 0xFF, 0x0F, 0x00, 0x18, 0xC0, 0x00, 0x00, 0x18, 0xC0, 0x00, 0x00, 0x18, + 0xC0, 0x00, 0x00, 0x18, 0xC0, 0x00, 0x00, 0x18, 0xC0, // 8225 + 0x00, 0x30, 0x03, 0x00, 0x00, 0xF8, 0x07, 0x00, 0x00, 0xFE, 0x1F, 0x00, 0x00, 0x36, 0x3B, 0x00, 0x00, 0x33, 0x73, 0x00, 0x00, + 0x33, 0x63, 0x00, 0x80, 0x31, 0xC3, 0x00, 0x80, 0x31, 0xC3, 0x00, 0x80, 0x31, 0xC3, 0x00, 0x80, 0x31, 0xC3, 0x00, 0x80, 0x31, + 0xC0, 0x00, 0x00, 0x03, 0x60, // 8364 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x00, 0x7F, 0x00, 0x00, 0x80, 0xC1, 0x00, 0x00, 0x80, 0x80, 0x00, 0x00, 0x80, + 0xC1, 0xE0, 0x00, 0x00, 0x7F, 0xF8, 0x00, 0x00, 0x3E, 0x3E, 0x00, 0x00, 0xC0, 0x07, 0x00, 0x00, 0xF0, 0x01, 0x00, 0x00, 0x3E, + 0x3E, 0x00, 0x80, 0x0F, 0x7F, 0x00, 0x80, 0x81, 0xC1, 0x00, 0x00, 0x80, 0x80, 0x00, 0x00, 0x80, 0xC1, 0x00, 0x00, 0x00, 0x7F, + 0x00, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x00, 0x7F, 0x00, 0x00, 0x80, 0xC1, 0x00, + 0x00, 0x80, 0x80, 0x00, 0x00, 0x80, 0xC1, 0x00, 0x00, 0x00, 0x7F, 0x00, 0x00, 0x00, 0x3E, // 8240 + 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0xE0, 0x00, 0x80, 0xFF, 0x7F, 0x00, 0x80, 0xFF, 0x3F, 0x00, 0x80, + 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, + 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0xFF, 0xFF, 0x00, 0x80, 0xFF, 0xFF, 0x00, 0x00, 0xC0, 0xC0, 0x00, 0x00, 0xC0, 0xC0, + 0x00, 0x00, 0xC0, 0xC0, 0x00, 0x00, 0xC0, 0xC0, 0x00, 0x00, 0xC0, 0xC0, 0x00, 0x00, 0xC0, 0xC0, 0x00, 0x00, 0xC0, 0xC0, 0x00, + 0x00, 0x80, 0x61, 0x00, 0x00, 0x80, 0x7F, 0x00, 0x00, 0x00, 0x1E, // 1033 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x80, 0x0F, 0x00, 0x00, 0xE0, 0x3D, 0x00, 0x00, 0x70, 0x70, 0x00, 0x00, + 0x10, 0x40, // 8249 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xFF, 0xFF, 0x00, 0x80, 0xFF, 0xFF, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, + 0xC0, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0xC0, + 0x00, 0x00, 0x80, 0xFF, 0xFF, 0x00, 0x80, 0xFF, 0xFF, 0x00, 0x00, 0xC0, 0xC0, 0x00, 0x00, 0xC0, 0xC0, 0x00, 0x00, 0xC0, 0xC0, + 0x00, 0x00, 0xC0, 0xC0, 0x00, 0x00, 0xC0, 0xC0, 0x00, 0x00, 0xC0, 0xC0, 0x00, 0x00, 0xC0, 0xC0, 0x00, 0x00, 0x80, 0x61, 0x00, + 0x00, 0x80, 0x7F, 0x00, 0x00, 0x00, 0x1E, // 1034 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xFF, 0xFF, 0x00, 0x80, 0xFF, 0xFF, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, + 0xC0, 0x00, 0x00, 0x00, 0xC0, 0x01, 0x00, 0x20, 0x60, 0x03, 0x00, 0x38, 0x78, 0x07, 0x00, 0x18, 0x1E, 0x0E, 0x00, 0x08, 0x07, + 0x38, 0x00, 0x80, 0x01, 0xF0, 0x00, 0x80, 0x01, 0xC0, 0x00, 0x80, 0x01, 0x80, // 1036 + 0x00, 0x00, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, + 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0xFF, 0xFF, 0x00, 0x80, 0xFF, 0xFF, 0x00, 0x80, 0xC1, 0x00, 0x00, 0x80, 0xC1, + 0x00, 0x00, 0x80, 0x61, 0x00, 0x00, 0x80, 0x61, 0x00, 0x00, 0x80, 0x61, 0x00, 0x00, 0x80, 0x61, 0x00, 0x00, 0x00, 0x60, 0x00, + 0x00, 0x00, 0xE0, 0x00, 0x00, 0x00, 0xC0, 0x01, 0x00, 0x00, 0xC0, 0xFF, 0x00, 0x00, 0x00, 0xFF, // 1035 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xFF, 0xFF, 0x00, 0x80, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, + 0x00, 0xC0, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0xC0, 0x1F, 0x00, 0x00, 0xC0, 0x1F, 0x00, 0x00, + 0xC0, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x80, 0xFF, 0xFF, 0x00, 0x80, 0xFF, 0xFF, // 1039 + 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x80, 0xFF, 0xFF, 0x00, 0x80, 0xFF, 0xFF, 0x00, 0x00, 0x73, 0x00, 0x00, 0x00, + 0x33, 0x00, 0x00, 0x00, 0x1B, 0x00, 0x00, 0x00, 0x1B, 0x00, 0x18, 0x00, 0x18, 0x00, 0x18, 0x00, 0x38, 0x00, 0x18, 0x00, 0xF0, + 0xFF, 0x1F, 0x00, 0xE0, 0xFF, 0x0F, // 1106 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x1F, 0x00, 0x00, 0x80, 0x19, // 8216 + 0x00, 0x00, 0x00, 0x00, 0x80, 0x19, 0x00, 0x00, 0x80, 0x0F, // 8217 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x1F, 0x00, 0x00, 0x80, 0x19, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x1F, 0x00, 0x00, 0x80, 0x19, // 8220 + 0x00, 0x00, 0x00, 0x00, 0x80, 0x19, 0x00, 0x00, 0x80, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, + 0x19, 0x00, 0x00, 0x80, 0x0F, // 8221 + 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0x03, 0x00, 0x00, 0xE0, 0x07, 0x00, 0x00, 0xE0, 0x07, 0x00, 0x00, 0xE0, 0x07, 0x00, 0x00, + 0xE0, 0x07, 0x00, 0x00, 0xC0, 0x03, // 8226 + 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, + 0x00, 0x06, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x06, // 8211 + 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, + 0x00, 0x06, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x06, + 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x06, 0x00, + 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x06, // 8212 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, + 0x01, 0x00, 0x00, 0x80, 0xFF, 0x01, 0x00, 0x80, 0xFF, 0x01, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xFF, 0x01, 0x00, 0x80, 0xFF, 0x01, 0x00, 0x80, 0x03, 0x00, 0x00, 0x00, 0x1E, 0x00, + 0x00, 0x00, 0xF0, 0x00, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0xF0, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x80, 0x03, 0x00, 0x00, + 0x80, 0xFF, 0x01, 0x00, 0x80, 0xFF, 0x01, // 8482 + 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0xF8, 0xFF, 0x00, 0x00, 0xF8, 0x7F, 0x00, 0x00, + 0x18, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x18, + 0x00, 0x00, 0x00, 0xF8, 0xFF, 0x00, 0x00, 0xF8, 0xFF, 0x00, 0x00, 0x00, 0xC3, 0x00, 0x00, 0x00, 0xC3, 0x00, 0x00, 0x00, 0xC3, + 0x00, 0x00, 0x00, 0xC3, 0x00, 0x00, 0x00, 0xC3, 0x00, 0x00, 0x00, 0xE7, 0x00, 0x00, 0x00, 0x7E, 0x00, 0x00, 0x00, + 0x3C, // 1113 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x40, 0x00, 0x00, 0x70, 0x70, 0x00, 0x00, 0xE0, 0x3D, 0x00, 0x00, + 0x80, 0x0F, 0x00, 0x00, 0x00, 0x02, // 8250 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF8, 0xFF, 0x00, 0x00, 0xF8, 0xFF, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, + 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0xF8, 0xFF, 0x00, 0x00, 0xF8, + 0xFF, 0x00, 0x00, 0x00, 0xC3, 0x00, 0x00, 0x00, 0xC3, 0x00, 0x00, 0x00, 0xC3, 0x00, 0x00, 0x00, 0xC3, 0x00, 0x00, 0x00, 0xC3, + 0x00, 0x00, 0x00, 0xE7, 0x00, 0x00, 0x00, 0x7E, 0x00, 0x00, 0x00, 0x3C, // 1114 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF8, 0xFF, 0x00, 0x00, 0xF8, 0xFF, 0x00, 0x00, 0x02, 0x02, 0x00, 0x80, + 0x03, 0x07, 0x00, 0x80, 0xC1, 0x0D, 0x00, 0x80, 0xF0, 0x38, 0x00, 0x00, 0x18, 0x70, 0x00, 0x00, 0x18, 0xE0, 0x00, 0x00, 0x00, + 0x80, // 1116 + 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x80, 0xFF, 0xFF, 0x00, 0x80, 0xFF, 0xFF, 0x00, 0x00, 0x73, 0x00, 0x00, 0x00, + 0x33, 0x00, 0x00, 0x00, 0x1B, 0x00, 0x00, 0x00, 0x1B, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0xF0, + 0xFF, 0x00, 0x00, 0xE0, 0xFF, // 1115 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF8, 0xFF, 0x00, 0x00, 0xF8, 0xFF, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, + 0x00, 0xC0, 0x00, 0x00, 0x00, 0xC0, 0x1F, 0x00, 0x00, 0xC0, 0x1F, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0xF8, + 0xFF, 0x00, 0x00, 0xF8, 0xFF, // 1119 + 0x00, 0x00, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x07, 0x00, 0x00, 0x00, 0x1E, 0xC0, 0x00, 0x18, 0x78, 0xC0, 0x00, 0x30, + 0xE0, 0xC1, 0x00, 0x20, 0x80, 0x77, 0x00, 0x20, 0x00, 0x3E, 0x00, 0x20, 0x80, 0x07, 0x00, 0x30, 0xE0, 0x01, 0x00, 0x18, 0x78, + 0x00, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x80, 0x07, 0x00, 0x00, 0x80, 0x01, // 1038 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0xF8, 0x01, 0x18, 0x80, 0xC1, 0x07, 0x18, 0x00, 0x03, 0x3E, 0x1C, 0x00, + 0x02, 0xF8, 0x0F, 0x00, 0x02, 0xF0, 0x03, 0x00, 0x02, 0x7F, 0x00, 0x00, 0xE3, 0x0F, 0x00, 0x80, 0xF9, 0x00, 0x00, 0x00, + 0x18, // 1118 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0x78, 0x00, 0x00, 0x00, 0xE0, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, + 0x00, 0xC0, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0xE0, 0x00, 0x80, 0xFF, 0x7F, 0x00, 0x80, 0xFF, 0x3F, // 1032 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x10, 0x00, 0x00, 0xDC, 0x3B, 0x00, 0x00, 0xF8, 0x1F, 0x00, 0x00, 0x30, 0x0C, 0x00, 0x00, + 0x18, 0x18, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x30, 0x0C, 0x00, 0x00, 0xF8, + 0x1F, 0x00, 0x00, 0xDC, 0x3B, 0x00, 0x00, 0x08, 0x10, // 164 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xFF, 0xFF, 0x00, 0x80, 0xFF, 0xFF, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, + 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0xFC, 0x01, 0x00, 0x00, 0xFC, + 0x01, // 1168 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xFF, 0xE1, 0x3F, 0x80, 0xFF, 0xE1, 0x3F, // 166 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x03, 0x00, 0x00, 0xCE, 0x07, 0x03, 0x00, 0x7F, 0x0C, 0x0F, 0x80, 0x33, 0x1C, 0x0C, 0x80, + 0x71, 0x18, 0x18, 0x80, 0x61, 0x30, 0x18, 0x80, 0xC1, 0x70, 0x18, 0x80, 0xC3, 0xE1, 0x1C, 0x00, 0x87, 0xD3, 0x0F, 0x00, 0x06, + 0x9F, 0x07, 0x00, 0x00, 0x0E, // 167 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xFF, 0xFF, 0x00, 0x80, 0xFF, 0xFF, 0x00, 0x80, 0xC1, 0xC0, 0x00, 0xB0, + 0xC1, 0xC0, 0x00, 0xB0, 0xC1, 0xC0, 0x00, 0x80, 0xC1, 0xC0, 0x00, 0x80, 0xC1, 0xC0, 0x00, 0xB0, 0xC1, 0xC0, 0x00, 0xB0, 0xC1, + 0xC0, 0x00, 0x80, 0xC1, 0xC0, 0x00, 0x80, 0xC1, 0xC0, 0x00, 0x80, 0xC1, 0xC0, 0x00, 0x80, 0x01, 0xC0, // 1025 + 0x00, 0xE0, 0x03, 0x00, 0x00, 0xF8, 0x0F, 0x00, 0x00, 0x1C, 0x1C, 0x00, 0x00, 0x06, 0x30, 0x00, 0x00, 0xE7, 0x71, 0x00, 0x00, + 0xFB, 0x67, 0x00, 0x80, 0x19, 0xC6, 0x00, 0x80, 0x0D, 0xCC, 0x00, 0x80, 0x0D, 0xCC, 0x00, 0x80, 0x0D, 0xCC, 0x00, 0x80, 0x0D, + 0xCC, 0x00, 0x80, 0x1D, 0xCE, 0x00, 0x00, 0x1B, 0x66, 0x00, 0x00, 0x17, 0x72, 0x00, 0x00, 0x06, 0x30, 0x00, 0x00, 0x1C, 0x1C, + 0x00, 0x00, 0xF8, 0x0F, 0x00, 0x00, 0xE0, 0x03, // 169 + 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0x07, 0x00, 0x00, 0xFC, 0x1F, 0x00, 0x00, 0xCE, 0x3C, 0x00, 0x00, 0xC7, 0x70, 0x00, 0x00, + 0xC3, 0x60, 0x00, 0x80, 0xC1, 0xC0, 0x00, 0x80, 0xC1, 0xC0, 0x00, 0x80, 0xC1, 0xC0, 0x00, 0x80, 0xC1, 0xC0, 0x00, 0x80, 0x01, + 0xC0, 0x00, 0x80, 0x03, 0xE0, 0x00, 0x00, 0x03, 0x60, 0x00, 0x00, 0x0F, 0x78, 0x00, 0x00, 0x1E, 0x3C, 0x00, 0x00, 0x08, + 0x08, // 1028 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x80, 0x0F, 0x00, 0x00, 0xE0, 0x3D, 0x00, 0x00, 0x70, 0x70, 0x00, 0x00, + 0x10, 0x42, 0x00, 0x00, 0x80, 0x0F, 0x00, 0x00, 0xE0, 0x3D, 0x00, 0x00, 0x70, 0x70, 0x00, 0x00, 0x10, 0x40, // 171 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, + 0x30, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0xF0, + 0x07, 0x00, 0x00, 0xF0, 0x07, // 172 + 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, + 0x00, 0x06, 0x00, 0x00, 0x00, 0x06, // 173 + 0x00, 0xE0, 0x03, 0x00, 0x00, 0xF8, 0x0F, 0x00, 0x00, 0x1C, 0x1C, 0x00, 0x00, 0x06, 0x30, 0x00, 0x00, 0x07, 0x70, 0x00, 0x00, + 0xFB, 0x6F, 0x00, 0x80, 0xF9, 0xCF, 0x00, 0x80, 0x99, 0xC1, 0x00, 0x80, 0x99, 0xC1, 0x00, 0x80, 0x99, 0xC3, 0x00, 0x80, 0xF9, + 0xC7, 0x00, 0x80, 0xF1, 0xCC, 0x00, 0x00, 0x03, 0x68, 0x00, 0x00, 0x07, 0x70, 0x00, 0x00, 0x06, 0x30, 0x00, 0x00, 0x1C, 0x1C, + 0x00, 0x00, 0xF8, 0x0F, 0x00, 0x00, 0xE0, 0x03, // 174 + 0x30, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x80, 0xFF, 0xFF, 0x00, 0x80, 0xFF, 0xFF, 0x00, 0x30, 0x00, 0x00, 0x00, + 0x30, // 1031 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x80, 0x20, 0x00, 0x00, 0x80, 0x20, 0x00, 0x00, 0x80, + 0x20, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x0E, // 176 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xC1, 0x00, 0x00, 0x80, 0xC1, 0x00, 0x00, 0x80, 0xC1, 0x00, 0x00, 0x80, 0xC1, 0x00, 0x00, + 0x80, 0xC1, 0x00, 0x00, 0xFC, 0xFF, 0x00, 0x00, 0xFC, 0xFF, 0x00, 0x00, 0x80, 0xC1, 0x00, 0x00, 0x80, 0xC1, 0x00, 0x00, 0x80, + 0xC1, 0x00, 0x00, 0x80, 0xC1, 0x00, 0x00, 0x80, 0xC1, // 177 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xFF, 0xFF, 0x00, 0x80, 0xFF, 0xFF, // 1030 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xF9, 0xFF, 0x00, 0x80, 0xF9, 0xFF, // 1110 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF8, 0xFF, 0x00, 0x00, 0xF8, 0xFF, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, + 0x18, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x80, 0x1F, 0x00, 0x00, 0x80, 0x1F, // 1169 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF8, 0xFF, 0x1F, 0x00, 0xF8, 0xFF, 0x1F, 0x00, 0x00, 0x60, 0x00, 0x00, + 0x00, 0xC0, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0xF8, + 0xFF, 0x00, 0x00, 0xF8, 0xFF, // 181 + 0x00, 0x3C, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x80, 0xFF, 0x01, 0x00, 0x80, 0xFF, 0x01, 0x00, 0x80, + 0xFF, 0xFF, 0x1F, 0x80, 0xFF, 0xFF, 0x1F, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0xFF, 0xFF, 0x1F, 0x80, 0xFF, + 0xFF, 0x1F, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, // 182 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, // 183 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x1F, 0x00, 0x00, 0xE0, 0x3F, 0x00, 0x00, 0x70, 0x73, 0x00, 0x00, 0x3B, 0xE3, 0x00, 0x00, + 0x1B, 0xC3, 0x00, 0x00, 0x18, 0xC3, 0x00, 0x00, 0x18, 0xC3, 0x00, 0x00, 0x3B, 0xE3, 0x00, 0x00, 0x73, 0x63, 0x00, 0x00, 0xE0, + 0x33, 0x00, 0x00, 0xC0, 0x13, // 1105 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xFF, 0xFF, 0x00, 0x80, 0xFF, 0xFF, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, + 0x1C, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0xF0, 0x00, 0x00, 0x00, 0xC0, 0x01, 0x00, 0x00, 0x80, 0x07, 0x00, 0x00, 0x00, + 0x0E, 0x00, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x00, 0x78, 0x00, 0x80, 0xFF, 0xFF, 0x00, 0x80, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0x63, 0x00, 0x00, 0xF0, 0x67, 0x00, 0x00, 0x38, 0x6E, 0x00, 0x00, 0x18, 0x6C, 0x00, + 0x00, 0x18, 0x6C, 0x00, 0x00, 0x38, 0x6E, 0x00, 0x00, 0xF0, 0x67, 0x00, 0x00, 0xE0, 0x63, // 8470 + 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0x1F, 0x00, 0x00, 0xE0, 0x7F, 0x00, 0x00, 0x70, 0x73, 0x00, 0x00, 0x18, 0xC3, 0x00, 0x00, + 0x18, 0xC3, 0x00, 0x00, 0x18, 0xC3, 0x00, 0x00, 0x18, 0xC0, 0x00, 0x00, 0x38, 0x60, 0x00, 0x00, 0x70, 0x78, 0x00, 0x00, 0x60, + 0x18, // 1108 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x40, 0x00, 0x00, 0x70, 0x70, 0x00, 0x00, + 0xE0, 0x3D, 0x00, 0x00, 0x80, 0x0F, 0x00, 0x00, 0x10, 0x42, 0x00, 0x00, 0x70, 0x70, 0x00, 0x00, 0xE0, 0x3D, 0x00, 0x00, 0x80, + 0x0F, 0x00, 0x00, 0x00, 0x02, // 187 + 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x18, 0x80, 0xF9, 0xFF, 0x1F, 0x80, 0xF9, 0xFF, 0x0F, // 1112 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x1C, 0x3C, 0x00, 0x00, 0x7F, 0x70, 0x00, 0x00, 0x63, 0x60, 0x00, 0x80, + 0xE1, 0xE0, 0x00, 0x80, 0xC1, 0xC0, 0x00, 0x80, 0xC1, 0xC0, 0x00, 0x80, 0xC1, 0xC0, 0x00, 0x80, 0xC1, 0xC0, 0x00, 0x80, 0xC1, + 0xC1, 0x00, 0x80, 0x83, 0xE1, 0x00, 0x00, 0x87, 0x63, 0x00, 0x00, 0x0E, 0x3F, 0x00, 0x00, 0x0C, 0x1E, // 1029 + 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0x30, 0x00, 0x00, 0xF0, 0x71, 0x00, 0x00, 0xB8, 0xE3, 0x00, 0x00, 0x18, 0xC3, 0x00, 0x00, + 0x18, 0xC3, 0x00, 0x00, 0x18, 0xC7, 0x00, 0x00, 0x18, 0xC7, 0x00, 0x00, 0x38, 0xE6, 0x00, 0x00, 0x70, 0x7E, 0x00, 0x00, 0x60, + 0x3C, // 1109 + 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0xF8, 0xFF, 0x00, 0x00, 0xF8, 0xFF, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x03, // 1111 + 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x00, 0xC0, 0x0F, 0x00, 0x00, 0xF8, 0x07, 0x00, 0x00, + 0x3E, 0x06, 0x00, 0x80, 0x0F, 0x06, 0x00, 0x80, 0x01, 0x06, 0x00, 0x80, 0x0F, 0x06, 0x00, 0x00, 0x3E, 0x06, 0x00, 0x00, 0xF8, + 0x07, 0x00, 0x00, 0xC0, 0x0F, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x00, 0x00, 0xC0, // 1040 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xFF, 0xFF, 0x00, 0x80, 0xFF, 0xFF, 0x00, 0x80, 0xC1, 0xC0, 0x00, 0x80, + 0xC1, 0xC0, 0x00, 0x80, 0xC1, 0xC0, 0x00, 0x80, 0xC1, 0xC0, 0x00, 0x80, 0xC1, 0xC0, 0x00, 0x80, 0xC1, 0xC0, 0x00, 0x80, 0xC1, + 0xC0, 0x00, 0x80, 0xC1, 0xC0, 0x00, 0x80, 0x81, 0x61, 0x00, 0x00, 0x80, 0x7F, 0x00, 0x00, 0x00, 0x1E, // 1041 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xFF, 0xFF, 0x00, 0x80, 0xFF, 0xFF, 0x00, 0x80, 0xC1, 0xC0, 0x00, 0x80, + 0xC1, 0xC0, 0x00, 0x80, 0xC1, 0xC0, 0x00, 0x80, 0xC1, 0xC0, 0x00, 0x80, 0xC1, 0xC0, 0x00, 0x80, 0xC1, 0xC0, 0x00, 0x80, 0xC1, + 0xC0, 0x00, 0x00, 0xE3, 0xC1, 0x00, 0x00, 0xFF, 0x61, 0x00, 0x00, 0x1E, 0x7F, 0x00, 0x00, 0x00, 0x1E, // 1042 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xFF, 0xFF, 0x00, 0x80, 0xFF, 0xFF, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, + 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, + 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, // 1043 + 0x00, 0x00, 0xC0, 0x0F, 0x00, 0x00, 0xE0, 0x0F, 0x00, 0x00, 0xF0, 0x00, 0x00, 0x00, 0xDF, 0x00, 0x80, 0xFF, 0xC7, 0x00, 0x80, + 0x7F, 0xC0, 0x00, 0x80, 0x01, 0xC0, 0x00, 0x80, 0x01, 0xC0, 0x00, 0x80, 0x01, 0xC0, 0x00, 0x80, 0x01, 0xC0, 0x00, 0x80, 0x01, + 0xC0, 0x00, 0x80, 0x01, 0xC0, 0x00, 0x80, 0xFF, 0xFF, 0x00, 0x80, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0xC0, 0x0F, 0x00, 0x00, 0xC0, + 0x0F, // 1044 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xFF, 0xFF, 0x00, 0x80, 0xFF, 0xFF, 0x00, 0x80, 0xC1, 0xC0, 0x00, 0x80, + 0xC1, 0xC0, 0x00, 0x80, 0xC1, 0xC0, 0x00, 0x80, 0xC1, 0xC0, 0x00, 0x80, 0xC1, 0xC0, 0x00, 0x80, 0xC1, 0xC0, 0x00, 0x80, 0xC1, + 0xC0, 0x00, 0x80, 0xC1, 0xC0, 0x00, 0x80, 0xC1, 0xC0, 0x00, 0x80, 0xC1, 0xC0, 0x00, 0x80, 0x01, 0xC0, // 1045 + 0x80, 0x01, 0x80, 0x00, 0x80, 0x01, 0xC0, 0x00, 0x80, 0x01, 0xF0, 0x00, 0x00, 0x07, 0x3C, 0x00, 0x00, 0x1E, 0x0E, 0x00, 0x00, + 0x38, 0x07, 0x00, 0x00, 0xE0, 0x03, 0x00, 0x00, 0xC0, 0x01, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x80, 0xFF, + 0xFF, 0x00, 0x80, 0xFF, 0xFF, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0xC0, 0x01, 0x00, 0x00, 0xE0, 0x03, + 0x00, 0x00, 0x38, 0x07, 0x00, 0x00, 0x1E, 0x0E, 0x00, 0x00, 0x07, 0x3C, 0x00, 0x80, 0x01, 0xF0, 0x00, 0x80, 0x01, 0xC0, 0x00, + 0x80, 0x01, 0x80, // 1046 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x0C, 0x08, 0x00, 0x00, 0x1E, 0x3C, 0x00, 0x00, 0x07, 0x78, 0x00, 0x80, 0x03, 0x60, 0x00, 0x80, + 0x01, 0xE0, 0x00, 0x80, 0xC1, 0xC0, 0x00, 0x80, 0xC1, 0xC0, 0x00, 0x80, 0xC1, 0xC0, 0x00, 0x80, 0xC1, 0xC0, 0x00, 0x00, 0xE3, + 0xE1, 0x00, 0x00, 0xBF, 0x73, 0x00, 0x00, 0x1C, 0x3F, 0x00, 0x00, 0x00, 0x1E, // 1047 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xFF, 0xFF, 0x00, 0x80, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, + 0x00, 0x3C, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x80, 0x07, 0x00, 0x00, 0xC0, 0x01, 0x00, 0x00, 0xF0, 0x00, 0x00, 0x00, 0x38, + 0x00, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x80, 0xFF, 0xFF, 0x00, 0x80, 0xFF, 0xFF, // 1048 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xFF, 0xFF, 0x00, 0x80, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x70, 0x00, 0x18, + 0x00, 0x3C, 0x00, 0x30, 0x00, 0x0E, 0x00, 0x20, 0x80, 0x07, 0x00, 0x20, 0xC0, 0x01, 0x00, 0x20, 0xF0, 0x00, 0x00, 0x30, 0x38, + 0x00, 0x00, 0x18, 0x1E, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x80, 0xFF, 0xFF, 0x00, 0x80, 0xFF, 0xFF, // 1049 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xFF, 0xFF, 0x00, 0x80, 0xFF, 0xFF, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, + 0xC0, 0x00, 0x00, 0x00, 0xC0, 0x01, 0x00, 0x00, 0x60, 0x03, 0x00, 0x00, 0x78, 0x07, 0x00, 0x00, 0x1E, 0x0E, 0x00, 0x00, 0x07, + 0x38, 0x00, 0x80, 0x01, 0xF0, 0x00, 0x80, 0x01, 0xC0, 0x00, 0x80, 0x01, 0x80, // 1050 + 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0xE0, 0x00, 0x80, 0xFF, 0x7F, 0x00, 0x80, 0xFF, 0x3F, 0x00, 0x80, + 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, + 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0xFF, 0xFF, 0x00, 0x80, 0xFF, 0xFF, // 1051 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xFF, 0xFF, 0x00, 0x80, 0xFF, 0xFF, 0x00, 0x80, 0x07, 0x00, 0x00, 0x00, + 0x3F, 0x00, 0x00, 0x00, 0xF8, 0x03, 0x00, 0x00, 0x80, 0x1F, 0x00, 0x00, 0x00, 0xFC, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, + 0xFC, 0x00, 0x00, 0x80, 0x1F, 0x00, 0x00, 0xF8, 0x03, 0x00, 0x00, 0x3F, 0x00, 0x00, 0x80, 0x07, 0x00, 0x00, 0x80, 0xFF, 0xFF, + 0x00, 0x80, 0xFF, 0xFF, // 1052 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xFF, 0xFF, 0x00, 0x80, 0xFF, 0xFF, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, + 0xC0, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0xC0, + 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x80, 0xFF, 0xFF, 0x00, 0x80, 0xFF, 0xFF, // 1053 + 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0x07, 0x00, 0x00, 0xFC, 0x1F, 0x00, 0x00, 0x1E, 0x3C, 0x00, 0x00, 0x07, 0x70, 0x00, 0x00, + 0x03, 0x60, 0x00, 0x80, 0x03, 0xE0, 0x00, 0x80, 0x01, 0xC0, 0x00, 0x80, 0x01, 0xC0, 0x00, 0x80, 0x01, 0xC0, 0x00, 0x80, 0x01, + 0xC0, 0x00, 0x80, 0x01, 0xC0, 0x00, 0x80, 0x03, 0xE0, 0x00, 0x00, 0x03, 0x60, 0x00, 0x00, 0x07, 0x70, 0x00, 0x00, 0x1E, 0x3C, + 0x00, 0x00, 0xFC, 0x1F, 0x00, 0x00, 0xF0, 0x07, // 1054 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xFF, 0xFF, 0x00, 0x80, 0xFF, 0xFF, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, + 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, + 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0xFF, 0xFF, 0x00, 0x80, 0xFF, 0xFF, // 1055 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xFF, 0xFF, 0x00, 0x80, 0xFF, 0xFF, 0x00, 0x80, 0x81, 0x01, 0x00, 0x80, + 0x81, 0x01, 0x00, 0x80, 0x81, 0x01, 0x00, 0x80, 0x81, 0x01, 0x00, 0x80, 0x81, 0x01, 0x00, 0x80, 0x81, 0x01, 0x00, 0x80, 0x81, + 0x01, 0x00, 0x80, 0x81, 0x01, 0x00, 0x00, 0xC3, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x3C, // 1056 + 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0x07, 0x00, 0x00, 0xFC, 0x1F, 0x00, 0x00, 0x1E, 0x3C, 0x00, 0x00, 0x07, 0x70, 0x00, 0x00, + 0x03, 0x60, 0x00, 0x80, 0x01, 0xC0, 0x00, 0x80, 0x01, 0xC0, 0x00, 0x80, 0x01, 0xC0, 0x00, 0x80, 0x01, 0xC0, 0x00, 0x80, 0x01, + 0xC0, 0x00, 0x80, 0x01, 0xC0, 0x00, 0x00, 0x03, 0x60, 0x00, 0x00, 0x07, 0x70, 0x00, 0x00, 0x0E, 0x3C, 0x00, 0x00, 0x08, + 0x0C, // 1057 + 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, + 0x01, 0x00, 0x00, 0x80, 0xFF, 0xFF, 0x00, 0x80, 0xFF, 0xFF, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, + 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, // 1058 + 0x00, 0x00, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x07, 0x00, 0x00, 0x00, 0x1E, 0xC0, 0x00, 0x00, 0x78, 0xC0, 0x00, 0x00, + 0xE0, 0xC1, 0x00, 0x00, 0x80, 0x77, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x80, 0x07, 0x00, 0x00, 0xE0, 0x01, 0x00, 0x00, 0x78, + 0x00, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x80, 0x07, 0x00, 0x00, 0x80, 0x01, // 1059 + 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0x03, 0x00, 0x00, 0xF8, 0x0F, 0x00, 0x00, 0x1C, 0x1C, 0x00, 0x00, 0x0C, 0x18, 0x00, 0x00, + 0x0E, 0x38, 0x00, 0x00, 0x06, 0x30, 0x00, 0x00, 0x06, 0x30, 0x00, 0x80, 0xFF, 0xFF, 0x00, 0x80, 0xFF, 0xFF, 0x00, 0x00, 0x06, + 0x30, 0x00, 0x00, 0x06, 0x30, 0x00, 0x00, 0x0E, 0x38, 0x00, 0x00, 0x0C, 0x18, 0x00, 0x00, 0x1C, 0x1C, 0x00, 0x00, 0xF8, 0x0F, + 0x00, 0x00, 0xE0, 0x03, // 1060 + 0x00, 0x00, 0x80, 0x00, 0x80, 0x00, 0xC0, 0x00, 0x80, 0x01, 0xF0, 0x00, 0x80, 0x07, 0x78, 0x00, 0x00, 0x0F, 0x1E, 0x00, 0x00, + 0x3C, 0x0F, 0x00, 0x00, 0xF8, 0x07, 0x00, 0x00, 0xE0, 0x01, 0x00, 0x00, 0xF8, 0x07, 0x00, 0x00, 0x1E, 0x0F, 0x00, 0x00, 0x0F, + 0x1C, 0x00, 0x80, 0x07, 0x78, 0x00, 0x80, 0x01, 0xF0, 0x00, 0x80, 0x00, 0xC0, 0x00, 0x00, 0x00, 0x80, // 1061 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xFF, 0xFF, 0x00, 0x80, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, + 0x00, 0xC0, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, + 0xC0, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x80, 0xFF, 0xFF, 0x00, 0x80, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0xC0, + 0x1F, 0x00, 0x00, 0xC0, 0x1F, // 1062 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x7F, 0x00, 0x00, 0x80, 0xFF, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, + 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x80, + 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x80, 0xFF, 0xFF, 0x00, 0x80, 0xFF, 0xFF, // 1063 + 0x00, 0x00, 0x00, 0x00, 0x80, 0xFF, 0xFF, 0x00, 0x80, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, + 0x00, 0xC0, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x80, 0xFF, + 0xFF, 0x00, 0x80, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0xC0, + 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x80, 0xFF, 0xFF, 0x00, 0x80, 0xFF, + 0xFF, // 1064 + 0x00, 0x00, 0x00, 0x00, 0x80, 0xFF, 0xFF, 0x00, 0x80, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, + 0x00, 0xC0, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x80, 0xFF, + 0xFF, 0x00, 0x80, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0xC0, + 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x80, 0xFF, 0xFF, 0x00, 0x80, 0xFF, 0xFF, 0x00, + 0x00, 0x00, 0xC0, 0x1F, 0x00, 0x00, 0xC0, 0x1F, // 1065 + 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, + 0xFF, 0xFF, 0x00, 0x80, 0xFF, 0xFF, 0x00, 0x00, 0xC0, 0xC0, 0x00, 0x00, 0xC0, 0xC0, 0x00, 0x00, 0xC0, 0xC0, 0x00, 0x00, 0xC0, + 0xC0, 0x00, 0x00, 0xC0, 0xC0, 0x00, 0x00, 0xC0, 0xC0, 0x00, 0x00, 0xC0, 0xC0, 0x00, 0x00, 0xC0, 0xC0, 0x00, 0x00, 0x80, 0x61, + 0x00, 0x00, 0x80, 0x7F, 0x00, 0x00, 0x00, 0x1E, // 1066 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xFF, 0xFF, 0x00, 0x80, 0xFF, 0xFF, 0x00, 0x00, 0xC0, 0xC0, 0x00, 0x00, + 0xC0, 0xC0, 0x00, 0x00, 0xC0, 0xC0, 0x00, 0x00, 0xC0, 0xC0, 0x00, 0x00, 0xC0, 0xC0, 0x00, 0x00, 0xC0, 0xC0, 0x00, 0x00, 0xC0, + 0xC0, 0x00, 0x00, 0xC0, 0xC0, 0x00, 0x00, 0x80, 0x61, 0x00, 0x00, 0x80, 0x7F, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xFF, 0xFF, 0x00, 0x80, 0xFF, 0xFF, // 1067 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xFF, 0xFF, 0x00, 0x80, 0xFF, 0xFF, 0x00, 0x00, 0xC0, 0xC0, 0x00, 0x00, + 0xC0, 0xC0, 0x00, 0x00, 0xC0, 0xC0, 0x00, 0x00, 0xC0, 0xC0, 0x00, 0x00, 0xC0, 0xC0, 0x00, 0x00, 0xC0, 0xC0, 0x00, 0x00, 0xC0, + 0xC0, 0x00, 0x00, 0xC0, 0xC0, 0x00, 0x00, 0x80, 0x61, 0x00, 0x00, 0x80, 0x7F, 0x00, 0x00, 0x00, 0x1E, // 1068 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x08, 0x00, 0x00, 0x1E, 0x3C, 0x00, 0x00, 0x0F, 0x78, 0x00, 0x00, 0x03, 0x60, 0x00, 0x80, + 0x03, 0xE0, 0x00, 0x80, 0x01, 0xC0, 0x00, 0x80, 0xC1, 0xC0, 0x00, 0x80, 0xC1, 0xC0, 0x00, 0x80, 0xC1, 0xC0, 0x00, 0x80, 0xC1, + 0xC0, 0x00, 0x00, 0xC3, 0x60, 0x00, 0x00, 0xC7, 0x70, 0x00, 0x00, 0xCE, 0x38, 0x00, 0x00, 0xFC, 0x1F, 0x00, 0x00, 0xF0, + 0x07, // 1069 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xFF, 0xFF, 0x00, 0x80, 0xFF, 0xFF, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, + 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0xF0, 0x07, 0x00, 0x00, 0xFC, 0x1F, 0x00, 0x00, 0x1E, 0x3C, 0x00, 0x00, 0x07, + 0x70, 0x00, 0x00, 0x03, 0x60, 0x00, 0x80, 0x01, 0xC0, 0x00, 0x80, 0x01, 0xC0, 0x00, 0x80, 0x01, 0xC0, 0x00, 0x80, 0x01, 0xC0, + 0x00, 0x80, 0x01, 0xC0, 0x00, 0x80, 0x01, 0xC0, 0x00, 0x00, 0x03, 0x60, 0x00, 0x00, 0x07, 0x70, 0x00, 0x00, 0x1E, 0x3C, 0x00, + 0x00, 0xFC, 0x1F, 0x00, 0x00, 0xF0, 0x07, // 1070 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x3E, 0xC0, 0x00, 0x00, 0x7F, 0xF0, 0x00, 0x80, 0x63, 0x7C, 0x00, 0x80, + 0xC1, 0x1E, 0x00, 0x80, 0xC1, 0x0F, 0x00, 0x80, 0xC1, 0x03, 0x00, 0x80, 0xC1, 0x01, 0x00, 0x80, 0xC1, 0x00, 0x00, 0x80, 0xC1, + 0x00, 0x00, 0x80, 0xC1, 0x00, 0x00, 0x80, 0xC1, 0x00, 0x00, 0x80, 0xFF, 0xFF, 0x00, 0x80, 0xFF, 0xFF, // 1071 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x38, 0x00, 0x00, 0x70, 0x7C, 0x00, 0x00, 0x30, 0xE6, 0x00, 0x00, 0x18, 0xC6, 0x00, 0x00, + 0x18, 0xC6, 0x00, 0x00, 0x18, 0xC3, 0x00, 0x00, 0x18, 0x63, 0x00, 0x00, 0x38, 0x63, 0x00, 0x00, 0xF0, 0x7F, 0x00, 0x00, 0xE0, + 0xFF, 0x00, 0x00, 0x00, 0x80, // 1072 + 0x00, 0x00, 0x00, 0x00, 0x00, 0xF8, 0x0F, 0x00, 0x00, 0xFE, 0x3F, 0x00, 0x00, 0x73, 0x70, 0x00, 0x00, 0x31, 0xE0, 0x00, 0x80, + 0x19, 0xC0, 0x00, 0x80, 0x19, 0xC0, 0x00, 0x80, 0x19, 0xC0, 0x00, 0x80, 0x19, 0xC0, 0x00, 0x80, 0x39, 0xE0, 0x00, 0x80, 0x71, + 0x70, 0x00, 0x80, 0xE1, 0x3F, 0x00, 0x80, 0x80, 0x0F, // 1073 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF8, 0xFF, 0x00, 0x00, 0xF8, 0xFF, 0x00, 0x00, 0x18, 0xC3, 0x00, 0x00, + 0x18, 0xC3, 0x00, 0x00, 0x18, 0xC3, 0x00, 0x00, 0x18, 0xC3, 0x00, 0x00, 0x18, 0xC3, 0x00, 0x00, 0xF0, 0xE7, 0x00, 0x00, 0xF0, + 0x7E, 0x00, 0x00, 0x00, 0x3C, // 1074 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF8, 0xFF, 0x00, 0x00, 0xF8, 0xFF, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, + 0x18, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x18, // 1075 + 0x00, 0x00, 0xC0, 0x0F, 0x00, 0x00, 0xE0, 0x0F, 0x00, 0x00, 0xFC, 0x00, 0x00, 0xF8, 0xDF, 0x00, 0x00, 0xF8, 0xC3, 0x00, 0x00, + 0x18, 0xC0, 0x00, 0x00, 0x18, 0xC0, 0x00, 0x00, 0x18, 0xC0, 0x00, 0x00, 0x18, 0xC0, 0x00, 0x00, 0xF8, 0xFF, 0x00, 0x00, 0xF8, + 0xFF, 0x00, 0x00, 0x00, 0xC0, 0x0F, 0x00, 0x00, 0xC0, 0x0F, // 1076 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x1F, 0x00, 0x00, 0xE0, 0x3F, 0x00, 0x00, 0x70, 0x73, 0x00, 0x00, 0x38, 0xE3, 0x00, 0x00, + 0x18, 0xC3, 0x00, 0x00, 0x18, 0xC3, 0x00, 0x00, 0x18, 0xC3, 0x00, 0x00, 0x38, 0xE3, 0x00, 0x00, 0x70, 0x63, 0x00, 0x00, 0xE0, + 0x33, 0x00, 0x00, 0xC0, 0x13, // 1077 + 0x00, 0x18, 0xE0, 0x00, 0x00, 0x18, 0x70, 0x00, 0x00, 0xF8, 0x38, 0x00, 0x00, 0xE0, 0x0D, 0x00, 0x00, 0x80, 0x07, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0xF8, 0xFF, 0x00, 0x00, 0xF8, 0xFF, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x80, 0x07, 0x00, 0x00, 0xE0, + 0x0D, 0x00, 0x00, 0xF8, 0x38, 0x00, 0x00, 0x18, 0x70, 0x00, 0x00, 0x18, 0xE0, 0x00, 0x00, 0x00, 0x80, // 1078 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x30, 0x00, 0x00, 0x70, 0x70, 0x00, 0x00, 0x38, 0xE0, 0x00, 0x00, 0x18, 0xC0, 0x00, 0x00, + 0x18, 0xC3, 0x00, 0x00, 0x18, 0xC3, 0x00, 0x00, 0x38, 0xE7, 0x00, 0x00, 0xF0, 0x7F, 0x00, 0x00, 0xE0, 0x3C, // 1079 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF8, 0xFF, 0x00, 0x00, 0xF8, 0xFF, 0x00, 0x00, 0x00, 0xF0, 0x00, 0x00, + 0x00, 0x3C, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x80, 0x07, 0x00, 0x00, 0xE0, 0x01, 0x00, 0x00, 0x78, 0x00, 0x00, 0x00, 0xF8, + 0xFF, 0x00, 0x00, 0xF8, 0xFF, // 1080 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF8, 0xFF, 0x00, 0x00, 0xF8, 0xFF, 0x00, 0x80, 0x01, 0xF0, 0x00, 0x00, + 0x03, 0x3C, 0x00, 0x00, 0x02, 0x0F, 0x00, 0x00, 0x82, 0x07, 0x00, 0x00, 0xE2, 0x01, 0x00, 0x00, 0x7B, 0x00, 0x00, 0x80, 0xF9, + 0xFF, 0x00, 0x00, 0xF8, 0xFF, // 1081 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF8, 0xFF, 0x00, 0x00, 0xF8, 0xFF, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, + 0x00, 0x07, 0x00, 0x00, 0xC0, 0x0D, 0x00, 0x00, 0xF0, 0x38, 0x00, 0x00, 0x18, 0x70, 0x00, 0x00, 0x18, 0xE0, 0x00, 0x00, 0x00, + 0x80, // 1082 + 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0xF8, 0xFF, 0x00, 0x00, 0xF8, 0x7F, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, + 0x18, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0xF8, + 0xFF, 0x00, 0x00, 0xF8, 0xFF, // 1083 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF8, 0xFF, 0x00, 0x00, 0xF8, 0xFF, 0x00, 0x00, 0x78, 0x00, 0x00, 0x00, + 0xF0, 0x03, 0x00, 0x00, 0x80, 0x3F, 0x00, 0x00, 0x00, 0xFC, 0x00, 0x00, 0x00, 0xE0, 0x00, 0x00, 0x00, 0xFC, 0x00, 0x00, 0x80, + 0x1F, 0x00, 0x00, 0xF0, 0x03, 0x00, 0x00, 0x78, 0x00, 0x00, 0x00, 0xF8, 0xFF, 0x00, 0x00, 0xF8, 0xFF, // 1084 + 0x00, 0x00, 0x00, 0x00, 0x00, 0xF8, 0xFF, 0x00, 0x00, 0xF8, 0xFF, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, + 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0xF8, + 0xFF, 0x00, 0x00, 0xF8, 0xFF, // 1085 + 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0x1F, 0x00, 0x00, 0xE0, 0x3F, 0x00, 0x00, 0x70, 0x70, 0x00, 0x00, 0x38, 0xE0, 0x00, 0x00, + 0x18, 0xC0, 0x00, 0x00, 0x18, 0xC0, 0x00, 0x00, 0x18, 0xC0, 0x00, 0x00, 0x38, 0xE0, 0x00, 0x00, 0x70, 0x70, 0x00, 0x00, 0xE0, + 0x3F, 0x00, 0x00, 0xC0, 0x1F, // 1086 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF8, 0xFF, 0x00, 0x00, 0xF8, 0xFF, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, + 0x18, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0xF8, + 0xFF, 0x00, 0x00, 0xF8, 0xFF, // 1087 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF8, 0xFF, 0x1F, 0x00, 0xF8, 0xFF, 0x1F, 0x00, 0x60, 0x30, 0x00, 0x00, + 0x30, 0x60, 0x00, 0x00, 0x18, 0xC0, 0x00, 0x00, 0x18, 0xC0, 0x00, 0x00, 0x18, 0xC0, 0x00, 0x00, 0x38, 0xE0, 0x00, 0x00, 0x70, + 0x70, 0x00, 0x00, 0xE0, 0x3F, 0x00, 0x00, 0xC0, 0x0F, // 1088 + 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0x1F, 0x00, 0x00, 0xE0, 0x3F, 0x00, 0x00, 0x70, 0x70, 0x00, 0x00, 0x38, 0xE0, 0x00, 0x00, + 0x18, 0xC0, 0x00, 0x00, 0x18, 0xC0, 0x00, 0x00, 0x18, 0xC0, 0x00, 0x00, 0x38, 0xE0, 0x00, 0x00, 0x70, 0x70, 0x00, 0x00, 0x60, + 0x30, // 1089 + 0x00, 0x18, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0xF8, 0xFF, 0x00, 0x00, + 0xF8, 0xFF, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x18, // 1090 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0xF8, 0x01, 0x18, 0x00, 0xC0, 0x07, 0x18, 0x00, 0x00, 0x3E, 0x1C, 0x00, + 0x00, 0xF8, 0x0F, 0x00, 0x00, 0xF0, 0x03, 0x00, 0x00, 0x7F, 0x00, 0x00, 0xE0, 0x0F, 0x00, 0x00, 0xF8, 0x00, 0x00, 0x00, + 0x18, // 1091 + 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0x1F, 0x00, 0x00, 0xE0, 0x3F, 0x00, 0x00, 0x70, 0x70, 0x00, 0x00, 0x38, 0xE0, 0x00, 0x00, + 0x18, 0xC0, 0x00, 0x00, 0x18, 0xC0, 0x00, 0x00, 0x18, 0xC0, 0x00, 0x00, 0x70, 0x60, 0x00, 0x80, 0xFF, 0xFF, 0x1F, 0x80, 0xFF, + 0xFF, 0x1F, 0x00, 0x70, 0x60, 0x00, 0x00, 0x18, 0xC0, 0x00, 0x00, 0x18, 0xC0, 0x00, 0x00, 0x18, 0xC0, 0x00, 0x00, 0x18, 0xE0, + 0x00, 0x00, 0x70, 0x70, 0x00, 0x00, 0xE0, 0x3F, 0x00, 0x00, 0xC0, 0x1F, // 1092 + 0x00, 0x08, 0x80, 0x00, 0x00, 0x18, 0xC0, 0x00, 0x00, 0x78, 0xF0, 0x00, 0x00, 0xE0, 0x38, 0x00, 0x00, 0xC0, 0x1F, 0x00, 0x00, + 0x00, 0x07, 0x00, 0x00, 0x80, 0x1F, 0x00, 0x00, 0xE0, 0x38, 0x00, 0x00, 0x70, 0xF0, 0x00, 0x00, 0x18, 0xC0, 0x00, 0x00, 0x08, + 0x80, // 1093 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF8, 0xFF, 0x00, 0x00, 0xF8, 0xFF, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, + 0x00, 0xC0, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0xF8, + 0xFF, 0x00, 0x00, 0xF8, 0xFF, 0x1F, 0x00, 0x00, 0xC0, 0x1F, // 1094 + 0x00, 0x00, 0x00, 0x00, 0x00, 0xF8, 0x01, 0x00, 0x00, 0xF8, 0x07, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, + 0x00, 0x0C, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0xF8, 0xFF, 0x00, 0x00, 0xF8, + 0xFF, // 1095 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF8, 0xFF, 0x00, 0x00, 0xF8, 0xFF, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, + 0x00, 0xC0, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0xF8, 0xFF, 0x00, 0x00, 0xF8, + 0xFF, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0xC0, + 0x00, 0x00, 0xF8, 0xFF, 0x00, 0x00, 0xF8, 0xFF, // 1096 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF8, 0xFF, 0x00, 0x00, 0xF8, 0xFF, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, + 0x00, 0xC0, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0xF8, 0xFF, 0x00, 0x00, 0xF8, + 0xFF, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0xC0, + 0x00, 0x00, 0xF8, 0xFF, 0x00, 0x00, 0xF8, 0xFF, 0x1F, 0x00, 0x00, 0xC0, 0x1F, // 1097 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0xF8, 0xFF, 0x00, 0x00, + 0xF8, 0xFF, 0x00, 0x00, 0x00, 0xC3, 0x00, 0x00, 0x00, 0xC3, 0x00, 0x00, 0x00, 0xC3, 0x00, 0x00, 0x00, 0xC3, 0x00, 0x00, 0x00, + 0xC3, 0x00, 0x00, 0x00, 0xE7, 0x00, 0x00, 0x00, 0x7E, 0x00, 0x00, 0x00, 0x3C, // 1098 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF8, 0xFF, 0x00, 0x00, 0xF8, 0xFF, 0x00, 0x00, 0x00, 0xC3, 0x00, 0x00, + 0x00, 0xC3, 0x00, 0x00, 0x00, 0xC3, 0x00, 0x00, 0x00, 0xC3, 0x00, 0x00, 0x00, 0xC3, 0x00, 0x00, 0x00, 0xE7, 0x00, 0x00, 0x00, + 0x7E, 0x00, 0x00, 0x00, 0x3C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF8, 0xFF, 0x00, 0x00, 0xF8, 0xFF, // 1099 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF8, 0xFF, 0x00, 0x00, 0xF8, 0xFF, 0x00, 0x00, 0x00, 0xC3, 0x00, 0x00, + 0x00, 0xC3, 0x00, 0x00, 0x00, 0xC3, 0x00, 0x00, 0x00, 0xC3, 0x00, 0x00, 0x00, 0xC3, 0x00, 0x00, 0x00, 0xE7, 0x00, 0x00, 0x00, + 0x7E, 0x00, 0x00, 0x00, 0x3C, // 1100 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x18, 0x00, 0x00, 0x70, 0x78, 0x00, 0x00, 0x38, 0x60, 0x00, 0x00, 0x18, 0xC0, 0x00, 0x00, + 0x18, 0xC3, 0x00, 0x00, 0x18, 0xC3, 0x00, 0x00, 0x18, 0xC3, 0x00, 0x00, 0x70, 0x73, 0x00, 0x00, 0xE0, 0x7F, 0x00, 0x00, 0xC0, + 0x1F, // 1101 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF8, 0xFF, 0x00, 0x00, 0xF8, 0xFF, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, + 0x00, 0x03, 0x00, 0x00, 0xC0, 0x1F, 0x00, 0x00, 0xE0, 0x3F, 0x00, 0x00, 0x70, 0x70, 0x00, 0x00, 0x38, 0xE0, 0x00, 0x00, 0x18, + 0xC0, 0x00, 0x00, 0x18, 0xC0, 0x00, 0x00, 0x18, 0xC0, 0x00, 0x00, 0x38, 0xE0, 0x00, 0x00, 0x70, 0x70, 0x00, 0x00, 0xE0, 0x3F, + 0x00, 0x00, 0xC0, 0x1F, // 1102 + 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0xC1, 0x00, 0x00, 0xF0, 0xE3, 0x00, 0x00, 0x38, 0x7B, 0x00, 0x00, 0x18, 0x1A, 0x00, 0x00, + 0x18, 0x0E, 0x00, 0x00, 0x18, 0x06, 0x00, 0x00, 0x18, 0x06, 0x00, 0x00, 0x18, 0x06, 0x00, 0x00, 0xF8, 0xFF, 0x00, 0x00, 0xF8, + 0xFF, // 1103 }; \ No newline at end of file diff --git a/src/graphics/fonts/OLEDDisplayFontsUA.h b/src/graphics/fonts/OLEDDisplayFontsUA.h index 3bd9bb4ca..dc313aed2 100644 --- a/src/graphics/fonts/OLEDDisplayFontsUA.h +++ b/src/graphics/fonts/OLEDDisplayFontsUA.h @@ -8,4 +8,6 @@ #endif extern const uint8_t ArialMT_Plain_10_UA[] PROGMEM; +extern const uint8_t ArialMT_Plain_16_UA[] PROGMEM; +extern const uint8_t ArialMT_Plain_24_UA[] PROGMEM; #endif \ No newline at end of file diff --git a/src/graphics/images.h b/src/graphics/images.h index 2b0854a33..b757dcf30 100644 --- a/src/graphics/images.h +++ b/src/graphics/images.h @@ -14,7 +14,7 @@ const uint8_t imgUser[] PROGMEM = {0x3C, 0x42, 0x99, 0xA5, 0xA5, 0x99, 0x42, 0x3 const uint8_t imgPositionEmpty[] PROGMEM = {0x20, 0x30, 0x28, 0x24, 0x42, 0xFF}; const uint8_t imgPositionSolid[] PROGMEM = {0x20, 0x30, 0x38, 0x3C, 0x7E, 0xFF}; -#ifdef T_WATCH_S3 +#if defined(DISPLAY_CLOCK_FRAME) const uint8_t bluetoothConnectedIcon[36] PROGMEM = {0xfe, 0x01, 0xff, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x00, 0xe3, 0x1f, 0xf3, 0x3f, 0x33, 0x30, 0x33, 0x33, 0x33, 0x33, 0x03, 0x33, 0xff, 0x33, 0xfe, 0x31, 0x00, 0x30, 0x30, 0x30, 0x30, 0x30, 0xf0, 0x3f, 0xe0, 0x1f}; @@ -56,6 +56,16 @@ static unsigned char thumbdown[] PROGMEM = { 0x80, 0x09, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, 0x1C, 0x00, 0x00, }; +#define smiley_height 30 +#define smiley_width 30 +static unsigned char smiley[] PROGMEM = { + 0x00, 0xfe, 0x0f, 0x00, 0x80, 0x01, 0x30, 0x00, 0x40, 0x00, 0xc0, 0x00, 0x20, 0x00, 0x00, 0x01, 0x10, 0x00, 0x00, 0x02, + 0x08, 0x00, 0x00, 0x04, 0x04, 0x00, 0x00, 0x08, 0x04, 0x00, 0x00, 0x10, 0x02, 0x0e, 0x0e, 0x10, 0x02, 0x09, 0x12, 0x10, + 0x01, 0x09, 0x12, 0x20, 0x01, 0x0f, 0x1e, 0x20, 0x01, 0x00, 0x00, 0x20, 0x01, 0x00, 0x00, 0x20, 0x01, 0x00, 0x00, 0x20, + 0x01, 0x00, 0x00, 0x20, 0x01, 0x00, 0x00, 0x20, 0x01, 0x00, 0x00, 0x20, 0x01, 0x00, 0x00, 0x20, 0x81, 0x00, 0x20, 0x20, + 0x82, 0x00, 0x20, 0x10, 0x02, 0x01, 0x10, 0x10, 0x04, 0x02, 0x08, 0x08, 0x04, 0xfc, 0x07, 0x08, 0x08, 0x00, 0x00, 0x04, + 0x10, 0x00, 0x00, 0x02, 0x20, 0x00, 0x00, 0x01, 0x40, 0x00, 0xc0, 0x00, 0x80, 0x01, 0x30, 0x00, 0x00, 0xfe, 0x0f, 0x00}; + #define question_height 25 #define question_width 25 static unsigned char question[] PROGMEM = { diff --git a/src/input/ExpressLRSFiveWay.cpp b/src/input/ExpressLRSFiveWay.cpp index af4433dae..56413bd55 100644 --- a/src/input/ExpressLRSFiveWay.cpp +++ b/src/input/ExpressLRSFiveWay.cpp @@ -233,7 +233,7 @@ void ExpressLRSFiveWay::sendAdhocPing() // Contained as one method for easier remapping of buttons by user void ExpressLRSFiveWay::shutdown() { - LOG_INFO("Shutdown from long press\n"); + LOG_INFO("Shutdown from long press"); powerFSM.trigger(EVENT_PRESS); screen->startAlert("Shutting down..."); // Don't set alerting = true. We don't want to auto-dismiss this alert. diff --git a/src/input/MPR121Keyboard.cpp b/src/input/MPR121Keyboard.cpp new file mode 100644 index 000000000..078d80272 --- /dev/null +++ b/src/input/MPR121Keyboard.cpp @@ -0,0 +1,430 @@ +// Based on the BBQ10 Keyboard + +#include "MPR121Keyboard.h" +#include "configuration.h" +#include + +#define _MPR121_REG_KEY 0x5a + +#define _MPR121_REG_TOUCH_STATUS 0x00 +#define _MPR121_REG_ELECTRODE_FILTERED_DATA +#define _MPR121_REG_BASELINE_VALUE 0x1E + +// Baseline filters +#define _MPR121_REG_MAX_HALF_DELTA_RISING 0x2B +#define _MPR121_REG_NOISE_HALF_DELTA_RISING 0x2C +#define _MPR121_REG_NOISE_COUNT_LIMIT_RISING 0x2D +#define _MPR121_REG_FILTER_DELAY_COUNT_RISING 0x2E +#define _MPR121_REG_MAX_HALF_DELTA_FALLING 0x2F +#define _MPR121_REG_NOISE_HALF_DELTA_FALLING 0x30 +#define _MPR121_REG_NOISE_COUNT_LIMIT_FALLING 0x31 +#define _MPR121_REG_FILTER_DELAY_COUNT_FALLING 0x32 +#define _MPR121_REG_NOISE_HALF_DELTA_TOUCHED 0x33 +#define _MPR121_REG_NOISE_COUNT_LIMIT_TOUCHED 0x34 +#define _MPR121_REG_FILTER_DELAY_COUNT_TOUCHED 0x35 + +#define _MPR121_REG_TOUCH_THRESHOLD 0x41 // First input, +2 for subsequent +#define _MPR121_REG_RELEASE_THRESHOLD 0x42 // First input, +2 for subsequent +#define _MPR121_REG_DEBOUNCE 0x5B +#define _MPR121_REG_CONFIG1 0x5C +#define _MPR121_REG_CONFIG2 0x5D +#define _MPR121_REG_ELECTRODE_CONFIG 0x5E +#define _MPR121_REG_SOFT_RESET 0x80 + +#define _KEY_MASK 0x0FFF // Key mask for the first 12 bits +#define _NUM_KEYS 12 + +#define ECR_CALIBRATION_TRACK_FROM_ZERO (0 << 6) +#define ECR_CALIBRATION_LOCK (1 << 6) +#define ECR_CALIBRATION_TRACK_FROM_PARTIAL_FILTER (2 << 6) // Recommended Typical Mode +#define ECR_CALIBRATION_TRACK_FROM_FULL_FILTER (3 << 6) +#define ECR_PROXIMITY_DETECTION_OFF (0 << 0) // Not using proximity detection +#define ECR_TOUCH_DETECTION_12CH (12 << 0) // Using all 12 channels + +#define MPR121_NONE 0x00 +#define MPR121_REBOOT 0x90 +#define MPR121_LEFT 0xb4 +#define MPR121_UP 0xb5 +#define MPR121_DOWN 0xb6 +#define MPR121_RIGHT 0xb7 +#define MPR121_ESC 0x1b +#define MPR121_BSP 0x08 +#define MPR121_SELECT 0x0d + +#define MPR121_FN_ON 0xf1 +#define MPR121_FN_OFF 0xf2 + +#define LONG_PRESS_THRESHOLD 2000 +#define MULTI_TAP_THRESHOLD 2000 + +uint8_t TapMod[12] = {1, 2, 1, 13, 7, 7, 7, 7, 7, 9, 7, 9}; // Num chars per key, Modulus for rotating through characters + +unsigned char MPR121_TapMap[12][13] = {{MPR121_BSP}, + {'0', ' '}, + {MPR121_SELECT}, + {'1', '.', ',', '?', '!', ':', ';', '-', '_', '\\', '/', '(', ')'}, + {'2', 'a', 'b', 'c', 'A', 'B', 'C'}, + {'3', 'd', 'e', 'f', 'D', 'E', 'F'}, + {'4', 'g', 'h', 'i', 'G', 'H', 'I'}, + {'5', 'j', 'k', 'l', 'J', 'K', 'L'}, + {'6', 'm', 'n', 'o', 'M', 'N', 'O'}, + {'7', 'p', 'q', 'r', 's', 'P', 'Q', 'R', 'S'}, + {'8', 't', 'u', 'v', 'T', 'U', 'V'}, + {'9', 'w', 'x', 'y', 'z', 'W', 'X', 'Y', 'Z'}}; + +unsigned char MPR121_LongPressMap[12] = {MPR121_ESC, ' ', MPR121_NONE, MPR121_NONE, MPR121_UP, MPR121_NONE, + MPR121_LEFT, MPR121_NONE, MPR121_RIGHT, MPR121_NONE, MPR121_DOWN, MPR121_NONE}; + +// Translation map from left to right, top to bottom layout to a more convenient layout to manufacture, matching the +// https://www.amazon.com.au/Capacitive-Sensitive-Sensitivity-Replacement-Traditional/dp/B0CTJD5KW9/ref=pd_ci_mcx_mh_mcx_views_0_title?th=1 +/*uint8_t MPR121_KeyMap[12] = { + 9, 6, 3, 0, + 10, 7, 4, 1, + 11, 8, 5, 2 +};*/ +// Rotated Layout +uint8_t MPR121_KeyMap[12] = {2, 5, 8, 11, 1, 4, 7, 10, 0, 3, 6, 9}; + +MPR121Keyboard::MPR121Keyboard() : m_wire(nullptr), m_addr(0), readCallback(nullptr), writeCallback(nullptr) +{ + // LOG_DEBUG("MPR121 @ %02x\n", m_addr); + state = Init; + last_key = -1; + last_tap = 0L; + char_idx = 0; + queue = ""; +} + +void MPR121Keyboard::begin(uint8_t addr, TwoWire *wire) +{ + m_addr = addr; + m_wire = wire; + + m_wire->begin(); + + reset(); +} + +void MPR121Keyboard::begin(i2c_com_fptr_t r, i2c_com_fptr_t w, uint8_t addr) +{ + m_addr = addr; + m_wire = nullptr; + writeCallback = w; + readCallback = r; + reset(); +} + +void MPR121Keyboard::reset() +{ + LOG_DEBUG("MPR121 Reset"); + // Trigger a MPR121 Soft Reset + if (m_wire) { + m_wire->beginTransmission(m_addr); + m_wire->write(_MPR121_REG_SOFT_RESET); + m_wire->endTransmission(); + } + if (writeCallback) { + uint8_t data = 0; + writeCallback(m_addr, _MPR121_REG_SOFT_RESET, &data, 0); + } + delay(100); + // Reset Electrode Configuration to 0x00, Stop Mode + writeRegister(_MPR121_REG_ELECTRODE_CONFIG, 0x00); + delay(100); + + LOG_DEBUG("MPR121 Configure"); + // Set touch release thresholds + for (uint8_t i = 0; i < 12; i++) { + // Set touch threshold + writeRegister(_MPR121_REG_TOUCH_THRESHOLD + (i * 2), 15); + delay(20); + // Set release threshold + writeRegister(_MPR121_REG_RELEASE_THRESHOLD + (i * 2), 7); + delay(20); + } + // Configure filtering and baseline registers + writeRegister(_MPR121_REG_MAX_HALF_DELTA_RISING, 0x01); + delay(20); + writeRegister(_MPR121_REG_MAX_HALF_DELTA_FALLING, 0x01); + delay(20); + writeRegister(_MPR121_REG_NOISE_HALF_DELTA_RISING, 0x01); + delay(20); + writeRegister(_MPR121_REG_NOISE_HALF_DELTA_FALLING, 0x05); + delay(20); + writeRegister(_MPR121_REG_NOISE_HALF_DELTA_TOUCHED, 0x00); + delay(20); + writeRegister(_MPR121_REG_NOISE_COUNT_LIMIT_RISING, 0x0e); + delay(20); + writeRegister(_MPR121_REG_NOISE_COUNT_LIMIT_FALLING, 0x01); + delay(20); + writeRegister(_MPR121_REG_NOISE_COUNT_LIMIT_TOUCHED, 0x00); + delay(20); + writeRegister(_MPR121_REG_FILTER_DELAY_COUNT_RISING, 0x00); + delay(20); + writeRegister(_MPR121_REG_FILTER_DELAY_COUNT_FALLING, 0x00); + delay(20); + writeRegister(_MPR121_REG_FILTER_DELAY_COUNT_TOUCHED, 0x00); + delay(20); + // Set Debounce to 0x02 + writeRegister(_MPR121_REG_DEBOUNCE, 0x00); + delay(20); + // Set Filter1 itterations and discharge current 6x and 16uA respectively (0x10) + writeRegister(_MPR121_REG_CONFIG1, 0x10); + delay(20); + // Set CDT to 0.5us, Filter2 itterations to 4x, and Sample interval = 0 (0x20) + writeRegister(_MPR121_REG_CONFIG2, 0x20); + delay(20); + // Enter run mode by Seting partial filter calibration tracking, disable proximity detection, enable 12 channels + writeRegister(_MPR121_REG_ELECTRODE_CONFIG, + ECR_CALIBRATION_TRACK_FROM_PARTIAL_FILTER | ECR_PROXIMITY_DETECTION_OFF | ECR_TOUCH_DETECTION_12CH); + delay(100); + LOG_DEBUG("MPR121 Run"); + state = Idle; +} + +void MPR121Keyboard::attachInterrupt(uint8_t pin, void (*func)(void)) const +{ + pinMode(pin, INPUT_PULLUP); + ::attachInterrupt(digitalPinToInterrupt(pin), func, RISING); +} + +void MPR121Keyboard::detachInterrupt(uint8_t pin) const +{ + ::detachInterrupt(pin); +} + +uint8_t MPR121Keyboard::status() const +{ + return readRegister16(_MPR121_REG_KEY); +} + +uint8_t MPR121Keyboard::keyCount() const +{ + // Read the key register + uint16_t keyRegister = readRegister16(_MPR121_REG_KEY); + return keyCount(keyRegister); +} + +uint8_t MPR121Keyboard::keyCount(uint16_t value) const +{ + // Mask the first 12 bits + uint16_t buttonState = value & _KEY_MASK; + + // Count how many bits are set to 1 (i.e., how many buttons are pressed) + uint8_t numButtonsPressed = 0; + for (uint8_t i = 0; i < 12; ++i) { + if (buttonState & (1 << i)) { + numButtonsPressed++; + } + } + + return numButtonsPressed; +} + +bool MPR121Keyboard::hasEvent() +{ + return queue.length() > 0; +} + +void MPR121Keyboard::queueEvent(char next) +{ + if (next == MPR121_NONE) { + return; + } + queue.concat(next); +} + +char MPR121Keyboard::dequeueEvent() +{ + if (queue.length() < 1) { + return MPR121_NONE; + } + char next = queue.charAt(0); + queue.remove(0, 1); + return next; +} + +void MPR121Keyboard::trigger() +{ + // Intended to fire in response to an interrupt from the MPR121 or a longpress callback + // Only functional if not in Init state + if (state != Init) { + // Read the key register + uint16_t keyRegister = readRegister16(_MPR121_REG_KEY); + uint8_t keysPressed = keyCount(keyRegister); + if (keysPressed == 0) { + // No buttons pressed + if (state == Held) + released(); + state = Idle; + return; + } + if (keysPressed == 1) { + // No buttons pressed + if (state == Held || state == HeldLong) + held(keyRegister); + if (state == Idle) + pressed(keyRegister); + return; + } + if (keysPressed > 1) { + // Multipress + state = Busy; + return; + } + } else { + reset(); + } +} + +void MPR121Keyboard::pressed(uint16_t keyRegister) +{ + if (state == Init || state == Busy) { + return; + } + if (keyCount(keyRegister) != 1) { + LOG_DEBUG("Multipress"); + return; + } else { + LOG_DEBUG("Pressed"); + } + uint16_t buttonState = keyRegister & _KEY_MASK; + uint8_t next_pin = 0; + for (uint8_t i = 0; i < 12; ++i) { + if (buttonState & (1 << i)) { + next_pin = i; + } + } + uint8_t next_key = MPR121_KeyMap[next_pin]; + LOG_DEBUG("MPR121 Pin: %i Key: %i", next_pin, next_key); + uint32_t now = millis(); + int32_t tap_interval = now - last_tap; + if (tap_interval < 0) { + // long running, millis has overflowed. + last_tap = 0; + state = Busy; + return; + } + if (next_key != last_key || tap_interval > MULTI_TAP_THRESHOLD) { + char_idx = 0; + } else { + char_idx += 1; + } + last_key = next_key; + last_tap = now; + state = Held; + return; +} + +void MPR121Keyboard::held(uint16_t keyRegister) +{ + if (state == Init || state == Busy) { + return; + } + if (keyCount(keyRegister) != 1) { + return; + } + LOG_DEBUG("Held"); + uint16_t buttonState = keyRegister & _KEY_MASK; + uint8_t next_key = 0; + for (uint8_t i = 0; i < 12; ++i) { + if (buttonState & (1 << i)) { + next_key = MPR121_KeyMap[i]; + } + } + uint32_t now = millis(); + int32_t held_interval = now - last_tap; + if (held_interval < 0 || next_key != last_key) { + // long running, millis has overflowed, or a key has been switched quickly... + last_tap = 0; + state = Busy; + return; + } + if (held_interval > LONG_PRESS_THRESHOLD) { + // Set state to heldlong, send a longpress, and reset the timer... + state = HeldLong; // heldlong will allow this function to still fire, but prevent a "release" + queueEvent(MPR121_LongPressMap[last_key]); + last_tap = now; + LOG_DEBUG("Long Press Key: %i Map: %i", last_key, MPR121_LongPressMap[last_key]); + } + return; +} + +void MPR121Keyboard::released() +{ + if (state != Held) { + return; + } + // would clear longpress callback... later. + if (last_key < 0 || last_key > _NUM_KEYS) { // reset to idle if last_key out of bounds + last_key = -1; + state = Idle; + return; + } + LOG_DEBUG("Released"); + if (char_idx > 0 && TapMod[last_key] > 1) { + queueEvent(MPR121_BSP); + LOG_DEBUG("Multi Press, Backspace"); + } + queueEvent(MPR121_TapMap[last_key][(char_idx % TapMod[last_key])]); + LOG_DEBUG("Key Press: %i Index:%i if %i Map: %i", last_key, char_idx, TapMod[last_key], + MPR121_TapMap[last_key][(char_idx % TapMod[last_key])]); +} + +uint8_t MPR121Keyboard::readRegister8(uint8_t reg) const +{ + if (m_wire) { + m_wire->beginTransmission(m_addr); + m_wire->write(reg); + m_wire->endTransmission(); + + m_wire->requestFrom(m_addr, (uint8_t)1); + if (m_wire->available() < 1) + return 0; + + return m_wire->read(); + } + if (readCallback) { + uint8_t data; + readCallback(m_addr, reg, &data, 1); + return data; + } + return 0; +} + +uint16_t MPR121Keyboard::readRegister16(uint8_t reg) const +{ + uint8_t data[2] = {0}; + // uint8_t low = 0, high = 0; + if (m_wire) { + m_wire->beginTransmission(m_addr); + m_wire->write(reg); + m_wire->endTransmission(); + + m_wire->requestFrom(m_addr, (uint8_t)2); + if (m_wire->available() < 2) + return 0; + data[0] = m_wire->read(); + data[1] = m_wire->read(); + } + if (readCallback) { + readCallback(m_addr, reg, data, 2); + } + return (data[1] << 8) | data[0]; +} + +void MPR121Keyboard::writeRegister(uint8_t reg, uint8_t value) +{ + uint8_t data[2]; + data[0] = reg; + data[1] = value; + + if (m_wire) { + m_wire->beginTransmission(m_addr); + m_wire->write(data, sizeof(uint8_t) * 2); + m_wire->endTransmission(); + } + if (writeCallback) { + writeCallback(m_addr, data[0], &(data[1]), 1); + } +} diff --git a/src/input/MPR121Keyboard.h b/src/input/MPR121Keyboard.h new file mode 100644 index 000000000..6349750ce --- /dev/null +++ b/src/input/MPR121Keyboard.h @@ -0,0 +1,56 @@ +// Based on the BBQ10 Keyboard + +#include "concurrency/NotifiedWorkerThread.h" +#include "configuration.h" +#include +#include + +class MPR121Keyboard +{ + public: + typedef uint8_t (*i2c_com_fptr_t)(uint8_t dev_addr, uint8_t reg_addr, uint8_t *data, uint8_t len); + + enum MPR121States { Init = 0, Idle, Held, HeldLong, Busy }; + + MPR121States state; + + int8_t last_key; + uint32_t last_tap; + uint8_t char_idx; + + String queue; + + MPR121Keyboard(); + + void begin(uint8_t addr = MPR121_KB_ADDR, TwoWire *wire = &Wire); + + void begin(i2c_com_fptr_t r, i2c_com_fptr_t w, uint8_t addr = MPR121_KB_ADDR); + + void reset(void); + + void attachInterrupt(uint8_t pin, void (*func)(void)) const; + void detachInterrupt(uint8_t pin) const; + + void trigger(void); + void pressed(uint16_t value); + void held(uint16_t value); + void released(void); + + uint8_t status(void) const; + uint8_t keyCount(void) const; + uint8_t keyCount(uint16_t value) const; + + bool hasEvent(void); + char dequeueEvent(void); + void queueEvent(char); + + uint8_t readRegister8(uint8_t reg) const; + uint16_t readRegister16(uint8_t reg) const; + void writeRegister(uint8_t reg, uint8_t value); + + private: + TwoWire *m_wire; + uint8_t m_addr; + i2c_com_fptr_t readCallback; + i2c_com_fptr_t writeCallback; +}; \ No newline at end of file diff --git a/src/input/RotaryEncoderInterruptBase.cpp b/src/input/RotaryEncoderInterruptBase.cpp index 0b8e8325d..785d98ebe 100644 --- a/src/input/RotaryEncoderInterruptBase.cpp +++ b/src/input/RotaryEncoderInterruptBase.cpp @@ -28,7 +28,7 @@ void RotaryEncoderInterruptBase::init( this->rotaryLevelA = digitalRead(this->_pinA); this->rotaryLevelB = digitalRead(this->_pinB); - LOG_INFO("Rotary initialized (%d, %d, %d)\n", this->_pinA, this->_pinB, pinPress); + LOG_INFO("Rotary initialized (%d, %d, %d)", this->_pinA, this->_pinB, pinPress); } int32_t RotaryEncoderInterruptBase::runOnce() @@ -38,13 +38,13 @@ int32_t RotaryEncoderInterruptBase::runOnce() e.source = this->_originName; if (this->action == ROTARY_ACTION_PRESSED) { - LOG_DEBUG("Rotary event Press\n"); + LOG_DEBUG("Rotary event Press"); e.inputEvent = this->_eventPressed; } else if (this->action == ROTARY_ACTION_CW) { - LOG_DEBUG("Rotary event CW\n"); + LOG_DEBUG("Rotary event CW"); e.inputEvent = this->_eventCw; } else if (this->action == ROTARY_ACTION_CCW) { - LOG_DEBUG("Rotary event CCW\n"); + LOG_DEBUG("Rotary event CCW"); e.inputEvent = this->_eventCcw; } diff --git a/src/input/ScanAndSelect.cpp b/src/input/ScanAndSelect.cpp index 65ca7e332..d8767fab8 100644 --- a/src/input/ScanAndSelect.cpp +++ b/src/input/ScanAndSelect.cpp @@ -47,7 +47,7 @@ bool ScanAndSelectInput::init() // Connect our class to the canned message module inputBroker->registerSource(this); - LOG_INFO("Initialized 'Scan and Select' input for Canned Messages, using pin %d\n", pin); + LOG_INFO("Initialized 'Scan and Select' input for Canned Messages, using pin %d", pin); return true; // Init succeded } @@ -59,7 +59,7 @@ int32_t ScanAndSelectInput::runOnce() // If: "no messages added" alert screen currently shown if (alertingNoMessage) { // Dismiss the alert screen several seconds after it appears - if (now > alertingSinceMs + durationAlertMs) { + if (!Throttle::isWithinTimespanMs(alertingSinceMs, durationAlertMs)) { alertingNoMessage = false; screen->endAlert(); } @@ -74,9 +74,9 @@ int32_t ScanAndSelectInput::runOnce() // Existing press else { - // Duration enough for long press + // Longer than shortpress window // Long press not yet fired (prevent repeat firing while held) - if (!longPressFired && Throttle::isWithinTimespanMs(downSinceMs, durationLongMs)) { + if (!longPressFired && !Throttle::isWithinTimespanMs(downSinceMs, durationLongMs)) { longPressFired = true; longPress(); } @@ -91,7 +91,9 @@ int32_t ScanAndSelectInput::runOnce() // Button newly released // Long press event didn't already fire if (held && !longPressFired) { - // Duration enough for short press + // Duration within shortpress window + // - longer than durationShortPress (debounce) + // - shorter than durationLongPress if (!Throttle::isWithinTimespanMs(downSinceMs, durationShortMs)) { shortPress(); } diff --git a/src/input/SerialKeyboard.cpp b/src/input/SerialKeyboard.cpp index 4827e8995..8d0730418 100644 --- a/src/input/SerialKeyboard.cpp +++ b/src/input/SerialKeyboard.cpp @@ -52,7 +52,7 @@ int32_t SerialKeyboard::runOnce() digitalWrite(KB_LOAD, HIGH); digitalWrite(KB_CLK, LOW); prevKeys = 0b1111111111111111; - LOG_DEBUG("Serial Keyboard setup\n"); + LOG_DEBUG("Serial Keyboard setup"); } if (INPUTBROKER_SERIAL_TYPE == 1) { // Chatter V1.0 & V2.0 keypads diff --git a/src/input/TouchScreenBase.cpp b/src/input/TouchScreenBase.cpp index 2f361ac4c..a63203362 100644 --- a/src/input/TouchScreenBase.cpp +++ b/src/input/TouchScreenBase.cpp @@ -1,6 +1,10 @@ #include "TouchScreenBase.h" #include "main.h" +#if defined(RAK14014) && !defined(MESHTASTIC_EXCLUDE_CANNEDMESSAGES) +#include "modules/CannedMessageModule.h" +#endif + #ifndef TIME_LONG_PRESS #define TIME_LONG_PRESS 400 #endif @@ -23,7 +27,7 @@ TouchScreenBase::TouchScreenBase(const char *name, uint16_t width, uint16_t heig void TouchScreenBase::init(bool hasTouch) { if (hasTouch) { - LOG_INFO("TouchScreen initialized %d %d\n", TOUCH_THRESHOLD_X, TOUCH_THRESHOLD_Y); + LOG_INFO("TouchScreen initialized %d %d", TOUCH_THRESHOLD_X, TOUCH_THRESHOLD_Y); this->setInterval(100); } else { disable(); @@ -68,20 +72,20 @@ int32_t TouchScreenBase::runOnce() if (adx > ady && adx > TOUCH_THRESHOLD_X) { if (0 > dx) { // swipe right to left e.touchEvent = static_cast(TOUCH_ACTION_LEFT); - LOG_DEBUG("action SWIPE: right to left\n"); + LOG_DEBUG("action SWIPE: right to left"); } else { // swipe left to right e.touchEvent = static_cast(TOUCH_ACTION_RIGHT); - LOG_DEBUG("action SWIPE: left to right\n"); + LOG_DEBUG("action SWIPE: left to right"); } } // swipe vertical else if (ady > adx && ady > TOUCH_THRESHOLD_Y) { if (0 > dy) { // swipe bottom to top e.touchEvent = static_cast(TOUCH_ACTION_UP); - LOG_DEBUG("action SWIPE: bottom to top\n"); + LOG_DEBUG("action SWIPE: bottom to top"); } else { // swipe top to bottom e.touchEvent = static_cast(TOUCH_ACTION_DOWN); - LOG_DEBUG("action SWIPE: top to bottom\n"); + LOG_DEBUG("action SWIPE: top to bottom"); } } // tap @@ -90,7 +94,7 @@ int32_t TouchScreenBase::runOnce() if (_tapped) { _tapped = false; e.touchEvent = static_cast(TOUCH_ACTION_DOUBLE_TAP); - LOG_DEBUG("action DOUBLE TAP(%d/%d)\n", x, y); + LOG_DEBUG("action DOUBLE TAP(%d/%d)", x, y); } else { _tapped = true; } @@ -102,19 +106,37 @@ int32_t TouchScreenBase::runOnce() } _touchedOld = touched; +#if defined RAK14014 + // Speed up the processing speed of the keyboard in virtual keyboard mode + auto state = cannedMessageModule->getRunState(); + if (state == CANNED_MESSAGE_RUN_STATE_FREETEXT) { + if (_tapped) { + _tapped = false; + e.touchEvent = static_cast(TOUCH_ACTION_TAP); + LOG_DEBUG("action TAP(%d/%d)\n", _last_x, _last_y); + } + } else { + if (_tapped && (time_t(millis()) - _start) > TIME_LONG_PRESS - 50) { + _tapped = false; + e.touchEvent = static_cast(TOUCH_ACTION_TAP); + LOG_DEBUG("action TAP(%d/%d)\n", _last_x, _last_y); + } + } +#else // fire TAP event when no 2nd tap occured within time if (_tapped && (time_t(millis()) - _start) > TIME_LONG_PRESS - 50) { _tapped = false; e.touchEvent = static_cast(TOUCH_ACTION_TAP); - LOG_DEBUG("action TAP(%d/%d)\n", _last_x, _last_y); + LOG_DEBUG("action TAP(%d/%d)", _last_x, _last_y); } +#endif // fire LONG_PRESS event without the need for release if (touched && (time_t(millis()) - _start) > TIME_LONG_PRESS) { // tricky: prevent reoccurring events and another touch event when releasing _start = millis() + 30000; e.touchEvent = static_cast(TOUCH_ACTION_LONG_PRESS); - LOG_DEBUG("action LONG PRESS(%d/%d)\n", _last_x, _last_y); + LOG_DEBUG("action LONG PRESS(%d/%d)", _last_x, _last_y); } if (e.touchEvent != TOUCH_ACTION_NONE) { diff --git a/src/input/TrackballInterruptBase.cpp b/src/input/TrackballInterruptBase.cpp index 71cd130cc..e35da3622 100644 --- a/src/input/TrackballInterruptBase.cpp +++ b/src/input/TrackballInterruptBase.cpp @@ -30,7 +30,7 @@ void TrackballInterruptBase::init(uint8_t pinDown, uint8_t pinUp, uint8_t pinLef attachInterrupt(this->_pinLeft, onIntLeft, RISING); attachInterrupt(this->_pinRight, onIntRight, RISING); - LOG_DEBUG("Trackball GPIO initialized (%d, %d, %d, %d, %d)\n", this->_pinUp, this->_pinDown, this->_pinLeft, this->_pinRight, + LOG_DEBUG("Trackball GPIO initialized (%d, %d, %d, %d, %d)", this->_pinUp, this->_pinDown, this->_pinLeft, this->_pinRight, pinPress); this->setInterval(100); @@ -42,19 +42,19 @@ int32_t TrackballInterruptBase::runOnce() e.inputEvent = meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_NONE; if (this->action == TB_ACTION_PRESSED) { - // LOG_DEBUG("Trackball event Press\n"); + // LOG_DEBUG("Trackball event Press"); e.inputEvent = this->_eventPressed; } else if (this->action == TB_ACTION_UP) { - // LOG_DEBUG("Trackball event UP\n"); + // LOG_DEBUG("Trackball event UP"); e.inputEvent = this->_eventUp; } else if (this->action == TB_ACTION_DOWN) { - // LOG_DEBUG("Trackball event DOWN\n"); + // LOG_DEBUG("Trackball event DOWN"); e.inputEvent = this->_eventDown; } else if (this->action == TB_ACTION_LEFT) { - // LOG_DEBUG("Trackball event LEFT\n"); + // LOG_DEBUG("Trackball event LEFT"); e.inputEvent = this->_eventLeft; } else if (this->action == TB_ACTION_RIGHT) { - // LOG_DEBUG("Trackball event RIGHT\n"); + // LOG_DEBUG("Trackball event RIGHT"); e.inputEvent = this->_eventRight; } diff --git a/src/input/UpDownInterruptBase.cpp b/src/input/UpDownInterruptBase.cpp index b1f83c56b..979489c57 100644 --- a/src/input/UpDownInterruptBase.cpp +++ b/src/input/UpDownInterruptBase.cpp @@ -23,7 +23,7 @@ void UpDownInterruptBase::init(uint8_t pinDown, uint8_t pinUp, uint8_t pinPress, attachInterrupt(this->_pinDown, onIntDown, RISING); attachInterrupt(this->_pinUp, onIntUp, RISING); - LOG_DEBUG("Up/down/press GPIO initialized (%d, %d, %d)\n", this->_pinUp, this->_pinDown, pinPress); + LOG_DEBUG("Up/down/press GPIO initialized (%d, %d, %d)", this->_pinUp, this->_pinDown, pinPress); this->setInterval(100); } @@ -34,13 +34,13 @@ int32_t UpDownInterruptBase::runOnce() e.inputEvent = meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_NONE; if (this->action == UPDOWN_ACTION_PRESSED) { - LOG_DEBUG("GPIO event Press\n"); + LOG_DEBUG("GPIO event Press"); e.inputEvent = this->_eventPressed; } else if (this->action == UPDOWN_ACTION_UP) { - LOG_DEBUG("GPIO event Up\n"); + LOG_DEBUG("GPIO event Up"); e.inputEvent = this->_eventUp; } else if (this->action == UPDOWN_ACTION_DOWN) { - LOG_DEBUG("GPIO event Down\n"); + LOG_DEBUG("GPIO event Down"); e.inputEvent = this->_eventDown; } diff --git a/src/input/cardKbI2cImpl.cpp b/src/input/cardKbI2cImpl.cpp index f1df6b137..eb9b07d6e 100644 --- a/src/input/cardKbI2cImpl.cpp +++ b/src/input/cardKbI2cImpl.cpp @@ -9,11 +9,11 @@ CardKbI2cImpl::CardKbI2cImpl() : KbI2cBase("cardKB") {} void CardKbI2cImpl::init() { -#if !MESHTASTIC_EXCLUDE_I2C && !defined(ARCH_PORTDUINO) +#if !MESHTASTIC_EXCLUDE_I2C && !defined(ARCH_PORTDUINO) && !defined(I2C_NO_RESCAN) if (cardkb_found.address == 0x00) { - LOG_DEBUG("Rescanning for I2C keyboard\n"); - uint8_t i2caddr_scan[] = {CARDKB_ADDR, TDECK_KB_ADDR, BBQ10_KB_ADDR}; - uint8_t i2caddr_asize = 3; + LOG_DEBUG("Rescan for I2C keyboard"); + uint8_t i2caddr_scan[] = {CARDKB_ADDR, TDECK_KB_ADDR, BBQ10_KB_ADDR, MPR121_KB_ADDR}; + uint8_t i2caddr_asize = 4; auto i2cScanner = std::unique_ptr(new ScanI2CTwoWire()); #if WIRE_INTERFACES_COUNT == 2 @@ -39,15 +39,21 @@ void CardKbI2cImpl::init() // assign an arbitrary value to distinguish from other models kb_model = 0x11; break; + case ScanI2C::DeviceType::MPR121KB: + // assign an arbitrary value to distinguish from other models + kb_model = 0x37; + break; default: // use this as default since it's also just zero - LOG_WARN("kb_info.type is unknown(0x%02x), setting kb_model=0x00\n", kb_info.type); + LOG_WARN("kb_info.type is unknown(0x%02x), setting kb_model=0x00", kb_info.type); kb_model = 0x00; } } if (cardkb_found.address == 0x00) { disable(); return; + } else { + LOG_DEBUG("Keyboard Type: 0x%02x Model: 0x%02x Address: 0x%02x", kb_info.type, kb_model, cardkb_found.address); } } #else diff --git a/src/input/kbI2cBase.cpp b/src/input/kbI2cBase.cpp index 8b201cd22..9b1a27745 100644 --- a/src/input/kbI2cBase.cpp +++ b/src/input/kbI2cBase.cpp @@ -34,21 +34,27 @@ int32_t KbI2cBase::runOnce() switch (cardkb_found.port) { case ScanI2C::WIRE1: #if WIRE_INTERFACES_COUNT == 2 - LOG_DEBUG("Using I2C Bus 1 (the second one)\n"); + LOG_DEBUG("Use I2C Bus 1 (the second one)"); i2cBus = &Wire1; if (cardkb_found.address == BBQ10_KB_ADDR) { Q10keyboard.begin(BBQ10_KB_ADDR, &Wire1); Q10keyboard.setBacklight(0); } + if (cardkb_found.address == MPR121_KB_ADDR) { + MPRkeyboard.begin(MPR121_KB_ADDR, &Wire1); + } break; #endif case ScanI2C::WIRE: - LOG_DEBUG("Using I2C Bus 0 (the first one)\n"); + LOG_DEBUG("Use I2C Bus 0 (the first one)"); i2cBus = &Wire; if (cardkb_found.address == BBQ10_KB_ADDR) { Q10keyboard.begin(BBQ10_KB_ADDR, &Wire); Q10keyboard.setBacklight(0); } + if (cardkb_found.address == MPR121_KB_ADDR) { + MPRkeyboard.begin(MPR121_KB_ADDR, &Wire); + } break; case ScanI2C::NO_I2C: default: @@ -157,6 +163,69 @@ int32_t KbI2cBase::runOnce() } break; } + case 0x37: { // MPR121 + MPRkeyboard.trigger(); + InputEvent e; + + while (MPRkeyboard.hasEvent()) { + char nextEvent = MPRkeyboard.dequeueEvent(); + e.inputEvent = ANYKEY; + e.kbchar = 0x00; + e.source = this->_originName; + switch (nextEvent) { + case 0x00: // MPR121_NONE + e.inputEvent = meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_NONE; + e.kbchar = 0x00; + break; + case 0x90: // MPR121_REBOOT + e.inputEvent = ANYKEY; + e.kbchar = INPUT_BROKER_MSG_REBOOT; + break; + case 0xb4: // MPR121_LEFT + e.inputEvent = meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_LEFT; + e.kbchar = 0x00; + break; + case 0xb5: // MPR121_UP + e.inputEvent = meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_UP; + e.kbchar = 0x00; + break; + case 0xb6: // MPR121_DOWN + e.inputEvent = meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_DOWN; + e.kbchar = 0x00; + break; + case 0xb7: // MPR121_RIGHT + e.inputEvent = meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_RIGHT; + e.kbchar = 0x00; + break; + case 0x1b: // MPR121_ESC + e.inputEvent = meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_CANCEL; + e.kbchar = 0x1b; + break; + case 0x08: // MPR121_BSP + e.inputEvent = meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_BACK; + e.kbchar = 0x08; + break; + case 0x0d: // MPR121_SELECT + e.inputEvent = meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_SELECT; + e.kbchar = 0x0d; + break; + default: + if (nextEvent > 127) { + e.inputEvent = meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_NONE; + e.kbchar = 0x00; + break; + } + e.inputEvent = ANYKEY; + e.kbchar = nextEvent; + break; + } + if (e.inputEvent != meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_NONE) { + LOG_DEBUG("MP121 Notifying: %i Char: %i", e.inputEvent, e.kbchar); + this->notifyObservers(&e); + } + } + break; + } case 0x02: { // RAK14004 uint8_t rDataBuf[8] = {0}; @@ -171,7 +240,7 @@ int32_t KbI2cBase::runOnce() } } if (PrintDataBuf != 0) { - LOG_DEBUG("RAK14004 key 0x%x pressed\n", PrintDataBuf); + LOG_DEBUG("RAK14004 key 0x%x pressed", PrintDataBuf); InputEvent e; e.inputEvent = MATRIXKEY; e.source = this->_originName; @@ -326,7 +395,7 @@ int32_t KbI2cBase::runOnce() break; } default: - LOG_WARN("Unknown kb_model 0x%02x\n", kb_model); + LOG_WARN("Unknown kb_model 0x%02x", kb_model); } return 300; } \ No newline at end of file diff --git a/src/input/kbI2cBase.h b/src/input/kbI2cBase.h index 35b9b0901..dc2414fc0 100644 --- a/src/input/kbI2cBase.h +++ b/src/input/kbI2cBase.h @@ -2,6 +2,7 @@ #include "BBQ10Keyboard.h" #include "InputBroker.h" +#include "MPR121Keyboard.h" #include "Wire.h" #include "concurrency/OSThread.h" @@ -19,5 +20,6 @@ class KbI2cBase : public Observable, public concurrency::OST TwoWire *i2cBus = 0; BBQ10Keyboard Q10keyboard; + MPR121Keyboard MPRkeyboard; bool is_sym = false; -}; +}; \ No newline at end of file diff --git a/src/input/kbMatrixBase.cpp b/src/input/kbMatrixBase.cpp index 823bfb629..51815b525 100644 --- a/src/input/kbMatrixBase.cpp +++ b/src/input/kbMatrixBase.cpp @@ -70,7 +70,7 @@ int32_t KbMatrixBase::runOnce() // debounce if (key != prevkey) { if (key != 0) { - LOG_DEBUG("Key 0x%x pressed\n", key); + LOG_DEBUG("Key 0x%x pressed", key); // reset shift now that we have a keypress InputEvent e; e.inputEvent = meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_NONE; @@ -122,7 +122,7 @@ int32_t KbMatrixBase::runOnce() } } else { - LOG_WARN("Unknown kb_model 0x%02x\n", INPUTBROKER_MATRIX_TYPE); + LOG_WARN("Unknown kb_model 0x%02x", INPUTBROKER_MATRIX_TYPE); return disable(); } return 50; // Keyscan every 50msec to avoid key bounce diff --git a/src/main.cpp b/src/main.cpp index bd88f47aa..9fccca8d1 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -27,7 +27,6 @@ #include "detect/ScanI2CTwoWire.h" #include #endif -#include "detect/axpDebug.h" #include "detect/einkScan.h" #include "graphics/RAKled.h" #include "graphics/Screen.h" @@ -152,10 +151,6 @@ ScanI2C::DeviceAddress accelerometer_found = ScanI2C::ADDRESS_NONE; // The I2C address of the RGB LED (if found) ScanI2C::FoundDevice rgb_found = ScanI2C::FoundDevice(ScanI2C::DeviceType::NONE, ScanI2C::ADDRESS_NONE); -#if !defined(ARCH_PORTDUINO) && !defined(ARCH_STM32WL) -ATECCX08A atecc; -#endif - #ifdef T_WATCH_S3 Adafruit_DRV2605 drv; #endif @@ -239,7 +234,7 @@ void lateInitVariant() {} */ void printInfo() { - LOG_INFO("S:B:%d,%s\n", HW_VENDOR, optstr(APP_VERSION)); + LOG_INFO("S:B:%d,%s", HW_VENDOR, optstr(APP_VERSION)); } #ifndef PIO_UNIT_TESTING void setup() @@ -268,17 +263,22 @@ void setup() #ifdef DEBUG_PORT consoleInit(); // Set serial baud rate and init our mesh console #endif + +#ifdef UNPHONE + unphone.printStore(); +#endif + #if ARCH_PORTDUINO struct timeval tv; tv.tv_sec = time(NULL); tv.tv_usec = 0; perhapsSetRTC(RTCQualityNTP, &tv); #endif - powerMonInit(); + powerMonInit(); serialSinceMsec = millis(); - LOG_INFO("\n\n//\\ E S H T /\\ S T / C\n\n"); + LOG_INFO("\n\n//\\ E S H T /\\ S T / C\n"); initDeepSleep(); @@ -325,7 +325,7 @@ void setup() #ifdef PERIPHERAL_WARMUP_MS // Some peripherals may require additional time to stabilize after power is connected // e.g. I2C on Heltec Vision Master - LOG_INFO("Waiting for peripherals to stabilize\n"); + LOG_INFO("Wait for peripherals to stabilize"); delay(PERIPHERAL_WARMUP_MS); #endif @@ -386,10 +386,10 @@ void setup() Wire.begin(I2C_SDA, I2C_SCL); #elif defined(ARCH_PORTDUINO) if (settingsStrings[i2cdev] != "") { - LOG_INFO("Using %s as I2C device.\n", settingsStrings[i2cdev].c_str()); + LOG_INFO("Use %s as I2C device", settingsStrings[i2cdev].c_str()); Wire.begin(settingsStrings[i2cdev].c_str()); } else { - LOG_INFO("No I2C device configured, skipping.\n"); + LOG_INFO("No I2C device configured, Skip"); } #elif HAS_WIRE Wire.begin(); @@ -432,7 +432,7 @@ void setup() // accessories auto i2cScanner = std::unique_ptr(new ScanI2CTwoWire()); #if HAS_WIRE - LOG_INFO("Scanning for i2c devices...\n"); + LOG_INFO("Scan for i2c devices"); #endif #if defined(I2C_SDA1) && defined(ARCH_RP2040) @@ -457,7 +457,7 @@ void setup() i2cScanner->scanPort(ScanI2C::I2CPort::WIRE); #elif defined(ARCH_PORTDUINO) if (settingsStrings[i2cdev] != "") { - LOG_INFO("Scanning for i2c devices...\n"); + LOG_INFO("Scan for i2c devices"); i2cScanner->scanPort(ScanI2C::I2CPort::WIRE); } #elif HAS_WIRE @@ -466,9 +466,9 @@ void setup() auto i2cCount = i2cScanner->countDevices(); if (i2cCount == 0) { - LOG_INFO("No I2C devices found\n"); + LOG_INFO("No I2C devices found"); } else { - LOG_INFO("%i I2C devices found\n", i2cCount); + LOG_INFO("%i I2C devices found", i2cCount); #ifdef SENSOR_GPS_CONFLICT sensor_detected = true; #endif @@ -524,9 +524,13 @@ void setup() // assign an arbitrary value to distinguish from other models kb_model = 0x11; break; + case ScanI2C::DeviceType::MPR121KB: + // assign an arbitrary value to distinguish from other models + kb_model = 0x37; + break; default: // use this as default since it's also just zero - LOG_WARN("kb_info.type is unknown(0x%02x), setting kb_model=0x00\n", kb_info.type); + LOG_WARN("kb_info.type is unknown(0x%02x), setting kb_model=0x00", kb_info.type); kb_model = 0x00; } } @@ -562,51 +566,40 @@ void setup() #if !defined(ARCH_PORTDUINO) && !defined(ARCH_STM32WL) auto acc_info = i2cScanner->firstAccelerometer(); accelerometer_found = acc_info.type != ScanI2C::DeviceType::NONE ? acc_info.address : accelerometer_found; - LOG_DEBUG("acc_info = %i\n", acc_info.type); + LOG_DEBUG("acc_info = %i", acc_info.type); #endif -#define STRING(S) #S - -#define SCANNER_TO_SENSORS_MAP(SCANNER_T, PB_T) \ - { \ - auto found = i2cScanner->find(SCANNER_T); \ - if (found.type != ScanI2C::DeviceType::NONE) { \ - nodeTelemetrySensorsMap[PB_T].first = found.address.address; \ - nodeTelemetrySensorsMap[PB_T].second = i2cScanner->fetchI2CBus(found.address); \ - LOG_DEBUG("found i2c sensor %s\n", STRING(PB_T)); \ - } \ - } - - SCANNER_TO_SENSORS_MAP(ScanI2C::DeviceType::BME_680, meshtastic_TelemetrySensorType_BME680) - SCANNER_TO_SENSORS_MAP(ScanI2C::DeviceType::BME_280, meshtastic_TelemetrySensorType_BME280) - SCANNER_TO_SENSORS_MAP(ScanI2C::DeviceType::BMP_280, meshtastic_TelemetrySensorType_BMP280) - SCANNER_TO_SENSORS_MAP(ScanI2C::DeviceType::BMP_3XX, meshtastic_TelemetrySensorType_BMP3XX) - SCANNER_TO_SENSORS_MAP(ScanI2C::DeviceType::BMP_085, meshtastic_TelemetrySensorType_BMP085) - SCANNER_TO_SENSORS_MAP(ScanI2C::DeviceType::INA260, meshtastic_TelemetrySensorType_INA260) - SCANNER_TO_SENSORS_MAP(ScanI2C::DeviceType::INA219, meshtastic_TelemetrySensorType_INA219) - SCANNER_TO_SENSORS_MAP(ScanI2C::DeviceType::INA3221, meshtastic_TelemetrySensorType_INA3221) - SCANNER_TO_SENSORS_MAP(ScanI2C::DeviceType::MAX17048, meshtastic_TelemetrySensorType_MAX17048) - SCANNER_TO_SENSORS_MAP(ScanI2C::DeviceType::MCP9808, meshtastic_TelemetrySensorType_MCP9808) - SCANNER_TO_SENSORS_MAP(ScanI2C::DeviceType::MCP9808, meshtastic_TelemetrySensorType_MCP9808) - SCANNER_TO_SENSORS_MAP(ScanI2C::DeviceType::SHT31, meshtastic_TelemetrySensorType_SHT31) - SCANNER_TO_SENSORS_MAP(ScanI2C::DeviceType::SHTC3, meshtastic_TelemetrySensorType_SHTC3) - SCANNER_TO_SENSORS_MAP(ScanI2C::DeviceType::LPS22HB, meshtastic_TelemetrySensorType_LPS22) - SCANNER_TO_SENSORS_MAP(ScanI2C::DeviceType::QMC6310, meshtastic_TelemetrySensorType_QMC6310) - SCANNER_TO_SENSORS_MAP(ScanI2C::DeviceType::QMI8658, meshtastic_TelemetrySensorType_QMI8658) - SCANNER_TO_SENSORS_MAP(ScanI2C::DeviceType::QMC5883L, meshtastic_TelemetrySensorType_QMC5883L) - SCANNER_TO_SENSORS_MAP(ScanI2C::DeviceType::HMC5883L, meshtastic_TelemetrySensorType_QMC5883L) - SCANNER_TO_SENSORS_MAP(ScanI2C::DeviceType::PMSA0031, meshtastic_TelemetrySensorType_PMSA003I) - SCANNER_TO_SENSORS_MAP(ScanI2C::DeviceType::RCWL9620, meshtastic_TelemetrySensorType_RCWL9620) - SCANNER_TO_SENSORS_MAP(ScanI2C::DeviceType::VEML7700, meshtastic_TelemetrySensorType_VEML7700) - SCANNER_TO_SENSORS_MAP(ScanI2C::DeviceType::TSL2591, meshtastic_TelemetrySensorType_TSL25911FN) - SCANNER_TO_SENSORS_MAP(ScanI2C::DeviceType::OPT3001, meshtastic_TelemetrySensorType_OPT3001) - SCANNER_TO_SENSORS_MAP(ScanI2C::DeviceType::MLX90632, meshtastic_TelemetrySensorType_MLX90632) - SCANNER_TO_SENSORS_MAP(ScanI2C::DeviceType::MLX90614, meshtastic_TelemetrySensorType_MLX90614) - SCANNER_TO_SENSORS_MAP(ScanI2C::DeviceType::SHT4X, meshtastic_TelemetrySensorType_SHT4X) - SCANNER_TO_SENSORS_MAP(ScanI2C::DeviceType::AHT10, meshtastic_TelemetrySensorType_AHT10) - SCANNER_TO_SENSORS_MAP(ScanI2C::DeviceType::DFROBOT_LARK, meshtastic_TelemetrySensorType_DFROBOT_LARK) - SCANNER_TO_SENSORS_MAP(ScanI2C::DeviceType::ICM20948, meshtastic_TelemetrySensorType_ICM20948) - SCANNER_TO_SENSORS_MAP(ScanI2C::DeviceType::MAX30102, meshtastic_TelemetrySensorType_MAX30102) + scannerToSensorsMap(i2cScanner, ScanI2C::DeviceType::BME_680, meshtastic_TelemetrySensorType_BME680); + scannerToSensorsMap(i2cScanner, ScanI2C::DeviceType::BME_280, meshtastic_TelemetrySensorType_BME280); + scannerToSensorsMap(i2cScanner, ScanI2C::DeviceType::BMP_280, meshtastic_TelemetrySensorType_BMP280); + scannerToSensorsMap(i2cScanner, ScanI2C::DeviceType::BMP_3XX, meshtastic_TelemetrySensorType_BMP3XX); + scannerToSensorsMap(i2cScanner, ScanI2C::DeviceType::BMP_085, meshtastic_TelemetrySensorType_BMP085); + scannerToSensorsMap(i2cScanner, ScanI2C::DeviceType::INA260, meshtastic_TelemetrySensorType_INA260); + scannerToSensorsMap(i2cScanner, ScanI2C::DeviceType::INA219, meshtastic_TelemetrySensorType_INA219); + scannerToSensorsMap(i2cScanner, ScanI2C::DeviceType::INA3221, meshtastic_TelemetrySensorType_INA3221); + scannerToSensorsMap(i2cScanner, ScanI2C::DeviceType::MAX17048, meshtastic_TelemetrySensorType_MAX17048); + scannerToSensorsMap(i2cScanner, ScanI2C::DeviceType::MCP9808, meshtastic_TelemetrySensorType_MCP9808); + scannerToSensorsMap(i2cScanner, ScanI2C::DeviceType::MCP9808, meshtastic_TelemetrySensorType_MCP9808); + scannerToSensorsMap(i2cScanner, ScanI2C::DeviceType::SHT31, meshtastic_TelemetrySensorType_SHT31); + scannerToSensorsMap(i2cScanner, ScanI2C::DeviceType::SHTC3, meshtastic_TelemetrySensorType_SHTC3); + scannerToSensorsMap(i2cScanner, ScanI2C::DeviceType::LPS22HB, meshtastic_TelemetrySensorType_LPS22); + scannerToSensorsMap(i2cScanner, ScanI2C::DeviceType::QMC6310, meshtastic_TelemetrySensorType_QMC6310); + scannerToSensorsMap(i2cScanner, ScanI2C::DeviceType::QMI8658, meshtastic_TelemetrySensorType_QMI8658); + scannerToSensorsMap(i2cScanner, ScanI2C::DeviceType::QMC5883L, meshtastic_TelemetrySensorType_QMC5883L); + scannerToSensorsMap(i2cScanner, ScanI2C::DeviceType::HMC5883L, meshtastic_TelemetrySensorType_QMC5883L); + scannerToSensorsMap(i2cScanner, ScanI2C::DeviceType::PMSA0031, meshtastic_TelemetrySensorType_PMSA003I); + scannerToSensorsMap(i2cScanner, ScanI2C::DeviceType::RCWL9620, meshtastic_TelemetrySensorType_RCWL9620); + scannerToSensorsMap(i2cScanner, ScanI2C::DeviceType::VEML7700, meshtastic_TelemetrySensorType_VEML7700); + scannerToSensorsMap(i2cScanner, ScanI2C::DeviceType::TSL2591, meshtastic_TelemetrySensorType_TSL25911FN); + scannerToSensorsMap(i2cScanner, ScanI2C::DeviceType::OPT3001, meshtastic_TelemetrySensorType_OPT3001); + scannerToSensorsMap(i2cScanner, ScanI2C::DeviceType::MLX90632, meshtastic_TelemetrySensorType_MLX90632); + scannerToSensorsMap(i2cScanner, ScanI2C::DeviceType::MLX90614, meshtastic_TelemetrySensorType_MLX90614); + scannerToSensorsMap(i2cScanner, ScanI2C::DeviceType::SHT4X, meshtastic_TelemetrySensorType_SHT4X); + scannerToSensorsMap(i2cScanner, ScanI2C::DeviceType::AHT10, meshtastic_TelemetrySensorType_AHT10); + scannerToSensorsMap(i2cScanner, ScanI2C::DeviceType::DFROBOT_LARK, meshtastic_TelemetrySensorType_DFROBOT_LARK); + scannerToSensorsMap(i2cScanner, ScanI2C::DeviceType::ICM20948, meshtastic_TelemetrySensorType_ICM20948); + scannerToSensorsMap(i2cScanner, ScanI2C::DeviceType::MAX30102, meshtastic_TelemetrySensorType_MAX30102); + scannerToSensorsMap(i2cScanner, ScanI2C::DeviceType::CGRADSENS, meshtastic_TelemetrySensorType_RADSENS); i2cScanner.reset(); #endif @@ -625,7 +618,7 @@ void setup() // Hello printInfo(); #ifdef BUILD_EPOCH - LOG_INFO("Build timestamp: %ld\n", BUILD_EPOCH); + LOG_INFO("Build timestamp: %ld", BUILD_EPOCH); #endif #ifdef ARCH_ESP32 @@ -640,6 +633,8 @@ void setup() rp2040Setup(); #endif + initSPI(); // needed here before reading from littleFS + // We do this as early as possible because this loads preferences from flash // but we need to do this after main cpu init (esp32setup), because we need the random seed set nodeDB = new NodeDB; @@ -662,7 +657,7 @@ void setup() if (config.power.is_power_saving == true && IS_ONE_OF(config.device.role, meshtastic_Config_DeviceConfig_Role_TRACKER, meshtastic_Config_DeviceConfig_Role_TAK_TRACKER, meshtastic_Config_DeviceConfig_Role_SENSOR)) - LOG_DEBUG("Tracker/Sensor: Skipping start melody\n"); + LOG_DEBUG("Tracker/Sensor: Skip start melody"); else playStartMelody(); @@ -703,7 +698,6 @@ void setup() #endif // Init our SPI controller (must be before screen and lora) - initSPI(); #ifdef ARCH_RP2040 #ifdef HW_SPI1_DEVICE SPI1.setSCK(LORA_SCK); @@ -723,7 +717,7 @@ void setup() #else // ESP32 SPI.begin(LORA_SCK, LORA_MISO, LORA_MOSI, LORA_CS); - LOG_DEBUG("SPI.begin(SCK=%d, MISO=%d, MOSI=%d, NSS=%d)\n", LORA_SCK, LORA_MISO, LORA_MOSI, LORA_CS); + LOG_DEBUG("SPI.begin(SCK=%d, MISO=%d, MOSI=%d, NSS=%d)", LORA_SCK, LORA_MISO, LORA_MOSI, LORA_CS); SPI.setFrequency(4000000); #endif @@ -732,9 +726,9 @@ void setup() // setup TZ prior to time actions. #if !MESHTASTIC_EXCLUDE_TZ - LOG_DEBUG("Using compiled/slipstreamed %s\n", slipstreamTZString); // important, removing this clobbers our magic string + LOG_DEBUG("Use compiled/slipstreamed %s", slipstreamTZString); // important, removing this clobbers our magic string if (*config.device.tzdef && config.device.tzdef[0] != 0) { - LOG_DEBUG("Saved TZ: %s \n", config.device.tzdef); + LOG_DEBUG("Saved TZ: %s ", config.device.tzdef); setenv("TZ", config.device.tzdef, 1); } else { if (strncmp((const char *)slipstreamTZString, "tzpl", 4) == 0) { @@ -745,7 +739,7 @@ void setup() } } tzset(); - LOG_DEBUG("Set Timezone to %s\n", getenv("TZ")); + LOG_DEBUG("Set Timezone to %s", getenv("TZ")); #endif readFromRTC(); // read the main CPU RTC at first (in case we can't get GPS time) @@ -762,7 +756,7 @@ void setup() if (gps) { gpsStatus->observe(&gps->newStatus); } else { - LOG_DEBUG("Running without GPS.\n"); + LOG_DEBUG("Run without GPS"); } } } @@ -775,7 +769,7 @@ void setup() nodeStatus->observe(&nodeDB->newStatus); #ifdef HAS_I2S - LOG_DEBUG("Starting audio thread\n"); + LOG_DEBUG("Start audio thread"); audioThread = new AudioThread(); #endif service = new MeshService(); @@ -822,62 +816,65 @@ void setup() #ifdef ARCH_PORTDUINO if (settingsMap[use_sx1262]) { if (!rIf) { - LOG_DEBUG("Attempting to activate sx1262 radio on SPI port %s\n", settingsStrings[spidev].c_str()); + LOG_DEBUG("Activate sx1262 radio on SPI port %s", settingsStrings[spidev].c_str()); Ch341Hal *RadioLibHAL = new Ch341Hal(0); +/* LockingArduinoHal *RadioLibHAL = + new LockingArduinoHal(SPI, spiSettings, (settingsMap[ch341Quirk] ? settingsMap[busy] : RADIOLIB_NC)); +*/ rIf = new SX1262Interface((LockingArduinoHal *)RadioLibHAL, settingsMap[cs], settingsMap[irq], settingsMap[reset], settingsMap[busy]); if (!rIf->init()) { - LOG_ERROR("Failed to find SX1262 radio\n"); + LOG_WARN("No SX1262 radio"); delete rIf; exit(EXIT_FAILURE); } else { - LOG_INFO("SX1262 Radio init succeeded, using SX1262 radio\n"); + LOG_INFO("SX1262 init success"); } } } else if (settingsMap[use_rf95]) { if (!rIf) { - LOG_DEBUG("Attempting to activate rf95 radio on SPI port %s\n", settingsStrings[spidev].c_str()); + LOG_DEBUG("Activate rf95 radio on SPI port %s", settingsStrings[spidev].c_str()); LockingArduinoHal *RadioLibHAL = new LockingArduinoHal(SPI, spiSettings, (settingsMap[ch341Quirk] ? settingsMap[busy] : RADIOLIB_NC)); rIf = new RF95Interface((LockingArduinoHal *)RadioLibHAL, settingsMap[cs], settingsMap[irq], settingsMap[reset], settingsMap[busy]); if (!rIf->init()) { - LOG_ERROR("Failed to find RF95 radio\n"); + LOG_WARN("No RF95 radio"); delete rIf; rIf = NULL; exit(EXIT_FAILURE); } else { - LOG_INFO("RF95 Radio init succeeded, using RF95 radio\n"); + LOG_INFO("RF95 init success"); } } } else if (settingsMap[use_sx1280]) { if (!rIf) { - LOG_DEBUG("Attempting to activate sx1280 radio on SPI port %s\n", settingsStrings[spidev].c_str()); + LOG_DEBUG("Activate sx1280 radio on SPI port %s", settingsStrings[spidev].c_str()); LockingArduinoHal *RadioLibHAL = new LockingArduinoHal(SPI, spiSettings); rIf = new SX1280Interface((LockingArduinoHal *)RadioLibHAL, settingsMap[cs], settingsMap[irq], settingsMap[reset], settingsMap[busy]); if (!rIf->init()) { - LOG_ERROR("Failed to find SX1280 radio\n"); + LOG_WARN("No SX1280 radio"); delete rIf; rIf = NULL; exit(EXIT_FAILURE); } else { - LOG_INFO("SX1280 Radio init succeeded, using SX1280 radio\n"); + LOG_INFO("SX1280 init success"); } } } else if (settingsMap[use_sx1268]) { if (!rIf) { - LOG_DEBUG("Attempting to activate sx1268 radio on SPI port %s\n", settingsStrings[spidev].c_str()); + LOG_DEBUG("Activate sx1268 radio on SPI port %s", settingsStrings[spidev].c_str()); LockingArduinoHal *RadioLibHAL = new LockingArduinoHal(SPI, spiSettings); rIf = new SX1268Interface((LockingArduinoHal *)RadioLibHAL, settingsMap[cs], settingsMap[irq], settingsMap[reset], settingsMap[busy]); if (!rIf->init()) { - LOG_ERROR("Failed to find SX1268 radio\n"); + LOG_WARN("No SX1268 radio"); delete rIf; rIf = NULL; exit(EXIT_FAILURE); } else { - LOG_INFO("SX1268 Radio init succeeded, using SX1268 radio\n"); + LOG_INFO("SX1268 init success"); } } } @@ -893,11 +890,11 @@ void setup() if (!rIf) { rIf = new STM32WLE5JCInterface(RadioLibHAL, SX126X_CS, SX126X_DIO1, SX126X_RESET, SX126X_BUSY); if (!rIf->init()) { - LOG_WARN("Failed to find STM32WL radio\n"); + LOG_WARN("No STM32WL radio"); delete rIf; rIf = NULL; } else { - LOG_INFO("STM32WL Radio init succeeded, using STM32WL radio\n"); + LOG_INFO("STM32WL init success"); radioType = STM32WLx_RADIO; } } @@ -907,11 +904,11 @@ void setup() if (!rIf) { rIf = new SimRadio; if (!rIf->init()) { - LOG_WARN("Failed to find simulated radio\n"); + LOG_WARN("No simulated radio"); delete rIf; rIf = NULL; } else { - LOG_INFO("Using SIMULATED radio!\n"); + LOG_INFO("Use SIMULATED radio!"); radioType = SIM_RADIO; } } @@ -921,11 +918,11 @@ void setup() if ((!rIf) && (config.lora.region != meshtastic_Config_LoRaConfig_RegionCode_LORA_24)) { rIf = new RF95Interface(RadioLibHAL, LORA_CS, RF95_IRQ, RF95_RESET, RF95_DIO1); if (!rIf->init()) { - LOG_WARN("Failed to find RF95 radio\n"); + LOG_WARN("No RF95 radio"); delete rIf; rIf = NULL; } else { - LOG_INFO("RF95 Radio init succeeded, using RF95 radio\n"); + LOG_INFO("RF95 init success"); radioType = RF95_RADIO; } } @@ -935,11 +932,11 @@ void setup() if ((!rIf) && (config.lora.region != meshtastic_Config_LoRaConfig_RegionCode_LORA_24)) { rIf = new SX1262Interface(RadioLibHAL, SX126X_CS, SX126X_DIO1, SX126X_RESET, SX126X_BUSY); if (!rIf->init()) { - LOG_WARN("Failed to find SX1262 radio\n"); + LOG_WARN("No SX1262 radio"); delete rIf; rIf = NULL; } else { - LOG_INFO("SX1262 Radio init succeeded, using SX1262 radio\n"); + LOG_INFO("SX1262 init success"); radioType = SX1262_RADIO; } } @@ -950,14 +947,12 @@ void setup() // Try using the specified TCXO voltage rIf = new SX1262Interface(RadioLibHAL, SX126X_CS, SX126X_DIO1, SX126X_RESET, SX126X_BUSY); if (!rIf->init()) { - LOG_WARN("Failed to find SX1262 radio with TCXO using DIO3 reference voltage at %f V\n", tcxoVoltage); + LOG_WARN("No SX1262 radio with TCXO, Vref %f V", tcxoVoltage); delete rIf; rIf = NULL; tcxoVoltage = 0; // if it fails, set the TCXO voltage to zero for the next attempt } else { - LOG_INFO("SX1262 Radio init succeeded, using "); - LOG_WARN("SX1262 Radio with TCXO"); - LOG_INFO(", reference voltage at %f V\n", tcxoVoltage); + LOG_WARN("SX1262 init success, TCXO, Vref %f V", tcxoVoltage); radioType = SX1262_RADIO; } } @@ -966,14 +961,12 @@ void setup() // If specified TCXO voltage fails, attempt to use DIO3 as a reference instea rIf = new SX1262Interface(RadioLibHAL, SX126X_CS, SX126X_DIO1, SX126X_RESET, SX126X_BUSY); if (!rIf->init()) { - LOG_WARN("Failed to find SX1262 radio with XTAL using DIO3 reference voltage at %f V\n", tcxoVoltage); + LOG_WARN("No SX1262 radio with XTAL, Vref %f V", tcxoVoltage); delete rIf; rIf = NULL; tcxoVoltage = SX126X_DIO3_TCXO_VOLTAGE; // if it fails, set the TCXO voltage back for the next radio search } else { - LOG_INFO("SX1262 Radio init succeeded, using "); - LOG_WARN("SX1262 Radio with XTAL"); - LOG_INFO(", reference voltage at %f V\n", tcxoVoltage); + LOG_INFO("SX1262 init success, XTAL, Vref %f V", tcxoVoltage); radioType = SX1262_RADIO; } } @@ -983,11 +976,11 @@ void setup() if ((!rIf) && (config.lora.region != meshtastic_Config_LoRaConfig_RegionCode_LORA_24)) { rIf = new SX1268Interface(RadioLibHAL, SX126X_CS, SX126X_DIO1, SX126X_RESET, SX126X_BUSY); if (!rIf->init()) { - LOG_WARN("Failed to find SX1268 radio\n"); + LOG_WARN("No SX1268 radio"); delete rIf; rIf = NULL; } else { - LOG_INFO("SX1268 Radio init succeeded, using SX1268 radio\n"); + LOG_INFO("SX1268 init success"); radioType = SX1268_RADIO; } } @@ -997,11 +990,11 @@ void setup() if ((!rIf) && (config.lora.region != meshtastic_Config_LoRaConfig_RegionCode_LORA_24)) { rIf = new LLCC68Interface(RadioLibHAL, SX126X_CS, SX126X_DIO1, SX126X_RESET, SX126X_BUSY); if (!rIf->init()) { - LOG_WARN("Failed to find LLCC68 radio\n"); + LOG_WARN("No LLCC68 radio"); delete rIf; rIf = NULL; } else { - LOG_INFO("LLCC68 Radio init succeeded, using LLCC68 radio\n"); + LOG_INFO("LLCC68 init success"); radioType = LLCC68_RADIO; } } @@ -1011,11 +1004,11 @@ void setup() if ((!rIf) && (config.lora.region != meshtastic_Config_LoRaConfig_RegionCode_LORA_24)) { rIf = new LR1110Interface(RadioLibHAL, LR1110_SPI_NSS_PIN, LR1110_IRQ_PIN, LR1110_NRESET_PIN, LR1110_BUSY_PIN); if (!rIf->init()) { - LOG_WARN("Failed to find LR1110 radio\n"); + LOG_WARN("No LR1110 radio"); delete rIf; rIf = NULL; } else { - LOG_INFO("LR1110 Radio init succeeded, using LR1110 radio\n"); + LOG_INFO("LR1110 init success"); radioType = LR1110_RADIO; } } @@ -1025,11 +1018,11 @@ void setup() if (!rIf) { rIf = new LR1120Interface(RadioLibHAL, LR1120_SPI_NSS_PIN, LR1120_IRQ_PIN, LR1120_NRESET_PIN, LR1120_BUSY_PIN); if (!rIf->init()) { - LOG_WARN("Failed to find LR1120 radio\n"); + LOG_WARN("No LR1120 radio"); delete rIf; rIf = NULL; } else { - LOG_INFO("LR1120 Radio init succeeded, using LR1120 radio\n"); + LOG_INFO("LR1120 init success"); radioType = LR1120_RADIO; } } @@ -1039,11 +1032,11 @@ void setup() if (!rIf) { rIf = new LR1121Interface(RadioLibHAL, LR1121_SPI_NSS_PIN, LR1121_IRQ_PIN, LR1121_NRESET_PIN, LR1121_BUSY_PIN); if (!rIf->init()) { - LOG_WARN("Failed to find LR1121 radio\n"); + LOG_WARN("No LR1121 radio"); delete rIf; rIf = NULL; } else { - LOG_INFO("LR1121 Radio init succeeded, using LR1121 radio\n"); + LOG_INFO("LR1121 init success"); radioType = LR1121_RADIO; } } @@ -1053,11 +1046,11 @@ void setup() if (!rIf) { rIf = new SX1280Interface(RadioLibHAL, SX128X_CS, SX128X_DIO1, SX128X_RESET, SX128X_BUSY); if (!rIf->init()) { - LOG_WARN("Failed to find SX1280 radio\n"); + LOG_WARN("No SX1280 radio"); delete rIf; rIf = NULL; } else { - LOG_INFO("SX1280 Radio init succeeded, using SX1280 radio\n"); + LOG_INFO("SX1280 init success"); radioType = SX1280_RADIO; } } @@ -1065,11 +1058,11 @@ void setup() // check if the radio chip matches the selected region if ((config.lora.region == meshtastic_Config_LoRaConfig_RegionCode_LORA_24) && (!rIf->wideLora())) { - LOG_WARN("Radio chip does not support 2.4GHz LoRa. Reverting to unset.\n"); + LOG_WARN("LoRa chip does not support 2.4GHz. Revert to unset"); config.lora.region = meshtastic_Config_LoRaConfig_RegionCode_UNSET; nodeDB->saveToDisk(SEGMENT_CONFIG); if (!rIf->reconfigure()) { - LOG_WARN("Reconfigure failed, rebooting\n"); + LOG_WARN("Reconfigure failed, rebooting"); screen->startAlert("Rebooting..."); rebootAtMsec = millis() + 5000; } @@ -1124,9 +1117,9 @@ void setup() router->addInterface(rIf); // Log bit rate to debug output - LOG_DEBUG("LoRA bitrate = %f bytes / sec\n", (float(meshtastic_Constants_DATA_PAYLOAD_LEN) / - (float(rIf->getPacketTime(meshtastic_Constants_DATA_PAYLOAD_LEN)))) * - 1000); + LOG_DEBUG("LoRA bitrate = %f bytes / sec", (float(meshtastic_Constants_DATA_PAYLOAD_LEN) / + (float(rIf->getPacketTime(meshtastic_Constants_DATA_PAYLOAD_LEN)))) * + 1000); } // This must be _after_ service.init because we need our preferences loaded from flash to have proper timeout values @@ -1155,11 +1148,50 @@ extern meshtastic_DeviceMetadata getDeviceMetadata() deviceMetadata.position_flags = config.position.position_flags; deviceMetadata.hw_model = HW_VENDOR; deviceMetadata.hasRemoteHardware = moduleConfig.remote_hardware.enabled; + deviceMetadata.excluded_modules = meshtastic_ExcludedModules_EXCLUDED_NONE; +#if MESHTASTIC_EXCLUDE_REMOTEHARDWARE + deviceMetadata.excluded_modules |= meshtastic_ExcludedModules_REMOTEHARDWARE_CONFIG; +#endif +#if MESHTASTIC_EXCLUDE_AUDIO + deviceMetadata.excluded_modules |= meshtastic_ExcludedModules_AUDIO_CONFIG; +#endif +#if !HAS_SCREEN || NO_EXT_GPIO + deviceMetadata.excluded_modules |= meshtastic_ExcludedModules_CANNEDMSG_CONFIG | meshtastic_ExcludedModules_EXTNOTIF_CONFIG; +#endif +// Only edge case here is if we apply this a device with built in Accelerometer and want to detect interrupts +// We'll have to macro guard against those targets potentially +#if NO_EXT_GPIO + deviceMetadata.excluded_modules |= meshtastic_ExcludedModules_DETECTIONSENSOR_CONFIG; +#endif +// If we don't have any GPIO and we don't have GPS, no purpose in having serial config +#if NO_EXT_GPIO && NO_GPS + deviceMetadata.excluded_modules |= meshtastic_ExcludedModules_SERIAL_CONFIG; +#endif +#ifndef ARCH_ESP32 + deviceMetadata.excluded_modules |= meshtastic_ExcludedModules_PAXCOUNTER_CONFIG; +#endif +#if !defined(HAS_NCP5623) && !defined(RGBLED_RED) && !defined(HAS_NEOPIXEL) && !defined(UNPHONE) && !RAK_4631 + deviceMetadata.excluded_modules |= meshtastic_ExcludedModules_AMBIENTLIGHTING_CONFIG; +#endif + #if !(MESHTASTIC_EXCLUDE_PKI) deviceMetadata.hasPKC = true; #endif return deviceMetadata; } + +#if !MESHTASTIC_EXCLUDE_I2C +void scannerToSensorsMap(const std::unique_ptr &i2cScanner, ScanI2C::DeviceType deviceType, + meshtastic_TelemetrySensorType sensorType) +{ + auto found = i2cScanner->find(deviceType); + if (found.type != ScanI2C::DeviceType::NONE) { + nodeTelemetrySensorsMap[sensorType].first = found.address.address; + nodeTelemetrySensorsMap[sensorType].second = i2cScanner->fetchI2CBus(found.address); + } +} +#endif + #ifndef PIO_UNIT_TESTING void loop() { @@ -1190,4 +1222,4 @@ void loop() mainDelay.delay(delayMsec); } } -#endif +#endif \ No newline at end of file diff --git a/src/main.h b/src/main.h index 5722f7cf0..b3f58ae4b 100644 --- a/src/main.h +++ b/src/main.h @@ -10,9 +10,6 @@ #include "mesh/generated/meshtastic/telemetry.pb.h" #include #include -#if !defined(ARCH_PORTDUINO) && !defined(ARCH_STM32WL) -#include -#endif #if defined(ARCH_ESP32) && !defined(CONFIG_IDF_TARGET_ESP32S2) #include "nimble/NimbleBluetooth.h" extern NimbleBluetooth *nimbleBluetooth; @@ -21,6 +18,9 @@ extern NimbleBluetooth *nimbleBluetooth; #include "NRF52Bluetooth.h" extern NRF52Bluetooth *nrf52Bluetooth; #endif +#if !MESHTASTIC_EXCLUDE_I2C +#include "detect/ScanI2CTwoWire.h" +#endif #if ARCH_PORTDUINO extern HardwareSPI *DisplaySPI; @@ -39,10 +39,6 @@ extern bool pmu_found; extern bool isCharging; extern bool isUSBPowered; -#if !defined(ARCH_PORTDUINO) && !defined(ARCH_STM32WL) -extern ATECCX08A atecc; -#endif - #ifdef T_WATCH_S3 #include extern Adafruit_DRV2605 drv; @@ -84,6 +80,10 @@ extern bool pauseBluetoothLogging; void nrf52Setup(), esp32Setup(), nrf52Loop(), esp32Loop(), rp2040Setup(), clearBonds(), enterDfuMode(); meshtastic_DeviceMetadata getDeviceMetadata(); +#if !MESHTASTIC_EXCLUDE_I2C +void scannerToSensorsMap(const std::unique_ptr &i2cScanner, ScanI2C::DeviceType deviceType, + meshtastic_TelemetrySensorType sensorType); +#endif // We default to 4MHz SPI, SPI mode 0 extern SPISettings spiSettings; \ No newline at end of file diff --git a/src/mesh/Channels.cpp b/src/mesh/Channels.cpp index bb30e501d..a516268eb 100644 --- a/src/mesh/Channels.cpp +++ b/src/mesh/Channels.cpp @@ -117,13 +117,18 @@ void Channels::initDefaultChannel(ChannelIndex chIndex) static const uint8_t defaultpsk0[] = USERPREFS_CHANNEL_0_PSK; memcpy(channelSettings.psk.bytes, defaultpsk0, sizeof(defaultpsk0)); channelSettings.psk.size = sizeof(defaultpsk0); - #endif #ifdef USERPREFS_CHANNEL_0_NAME strcpy(channelSettings.name, USERPREFS_CHANNEL_0_NAME); #endif #ifdef USERPREFS_CHANNEL_0_PRECISION channelSettings.module_settings.position_precision = USERPREFS_CHANNEL_0_PRECISION; +#endif +#ifdef USERPREFS_CHANNEL_0_UPLINK_ENABLED + channelSettings.uplink_enabled = USERPREFS_CHANNEL_0_UPLINK_ENABLED; +#endif +#ifdef USERPREFS_CHANNEL_0_DOWNLINK_ENABLED + channelSettings.downlink_enabled = USERPREFS_CHANNEL_0_DOWNLINK_ENABLED; #endif break; case 1: @@ -131,13 +136,18 @@ void Channels::initDefaultChannel(ChannelIndex chIndex) static const uint8_t defaultpsk1[] = USERPREFS_CHANNEL_1_PSK; memcpy(channelSettings.psk.bytes, defaultpsk1, sizeof(defaultpsk1)); channelSettings.psk.size = sizeof(defaultpsk1); - #endif #ifdef USERPREFS_CHANNEL_1_NAME strcpy(channelSettings.name, USERPREFS_CHANNEL_1_NAME); #endif #ifdef USERPREFS_CHANNEL_1_PRECISION channelSettings.module_settings.position_precision = USERPREFS_CHANNEL_1_PRECISION; +#endif +#ifdef USERPREFS_CHANNEL_1_UPLINK_ENABLED + channelSettings.uplink_enabled = USERPREFS_CHANNEL_1_UPLINK_ENABLED; +#endif +#ifdef USERPREFS_CHANNEL_1_DOWNLINK_ENABLED + channelSettings.downlink_enabled = USERPREFS_CHANNEL_1_DOWNLINK_ENABLED; #endif break; case 2: @@ -145,13 +155,18 @@ void Channels::initDefaultChannel(ChannelIndex chIndex) static const uint8_t defaultpsk2[] = USERPREFS_CHANNEL_2_PSK; memcpy(channelSettings.psk.bytes, defaultpsk2, sizeof(defaultpsk2)); channelSettings.psk.size = sizeof(defaultpsk2); - #endif #ifdef USERPREFS_CHANNEL_2_NAME strcpy(channelSettings.name, USERPREFS_CHANNEL_2_NAME); #endif #ifdef USERPREFS_CHANNEL_2_PRECISION channelSettings.module_settings.position_precision = USERPREFS_CHANNEL_2_PRECISION; +#endif +#ifdef USERPREFS_CHANNEL_2_UPLINK_ENABLED + channelSettings.uplink_enabled = USERPREFS_CHANNEL_2_UPLINK_ENABLED; +#endif +#ifdef USERPREFS_CHANNEL_2_DOWNLINK_ENABLED + channelSettings.downlink_enabled = USERPREFS_CHANNEL_2_DOWNLINK_ENABLED; #endif break; default: @@ -175,34 +190,19 @@ CryptoKey Channels::getKey(ChannelIndex chIndex) k.length = channelSettings.psk.size; if (k.length == 0) { if (ch.role == meshtastic_Channel_Role_SECONDARY) { - LOG_DEBUG("Unset PSK for secondary channel %s. using primary key\n", ch.settings.name); + LOG_DEBUG("Unset PSK for secondary channel %s. use primary key", ch.settings.name); k = getKey(primaryIndex); } else { - LOG_WARN("User disabled encryption\n"); + LOG_WARN("User disabled encryption"); } } else if (k.length == 1) { // Convert the short single byte variants of psk into variant that can be used more generally uint8_t pskIndex = k.bytes[0]; - LOG_DEBUG("Expanding short PSK #%d\n", pskIndex); + LOG_DEBUG("Expand short PSK #%d", pskIndex); if (pskIndex == 0) k.length = 0; // Turn off encryption - else if (oemStore.oem_aes_key.size > 1) { - // Use the OEM key - LOG_DEBUG("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) { - LOG_WARN("OEM provided a too short AES128 key - padding\n"); - k.length = 16; - } else if (k.length < 32 && k.length != 16) { - LOG_WARN("OEM provided a too short AES256 key - padding\n"); - k.length = 32; - } - } else { + else { memcpy(k.bytes, defaultpsk, sizeof(defaultpsk)); k.length = sizeof(defaultpsk); // Bump up the last byte of PSK as needed @@ -212,12 +212,12 @@ CryptoKey Channels::getKey(ChannelIndex chIndex) } else if (k.length < 16) { // Error! The user specified only the first few bits of an AES128 key. So by convention we just pad the rest of the // key with zeros - LOG_WARN("User provided a too short AES128 key - padding\n"); + LOG_WARN("User provided a too short AES128 key - padding"); k.length = 16; } else if (k.length < 32 && k.length != 16) { // Error! The user specified only the first few bits of an AES256 key. So by convention we just pad the rest of the // key with zeros - LOG_WARN("User provided a too short AES256 key - padding\n"); + LOG_WARN("User provided a too short AES256 key - padding"); k.length = 32; } } @@ -267,7 +267,7 @@ void Channels::onConfigChanged() } #if !MESHTASTIC_EXCLUDE_MQTT if (channels.anyMqttEnabled() && mqtt && !mqtt->isEnabled()) { - LOG_DEBUG("MQTT is enabled on at least one channel, so set MQTT thread to run immediately\n"); + LOG_DEBUG("MQTT is enabled on at least one channel, so set MQTT thread to run immediately"); mqtt->start(); } #endif @@ -280,7 +280,7 @@ meshtastic_Channel &Channels::getByIndex(ChannelIndex chIndex) meshtastic_Channel *ch = channelFile.channels + chIndex; return *ch; } else { - LOG_ERROR("Invalid channel index %d > %d, malformed packet received?\n", chIndex, channelFile.channels_count); + LOG_ERROR("Invalid channel index %d > %d, malformed packet received?", chIndex, channelFile.channels_count); static meshtastic_Channel *ch = (meshtastic_Channel *)malloc(sizeof(meshtastic_Channel)); memset(ch, 0, sizeof(meshtastic_Channel)); @@ -384,11 +384,11 @@ bool Channels::hasDefaultChannel() bool Channels::decryptForHash(ChannelIndex chIndex, ChannelHash channelHash) { if (chIndex > getNumChannels() || getHash(chIndex) != channelHash) { - // LOG_DEBUG("Skipping channel %d (hash %x) due to invalid hash/index, want=%x\n", chIndex, getHash(chIndex), + // LOG_DEBUG("Skip channel %d (hash %x) due to invalid hash/index, want=%x", chIndex, getHash(chIndex), // channelHash); return false; } else { - LOG_DEBUG("Using channel %d (hash 0x%x)\n", chIndex, channelHash); + LOG_DEBUG("Use channel %d (hash 0x%x)", chIndex, channelHash); setCrypto(chIndex); return true; } @@ -398,7 +398,7 @@ bool Channels::decryptForHash(ChannelIndex chIndex, ChannelHash channelHash) * * This method is called before encoding outbound packets * - * @eturn the (0 to 255) hash for that channel - if no suitable channel could be found, return -1 + * @return the (0 to 255) hash for that channel - if no suitable channel could be found, return -1 */ int16_t Channels::setActiveByIndex(ChannelIndex channelIndex) { diff --git a/src/mesh/CryptoEngine.cpp b/src/mesh/CryptoEngine.cpp index 484114802..94b9b6543 100644 --- a/src/mesh/CryptoEngine.cpp +++ b/src/mesh/CryptoEngine.cpp @@ -18,7 +18,7 @@ */ void CryptoEngine::generateKeyPair(uint8_t *pubKey, uint8_t *privKey) { - LOG_DEBUG("Generating Curve25519 key pair...\n"); + LOG_DEBUG("Generate Curve25519 keypair"); Curve25519::dh1(public_key, private_key); memcpy(pubKey, public_key, sizeof(public_key)); memcpy(privKey, private_key, sizeof(private_key)); @@ -35,14 +35,14 @@ bool CryptoEngine::regeneratePublicKey(uint8_t *pubKey, uint8_t *privKey) if (!memfll(privKey, 0, sizeof(private_key))) { Curve25519::eval(pubKey, privKey, 0); if (Curve25519::isWeakPoint(pubKey)) { - LOG_ERROR("PKI key generation failed. Specified private key results in a weak\n"); + LOG_ERROR("PKI key generation failed. Specified private key results in a weak"); memset(pubKey, 0, 32); return false; } memcpy(private_key, privKey, sizeof(private_key)); memcpy(public_key, pubKey, sizeof(public_key)); } else { - LOG_WARN("X25519 key generation failed due to blank private key\n"); + LOG_WARN("X25519 key generation failed due to blank private key"); return false; } return true; @@ -68,9 +68,9 @@ bool CryptoEngine::encryptCurve25519(uint32_t toNode, uint32_t fromNode, meshtas auth = bytesOut + numBytes; memcpy((uint8_t *)(auth + 8), &extraNonceTmp, sizeof(uint32_t)); // do not use dereference on potential non aligned pointers : *extraNonce = extraNonceTmp; - LOG_INFO("Random nonce value: %d\n", extraNonceTmp); + LOG_INFO("Random nonce value: %d", extraNonceTmp); if (remotePublic.size == 0) { - LOG_DEBUG("Node %d or their public_key not found\n", toNode); + LOG_DEBUG("Node %d or their public_key not found", toNode); return false; } if (!crypto->setDHPublicKey(remotePublic.bytes)) { @@ -80,8 +80,8 @@ bool CryptoEngine::encryptCurve25519(uint32_t toNode, uint32_t fromNode, meshtas initNonce(fromNode, packetNum, extraNonceTmp); // Calculate the shared secret with the destination node and encrypt - printBytes("Attempting encrypt using nonce: ", nonce, 13); - printBytes("Attempting encrypt using shared_key starting with: ", shared_key, 8); + printBytes("Attempt encrypt with nonce: ", nonce, 13); + printBytes("Attempt encrypt with shared_key starting with: ", shared_key, 8); aes_ccm_ae(shared_key, 32, nonce, 8, bytes, numBytes, nullptr, 0, bytesOut, auth); // this can write up to 15 bytes longer than numbytes past bytesOut memcpy((uint8_t *)(auth + 8), &extraNonceTmp, @@ -103,10 +103,10 @@ bool CryptoEngine::decryptCurve25519(uint32_t fromNode, meshtastic_UserLite_publ auth = bytes + numBytes - 12; memcpy(&extraNonce, auth + 8, sizeof(uint32_t)); // do not use dereference on potential non aligned pointers : (uint32_t *)(auth + 8); - LOG_INFO("Random nonce value: %d\n", extraNonce); + LOG_INFO("Random nonce value: %d", extraNonce); if (remotePublic.size == 0) { - LOG_DEBUG("Node or its public key not found in database\n"); + LOG_DEBUG("Node or its public key not found in database"); return false; } @@ -117,8 +117,8 @@ bool CryptoEngine::decryptCurve25519(uint32_t fromNode, meshtastic_UserLite_publ crypto->hash(shared_key, 32); initNonce(fromNode, packetNum, extraNonce); - printBytes("Attempting decrypt using nonce: ", nonce, 13); - printBytes("Attempting decrypt using shared_key starting with: ", shared_key, 8); + printBytes("Attempt decrypt with nonce: ", nonce, 13); + printBytes("Attempt decrypt with shared_key starting with: ", shared_key, 8); return aes_ccm_ad(shared_key, 32, nonce, 8, bytes, numBytes - 12, nullptr, 0, auth, bytesOut); } @@ -174,7 +174,7 @@ bool CryptoEngine::setDHPublicKey(uint8_t *pubKey) // Calculate the shared secret with the specified node's public key and our private key // This includes an internal weak key check, which among other things looks for an all 0 public key and shared key. if (!Curve25519::dh2(shared_key, local_priv)) { - LOG_WARN("Curve25519DH step 2 failed!\n"); + LOG_WARN("Curve25519DH step 2 failed!"); return false; } return true; @@ -185,7 +185,7 @@ concurrency::Lock *cryptLock; void CryptoEngine::setKey(const CryptoKey &k) { - LOG_DEBUG("Using AES%d key!\n", k.length * 8); + LOG_DEBUG("Use AES%d key!", k.length * 8); key = k; } @@ -201,7 +201,7 @@ void CryptoEngine::encryptPacket(uint32_t fromNode, uint64_t packetId, size_t nu if (numBytes <= MAX_BLOCKSIZE) { encryptAESCtr(key, nonce, numBytes, bytes); } else { - LOG_ERROR("Packet too large for crypto engine: %d. noop encryption!\n", numBytes); + LOG_ERROR("Packet too large for crypto engine: %d. noop encryption!", numBytes); } } } @@ -249,4 +249,4 @@ void CryptoEngine::initNonce(uint32_t fromNode, uint64_t packetId, uint32_t extr } #ifndef HAS_CUSTOM_CRYPTO_ENGINE CryptoEngine *crypto = new CryptoEngine; -#endif \ No newline at end of file +#endif diff --git a/src/mesh/Default.cpp b/src/mesh/Default.cpp index 0bedcfc91..ba1dafe70 100644 --- a/src/mesh/Default.cpp +++ b/src/mesh/Default.cpp @@ -1,5 +1,6 @@ #include "Default.h" #include "../userPrefs.h" +#include "meshUtils.h" uint32_t Default::getConfiguredOrDefaultMs(uint32_t configuredInterval, uint32_t defaultInterval) { @@ -40,9 +41,22 @@ uint32_t Default::getConfiguredOrDefaultMsScaled(uint32_t configured, uint32_t d if (config.device.role == meshtastic_Config_DeviceConfig_Role_ROUTER) return getConfiguredOrDefaultMs(configured, defaultValue); + // Additionally if we're a tracker or sensor, we want priority to send position and telemetry + if (IS_ONE_OF(config.device.role, meshtastic_Config_DeviceConfig_Role_SENSOR, meshtastic_Config_DeviceConfig_Role_TRACKER)) + return getConfiguredOrDefaultMs(configured, defaultValue); + return getConfiguredOrDefaultMs(configured, defaultValue) * congestionScalingCoefficient(numOnlineNodes); } +uint32_t Default::getConfiguredOrMinimumValue(uint32_t configured, uint32_t minValue) +{ + // If zero, intervals should be coalesced later by getConfiguredOrDefault... methods + if (configured == 0) + return configured; + + return configured < minValue ? minValue : configured; +} + uint8_t Default::getConfiguredOrDefaultHopLimit(uint8_t configured) { #if USERPREFS_EVENT_MODE diff --git a/src/mesh/Default.h b/src/mesh/Default.h index 5641b5d25..d39886d1c 100644 --- a/src/mesh/Default.h +++ b/src/mesh/Default.h @@ -1,13 +1,15 @@ #pragma once #include #include +#include #define ONE_DAY 24 * 60 * 60 #define ONE_MINUTE_MS 60 * 1000 #define THIRTY_SECONDS_MS 30 * 1000 #define FIVE_SECONDS_MS 5 * 1000 +#define min_default_telemetry_interval_secs 30 * 60 #define default_gps_update_interval IF_ROUTER(ONE_DAY, 2 * 60) -#define default_telemetry_broadcast_interval_secs IF_ROUTER(ONE_DAY / 2, 30 * 60) +#define default_telemetry_broadcast_interval_secs IF_ROUTER(ONE_DAY / 2, 60 * 60) #define default_broadcast_interval_secs IF_ROUTER(ONE_DAY / 2, 15 * 60) #define default_wait_bluetooth_secs IF_ROUTER(1, 60) #define default_sds_secs IF_ROUTER(ONE_DAY, UINT32_MAX) // Default to forever super deep sleep @@ -17,7 +19,7 @@ #define default_node_info_broadcast_secs 3 * 60 * 60 #define default_neighbor_info_broadcast_secs 6 * 60 * 60 #define min_node_info_broadcast_secs 60 * 60 // No regular broadcasts of more than once an hour -#define min_neighbor_info_broadcast_secs 2 * 60 * 60 +#define min_neighbor_info_broadcast_secs 4 * 60 * 60 #define default_mqtt_address "mqtt.meshtastic.org" #define default_mqtt_username "meshdev" @@ -35,16 +37,35 @@ class Default static uint32_t getConfiguredOrDefault(uint32_t configured, uint32_t defaultValue); static uint32_t getConfiguredOrDefaultMsScaled(uint32_t configured, uint32_t defaultValue, uint32_t numOnlineNodes); static uint8_t getConfiguredOrDefaultHopLimit(uint8_t configured); + static uint32_t getConfiguredOrMinimumValue(uint32_t configured, uint32_t minValue); private: static float congestionScalingCoefficient(int numOnlineNodes) { - if (numOnlineNodes <= 40) { - return 1.0; // No scaling for 40 or fewer nodes + // Increase frequency of broadcasts for small networks regardless of preset + if (numOnlineNodes <= 10) { + return 0.6; + } else if (numOnlineNodes <= 20) { + return 0.7; + } else if (numOnlineNodes <= 30) { + return 0.8; + } else if (numOnlineNodes <= 40) { + return 1.0; } else { - // Sscaling based on number of nodes over 40 + float throttlingFactor = 0.075; + if (config.lora.use_preset && config.lora.modem_preset == meshtastic_Config_LoRaConfig_ModemPreset_MEDIUM_SLOW) + throttlingFactor = 0.04; + else if (config.lora.use_preset && config.lora.modem_preset == meshtastic_Config_LoRaConfig_ModemPreset_MEDIUM_FAST) + throttlingFactor = 0.02; + else if (config.lora.use_preset && config.lora.modem_preset == meshtastic_Config_LoRaConfig_ModemPreset_SHORT_SLOW) + throttlingFactor = 0.01; + else if (config.lora.use_preset && + IS_ONE_OF(config.lora.modem_preset, meshtastic_Config_LoRaConfig_ModemPreset_SHORT_FAST, + meshtastic_Config_LoRaConfig_ModemPreset_SHORT_TURBO)) + return 1.0; // Don't bother throttling for highest bandwidth presets + // Scaling up traffic based on number of nodes over 40 int nodesOverForty = (numOnlineNodes - 40); - return 1.0 + (nodesOverForty * 0.075); // Each number of online node scales by 0.075 + return 1.0 + (nodesOverForty * throttlingFactor); // Each number of online node scales by 0.075 (default) } } }; \ No newline at end of file diff --git a/src/mesh/FloodingRouter.cpp b/src/mesh/FloodingRouter.cpp index 33166e3e5..81ea72381 100644 --- a/src/mesh/FloodingRouter.cpp +++ b/src/mesh/FloodingRouter.cpp @@ -21,7 +21,7 @@ ErrorCode FloodingRouter::send(meshtastic_MeshPacket *p) bool FloodingRouter::shouldFilterReceived(const meshtastic_MeshPacket *p) { if (wasSeenRecently(p)) { // Note: this will also add a recent packet record - printPacket("Ignoring dupe incoming msg", p); + printPacket("Ignore dupe incoming msg", p); rxDupe++; if (config.device.role != meshtastic_Config_DeviceConfig_Role_ROUTER && config.device.role != meshtastic_Config_DeviceConfig_Role_REPEATER) { @@ -29,6 +29,17 @@ bool FloodingRouter::shouldFilterReceived(const meshtastic_MeshPacket *p) if (Router::cancelSending(p->from, p->id)) txRelayCanceled++; } + + /* If the original transmitter is doing retransmissions (hopStart equals hopLimit) for a reliable transmission, e.g., when + the ACK got lost, we will handle the packet again to make sure it gets an ACK to its packet. */ + bool isRepeated = p->hop_start > 0 && p->hop_start == p->hop_limit; + if (isRepeated) { + LOG_DEBUG("Repeated reliable tx"); + if (!perhapsRebroadcast(p) && isToUs(p) && p->want_ack) { + sendAckNak(meshtastic_Routing_Error_NONE, getFrom(p), p->id, p->channel, 0); + } + } + return true; } @@ -41,14 +52,8 @@ bool FloodingRouter::isRebroadcaster() config.device.rebroadcast_mode != meshtastic_Config_DeviceConfig_RebroadcastMode_NONE; } -void FloodingRouter::sniffReceived(const meshtastic_MeshPacket *p, const meshtastic_Routing *c) +bool FloodingRouter::perhapsRebroadcast(const meshtastic_MeshPacket *p) { - bool isAckorReply = (p->which_payload_variant == meshtastic_MeshPacket_decoded_tag) && (p->decoded.request_id != 0); - if (isAckorReply && !isToUs(p) && p->to != NODENUM_BROADCAST) { - // do not flood direct message that is ACKed or replied to - LOG_DEBUG("Rxd an ACK/reply not for me, cancel rebroadcast.\n"); - Router::cancelSending(p->to, p->decoded.request_id); // cancel rebroadcast for this DM - } if (!isToUs(p) && (p->hop_limit > 0) && !isFromUs(p)) { if (p->id != 0) { if (isRebroadcaster()) { @@ -63,17 +68,34 @@ void FloodingRouter::sniffReceived(const meshtastic_MeshPacket *p, const meshtas } #endif - LOG_INFO("Rebroadcasting received floodmsg\n"); + LOG_INFO("Rebroadcast received floodmsg"); // Note: we are careful to resend using the original senders node id // We are careful not to call our hooked version of send() - because we don't want to check this again Router::send(tosend); + + return true; } else { - LOG_DEBUG("Not rebroadcasting: Role = CLIENT_MUTE or Rebroadcast Mode = NONE\n"); + LOG_DEBUG("No rebroadcast: Role = CLIENT_MUTE or Rebroadcast Mode = NONE"); } } else { - LOG_DEBUG("Ignoring 0 id broadcast\n"); + LOG_DEBUG("Ignore 0 id broadcast"); } } + + return false; +} + +void FloodingRouter::sniffReceived(const meshtastic_MeshPacket *p, const meshtastic_Routing *c) +{ + bool isAckorReply = (p->which_payload_variant == meshtastic_MeshPacket_decoded_tag) && (p->decoded.request_id != 0); + if (isAckorReply && !isToUs(p) && !isBroadcast(p->to)) { + // do not flood direct message that is ACKed or replied to + LOG_DEBUG("Rxd an ACK/reply not for me, cancel rebroadcast"); + Router::cancelSending(p->to, p->decoded.request_id); // cancel rebroadcast for this DM + } + + perhapsRebroadcast(p); + // handle the packet as normal Router::sniffReceived(p, c); } \ No newline at end of file diff --git a/src/mesh/FloodingRouter.h b/src/mesh/FloodingRouter.h index 0ed2b5582..52614f391 100644 --- a/src/mesh/FloodingRouter.h +++ b/src/mesh/FloodingRouter.h @@ -31,6 +31,10 @@ class FloodingRouter : public Router, protected PacketHistory private: bool isRebroadcaster(); + /** Check if we should rebroadcast this packet, and do so if needed + * @return true if rebroadcasted */ + bool perhapsRebroadcast(const meshtastic_MeshPacket *p); + public: /** * Constructor diff --git a/src/mesh/LR11x0Interface.cpp b/src/mesh/LR11x0Interface.cpp index a985a9006..30ef8f9af 100644 --- a/src/mesh/LR11x0Interface.cpp +++ b/src/mesh/LR11x0Interface.cpp @@ -35,7 +35,7 @@ LR11x0Interface::LR11x0Interface(LockingArduinoHal *hal, RADIOLIB_PIN_TYPE cs RADIOLIB_PIN_TYPE busy) : RadioLibInterface(hal, cs, irq, rst, busy, &lora), lora(&module) { - LOG_WARN("LR11x0Interface(cs=%d, irq=%d, rst=%d, busy=%d)\n", cs, irq, rst, busy); + LOG_WARN("LR11x0Interface(cs=%d, irq=%d, rst=%d, busy=%d)", cs, irq, rst, busy); } /// Initialise the Driver transport hardware and software. @@ -54,10 +54,10 @@ template bool LR11x0Interface::init() 0; // "TCXO reference voltage to be set on DIO3. Defaults to 1.6 V, set to 0 to skip." per // https://github.com/jgromes/RadioLib/blob/690a050ebb46e6097c5d00c371e961c1caa3b52e/src/modules/LR11x0/LR11x0.h#L471C26-L471C104 // (DIO3 is free to be used as an IRQ) - LOG_DEBUG("LR11X0_DIO3_TCXO_VOLTAGE not defined, not using DIO3 as TCXO reference voltage\n"); + LOG_DEBUG("LR11X0_DIO3_TCXO_VOLTAGE not defined, not using DIO3 as TCXO reference voltage"); #else float tcxoVoltage = LR11X0_DIO3_TCXO_VOLTAGE; - LOG_DEBUG("LR11X0_DIO3_TCXO_VOLTAGE defined, using DIO3 as TCXO reference voltage at %f V\n", LR11X0_DIO3_TCXO_VOLTAGE); + LOG_DEBUG("LR11X0_DIO3_TCXO_VOLTAGE defined, using DIO3 as TCXO reference voltage at %f V", LR11X0_DIO3_TCXO_VOLTAGE); // (DIO3 is not free to be used as an IRQ) #endif @@ -67,39 +67,40 @@ template bool LR11x0Interface::init() power = LR1110_MAX_POWER; if ((power > LR1120_MAX_POWER) && - (config.lora.region == meshtastic_Config_LoRaConfig_RegionCode_LORA_24)) // clamp again if wide freq range + (config.lora.region == meshtastic_Config_LoRaConfig_RegionCode_LORA_24)) { // clamp again if wide freq range power = LR1120_MAX_POWER; + preambleLength = 12; // 12 is the default for operation above 2GHz + } limitPower(); #ifdef LR11X0_RF_SWITCH_SUBGHZ pinMode(LR11X0_RF_SWITCH_SUBGHZ, OUTPUT); digitalWrite(LR11X0_RF_SWITCH_SUBGHZ, getFreq() < 1e9 ? HIGH : LOW); - LOG_DEBUG("Setting RF0 switch to %s\n", getFreq() < 1e9 ? "SubGHz" : "2.4GHz"); + LOG_DEBUG("Set RF0 switch to %s", getFreq() < 1e9 ? "SubGHz" : "2.4GHz"); #endif #ifdef LR11X0_RF_SWITCH_2_4GHZ pinMode(LR11X0_RF_SWITCH_2_4GHZ, OUTPUT); digitalWrite(LR11X0_RF_SWITCH_2_4GHZ, getFreq() < 1e9 ? LOW : HIGH); - LOG_DEBUG("Setting RF1 switch to %s\n", getFreq() < 1e9 ? "SubGHz" : "2.4GHz"); + LOG_DEBUG("Set RF1 switch to %s", getFreq() < 1e9 ? "SubGHz" : "2.4GHz"); #endif int res = lora.begin(getFreq(), bw, sf, cr, syncWord, power, preambleLength, tcxoVoltage); // \todo Display actual typename of the adapter, not just `LR11x0` - LOG_INFO("LR11x0 init result %d\n", res); + LOG_INFO("LR11x0 init result %d", res); if (res == RADIOLIB_ERR_CHIP_NOT_FOUND) return false; LR11x0VersionInfo_t version; res = lora.getVersionInfo(&version); if (res == RADIOLIB_ERR_NONE) - LOG_DEBUG("LR11x0 Device %d, HW %d, FW %d.%d, WiFi %d.%d, GNSS %d.%d\n", version.device, version.hardware, - version.fwMajor, version.fwMinor, version.fwMajorWiFi, version.fwMinorWiFi, version.fwGNSS, - version.almanacGNSS); + LOG_DEBUG("LR11x0 Device %d, HW %d, FW %d.%d, WiFi %d.%d, GNSS %d.%d", version.device, version.hardware, version.fwMajor, + version.fwMinor, version.fwMajorWiFi, version.fwMinorWiFi, version.fwGNSS, version.almanacGNSS); - LOG_INFO("Frequency set to %f\n", getFreq()); - LOG_INFO("Bandwidth set to %f\n", bw); - LOG_INFO("Power output set to %d\n", power); + LOG_INFO("Frequency set to %f", getFreq()); + LOG_INFO("Bandwidth set to %f", bw); + LOG_INFO("Power output set to %d", power); if (res == RADIOLIB_ERR_NONE) res = lora.setCRC(2); @@ -121,16 +122,16 @@ template bool LR11x0Interface::init() if (dioAsRfSwitch) { lora.setRfSwitchTable(rfswitch_dio_pins, rfswitch_table); - LOG_DEBUG("Setting DIO RF switch\n", res); + LOG_DEBUG("Set DIO RF switch", res); } if (res == RADIOLIB_ERR_NONE) { if (config.lora.sx126x_rx_boosted_gain) { // the name is unfortunate but historically accurate res = lora.setRxBoostedGainMode(true); - LOG_INFO("Set RX gain to boosted mode; result: %d\n", res); + LOG_INFO("Set RX gain to boosted mode; result: %d", res); } else { res = lora.setRxBoostedGainMode(false); - LOG_INFO("Set RX gain to power saving mode (boosted mode off); result: %d\n", res); + LOG_INFO("Set RX gain to power saving mode (boosted mode off); result: %d", res); } } @@ -160,11 +161,6 @@ template bool LR11x0Interface::reconfigure() if (err != RADIOLIB_ERR_NONE) RECORD_CRITICALERROR(meshtastic_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); @@ -200,7 +196,7 @@ template void LR11x0Interface::setStandby() int err = lora.standby(); if (err != RADIOLIB_ERR_NONE) { - LOG_DEBUG("LR11x0 standby failed with error %d\n", err); + LOG_DEBUG("LR11x0 standby failed with error %d", err); } assert(err == RADIOLIB_ERR_NONE); @@ -217,7 +213,7 @@ template void LR11x0Interface::setStandby() */ template void LR11x0Interface::addReceiveMetadata(meshtastic_MeshPacket *mp) { - // LOG_DEBUG("PacketStatus %x\n", lora.getPacketStatus()); + // LOG_DEBUG("PacketStatus %x", lora.getPacketStatus()); mp->rx_snr = lora.getSNR(); mp->rx_rssi = lround(lora.getRSSI()); } @@ -282,7 +278,7 @@ template bool LR11x0Interface::isActivelyReceiving() template bool LR11x0Interface::sleep() { // \todo Display actual typename of the adapter, not just `LR11x0` - LOG_DEBUG("LR11x0 entering sleep mode\n"); + LOG_DEBUG("LR11x0 entering sleep mode"); setStandby(); // Stop any pending operations // turn off TCXO if it was powered diff --git a/src/mesh/MeshModule.cpp b/src/mesh/MeshModule.cpp index c60404c98..9de54ade5 100644 --- a/src/mesh/MeshModule.cpp +++ b/src/mesh/MeshModule.cpp @@ -33,7 +33,7 @@ MeshModule::~MeshModule() } meshtastic_MeshPacket *MeshModule::allocAckNak(meshtastic_Routing_Error err, NodeNum to, PacketId idFrom, ChannelIndex chIndex, - uint8_t hopStart, uint8_t hopLimit) + uint8_t hopLimit) { meshtastic_Routing c = meshtastic_Routing_init_default; @@ -50,12 +50,12 @@ meshtastic_MeshPacket *MeshModule::allocAckNak(meshtastic_Routing_Error err, Nod p->priority = meshtastic_MeshPacket_Priority_ACK; - p->hop_limit = routingModule->getHopLimitForResponse(hopStart, hopLimit); // Flood ACK back to original sender + p->hop_limit = hopLimit; // Flood ACK back to original sender p->to = to; p->decoded.request_id = idFrom; p->channel = chIndex; if (err != meshtastic_Routing_Error_NONE) - LOG_WARN("Alloc an err=%d,to=0x%x,idFrom=0x%x,id=0x%x\n", err, to, idFrom, p->id); + LOG_WARN("Alloc an err=%d,to=0x%x,idFrom=0x%x,id=0x%x", err, to, idFrom, p->id); return p; } @@ -74,7 +74,7 @@ meshtastic_MeshPacket *MeshModule::allocErrorResponse(meshtastic_Routing_Error e void MeshModule::callModules(meshtastic_MeshPacket &mp, RxSource src) { - // LOG_DEBUG("In call modules\n"); + // LOG_DEBUG("In call modules"); bool moduleFound = false; // We now allow **encrypted** packets to pass through the modules @@ -86,7 +86,7 @@ void MeshModule::callModules(meshtastic_MeshPacket &mp, RxSource src) // Was this message directed to us specifically? Will be false if we are sniffing someone elses packets auto ourNodeNum = nodeDB->getNodeNum(); - bool toUs = mp.to == NODENUM_BROADCAST || isToUs(&mp); + bool toUs = isBroadcast(mp.to) || isToUs(&mp); for (auto i = modules->begin(); i != modules->end(); ++i) { auto &pi = **i; @@ -104,7 +104,7 @@ void MeshModule::callModules(meshtastic_MeshPacket &mp, RxSource src) assert(!pi.myReply); // If it is !null it means we have a bug, because it should have been sent the previous time if (wantsPacket) { - LOG_DEBUG("Module '%s' wantsPacket=%d\n", pi.name, wantsPacket); + LOG_DEBUG("Module '%s' wantsPacket=%d", pi.name, wantsPacket); moduleFound = true; @@ -144,20 +144,20 @@ void MeshModule::callModules(meshtastic_MeshPacket &mp, RxSource src) if (isDecoded && mp.decoded.want_response && toUs && (!isFromUs(&mp) || isToUs(&mp)) && !currentReply) { pi.sendResponse(mp); ignoreRequest = ignoreRequest || pi.ignoreRequest; // If at least one module asks it, we may ignore a request - LOG_INFO("Asked module '%s' to send a response\n", pi.name); + LOG_INFO("Asked module '%s' to send a response", pi.name); } else { - LOG_DEBUG("Module '%s' considered\n", pi.name); + LOG_DEBUG("Module '%s' considered", pi.name); } // If the requester didn't ask for a response we might need to discard unused replies to prevent memory leaks if (pi.myReply) { - LOG_DEBUG("Discarding an unneeded response\n"); + LOG_DEBUG("Discard an unneeded response"); packetPool.release(pi.myReply); pi.myReply = NULL; } if (handled == ProcessMessage::STOP) { - LOG_DEBUG("Module '%s' handled and skipped other processing\n", pi.name); + LOG_DEBUG("Module '%s' handled and skipped other processing", pi.name); break; } } @@ -168,7 +168,7 @@ void MeshModule::callModules(meshtastic_MeshPacket &mp, RxSource src) if (isDecoded && mp.decoded.want_response && toUs) { if (currentReply) { - printPacket("Sending response", currentReply); + printPacket("Send response", currentReply); service->sendToMesh(currentReply); currentReply = NULL; } else if (mp.from != ourNodeNum && !ignoreRequest) { @@ -176,19 +176,18 @@ void MeshModule::callModules(meshtastic_MeshPacket &mp, RxSource src) // no response reply // No one wanted to reply to this request, tell the requster that happened - LOG_DEBUG("No one responded, send a nak\n"); + LOG_DEBUG("No one responded, send a nak"); // SECURITY NOTE! I considered sending back a different error code if we didn't find the psk (i.e. !isDecoded) // but opted NOT TO. Because it is not a good idea to let remote nodes 'probe' to find out which PSKs were "good" vs // bad. - routingModule->sendAckNak(meshtastic_Routing_Error_NO_RESPONSE, getFrom(&mp), mp.id, mp.channel, mp.hop_start, - mp.hop_limit); + routingModule->sendAckNak(meshtastic_Routing_Error_NO_RESPONSE, getFrom(&mp), mp.id, mp.channel, + routingModule->getHopLimitForResponse(mp.hop_start, mp.hop_limit)); } } if (!moduleFound && isDecoded) { - LOG_DEBUG("No modules interested in portnum=%d, src=%s\n", mp.decoded.portnum, - (src == RX_SRC_LOCAL) ? "LOCAL" : "REMOTE"); + LOG_DEBUG("No modules interested in portnum=%d, src=%s", mp.decoded.portnum, (src == RX_SRC_LOCAL) ? "LOCAL" : "REMOTE"); } } @@ -211,7 +210,7 @@ void MeshModule::sendResponse(const meshtastic_MeshPacket &req) currentReply = r; } else { // Ignore - this is now expected behavior for routing module (because it ignores some replies) - // LOG_WARN("Client requested response but this module did not provide\n"); + // LOG_WARN("Client requested response but this module did not provide"); } } @@ -240,7 +239,7 @@ std::vector MeshModule::GetMeshModulesWithUIFrames() for (auto i = modules->begin(); i != modules->end(); ++i) { auto &pi = **i; if (pi.wantUIFrame()) { - LOG_DEBUG("%s wants a UI Frame\n", pi.name); + LOG_DEBUG("%s wants a UI Frame", pi.name); modulesWithUIFrames.push_back(&pi); } } @@ -255,7 +254,7 @@ void MeshModule::observeUIEvents(Observer *observer) auto &pi = **i; Observable *observable = pi.getUIFrameObservable(); if (observable != NULL) { - LOG_DEBUG("%s wants a UI Frame\n", pi.name); + LOG_DEBUG("%s wants a UI Frame", pi.name); observer->observe(observable); } } @@ -273,7 +272,7 @@ AdminMessageHandleResult MeshModule::handleAdminMessageForAllModules(const mesht AdminMessageHandleResult h = pi.handleAdminMessageForModule(mp, request, response); if (h == AdminMessageHandleResult::HANDLED_WITH_RESPONSE) { // In case we have a response it always has priority. - LOG_DEBUG("Reply prepared by module '%s' of variant: %d\n", pi.name, response->which_payload_variant); + LOG_DEBUG("Reply prepared by module '%s' of variant: %d", pi.name, response->which_payload_variant); handled = h; } else if ((handled != AdminMessageHandleResult::HANDLED_WITH_RESPONSE) && (h == AdminMessageHandleResult::HANDLED)) { // In case the message is handled it should be populated, but will not overwrite @@ -296,4 +295,4 @@ bool MeshModule::isRequestingFocus() } else return false; } -#endif +#endif \ No newline at end of file diff --git a/src/mesh/MeshModule.h b/src/mesh/MeshModule.h index 7929ba972..a88f1e6ff 100644 --- a/src/mesh/MeshModule.h +++ b/src/mesh/MeshModule.h @@ -40,7 +40,7 @@ struct UIFrameEvent { enum Action { REDRAW_ONLY, // Don't change which frames are show, just redraw, asap REGENERATE_FRAMESET, // Regenerate (change? add? remove?) screen frames, honoring requestFocus() - REGENERATE_FRAMESET_BACKGROUND, // Regenerate screen frames, attempting to remain on the same frame throughout + REGENERATE_FRAMESET_BACKGROUND, // Regenerate screen frames, Attempt to remain on the same frame throughout } action = REDRAW_ONLY; // We might want to pass additional data inside this struct at some point @@ -162,7 +162,7 @@ class MeshModule virtual Observable *getUIFrameObservable() { return NULL; } meshtastic_MeshPacket *allocAckNak(meshtastic_Routing_Error err, NodeNum to, PacketId idFrom, ChannelIndex chIndex, - uint8_t hopStart = 0, uint8_t hopLimit = 0); + uint8_t hopLimit = 0); /// Send an error response for the specified packet. meshtastic_MeshPacket *allocErrorResponse(meshtastic_Routing_Error err, const meshtastic_MeshPacket *p); diff --git a/src/mesh/MeshService.cpp b/src/mesh/MeshService.cpp index 655348bd5..8f7717585 100644 --- a/src/mesh/MeshService.cpp +++ b/src/mesh/MeshService.cpp @@ -13,6 +13,7 @@ #include "TypeConversions.h" #include "main.h" #include "mesh-pb-constants.h" +#include "meshUtils.h" #include "modules/NodeInfoModule.h" #include "modules/PositionModule.h" #include "power.h" @@ -78,17 +79,19 @@ int MeshService::handleFromRadio(const meshtastic_MeshPacket *mp) powerFSM.trigger(EVENT_PACKET_FOR_PHONE); // Possibly keep the node from sleeping nodeDB->updateFrom(*mp); // update our DB state based off sniffing every RX packet from the radio + bool isPreferredRebroadcaster = + IS_ONE_OF(config.device.role, meshtastic_Config_DeviceConfig_Role_ROUTER, meshtastic_Config_DeviceConfig_Role_REPEATER); if (mp->which_payload_variant == meshtastic_MeshPacket_decoded_tag && mp->decoded.portnum == meshtastic_PortNum_TELEMETRY_APP && mp->decoded.request_id > 0) { - LOG_DEBUG("Received telemetry response. Skip sending our NodeInfo.\n"); // because this potentially a Repeater which will - // ignore our request for its NodeInfo + LOG_DEBUG("Received telemetry response. Skip sending our NodeInfo"); // because this potentially a Repeater which will + // ignore our request for its NodeInfo } else if (mp->which_payload_variant == meshtastic_MeshPacket_decoded_tag && !nodeDB->getMeshNode(mp->from)->has_user && - nodeInfoModule) { - LOG_INFO("Heard new node on channel %d, sending NodeInfo and asking for a response.\n", mp->channel); + nodeInfoModule && !isPreferredRebroadcaster && !nodeDB->isFull()) { if (airTime->isTxAllowedChannelUtil(true)) { + LOG_INFO("Heard new node on ch. %d, send NodeInfo and ask for response", mp->channel); nodeInfoModule->sendOurNodeInfo(mp->from, true, mp->channel); } else { - LOG_DEBUG("Skip sending NodeInfo due to > 25 percent channel util.\n"); + LOG_DEBUG("Skip sending NodeInfo > 25%% ch. util"); } } @@ -130,7 +133,7 @@ bool MeshService::reloadConfig(int saveWhat) /// The owner User record just got updated, update our node DB and broadcast the info into the mesh void MeshService::reloadOwner(bool shouldSave) { - // LOG_DEBUG("reloadOwner()\n"); + // LOG_DEBUG("reloadOwner()"); // update our local data directly nodeDB->updateUser(nodeDB->getNodeNum(), owner); assert(nodeInfoModule); @@ -164,7 +167,7 @@ NodeNum MeshService::getNodenumFromRequestId(uint32_t request_id) void MeshService::handleToRadio(meshtastic_MeshPacket &p) { #if defined(ARCH_PORTDUINO) && !HAS_RADIO - // Simulates device is receiving a packet via the LoRa chip + // Simulates device received a packet via the LoRa chip if (p.decoded.portnum == meshtastic_PortNum_SIMULATOR_APP) { // Simulator packet (=Compressed packet) is encapsulated in a MeshPacket, so need to unwrap first meshtastic_Compressed scratch; @@ -180,7 +183,7 @@ void MeshService::handleToRadio(meshtastic_MeshPacket &p) // Switch the port from PortNum_SIMULATOR_APP back to the original PortNum p.decoded.portnum = decoded->portnum; } else - LOG_ERROR("Error decoding protobuf for simulator message!\n"); + LOG_ERROR("Error decoding proto for simulator message!"); } // Let SimRadio receive as if it did via its LoRa chip SimRadio::instance->startReceive(&p); @@ -222,7 +225,7 @@ ErrorCode MeshService::sendQueueStatusToPhone(const meshtastic_QueueStatus &qs, copied->mesh_packet_id = mesh_packet_id; if (toPhoneQueueStatusQueue.numFree() == 0) { - LOG_INFO("tophone queue status queue is full, discarding oldest\n"); + LOG_INFO("tophone queue status queue is full, discard oldest"); meshtastic_QueueStatus *d = toPhoneQueueStatusQueue.dequeuePtr(0); if (d) releaseQueueStatusToPool(d); @@ -252,9 +255,14 @@ void MeshService::sendToMesh(meshtastic_MeshPacket *p, RxSource src, bool ccToPh LOG_DEBUG("Can't send status to phone"); } - if (res == ERRNO_OK && ccToPhone) { // Check if p is not released in case it couldn't be sent + if ((res == ERRNO_OK || res == ERRNO_SHOULD_RELEASE) && ccToPhone) { // Check if p is not released in case it couldn't be sent sendToPhone(packetPool.allocCopy(*p)); } + + // Router may ask us to release the packet if it wasn't sent + if (res == ERRNO_SHOULD_RELEASE) { + releaseToPool(p); + } } bool MeshService::trySendPosition(NodeNum dest, bool wantReplies) @@ -263,17 +271,17 @@ bool MeshService::trySendPosition(NodeNum dest, bool wantReplies) assert(node); - if (hasValidPosition(node)) { + if (nodeDB->hasValidPosition(node)) { #if HAS_GPS && !MESHTASTIC_EXCLUDE_GPS if (positionModule) { - LOG_INFO("Sending position ping to 0x%x, wantReplies=%d, channel=%d\n", dest, wantReplies, node->channel); + LOG_INFO("Send position ping to 0x%x, wantReplies=%d, channel=%d", dest, wantReplies, node->channel); positionModule->sendOurPosition(dest, wantReplies, node->channel); return true; } } else { #endif if (nodeInfoModule) { - LOG_INFO("Sending nodeinfo ping to 0x%x, wantReplies=%d, channel=%d\n", dest, wantReplies, node->channel); + LOG_INFO("Send nodeinfo ping to 0x%x, wantReplies=%d, channel=%d", dest, wantReplies, node->channel); nodeInfoModule->sendOurNodeInfo(dest, wantReplies, node->channel); } } @@ -298,12 +306,12 @@ void MeshService::sendToPhone(meshtastic_MeshPacket *p) if (toPhoneQueue.numFree() == 0) { if (p->decoded.portnum == meshtastic_PortNum_TEXT_MESSAGE_APP || p->decoded.portnum == meshtastic_PortNum_RANGE_TEST_APP) { - LOG_WARN("ToPhone queue is full, discarding oldest\n"); + LOG_WARN("ToPhone queue is full, discard oldest"); meshtastic_MeshPacket *d = toPhoneQueue.dequeuePtr(0); if (d) releaseToPool(d); } else { - LOG_WARN("ToPhone queue is full, dropping packet.\n"); + LOG_WARN("ToPhone queue is full, drop packet"); releaseToPool(p); fromNum++; // Make sure to notify observers in case they are reconnected so they can get the packets return; @@ -316,9 +324,9 @@ void MeshService::sendToPhone(meshtastic_MeshPacket *p) void MeshService::sendMqttMessageToClientProxy(meshtastic_MqttClientProxyMessage *m) { - LOG_DEBUG("Sending mqtt message on topic '%s' to client for proxy\n", m->topic); + LOG_DEBUG("Send mqtt message on topic '%s' to client for proxy", m->topic); if (toPhoneMqttProxyQueue.numFree() == 0) { - LOG_WARN("MqttClientProxyMessagePool queue is full, discarding oldest\n"); + LOG_WARN("MqttClientProxyMessagePool queue is full, discard oldest"); meshtastic_MqttClientProxyMessage *d = toPhoneMqttProxyQueue.dequeuePtr(0); if (d) releaseMqttClientProxyMessageToPool(d); @@ -330,9 +338,9 @@ void MeshService::sendMqttMessageToClientProxy(meshtastic_MqttClientProxyMessage void MeshService::sendClientNotification(meshtastic_ClientNotification *n) { - LOG_DEBUG("Sending client notification to phone\n"); + LOG_DEBUG("Send client notification to phone"); if (toPhoneClientNotificationQueue.numFree() == 0) { - LOG_WARN("ClientNotification queue is full, discarding oldest\n"); + LOG_WARN("ClientNotification queue is full, discard oldest"); meshtastic_ClientNotification *d = toPhoneClientNotificationQueue.dequeuePtr(0); if (d) releaseClientNotificationToPool(d); @@ -380,13 +388,13 @@ int MeshService::onGPSChanged(const meshtastic::GPSStatus *newStatus) pos = gps->p; } else { // The GPS has lost lock -#ifdef GPS_EXTRAVERBOSE - LOG_DEBUG("onGPSchanged() - lost validLocation\n"); +#ifdef GPS_DEBUG + LOG_DEBUG("onGPSchanged() - lost validLocation"); #endif } // Used fixed position if configured regardless of GPS lock if (config.position.fixed_position) { - LOG_WARN("Using fixed position\n"); + LOG_WARN("Use fixed position"); pos = TypeConversions::ConvertToPosition(node->position); } @@ -394,7 +402,7 @@ int MeshService::onGPSChanged(const meshtastic::GPSStatus *newStatus) pos.time = getValidTime(RTCQualityFromNet); // In debug logs, identify position by @timestamp:stage (stage 4 = nodeDB) - LOG_DEBUG("onGPSChanged() pos@%x time=%u lat=%d lon=%d alt=%d\n", pos.timestamp, pos.time, pos.latitude_i, pos.longitude_i, + LOG_DEBUG("onGPSChanged() pos@%x time=%u lat=%d lon=%d alt=%d", pos.timestamp, pos.time, pos.latitude_i, pos.longitude_i, pos.altitude); // Update our current position in the local DB @@ -418,4 +426,4 @@ uint32_t MeshService::GetTimeSinceMeshPacket(const meshtastic_MeshPacket *mp) delta = 0; return delta; -} +} \ No newline at end of file diff --git a/src/mesh/MeshTypes.h b/src/mesh/MeshTypes.h index 27d100fbe..cf1b54c78 100644 --- a/src/mesh/MeshTypes.h +++ b/src/mesh/MeshTypes.h @@ -16,6 +16,7 @@ typedef uint32_t PacketId; // A packet sequence number #define ERRNO_NO_INTERFACES 33 #define ERRNO_UNKNOWN 32 // pick something that doesn't conflict with RH_ROUTER_ERROR_UNABLE_TO_DELIVER #define ERRNO_DISABLED 34 // the interface is disabled +#define ERRNO_SHOULD_RELEASE 35 // no error, but the packet should still be released #define ID_COUNTER_MASK (UINT32_MAX >> 22) // mask to select the counter portion of the ID /* @@ -57,4 +58,6 @@ bool isFromUs(const meshtastic_MeshPacket *p); bool isToUs(const meshtastic_MeshPacket *p); /* Some clients might not properly set priority, therefore we fix it here. */ -void fixPriority(meshtastic_MeshPacket *p); \ No newline at end of file +void fixPriority(meshtastic_MeshPacket *p); + +bool isBroadcast(uint32_t dest); \ No newline at end of file diff --git a/src/mesh/NodeDB.cpp b/src/mesh/NodeDB.cpp index 71aea7002..b38f55ae6 100644 --- a/src/mesh/NodeDB.cpp +++ b/src/mesh/NodeDB.cpp @@ -32,9 +32,14 @@ #if HAS_WIFI #include "mesh/wifi/WiFiAPClient.h" #endif +#include "SPILock.h" #include "modules/StoreForwardModule.h" #include +#include +#include #include +#include +#include #endif #ifdef ARCH_PORTDUINO @@ -55,8 +60,16 @@ meshtastic_MyNodeInfo &myNodeInfo = devicestate.my_node; meshtastic_LocalConfig config; meshtastic_LocalModuleConfig moduleConfig; meshtastic_ChannelFile channelFile; -meshtastic_OEMStore oemStore; -static bool hasOemStore = false; + +#ifdef USERPREFS_USE_ADMIN_KEY_0 +static unsigned char userprefs_admin_key_0[] = USERPREFS_USE_ADMIN_KEY_0; +#endif +#ifdef USERPREFS_USE_ADMIN_KEY_1 +static unsigned char userprefs_admin_key_1[] = USERPREFS_USE_ADMIN_KEY_1; +#endif +#ifdef USERPREFS_USE_ADMIN_KEY_2 +static unsigned char userprefs_admin_key_2[] = USERPREFS_USE_ADMIN_KEY_2; +#endif bool meshtastic_DeviceState_callback(pb_istream_t *istream, pb_ostream_t *ostream, const pb_field_iter_t *field) { @@ -102,7 +115,7 @@ static uint8_t ourMacAddr[6]; NodeDB::NodeDB() { - LOG_INFO("Initializing NodeDB\n"); + LOG_INFO("Init NodeDB"); loadFromDisk(); cleanupMeshDB(); @@ -111,6 +124,44 @@ NodeDB::NodeDB() uint32_t channelFileCRC = crc32Buffer(&channelFile, sizeof(channelFile)); int saveWhat = 0; + // bool hasUniqueId = false; + // Get device unique id +#if defined(ARCH_ESP32) && defined(ESP_EFUSE_OPTIONAL_UNIQUE_ID) + uint32_t unique_id[4]; + // ESP32 factory burns a unique id in efuse for S2+ series and evidently C3+ series + // This is used for HMACs in the esp-rainmaker AIOT platform and seems to be a good choice for us + esp_err_t err = esp_efuse_read_field_blob(ESP_EFUSE_OPTIONAL_UNIQUE_ID, unique_id, sizeof(unique_id) * 8); + if (err == ESP_OK) { + memcpy(myNodeInfo.device_id.bytes, unique_id, sizeof(unique_id)); + myNodeInfo.device_id.size = 16; + hasUniqueId = true; + } else { + LOG_WARN("Failed to read unique id from efuse"); + } +#elif defined(ARCH_NRF52) + // Nordic applies a FIPS compliant Random ID to each chip at the factory + // We concatenate the device address to the Random ID to create a unique ID for now + // This will likely utilize a crypto module in the future + uint64_t device_id_start = ((uint64_t)NRF_FICR->DEVICEID[1] << 32) | NRF_FICR->DEVICEID[0]; + uint64_t device_id_end = ((uint64_t)NRF_FICR->DEVICEADDR[1] << 32) | NRF_FICR->DEVICEADDR[0]; + memcpy(myNodeInfo.device_id.bytes, &device_id_start, sizeof(device_id_start)); + memcpy(myNodeInfo.device_id.bytes + sizeof(device_id_start), &device_id_end, sizeof(device_id_end)); + myNodeInfo.device_id.size = 16; + // Uncomment below to print the device id + // hasUniqueId = true; +#else + // FIXME - implement for other platforms +#endif + + // if (hasUniqueId) { + // std::string deviceIdHex; + // for (size_t i = 0; i < myNodeInfo.device_id.size; ++i) { + // char buf[3]; + // snprintf(buf, sizeof(buf), "%02X", myNodeInfo.device_id.bytes[i]); + // deviceIdHex += buf; + // } + // LOG_DEBUG("Device ID (HEX): %s", deviceIdHex.c_str()); + // } // likewise - we always want the app requirements to come from the running appload myNodeInfo.min_app_version = 30200; // format is Mmmss (where M is 1+the numeric major number. i.e. 30200 means 2.2.00 @@ -134,21 +185,23 @@ NodeDB::NodeDB() } #if !(MESHTASTIC_EXCLUDE_PKI_KEYGEN || MESHTASTIC_EXCLUDE_PKI) - bool keygenSuccess = false; - if (config.security.private_key.size == 32) { - if (crypto->regeneratePublicKey(config.security.public_key.bytes, config.security.private_key.bytes)) { + if (!owner.is_licensed) { + bool keygenSuccess = false; + if (config.security.private_key.size == 32) { + if (crypto->regeneratePublicKey(config.security.public_key.bytes, config.security.private_key.bytes)) { + keygenSuccess = true; + } + } else { + LOG_INFO("Generate new PKI keys"); + crypto->generateKeyPair(config.security.public_key.bytes, config.security.private_key.bytes); keygenSuccess = true; } - } else { - LOG_INFO("Generating new PKI keys\n"); - crypto->generateKeyPair(config.security.public_key.bytes, config.security.private_key.bytes); - keygenSuccess = true; - } - if (keygenSuccess) { - config.security.public_key.size = 32; - config.security.private_key.size = 32; - owner.public_key.size = 32; - memcpy(owner.public_key.bytes, config.security.public_key.bytes, 32); + if (keygenSuccess) { + config.security.public_key.size = 32; + config.security.private_key.size = 32; + owner.public_key.size = 32; + memcpy(owner.public_key.bytes, config.security.public_key.bytes, 32); + } } #elif !(MESHTASTIC_EXCLUDE_PKI) // Calculate Curve25519 public and private keys @@ -167,11 +220,30 @@ NodeDB::NodeDB() preferences.begin("meshtastic", false); myNodeInfo.reboot_count = preferences.getUInt("rebootCounter", 0); preferences.end(); - LOG_DEBUG("Number of Device Reboots: %d\n", myNodeInfo.reboot_count); + LOG_DEBUG("Number of Device Reboots: %d", myNodeInfo.reboot_count); #endif resetRadioConfig(); // If bogus settings got saved, then fix them - // nodeDB->LOG_DEBUG("region=%d, NODENUM=0x%x, dbsize=%d\n", config.lora.region, myNodeInfo.my_node_num, numMeshNodes); + // nodeDB->LOG_DEBUG("region=%d, NODENUM=0x%x, dbsize=%d", config.lora.region, myNodeInfo.my_node_num, numMeshNodes); + + // If we are setup to broadcast on the default channel, ensure that the telemetry intervals are coerced to the minimum value + // of 30 minutes or more + if (channels.isDefaultChannel(channels.getPrimaryIndex())) { + LOG_DEBUG("Coerce telemetry to min of 30 minutes on defaults"); + moduleConfig.telemetry.device_update_interval = Default::getConfiguredOrMinimumValue( + moduleConfig.telemetry.device_update_interval, min_default_telemetry_interval_secs); + moduleConfig.telemetry.environment_update_interval = Default::getConfiguredOrMinimumValue( + moduleConfig.telemetry.environment_update_interval, min_default_telemetry_interval_secs); + moduleConfig.telemetry.air_quality_interval = Default::getConfiguredOrMinimumValue( + moduleConfig.telemetry.air_quality_interval, min_default_telemetry_interval_secs); + moduleConfig.telemetry.power_update_interval = Default::getConfiguredOrMinimumValue( + moduleConfig.telemetry.power_update_interval, min_default_telemetry_interval_secs); + moduleConfig.telemetry.health_update_interval = Default::getConfiguredOrMinimumValue( + moduleConfig.telemetry.health_update_interval, min_default_telemetry_interval_secs); + } + // Ensure that the neighbor info update interval is coerced to the minimum + moduleConfig.neighbor_info.update_interval = + Default::getConfiguredOrMinimumValue(moduleConfig.neighbor_info.update_interval, min_neighbor_info_broadcast_secs); if (devicestateCRC != crc32Buffer(&devicestate, sizeof(devicestate))) saveWhat |= SEGMENT_DEVICESTATE; @@ -184,6 +256,31 @@ NodeDB::NodeDB() config.position.gps_mode = meshtastic_Config_PositionConfig_GpsMode_ENABLED; config.position.gps_enabled = 0; } +#ifdef USERPREFS_FIXED_GPS + if (myNodeInfo.reboot_count == 1) { // Check if First boot ever or after Factory Reset. + meshtastic_Position fixedGPS = meshtastic_Position_init_default; +#ifdef USERPREFS_FIXED_GPS_LAT + fixedGPS.latitude_i = (int32_t)(USERPREFS_FIXED_GPS_LAT * 1e7); + fixedGPS.has_latitude_i = true; +#endif +#ifdef USERPREFS_FIXED_GPS_LON + fixedGPS.longitude_i = (int32_t)(USERPREFS_FIXED_GPS_LON * 1e7); + fixedGPS.has_longitude_i = true; +#endif +#ifdef USERPREFS_FIXED_GPS_ALT + fixedGPS.altitude = USERPREFS_FIXED_GPS_ALT; + fixedGPS.has_altitude = true; +#endif +#if defined(USERPREFS_FIXED_GPS_LAT) && defined(USERPREFS_FIXED_GPS_LON) + fixedGPS.location_source = meshtastic_Position_LocSource_LOC_MANUAL; + config.has_position = true; + info->has_position = true; + info->position = TypeConversions::ConvertToPositionLite(fixedGPS); + nodeDB->setLocalPosition(fixedGPS); + config.position.fixed_position = true; +#endif + } +#endif saveToDisk(saveWhat); } @@ -208,6 +305,11 @@ bool isToUs(const meshtastic_MeshPacket *p) return p->to == nodeDB->getNodeNum(); } +bool isBroadcast(uint32_t dest) +{ + return dest == NODENUM_BROADCAST || dest == NODENUM_BROADCAST_NO_LORA; +} + bool NodeDB::resetRadioConfig(bool factory_reset) { bool didFactoryReset = false; @@ -219,7 +321,7 @@ bool NodeDB::resetRadioConfig(bool factory_reset) } if (channelFile.channels_count != MAX_NUM_CHANNELS) { - LOG_INFO("Setting default channel and radio preferences!\n"); + LOG_INFO("Set default channel and radio preferences!"); channels.initDefaults(); } @@ -230,7 +332,7 @@ bool NodeDB::resetRadioConfig(bool factory_reset) initRegion(); if (didFactoryReset) { - LOG_INFO("Rebooting due to factory reset"); + LOG_INFO("Reboot due to factory reset"); screen->startAlert("Rebooting..."); rebootAtMsec = millis() + (5 * 1000); } @@ -240,12 +342,12 @@ bool NodeDB::resetRadioConfig(bool factory_reset) bool NodeDB::factoryReset(bool eraseBleBonds) { - LOG_INFO("Performing factory reset!\n"); + LOG_INFO("Perform factory reset!"); // first, remove the "/prefs" (this removes most prefs) rmDir("/prefs"); #ifdef FSCom if (FSCom.exists("/static/rangetest.csv") && !FSCom.remove("/static/rangetest.csv")) { - LOG_ERROR("Could not remove rangetest.csv file\n"); + LOG_ERROR("Could not remove rangetest.csv file"); } #endif // second, install default state (this will deal with the duplicate mac address issue) @@ -256,14 +358,14 @@ bool NodeDB::factoryReset(bool eraseBleBonds) // third, write everything to disk saveToDisk(); if (eraseBleBonds) { - LOG_INFO("Erasing BLE bonds\n"); + LOG_INFO("Erase BLE bonds"); #ifdef ARCH_ESP32 // This will erase what's in NVS including ssl keys, persistent variables and ble pairing nvs_flash_erase(); #endif #ifdef ARCH_NRF52 Bluefruit.begin(); - LOG_INFO("Clearing bluetooth bonds!\n"); + LOG_INFO("Clear bluetooth bonds!"); bond_print_list(BLE_GAP_ROLE_PERIPH); bond_print_list(BLE_GAP_ROLE_CENTRAL); Bluefruit.Periph.clearBonds(); @@ -280,7 +382,7 @@ void NodeDB::installDefaultConfig(bool preserveKey = false) if (shouldPreserveKey) { memcpy(private_key_temp, config.security.private_key.bytes, config.security.private_key.size); } - LOG_INFO("Installing default LocalConfig\n"); + LOG_INFO("Install default LocalConfig"); memset(&config, 0, sizeof(meshtastic_LocalConfig)); config.version = DEVICESTATE_CUR_VER; config.has_device = true; @@ -314,11 +416,37 @@ void NodeDB::installDefaultConfig(bool preserveKey = false) #else config.lora.ignore_mqtt = false; #endif -#ifdef USERPREFS_USE_ADMIN_KEY - memcpy(config.security.admin_key[0].bytes, USERPREFS_ADMIN_KEY, 32); - config.security.admin_key[0].size = 32; - config.security.admin_key_count = 1; + // Initialize admin_key_count to zero + byte numAdminKeys = 0; + +#ifdef USERPREFS_USE_ADMIN_KEY_0 + // Check if USERPREFS_ADMIN_KEY_0 is non-empty + if (sizeof(userprefs_admin_key_0) > 0) { + memcpy(config.security.admin_key[0].bytes, userprefs_admin_key_0, 32); + config.security.admin_key[0].size = 32; + numAdminKeys++; + } #endif + +#ifdef USERPREFS_USE_ADMIN_KEY_1 + // Check if USERPREFS_ADMIN_KEY_1 is non-empty + if (sizeof(userprefs_admin_key_1) > 0) { + memcpy(config.security.admin_key[1].bytes, userprefs_admin_key_1, 32); + config.security.admin_key[1].size = 32; + numAdminKeys++; + } +#endif + +#ifdef USERPREFS_USE_ADMIN_KEY_2 + // Check if USERPREFS_ADMIN_KEY_2 is non-empty + if (sizeof(userprefs_admin_key_2) > 0) { + memcpy(config.security.admin_key[2].bytes, userprefs_admin_key_2, 32); + config.security.admin_key[2].size = 32; + numAdminKeys++; + } +#endif + config.security.admin_key_count = numAdminKeys; + if (shouldPreserveKey) { config.security.private_key.size = 32; memcpy(config.security.private_key.bytes, private_key_temp, config.security.private_key.size); @@ -371,8 +499,13 @@ void NodeDB::installDefaultConfig(bool preserveKey = false) #else bool hasScreen = screen_found.port != ScanI2C::I2CPort::NO_I2C; #endif +#ifdef USERPREFS_FIXED_BLUETOOTH + config.bluetooth.fixed_pin = USERPREFS_FIXED_BLUETOOTH; + config.bluetooth.mode = meshtastic_Config_BluetoothConfig_PairingMode_FIXED_PIN; +#else config.bluetooth.mode = hasScreen ? meshtastic_Config_BluetoothConfig_PairingMode_RANDOM_PIN : meshtastic_Config_BluetoothConfig_PairingMode_FIXED_PIN; +#endif // for backward compat, default position flags are ALT+MSL config.position.position_flags = (meshtastic_Config_PositionConfig_PositionFlags_ALTITUDE | meshtastic_Config_PositionConfig_PositionFlags_ALTITUDE_MSL | @@ -385,7 +518,7 @@ void NodeDB::installDefaultConfig(bool preserveKey = false) #ifdef RAK4630 config.display.wake_on_tap_or_motion = true; #endif -#ifdef T_WATCH_S3 +#if defined(T_WATCH_S3) || defined(SENSECAP_INDICATOR) config.display.screen_on_secs = 30; config.display.wake_on_tap_or_motion = true; #endif @@ -409,7 +542,7 @@ void NodeDB::initConfigIntervals() config.display.screen_on_secs = default_screen_on_secs; -#if defined(T_WATCH_S3) || defined(T_DECK) +#if defined(T_WATCH_S3) || defined(T_DECK) || defined(RAK14014) config.power.is_power_saving = true; config.display.screen_on_secs = 30; config.power.wait_bluetooth_secs = 30; @@ -418,7 +551,7 @@ void NodeDB::initConfigIntervals() void NodeDB::installDefaultModuleConfig() { - LOG_INFO("Installing default ModuleConfig\n"); + LOG_INFO("Install default ModuleConfig"); memset(&moduleConfig, 0, sizeof(meshtastic_ModuleConfig)); moduleConfig.version = DEVICESTATE_CUR_VER; @@ -496,8 +629,10 @@ void NodeDB::installRoleDefaults(meshtastic_Config_DeviceConfig_Role role) if (role == meshtastic_Config_DeviceConfig_Role_ROUTER) { initConfigIntervals(); initModuleConfigIntervals(); + config.device.rebroadcast_mode = meshtastic_Config_DeviceConfig_RebroadcastMode_CORE_PORTNUMS_ONLY; } else if (role == meshtastic_Config_DeviceConfig_Role_REPEATER) { config.display.screen_on_secs = 1; + config.device.rebroadcast_mode = meshtastic_Config_DeviceConfig_RebroadcastMode_CORE_PORTNUMS_ONLY; } else if (role == meshtastic_Config_DeviceConfig_Role_SENSOR) { moduleConfig.telemetry.environment_measurement_enabled = true; moduleConfig.telemetry.environment_update_interval = 300; @@ -551,14 +686,15 @@ void NodeDB::initModuleConfigIntervals() void NodeDB::installDefaultChannels() { - LOG_INFO("Installing default ChannelFile\n"); + LOG_INFO("Install default ChannelFile"); memset(&channelFile, 0, sizeof(meshtastic_ChannelFile)); channelFile.version = DEVICESTATE_CUR_VER; } void NodeDB::resetNodes() { - clearLocalPosition(); + if (!config.position.fixed_position) + clearLocalPosition(); numMeshNodes = 1; std::fill(devicestate.node_db_lite.begin() + 1, devicestate.node_db_lite.end(), meshtastic_NodeInfoLite()); devicestate.has_rx_text_message = false; @@ -580,7 +716,7 @@ void NodeDB::removeNodeByNum(NodeNum nodeNum) numMeshNodes -= removed; std::fill(devicestate.node_db_lite.begin() + numMeshNodes, devicestate.node_db_lite.begin() + numMeshNodes + 1, meshtastic_NodeInfoLite()); - LOG_DEBUG("NodeDB::removeNodeByNum purged %d entries. Saving changes...\n", removed); + LOG_DEBUG("NodeDB::removeNodeByNum purged %d entries. Save changes", removed); saveDeviceStateToDisk(); } @@ -612,12 +748,12 @@ void NodeDB::cleanupMeshDB() numMeshNodes -= removed; std::fill(devicestate.node_db_lite.begin() + numMeshNodes, devicestate.node_db_lite.begin() + numMeshNodes + removed, meshtastic_NodeInfoLite()); - LOG_DEBUG("cleanupMeshDB purged %d entries\n", removed); + LOG_DEBUG("cleanupMeshDB purged %d entries", removed); } void NodeDB::installDefaultDeviceState() { - LOG_INFO("Installing default DeviceState\n"); + LOG_INFO("Install default DeviceState"); // memset(&devicestate, 0, sizeof(meshtastic_DeviceState)); numMeshNodes = 0; @@ -671,11 +807,11 @@ void NodeDB::pickNewNodeNum() NodeNum candidate = random(NUM_RESERVED, LONG_MAX); // try a new random choice if (found) LOG_WARN("NOTE! Our desired nodenum 0x%x is invalid or in use, by MAC ending in 0x%02x%02x vs our 0x%02x%02x, so " - "trying for 0x%x\n", + "trying for 0x%x", nodeNum, found->user.macaddr[4], found->user.macaddr[5], ourMacAddr[4], ourMacAddr[5], candidate); nodeNum = candidate; } - LOG_DEBUG("Using nodenum 0x%x \n", nodeNum); + LOG_DEBUG("Use nodenum 0x%x ", nodeNum); myNodeInfo.my_node_num = nodeNum; } @@ -684,7 +820,6 @@ 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 LoadFileResult */ LoadFileResult NodeDB::loadProto(const char *filename, size_t protoSize, size_t objSize, const pb_msgdesc_t *fields, @@ -696,23 +831,23 @@ LoadFileResult NodeDB::loadProto(const char *filename, size_t protoSize, size_t auto f = FSCom.open(filename, FILE_O_READ); if (f) { - LOG_INFO("Loading %s\n", filename); + LOG_INFO("Load %s", filename); pb_istream_t stream = {&readcb, &f, protoSize}; memset(dest_struct, 0, objSize); if (!pb_decode(&stream, fields, dest_struct)) { - LOG_ERROR("Error: can't decode protobuf %s\n", PB_GET_ERROR(&stream)); + LOG_ERROR("Error: can't decode protobuf %s", PB_GET_ERROR(&stream)); state = LoadFileResult::DECODE_FAILED; } else { - LOG_INFO("Loaded %s successfully\n", filename); + LOG_INFO("Loaded %s successfully", filename); state = LoadFileResult::LOAD_SUCCESS; } f.close(); } else { - LOG_ERROR("Could not open / read %s\n", filename); + LOG_ERROR("Could not open / read %s", filename); } #else - LOG_ERROR("ERROR: Filesystem not implemented\n"); + LOG_ERROR("ERROR: Filesystem not implemented"); state = LoadFileResult::NO_FILESYSTEM; #endif return state; @@ -724,8 +859,13 @@ void NodeDB::loadFromDisk() 0; // Mark the current device state as completely unusable, so that if we fail reading the entire file from // disk we will still factoryReset to restore things. +#ifdef ARCH_ESP32 + if (FSCom.exists("/static/static")) + rmDir("/static/static"); // Remove bad static web files bundle from initial 2.5.13 release +#endif + // static DeviceState scratch; We no longer read into a tempbuf because this structure is 15KB of valuable RAM - auto state = loadProto(prefFileName, sizeof(meshtastic_DeviceState) + MAX_NUM_NODES * sizeof(meshtastic_NodeInfo), + auto state = loadProto(prefFileName, sizeof(meshtastic_DeviceState) + MAX_NUM_NODES_FS * sizeof(meshtastic_NodeInfo), sizeof(meshtastic_DeviceState), &meshtastic_DeviceState_msg, &devicestate); // See https://github.com/meshtastic/firmware/issues/4184#issuecomment-2269390786 @@ -737,14 +877,17 @@ void NodeDB::loadFromDisk() // installDefaultDeviceState(); // Our in RAM copy might now be corrupt //} else { if (devicestate.version < DEVICESTATE_MIN_VER) { - LOG_WARN("Devicestate %d is old, discarding\n", devicestate.version); + LOG_WARN("Devicestate %d is old, discard", devicestate.version); installDefaultDeviceState(); } else { - LOG_INFO("Loaded saved devicestate version %d, with nodecount: %d\n", devicestate.version, - devicestate.node_db_lite.size()); + LOG_INFO("Loaded saved devicestate version %d, with nodecount: %d", devicestate.version, devicestate.node_db_lite.size()); meshNodes = &devicestate.node_db_lite; numMeshNodes = devicestate.node_db_lite.size(); } + if (numMeshNodes > MAX_NUM_NODES) { + LOG_WARN("Node count %d exceeds MAX_NUM_NODES %d, truncating", numMeshNodes, MAX_NUM_NODES); + numMeshNodes = MAX_NUM_NODES; + } meshNodes->resize(MAX_NUM_NODES); state = loadProto(configFileName, meshtastic_LocalConfig_size, sizeof(meshtastic_LocalConfig), &meshtastic_LocalConfig_msg, @@ -753,23 +896,71 @@ void NodeDB::loadFromDisk() installDefaultConfig(); // Our in RAM copy might now be corrupt } else { if (config.version < DEVICESTATE_MIN_VER) { - LOG_WARN("config %d is old, discarding\n", config.version); + LOG_WARN("config %d is old, discard", config.version); installDefaultConfig(true); } else { - LOG_INFO("Loaded saved config version %d\n", config.version); + LOG_INFO("Loaded saved config version %d", config.version); } } + // Make sure we load hard coded admin keys even when the configuration file has none. + // Initialize admin_key_count to zero + byte numAdminKeys = 0; + uint16_t sum = 0; +#ifdef USERPREFS_USE_ADMIN_KEY_0 + for (uint8_t b = 0; b < 32; b++) { + sum += config.security.admin_key[0].bytes[b]; + } + if (sum == 0) { + numAdminKeys += 1; + LOG_INFO("Admin 0 key zero. Loading hard coded key from user preferences."); + memcpy(config.security.admin_key[0].bytes, userprefs_admin_key_0, 32); + config.security.admin_key[0].size = 32; + config.security.admin_key_count = numAdminKeys; + saveToDisk(SEGMENT_CONFIG); + } +#endif + +#ifdef USERPREFS_USE_ADMIN_KEY_1 + sum = 0; + for (uint8_t b = 0; b < 32; b++) { + sum += config.security.admin_key[1].bytes[b]; + } + if (sum == 0) { + numAdminKeys += 1; + LOG_INFO("Admin 1 key zero. Loading hard coded key from user preferences."); + memcpy(config.security.admin_key[1].bytes, userprefs_admin_key_1, 32); + config.security.admin_key[1].size = 32; + config.security.admin_key_count = numAdminKeys; + saveToDisk(SEGMENT_CONFIG); + } +#endif + +#ifdef USERPREFS_USE_ADMIN_KEY_2 + sum = 0; + for (uint8_t b = 0; b < 32; b++) { + sum += config.security.admin_key[2].bytes[b]; + } + if (sum == 0) { + numAdminKeys += 1; + LOG_INFO("Admin 2 key zero. Loading hard coded key from user preferences."); + memcpy(config.security.admin_key[2].bytes, userprefs_admin_key_2, 32); + config.security.admin_key[2].size = 32; + config.security.admin_key_count = numAdminKeys; + saveToDisk(SEGMENT_CONFIG); + } +#endif + state = loadProto(moduleConfigFileName, meshtastic_LocalModuleConfig_size, sizeof(meshtastic_LocalModuleConfig), &meshtastic_LocalModuleConfig_msg, &moduleConfig); if (state != LoadFileResult::LOAD_SUCCESS) { installDefaultModuleConfig(); // Our in RAM copy might now be corrupt } else { if (moduleConfig.version < DEVICESTATE_MIN_VER) { - LOG_WARN("moduleConfig %d is old, discarding\n", moduleConfig.version); + LOG_WARN("moduleConfig %d is old, discard", moduleConfig.version); installDefaultModuleConfig(); } else { - LOG_INFO("Loaded saved moduleConfig version %d\n", moduleConfig.version); + LOG_INFO("Loaded saved moduleConfig version %d", moduleConfig.version); } } @@ -779,22 +970,16 @@ void NodeDB::loadFromDisk() installDefaultChannels(); // Our in RAM copy might now be corrupt } else { if (channelFile.version < DEVICESTATE_MIN_VER) { - LOG_WARN("channelFile %d is old, discarding\n", channelFile.version); + LOG_WARN("channelFile %d is old, discard", channelFile.version); installDefaultChannels(); } else { - LOG_INFO("Loaded saved channelFile version %d\n", channelFile.version); + LOG_INFO("Loaded saved channelFile version %d", channelFile.version); } } - state = loadProto(oemConfigFile, meshtastic_OEMStore_size, sizeof(meshtastic_OEMStore), &meshtastic_OEMStore_msg, &oemStore); - if (state == LoadFileResult::LOAD_SUCCESS) { - LOG_INFO("Loaded OEMStore\n"); - hasOemStore = true; - } - // 2.4.X - configuration migration to update new default intervals if (moduleConfig.version < 23) { - LOG_DEBUG("ModuleConfig version %d is stale, upgrading to new default intervals\n", moduleConfig.version); + LOG_DEBUG("ModuleConfig version %d is stale, upgrading to new default intervals", moduleConfig.version); moduleConfig.version = DEVICESTATE_CUR_VER; if (moduleConfig.telemetry.device_update_interval == 900) moduleConfig.telemetry.device_update_interval = 0; @@ -817,15 +1002,18 @@ void NodeDB::loadFromDisk() bool NodeDB::saveProto(const char *filename, size_t protoSize, const pb_msgdesc_t *fields, const void *dest_struct, bool fullAtomic) { +#ifdef ARCH_ESP32 + concurrency::LockGuard g(spiLock); +#endif bool okay = false; #ifdef FSCom auto f = SafeFile(filename, fullAtomic); - LOG_INFO("Saving %s\n", filename); + LOG_INFO("Save %s", filename); pb_ostream_t stream = {&writecb, static_cast(&f), protoSize}; if (!pb_encode(&stream, fields, dest_struct)) { - LOG_ERROR("Error: can't encode protobuf %s\n", PB_GET_ERROR(&stream)); + LOG_ERROR("Error: can't encode protobuf %s", PB_GET_ERROR(&stream)); } else { okay = true; } @@ -833,10 +1021,10 @@ bool NodeDB::saveProto(const char *filename, size_t protoSize, const pb_msgdesc_ bool writeSucceeded = f.close(); if (!okay || !writeSucceeded) { - LOG_ERROR("Can't write prefs!\n"); + LOG_ERROR("Can't write prefs!"); } #else - LOG_ERROR("ERROR: Filesystem not implemented\n"); + LOG_ERROR("ERROR: Filesystem not implemented"); #endif return okay; } @@ -898,11 +1086,6 @@ bool NodeDB::saveToDiskNoRetry(int saveWhat) saveProto(moduleConfigFileName, meshtastic_LocalModuleConfig_size, &meshtastic_LocalModuleConfig_msg, &moduleConfig); } - // We might need to rewrite the OEM data if we are reformatting the FS - if ((saveWhat & SEGMENT_OEM) && hasOemStore) { - success &= saveProto(oemConfigFile, meshtastic_OEMStore_size, &meshtastic_OEMStore_msg, &oemStore); - } - if (saveWhat & SEGMENT_CHANNELS) { success &= saveChannelsToDisk(); } @@ -919,12 +1102,10 @@ bool NodeDB::saveToDisk(int saveWhat) bool success = saveToDiskNoRetry(saveWhat); if (!success) { - LOG_ERROR("Failed to save to disk, retrying...\n"); + LOG_ERROR("Failed to save to disk, retrying"); #ifdef ARCH_NRF52 // @geeksville is not ready yet to say we should do this on other platforms. See bug #4184 discussion FSCom.format(); - // We need to rewrite the OEM data if we are reformatting the FS - saveWhat |= SEGMENT_OEM; #endif success = saveToDiskNoRetry(saveWhat); @@ -997,7 +1178,7 @@ void NodeDB::updatePosition(uint32_t nodeId, const meshtastic_Position &p, RxSou if (src == RX_SRC_LOCAL) { // Local packet, fully authoritative - LOG_INFO("updatePosition LOCAL pos@%x time=%u lat=%d lon=%d alt=%d\n", p.timestamp, p.time, p.latitude_i, p.longitude_i, + LOG_INFO("updatePosition LOCAL pos@%x time=%u lat=%d lon=%d alt=%d", p.timestamp, p.time, p.latitude_i, p.longitude_i, p.altitude); setLocalPosition(p); @@ -1005,7 +1186,7 @@ void NodeDB::updatePosition(uint32_t nodeId, const meshtastic_Position &p, RxSou } else if ((p.time > 0) && !p.latitude_i && !p.longitude_i && !p.timestamp && !p.location_source) { // FIXME SPECIAL TIME SETTING PACKET FROM EUD TO RADIO // (stop-gap fix for issue #900) - LOG_DEBUG("updatePosition SPECIAL time setting time=%u\n", p.time); + LOG_DEBUG("updatePosition SPECIAL time setting time=%u", p.time); info->position.time = p.time; } else { // Be careful to only update fields that have been set by the REMOTE sender @@ -1013,7 +1194,7 @@ void NodeDB::updatePosition(uint32_t nodeId, const meshtastic_Position &p, RxSou // recorded based on the packet rxTime // // FIXME perhaps handle RX_SRC_USER separately? - LOG_INFO("updatePosition REMOTE node=0x%x time=%u lat=%d lon=%d\n", nodeId, p.time, p.latitude_i, p.longitude_i); + LOG_INFO("updatePosition REMOTE node=0x%x time=%u lat=%d lon=%d", nodeId, p.time, p.latitude_i, p.longitude_i); // First, back up fields that we want to protect from overwrite uint32_t tmp_time = info->position.time; @@ -1043,9 +1224,9 @@ void NodeDB::updateTelemetry(uint32_t nodeId, const meshtastic_Telemetry &t, RxS if (src == RX_SRC_LOCAL) { // Local packet, fully authoritative - LOG_DEBUG("updateTelemetry LOCAL\n"); + LOG_DEBUG("updateTelemetry LOCAL"); } else { - LOG_DEBUG("updateTelemetry REMOTE node=0x%x \n", nodeId); + LOG_DEBUG("updateTelemetry REMOTE node=0x%x ", nodeId); } info->device_metrics = t.variant.device_metrics; info->has_device_metrics = true; @@ -1062,16 +1243,16 @@ bool NodeDB::updateUser(uint32_t nodeId, meshtastic_User &p, uint8_t channelInde return false; } - LOG_DEBUG("old user %s/%s, channel=%d\n", info->user.long_name, info->user.short_name, info->channel); + LOG_DEBUG("old user %s/%s, channel=%d", info->user.long_name, info->user.short_name, info->channel); #if !(MESHTASTIC_EXCLUDE_PKI) if (p.public_key.size > 0) { printBytes("Incoming Pubkey: ", p.public_key.bytes, 32); if (info->user.public_key.size > 0) { // if we have a key for this user already, don't overwrite with a new one - LOG_INFO("Public Key set for node, not updating!\n"); + LOG_INFO("Public Key set for node, not updating!"); // we copy the key into the incoming packet, to prevent overwrite memcpy(p.public_key.bytes, info->user.public_key.bytes, 32); } else { - LOG_INFO("Updating Node Pubkey!\n"); + LOG_INFO("Update Node Pubkey!"); } } #endif @@ -1086,8 +1267,7 @@ bool NodeDB::updateUser(uint32_t nodeId, meshtastic_User &p, uint8_t channelInde } if (nodeId != getNodeNum()) info->channel = channelIndex; // Set channel we need to use to reach this node (but don't set our own channel) - LOG_DEBUG("updating changed=%d user %s/%s, channel=%d\n", changed, info->user.long_name, info->user.short_name, - info->channel); + LOG_DEBUG("Update changed=%d user %s/%s, channel=%d", changed, info->user.long_name, info->user.short_name, info->channel); info->has_user = true; if (changed) { @@ -1095,10 +1275,14 @@ bool NodeDB::updateUser(uint32_t nodeId, meshtastic_User &p, uint8_t channelInde powerFSM.trigger(EVENT_NODEDB_UPDATED); notifyObservers(true); // Force an update whether or not our node counts have changed - // We just changed something about the user, store our DB - Throttle::execute( - &lastNodeDbSave, ONE_MINUTE_MS, []() { nodeDB->saveToDisk(SEGMENT_DEVICESTATE); }, - []() { LOG_DEBUG("Deferring NodeDB saveToDisk for now\n"); }); // since we saved less than a minute ago + // We just changed something about a User, + // store our DB unless we just did so less than a minute ago + if (!Throttle::isWithinTimespanMs(lastNodeDbSave, ONE_MINUTE_MS)) { + saveToDisk(SEGMENT_DEVICESTATE); + lastNodeDbSave = millis(); + } else { + LOG_DEBUG("Defer NodeDB saveToDisk for now"); + } } return changed; @@ -1109,7 +1293,7 @@ bool NodeDB::updateUser(uint32_t nodeId, meshtastic_User &p, uint8_t channelInde void NodeDB::updateFrom(const meshtastic_MeshPacket &mp) { if (mp.which_payload_variant == meshtastic_MeshPacket_decoded_tag && mp.from) { - LOG_DEBUG("Update DB node 0x%x, rx_time=%u\n", mp.from, mp.rx_time); + LOG_DEBUG("Update DB node 0x%x, rx_time=%u", mp.from, mp.rx_time); meshtastic_NodeInfoLite *info = getOrCreateMeshNode(getFrom(&mp)); if (!info) { @@ -1152,16 +1336,20 @@ meshtastic_NodeInfoLite *NodeDB::getMeshNode(NodeNum n) return NULL; } +// returns true if the maximum number of nodes is reached or we are running low on memory +bool NodeDB::isFull() +{ + return (numMeshNodes >= MAX_NUM_NODES) || (memGet.getFreeHeap() < MINIMUM_SAFE_FREE_HEAP); +} + /// Find a node in our DB, create an empty NodeInfo if missing meshtastic_NodeInfoLite *NodeDB::getOrCreateMeshNode(NodeNum n) { meshtastic_NodeInfoLite *lite = getMeshNode(n); if (!lite) { - if ((numMeshNodes >= MAX_NUM_NODES) || (memGet.getFreeHeap() < MINIMUM_SAFE_FREE_HEAP)) { - if (screen) - screen->print("Warn: node database full!\nErasing oldest entry\n"); - LOG_WARN("Node database full with %i nodes and %i bytes free! Erasing oldest entry\n", numMeshNodes, + if (isFull()) { + LOG_INFO("Node database full with %i nodes and %i bytes free. Erasing oldest entry", numMeshNodes, memGet.getFreeHeap()); // look for oldest node and erase it uint32_t oldest = UINT32_MAX; @@ -1170,12 +1358,12 @@ meshtastic_NodeInfoLite *NodeDB::getOrCreateMeshNode(NodeNum n) int oldestBoringIndex = -1; for (int i = 1; i < numMeshNodes; i++) { // Simply the oldest non-favorite node - if (!meshNodes->at(i).is_favorite && meshNodes->at(i).last_heard < oldest) { + if (!meshNodes->at(i).is_favorite && !meshNodes->at(i).is_ignored && meshNodes->at(i).last_heard < oldest) { oldest = meshNodes->at(i).last_heard; oldestIndex = i; } // The oldest "boring" node - if (!meshNodes->at(i).is_favorite && meshNodes->at(i).user.public_key.size == 0 && + if (!meshNodes->at(i).is_favorite && !meshNodes->at(i).is_ignored && meshNodes->at(i).user.public_key.size == 0 && meshNodes->at(i).last_heard < oldestBoring) { oldestBoring = meshNodes->at(i).last_heard; oldestBoringIndex = i; @@ -1197,12 +1385,19 @@ meshtastic_NodeInfoLite *NodeDB::getOrCreateMeshNode(NodeNum n) // everything is missing except the nodenum memset(lite, 0, sizeof(*lite)); lite->num = n; - LOG_INFO("Adding node to database with %i nodes and %i bytes free!\n", numMeshNodes, memGet.getFreeHeap()); + LOG_INFO("Adding node to database with %i nodes and %i bytes free!", numMeshNodes, memGet.getFreeHeap()); } return lite; } +/// Sometimes we will have Position objects that only have a time, so check for +/// valid lat/lon +bool NodeDB::hasValidPosition(const meshtastic_NodeInfoLite *n) +{ + return n->has_position && (n->position.latitude_i != 0 || n->position.longitude_i != 0); +} + /// Record an error that should be reported via analytics void recordCriticalError(meshtastic_CriticalErrorCode code, uint32_t address, const char *filename) { @@ -1211,9 +1406,9 @@ void recordCriticalError(meshtastic_CriticalErrorCode code, uint32_t address, co if (screen) screen->print(lcd.c_str()); if (filename) { - LOG_ERROR("NOTE! Recording critical error %d at %s:%lu\n", code, filename, address); + LOG_ERROR("NOTE! Record critical error %d at %s:%lu", code, filename, address); } else { - LOG_ERROR("NOTE! Recording critical error %d, address=0x%lx\n", code, address); + LOG_ERROR("NOTE! Record critical error %d, address=0x%lx", code, address); } // Record error to DB @@ -1222,7 +1417,7 @@ void recordCriticalError(meshtastic_CriticalErrorCode code, uint32_t address, co // Currently portuino is mostly used for simulation. Make sure the user notices something really bad happened #ifdef ARCH_PORTDUINO - LOG_ERROR("A critical failure occurred, portduino is exiting..."); + LOG_ERROR("A critical failure occurred, portduino is exiting"); exit(2); #endif } \ No newline at end of file diff --git a/src/mesh/NodeDB.h b/src/mesh/NodeDB.h index 1be61759a..7e51a1240 100644 --- a/src/mesh/NodeDB.h +++ b/src/mesh/NodeDB.h @@ -21,7 +21,6 @@ DeviceState versions used to be defined in the .proto file but really only this #define SEGMENT_MODULECONFIG 2 #define SEGMENT_DEVICESTATE 4 #define SEGMENT_CHANNELS 8 -#define SEGMENT_OEM 16 #define DEVICESTATE_CUR_VER 23 #define DEVICESTATE_MIN_VER 22 @@ -31,7 +30,6 @@ extern meshtastic_ChannelFile channelFile; extern meshtastic_MyNodeInfo &myNodeInfo; extern meshtastic_LocalConfig config; extern meshtastic_LocalModuleConfig moduleConfig; -extern meshtastic_OEMStore oemStore; extern meshtastic_User &owner; extern meshtastic_Position localPosition; @@ -149,21 +147,26 @@ class NodeDB meshtastic_NodeInfoLite *getMeshNode(NodeNum n); size_t getNumMeshNodes() { return numMeshNodes; } + // returns true if the maximum number of nodes is reached or we are running low on memory + bool isFull(); + void clearLocalPosition(); void setLocalPosition(meshtastic_Position position, bool timeOnly = false) { if (timeOnly) { - LOG_DEBUG("Setting local position time only: time=%u timestamp=%u\n", position.time, position.timestamp); + LOG_DEBUG("Set local position time only: time=%u timestamp=%u", position.time, position.timestamp); localPosition.time = position.time; localPosition.timestamp = position.timestamp > 0 ? position.timestamp : position.time; return; } - LOG_DEBUG("Setting local position: lat=%i lon=%i time=%u timestamp=%u\n", position.latitude_i, position.longitude_i, + LOG_DEBUG("Set local position: lat=%i lon=%i time=%u timestamp=%u", position.latitude_i, position.longitude_i, position.time, position.timestamp); localPosition = position; } + bool hasValidPosition(const meshtastic_NodeInfoLite *n); + private: uint32_t lastNodeDbSave = 0; // when we last saved our db to flash /// Find a node in our DB, create an empty NodeInfoLite if missing @@ -216,13 +219,6 @@ extern NodeDB *nodeDB; prefs.is_power_saving = True */ -/// Sometimes we will have Position objects that only have a time, so check for -/// valid lat/lon -static inline bool hasValidPosition(const meshtastic_NodeInfoLite *n) -{ - return n->has_position && (n->position.latitude_i != 0 || n->position.longitude_i != 0); -} - /** 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. */ diff --git a/src/mesh/PacketHistory.cpp b/src/mesh/PacketHistory.cpp index ed1c3c59c..6eb4b6ea1 100644 --- a/src/mesh/PacketHistory.cpp +++ b/src/mesh/PacketHistory.cpp @@ -19,7 +19,7 @@ PacketHistory::PacketHistory() bool PacketHistory::wasSeenRecently(const meshtastic_MeshPacket *p, bool withUpdate) { if (p->id == 0) { - LOG_DEBUG("Ignoring message with zero id\n"); + LOG_DEBUG("Ignore message with zero id"); return false; // Not a floodable message ID, so we don't care } @@ -39,7 +39,7 @@ bool PacketHistory::wasSeenRecently(const meshtastic_MeshPacket *p, bool withUpd } if (seenRecently) { - LOG_DEBUG("Found existing packet record for fr=0x%x,to=0x%x,id=0x%x\n", p->from, p->to, p->id); + LOG_DEBUG("Found existing packet record for fr=0x%x,to=0x%x,id=0x%x", p->from, p->to, p->id); } if (withUpdate) { @@ -64,7 +64,7 @@ bool PacketHistory::wasSeenRecently(const meshtastic_MeshPacket *p, bool withUpd */ void PacketHistory::clearExpiredRecentPackets() { - LOG_DEBUG("recentPackets size=%ld\n", recentPackets.size()); + LOG_DEBUG("recentPackets size=%ld", recentPackets.size()); for (auto it = recentPackets.begin(); it != recentPackets.end();) { if (!Throttle::isWithinTimespanMs(it->rxTimeMsec, FLOOD_EXPIRE_TIME)) { @@ -74,5 +74,5 @@ void PacketHistory::clearExpiredRecentPackets() } } - LOG_DEBUG("recentPackets size=%ld (after clearing expired packets)\n", recentPackets.size()); + LOG_DEBUG("recentPackets size=%ld (after clearing expired packets)", recentPackets.size()); } \ No newline at end of file diff --git a/src/mesh/PhoneAPI.cpp b/src/mesh/PhoneAPI.cpp index ad4a1f33d..20421e73e 100644 --- a/src/mesh/PhoneAPI.cpp +++ b/src/mesh/PhoneAPI.cpp @@ -55,16 +55,16 @@ void PhoneAPI::handleStartConfig() state = STATE_SEND_MY_INFO; pauseBluetoothLogging = true; filesManifest = getFiles("/", 10); - LOG_DEBUG("Got %d files in manifest\n", filesManifest.size()); + LOG_DEBUG("Got %d files in manifest", filesManifest.size()); - LOG_INFO("Starting API client config\n"); + LOG_INFO("Start API client config"); nodeInfoForPhone.num = 0; // Don't keep returning old nodeinfos resetReadIndex(); } void PhoneAPI::close() { - LOG_INFO("PhoneAPI::close()\n"); + LOG_DEBUG("PhoneAPI::close()"); if (state != STATE_SEND_NOTHING) { state = STATE_SEND_NOTHING; @@ -95,7 +95,7 @@ bool PhoneAPI::checkConnectionTimeout() if (isConnected()) { bool newContact = checkIsConnected(); if (!newContact) { - LOG_INFO("Lost phone connection\n"); + LOG_INFO("Lost phone connection"); close(); return true; } @@ -118,41 +118,40 @@ bool PhoneAPI::handleToRadio(const uint8_t *buf, size_t bufLength) return handleToRadioPacket(toRadioScratch.packet); case meshtastic_ToRadio_want_config_id_tag: config_nonce = toRadioScratch.want_config_id; - LOG_INFO("Client wants config, nonce=%u\n", config_nonce); + LOG_INFO("Client wants config, nonce=%u", config_nonce); handleStartConfig(); break; case meshtastic_ToRadio_disconnect_tag: - LOG_INFO("Disconnecting from phone\n"); + LOG_INFO("Disconnect from phone"); close(); break; case meshtastic_ToRadio_xmodemPacket_tag: - LOG_INFO("Got xmodem packet\n"); + LOG_INFO("Got xmodem packet"); #ifdef FSCom xModem.handlePacket(toRadioScratch.xmodemPacket); #endif break; #if !MESHTASTIC_EXCLUDE_MQTT case meshtastic_ToRadio_mqttClientProxyMessage_tag: - LOG_INFO("Got MqttClientProxy message\n"); + LOG_DEBUG("Got MqttClientProxy message"); if (mqtt && moduleConfig.mqtt.proxy_to_client_enabled && moduleConfig.mqtt.enabled && (channels.anyMqttEnabled() || moduleConfig.mqtt.map_reporting_enabled)) { mqtt->onClientProxyReceive(toRadioScratch.mqttClientProxyMessage); } else { LOG_WARN("MqttClientProxy received but proxy is not enabled, no channels have up/downlink, or map reporting " - "not enabled\n"); + "not enabled"); } break; #endif case meshtastic_ToRadio_heartbeat_tag: - LOG_DEBUG("Got client heartbeat\n"); + LOG_DEBUG("Got client heartbeat"); break; default: // Ignore nop messages - // LOG_DEBUG("Error: unexpected ToRadio variant\n"); break; } } else { - LOG_ERROR("Error: ignoring malformed toradio\n"); + LOG_ERROR("Error: ignore malformed toradio"); } return false; @@ -179,7 +178,6 @@ bool PhoneAPI::handleToRadio(const uint8_t *buf, size_t bufLength) size_t PhoneAPI::getFromRadio(uint8_t *buf) { if (!available()) { - // LOG_DEBUG("getFromRadio=not available\n"); return 0; } // In case we send a FromRadio packet @@ -188,14 +186,15 @@ size_t PhoneAPI::getFromRadio(uint8_t *buf) // Advance states as needed switch (state) { case STATE_SEND_NOTHING: - LOG_INFO("getFromRadio=STATE_SEND_NOTHING\n"); + LOG_DEBUG("FromRadio=STATE_SEND_NOTHING"); break; case STATE_SEND_MY_INFO: - LOG_INFO("getFromRadio=STATE_SEND_MY_INFO\n"); + LOG_DEBUG("FromRadio=STATE_SEND_MY_INFO"); // If the user has specified they don't want our node to share its location, make sure to tell the phone // app not to send locations on our behalf. fromRadioScratch.which_payload_variant = meshtastic_FromRadio_my_info_tag; + strncpy(myNodeInfo.pio_env, optstr(APP_ENV), sizeof(myNodeInfo.pio_env)); fromRadioScratch.my_info = myNodeInfo; state = STATE_SEND_OWN_NODEINFO; @@ -203,7 +202,7 @@ size_t PhoneAPI::getFromRadio(uint8_t *buf) break; case STATE_SEND_OWN_NODEINFO: { - LOG_INFO("getFromRadio=STATE_SEND_OWN_NODEINFO\n"); + LOG_DEBUG("Send My NodeInfo"); auto us = nodeDB->readNextMeshNode(readIndex); if (us) { nodeInfoForPhone = TypeConversions::ConvertToNodeInfo(us); @@ -219,66 +218,74 @@ size_t PhoneAPI::getFromRadio(uint8_t *buf) } case STATE_SEND_METADATA: - LOG_INFO("getFromRadio=STATE_SEND_METADATA\n"); + LOG_DEBUG("Send device metadata"); fromRadioScratch.which_payload_variant = meshtastic_FromRadio_metadata_tag; fromRadioScratch.metadata = getDeviceMetadata(); state = STATE_SEND_CHANNELS; break; case STATE_SEND_CHANNELS: - LOG_INFO("getFromRadio=STATE_SEND_CHANNELS\n"); fromRadioScratch.which_payload_variant = meshtastic_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) { + LOG_DEBUG("Send channels %d", config_state); state = STATE_SEND_CONFIG; config_state = _meshtastic_AdminMessage_ConfigType_MIN + 1; } break; case STATE_SEND_CONFIG: - LOG_INFO("getFromRadio=STATE_SEND_CONFIG\n"); fromRadioScratch.which_payload_variant = meshtastic_FromRadio_config_tag; switch (config_state) { case meshtastic_Config_device_tag: + LOG_DEBUG("Send config: device"); fromRadioScratch.config.which_payload_variant = meshtastic_Config_device_tag; fromRadioScratch.config.payload_variant.device = config.device; break; case meshtastic_Config_position_tag: + LOG_DEBUG("Send config: position"); fromRadioScratch.config.which_payload_variant = meshtastic_Config_position_tag; fromRadioScratch.config.payload_variant.position = config.position; break; case meshtastic_Config_power_tag: + LOG_DEBUG("Send config: power"); fromRadioScratch.config.which_payload_variant = meshtastic_Config_power_tag; fromRadioScratch.config.payload_variant.power = config.power; fromRadioScratch.config.payload_variant.power.ls_secs = default_ls_secs; break; case meshtastic_Config_network_tag: + LOG_DEBUG("Send config: network"); fromRadioScratch.config.which_payload_variant = meshtastic_Config_network_tag; fromRadioScratch.config.payload_variant.network = config.network; break; case meshtastic_Config_display_tag: + LOG_DEBUG("Send config: display"); fromRadioScratch.config.which_payload_variant = meshtastic_Config_display_tag; fromRadioScratch.config.payload_variant.display = config.display; break; case meshtastic_Config_lora_tag: + LOG_DEBUG("Send config: lora"); fromRadioScratch.config.which_payload_variant = meshtastic_Config_lora_tag; fromRadioScratch.config.payload_variant.lora = config.lora; break; case meshtastic_Config_bluetooth_tag: + LOG_DEBUG("Send config: bluetooth"); fromRadioScratch.config.which_payload_variant = meshtastic_Config_bluetooth_tag; fromRadioScratch.config.payload_variant.bluetooth = config.bluetooth; break; case meshtastic_Config_security_tag: + LOG_DEBUG("Send config: security"); fromRadioScratch.config.which_payload_variant = meshtastic_Config_security_tag; fromRadioScratch.config.payload_variant.security = config.security; break; case meshtastic_Config_sessionkey_tag: + LOG_DEBUG("Send config: sessionkey"); fromRadioScratch.config.which_payload_variant = meshtastic_Config_sessionkey_tag; break; default: - LOG_ERROR("Unknown config type %d\n", config_state); + LOG_ERROR("Unknown config type %d", config_state); } // 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 @@ -293,63 +300,75 @@ size_t PhoneAPI::getFromRadio(uint8_t *buf) break; case STATE_SEND_MODULECONFIG: - LOG_INFO("getFromRadio=STATE_SEND_MODULECONFIG\n"); fromRadioScratch.which_payload_variant = meshtastic_FromRadio_moduleConfig_tag; switch (config_state) { case meshtastic_ModuleConfig_mqtt_tag: + LOG_DEBUG("Send module config: mqtt"); fromRadioScratch.moduleConfig.which_payload_variant = meshtastic_ModuleConfig_mqtt_tag; fromRadioScratch.moduleConfig.payload_variant.mqtt = moduleConfig.mqtt; break; case meshtastic_ModuleConfig_serial_tag: + LOG_DEBUG("Send module config: serial"); fromRadioScratch.moduleConfig.which_payload_variant = meshtastic_ModuleConfig_serial_tag; fromRadioScratch.moduleConfig.payload_variant.serial = moduleConfig.serial; break; case meshtastic_ModuleConfig_external_notification_tag: + LOG_DEBUG("Send module config: ext notification"); fromRadioScratch.moduleConfig.which_payload_variant = meshtastic_ModuleConfig_external_notification_tag; fromRadioScratch.moduleConfig.payload_variant.external_notification = moduleConfig.external_notification; break; case meshtastic_ModuleConfig_store_forward_tag: + LOG_DEBUG("Send module config: store forward"); fromRadioScratch.moduleConfig.which_payload_variant = meshtastic_ModuleConfig_store_forward_tag; fromRadioScratch.moduleConfig.payload_variant.store_forward = moduleConfig.store_forward; break; case meshtastic_ModuleConfig_range_test_tag: + LOG_DEBUG("Send module config: range test"); fromRadioScratch.moduleConfig.which_payload_variant = meshtastic_ModuleConfig_range_test_tag; fromRadioScratch.moduleConfig.payload_variant.range_test = moduleConfig.range_test; break; case meshtastic_ModuleConfig_telemetry_tag: + LOG_DEBUG("Send module config: telemetry"); fromRadioScratch.moduleConfig.which_payload_variant = meshtastic_ModuleConfig_telemetry_tag; fromRadioScratch.moduleConfig.payload_variant.telemetry = moduleConfig.telemetry; break; case meshtastic_ModuleConfig_canned_message_tag: + LOG_DEBUG("Send module config: canned message"); fromRadioScratch.moduleConfig.which_payload_variant = meshtastic_ModuleConfig_canned_message_tag; fromRadioScratch.moduleConfig.payload_variant.canned_message = moduleConfig.canned_message; break; case meshtastic_ModuleConfig_audio_tag: + LOG_DEBUG("Send module config: audio"); fromRadioScratch.moduleConfig.which_payload_variant = meshtastic_ModuleConfig_audio_tag; fromRadioScratch.moduleConfig.payload_variant.audio = moduleConfig.audio; break; case meshtastic_ModuleConfig_remote_hardware_tag: + LOG_DEBUG("Send module config: remote hardware"); fromRadioScratch.moduleConfig.which_payload_variant = meshtastic_ModuleConfig_remote_hardware_tag; fromRadioScratch.moduleConfig.payload_variant.remote_hardware = moduleConfig.remote_hardware; break; case meshtastic_ModuleConfig_neighbor_info_tag: + LOG_DEBUG("Send module config: neighbor info"); fromRadioScratch.moduleConfig.which_payload_variant = meshtastic_ModuleConfig_neighbor_info_tag; fromRadioScratch.moduleConfig.payload_variant.neighbor_info = moduleConfig.neighbor_info; break; case meshtastic_ModuleConfig_detection_sensor_tag: + LOG_DEBUG("Send module config: detection sensor"); fromRadioScratch.moduleConfig.which_payload_variant = meshtastic_ModuleConfig_detection_sensor_tag; fromRadioScratch.moduleConfig.payload_variant.detection_sensor = moduleConfig.detection_sensor; break; case meshtastic_ModuleConfig_ambient_lighting_tag: + LOG_DEBUG("Send module config: ambient lighting"); fromRadioScratch.moduleConfig.which_payload_variant = meshtastic_ModuleConfig_ambient_lighting_tag; fromRadioScratch.moduleConfig.payload_variant.ambient_lighting = moduleConfig.ambient_lighting; break; case meshtastic_ModuleConfig_paxcounter_tag: + LOG_DEBUG("Send module config: paxcounter"); fromRadioScratch.moduleConfig.which_payload_variant = meshtastic_ModuleConfig_paxcounter_tag; fromRadioScratch.moduleConfig.payload_variant.paxcounter = moduleConfig.paxcounter; break; default: - LOG_ERROR("Unknown module config type %d\n", config_state); + LOG_ERROR("Unknown module config type %d", config_state); } config_state++; @@ -362,16 +381,16 @@ size_t PhoneAPI::getFromRadio(uint8_t *buf) break; case STATE_SEND_OTHER_NODEINFOS: { - LOG_INFO("getFromRadio=STATE_SEND_OTHER_NODEINFOS\n"); + LOG_DEBUG("Send known nodes"); if (nodeInfoForPhone.num != 0) { - LOG_INFO("nodeinfo: num=0x%x, lastseen=%u, id=%s, name=%s\n", nodeInfoForPhone.num, nodeInfoForPhone.last_heard, + LOG_INFO("nodeinfo: num=0x%x, lastseen=%u, id=%s, name=%s", nodeInfoForPhone.num, nodeInfoForPhone.last_heard, nodeInfoForPhone.user.id, nodeInfoForPhone.user.long_name); fromRadioScratch.which_payload_variant = meshtastic_FromRadio_node_info_tag; fromRadioScratch.node_info = nodeInfoForPhone; // Stay in current state until done sending nodeinfos nodeInfoForPhone.num = 0; // We just consumed a nodeinfo, will need a new one next time } else { - LOG_INFO("Done sending nodeinfos\n"); + LOG_DEBUG("Done sending nodeinfo"); state = STATE_SEND_FILEMANIFEST; // Go ahead and send that ID right now return getFromRadio(buf); @@ -380,7 +399,7 @@ size_t PhoneAPI::getFromRadio(uint8_t *buf) } case STATE_SEND_FILEMANIFEST: { - LOG_INFO("getFromRadio=STATE_SEND_FILEMANIFEST\n"); + LOG_DEBUG("FromRadio=STATE_SEND_FILEMANIFEST"); // last element if (config_state == filesManifest.size()) { // also handles an empty filesManifest config_state = 0; @@ -390,7 +409,7 @@ size_t PhoneAPI::getFromRadio(uint8_t *buf) } else { fromRadioScratch.which_payload_variant = meshtastic_FromRadio_fileInfo_tag; fromRadioScratch.fileInfo = filesManifest.at(config_state); - LOG_DEBUG("File: %s (%d) bytes\n", fromRadioScratch.fileInfo.file_name, fromRadioScratch.fileInfo.size_bytes); + LOG_DEBUG("File: %s (%d) bytes", fromRadioScratch.fileInfo.file_name, fromRadioScratch.fileInfo.size_bytes); config_state++; } break; @@ -403,7 +422,7 @@ size_t PhoneAPI::getFromRadio(uint8_t *buf) case STATE_SEND_PACKETS: pauseBluetoothLogging = false; // Do we have a message from the mesh or packet from the local device? - LOG_INFO("getFromRadio=STATE_SEND_PACKETS\n"); + LOG_DEBUG("FromRadio=STATE_SEND_PACKETS"); if (queueStatusPacketForPhone) { fromRadioScratch.which_payload_variant = meshtastic_FromRadio_queueStatus_tag; fromRadioScratch.queueStatus = *queueStatusPacketForPhone; @@ -431,7 +450,7 @@ size_t PhoneAPI::getFromRadio(uint8_t *buf) break; default: - LOG_ERROR("getFromRadio unexpected state %d\n", state); + LOG_ERROR("getFromRadio unexpected state %d", state); } // Do we have a message from the mesh? @@ -441,17 +460,16 @@ size_t PhoneAPI::getFromRadio(uint8_t *buf) // VERY IMPORTANT to not print debug messages while writing to fromRadioScratch - because we use that same buffer // for logging (when we are encapsulating with protobufs) - // LOG_DEBUG("encoding toPhone packet to phone variant=%d, %d bytes\n", fromRadioScratch.which_payload_variant, numbytes); return numbytes; } - LOG_DEBUG("no FromRadio packet available\n"); + LOG_DEBUG("No FromRadio packet available"); return 0; } void PhoneAPI::sendConfigComplete() { - LOG_INFO("getFromRadio=STATE_SEND_COMPLETE_ID\n"); + LOG_INFO("Config Send Complete"); fromRadioScratch.which_payload_variant = meshtastic_FromRadio_config_complete_id_tag; fromRadioScratch.config_complete_id = config_nonce; config_nonce = 0; @@ -551,11 +569,10 @@ bool PhoneAPI::available() if (!packetForPhone) packetForPhone = service->getForPhone(); hasPacket = !!packetForPhone; - // LOG_DEBUG("available hasPacket=%d\n", hasPacket); return hasPacket; } default: - LOG_ERROR("PhoneAPI::available unexpected state %d\n", state); + LOG_ERROR("PhoneAPI::available unexpected state %d", state); } return false; @@ -596,21 +613,24 @@ bool PhoneAPI::handleToRadioPacket(meshtastic_MeshPacket &p) { printPacket("PACKET FROM PHONE", &p); +// For use with the simulator, we should not ignore duplicate packets +#if !(defined(ARCH_PORTDUINO) && !HAS_RADIO) if (p.id > 0 && wasSeenRecently(p.id)) { - LOG_DEBUG("Ignoring packet from phone, already seen recently\n"); + LOG_DEBUG("Ignore packet from phone, already seen recently"); return false; } +#endif if (p.decoded.portnum == meshtastic_PortNum_TRACEROUTE_APP && lastPortNumToRadio[p.decoded.portnum] && Throttle::isWithinTimespanMs(lastPortNumToRadio[p.decoded.portnum], THIRTY_SECONDS_MS)) { - LOG_WARN("Rate limiting portnum %d\n", p.decoded.portnum); + LOG_WARN("Rate limit portnum %d", p.decoded.portnum); sendNotification(meshtastic_LogRecord_Level_WARNING, p.id, "TraceRoute can only be sent once every 30 seconds"); meshtastic_QueueStatus qs = router->getQueueStatus(); service->sendQueueStatusToPhone(qs, 0, p.id); return false; } else if (p.decoded.portnum == meshtastic_PortNum_POSITION_APP && lastPortNumToRadio[p.decoded.portnum] && Throttle::isWithinTimespanMs(lastPortNumToRadio[p.decoded.portnum], FIVE_SECONDS_MS)) { - LOG_WARN("Rate limiting portnum %d\n", p.decoded.portnum); + LOG_WARN("Rate limit portnum %d", p.decoded.portnum); meshtastic_QueueStatus qs = router->getQueueStatus(); service->sendQueueStatusToPhone(qs, 0, p.id); // FIXME: Figure out why this continues to happen @@ -629,11 +649,11 @@ int PhoneAPI::onNotify(uint32_t newValue) // doesn't call this from idle) if (state == STATE_SEND_PACKETS) { - LOG_INFO("Telling client we have new packets %u\n", newValue); + LOG_INFO("Tell client we have new packets %u", newValue); onNowHasData(newValue); } else { - LOG_DEBUG("(Client not yet interested in packets)\n"); + LOG_DEBUG("(Client not yet interested in packets)"); } return timeout ? -1 : 0; // If we timed out, MeshService should stop iterating through observers as we just removed one -} \ No newline at end of file +} diff --git a/src/mesh/ProtobufModule.h b/src/mesh/ProtobufModule.h index e4d4e5c09..e038e9bb8 100644 --- a/src/mesh/ProtobufModule.h +++ b/src/mesh/ProtobufModule.h @@ -47,7 +47,7 @@ template class ProtobufModule : protected SinglePortModule p->decoded.payload.size = pb_encode_to_bytes(p->decoded.payload.bytes, sizeof(p->decoded.payload.bytes), fields, &payload); - // LOG_DEBUG("did encode\n"); + // LOG_DEBUG("did encode"); return p; } @@ -82,7 +82,7 @@ template class ProtobufModule : protected SinglePortModule // it would be better to update even if the message was destined to others. auto &p = mp.decoded; - LOG_INFO("Received %s from=0x%0x, id=0x%x, portnum=%d, payloadlen=%d\n", name, mp.from, mp.id, p.portnum, p.payload.size); + LOG_INFO("Received %s from=0x%0x, id=0x%x, portnum=%d, payloadlen=%d", name, mp.from, mp.id, p.portnum, p.payload.size); T scratch; T *decoded = NULL; @@ -91,7 +91,7 @@ template class ProtobufModule : protected SinglePortModule if (pb_decode_from_bytes(p.payload.bytes, p.payload.size, fields, &scratch)) { decoded = &scratch; } else { - LOG_ERROR("Error decoding protobuf module!\n"); + LOG_ERROR("Error decoding proto module!"); // if we can't decode it, nobody can process it! return ProcessMessage::STOP; } @@ -112,7 +112,7 @@ template class ProtobufModule : protected SinglePortModule if (pb_decode_from_bytes(p.payload.bytes, p.payload.size, fields, &scratch)) { decoded = &scratch; } else { - LOG_ERROR("Error decoding protobuf module!\n"); + LOG_ERROR("Error decoding proto module!"); // if we can't decode it, nobody can process it! return; } diff --git a/src/mesh/RF95Interface.cpp b/src/mesh/RF95Interface.cpp index 581fd9034..9ef045099 100644 --- a/src/mesh/RF95Interface.cpp +++ b/src/mesh/RF95Interface.cpp @@ -82,7 +82,7 @@ RF95Interface::RF95Interface(LockingArduinoHal *hal, RADIOLIB_PIN_TYPE cs, RADIO RADIOLIB_PIN_TYPE busy) : RadioLibInterface(hal, cs, irq, rst, busy) { - LOG_DEBUG("RF95Interface(cs=%d, irq=%d, rst=%d, busy=%d)\n", cs, irq, rst, busy); + LOG_DEBUG("RF95Interface(cs=%d, irq=%d, rst=%d, busy=%d)", cs, irq, rst, busy); } /** Some boards require GPIO control of tx vs rx paths */ @@ -176,12 +176,12 @@ bool RF95Interface::init() setTransmitEnable(false); int res = lora->begin(getFreq(), bw, sf, cr, syncWord, power, preambleLength); - LOG_INFO("RF95 init result %d\n", res); - LOG_INFO("Frequency set to %f\n", getFreq()); - LOG_INFO("Bandwidth set to %f\n", bw); - LOG_INFO("Power output set to %d\n", power); + LOG_INFO("RF95 init result %d", res); + LOG_INFO("Frequency set to %f", getFreq()); + LOG_INFO("Bandwidth set to %f", bw); + LOG_INFO("Power output set to %d", power); #if defined(RADIOMASTER_900_BANDIT_NANO) || defined(RADIOMASTER_900_BANDIT) - LOG_INFO("DAC output set to %d\n", powerDAC); + LOG_INFO("DAC output set to %d", powerDAC); #endif if (res == RADIOLIB_ERR_NONE) @@ -220,17 +220,17 @@ bool RF95Interface::reconfigure() err = lora->setSyncWord(syncWord); if (err != RADIOLIB_ERR_NONE) - LOG_ERROR("RF95 setSyncWord %s%d\n", radioLibErr, err); + LOG_ERROR("RF95 setSyncWord %s%d", radioLibErr, err); assert(err == RADIOLIB_ERR_NONE); err = lora->setCurrentLimit(currentLimit); if (err != RADIOLIB_ERR_NONE) - LOG_ERROR("RF95 setCurrentLimit %s%d\n", radioLibErr, err); + LOG_ERROR("RF95 setCurrentLimit %s%d", radioLibErr, err); assert(err == RADIOLIB_ERR_NONE); err = lora->setPreambleLength(preambleLength); if (err != RADIOLIB_ERR_NONE) - LOG_ERROR(" RF95 setPreambleLength %s%d\n", radioLibErr, err); + LOG_ERROR("RF95 setPreambleLength %s%d", radioLibErr, err); assert(err == RADIOLIB_ERR_NONE); err = lora->setFrequency(getFreq()); @@ -266,7 +266,7 @@ void RF95Interface::setStandby() { int err = lora->standby(); if (err != RADIOLIB_ERR_NONE) - LOG_ERROR("RF95 standby %s%d\n", radioLibErr, err); + LOG_ERROR("RF95 standby %s%d", radioLibErr, err); assert(err == RADIOLIB_ERR_NONE); isReceiving = false; // If we were receiving, not any more @@ -290,7 +290,7 @@ void RF95Interface::startReceive() setStandby(); int err = lora->startReceive(); if (err != RADIOLIB_ERR_NONE) - LOG_ERROR("RF95 startReceive %s%d\n", radioLibErr, err); + LOG_ERROR("RF95 startReceive %s%d", radioLibErr, err); assert(err == RADIOLIB_ERR_NONE); isReceiving = true; @@ -308,14 +308,14 @@ bool RF95Interface::isChannelActive() result = lora->scanChannel(); if (result == RADIOLIB_PREAMBLE_DETECTED) { - // LOG_DEBUG("Channel is busy!\n"); + // LOG_DEBUG("Channel is busy!"); return true; } if (result != RADIOLIB_CHANNEL_FREE) - LOG_ERROR("RF95 isChannelActive %s%d\n", radioLibErr, result); + LOG_ERROR("RF95 isChannelActive %s%d", radioLibErr, result); assert(result != RADIOLIB_ERR_WRONG_MODEM); - // LOG_DEBUG("Channel is free!\n"); + // LOG_DEBUG("Channel is free!"); return false; } diff --git a/src/mesh/RadioInterface.cpp b/src/mesh/RadioInterface.cpp index 7501852f2..53b66ff0a 100644 --- a/src/mesh/RadioInterface.cpp +++ b/src/mesh/RadioInterface.cpp @@ -140,7 +140,7 @@ const RegionInfo regions[] = { Philippines 433 - 434.7 MHz <10 mW erp, NTC approved device required 868 - 869.4 MHz <25 mW erp, NTC approved device required - 915 - 918 MHz <250 mW EIRP, no external antennna allowed + 915 - 918 MHz <250 mW EIRP, no external antenna allowed https://github.com/meshtastic/firmware/issues/4948#issuecomment-2394926135 */ @@ -170,11 +170,11 @@ void initRegion() #ifdef REGULATORY_LORA_REGIONCODE for (; r->code != meshtastic_Config_LoRaConfig_RegionCode_UNSET && r->code != REGULATORY_LORA_REGIONCODE; r++) ; - LOG_INFO("Wanted region %d, regulatory override to %s\n", config.lora.region, r->name); + LOG_INFO("Wanted region %d, regulatory override to %s", config.lora.region, r->name); #else for (; r->code != meshtastic_Config_LoRaConfig_RegionCode_UNSET && r->code != config.lora.region; r++) ; - LOG_INFO("Wanted region %d, using %s\n", config.lora.region, r->name); + LOG_INFO("Wanted region %d, using %s", config.lora.region, r->name); #endif myRegion = r; } @@ -234,7 +234,7 @@ uint32_t RadioInterface::getRetransmissionMsec(const meshtastic_MeshPacket *p) size_t numbytes = pb_encode_to_bytes(bytes, sizeof(bytes), &meshtastic_Data_msg, &p->decoded); uint32_t packetAirtime = getPacketTime(numbytes + sizeof(PacketHeader)); // Make sure enough time has elapsed for this packet to be sent and an ACK is received. - // LOG_DEBUG("Waiting for flooding message with airtime %d and slotTime is %d\n", packetAirtime, slotTimeMsec); + // LOG_DEBUG("Waiting for flooding message with airtime %d and slotTime is %d", packetAirtime, slotTimeMsec); float channelUtil = airTime->channelUtilizationPercent(); uint8_t CWsize = map(channelUtil, 0, 100, CWmin, CWmax); // Assuming we pick max. of CWsize and there will be a client with SNR at half the range @@ -250,7 +250,7 @@ uint32_t RadioInterface::getTxDelayMsec() current channel utilization. */ float channelUtil = airTime->channelUtilizationPercent(); uint8_t CWsize = map(channelUtil, 0, 100, CWmin, CWmax); - // LOG_DEBUG("Current channel utilization is %f so setting CWsize to %d\n", channelUtil, CWsize); + // LOG_DEBUG("Current channel utilization is %f so setting CWsize to %d", channelUtil, CWsize); return random(0, pow(2, CWsize)) * slotTimeMsec; } @@ -267,15 +267,15 @@ uint32_t RadioInterface::getTxDelayMsecWeighted(float snr) // low SNR = small CW size (Short Delay) uint32_t delay = 0; uint8_t CWsize = map(snr, SNR_MIN, SNR_MAX, CWmin, CWmax); - // LOG_DEBUG("rx_snr of %f so setting CWsize to:%d\n", snr, CWsize); + // LOG_DEBUG("rx_snr of %f so setting CWsize to:%d", snr, CWsize); if (config.device.role == meshtastic_Config_DeviceConfig_Role_ROUTER || config.device.role == meshtastic_Config_DeviceConfig_Role_REPEATER) { delay = random(0, 2 * CWsize) * slotTimeMsec; - LOG_DEBUG("rx_snr found in packet. As a router, setting tx delay:%d\n", delay); + LOG_DEBUG("rx_snr found in packet. Router: setting tx delay:%d", delay); } else { // offset the maximum delay for routers: (2 * CWmax * slotTimeMsec) delay = (2 * CWmax * slotTimeMsec) + random(0, pow(2, CWsize)) * slotTimeMsec; - LOG_DEBUG("rx_snr found in packet. Setting tx delay:%d\n", delay); + LOG_DEBUG("rx_snr found in packet. Setting tx delay:%d", delay); } return delay; @@ -329,7 +329,7 @@ void printPacket(const char *prefix, const meshtastic_MeshPacket *p) out += DEBUG_PORT.mt_sprintf(" priority=%d", p->priority); out += ")"; - LOG_DEBUG("%s\n", out.c_str()); + LOG_DEBUG("%s", out.c_str()); #endif } @@ -346,7 +346,7 @@ bool RadioInterface::reconfigure() bool RadioInterface::init() { - LOG_INFO("Starting meshradio init...\n"); + LOG_INFO("Start meshradio init"); configChangedObserver.observe(&service->configChanged); preflightSleepObserver.observe(&preflightSleep); @@ -494,8 +494,7 @@ void RadioInterface::applyModemConfig() } if ((myRegion->freqEnd - myRegion->freqStart) < bw / 1000) { - static const char *err_string = - "Regional frequency range is smaller than bandwidth. Falling back to default preset.\n"; + static const char *err_string = "Regional frequency range is smaller than bandwidth. Fall back to default preset"; LOG_ERROR(err_string); RECORD_CRITICALERROR(meshtastic_CriticalErrorCode_INVALID_RADIO_SETTING); @@ -556,15 +555,15 @@ void RadioInterface::applyModemConfig() preambleTimeMsec = getPacketTime((uint32_t)0); maxPacketTimeMsec = getPacketTime(meshtastic_Constants_DATA_PAYLOAD_LEN + sizeof(PacketHeader)); - LOG_INFO("Radio freq=%.3f, config.lora.frequency_offset=%.3f\n", freq, loraConfig.frequency_offset); - LOG_INFO("Set radio: region=%s, name=%s, config=%u, ch=%d, power=%d\n", myRegion->name, channelName, loraConfig.modem_preset, + LOG_INFO("Radio freq=%.3f, config.lora.frequency_offset=%.3f", freq, loraConfig.frequency_offset); + LOG_INFO("Set radio: region=%s, name=%s, config=%u, ch=%d, power=%d", myRegion->name, channelName, loraConfig.modem_preset, channel_num, power); - LOG_INFO("myRegion->freqStart -> myRegion->freqEnd: %f -> %f (%f MHz)\n", myRegion->freqStart, myRegion->freqEnd, + LOG_INFO("myRegion->freqStart -> myRegion->freqEnd: %f -> %f (%f MHz)", myRegion->freqStart, myRegion->freqEnd, myRegion->freqEnd - myRegion->freqStart); - LOG_INFO("numChannels: %d x %.3fkHz\n", numChannels, bw); - LOG_INFO("channel_num: %d\n", channel_num + 1); - LOG_INFO("frequency: %f\n", getFreq()); - LOG_INFO("Slot time: %u msec\n", slotTimeMsec); + LOG_INFO("numChannels: %d x %.3fkHz", numChannels, bw); + LOG_INFO("channel_num: %d", channel_num + 1); + LOG_INFO("frequency: %f", getFreq()); + LOG_INFO("Slot time: %u msec", slotTimeMsec); } /** @@ -579,11 +578,11 @@ void RadioInterface::limitPower() maxPower = myRegion->powerLimit; if ((power > maxPower) && !devicestate.owner.is_licensed) { - LOG_INFO("Lowering transmit power because of regulatory limits\n"); + LOG_INFO("Lower transmit power because of regulatory limits"); power = maxPower; } - LOG_INFO("Set radio: final power level=%d\n", power); + LOG_INFO("Set radio: final power level=%d", power); } void RadioInterface::deliverToReceiver(meshtastic_MeshPacket *p) @@ -599,11 +598,9 @@ size_t RadioInterface::beginSending(meshtastic_MeshPacket *p) { assert(!sendingPacket); - // LOG_DEBUG("sending queued packet on mesh (txGood=%d,rxGood=%d,rxBad=%d)\n", rf95.txGood(), rf95.rxGood(), rf95.rxBad()); + // LOG_DEBUG("Send queued packet on mesh (txGood=%d,rxGood=%d,rxBad=%d)", rf95.txGood(), rf95.rxGood(), rf95.rxBad()); assert(p->which_payload_variant == meshtastic_MeshPacket_encrypted_tag); // It should have already been encoded by now - lastTxStart = millis(); - radioBuffer.header.from = p->from; radioBuffer.header.to = p->to; radioBuffer.header.id = p->id; @@ -611,7 +608,7 @@ size_t RadioInterface::beginSending(meshtastic_MeshPacket *p) radioBuffer.header.next_hop = 0; // *** For future use *** radioBuffer.header.relay_node = 0; // *** For future use *** if (p->hop_limit > HOP_MAX) { - LOG_WARN("hop limit %d is too high, setting to %d\n", p->hop_limit, HOP_RELIABLE); + LOG_WARN("hop limit %d is too high, setting to %d", p->hop_limit, HOP_RELIABLE); p->hop_limit = HOP_RELIABLE; } radioBuffer.header.flags = diff --git a/src/mesh/RadioLibInterface.cpp b/src/mesh/RadioLibInterface.cpp index 818826018..5f82a41ce 100644 --- a/src/mesh/RadioLibInterface.cpp +++ b/src/mesh/RadioLibInterface.cpp @@ -11,6 +11,10 @@ #include #include +#if ARCH_PORTDUINO +#include "PortduinoGlue.h" +#include "meshUtils.h" +#endif void LockingArduinoHal::spiBeginTransaction() { spiLock->lock(); @@ -20,9 +24,9 @@ void LockingArduinoHal::spiBeginTransaction() void LockingArduinoHal::spiEndTransaction() { - spiLock->unlock(); - ArduinoHal::spiEndTransaction(); + + spiLock->unlock(); } #if ARCH_PORTDUINO void LockingArduinoHal::spiTransfer(uint8_t *out, size_t len, uint8_t *in) @@ -111,18 +115,18 @@ bool RadioLibInterface::canSendImmediately() if (busyTx || busyRx) { if (busyTx) { - LOG_WARN("Can not send yet, busyTx\n"); + LOG_WARN("Can not send yet, busyTx"); } // If we've been trying to send the same packet more than one minute and we haven't gotten a // TX IRQ from the radio, the radio is probably broken. if (busyTx && !Throttle::isWithinTimespanMs(lastTxStart, 60000)) { - LOG_ERROR("Hardware Failure! busyTx for more than 60s\n"); + LOG_ERROR("Hardware Failure! busyTx for more than 60s"); RECORD_CRITICALERROR(meshtastic_CriticalErrorCode_TRANSMIT_FAILED); // reboot in 5 seconds when this condition occurs. rebootAtMsec = lastTxStart + 65000; } if (busyRx) { - LOG_WARN("Can not send yet, busyRx\n"); + LOG_WARN("Can not send yet, busyRx"); } return false; } else @@ -139,12 +143,12 @@ bool RadioLibInterface::receiveDetected(uint16_t irq, ulong syncWordHeaderValidF } else if (!Throttle::isWithinTimespanMs(activeReceiveStart, 2 * preambleTimeMsec) && !(irq & syncWordHeaderValidFlag)) { // The HEADER_VALID flag should be set by now if it was really a packet, so ignore PREAMBLE_DETECTED flag activeReceiveStart = 0; - LOG_DEBUG("Ignore false preamble detection.\n"); + LOG_DEBUG("Ignore false preamble detection"); return false; } else if (!Throttle::isWithinTimespanMs(activeReceiveStart, maxPacketTimeMsec)) { // We should have gotten an RX_DONE IRQ by now if it was really a packet, so ignore HEADER_VALID flag activeReceiveStart = 0; - LOG_DEBUG("Ignore false header detection.\n"); + LOG_DEBUG("Ignore false header detection"); return false; } } @@ -161,13 +165,13 @@ ErrorCode RadioLibInterface::send(meshtastic_MeshPacket *p) if (config.lora.region != meshtastic_Config_LoRaConfig_RegionCode_UNSET) { if (disabled || !config.lora.tx_enabled) { - LOG_WARN("send - !config.lora.tx_enabled\n"); + LOG_WARN("send - !config.lora.tx_enabled"); packetPool.release(p); return ERRNO_DISABLED; } } else { - LOG_WARN("send - lora tx disabled because RegionCode_Unset\n"); + LOG_WARN("send - lora tx disabled: Region unset"); packetPool.release(p); return ERRNO_DISABLED; } @@ -175,18 +179,23 @@ ErrorCode RadioLibInterface::send(meshtastic_MeshPacket *p) #else if (disabled || !config.lora.tx_enabled) { - LOG_WARN("send - !config.lora.tx_enabled\n"); + LOG_WARN("send - !config.lora.tx_enabled"); packetPool.release(p); return ERRNO_DISABLED; } #endif + if (p->to == NODENUM_BROADCAST_NO_LORA) { + LOG_DEBUG("Drop no-LoRa pkt"); + return ERRNO_SHOULD_RELEASE; + } + // Sometimes when testing it is useful to be able to never turn on the xmitter #ifndef LORA_DISABLE_SENDING - printPacket("enqueuing for send", p); + printPacket("enqueue for send", p); - LOG_DEBUG("txGood=%d,txRelay=%d,rxGood=%d,rxBad=%d\n", txGood, txRelay, rxGood, rxBad); + LOG_DEBUG("txGood=%d,txRelay=%d,rxGood=%d,rxBad=%d", txGood, txRelay, rxGood, rxBad); ErrorCode res = txQueue.enqueue(p) ? ERRNO_OK : ERRNO_UNKNOWN; if (res != ERRNO_OK) { // we weren't able to queue it, so we must drop it to prevent leaks @@ -196,7 +205,6 @@ ErrorCode RadioLibInterface::send(meshtastic_MeshPacket *p) // set (random) transmit delay to let others reconfigure their radio, // to avoid collisions and implement timing-based flooding - // LOG_DEBUG("Set random delay before transmitting.\n"); setTransmitDelay(); return res; @@ -221,7 +229,7 @@ bool RadioLibInterface::canSleep() { bool res = txQueue.empty(); if (!res) { // only print debug messages if we are vetoing sleep - LOG_DEBUG("radio wait to sleep, txEmpty=%d\n", res); + LOG_DEBUG("Radio wait to sleep, txEmpty=%d", res); } return res; } @@ -234,7 +242,7 @@ bool RadioLibInterface::cancelSending(NodeNum from, PacketId id) packetPool.release(p); // free the packet we just removed bool result = (p != NULL); - LOG_DEBUG("cancelSending id=0x%x, removed=%d\n", id, result); + LOG_DEBUG("cancelSending id=0x%x, removed=%d", id, result); return result; } @@ -251,42 +259,38 @@ void RadioLibInterface::onNotify(uint32_t notification) case ISR_TX: handleTransmitInterrupt(); startReceive(); - // LOG_DEBUG("tx complete - starting timer\n"); startTransmitTimer(); break; case ISR_RX: handleReceiveInterrupt(); startReceive(); - // LOG_DEBUG("rx complete - starting timer\n"); startTransmitTimer(); break; case TRANSMIT_DELAY_COMPLETED: - // LOG_DEBUG("delay done\n"); // If we are not currently in receive mode, then restart the random delay (this can happen if the main thread // has placed the unit into standby) FIXME, how will this work if the chipset is in sleep mode? if (!txQueue.empty()) { if (!canSendImmediately()) { - // LOG_DEBUG("Currently Rx/Tx-ing: set random delay\n"); setTransmitDelay(); // currently Rx/Tx-ing: reset random delay } else { if (isChannelActive()) { // check if there is currently a LoRa packet on the channel - // LOG_DEBUG("Channel is active, try receiving first.\n"); - startReceive(); // try receiving this packet, afterwards we'll be trying to transmit again + startReceive(); // try receiving this packet, afterwards we'll be trying to transmit again setTransmitDelay(); } else { - // Send any outgoing packets we have ready + // Send any outgoing packets we have ready as fast as possible to keep the time between channel scan and + // actual transmission as short as possible meshtastic_MeshPacket *txp = txQueue.dequeue(); assert(txp); - startSend(txp); - - // Packet has been sent, count it toward our TX airtime utilization. - uint32_t xmitMsec = getPacketTime(txp); - airTime->logAirtime(TX_LOG, xmitMsec); + bool sent = startSend(txp); + if (sent) { + // Packet has been sent, count it toward our TX airtime utilization. + uint32_t xmitMsec = getPacketTime(txp); + airTime->logAirtime(TX_LOG, xmitMsec); + } } } } else { - // LOG_DEBUG("done with txqueue\n"); } break; default: @@ -309,7 +313,7 @@ void RadioLibInterface::setTransmitDelay() startTransmitTimer(true); } else { // If there is a SNR, start a timer scaled based on that SNR. - LOG_DEBUG("rx_snr found. hop_limit:%d rx_snr:%f\n", p->hop_limit, p->rx_snr); + LOG_DEBUG("rx_snr found. hop_limit:%d rx_snr:%f", p->hop_limit, p->rx_snr); startTransmitTimerSNR(p->rx_snr); } } @@ -319,7 +323,6 @@ void RadioLibInterface::startTransmitTimer(bool withDelay) // If we have work to do and the timer wasn't already scheduled, schedule it now if (!txQueue.empty()) { uint32_t delay = !withDelay ? 1 : getTxDelayMsec(); - // LOG_DEBUG("xmit timer %d\n", delay); notifyLater(delay, TRANSMIT_DELAY_COMPLETED, false); // This will implicitly enable } } @@ -329,19 +332,17 @@ void RadioLibInterface::startTransmitTimerSNR(float snr) // If we have work to do and the timer wasn't already scheduled, schedule it now if (!txQueue.empty()) { uint32_t delay = getTxDelayMsecWeighted(snr); - // LOG_DEBUG("xmit timer %d\n", delay); notifyLater(delay, TRANSMIT_DELAY_COMPLETED, false); // This will implicitly enable } } void RadioLibInterface::handleTransmitInterrupt() { - // LOG_DEBUG("handling lora TX interrupt\n"); // This can be null if we forced the device to enter standby mode. In that case // ignore the transmit interrupt if (sendingPacket) completeSending(); - powerMon->clearState(meshtastic_PowerMon_State_Lora_TXOn); // But our transmitter is deffinitely off now + powerMon->clearState(meshtastic_PowerMon_State_Lora_TXOn); // But our transmitter is definitely off now } void RadioLibInterface::completeSending() @@ -359,7 +360,6 @@ void RadioLibInterface::completeSending() // We are done sending that packet, release it packetPool.release(p); - // LOG_DEBUG("Done with send\n"); } } @@ -370,7 +370,7 @@ void RadioLibInterface::handleReceiveInterrupt() // when this is called, we should be in receive mode - if we are not, just jump out instead of bombing. Possible Race // Condition? if (!isReceiving) { - LOG_ERROR("handleReceiveInterrupt called when not in receive mode, which shouldn't happen.\n"); + LOG_ERROR("handleReceiveInterrupt called when not in rx mode, which shouldn't happen"); return; } @@ -383,15 +383,20 @@ void RadioLibInterface::handleReceiveInterrupt() #ifndef DISABLE_WELCOME_UNSET if (config.lora.region == meshtastic_Config_LoRaConfig_RegionCode_UNSET) { - LOG_WARN("recv - lora rx disabled because RegionCode_Unset\n"); + LOG_WARN("lora rx disabled: Region unset"); airTime->logAirtime(RX_ALL_LOG, xmitMsec); return; } #endif int state = iface->readData((uint8_t *)&radioBuffer, length); +#if ARCH_PORTDUINO + if (settingsMap[logoutputlevel] == level_trace) { + printBytes("Raw incoming packet: ", (uint8_t *)&radioBuffer, length); + } +#endif if (state != RADIOLIB_ERR_NONE) { - LOG_ERROR("ignoring received packet due to error=%d\n", state); + LOG_ERROR("Ignore received packet due to error=%d", state); rxBad++; airTime->logAirtime(RX_ALL_LOG, xmitMsec); @@ -402,14 +407,14 @@ void RadioLibInterface::handleReceiveInterrupt() // check for short packets if (payloadLen < 0) { - LOG_WARN("ignoring received packet too short\n"); + LOG_WARN("Ignore received packet too short"); rxBad++; airTime->logAirtime(RX_ALL_LOG, xmitMsec); } else { rxGood++; // altered packet with "from == 0" can do Remote Node Administration without permission if (radioBuffer.header.from == 0) { - LOG_WARN("ignoring received packet without sender\n"); + LOG_WARN("Ignore received packet without sender"); return; } @@ -464,12 +469,14 @@ void RadioLibInterface::setStandby() } /** start an immediate transmit */ -void RadioLibInterface::startSend(meshtastic_MeshPacket *txp) +bool RadioLibInterface::startSend(meshtastic_MeshPacket *txp) { - printPacket("Starting low level send", txp); + /* NOTE: Minimize the actions before startTransmit() to keep the time between + channel scan and actual transmit as low as possible to avoid collisions. */ if (disabled || !config.lora.tx_enabled) { - LOG_WARN("Drop Tx packet because LoRa Tx disabled\n"); + LOG_WARN("Drop Tx packet because LoRa Tx disabled"); packetPool.release(txp); + return false; } else { configHardwareForSend(); // must be after setStandby @@ -477,17 +484,22 @@ void RadioLibInterface::startSend(meshtastic_MeshPacket *txp) int res = iface->startTransmit((uint8_t *)&radioBuffer, numbytes); if (res != RADIOLIB_ERR_NONE) { - LOG_ERROR("startTransmit failed, error=%d\n", res); + LOG_ERROR("startTransmit failed, error=%d", res); RECORD_CRITICALERROR(meshtastic_CriticalErrorCode_RADIO_SPI_BUG); // This send failed, but make sure to 'complete' it properly completeSending(); powerMon->clearState(meshtastic_PowerMon_State_Lora_TXOn); // Transmitter off now startReceive(); // Restart receive mode (because startTransmit failed to put us in xmit mode) + } else { + lastTxStart = millis(); + printPacket("Started Tx", txp); } // Must be done AFTER, starting transmit, because startTransmit clears (possibly stale) interrupt pending register // bits enableInterrupt(isrTxLevel0); + + return res == RADIOLIB_ERR_NONE; } } \ No newline at end of file diff --git a/src/mesh/RadioLibInterface.h b/src/mesh/RadioLibInterface.h index 673f53a32..a5c2e30dd 100644 --- a/src/mesh/RadioLibInterface.h +++ b/src/mesh/RadioLibInterface.h @@ -5,6 +5,7 @@ #include "concurrency/NotifiedWorkerThread.h" #include +#include // ESP32 has special rules about ISR code #ifdef ARDUINO_ARCH_ESP32 @@ -161,8 +162,9 @@ class RadioLibInterface : public RadioInterface, protected concurrency::Notified /** start an immediate transmit * This method is virtual so subclasses can hook as needed, subclasses should not call directly + * @return true if packet was sent */ - virtual void startSend(meshtastic_MeshPacket *txp); + virtual bool startSend(meshtastic_MeshPacket *txp); meshtastic_QueueStatus getQueueStatus(); @@ -197,5 +199,5 @@ class RadioLibInterface : public RadioInterface, protected concurrency::Notified */ virtual void setStandby(); - const char *radioLibErr = "RadioLib err=\n"; + const char *radioLibErr = "RadioLib err="; }; \ No newline at end of file diff --git a/src/mesh/RadioLibRF95.cpp b/src/mesh/RadioLibRF95.cpp index fe9bbdc93..a34c0605f 100644 --- a/src/mesh/RadioLibRF95.cpp +++ b/src/mesh/RadioLibRF95.cpp @@ -18,8 +18,8 @@ int16_t RadioLibRF95::begin(float freq, float bw, uint8_t sf, uint8_t cr, uint8_ // current limit was removed from module' ctor // override default value (60 mA) state = setCurrentLimit(currentLimit); - LOG_DEBUG("Current limit set to %f\n", currentLimit); - LOG_DEBUG("Current limit set result %d\n", state); + LOG_DEBUG("Current limit set to %f", currentLimit); + LOG_DEBUG("Current limit set result %d", state); // configure settings not accessible by API // state = config(); @@ -73,7 +73,7 @@ bool RadioLibRF95::isReceiving() { // 0x0b == Look for header info valid, signal synchronized or signal detected uint8_t reg = readReg(RADIOLIB_SX127X_REG_MODEM_STAT); - // Serial.printf("reg %x\n", reg); + // Serial.printf("reg %x", reg); return (reg & (RH_RF95_MODEM_STATUS_SIGNAL_DETECTED | RH_RF95_MODEM_STATUS_SIGNAL_SYNCHRONIZED | RH_RF95_MODEM_STATUS_HEADER_INFO_VALID)) != 0; } diff --git a/src/mesh/ReliableRouter.cpp b/src/mesh/ReliableRouter.cpp index fa05e7973..3e2850bcf 100644 --- a/src/mesh/ReliableRouter.cpp +++ b/src/mesh/ReliableRouter.cpp @@ -1,10 +1,10 @@ #include "ReliableRouter.h" #include "Default.h" -#include "MeshModule.h" #include "MeshTypes.h" #include "configuration.h" #include "mesh-pb-constants.h" #include "modules/NodeInfoModule.h" +#include "modules/RoutingModule.h" // ReliableRouter::ReliableRouter() {} @@ -53,14 +53,14 @@ bool ReliableRouter::shouldFilterReceived(const meshtastic_MeshPacket *p) auto key = GlobalPacketId(getFrom(p), p->id); auto old = findPendingPacket(key); if (old) { - LOG_DEBUG("generating implicit ack\n"); + LOG_DEBUG("Generate implicit ack"); // NOTE: we do NOT check p->wantAck here because p is the INCOMING rebroadcast and that packet is not expected to be // marked as wantAck sendAckNak(meshtastic_Routing_Error_NONE, getFrom(p), p->id, old->packet->channel); stopRetransmission(key); } else { - LOG_DEBUG("didn't find pending packet\n"); + LOG_DEBUG("Didn't find pending packet"); } } @@ -73,18 +73,6 @@ bool ReliableRouter::shouldFilterReceived(const meshtastic_MeshPacket *p) i->second.nextTxMsec += iface->getPacketTime(p); } - /* Resend implicit ACKs for repeated packets (hopStart equals hopLimit); - * this way if an implicit ACK is dropped and a packet is resent we'll rebroadcast again. - * Resending real ACKs is omitted, as you might receive a packet multiple times due to flooding and - * flooding this ACK back to the original sender already adds redundancy. */ - bool isRepeated = p->hop_start == 0 ? (p->hop_limit == HOP_RELIABLE) : (p->hop_start == p->hop_limit); - if (wasSeenRecently(p, false) && isRepeated && !MeshModule::currentReply && !isToUs(p)) { - LOG_DEBUG("Resending implicit ack for a repeated floodmsg\n"); - meshtastic_MeshPacket *tosend = packetPool.allocCopy(*p); - tosend->hop_limit--; // bump down the hop count - Router::send(tosend); - } - return FloodingRouter::shouldFilterReceived(p); } @@ -105,18 +93,24 @@ void ReliableRouter::sniffReceived(const meshtastic_MeshPacket *p, const meshtas if (isToUs(p)) { // ignore ack/nak/want_ack packets that are not address to us (we only handle 0 hop reliability) if (p->want_ack) { if (MeshModule::currentReply) { - LOG_DEBUG("Another module replied to this message, no need for 2nd ack\n"); + LOG_DEBUG("Another module replied to this message, no need for 2nd ack"); } else if (p->which_payload_variant == meshtastic_MeshPacket_decoded_tag) { - sendAckNak(meshtastic_Routing_Error_NONE, getFrom(p), p->id, p->channel, p->hop_start, p->hop_limit); + // A response may be set to want_ack for retransmissions, but we don't need to ACK a response if it received an + // implicit ACK already. If we received it directly, only ACK with a hop limit of 0 + if (!p->decoded.request_id) + sendAckNak(meshtastic_Routing_Error_NONE, getFrom(p), p->id, p->channel, + routingModule->getHopLimitForResponse(p->hop_start, p->hop_limit)); + else if (p->hop_start > 0 && p->hop_start == p->hop_limit) + sendAckNak(meshtastic_Routing_Error_NONE, getFrom(p), p->id, p->channel, 0); } else if (p->which_payload_variant == meshtastic_MeshPacket_encrypted_tag && p->channel == 0 && (nodeDB->getMeshNode(p->from) == nullptr || nodeDB->getMeshNode(p->from)->user.public_key.size == 0)) { - LOG_INFO("PKI packet from unknown node, send PKI_UNKNOWN_PUBKEY\n"); + LOG_INFO("PKI packet from unknown node, send PKI_UNKNOWN_PUBKEY"); sendAckNak(meshtastic_Routing_Error_PKI_UNKNOWN_PUBKEY, getFrom(p), p->id, channels.getPrimaryIndex(), - p->hop_start, p->hop_limit); + routingModule->getHopLimitForResponse(p->hop_start, p->hop_limit)); } else { // Send a 'NO_CHANNEL' error on the primary channel if want_ack packet destined for us cannot be decoded - sendAckNak(meshtastic_Routing_Error_NO_CHANNEL, getFrom(p), p->id, channels.getPrimaryIndex(), p->hop_start, - p->hop_limit); + sendAckNak(meshtastic_Routing_Error_NO_CHANNEL, getFrom(p), p->id, channels.getPrimaryIndex(), + routingModule->getHopLimitForResponse(p->hop_start, p->hop_limit)); } } if (p->which_payload_variant == meshtastic_MeshPacket_decoded_tag && c && @@ -134,7 +128,7 @@ void ReliableRouter::sniffReceived(const meshtastic_MeshPacket *p, const meshtas // We intentionally don't check wasSeenRecently, because it is harmless to delete non existent retransmission records if (ackId || nakId) { - LOG_DEBUG("Received a %s for 0x%x, stopping retransmissions\n", ackId ? "ACK" : "NAK", ackId); + LOG_DEBUG("Received a %s for 0x%x, stopping retransmissions", ackId ? "ACK" : "NAK", ackId); if (ackId) { stopRetransmission(p->to, ackId); } else { @@ -182,9 +176,9 @@ bool ReliableRouter::stopRetransmission(GlobalPacketId key) if (old->numRetransmissions < NUM_RETRANSMISSIONS - 1) { // remove the 'original' (identified by originator and packet->id) from the txqueue and free it cancelSending(getFrom(p), p->id); - // now free the pooled copy for retransmission too - packetPool.release(p); } + // now free the pooled copy for retransmission too + packetPool.release(p); auto numErased = pending.erase(key); assert(numErased == 1); return true; @@ -224,18 +218,18 @@ int32_t ReliableRouter::doRetransmissions() bool stillValid = true; // assume we'll keep this record around - // FIXME, handle 51 day rolloever here!!! + // FIXME, handle 51 day rollover here!!! if (p.nextTxMsec <= now) { if (p.numRetransmissions == 0) { - LOG_DEBUG("Reliable send failed, returning a nak for fr=0x%x,to=0x%x,id=0x%x\n", p.packet->from, p.packet->to, + LOG_DEBUG("Reliable send failed, return a nak for fr=0x%x,to=0x%x,id=0x%x", p.packet->from, p.packet->to, p.packet->id); sendAckNak(meshtastic_Routing_Error_MAX_RETRANSMIT, getFrom(p.packet), p.packet->id, p.packet->channel); // Note: we don't stop retransmission here, instead the Nak packet gets processed in sniffReceived stopRetransmission(it->first); stillValid = false; // just deleted it } else { - LOG_DEBUG("Sending reliable retransmission fr=0x%x,to=0x%x,id=0x%x, tries left=%d\n", p.packet->from, - p.packet->to, p.packet->id, p.numRetransmissions); + LOG_DEBUG("Send reliable retransmission fr=0x%x,to=0x%x,id=0x%x, tries left=%d", p.packet->from, p.packet->to, + p.packet->id, p.numRetransmissions); // Note: we call the superclass version because we don't want to have our version of send() add a new // retransmission record @@ -263,7 +257,7 @@ void ReliableRouter::setNextTx(PendingPacket *pending) assert(iface); auto d = iface->getRetransmissionMsec(pending->packet); pending->nextTxMsec = millis() + d; - LOG_DEBUG("Setting next retransmission in %u msecs: ", d); + LOG_DEBUG("Set next retransmission in %u msecs: ", d); printPacket("", pending->packet); setReceivedMessage(); // Run ASAP, so we can figure out our correct sleep time } \ No newline at end of file diff --git a/src/mesh/ReliableRouter.h b/src/mesh/ReliableRouter.h index 259da7249..ba9ab8c25 100644 --- a/src/mesh/ReliableRouter.h +++ b/src/mesh/ReliableRouter.h @@ -4,7 +4,7 @@ #include /** - * An identifier for a globalally unique message - a pair of the sending nodenum and the packet id assigned + * An identifier for a globally unique message - a pair of the sending nodenum and the packet id assigned * to that message */ struct GlobalPacketId { diff --git a/src/mesh/Router.cpp b/src/mesh/Router.cpp index 48d58ee3c..7b792db30 100644 --- a/src/mesh/Router.cpp +++ b/src/mesh/Router.cpp @@ -48,9 +48,9 @@ Router::Router() : concurrency::OSThread("Router"), fromRadioQueue(MAX_RX_FROMRA { // This is called pre main(), don't touch anything here, the following code is not safe - /* LOG_DEBUG("Size of NodeInfo %d\n", sizeof(NodeInfo)); - LOG_DEBUG("Size of SubPacket %d\n", sizeof(SubPacket)); - LOG_DEBUG("Size of MeshPacket %d\n", sizeof(MeshPacket)); */ + /* LOG_DEBUG("Size of NodeInfo %d", sizeof(NodeInfo)); + LOG_DEBUG("Size of SubPacket %d", sizeof(SubPacket)); + LOG_DEBUG("Size of MeshPacket %d", sizeof(MeshPacket)); */ fromRadioQueue.setReader(this); @@ -71,7 +71,7 @@ int32_t Router::runOnce() perhapsHandleReceived(mp); } - // LOG_DEBUG("sleeping forever!\n"); + // LOG_DEBUG("Sleep forever!"); return INT32_MAX; // Wait a long time - until we get woken for the message queue } @@ -81,14 +81,17 @@ int32_t Router::runOnce() */ void Router::enqueueReceivedMessage(meshtastic_MeshPacket *p) { - if (fromRadioQueue.enqueue(p, 0)) { // NOWAIT - fixme, if queue is full, delete older messages - - // Nasty hack because our threading is primitive. interfaces shouldn't need to know about routers FIXME - setReceivedMessage(); - } else { - printPacket("BUG! fromRadioQueue is full! Discarding!", p); - packetPool.release(p); + // Try enqueue until successful + while (!fromRadioQueue.enqueue(p, 0)) { + meshtastic_MeshPacket *old_p; + old_p = fromRadioQueue.dequeuePtr(0); // Dequeue and discard the oldest packet + if (old_p) { + printPacket("fromRadioQ full, drop oldest!", old_p); + packetPool.release(old_p); + } } + // Nasty hack because our threading is primitive. interfaces shouldn't need to know about routers FIXME + setReceivedMessage(); } /// Generate a unique packet id @@ -104,14 +107,14 @@ PacketId generatePacketId() // pick a random initial sequence number at boot (to prevent repeated reboots always starting at 0) // Note: we mask the high order bit to ensure that we never pass a 'negative' number to random rollingPacketId = random(UINT32_MAX & 0x7fffffff); - LOG_DEBUG("Initial packet id %u\n", rollingPacketId); + LOG_DEBUG("Initial packet id %u", rollingPacketId); } rollingPacketId++; rollingPacketId &= ID_COUNTER_MASK; // Mask out the top 22 bits PacketId id = rollingPacketId | random(UINT32_MAX & 0x7fffffff) << 10; // top 22 bits - LOG_DEBUG("Partially randomized packet id %u\n", id); + LOG_DEBUG("Partially randomized packet id %u", id); return id; } @@ -133,22 +136,21 @@ meshtastic_MeshPacket *Router::allocForSending() /** * Send an ack or a nak packet back towards whoever sent idFrom */ -void Router::sendAckNak(meshtastic_Routing_Error err, NodeNum to, PacketId idFrom, ChannelIndex chIndex, uint8_t hopStart, - uint8_t hopLimit) +void Router::sendAckNak(meshtastic_Routing_Error err, NodeNum to, PacketId idFrom, ChannelIndex chIndex, uint8_t hopLimit) { - routingModule->sendAckNak(err, to, idFrom, chIndex, hopStart, hopLimit); + routingModule->sendAckNak(err, to, idFrom, chIndex, hopLimit); } void Router::abortSendAndNak(meshtastic_Routing_Error err, meshtastic_MeshPacket *p) { - LOG_ERROR("Error=%d, returning NAK and dropping packet.\n", err); + LOG_ERROR("Error=%d, return NAK and drop packet", err); sendAckNak(err, getFrom(p), p->id, p->channel); packetPool.release(p); } void Router::setReceivedMessage() { - // LOG_DEBUG("set interval to ASAP\n"); + // LOG_DEBUG("set interval to ASAP"); setInterval(0); // Run ASAP, so we can figure out our correct sleep time runASAP = true; } @@ -166,7 +168,7 @@ meshtastic_QueueStatus Router::getQueueStatus() ErrorCode Router::sendLocal(meshtastic_MeshPacket *p, RxSource src) { if (p->to == 0) { - LOG_ERROR("Packet received with to: of 0!\n"); + LOG_ERROR("Packet received with to: of 0!"); } // No need to deliver externally if the destination is the local node if (isToUs(p)) { @@ -181,15 +183,16 @@ ErrorCode Router::sendLocal(meshtastic_MeshPacket *p, RxSource src) } else { // If we are sending a broadcast, we also treat it as if we just received it ourself // this allows local apps (and PCs) to see broadcasts sourced locally - if (p->to == NODENUM_BROADCAST) { + if (isBroadcast(p->to)) { handleReceived(p, src); } - if (!p->channel && !p->pki_encrypted) { // don't override if a channel was requested + // don't override if a channel was requested and no need to set it when PKI is enforced + if (!p->channel && !p->pki_encrypted) { meshtastic_NodeInfoLite *node = nodeDB->getMeshNode(p->to); - if (node && node->user.public_key.size == 0) { + if (node) { p->channel = node->channel; - LOG_DEBUG("localSend to channel %d\n", p->channel); + LOG_DEBUG("localSend to channel %d", p->channel); } } @@ -205,7 +208,7 @@ ErrorCode Router::sendLocal(meshtastic_MeshPacket *p, RxSource src) ErrorCode Router::send(meshtastic_MeshPacket *p) { if (isToUs(p)) { - LOG_ERROR("BUG! send() called with packet destined for local node!\n"); + LOG_ERROR("BUG! send() called with packet destined for local node!"); packetPool.release(p); return meshtastic_Routing_Error_BAD_REQUEST; } // should have already been handled by sendLocal @@ -216,13 +219,13 @@ ErrorCode Router::send(meshtastic_MeshPacket *p) if (hourlyTxPercent > myRegion->dutyCycle) { #ifdef DEBUG_PORT uint8_t silentMinutes = airTime->getSilentMinutes(hourlyTxPercent, myRegion->dutyCycle); - LOG_WARN("Duty cycle limit exceeded. Aborting send for now, you can send again in %d minutes.\n", silentMinutes); + LOG_WARN("Duty cycle limit exceeded. Aborting send for now, you can send again in %d mins", silentMinutes); meshtastic_ClientNotification *cn = clientNotificationPool.allocZeroed(); cn->has_reply_id = true; cn->reply_id = p->id; cn->level = meshtastic_LogRecord_Level_WARNING; cn->time = getValidTime(RTCQualityFromNet); - sprintf(cn->message, "Duty cycle limit exceeded. You can send again in %d minutes.", silentMinutes); + sprintf(cn->message, "Duty cycle limit exceeded. You can send again in %d mins", silentMinutes); service->sendClientNotification(cn); #endif meshtastic_Routing_Error err = meshtastic_Routing_Error_DUTY_CYCLE_LIMIT; @@ -240,7 +243,7 @@ ErrorCode Router::send(meshtastic_MeshPacket *p) // assert // Never set the want_ack flag on broadcast packets sent over the air. - if (p->to == NODENUM_BROADCAST) + if (isBroadcast(p->to)) p->want_ack = false; // Up until this point we might have been using 0 for the from address (if it started with the phone), but when we send over @@ -309,7 +312,7 @@ bool perhapsDecode(meshtastic_MeshPacket *p) if (config.device.rebroadcast_mode == meshtastic_Config_DeviceConfig_RebroadcastMode_KNOWN_ONLY && (nodeDB->getMeshNode(p->from) == NULL || !nodeDB->getMeshNode(p->from)->has_user)) { - LOG_DEBUG("Node 0x%x not in nodeDB-> Rebroadcast mode KNOWN_ONLY will ignore packet\n", p->from); + LOG_DEBUG("Node 0x%x not in nodeDB-> Rebroadcast mode KNOWN_ONLY will ignore packet", p->from); return false; } @@ -318,7 +321,7 @@ bool perhapsDecode(meshtastic_MeshPacket *p) size_t rawSize = p->encrypted.size; if (rawSize > sizeof(bytes)) { - LOG_ERROR("Packet too large to attempt decryption! (rawSize=%d > 256)\n", rawSize); + LOG_ERROR("Packet too large to attempt decryption! (rawSize=%d > 256)", rawSize); return false; } bool decrypted = false; @@ -328,31 +331,31 @@ bool perhapsDecode(meshtastic_MeshPacket *p) memcpy(ScratchEncrypted, p->encrypted.bytes, rawSize); #if !(MESHTASTIC_EXCLUDE_PKI) // Attempt PKI decryption first - if (p->channel == 0 && isToUs(p) && p->to > 0 && p->to != NODENUM_BROADCAST && nodeDB->getMeshNode(p->from) != nullptr && + if (p->channel == 0 && isToUs(p) && p->to > 0 && !isBroadcast(p->to) && nodeDB->getMeshNode(p->from) != nullptr && nodeDB->getMeshNode(p->from)->user.public_key.size > 0 && nodeDB->getMeshNode(p->to)->user.public_key.size > 0 && rawSize > MESHTASTIC_PKC_OVERHEAD) { - LOG_DEBUG("Attempting PKI decryption\n"); + LOG_DEBUG("Attempt PKI decryption"); if (crypto->decryptCurve25519(p->from, nodeDB->getMeshNode(p->from)->user.public_key, p->id, rawSize, ScratchEncrypted, bytes)) { - LOG_INFO("PKI Decryption worked!\n"); + LOG_INFO("PKI Decryption worked!"); memset(&p->decoded, 0, sizeof(p->decoded)); rawSize -= MESHTASTIC_PKC_OVERHEAD; if (pb_decode_from_bytes(bytes, rawSize, &meshtastic_Data_msg, &p->decoded) && p->decoded.portnum != meshtastic_PortNum_UNKNOWN_APP) { decrypted = true; - LOG_INFO("Packet decrypted using PKI!\n"); + LOG_INFO("Packet decrypted using PKI!"); p->pki_encrypted = true; memcpy(&p->public_key.bytes, nodeDB->getMeshNode(p->from)->user.public_key.bytes, 32); p->public_key.size = 32; // memcpy(bytes, ScratchEncrypted, rawSize); // TODO: Rename the bytes buffers // chIndex = 8; } else { - LOG_ERROR("PKC Decrypted, but pb_decode failed!\n"); + LOG_ERROR("PKC Decrypted, but pb_decode failed!"); return false; } } else { - LOG_WARN("PKC decrypt attempted but failed!\n"); + LOG_WARN("PKC decrypt attempted but failed!"); } } #endif @@ -371,9 +374,9 @@ bool perhapsDecode(meshtastic_MeshPacket *p) // Take those raw bytes and convert them back into a well structured protobuf we can understand memset(&p->decoded, 0, sizeof(p->decoded)); if (!pb_decode_from_bytes(bytes, rawSize, &meshtastic_Data_msg, &p->decoded)) { - LOG_ERROR("Invalid protobufs in received mesh packet id=0x%08x (bad psk?)!\n", p->id); + LOG_ERROR("Invalid protobufs in received mesh packet id=0x%08x (bad psk?)!", p->id); } else if (p->decoded.portnum == meshtastic_PortNum_UNKNOWN_APP) { - LOG_ERROR("Invalid portnum (bad psk?)!\n"); + LOG_ERROR("Invalid portnum (bad psk?)!"); } else { decrypted = true; break; @@ -400,7 +403,7 @@ bool perhapsDecode(meshtastic_MeshPacket *p) decompressed_len = unishox2_decompress_simple(compressed_in, p->decoded.payload.size, decompressed_out); - // LOG_DEBUG("\n\n**\n\nDecompressed length - %d \n", decompressed_len); + // LOG_DEBUG("**Decompressed length - %d ", decompressed_len); memcpy(p->decoded.payload.bytes, decompressed_out, decompressed_len); @@ -410,20 +413,20 @@ bool perhapsDecode(meshtastic_MeshPacket *p) printPacket("decoded message", p); #if ENABLE_JSON_LOGGING - LOG_TRACE("%s\n", MeshPacketSerializer::JsonSerialize(p, false).c_str()); + LOG_TRACE("%s", MeshPacketSerializer::JsonSerialize(p, false).c_str()); #elif ARCH_PORTDUINO if (settingsStrings[traceFilename] != "" || settingsMap[logoutputlevel] == level_trace) { - LOG_TRACE("%s\n", MeshPacketSerializer::JsonSerialize(p, false).c_str()); + LOG_TRACE("%s", MeshPacketSerializer::JsonSerialize(p, false).c_str()); } #endif return true; } else { - LOG_WARN("No suitable channel found for decoding, hash was 0x%x!\n", p->channel); + LOG_WARN("No suitable channel found for decoding, hash was 0x%x!", p->channel); return false; } } -/** Return 0 for success or a Routing_Errror code for failure +/** Return 0 for success or a Routing_Error code for failure */ meshtastic_Routing_Error perhapsEncode(meshtastic_MeshPacket *p) { @@ -453,20 +456,20 @@ meshtastic_Routing_Error perhapsEncode(meshtastic_MeshPacket *p) int compressed_len; compressed_len = unishox2_compress_simple(original_payload, p->decoded.payload.size, compressed_out); - LOG_DEBUG("Original length - %d \n", p->decoded.payload.size); - LOG_DEBUG("Compressed length - %d \n", compressed_len); - LOG_DEBUG("Original message - %s \n", p->decoded.payload.bytes); + LOG_DEBUG("Original length - %d ", p->decoded.payload.size); + LOG_DEBUG("Compressed length - %d ", compressed_len); + LOG_DEBUG("Original message - %s ", p->decoded.payload.bytes); // If the compressed length is greater than or equal to the original size, don't use the compressed form if (compressed_len >= p->decoded.payload.size) { - LOG_DEBUG("Not using compressing message.\n"); + LOG_DEBUG("Not using compressing message"); // Set the uncompressed payload variant anyway. Shouldn't hurt? // p->decoded.which_payloadVariant = Data_payload_tag; // Otherwise we use the compressor } else { - LOG_DEBUG("Using compressed message.\n"); + LOG_DEBUG("Use compressed message"); // Copy the compressed data into the meshpacket p->decoded.payload.size = compressed_len; @@ -487,24 +490,25 @@ meshtastic_Routing_Error perhapsEncode(meshtastic_MeshPacket *p) meshtastic_NodeInfoLite *node = nodeDB->getMeshNode(p->to); // We may want to retool things so we can send a PKC packet when the client specifies a key and nodenum, even if the node // is not in the local nodedb - if ( + // First, only PKC encrypt packets we are originating + if (isFromUs(p) && // Don't use PKC with Ham mode !owner.is_licensed && // Don't use PKC if it's not explicitly requested and a non-primary channel is requested !(p->pki_encrypted != true && p->channel > 0) && // Check for valid keys and single node destination - config.security.private_key.size == 32 && p->to != NODENUM_BROADCAST && node != nullptr && + config.security.private_key.size == 32 && !isBroadcast(p->to) && node != nullptr && // Check for a known public key for the destination (node->user.public_key.size == 32) && // Some portnums either make no sense to send with PKC p->decoded.portnum != meshtastic_PortNum_TRACEROUTE_APP && p->decoded.portnum != meshtastic_PortNum_NODEINFO_APP && p->decoded.portnum != meshtastic_PortNum_ROUTING_APP && p->decoded.portnum != meshtastic_PortNum_POSITION_APP) { - LOG_DEBUG("Using PKI!\n"); + LOG_DEBUG("Use PKI!"); if (numbytes + MESHTASTIC_HEADER_LENGTH + MESHTASTIC_PKC_OVERHEAD > MAX_LORA_PAYLOAD_LEN) return meshtastic_Routing_Error_TOO_LARGE; if (p->pki_encrypted && !memfll(p->public_key.bytes, 0, 32) && memcmp(p->public_key.bytes, node->user.public_key.bytes, 32) != 0) { - LOG_WARN("Client public key differs from requested: 0x%02x, stored key begins 0x%02x\n", *p->public_key.bytes, + LOG_WARN("Client public key differs from requested: 0x%02x, stored key begins 0x%02x", *p->public_key.bytes, *node->user.public_key.bytes); return meshtastic_Routing_Error_PKI_FAILED; } @@ -586,24 +590,25 @@ void Router::handleReceived(meshtastic_MeshPacket *p, RxSource src) if (p->which_payload_variant == meshtastic_MeshPacket_decoded_tag && p->decoded.portnum == meshtastic_PortNum_NEIGHBORINFO_APP && (!moduleConfig.has_neighbor_info || !moduleConfig.neighbor_info.enabled)) { - LOG_DEBUG("Neighbor info module is disabled, ignoring neighbor packet\n"); + LOG_DEBUG("Neighbor info module is disabled, ignore neighbor packet"); cancelSending(p->from, p->id); skipHandle = true; } + bool shouldIgnoreNonstandardPorts = + config.device.rebroadcast_mode == meshtastic_Config_DeviceConfig_RebroadcastMode_CORE_PORTNUMS_ONLY; #if USERPREFS_EVENT_MODE - if (p->which_payload_variant == meshtastic_MeshPacket_decoded_tag && - (p->decoded.portnum == meshtastic_PortNum_ATAK_FORWARDER || p->decoded.portnum == meshtastic_PortNum_ATAK_PLUGIN || - p->decoded.portnum == meshtastic_PortNum_PAXCOUNTER_APP || p->decoded.portnum == meshtastic_PortNum_IP_TUNNEL_APP || - p->decoded.portnum == meshtastic_PortNum_AUDIO_APP || p->decoded.portnum == meshtastic_PortNum_PRIVATE_APP || - p->decoded.portnum == meshtastic_PortNum_DETECTION_SENSOR_APP || - p->decoded.portnum == meshtastic_PortNum_RANGE_TEST_APP || - p->decoded.portnum == meshtastic_PortNum_REMOTE_HARDWARE_APP)) { - LOG_DEBUG("Ignoring packet on blacklisted portnum during event\n"); + shouldIgnoreNonstandardPorts = true; +#endif + if (shouldIgnoreNonstandardPorts && p->which_payload_variant == meshtastic_MeshPacket_decoded_tag && + IS_ONE_OF(p->decoded.portnum, meshtastic_PortNum_ATAK_FORWARDER, meshtastic_PortNum_ATAK_PLUGIN, + meshtastic_PortNum_PAXCOUNTER_APP, meshtastic_PortNum_IP_TUNNEL_APP, meshtastic_PortNum_AUDIO_APP, + meshtastic_PortNum_PRIVATE_APP, meshtastic_PortNum_DETECTION_SENSOR_APP, meshtastic_PortNum_RANGE_TEST_APP, + meshtastic_PortNum_REMOTE_HARDWARE_APP)) { + LOG_DEBUG("Ignore packet on blacklisted portnum for CORE_PORTNUMS_ONLY"); cancelSending(p->from, p->id); skipHandle = true; } -#endif } else { printPacket("packet decoding failed or skipped (no PSK?)", p); } @@ -615,7 +620,7 @@ void Router::handleReceived(meshtastic_MeshPacket *p, RxSource src) #if !MESHTASTIC_EXCLUDE_MQTT // Mark as pki_encrypted if it is not yet decoded and MQTT encryption is also enabled, hash matches and it's a DM not to // us (because we would be able to decrypt it) - if (!decoded && moduleConfig.mqtt.encryption_enabled && p->channel == 0x00 && p->to != NODENUM_BROADCAST && !isToUs(p)) + if (!decoded && moduleConfig.mqtt.encryption_enabled && p->channel == 0x00 && !isBroadcast(p->to) && !isToUs(p)) p_encrypted->pki_encrypted = true; // After potentially altering it, publish received message to MQTT if we're not the original transmitter of the packet if ((decoded || p_encrypted->pki_encrypted) && moduleConfig.mqtt.enabled && !isFromUs(p) && mqtt) @@ -631,35 +636,42 @@ void Router::perhapsHandleReceived(meshtastic_MeshPacket *p) #if ENABLE_JSON_LOGGING // Even ignored packets get logged in the trace p->rx_time = getValidTime(RTCQualityFromNet); // store the arrival timestamp for the phone - LOG_TRACE("%s\n", MeshPacketSerializer::JsonSerializeEncrypted(p).c_str()); + LOG_TRACE("%s", MeshPacketSerializer::JsonSerializeEncrypted(p).c_str()); #elif ARCH_PORTDUINO // Even ignored packets get logged in the trace if (settingsStrings[traceFilename] != "" || settingsMap[logoutputlevel] == level_trace) { p->rx_time = getValidTime(RTCQualityFromNet); // store the arrival timestamp for the phone - LOG_TRACE("%s\n", MeshPacketSerializer::JsonSerializeEncrypted(p).c_str()); + LOG_TRACE("%s", MeshPacketSerializer::JsonSerializeEncrypted(p).c_str()); } #endif // assert(radioConfig.has_preferences); if (is_in_repeated(config.lora.ignore_incoming, p->from)) { - LOG_DEBUG("Ignoring msg, 0x%x is in our ignore list\n", p->from); + LOG_DEBUG("Ignore msg, 0x%x is in our ignore list", p->from); + packetPool.release(p); + return; + } + + meshtastic_NodeInfoLite *node = nodeDB->getMeshNode(p->from); + if (node != NULL && node->is_ignored) { + LOG_DEBUG("Ignore msg, 0x%x is ignored", p->from); packetPool.release(p); return; } if (p->from == NODENUM_BROADCAST) { - LOG_DEBUG("Ignoring msg from broadcast address\n"); + LOG_DEBUG("Ignore msg from broadcast address"); packetPool.release(p); return; } if (config.lora.ignore_mqtt && p->via_mqtt) { - LOG_DEBUG("Msg came in via MQTT from 0x%x\n", p->from); + LOG_DEBUG("Msg came in via MQTT from 0x%x", p->from); packetPool.release(p); return; } if (shouldFilterReceived(p)) { - LOG_DEBUG("Incoming msg was filtered from 0x%x\n", p->from); + LOG_DEBUG("Incoming msg was filtered from 0x%x", p->from); packetPool.release(p); return; } diff --git a/src/mesh/Router.h b/src/mesh/Router.h index 8ebc1a3e5..da44d67df 100644 --- a/src/mesh/Router.h +++ b/src/mesh/Router.h @@ -108,8 +108,7 @@ class Router : protected concurrency::OSThread /** * Send an ack or a nak packet back towards whoever sent idFrom */ - void sendAckNak(meshtastic_Routing_Error err, NodeNum to, PacketId idFrom, ChannelIndex chIndex, uint8_t hopStart = 0, - uint8_t hopLimit = 0); + void sendAckNak(meshtastic_Routing_Error err, NodeNum to, PacketId idFrom, ChannelIndex chIndex, uint8_t hopLimit = 0); private: /** @@ -132,7 +131,7 @@ class Router : protected concurrency::OSThread */ void handleReceived(meshtastic_MeshPacket *p, RxSource src = RX_SRC_RADIO); - /** Frees the provided packet, and generates a NAK indicating the speicifed error while sending */ + /** Frees the provided packet, and generates a NAK indicating the specifed error while sending */ void abortSendAndNak(meshtastic_Routing_Error err, meshtastic_MeshPacket *p); }; @@ -143,7 +142,7 @@ class Router : protected concurrency::OSThread */ bool perhapsDecode(meshtastic_MeshPacket *p); -/** Return 0 for success or a Routing_Errror code for failure +/** Return 0 for success or a Routing_Error code for failure */ meshtastic_Routing_Error perhapsEncode(meshtastic_MeshPacket *p); diff --git a/src/mesh/STM32WLE5JCInterface.cpp b/src/mesh/STM32WLE5JCInterface.cpp index 3c1870d3b..499db9176 100644 --- a/src/mesh/STM32WLE5JCInterface.cpp +++ b/src/mesh/STM32WLE5JCInterface.cpp @@ -27,11 +27,11 @@ bool STM32WLE5JCInterface::init() int res = lora.begin(getFreq(), bw, sf, cr, syncWord, power, preambleLength, tcxoVoltage); - LOG_INFO("STM32WLx init result %d\n", res); + LOG_INFO("STM32WLx init result %d", res); - LOG_INFO("Frequency set to %f\n", getFreq()); - LOG_INFO("Bandwidth set to %f\n", bw); - LOG_INFO("Power output set to %d\n", power); + LOG_INFO("Frequency set to %f", getFreq()); + LOG_INFO("Bandwidth set to %f", bw); + LOG_INFO("Power output set to %d", power); if (res == RADIOLIB_ERR_NONE) startReceive(); // start receiving diff --git a/src/mesh/SX126xInterface.cpp b/src/mesh/SX126xInterface.cpp index 060e29412..1e8ed14d6 100644 --- a/src/mesh/SX126xInterface.cpp +++ b/src/mesh/SX126xInterface.cpp @@ -20,7 +20,7 @@ SX126xInterface::SX126xInterface(LockingArduinoHal *hal, RADIOLIB_PIN_TYPE cs RADIOLIB_PIN_TYPE busy) : RadioLibInterface(hal, cs, irq, rst, busy, &lora), lora(&module) { - LOG_DEBUG("SX126xInterface(cs=%d, irq=%d, rst=%d, busy=%d)\n", cs, irq, rst, busy); + LOG_DEBUG("SX126xInterface(cs=%d, irq=%d, rst=%d, busy=%d)", cs, irq, rst, busy); } /// Initialise the Driver transport hardware and software. @@ -70,9 +70,9 @@ template bool SX126xInterface::init() // (DIO3 is not free to be used as an IRQ) #endif if (tcxoVoltage == 0) - LOG_DEBUG("SX126X_DIO3_TCXO_VOLTAGE not defined, not using DIO3 as TCXO reference voltage\n"); + LOG_DEBUG("SX126X_DIO3_TCXO_VOLTAGE not defined, not using DIO3 as TCXO reference voltage"); else - LOG_DEBUG("SX126X_DIO3_TCXO_VOLTAGE defined, using DIO3 as TCXO reference voltage at %f V\n", tcxoVoltage); + LOG_DEBUG("SX126X_DIO3_TCXO_VOLTAGE defined, using DIO3 as TCXO reference voltage at %f V", tcxoVoltage); // FIXME: May want to set depending on a definition, currently all SX126x variant files use the DC-DC regulator option bool useRegulatorLDO = false; // Seems to depend on the connection to pin 9/DCC_SW - if an inductor DCDC? @@ -86,25 +86,25 @@ template bool SX126xInterface::init() int res = lora.begin(getFreq(), bw, sf, cr, syncWord, power, preambleLength, tcxoVoltage, useRegulatorLDO); // \todo Display actual typename of the adapter, not just `SX126x` - LOG_INFO("SX126x init result %d\n", res); + LOG_INFO("SX126x init result %d", res); if (res == RADIOLIB_ERR_CHIP_NOT_FOUND) return false; - LOG_INFO("Frequency set to %f\n", getFreq()); - LOG_INFO("Bandwidth set to %f\n", bw); - LOG_INFO("Power output set to %d\n", power); + LOG_INFO("Frequency set to %f", getFreq()); + LOG_INFO("Bandwidth set to %f", bw); + LOG_INFO("Power output set to %d", power); // Overriding current limit // (https://github.com/jgromes/RadioLib/blob/690a050ebb46e6097c5d00c371e961c1caa3b52e/src/modules/SX126x/SX126x.cpp#L85) using - // value in SX126xInterface.h (currently 140 mA) It may or may not be neccessary, depending on how RadioLib functions, from + // value in SX126xInterface.h (currently 140 mA) It may or may not be necessary, depending on how RadioLib functions, from // SX1261/2 datasheet: OCP after setting DeviceSel with SetPaConfig(): SX1261 - 60 mA, SX1262 - 140 mA For the SX1268 the IC // defaults to 140mA no matter the set power level, but RadioLib set it lower, this would need further checking Default values // are: SX1262, SX1268: 0x38 (140 mA), SX1261: 0x18 (60 mA) // FIXME: Not ideal to increase SX1261 current limit above 60mA as it can only transmit max 15dBm, should probably only do it // if using SX1262 or SX1268 res = lora.setCurrentLimit(currentLimit); - LOG_DEBUG("Current limit set to %f\n", currentLimit); - LOG_DEBUG("Current limit set result %d\n", res); + LOG_DEBUG("Current limit set to %f", currentLimit); + LOG_DEBUG("Current limit set result %d", res); if (res == RADIOLIB_ERR_NONE) { #ifdef SX126X_DIO2_AS_RF_SWITCH @@ -118,36 +118,36 @@ template bool SX126xInterface::init() bool dio2AsRfSwitch = false; #endif res = lora.setDio2AsRfSwitch(dio2AsRfSwitch); - LOG_DEBUG("Set DIO2 as %sRF switch, result: %d\n", dio2AsRfSwitch ? "" : "not ", res); + LOG_DEBUG("Set DIO2 as %sRF switch, result: %d", dio2AsRfSwitch ? "" : "not ", res); } // If a pin isn't defined, we set it to RADIOLIB_NC, it is safe to always do external RF switching with RADIOLIB_NC as it has // no effect #if ARCH_PORTDUINO if (res == RADIOLIB_ERR_NONE) { - LOG_DEBUG("Using MCU pin %i as RXEN and pin %i as TXEN to control RF switching\n", settingsMap[rxen], settingsMap[txen]); + LOG_DEBUG("Use MCU pin %i as RXEN and pin %i as TXEN to control RF switching", settingsMap[rxen], settingsMap[txen]); lora.setRfSwitchPins(settingsMap[rxen], settingsMap[txen]); } #else #ifndef SX126X_RXEN #define SX126X_RXEN RADIOLIB_NC - LOG_DEBUG("SX126X_RXEN not defined, defaulting to RADIOLIB_NC\n"); + LOG_DEBUG("SX126X_RXEN not defined, defaulting to RADIOLIB_NC"); #endif #ifndef SX126X_TXEN #define SX126X_TXEN RADIOLIB_NC - LOG_DEBUG("SX126X_TXEN not defined, defaulting to RADIOLIB_NC\n"); + LOG_DEBUG("SX126X_TXEN not defined, defaulting to RADIOLIB_NC"); #endif if (res == RADIOLIB_ERR_NONE) { - LOG_DEBUG("Using MCU pin %i as RXEN and pin %i as TXEN to control RF switching\n", SX126X_RXEN, SX126X_TXEN); + LOG_DEBUG("Use MCU pin %i as RXEN and pin %i as TXEN to control RF switching", SX126X_RXEN, SX126X_TXEN); lora.setRfSwitchPins(SX126X_RXEN, SX126X_TXEN); } #endif if (config.lora.sx126x_rx_boosted_gain) { uint16_t result = lora.setRxBoostedGainMode(true); - LOG_INFO("Set RX gain to boosted mode; result: %d\n", result); + LOG_INFO("Set RX gain to boosted mode; result: %d", result); } else { uint16_t result = lora.setRxBoostedGainMode(false); - LOG_INFO("Set RX gain to power saving mode (boosted mode off); result: %d\n", result); + LOG_INFO("Set RX gain to power saving mode (boosted mode off); result: %d", result); } #if 0 @@ -205,17 +205,17 @@ template bool SX126xInterface::reconfigure() err = lora.setSyncWord(syncWord); if (err != RADIOLIB_ERR_NONE) - LOG_ERROR("SX126X setSyncWord %s%d\n", radioLibErr, err); + LOG_ERROR("SX126X setSyncWord %s%d", radioLibErr, err); assert(err == RADIOLIB_ERR_NONE); err = lora.setCurrentLimit(currentLimit); if (err != RADIOLIB_ERR_NONE) - LOG_ERROR("SX126X setCurrentLimit %s%d\n", radioLibErr, err); + LOG_ERROR("SX126X setCurrentLimit %s%d", radioLibErr, err); assert(err == RADIOLIB_ERR_NONE); err = lora.setPreambleLength(preambleLength); if (err != RADIOLIB_ERR_NONE) - LOG_ERROR("SX126X setPreambleLength %s%d\n", radioLibErr, err); + LOG_ERROR("SX126X setPreambleLength %s%d", radioLibErr, err); assert(err == RADIOLIB_ERR_NONE); err = lora.setFrequency(getFreq()); @@ -227,7 +227,7 @@ template bool SX126xInterface::reconfigure() err = lora.setOutputPower(power); if (err != RADIOLIB_ERR_NONE) - LOG_ERROR("SX126X setOutputPower %s%d\n", radioLibErr, err); + LOG_ERROR("SX126X setOutputPower %s%d", radioLibErr, err); assert(err == RADIOLIB_ERR_NONE); startReceive(); // restart receiving @@ -247,7 +247,7 @@ template void SX126xInterface::setStandby() int err = lora.standby(); if (err != RADIOLIB_ERR_NONE) - LOG_DEBUG("SX126x standby %s%d\n", radioLibErr, err); + LOG_DEBUG("SX126x standby %s%d", radioLibErr, err); assert(err == RADIOLIB_ERR_NONE); isReceiving = false; // If we were receiving, not any more @@ -262,7 +262,7 @@ template void SX126xInterface::setStandby() */ template void SX126xInterface::addReceiveMetadata(meshtastic_MeshPacket *mp) { - // LOG_DEBUG("PacketStatus %x\n", lora.getPacketStatus()); + // LOG_DEBUG("PacketStatus %x", lora.getPacketStatus()); mp->rx_snr = lora.getSNR(); mp->rx_rssi = lround(lora.getRSSI()); } @@ -289,7 +289,7 @@ template void SX126xInterface::startReceive() // Furthermore, we need the PREAMBLE_DETECTED and HEADER_VALID IRQ flag to detect whether we are actively receiving int err = lora.startReceiveDutyCycleAuto(preambleLength, 8, RADIOLIB_IRQ_RX_DEFAULT_FLAGS | RADIOLIB_IRQ_PREAMBLE_DETECTED); if (err != RADIOLIB_ERR_NONE) - LOG_ERROR("SX126X startReceiveDutyCycleAuto %s%d\n", radioLibErr, err); + LOG_ERROR("SX126X startReceiveDutyCycleAuto %s%d", radioLibErr, err); assert(err == RADIOLIB_ERR_NONE); RadioLibInterface::startReceive(); @@ -310,7 +310,7 @@ template bool SX126xInterface::isChannelActive() if (result == RADIOLIB_LORA_DETECTED) return true; if (result != RADIOLIB_CHANNEL_FREE) - LOG_ERROR("SX126X scanChannel %s%d\n", radioLibErr, result); + LOG_ERROR("SX126X scanChannel %s%d", radioLibErr, result); assert(result != RADIOLIB_ERR_WRONG_MODEM); return false; @@ -328,8 +328,8 @@ template bool SX126xInterface::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 `SX126x` - LOG_DEBUG("SX126x entering sleep mode\n"); // (FIXME, don't keep config) - setStandby(); // Stop any pending operations + LOG_DEBUG("SX126x entering sleep mode"); // (FIXME, don't keep config) + setStandby(); // Stop any pending operations // turn off TCXO if it was powered // FIXME - this isn't correct diff --git a/src/mesh/SX128xInterface.cpp b/src/mesh/SX128xInterface.cpp index d379f26e6..013164bca 100644 --- a/src/mesh/SX128xInterface.cpp +++ b/src/mesh/SX128xInterface.cpp @@ -19,7 +19,7 @@ SX128xInterface::SX128xInterface(LockingArduinoHal *hal, RADIOLIB_PIN_TYPE cs RADIOLIB_PIN_TYPE busy) : RadioLibInterface(hal, cs, irq, rst, busy, &lora), lora(&module) { - LOG_DEBUG("SX128xInterface(cs=%d, irq=%d, rst=%d, busy=%d)\n", cs, irq, rst, busy); + LOG_DEBUG("SX128xInterface(cs=%d, irq=%d, rst=%d, busy=%d)", cs, irq, rst, busy); } /// Initialise the Driver transport hardware and software. @@ -68,10 +68,10 @@ template bool SX128xInterface::init() int res = lora.begin(getFreq(), bw, sf, cr, syncWord, power, preambleLength); // \todo Display actual typename of the adapter, not just `SX128x` - LOG_INFO("SX128x init result %d\n", res); + LOG_INFO("SX128x init result %d", res); if ((config.lora.region != meshtastic_Config_LoRaConfig_RegionCode_LORA_24) && (res == RADIOLIB_ERR_INVALID_FREQUENCY)) { - LOG_WARN("Radio chip only supports 2.4GHz LoRa. Adjusting Region and rebooting.\n"); + LOG_WARN("Radio only supports 2.4GHz LoRa. Adjusting Region and rebooting"); config.lora.region = meshtastic_Config_LoRaConfig_RegionCode_LORA_24; nodeDB->saveToDisk(SEGMENT_CONFIG); delay(2000); @@ -80,13 +80,13 @@ template bool SX128xInterface::init() #elif defined(ARCH_NRF52) NVIC_SystemReset(); #else - LOG_ERROR("FIXME implement reboot for this platform. Skipping for now.\n"); + LOG_ERROR("FIXME implement reboot for this platform. Skip for now"); #endif } - LOG_INFO("Frequency set to %f\n", getFreq()); - LOG_INFO("Bandwidth set to %f\n", bw); - LOG_INFO("Power output set to %d\n", power); + LOG_INFO("Frequency set to %f", getFreq()); + LOG_INFO("Bandwidth set to %f", bw); + LOG_INFO("Power output set to %d", power); #if defined(SX128X_TXEN) && (SX128X_TXEN != RADIOLIB_NC) && defined(SX128X_RXEN) && (SX128X_RXEN != RADIOLIB_NC) if (res == RADIOLIB_ERR_NONE) { @@ -129,12 +129,12 @@ template bool SX128xInterface::reconfigure() err = lora.setSyncWord(syncWord); if (err != RADIOLIB_ERR_NONE) - LOG_ERROR("SX128X setSyncWord %s%d\n", radioLibErr, err); + LOG_ERROR("SX128X setSyncWord %s%d", radioLibErr, err); assert(err == RADIOLIB_ERR_NONE); err = lora.setPreambleLength(preambleLength); if (err != RADIOLIB_ERR_NONE) - LOG_ERROR("SX128X setPreambleLength %s%d\n", radioLibErr, err); + LOG_ERROR("SX128X setPreambleLength %s%d", radioLibErr, err); assert(err == RADIOLIB_ERR_NONE); err = lora.setFrequency(getFreq()); @@ -146,7 +146,7 @@ template bool SX128xInterface::reconfigure() err = lora.setOutputPower(power); if (err != RADIOLIB_ERR_NONE) - LOG_ERROR("SX128X setOutputPower %s%d\n", radioLibErr, err); + LOG_ERROR("SX128X setOutputPower %s%d", radioLibErr, err); assert(err == RADIOLIB_ERR_NONE); startReceive(); // restart receiving @@ -171,7 +171,7 @@ template void SX128xInterface::setStandby() int err = lora.standby(); if (err != RADIOLIB_ERR_NONE) - LOG_ERROR("SX128x standby %s%d\n", radioLibErr, err); + LOG_ERROR("SX128x standby %s%d", radioLibErr, err); assert(err == RADIOLIB_ERR_NONE); #if ARCH_PORTDUINO if (settingsMap[rxen] != RADIOLIB_NC) { @@ -200,7 +200,7 @@ template void SX128xInterface::setStandby() */ template void SX128xInterface::addReceiveMetadata(meshtastic_MeshPacket *mp) { - // LOG_DEBUG("PacketStatus %x\n", lora.getPacketStatus()); + // LOG_DEBUG("PacketStatus %x", lora.getPacketStatus()); mp->rx_snr = lora.getSNR(); mp->rx_rssi = lround(lora.getRSSI()); } @@ -261,7 +261,7 @@ template void SX128xInterface::startReceive() int err = lora.startReceive(RADIOLIB_SX128X_RX_TIMEOUT_INF, RADIOLIB_IRQ_RX_DEFAULT_FLAGS | RADIOLIB_IRQ_PREAMBLE_DETECTED); if (err != RADIOLIB_ERR_NONE) - LOG_ERROR("SX128X startReceive %s%d\n", radioLibErr, err); + LOG_ERROR("SX128X startReceive %s%d", radioLibErr, err); assert(err == RADIOLIB_ERR_NONE); RadioLibInterface::startReceive(); @@ -282,7 +282,7 @@ template bool SX128xInterface::isChannelActive() if (result == RADIOLIB_LORA_DETECTED) return true; if (result != RADIOLIB_CHANNEL_FREE) - LOG_ERROR("SX128X scanChannel %s%d\n", radioLibErr, result); + LOG_ERROR("SX128X scanChannel %s%d", radioLibErr, result); assert(result != RADIOLIB_ERR_WRONG_MODEM); return false; @@ -298,8 +298,8 @@ 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` - LOG_DEBUG("SX128x entering sleep mode\n"); // (FIXME, don't keep config) - setStandby(); // Stop any pending operations + LOG_DEBUG("SX128x entering sleep mode"); // (FIXME, don't keep config) + setStandby(); // Stop any pending operations // turn off TCXO if it was powered // FIXME - this isn't correct diff --git a/src/mesh/StreamAPI.cpp b/src/mesh/StreamAPI.cpp index c3d85ed33..4a42e5197 100644 --- a/src/mesh/StreamAPI.cpp +++ b/src/mesh/StreamAPI.cpp @@ -115,7 +115,7 @@ void StreamAPI::emitRebooted() fromRadioScratch.which_payload_variant = meshtastic_FromRadio_rebooted_tag; fromRadioScratch.rebooted = true; - // LOG_DEBUG("Emitting reboot packet for serial shell\n"); + // LOG_DEBUG("Emitting reboot packet for serial shell"); emitTxBuffer(pb_encode_to_bytes(txBuf + HEADER_LEN, meshtastic_FromRadio_size, &meshtastic_FromRadio_msg, &fromRadioScratch)); } diff --git a/src/mesh/StreamAPI.h b/src/mesh/StreamAPI.h index 45cbb231c..6e0364bc1 100644 --- a/src/mesh/StreamAPI.h +++ b/src/mesh/StreamAPI.h @@ -3,6 +3,7 @@ #include "PhoneAPI.h" #include "Stream.h" #include "concurrency/OSThread.h" +#include // A To/FromRadio packet + our 32 bit header #define MAX_STREAM_BUF_SIZE (MAX_TO_FROM_RADIO_SIZE + sizeof(uint32_t)) diff --git a/src/mesh/TypeConversions.cpp b/src/mesh/TypeConversions.cpp index 550f87021..5fc6b8a64 100644 --- a/src/mesh/TypeConversions.cpp +++ b/src/mesh/TypeConversions.cpp @@ -12,6 +12,7 @@ meshtastic_NodeInfo TypeConversions::ConvertToNodeInfo(const meshtastic_NodeInfo info.channel = lite->channel; info.via_mqtt = lite->via_mqtt; info.is_favorite = lite->is_favorite; + info.is_ignored = lite->is_ignored; if (lite->has_hops_away) { info.has_hops_away = true; diff --git a/src/mesh/aes-ccm.cpp b/src/mesh/aes-ccm.cpp index b9af14fdb..8bc2989bf 100644 --- a/src/mesh/aes-ccm.cpp +++ b/src/mesh/aes-ccm.cpp @@ -10,7 +10,7 @@ #include "aes-ccm.h" #if !MESHTASTIC_EXCLUDE_PKI -static inline void WPA_PUT_BE16(uint8_t *a, uint16_t val) +static void WPA_PUT_BE16(uint8_t *a, uint16_t val) { a[0] = val >> 8; a[1] = val & 0xff; diff --git a/src/mesh/api/ServerAPI.cpp b/src/mesh/api/ServerAPI.cpp index 42217428d..e28e4c815 100644 --- a/src/mesh/api/ServerAPI.cpp +++ b/src/mesh/api/ServerAPI.cpp @@ -5,7 +5,7 @@ template ServerAPI::ServerAPI(T &_client) : StreamAPI(&client), concurrency::OSThread("ServerAPI"), client(_client) { - LOG_INFO("Incoming API connection\n"); + LOG_INFO("Incoming API connection"); } template ServerAPI::~ServerAPI() @@ -30,7 +30,7 @@ template int32_t ServerAPI::runOnce() if (client.connected()) { return StreamAPI::runOncePart(); } else { - LOG_INFO("Client dropped connection, suspending API service\n"); + LOG_INFO("Client dropped connection, suspend API service"); enabled = false; // we no longer need to run return 0; } @@ -63,11 +63,11 @@ template int32_t APIServerPort::runOnce() // Reconnections are delayed by full wait time if (waitTime < 400) { waitTime *= 2; - LOG_INFO("Previous TCP connection still open, trying again in %dms\n", waitTime); + LOG_INFO("Previous TCP connection still open, try again in %dms", waitTime); return waitTime; } #endif - LOG_INFO("Force closing previous TCP connection\n"); + LOG_INFO("Force close previous TCP connection"); delete openAPI; } diff --git a/src/mesh/api/WiFiServerAPI.cpp b/src/mesh/api/WiFiServerAPI.cpp index ba31f76e5..5b63bc165 100644 --- a/src/mesh/api/WiFiServerAPI.cpp +++ b/src/mesh/api/WiFiServerAPI.cpp @@ -11,7 +11,7 @@ void initApiServer(int port) // Start API server on port 4403 if (!apiPort) { apiPort = new WiFiServerPort(port); - LOG_INFO("API server listening on TCP port %d\n", port); + LOG_INFO("API server listen on TCP port %d", port); apiPort->init(); } } @@ -22,7 +22,7 @@ void deInitApiServer() WiFiServerAPI::WiFiServerAPI(WiFiClient &_client) : ServerAPI(_client) { - LOG_INFO("Incoming wifi connection\n"); + LOG_INFO("Incoming wifi connection"); } WiFiServerPort::WiFiServerPort(int port) : APIServerPort(port) {} diff --git a/src/mesh/api/ethServerAPI.cpp b/src/mesh/api/ethServerAPI.cpp index 3badcdfde..a8701848a 100644 --- a/src/mesh/api/ethServerAPI.cpp +++ b/src/mesh/api/ethServerAPI.cpp @@ -12,14 +12,14 @@ void initApiServer(int port) // Start API server on port 4403 if (!apiPort) { apiPort = new ethServerPort(port); - LOG_INFO("API server listening on TCP port %d\n", port); + LOG_INFO("API server listening on TCP port %d", port); apiPort->init(); } } ethServerAPI::ethServerAPI(EthernetClient &_client) : ServerAPI(_client) { - LOG_INFO("Incoming ethernet connection\n"); + LOG_INFO("Incoming ethernet connection"); } ethServerPort::ethServerPort(int port) : APIServerPort(port) {} diff --git a/src/mesh/eth/ethClient.cpp b/src/mesh/eth/ethClient.cpp index 1c97f3bed..3b4d716f5 100644 --- a/src/mesh/eth/ethClient.cpp +++ b/src/mesh/eth/ethClient.cpp @@ -38,16 +38,16 @@ static int32_t reconnectETH() Ethernet.maintain(); if (!ethStartupComplete) { // Start web server - LOG_INFO("Starting Ethernet network services\n"); + LOG_INFO("Start Ethernet network services"); #ifndef DISABLE_NTP - LOG_INFO("Starting NTP time client\n"); + LOG_INFO("Start NTP time client"); timeClient.begin(); timeClient.setUpdateInterval(60 * 60); // Update once an hour #endif if (config.network.rsyslog_server[0]) { - LOG_INFO("Starting Syslog client\n"); + LOG_INFO("Start Syslog client"); // Defaults int serverPort = 514; const char *serverAddr = config.network.rsyslog_server; @@ -82,9 +82,9 @@ static int32_t reconnectETH() #ifndef DISABLE_NTP if (isEthernetAvailable() && (ntp_renew < millis())) { - LOG_INFO("Updating NTP time from %s\n", config.network.ntp_server); + LOG_INFO("Update NTP time from %s", config.network.ntp_server); if (timeClient.update()) { - LOG_DEBUG("NTP Request Success - Setting RTCQualityNTP if needed\n"); + LOG_DEBUG("NTP Request Success - Set RTCQualityNTP if needed"); struct timeval tv; tv.tv_sec = timeClient.getEpochTime(); @@ -94,7 +94,7 @@ static int32_t reconnectETH() ntp_renew = millis() + 43200 * 1000; // success, refresh every 12 hours } else { - LOG_ERROR("NTP Update failed\n"); + LOG_ERROR("NTP Update failed"); ntp_renew = millis() + 300 * 1000; // failure, retry every 5 minutes } } @@ -127,45 +127,45 @@ bool initEthernet() mac[0] &= 0xfe; // Make sure this is not a multicast MAC if (config.network.address_mode == meshtastic_Config_NetworkConfig_AddressMode_DHCP) { - LOG_INFO("starting Ethernet DHCP\n"); + LOG_INFO("Start Ethernet DHCP"); status = Ethernet.begin(mac); } else if (config.network.address_mode == meshtastic_Config_NetworkConfig_AddressMode_STATIC) { - LOG_INFO("starting Ethernet Static\n"); + LOG_INFO("Start Ethernet Static"); Ethernet.begin(mac, config.network.ipv4_config.ip, config.network.ipv4_config.dns, config.network.ipv4_config.gateway, config.network.ipv4_config.subnet); status = 1; } else { - LOG_INFO("Ethernet Disabled\n"); + LOG_INFO("Ethernet Disabled"); return false; } if (status == 0) { if (Ethernet.hardwareStatus() == EthernetNoHardware) { - LOG_ERROR("Ethernet shield was not found.\n"); + LOG_ERROR("Ethernet shield was not found"); return false; } else if (Ethernet.linkStatus() == LinkOFF) { - LOG_ERROR("Ethernet cable is not connected.\n"); + LOG_ERROR("Ethernet cable is not connected"); return false; } else { - LOG_ERROR("Unknown Ethernet error.\n"); + LOG_ERROR("Unknown Ethernet error"); return false; } } else { - LOG_INFO("Local IP %u.%u.%u.%u\n", Ethernet.localIP()[0], Ethernet.localIP()[1], Ethernet.localIP()[2], + LOG_INFO("Local IP %u.%u.%u.%u", Ethernet.localIP()[0], Ethernet.localIP()[1], Ethernet.localIP()[2], Ethernet.localIP()[3]); - LOG_INFO("Subnet Mask %u.%u.%u.%u\n", Ethernet.subnetMask()[0], Ethernet.subnetMask()[1], Ethernet.subnetMask()[2], + LOG_INFO("Subnet Mask %u.%u.%u.%u", Ethernet.subnetMask()[0], Ethernet.subnetMask()[1], Ethernet.subnetMask()[2], Ethernet.subnetMask()[3]); - LOG_INFO("Gateway IP %u.%u.%u.%u\n", Ethernet.gatewayIP()[0], Ethernet.gatewayIP()[1], Ethernet.gatewayIP()[2], + LOG_INFO("Gateway IP %u.%u.%u.%u", Ethernet.gatewayIP()[0], Ethernet.gatewayIP()[1], Ethernet.gatewayIP()[2], Ethernet.gatewayIP()[3]); - LOG_INFO("DNS Server IP %u.%u.%u.%u\n", Ethernet.dnsServerIP()[0], Ethernet.dnsServerIP()[1], - Ethernet.dnsServerIP()[2], Ethernet.dnsServerIP()[3]); + LOG_INFO("DNS Server IP %u.%u.%u.%u", Ethernet.dnsServerIP()[0], Ethernet.dnsServerIP()[1], Ethernet.dnsServerIP()[2], + Ethernet.dnsServerIP()[3]); } ethEvent = new Periodic("ethConnect", reconnectETH); return true; } else { - LOG_INFO("Not using Ethernet\n"); + LOG_INFO("Not using Ethernet"); return false; } } diff --git a/src/mesh/generated/meshtastic/admin.pb.h b/src/mesh/generated/meshtastic/admin.pb.h index d802eb3da..bbf633ef5 100644 --- a/src/mesh/generated/meshtastic/admin.pb.h +++ b/src/mesh/generated/meshtastic/admin.pb.h @@ -180,6 +180,10 @@ typedef struct _meshtastic_AdminMessage { meshtastic_DeviceUIConfig get_ui_config_response; /* Tell the node to store UI data persistently. */ meshtastic_DeviceUIConfig store_ui_config; + /* Set specified node-num to be ignored on the NodeDB on the device */ + uint32_t set_ignored_node; + /* Set specified node-num to be un-ignored on the NodeDB on the device */ + uint32_t remove_ignored_node; /* Begins an edit transaction for config, module config, owner, and channel settings changes This will delay the standard *implicit* save to the file system and subsequent reboot behavior until committed (commit_edit_settings) */ bool begin_edit_settings; @@ -279,6 +283,8 @@ extern "C" { #define meshtastic_AdminMessage_get_ui_config_request_tag 44 #define meshtastic_AdminMessage_get_ui_config_response_tag 45 #define meshtastic_AdminMessage_store_ui_config_tag 46 +#define meshtastic_AdminMessage_set_ignored_node_tag 47 +#define meshtastic_AdminMessage_remove_ignored_node_tag 48 #define meshtastic_AdminMessage_begin_edit_settings_tag 64 #define meshtastic_AdminMessage_commit_edit_settings_tag 65 #define meshtastic_AdminMessage_factory_reset_device_tag 94 @@ -329,6 +335,8 @@ X(a, STATIC, ONEOF, FIXED32, (payload_variant,set_time_only,set_time_only) X(a, STATIC, ONEOF, BOOL, (payload_variant,get_ui_config_request,get_ui_config_request), 44) \ X(a, STATIC, ONEOF, MESSAGE, (payload_variant,get_ui_config_response,get_ui_config_response), 45) \ X(a, STATIC, ONEOF, MESSAGE, (payload_variant,store_ui_config,store_ui_config), 46) \ +X(a, STATIC, ONEOF, UINT32, (payload_variant,set_ignored_node,set_ignored_node), 47) \ +X(a, STATIC, ONEOF, UINT32, (payload_variant,remove_ignored_node,remove_ignored_node), 48) \ X(a, STATIC, ONEOF, BOOL, (payload_variant,begin_edit_settings,begin_edit_settings), 64) \ X(a, STATIC, ONEOF, BOOL, (payload_variant,commit_edit_settings,commit_edit_settings), 65) \ X(a, STATIC, ONEOF, INT32, (payload_variant,factory_reset_device,factory_reset_device), 94) \ diff --git a/src/mesh/generated/meshtastic/config.pb.h b/src/mesh/generated/meshtastic/config.pb.h index 72a89fcdf..fab23ae34 100644 --- a/src/mesh/generated/meshtastic/config.pb.h +++ b/src/mesh/generated/meshtastic/config.pb.h @@ -4,6 +4,7 @@ #ifndef PB_MESHTASTIC_MESHTASTIC_CONFIG_PB_H_INCLUDED #define PB_MESHTASTIC_MESHTASTIC_CONFIG_PB_H_INCLUDED #include +#include "meshtastic/device_ui.pb.h" #if PB_PROTO_HEADER_VERSION != 40 #error Regenerate this file with the current version of nanopb generator. @@ -75,7 +76,10 @@ typedef enum _meshtastic_Config_DeviceConfig_RebroadcastMode { but takes it step further by also ignoring messages from nodenums not in the node's known list (NodeDB) */ meshtastic_Config_DeviceConfig_RebroadcastMode_KNOWN_ONLY = 3, /* Only permitted for SENSOR, TRACKER and TAK_TRACKER roles, this will inhibit all rebroadcasts, not unlike CLIENT_MUTE role. */ - meshtastic_Config_DeviceConfig_RebroadcastMode_NONE = 4 + meshtastic_Config_DeviceConfig_RebroadcastMode_NONE = 4, + /* Ignores packets from non-standard portnums such as: TAK, RangeTest, PaxCounter, etc. + Only rebroadcasts packets with standard portnums: NodeInfo, Text, Position, Telemetry, and Routing. */ + meshtastic_Config_DeviceConfig_RebroadcastMode_CORE_PORTNUMS_ONLY = 5 } meshtastic_Config_DeviceConfig_RebroadcastMode; /* Bit field of boolean configuration options, indicating which optional @@ -573,6 +577,7 @@ typedef struct _meshtastic_Config { meshtastic_Config_BluetoothConfig bluetooth; meshtastic_Config_SecurityConfig security; meshtastic_Config_SessionkeyConfig sessionkey; + meshtastic_DeviceUIConfig device_ui; } payload_variant; } meshtastic_Config; @@ -587,8 +592,8 @@ extern "C" { #define _meshtastic_Config_DeviceConfig_Role_ARRAYSIZE ((meshtastic_Config_DeviceConfig_Role)(meshtastic_Config_DeviceConfig_Role_TAK_TRACKER+1)) #define _meshtastic_Config_DeviceConfig_RebroadcastMode_MIN meshtastic_Config_DeviceConfig_RebroadcastMode_ALL -#define _meshtastic_Config_DeviceConfig_RebroadcastMode_MAX meshtastic_Config_DeviceConfig_RebroadcastMode_NONE -#define _meshtastic_Config_DeviceConfig_RebroadcastMode_ARRAYSIZE ((meshtastic_Config_DeviceConfig_RebroadcastMode)(meshtastic_Config_DeviceConfig_RebroadcastMode_NONE+1)) +#define _meshtastic_Config_DeviceConfig_RebroadcastMode_MAX meshtastic_Config_DeviceConfig_RebroadcastMode_CORE_PORTNUMS_ONLY +#define _meshtastic_Config_DeviceConfig_RebroadcastMode_ARRAYSIZE ((meshtastic_Config_DeviceConfig_RebroadcastMode)(meshtastic_Config_DeviceConfig_RebroadcastMode_CORE_PORTNUMS_ONLY+1)) #define _meshtastic_Config_PositionConfig_PositionFlags_MIN meshtastic_Config_PositionConfig_PositionFlags_UNSET #define _meshtastic_Config_PositionConfig_PositionFlags_MAX meshtastic_Config_PositionConfig_PositionFlags_SPEED @@ -776,6 +781,7 @@ extern "C" { #define meshtastic_Config_bluetooth_tag 7 #define meshtastic_Config_security_tag 8 #define meshtastic_Config_sessionkey_tag 9 +#define meshtastic_Config_device_ui_tag 10 /* Struct field encoding specification for nanopb */ #define meshtastic_Config_FIELDLIST(X, a) \ @@ -787,7 +793,8 @@ X(a, STATIC, ONEOF, MESSAGE, (payload_variant,display,payload_variant.disp X(a, STATIC, ONEOF, MESSAGE, (payload_variant,lora,payload_variant.lora), 6) \ X(a, STATIC, ONEOF, MESSAGE, (payload_variant,bluetooth,payload_variant.bluetooth), 7) \ X(a, STATIC, ONEOF, MESSAGE, (payload_variant,security,payload_variant.security), 8) \ -X(a, STATIC, ONEOF, MESSAGE, (payload_variant,sessionkey,payload_variant.sessionkey), 9) +X(a, STATIC, ONEOF, MESSAGE, (payload_variant,sessionkey,payload_variant.sessionkey), 9) \ +X(a, STATIC, ONEOF, MESSAGE, (payload_variant,device_ui,payload_variant.device_ui), 10) #define meshtastic_Config_CALLBACK NULL #define meshtastic_Config_DEFAULT NULL #define meshtastic_Config_payload_variant_device_MSGTYPE meshtastic_Config_DeviceConfig @@ -799,6 +806,7 @@ X(a, STATIC, ONEOF, MESSAGE, (payload_variant,sessionkey,payload_variant.s #define meshtastic_Config_payload_variant_bluetooth_MSGTYPE meshtastic_Config_BluetoothConfig #define meshtastic_Config_payload_variant_security_MSGTYPE meshtastic_Config_SecurityConfig #define meshtastic_Config_payload_variant_sessionkey_MSGTYPE meshtastic_Config_SessionkeyConfig +#define meshtastic_Config_payload_variant_device_ui_MSGTYPE meshtastic_DeviceUIConfig #define meshtastic_Config_DeviceConfig_FIELDLIST(X, a) \ X(a, STATIC, SINGULAR, UENUM, role, 1) \ diff --git a/src/mesh/generated/meshtastic/device_ui.pb.h b/src/mesh/generated/meshtastic/device_ui.pb.h index 014be465d..107aa8846 100644 --- a/src/mesh/generated/meshtastic/device_ui.pb.h +++ b/src/mesh/generated/meshtastic/device_ui.pb.h @@ -32,7 +32,29 @@ typedef enum _meshtastic_Language { /* Portuguese */ meshtastic_Language_PORTUGUESE = 4, /* Spanish */ - meshtastic_Language_SPANISH = 5 + meshtastic_Language_SPANISH = 5, + /* Swedish */ + meshtastic_Language_SWEDISH = 6, + /* Finnish */ + meshtastic_Language_FINNISH = 7, + /* Polish */ + meshtastic_Language_POLISH = 8, + /* Turkish */ + meshtastic_Language_TURKISH = 9, + /* Serbian */ + meshtastic_Language_SERBIAN = 10, + /* Russian */ + meshtastic_Language_RUSSIAN = 11, + /* Dutch */ + meshtastic_Language_DUTCH = 12, + /* Greek */ + meshtastic_Language_GREEK = 13, + /* Norwegian */ + meshtastic_Language_NORWEGIAN = 14, + /* Simplified Chinese (experimental) */ + meshtastic_Language_SIMPLIFIED_CHINESE = 30, + /* Traditional Chinese (experimental) */ + meshtastic_Language_TRADITIONAL_CHINESE = 31 } meshtastic_Language; /* Struct definitions */ @@ -64,17 +86,24 @@ typedef struct _meshtastic_NodeHighlight { char node_name[16]; } meshtastic_NodeHighlight; +typedef PB_BYTES_ARRAY_T(16) meshtastic_DeviceUIConfig_calibration_data_t; typedef struct _meshtastic_DeviceUIConfig { + /* A version integer used to invalidate saved files when we make incompatible changes. */ + uint32_t version; /* TFT display brightness 1..255 */ uint8_t screen_brightness; /* Screen timeout 0..900 */ uint16_t screen_timeout; - /* Screen lock enabled */ + /* Screen/Settings lock enabled */ bool screen_lock; + bool settings_lock; + uint32_t pin_code; /* Color theme */ meshtastic_Theme theme; - /* Audible message alert enabled */ + /* Audible message, banner and ring tone */ bool alert_enabled; + bool banner_enabled; + uint8_t ring_tone_id; /* Localization */ meshtastic_Language language; /* Node list filter */ @@ -83,6 +112,8 @@ typedef struct _meshtastic_DeviceUIConfig { /* Node list highlightening */ bool has_node_highlight; meshtastic_NodeHighlight node_highlight; + /* 8 integers for screen calibration data */ + meshtastic_DeviceUIConfig_calibration_data_t calibration_data; } meshtastic_DeviceUIConfig; @@ -96,8 +127,8 @@ extern "C" { #define _meshtastic_Theme_ARRAYSIZE ((meshtastic_Theme)(meshtastic_Theme_RED+1)) #define _meshtastic_Language_MIN meshtastic_Language_ENGLISH -#define _meshtastic_Language_MAX meshtastic_Language_SPANISH -#define _meshtastic_Language_ARRAYSIZE ((meshtastic_Language)(meshtastic_Language_SPANISH+1)) +#define _meshtastic_Language_MAX meshtastic_Language_TRADITIONAL_CHINESE +#define _meshtastic_Language_ARRAYSIZE ((meshtastic_Language)(meshtastic_Language_TRADITIONAL_CHINESE+1)) #define meshtastic_DeviceUIConfig_theme_ENUMTYPE meshtastic_Theme #define meshtastic_DeviceUIConfig_language_ENUMTYPE meshtastic_Language @@ -106,10 +137,10 @@ extern "C" { /* Initializer values for message structs */ -#define meshtastic_DeviceUIConfig_init_default {0, 0, 0, _meshtastic_Theme_MIN, 0, _meshtastic_Language_MIN, false, meshtastic_NodeFilter_init_default, false, meshtastic_NodeHighlight_init_default} +#define meshtastic_DeviceUIConfig_init_default {0, 0, 0, 0, 0, 0, _meshtastic_Theme_MIN, 0, 0, 0, _meshtastic_Language_MIN, false, meshtastic_NodeFilter_init_default, false, meshtastic_NodeHighlight_init_default, {0, {0}}} #define meshtastic_NodeFilter_init_default {0, 0, 0, 0, 0, ""} #define meshtastic_NodeHighlight_init_default {0, 0, 0, 0, ""} -#define meshtastic_DeviceUIConfig_init_zero {0, 0, 0, _meshtastic_Theme_MIN, 0, _meshtastic_Language_MIN, false, meshtastic_NodeFilter_init_zero, false, meshtastic_NodeHighlight_init_zero} +#define meshtastic_DeviceUIConfig_init_zero {0, 0, 0, 0, 0, 0, _meshtastic_Theme_MIN, 0, 0, 0, _meshtastic_Language_MIN, false, meshtastic_NodeFilter_init_zero, false, meshtastic_NodeHighlight_init_zero, {0, {0}}} #define meshtastic_NodeFilter_init_zero {0, 0, 0, 0, 0, ""} #define meshtastic_NodeHighlight_init_zero {0, 0, 0, 0, ""} @@ -125,25 +156,37 @@ extern "C" { #define meshtastic_NodeHighlight_telemetry_switch_tag 3 #define meshtastic_NodeHighlight_iaq_switch_tag 4 #define meshtastic_NodeHighlight_node_name_tag 5 -#define meshtastic_DeviceUIConfig_screen_brightness_tag 1 -#define meshtastic_DeviceUIConfig_screen_timeout_tag 2 -#define meshtastic_DeviceUIConfig_screen_lock_tag 3 -#define meshtastic_DeviceUIConfig_theme_tag 4 -#define meshtastic_DeviceUIConfig_alert_enabled_tag 5 -#define meshtastic_DeviceUIConfig_language_tag 6 -#define meshtastic_DeviceUIConfig_node_filter_tag 7 -#define meshtastic_DeviceUIConfig_node_highlight_tag 8 +#define meshtastic_DeviceUIConfig_version_tag 1 +#define meshtastic_DeviceUIConfig_screen_brightness_tag 2 +#define meshtastic_DeviceUIConfig_screen_timeout_tag 3 +#define meshtastic_DeviceUIConfig_screen_lock_tag 4 +#define meshtastic_DeviceUIConfig_settings_lock_tag 5 +#define meshtastic_DeviceUIConfig_pin_code_tag 6 +#define meshtastic_DeviceUIConfig_theme_tag 7 +#define meshtastic_DeviceUIConfig_alert_enabled_tag 8 +#define meshtastic_DeviceUIConfig_banner_enabled_tag 9 +#define meshtastic_DeviceUIConfig_ring_tone_id_tag 10 +#define meshtastic_DeviceUIConfig_language_tag 11 +#define meshtastic_DeviceUIConfig_node_filter_tag 12 +#define meshtastic_DeviceUIConfig_node_highlight_tag 13 +#define meshtastic_DeviceUIConfig_calibration_data_tag 14 /* Struct field encoding specification for nanopb */ #define meshtastic_DeviceUIConfig_FIELDLIST(X, a) \ -X(a, STATIC, SINGULAR, UINT32, screen_brightness, 1) \ -X(a, STATIC, SINGULAR, UINT32, screen_timeout, 2) \ -X(a, STATIC, SINGULAR, BOOL, screen_lock, 3) \ -X(a, STATIC, SINGULAR, UENUM, theme, 4) \ -X(a, STATIC, SINGULAR, BOOL, alert_enabled, 5) \ -X(a, STATIC, SINGULAR, UENUM, language, 6) \ -X(a, STATIC, OPTIONAL, MESSAGE, node_filter, 7) \ -X(a, STATIC, OPTIONAL, MESSAGE, node_highlight, 8) +X(a, STATIC, SINGULAR, UINT32, version, 1) \ +X(a, STATIC, SINGULAR, UINT32, screen_brightness, 2) \ +X(a, STATIC, SINGULAR, UINT32, screen_timeout, 3) \ +X(a, STATIC, SINGULAR, BOOL, screen_lock, 4) \ +X(a, STATIC, SINGULAR, BOOL, settings_lock, 5) \ +X(a, STATIC, SINGULAR, UINT32, pin_code, 6) \ +X(a, STATIC, SINGULAR, UENUM, theme, 7) \ +X(a, STATIC, SINGULAR, BOOL, alert_enabled, 8) \ +X(a, STATIC, SINGULAR, BOOL, banner_enabled, 9) \ +X(a, STATIC, SINGULAR, UINT32, ring_tone_id, 10) \ +X(a, STATIC, SINGULAR, UENUM, language, 11) \ +X(a, STATIC, OPTIONAL, MESSAGE, node_filter, 12) \ +X(a, STATIC, OPTIONAL, MESSAGE, node_highlight, 13) \ +X(a, STATIC, SINGULAR, BYTES, calibration_data, 14) #define meshtastic_DeviceUIConfig_CALLBACK NULL #define meshtastic_DeviceUIConfig_DEFAULT NULL #define meshtastic_DeviceUIConfig_node_filter_MSGTYPE meshtastic_NodeFilter @@ -179,7 +222,7 @@ extern const pb_msgdesc_t meshtastic_NodeHighlight_msg; /* Maximum encoded size of messages (where known) */ #define MESHTASTIC_MESHTASTIC_DEVICE_UI_PB_H_MAX_SIZE meshtastic_DeviceUIConfig_size -#define meshtastic_DeviceUIConfig_size 80 +#define meshtastic_DeviceUIConfig_size 117 #define meshtastic_NodeFilter_size 36 #define meshtastic_NodeHighlight_size 25 diff --git a/src/mesh/generated/meshtastic/deviceonly.pb.cpp b/src/mesh/generated/meshtastic/deviceonly.pb.cpp index 135634762..92853f00d 100644 --- a/src/mesh/generated/meshtastic/deviceonly.pb.cpp +++ b/src/mesh/generated/meshtastic/deviceonly.pb.cpp @@ -21,9 +21,4 @@ PB_BIND(meshtastic_DeviceState, meshtastic_DeviceState, 2) PB_BIND(meshtastic_ChannelFile, meshtastic_ChannelFile, 2) -PB_BIND(meshtastic_OEMStore, meshtastic_OEMStore, 2) - - - - diff --git a/src/mesh/generated/meshtastic/deviceonly.pb.h b/src/mesh/generated/meshtastic/deviceonly.pb.h index 2aa8fda8e..e52a914e0 100644 --- a/src/mesh/generated/meshtastic/deviceonly.pb.h +++ b/src/mesh/generated/meshtastic/deviceonly.pb.h @@ -6,7 +6,6 @@ #include #include #include "meshtastic/channel.pb.h" -#include "meshtastic/localonly.pb.h" #include "meshtastic/mesh.pb.h" #include "meshtastic/telemetry.pb.h" #include "meshtastic/config.pb.h" @@ -15,17 +14,6 @@ #error Regenerate this file with the current version of nanopb generator. #endif -/* Enum definitions */ -/* Font sizes for the device screen */ -typedef enum _meshtastic_ScreenFonts { - /* TODO: REPLACE */ - meshtastic_ScreenFonts_FONT_SMALL = 0, - /* TODO: REPLACE */ - meshtastic_ScreenFonts_FONT_MEDIUM = 1, - /* TODO: REPLACE */ - meshtastic_ScreenFonts_FONT_LARGE = 2 -} meshtastic_ScreenFonts; - /* Struct definitions */ /* Position with static location information only for NodeDBLite */ typedef struct _meshtastic_PositionLite { @@ -93,12 +81,17 @@ typedef struct _meshtastic_NodeInfoLite { uint8_t channel; /* True if we witnessed the node over MQTT instead of LoRA transport */ bool via_mqtt; - /* Number of hops away from us this node is (0 if adjacent) */ + /* Number of hops away from us this node is (0 if direct neighbor) */ bool has_hops_away; uint8_t hops_away; /* True if node is in our favorites list Persists between NodeDB internal clean ups */ bool is_favorite; + /* True if node is in our ignored list + Persists between NodeDB internal clean ups */ + bool is_ignored; + /* Last byte of the node number of the node that should be used as the next hop to reach this node. */ + uint8_t next_hop; } meshtastic_NodeInfoLite; /* This message is never sent over the wire, but it is used for serializing DB @@ -154,65 +147,22 @@ typedef struct _meshtastic_ChannelFile { uint32_t version; } meshtastic_ChannelFile; -typedef PB_BYTES_ARRAY_T(2048) meshtastic_OEMStore_oem_icon_bits_t; -typedef PB_BYTES_ARRAY_T(32) meshtastic_OEMStore_oem_aes_key_t; -/* This can be used for customizing the firmware distribution. If populated, - show a secondary bootup screen with custom logo and text for 2.5 seconds. */ -typedef struct _meshtastic_OEMStore { - /* The Logo width in Px */ - uint32_t oem_icon_width; - /* The Logo height in Px */ - uint32_t oem_icon_height; - /* The Logo in XBM bytechar format */ - meshtastic_OEMStore_oem_icon_bits_t oem_icon_bits; - /* Use this font for the OEM text. */ - meshtastic_ScreenFonts oem_font; - /* Use this font for the OEM text. */ - char oem_text[40]; - /* The default device encryption key, 16 or 32 byte */ - meshtastic_OEMStore_oem_aes_key_t oem_aes_key; - /* A Preset LocalConfig to apply during factory reset */ - bool has_oem_local_config; - meshtastic_LocalConfig oem_local_config; - /* A Preset LocalModuleConfig to apply during factory reset */ - bool has_oem_local_module_config; - meshtastic_LocalModuleConfig oem_local_module_config; -} meshtastic_OEMStore; - #ifdef __cplusplus extern "C" { #endif -/* Helper constants for enums */ -#define _meshtastic_ScreenFonts_MIN meshtastic_ScreenFonts_FONT_SMALL -#define _meshtastic_ScreenFonts_MAX meshtastic_ScreenFonts_FONT_LARGE -#define _meshtastic_ScreenFonts_ARRAYSIZE ((meshtastic_ScreenFonts)(meshtastic_ScreenFonts_FONT_LARGE+1)) - -#define meshtastic_PositionLite_location_source_ENUMTYPE meshtastic_Position_LocSource - -#define meshtastic_UserLite_hw_model_ENUMTYPE meshtastic_HardwareModel -#define meshtastic_UserLite_role_ENUMTYPE meshtastic_Config_DeviceConfig_Role - - - - -#define meshtastic_OEMStore_oem_font_ENUMTYPE meshtastic_ScreenFonts - - /* Initializer values for message structs */ #define meshtastic_PositionLite_init_default {0, 0, 0, 0, _meshtastic_Position_LocSource_MIN} #define meshtastic_UserLite_init_default {{0}, "", "", _meshtastic_HardwareModel_MIN, 0, _meshtastic_Config_DeviceConfig_Role_MIN, {0, {0}}} -#define meshtastic_NodeInfoLite_init_default {0, false, meshtastic_UserLite_init_default, false, meshtastic_PositionLite_init_default, 0, 0, false, meshtastic_DeviceMetrics_init_default, 0, 0, false, 0, 0} +#define meshtastic_NodeInfoLite_init_default {0, false, meshtastic_UserLite_init_default, false, meshtastic_PositionLite_init_default, 0, 0, false, meshtastic_DeviceMetrics_init_default, 0, 0, false, 0, 0, 0, 0} #define meshtastic_DeviceState_init_default {false, meshtastic_MyNodeInfo_init_default, false, meshtastic_User_init_default, 0, {meshtastic_MeshPacket_init_default}, false, meshtastic_MeshPacket_init_default, 0, 0, 0, false, meshtastic_MeshPacket_init_default, 0, {meshtastic_NodeRemoteHardwarePin_init_default, meshtastic_NodeRemoteHardwarePin_init_default, meshtastic_NodeRemoteHardwarePin_init_default, meshtastic_NodeRemoteHardwarePin_init_default, meshtastic_NodeRemoteHardwarePin_init_default, meshtastic_NodeRemoteHardwarePin_init_default, meshtastic_NodeRemoteHardwarePin_init_default, meshtastic_NodeRemoteHardwarePin_init_default, meshtastic_NodeRemoteHardwarePin_init_default, meshtastic_NodeRemoteHardwarePin_init_default, meshtastic_NodeRemoteHardwarePin_init_default, meshtastic_NodeRemoteHardwarePin_init_default}, {0}} #define meshtastic_ChannelFile_init_default {0, {meshtastic_Channel_init_default, meshtastic_Channel_init_default, meshtastic_Channel_init_default, meshtastic_Channel_init_default, meshtastic_Channel_init_default, meshtastic_Channel_init_default, meshtastic_Channel_init_default, meshtastic_Channel_init_default}, 0} -#define meshtastic_OEMStore_init_default {0, 0, {0, {0}}, _meshtastic_ScreenFonts_MIN, "", {0, {0}}, false, meshtastic_LocalConfig_init_default, false, meshtastic_LocalModuleConfig_init_default} #define meshtastic_PositionLite_init_zero {0, 0, 0, 0, _meshtastic_Position_LocSource_MIN} #define meshtastic_UserLite_init_zero {{0}, "", "", _meshtastic_HardwareModel_MIN, 0, _meshtastic_Config_DeviceConfig_Role_MIN, {0, {0}}} -#define meshtastic_NodeInfoLite_init_zero {0, false, meshtastic_UserLite_init_zero, false, meshtastic_PositionLite_init_zero, 0, 0, false, meshtastic_DeviceMetrics_init_zero, 0, 0, false, 0, 0} +#define meshtastic_NodeInfoLite_init_zero {0, false, meshtastic_UserLite_init_zero, false, meshtastic_PositionLite_init_zero, 0, 0, false, meshtastic_DeviceMetrics_init_zero, 0, 0, false, 0, 0, 0, 0} #define meshtastic_DeviceState_init_zero {false, meshtastic_MyNodeInfo_init_zero, false, meshtastic_User_init_zero, 0, {meshtastic_MeshPacket_init_zero}, false, meshtastic_MeshPacket_init_zero, 0, 0, 0, false, meshtastic_MeshPacket_init_zero, 0, {meshtastic_NodeRemoteHardwarePin_init_zero, meshtastic_NodeRemoteHardwarePin_init_zero, meshtastic_NodeRemoteHardwarePin_init_zero, meshtastic_NodeRemoteHardwarePin_init_zero, meshtastic_NodeRemoteHardwarePin_init_zero, meshtastic_NodeRemoteHardwarePin_init_zero, meshtastic_NodeRemoteHardwarePin_init_zero, meshtastic_NodeRemoteHardwarePin_init_zero, meshtastic_NodeRemoteHardwarePin_init_zero, meshtastic_NodeRemoteHardwarePin_init_zero, meshtastic_NodeRemoteHardwarePin_init_zero, meshtastic_NodeRemoteHardwarePin_init_zero}, {0}} #define meshtastic_ChannelFile_init_zero {0, {meshtastic_Channel_init_zero, meshtastic_Channel_init_zero, meshtastic_Channel_init_zero, meshtastic_Channel_init_zero, meshtastic_Channel_init_zero, meshtastic_Channel_init_zero, meshtastic_Channel_init_zero, meshtastic_Channel_init_zero}, 0} -#define meshtastic_OEMStore_init_zero {0, 0, {0, {0}}, _meshtastic_ScreenFonts_MIN, "", {0, {0}}, false, meshtastic_LocalConfig_init_zero, false, meshtastic_LocalModuleConfig_init_zero} /* Field tags (for use in manual encoding/decoding) */ #define meshtastic_PositionLite_latitude_i_tag 1 @@ -237,6 +187,8 @@ extern "C" { #define meshtastic_NodeInfoLite_via_mqtt_tag 8 #define meshtastic_NodeInfoLite_hops_away_tag 9 #define meshtastic_NodeInfoLite_is_favorite_tag 10 +#define meshtastic_NodeInfoLite_is_ignored_tag 11 +#define meshtastic_NodeInfoLite_next_hop_tag 12 #define meshtastic_DeviceState_my_node_tag 2 #define meshtastic_DeviceState_owner_tag 3 #define meshtastic_DeviceState_receive_queue_tag 5 @@ -249,14 +201,6 @@ extern "C" { #define meshtastic_DeviceState_node_db_lite_tag 14 #define meshtastic_ChannelFile_channels_tag 1 #define meshtastic_ChannelFile_version_tag 2 -#define meshtastic_OEMStore_oem_icon_width_tag 1 -#define meshtastic_OEMStore_oem_icon_height_tag 2 -#define meshtastic_OEMStore_oem_icon_bits_tag 3 -#define meshtastic_OEMStore_oem_font_tag 4 -#define meshtastic_OEMStore_oem_text_tag 5 -#define meshtastic_OEMStore_oem_aes_key_tag 6 -#define meshtastic_OEMStore_oem_local_config_tag 7 -#define meshtastic_OEMStore_oem_local_module_config_tag 8 /* Struct field encoding specification for nanopb */ #define meshtastic_PositionLite_FIELDLIST(X, a) \ @@ -289,7 +233,9 @@ X(a, STATIC, OPTIONAL, MESSAGE, device_metrics, 6) \ X(a, STATIC, SINGULAR, UINT32, channel, 7) \ X(a, STATIC, SINGULAR, BOOL, via_mqtt, 8) \ X(a, STATIC, OPTIONAL, UINT32, hops_away, 9) \ -X(a, STATIC, SINGULAR, BOOL, is_favorite, 10) +X(a, STATIC, SINGULAR, BOOL, is_favorite, 10) \ +X(a, STATIC, SINGULAR, BOOL, is_ignored, 11) \ +X(a, STATIC, SINGULAR, UINT32, next_hop, 12) #define meshtastic_NodeInfoLite_CALLBACK NULL #define meshtastic_NodeInfoLite_DEFAULT NULL #define meshtastic_NodeInfoLite_user_MSGTYPE meshtastic_UserLite @@ -325,26 +271,11 @@ X(a, STATIC, SINGULAR, UINT32, version, 2) #define meshtastic_ChannelFile_DEFAULT NULL #define meshtastic_ChannelFile_channels_MSGTYPE meshtastic_Channel -#define meshtastic_OEMStore_FIELDLIST(X, a) \ -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, BYTES, oem_aes_key, 6) \ -X(a, STATIC, OPTIONAL, MESSAGE, oem_local_config, 7) \ -X(a, STATIC, OPTIONAL, MESSAGE, oem_local_module_config, 8) -#define meshtastic_OEMStore_CALLBACK NULL -#define meshtastic_OEMStore_DEFAULT NULL -#define meshtastic_OEMStore_oem_local_config_MSGTYPE meshtastic_LocalConfig -#define meshtastic_OEMStore_oem_local_module_config_MSGTYPE meshtastic_LocalModuleConfig - extern const pb_msgdesc_t meshtastic_PositionLite_msg; extern const pb_msgdesc_t meshtastic_UserLite_msg; extern const pb_msgdesc_t meshtastic_NodeInfoLite_msg; extern const pb_msgdesc_t meshtastic_DeviceState_msg; extern const pb_msgdesc_t meshtastic_ChannelFile_msg; -extern const pb_msgdesc_t meshtastic_OEMStore_msg; /* Defines for backwards compatibility with code written before nanopb-0.4.0 */ #define meshtastic_PositionLite_fields &meshtastic_PositionLite_msg @@ -352,14 +283,12 @@ extern const pb_msgdesc_t meshtastic_OEMStore_msg; #define meshtastic_NodeInfoLite_fields &meshtastic_NodeInfoLite_msg #define meshtastic_DeviceState_fields &meshtastic_DeviceState_msg #define meshtastic_ChannelFile_fields &meshtastic_ChannelFile_msg -#define meshtastic_OEMStore_fields &meshtastic_OEMStore_msg /* Maximum encoded size of messages (where known) */ /* meshtastic_DeviceState_size depends on runtime parameters */ -#define MESHTASTIC_MESHTASTIC_DEVICEONLY_PB_H_MAX_SIZE meshtastic_OEMStore_size +#define MESHTASTIC_MESHTASTIC_DEVICEONLY_PB_H_MAX_SIZE meshtastic_ChannelFile_size #define meshtastic_ChannelFile_size 718 -#define meshtastic_NodeInfoLite_size 183 -#define meshtastic_OEMStore_size 3578 +#define meshtastic_NodeInfoLite_size 188 #define meshtastic_PositionLite_size 28 #define meshtastic_UserLite_size 96 diff --git a/src/mesh/generated/meshtastic/localonly.pb.h b/src/mesh/generated/meshtastic/localonly.pb.h index 6409aef74..8f92b2a77 100644 --- a/src/mesh/generated/meshtastic/localonly.pb.h +++ b/src/mesh/generated/meshtastic/localonly.pb.h @@ -188,7 +188,7 @@ extern const pb_msgdesc_t meshtastic_LocalModuleConfig_msg; /* Maximum encoded size of messages (where known) */ #define MESHTASTIC_MESHTASTIC_LOCALONLY_PB_H_MAX_SIZE meshtastic_LocalConfig_size #define meshtastic_LocalConfig_size 735 -#define meshtastic_LocalModuleConfig_size 697 +#define meshtastic_LocalModuleConfig_size 699 #ifdef __cplusplus } /* extern "C" */ diff --git a/src/mesh/generated/meshtastic/mesh.pb.cpp b/src/mesh/generated/meshtastic/mesh.pb.cpp index a0c1e2e73..a9f42f979 100644 --- a/src/mesh/generated/meshtastic/mesh.pb.cpp +++ b/src/mesh/generated/meshtastic/mesh.pb.cpp @@ -97,6 +97,8 @@ PB_BIND(meshtastic_ChunkedPayloadResponse, meshtastic_ChunkedPayloadResponse, AU + + diff --git a/src/mesh/generated/meshtastic/mesh.pb.h b/src/mesh/generated/meshtastic/mesh.pb.h index 313719d9b..a173adb4d 100644 --- a/src/mesh/generated/meshtastic/mesh.pb.h +++ b/src/mesh/generated/meshtastic/mesh.pb.h @@ -212,6 +212,9 @@ typedef enum _meshtastic_HardwareModel { meshtastic_HardwareModel_MS24SF1 = 82, /* Lilygo TLora-C6 with the new ESP32-C6 MCU */ meshtastic_HardwareModel_TLORA_C6 = 83, + /* WisMesh Tap + RAK-4631 w/ TFT in injection modled case */ + meshtastic_HardwareModel_WISMESH_TAP = 84, /* ------------------------------------------------------------------------------------------------------------------------------------------ Reserved ID For developing private Ports. These will show up in live traffic sparsely, so we can use a high number. Keep it within 8 bits. ------------------------------------------------------------------------------------------------------------------------------------------ */ @@ -226,7 +229,7 @@ typedef enum _meshtastic_Constants { /* From mesh.options note: this payload length is ONLY the bytes that are sent inside of the Data protobuf (excluding protobuf overhead). The 16 byte header is outside of this envelope */ - meshtastic_Constants_DATA_PAYLOAD_LEN = 237 + meshtastic_Constants_DATA_PAYLOAD_LEN = 233 } meshtastic_Constants; /* Error codes for critical errors @@ -270,6 +273,40 @@ typedef enum _meshtastic_CriticalErrorCode { meshtastic_CriticalErrorCode_FLASH_CORRUPTION_UNRECOVERABLE = 13 } meshtastic_CriticalErrorCode; +/* Enum for modules excluded from a device's configuration. + Each value represents a ModuleConfigType that can be toggled as excluded + by setting its corresponding bit in the `excluded_modules` bitmask field. */ +typedef enum _meshtastic_ExcludedModules { + /* Default value of 0 indicates no modules are excluded. */ + meshtastic_ExcludedModules_EXCLUDED_NONE = 0, + /* MQTT module */ + meshtastic_ExcludedModules_MQTT_CONFIG = 1, + /* Serial module */ + meshtastic_ExcludedModules_SERIAL_CONFIG = 2, + /* External Notification module */ + meshtastic_ExcludedModules_EXTNOTIF_CONFIG = 4, + /* Store and Forward module */ + meshtastic_ExcludedModules_STOREFORWARD_CONFIG = 8, + /* Range Test module */ + meshtastic_ExcludedModules_RANGETEST_CONFIG = 16, + /* Telemetry module */ + meshtastic_ExcludedModules_TELEMETRY_CONFIG = 32, + /* Canned Message module */ + meshtastic_ExcludedModules_CANNEDMSG_CONFIG = 64, + /* Audio module */ + meshtastic_ExcludedModules_AUDIO_CONFIG = 128, + /* Remote Hardware module */ + meshtastic_ExcludedModules_REMOTEHARDWARE_CONFIG = 256, + /* Neighbor Info module */ + meshtastic_ExcludedModules_NEIGHBORINFO_CONFIG = 512, + /* Ambient Lighting module */ + meshtastic_ExcludedModules_AMBIENTLIGHTING_CONFIG = 1024, + /* Detection Sensor module */ + meshtastic_ExcludedModules_DETECTIONSENSOR_CONFIG = 2048, + /* Paxcounter module */ + meshtastic_ExcludedModules_PAXCOUNTER_CONFIG = 4096 +} meshtastic_ExcludedModules; + /* How the location was acquired: manual, onboard GPS, external (EUD) GPS */ typedef enum _meshtastic_Position_LocSource { /* TODO: REPLACE */ @@ -408,7 +445,7 @@ typedef enum _meshtastic_LogRecord_Level { } meshtastic_LogRecord_Level; /* Struct definitions */ -/* a gps position */ +/* A GPS Position */ typedef struct _meshtastic_Position { /* The new preferred location encoding, multiply by 1e-7 to get degrees in floating point */ @@ -566,7 +603,7 @@ typedef struct _meshtastic_Routing { }; } meshtastic_Routing; -typedef PB_BYTES_ARRAY_T(237) meshtastic_Data_payload_t; +typedef PB_BYTES_ARRAY_T(233) meshtastic_Data_payload_t; /* (Formerly called SubPacket) The payload portion fo a packet, this is the actual bytes that are sent inside a radio packet (because from/to are broken out by the comms library) */ @@ -686,7 +723,7 @@ typedef struct _meshtastic_MeshPacket { Set during reception to indicate the SNR of this packet. Used to collect statistics on current link quality. */ float rx_snr; - /* If unset treated as zero (no forwarding, send to adjacent nodes only) + /* If unset treated as zero (no forwarding, send to direct neighbor nodes only) if 1, allow hopping through one node, etc... For our usecase real world topologies probably have a max of about 3. This field is normally placed into a few of bits in the header. */ @@ -717,6 +754,12 @@ typedef struct _meshtastic_MeshPacket { meshtastic_MeshPacket_public_key_t public_key; /* Indicates whether the packet was en/decrypted using PKI */ bool pki_encrypted; + /* Last byte of the node number of the node that should be used as the next hop in routing. + Set by the firmware internally, clients are not supposed to set this. */ + uint8_t next_hop; + /* Last byte of the node number of the node that will relay/relayed this packet. + Set by the firmware internally, clients are not supposed to set this. */ + uint8_t relay_node; } meshtastic_MeshPacket; /* The bluetooth to device link: @@ -757,14 +800,18 @@ typedef struct _meshtastic_NodeInfo { uint8_t channel; /* True if we witnessed the node over MQTT instead of LoRA transport */ bool via_mqtt; - /* Number of hops away from us this node is (0 if adjacent) */ + /* Number of hops away from us this node is (0 if direct neighbor) */ bool has_hops_away; uint8_t hops_away; /* True if node is in our favorites list Persists between NodeDB internal clean ups */ bool is_favorite; + /* True if node is in our ignored list + Persists between NodeDB internal clean ups */ + bool is_ignored; } meshtastic_NodeInfo; +typedef PB_BYTES_ARRAY_T(16) meshtastic_MyNodeInfo_device_id_t; /* Unique local debugging info for this node Note: we don't include position or the user info, because that will come in the Sent to the phone in response to WantNodes. */ @@ -778,6 +825,10 @@ typedef struct _meshtastic_MyNodeInfo { /* The minimum app version that can talk to this device. Phone/PC apps should compare this to their build number and if too low tell the user they must update their app */ uint32_t min_app_version; + /* Unique hardware identifier for this device */ + meshtastic_MyNodeInfo_device_id_t device_id; + /* The PlatformIO environment used to build this firmware */ + char pio_env[40]; } meshtastic_MyNodeInfo; /* Debug output from the device. @@ -831,7 +882,7 @@ typedef struct _meshtastic_FileInfo { uint32_t size_bytes; } meshtastic_FileInfo; -typedef PB_BYTES_ARRAY_T(237) meshtastic_Compressed_data_t; +typedef PB_BYTES_ARRAY_T(233) meshtastic_Compressed_data_t; /* Compressed message payload */ typedef struct _meshtastic_Compressed { /* PortNum to determine the how to handle the compressed payload. */ @@ -891,6 +942,9 @@ typedef struct _meshtastic_DeviceMetadata { bool hasRemoteHardware; /* Has PKC capabilities */ bool hasPKC; + /* Bit field of boolean for excluded modules + (bitwise OR of ExcludedModules) */ + uint32_t excluded_modules; } meshtastic_DeviceMetadata; /* Packets from the radio to the phone will appear on the fromRadio characteristic. @@ -1039,6 +1093,10 @@ extern "C" { #define _meshtastic_CriticalErrorCode_MAX meshtastic_CriticalErrorCode_FLASH_CORRUPTION_UNRECOVERABLE #define _meshtastic_CriticalErrorCode_ARRAYSIZE ((meshtastic_CriticalErrorCode)(meshtastic_CriticalErrorCode_FLASH_CORRUPTION_UNRECOVERABLE+1)) +#define _meshtastic_ExcludedModules_MIN meshtastic_ExcludedModules_EXCLUDED_NONE +#define _meshtastic_ExcludedModules_MAX meshtastic_ExcludedModules_PAXCOUNTER_CONFIG +#define _meshtastic_ExcludedModules_ARRAYSIZE ((meshtastic_ExcludedModules)(meshtastic_ExcludedModules_PAXCOUNTER_CONFIG+1)) + #define _meshtastic_Position_LocSource_MIN meshtastic_Position_LocSource_LOC_UNSET #define _meshtastic_Position_LocSource_MAX meshtastic_Position_LocSource_LOC_EXTERNAL #define _meshtastic_Position_LocSource_ARRAYSIZE ((meshtastic_Position_LocSource)(meshtastic_Position_LocSource_LOC_EXTERNAL+1)) @@ -1110,9 +1168,9 @@ extern "C" { #define meshtastic_Data_init_default {_meshtastic_PortNum_MIN, {0, {0}}, 0, 0, 0, 0, 0, 0, false, 0} #define meshtastic_Waypoint_init_default {0, false, 0, false, 0, 0, 0, "", "", 0} #define meshtastic_MqttClientProxyMessage_init_default {"", 0, {{0, {0}}}, 0} -#define meshtastic_MeshPacket_init_default {0, 0, 0, 0, {meshtastic_Data_init_default}, 0, 0, 0, 0, 0, _meshtastic_MeshPacket_Priority_MIN, 0, _meshtastic_MeshPacket_Delayed_MIN, 0, 0, {0, {0}}, 0} -#define meshtastic_NodeInfo_init_default {0, false, meshtastic_User_init_default, false, meshtastic_Position_init_default, 0, 0, false, meshtastic_DeviceMetrics_init_default, 0, 0, false, 0, 0} -#define meshtastic_MyNodeInfo_init_default {0, 0, 0} +#define meshtastic_MeshPacket_init_default {0, 0, 0, 0, {meshtastic_Data_init_default}, 0, 0, 0, 0, 0, _meshtastic_MeshPacket_Priority_MIN, 0, _meshtastic_MeshPacket_Delayed_MIN, 0, 0, {0, {0}}, 0, 0, 0} +#define meshtastic_NodeInfo_init_default {0, false, meshtastic_User_init_default, false, meshtastic_Position_init_default, 0, 0, false, meshtastic_DeviceMetrics_init_default, 0, 0, false, 0, 0, 0} +#define meshtastic_MyNodeInfo_init_default {0, 0, 0, {0, {0}}, ""} #define meshtastic_LogRecord_init_default {"", 0, "", _meshtastic_LogRecord_Level_MIN} #define meshtastic_QueueStatus_init_default {0, 0, 0, 0} #define meshtastic_FromRadio_init_default {0, 0, {meshtastic_MeshPacket_init_default}} @@ -1122,7 +1180,7 @@ extern "C" { #define meshtastic_Compressed_init_default {_meshtastic_PortNum_MIN, {0, {0}}} #define meshtastic_NeighborInfo_init_default {0, 0, 0, 0, {meshtastic_Neighbor_init_default, meshtastic_Neighbor_init_default, meshtastic_Neighbor_init_default, meshtastic_Neighbor_init_default, meshtastic_Neighbor_init_default, meshtastic_Neighbor_init_default, meshtastic_Neighbor_init_default, meshtastic_Neighbor_init_default, meshtastic_Neighbor_init_default, meshtastic_Neighbor_init_default}} #define meshtastic_Neighbor_init_default {0, 0, 0, 0} -#define meshtastic_DeviceMetadata_init_default {"", 0, 0, 0, 0, 0, _meshtastic_Config_DeviceConfig_Role_MIN, 0, _meshtastic_HardwareModel_MIN, 0, 0} +#define meshtastic_DeviceMetadata_init_default {"", 0, 0, 0, 0, 0, _meshtastic_Config_DeviceConfig_Role_MIN, 0, _meshtastic_HardwareModel_MIN, 0, 0, 0} #define meshtastic_Heartbeat_init_default {0} #define meshtastic_NodeRemoteHardwarePin_init_default {0, false, meshtastic_RemoteHardwarePin_init_default} #define meshtastic_ChunkedPayload_init_default {0, 0, 0, {0, {0}}} @@ -1135,9 +1193,9 @@ extern "C" { #define meshtastic_Data_init_zero {_meshtastic_PortNum_MIN, {0, {0}}, 0, 0, 0, 0, 0, 0, false, 0} #define meshtastic_Waypoint_init_zero {0, false, 0, false, 0, 0, 0, "", "", 0} #define meshtastic_MqttClientProxyMessage_init_zero {"", 0, {{0, {0}}}, 0} -#define meshtastic_MeshPacket_init_zero {0, 0, 0, 0, {meshtastic_Data_init_zero}, 0, 0, 0, 0, 0, _meshtastic_MeshPacket_Priority_MIN, 0, _meshtastic_MeshPacket_Delayed_MIN, 0, 0, {0, {0}}, 0} -#define meshtastic_NodeInfo_init_zero {0, false, meshtastic_User_init_zero, false, meshtastic_Position_init_zero, 0, 0, false, meshtastic_DeviceMetrics_init_zero, 0, 0, false, 0, 0} -#define meshtastic_MyNodeInfo_init_zero {0, 0, 0} +#define meshtastic_MeshPacket_init_zero {0, 0, 0, 0, {meshtastic_Data_init_zero}, 0, 0, 0, 0, 0, _meshtastic_MeshPacket_Priority_MIN, 0, _meshtastic_MeshPacket_Delayed_MIN, 0, 0, {0, {0}}, 0, 0, 0} +#define meshtastic_NodeInfo_init_zero {0, false, meshtastic_User_init_zero, false, meshtastic_Position_init_zero, 0, 0, false, meshtastic_DeviceMetrics_init_zero, 0, 0, false, 0, 0, 0} +#define meshtastic_MyNodeInfo_init_zero {0, 0, 0, {0, {0}}, ""} #define meshtastic_LogRecord_init_zero {"", 0, "", _meshtastic_LogRecord_Level_MIN} #define meshtastic_QueueStatus_init_zero {0, 0, 0, 0} #define meshtastic_FromRadio_init_zero {0, 0, {meshtastic_MeshPacket_init_zero}} @@ -1147,7 +1205,7 @@ extern "C" { #define meshtastic_Compressed_init_zero {_meshtastic_PortNum_MIN, {0, {0}}} #define meshtastic_NeighborInfo_init_zero {0, 0, 0, 0, {meshtastic_Neighbor_init_zero, meshtastic_Neighbor_init_zero, meshtastic_Neighbor_init_zero, meshtastic_Neighbor_init_zero, meshtastic_Neighbor_init_zero, meshtastic_Neighbor_init_zero, meshtastic_Neighbor_init_zero, meshtastic_Neighbor_init_zero, meshtastic_Neighbor_init_zero, meshtastic_Neighbor_init_zero}} #define meshtastic_Neighbor_init_zero {0, 0, 0, 0} -#define meshtastic_DeviceMetadata_init_zero {"", 0, 0, 0, 0, 0, _meshtastic_Config_DeviceConfig_Role_MIN, 0, _meshtastic_HardwareModel_MIN, 0, 0} +#define meshtastic_DeviceMetadata_init_zero {"", 0, 0, 0, 0, 0, _meshtastic_Config_DeviceConfig_Role_MIN, 0, _meshtastic_HardwareModel_MIN, 0, 0, 0} #define meshtastic_Heartbeat_init_zero {0} #define meshtastic_NodeRemoteHardwarePin_init_zero {0, false, meshtastic_RemoteHardwarePin_init_zero} #define meshtastic_ChunkedPayload_init_zero {0, 0, 0, {0, {0}}} @@ -1231,6 +1289,8 @@ extern "C" { #define meshtastic_MeshPacket_hop_start_tag 15 #define meshtastic_MeshPacket_public_key_tag 16 #define meshtastic_MeshPacket_pki_encrypted_tag 17 +#define meshtastic_MeshPacket_next_hop_tag 18 +#define meshtastic_MeshPacket_relay_node_tag 19 #define meshtastic_NodeInfo_num_tag 1 #define meshtastic_NodeInfo_user_tag 2 #define meshtastic_NodeInfo_position_tag 3 @@ -1241,9 +1301,12 @@ extern "C" { #define meshtastic_NodeInfo_via_mqtt_tag 8 #define meshtastic_NodeInfo_hops_away_tag 9 #define meshtastic_NodeInfo_is_favorite_tag 10 +#define meshtastic_NodeInfo_is_ignored_tag 11 #define meshtastic_MyNodeInfo_my_node_num_tag 1 #define meshtastic_MyNodeInfo_reboot_count_tag 8 #define meshtastic_MyNodeInfo_min_app_version_tag 11 +#define meshtastic_MyNodeInfo_device_id_tag 12 +#define meshtastic_MyNodeInfo_pio_env_tag 13 #define meshtastic_LogRecord_message_tag 1 #define meshtastic_LogRecord_time_tag 2 #define meshtastic_LogRecord_source_tag 3 @@ -1279,6 +1342,7 @@ extern "C" { #define meshtastic_DeviceMetadata_hw_model_tag 9 #define meshtastic_DeviceMetadata_hasRemoteHardware_tag 10 #define meshtastic_DeviceMetadata_hasPKC_tag 11 +#define meshtastic_DeviceMetadata_excluded_modules_tag 12 #define meshtastic_FromRadio_id_tag 1 #define meshtastic_FromRadio_packet_tag 2 #define meshtastic_FromRadio_my_info_tag 3 @@ -1421,7 +1485,9 @@ X(a, STATIC, SINGULAR, UENUM, delayed, 13) \ X(a, STATIC, SINGULAR, BOOL, via_mqtt, 14) \ X(a, STATIC, SINGULAR, UINT32, hop_start, 15) \ X(a, STATIC, SINGULAR, BYTES, public_key, 16) \ -X(a, STATIC, SINGULAR, BOOL, pki_encrypted, 17) +X(a, STATIC, SINGULAR, BOOL, pki_encrypted, 17) \ +X(a, STATIC, SINGULAR, UINT32, next_hop, 18) \ +X(a, STATIC, SINGULAR, UINT32, relay_node, 19) #define meshtastic_MeshPacket_CALLBACK NULL #define meshtastic_MeshPacket_DEFAULT NULL #define meshtastic_MeshPacket_payload_variant_decoded_MSGTYPE meshtastic_Data @@ -1436,7 +1502,8 @@ X(a, STATIC, OPTIONAL, MESSAGE, device_metrics, 6) \ X(a, STATIC, SINGULAR, UINT32, channel, 7) \ X(a, STATIC, SINGULAR, BOOL, via_mqtt, 8) \ X(a, STATIC, OPTIONAL, UINT32, hops_away, 9) \ -X(a, STATIC, SINGULAR, BOOL, is_favorite, 10) +X(a, STATIC, SINGULAR, BOOL, is_favorite, 10) \ +X(a, STATIC, SINGULAR, BOOL, is_ignored, 11) #define meshtastic_NodeInfo_CALLBACK NULL #define meshtastic_NodeInfo_DEFAULT NULL #define meshtastic_NodeInfo_user_MSGTYPE meshtastic_User @@ -1446,7 +1513,9 @@ X(a, STATIC, SINGULAR, BOOL, is_favorite, 10) #define meshtastic_MyNodeInfo_FIELDLIST(X, a) \ X(a, STATIC, SINGULAR, UINT32, my_node_num, 1) \ X(a, STATIC, SINGULAR, UINT32, reboot_count, 8) \ -X(a, STATIC, SINGULAR, UINT32, min_app_version, 11) +X(a, STATIC, SINGULAR, UINT32, min_app_version, 11) \ +X(a, STATIC, SINGULAR, BYTES, device_id, 12) \ +X(a, STATIC, SINGULAR, STRING, pio_env, 13) #define meshtastic_MyNodeInfo_CALLBACK NULL #define meshtastic_MyNodeInfo_DEFAULT NULL @@ -1563,7 +1632,8 @@ X(a, STATIC, SINGULAR, UENUM, role, 7) \ X(a, STATIC, SINGULAR, UINT32, position_flags, 8) \ X(a, STATIC, SINGULAR, UENUM, hw_model, 9) \ X(a, STATIC, SINGULAR, BOOL, hasRemoteHardware, 10) \ -X(a, STATIC, SINGULAR, BOOL, hasPKC, 11) +X(a, STATIC, SINGULAR, BOOL, hasPKC, 11) \ +X(a, STATIC, SINGULAR, UINT32, excluded_modules, 12) #define meshtastic_DeviceMetadata_CALLBACK NULL #define meshtastic_DeviceMetadata_DEFAULT NULL @@ -1660,19 +1730,19 @@ extern const pb_msgdesc_t meshtastic_ChunkedPayloadResponse_msg; #define MESHTASTIC_MESHTASTIC_MESH_PB_H_MAX_SIZE meshtastic_FromRadio_size #define meshtastic_ChunkedPayload_size 245 #define meshtastic_ClientNotification_size 415 -#define meshtastic_Compressed_size 243 -#define meshtastic_Data_size 273 -#define meshtastic_DeviceMetadata_size 48 +#define meshtastic_Compressed_size 239 +#define meshtastic_Data_size 269 +#define meshtastic_DeviceMetadata_size 54 #define meshtastic_FileInfo_size 236 #define meshtastic_FromRadio_size 510 #define meshtastic_Heartbeat_size 0 #define meshtastic_LogRecord_size 426 -#define meshtastic_MeshPacket_size 367 +#define meshtastic_MeshPacket_size 371 #define meshtastic_MqttClientProxyMessage_size 501 -#define meshtastic_MyNodeInfo_size 18 +#define meshtastic_MyNodeInfo_size 77 #define meshtastic_NeighborInfo_size 258 #define meshtastic_Neighbor_size 22 -#define meshtastic_NodeInfo_size 317 +#define meshtastic_NodeInfo_size 319 #define meshtastic_NodeRemoteHardwarePin_size 29 #define meshtastic_Position_size 144 #define meshtastic_QueueStatus_size 23 diff --git a/src/mesh/generated/meshtastic/module_config.pb.h b/src/mesh/generated/meshtastic/module_config.pb.h index 32d5ded23..8f7bb701d 100644 --- a/src/mesh/generated/meshtastic/module_config.pb.h +++ b/src/mesh/generated/meshtastic/module_config.pb.h @@ -153,8 +153,11 @@ typedef struct _meshtastic_ModuleConfig_NeighborInfoConfig { /* Whether the Module is enabled */ bool enabled; /* Interval in seconds of how often we should try to send our - Neighbor Info to the mesh */ + Neighbor Info (minimum is 14400, i.e., 4 hours) */ uint32_t update_interval; + /* Whether in addition to sending it to MQTT and the PhoneAPI, our NeighborInfo should be transmitted over LoRa. + Note that this is not available on a channel with default key and name. */ + bool transmit_over_lora; } meshtastic_ModuleConfig_NeighborInfoConfig; /* Detection Sensor Module Config */ @@ -501,7 +504,7 @@ extern "C" { #define meshtastic_ModuleConfig_MQTTConfig_init_default {0, "", "", "", 0, 0, 0, "", 0, 0, false, meshtastic_ModuleConfig_MapReportSettings_init_default} #define meshtastic_ModuleConfig_MapReportSettings_init_default {0, 0} #define meshtastic_ModuleConfig_RemoteHardwareConfig_init_default {0, 0, 0, {meshtastic_RemoteHardwarePin_init_default, meshtastic_RemoteHardwarePin_init_default, meshtastic_RemoteHardwarePin_init_default, meshtastic_RemoteHardwarePin_init_default}} -#define meshtastic_ModuleConfig_NeighborInfoConfig_init_default {0, 0} +#define meshtastic_ModuleConfig_NeighborInfoConfig_init_default {0, 0, 0} #define meshtastic_ModuleConfig_DetectionSensorConfig_init_default {0, 0, 0, 0, "", 0, _meshtastic_ModuleConfig_DetectionSensorConfig_TriggerType_MIN, 0} #define meshtastic_ModuleConfig_AudioConfig_init_default {0, 0, _meshtastic_ModuleConfig_AudioConfig_Audio_Baud_MIN, 0, 0, 0, 0} #define meshtastic_ModuleConfig_PaxcounterConfig_init_default {0, 0, 0, 0} @@ -517,7 +520,7 @@ extern "C" { #define meshtastic_ModuleConfig_MQTTConfig_init_zero {0, "", "", "", 0, 0, 0, "", 0, 0, false, meshtastic_ModuleConfig_MapReportSettings_init_zero} #define meshtastic_ModuleConfig_MapReportSettings_init_zero {0, 0} #define meshtastic_ModuleConfig_RemoteHardwareConfig_init_zero {0, 0, 0, {meshtastic_RemoteHardwarePin_init_zero, meshtastic_RemoteHardwarePin_init_zero, meshtastic_RemoteHardwarePin_init_zero, meshtastic_RemoteHardwarePin_init_zero}} -#define meshtastic_ModuleConfig_NeighborInfoConfig_init_zero {0, 0} +#define meshtastic_ModuleConfig_NeighborInfoConfig_init_zero {0, 0, 0} #define meshtastic_ModuleConfig_DetectionSensorConfig_init_zero {0, 0, 0, 0, "", 0, _meshtastic_ModuleConfig_DetectionSensorConfig_TriggerType_MIN, 0} #define meshtastic_ModuleConfig_AudioConfig_init_zero {0, 0, _meshtastic_ModuleConfig_AudioConfig_Audio_Baud_MIN, 0, 0, 0, 0} #define meshtastic_ModuleConfig_PaxcounterConfig_init_zero {0, 0, 0, 0} @@ -546,6 +549,7 @@ extern "C" { #define meshtastic_ModuleConfig_MQTTConfig_map_report_settings_tag 11 #define meshtastic_ModuleConfig_NeighborInfoConfig_enabled_tag 1 #define meshtastic_ModuleConfig_NeighborInfoConfig_update_interval_tag 2 +#define meshtastic_ModuleConfig_NeighborInfoConfig_transmit_over_lora_tag 3 #define meshtastic_ModuleConfig_DetectionSensorConfig_enabled_tag 1 #define meshtastic_ModuleConfig_DetectionSensorConfig_minimum_broadcast_secs_tag 2 #define meshtastic_ModuleConfig_DetectionSensorConfig_state_broadcast_secs_tag 3 @@ -709,7 +713,8 @@ X(a, STATIC, REPEATED, MESSAGE, available_pins, 3) #define meshtastic_ModuleConfig_NeighborInfoConfig_FIELDLIST(X, a) \ X(a, STATIC, SINGULAR, BOOL, enabled, 1) \ -X(a, STATIC, SINGULAR, UINT32, update_interval, 2) +X(a, STATIC, SINGULAR, UINT32, update_interval, 2) \ +X(a, STATIC, SINGULAR, BOOL, transmit_over_lora, 3) #define meshtastic_ModuleConfig_NeighborInfoConfig_CALLBACK NULL #define meshtastic_ModuleConfig_NeighborInfoConfig_DEFAULT NULL @@ -884,7 +889,7 @@ extern const pb_msgdesc_t meshtastic_RemoteHardwarePin_msg; #define meshtastic_ModuleConfig_ExternalNotificationConfig_size 42 #define meshtastic_ModuleConfig_MQTTConfig_size 254 #define meshtastic_ModuleConfig_MapReportSettings_size 12 -#define meshtastic_ModuleConfig_NeighborInfoConfig_size 8 +#define meshtastic_ModuleConfig_NeighborInfoConfig_size 10 #define meshtastic_ModuleConfig_PaxcounterConfig_size 30 #define meshtastic_ModuleConfig_RangeTestConfig_size 10 #define meshtastic_ModuleConfig_RemoteHardwareConfig_size 96 diff --git a/src/mesh/generated/meshtastic/rtttl.pb.h b/src/mesh/generated/meshtastic/rtttl.pb.h index 2b7e35f11..0572265f7 100644 --- a/src/mesh/generated/meshtastic/rtttl.pb.h +++ b/src/mesh/generated/meshtastic/rtttl.pb.h @@ -13,7 +13,7 @@ /* Canned message module configuration. */ typedef struct _meshtastic_RTTTLConfig { /* Ringtone for PWM Buzzer in RTTTL Format. */ - char ringtone[230]; + char ringtone[231]; } meshtastic_RTTTLConfig; @@ -41,7 +41,7 @@ extern const pb_msgdesc_t meshtastic_RTTTLConfig_msg; /* Maximum encoded size of messages (where known) */ #define MESHTASTIC_MESHTASTIC_RTTTL_PB_H_MAX_SIZE meshtastic_RTTTLConfig_size -#define meshtastic_RTTTLConfig_size 232 +#define meshtastic_RTTTLConfig_size 233 #ifdef __cplusplus } /* extern "C" */ diff --git a/src/mesh/generated/meshtastic/storeforward.pb.h b/src/mesh/generated/meshtastic/storeforward.pb.h index 71f2fcad5..44ffd098c 100644 --- a/src/mesh/generated/meshtastic/storeforward.pb.h +++ b/src/mesh/generated/meshtastic/storeforward.pb.h @@ -91,7 +91,7 @@ typedef struct _meshtastic_StoreAndForward_Heartbeat { uint32_t secondary; } meshtastic_StoreAndForward_Heartbeat; -typedef PB_BYTES_ARRAY_T(237) meshtastic_StoreAndForward_text_t; +typedef PB_BYTES_ARRAY_T(233) meshtastic_StoreAndForward_text_t; /* TODO: REPLACE */ typedef struct _meshtastic_StoreAndForward { /* TODO: REPLACE */ @@ -211,7 +211,7 @@ extern const pb_msgdesc_t meshtastic_StoreAndForward_Heartbeat_msg; #define meshtastic_StoreAndForward_Heartbeat_size 12 #define meshtastic_StoreAndForward_History_size 18 #define meshtastic_StoreAndForward_Statistics_size 50 -#define meshtastic_StoreAndForward_size 242 +#define meshtastic_StoreAndForward_size 238 #ifdef __cplusplus } /* extern "C" */ diff --git a/src/mesh/generated/meshtastic/telemetry.pb.h b/src/mesh/generated/meshtastic/telemetry.pb.h index 309c01dc7..874eef60f 100644 --- a/src/mesh/generated/meshtastic/telemetry.pb.h +++ b/src/mesh/generated/meshtastic/telemetry.pb.h @@ -77,7 +77,9 @@ typedef enum _meshtastic_TelemetrySensorType { /* MLX90614 non-contact IR temperature sensor */ meshtastic_TelemetrySensorType_MLX90614 = 31, /* SCD40/SCD41 CO2, humidity, temperature sensor */ - meshtastic_TelemetrySensorType_SCD4X = 32 + meshtastic_TelemetrySensorType_SCD4X = 32, + /* ClimateGuard RadSens, radiation, Geiger-Muller Tube */ + meshtastic_TelemetrySensorType_RADSENS = 33 } meshtastic_TelemetrySensorType; /* Struct definitions */ @@ -155,6 +157,9 @@ typedef struct _meshtastic_EnvironmentMetrics { /* Wind lull in m/s */ bool has_wind_lull; float wind_lull; + /* Radiation in µR/h */ + bool has_radiation; + float radiation; } meshtastic_EnvironmentMetrics; /* Power Metrics (voltage / current / etc) */ @@ -299,8 +304,8 @@ extern "C" { /* Helper constants for enums */ #define _meshtastic_TelemetrySensorType_MIN meshtastic_TelemetrySensorType_SENSOR_UNSET -#define _meshtastic_TelemetrySensorType_MAX meshtastic_TelemetrySensorType_SCD4X -#define _meshtastic_TelemetrySensorType_ARRAYSIZE ((meshtastic_TelemetrySensorType)(meshtastic_TelemetrySensorType_SCD4X+1)) +#define _meshtastic_TelemetrySensorType_MAX meshtastic_TelemetrySensorType_RADSENS +#define _meshtastic_TelemetrySensorType_ARRAYSIZE ((meshtastic_TelemetrySensorType)(meshtastic_TelemetrySensorType_RADSENS+1)) @@ -313,7 +318,7 @@ extern "C" { /* Initializer values for message structs */ #define meshtastic_DeviceMetrics_init_default {false, 0, false, 0, false, 0, false, 0, false, 0} -#define meshtastic_EnvironmentMetrics_init_default {false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0} +#define meshtastic_EnvironmentMetrics_init_default {false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0} #define meshtastic_PowerMetrics_init_default {false, 0, false, 0, false, 0, false, 0, false, 0, false, 0} #define meshtastic_AirQualityMetrics_init_default {false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0} #define meshtastic_LocalStats_init_default {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} @@ -321,7 +326,7 @@ extern "C" { #define meshtastic_Telemetry_init_default {0, 0, {meshtastic_DeviceMetrics_init_default}} #define meshtastic_Nau7802Config_init_default {0, 0} #define meshtastic_DeviceMetrics_init_zero {false, 0, false, 0, false, 0, false, 0, false, 0} -#define meshtastic_EnvironmentMetrics_init_zero {false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0} +#define meshtastic_EnvironmentMetrics_init_zero {false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0} #define meshtastic_PowerMetrics_init_zero {false, 0, false, 0, false, 0, false, 0, false, 0, false, 0} #define meshtastic_AirQualityMetrics_init_zero {false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0} #define meshtastic_LocalStats_init_zero {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} @@ -352,6 +357,7 @@ extern "C" { #define meshtastic_EnvironmentMetrics_weight_tag 15 #define meshtastic_EnvironmentMetrics_wind_gust_tag 16 #define meshtastic_EnvironmentMetrics_wind_lull_tag 17 +#define meshtastic_EnvironmentMetrics_radiation_tag 18 #define meshtastic_PowerMetrics_ch1_voltage_tag 1 #define meshtastic_PowerMetrics_ch1_current_tag 2 #define meshtastic_PowerMetrics_ch2_voltage_tag 3 @@ -422,7 +428,8 @@ X(a, STATIC, OPTIONAL, UINT32, wind_direction, 13) \ X(a, STATIC, OPTIONAL, FLOAT, wind_speed, 14) \ X(a, STATIC, OPTIONAL, FLOAT, weight, 15) \ X(a, STATIC, OPTIONAL, FLOAT, wind_gust, 16) \ -X(a, STATIC, OPTIONAL, FLOAT, wind_lull, 17) +X(a, STATIC, OPTIONAL, FLOAT, wind_lull, 17) \ +X(a, STATIC, OPTIONAL, FLOAT, radiation, 18) #define meshtastic_EnvironmentMetrics_CALLBACK NULL #define meshtastic_EnvironmentMetrics_DEFAULT NULL @@ -521,12 +528,12 @@ extern const pb_msgdesc_t meshtastic_Nau7802Config_msg; #define MESHTASTIC_MESHTASTIC_TELEMETRY_PB_H_MAX_SIZE meshtastic_Telemetry_size #define meshtastic_AirQualityMetrics_size 78 #define meshtastic_DeviceMetrics_size 27 -#define meshtastic_EnvironmentMetrics_size 85 +#define meshtastic_EnvironmentMetrics_size 91 #define meshtastic_HealthMetrics_size 11 #define meshtastic_LocalStats_size 60 #define meshtastic_Nau7802Config_size 16 #define meshtastic_PowerMetrics_size 30 -#define meshtastic_Telemetry_size 92 +#define meshtastic_Telemetry_size 98 #ifdef __cplusplus } /* extern "C" */ diff --git a/src/mesh/http/ContentHandler.cpp b/src/mesh/http/ContentHandler.cpp index c5ea86429..64f7164c9 100644 --- a/src/mesh/http/ContentHandler.cpp +++ b/src/mesh/http/ContentHandler.cpp @@ -74,6 +74,7 @@ void registerHandlers(HTTPServer *insecureServer, HTTPSServer *secureServer) ResourceNode *nodeAPIv1ToRadioOptions = new ResourceNode("/api/v1/toradio", "OPTIONS", &handleAPIv1ToRadio); ResourceNode *nodeAPIv1ToRadio = new ResourceNode("/api/v1/toradio", "PUT", &handleAPIv1ToRadio); + ResourceNode *nodeAPIv1FromRadioOptions = new ResourceNode("/api/v1/fromradio", "OPTIONS", &handleAPIv1FromRadio); ResourceNode *nodeAPIv1FromRadio = new ResourceNode("/api/v1/fromradio", "GET", &handleAPIv1FromRadio); // ResourceNode *nodeHotspotApple = new ResourceNode("/hotspot-detect.html", "GET", &handleHotspot); @@ -100,6 +101,7 @@ void registerHandlers(HTTPServer *insecureServer, HTTPSServer *secureServer) // Secure nodes secureServer->registerNode(nodeAPIv1ToRadioOptions); secureServer->registerNode(nodeAPIv1ToRadio); + secureServer->registerNode(nodeAPIv1FromRadioOptions); secureServer->registerNode(nodeAPIv1FromRadio); // secureServer->registerNode(nodeHotspotApple); // secureServer->registerNode(nodeHotspotAndroid); @@ -121,6 +123,7 @@ void registerHandlers(HTTPServer *insecureServer, HTTPSServer *secureServer) // Insecure nodes insecureServer->registerNode(nodeAPIv1ToRadioOptions); insecureServer->registerNode(nodeAPIv1ToRadio); + insecureServer->registerNode(nodeAPIv1FromRadioOptions); insecureServer->registerNode(nodeAPIv1FromRadio); // insecureServer->registerNode(nodeHotspotApple); // insecureServer->registerNode(nodeHotspotAndroid); @@ -143,7 +146,7 @@ void registerHandlers(HTTPServer *insecureServer, HTTPSServer *secureServer) void handleAPIv1FromRadio(HTTPRequest *req, HTTPResponse *res) { - LOG_DEBUG("webAPI handleAPIv1FromRadio\n"); + LOG_DEBUG("webAPI handleAPIv1FromRadio"); /* For documentation, see: @@ -163,6 +166,12 @@ void handleAPIv1FromRadio(HTTPRequest *req, HTTPResponse *res) res->setHeader("Access-Control-Allow-Methods", "GET"); res->setHeader("X-Protobuf-Schema", "https://raw.githubusercontent.com/meshtastic/protobufs/master/meshtastic/mesh.proto"); + if (req->getMethod() == "OPTIONS") { + res->setStatusCode(204); // Success with no content + // res->print(""); @todo remove + return; + } + uint8_t txBuf[MAX_STREAM_BUF_SIZE]; uint32_t len = 1; @@ -188,12 +197,12 @@ void handleAPIv1FromRadio(HTTPRequest *req, HTTPResponse *res) res->write(txBuf, len); } - LOG_DEBUG("webAPI handleAPIv1FromRadio, len %d\n", len); + LOG_DEBUG("webAPI handleAPIv1FromRadio, len %d", len); } void handleAPIv1ToRadio(HTTPRequest *req, HTTPResponse *res) { - LOG_DEBUG("webAPI handleAPIv1ToRadio\n"); + LOG_DEBUG("webAPI handleAPIv1ToRadio"); /* For documentation, see: @@ -216,11 +225,11 @@ void handleAPIv1ToRadio(HTTPRequest *req, HTTPResponse *res) byte buffer[MAX_TO_FROM_RADIO_SIZE]; size_t s = req->readBytes(buffer, MAX_TO_FROM_RADIO_SIZE); - LOG_DEBUG("Received %d bytes from PUT request\n", s); + LOG_DEBUG("Received %d bytes from PUT request", s); webAPI.handleToRadio(buffer, s); res->write(buffer, s); - LOG_DEBUG("webAPI handleAPIv1ToRadio\n"); + LOG_DEBUG("webAPI handleAPIv1ToRadio"); } void htmlDeleteDir(const char *dirname) @@ -243,7 +252,7 @@ void htmlDeleteDir(const char *dirname) String fileName = String(file.name()); file.flush(); file.close(); - LOG_DEBUG(" %s\n", fileName.c_str()); + LOG_DEBUG(" %s", fileName.c_str()); FSCom.remove(fileName); } file = root.openNextFile(); @@ -341,7 +350,7 @@ void handleFsDeleteStatic(HTTPRequest *req, HTTPResponse *res) if (params->getQueryParameter("delete", paramValDelete)) { std::string pathDelete = "/" + paramValDelete; if (FSCom.remove(pathDelete.c_str())) { - LOG_INFO("%s\n", pathDelete.c_str()); + LOG_INFO("%s", pathDelete.c_str()); JSONObject jsonObjOuter; jsonObjOuter["status"] = new JSONValue("ok"); JSONValue *value = new JSONValue(jsonObjOuter); @@ -349,7 +358,7 @@ void handleFsDeleteStatic(HTTPRequest *req, HTTPResponse *res) delete value; return; } else { - LOG_INFO("%s\n", pathDelete.c_str()); + LOG_INFO("%s", pathDelete.c_str()); JSONObject jsonObjOuter; jsonObjOuter["status"] = new JSONValue("Error"); JSONValue *value = new JSONValue(jsonObjOuter); @@ -385,13 +394,13 @@ void handleStatic(HTTPRequest *req, HTTPResponse *res) if (FSCom.exists(filename.c_str())) { file = FSCom.open(filename.c_str()); if (!file.available()) { - LOG_WARN("File not available - %s\n", filename.c_str()); + LOG_WARN("File not available - %s", filename.c_str()); } } else if (FSCom.exists(filenameGzip.c_str())) { file = FSCom.open(filenameGzip.c_str()); res->setHeader("Content-Encoding", "gzip"); if (!file.available()) { - LOG_WARN("File not available - %s\n", filenameGzip.c_str()); + LOG_WARN("File not available - %s", filenameGzip.c_str()); } } else { has_set_content_type = true; @@ -399,7 +408,7 @@ void handleStatic(HTTPRequest *req, HTTPResponse *res) file = FSCom.open(filenameGzip.c_str()); res->setHeader("Content-Type", "text/html"); if (!file.available()) { - LOG_WARN("File not available - %s\n", filenameGzip.c_str()); + LOG_WARN("File not available - %s", filenameGzip.c_str()); res->println("Web server is running.

The content you are looking for can't be found. Please see: FAQ.

admin"); @@ -441,15 +450,15 @@ void handleStatic(HTTPRequest *req, HTTPResponse *res) return; } else { - LOG_ERROR("This should not have happened...\n"); - res->println("ERROR: This should not have happened..."); + LOG_ERROR("This should not have happened"); + res->println("ERROR: This should not have happened"); } } void handleFormUpload(HTTPRequest *req, HTTPResponse *res) { - LOG_DEBUG("Form Upload - Disabling keep-alive\n"); + LOG_DEBUG("Form Upload - Disable keep-alive"); res->setHeader("Connection", "close"); // First, we need to check the encoding of the form that we have received. @@ -457,7 +466,7 @@ void handleFormUpload(HTTPRequest *req, HTTPResponse *res) // Then we select the body parser based on the encoding. // Actually we do this only for documentary purposes, we know the form is going // to be multipart/form-data. - LOG_DEBUG("Form Upload - Creating body parser reference\n"); + LOG_DEBUG("Form Upload - Creating body parser reference"); HTTPBodyParser *parser; std::string contentType = req->getHeader("Content-Type"); @@ -473,10 +482,10 @@ void handleFormUpload(HTTPRequest *req, HTTPResponse *res) // Now, we can decide based on the content type: if (contentType == "multipart/form-data") { - LOG_DEBUG("Form Upload - multipart/form-data\n"); + LOG_DEBUG("Form Upload - multipart/form-data"); parser = new HTTPMultipartBodyParser(req); } else { - LOG_DEBUG("Unknown POST Content-Type: %s\n", contentType.c_str()); + LOG_DEBUG("Unknown POST Content-Type: %s", contentType.c_str()); return; } @@ -503,19 +512,19 @@ void handleFormUpload(HTTPRequest *req, HTTPResponse *res) std::string filename = parser->getFieldFilename(); std::string mimeType = parser->getFieldMimeType(); // We log all three values, so that you can observe the upload on the serial monitor: - LOG_DEBUG("handleFormUpload: field name='%s', filename='%s', mimetype='%s'\n", name.c_str(), filename.c_str(), + LOG_DEBUG("handleFormUpload: field name='%s', filename='%s', mimetype='%s'", name.c_str(), filename.c_str(), mimeType.c_str()); // Double check that it is what we expect if (name != "file") { - LOG_DEBUG("Skipping unexpected field\n"); + LOG_DEBUG("Skip unexpected field"); res->println("

No file found.

"); return; } // Double check that it is what we expect if (filename == "") { - LOG_DEBUG("Skipping unexpected field\n"); + LOG_DEBUG("Skip unexpected field"); res->println("

No file found.

"); return; } @@ -536,7 +545,7 @@ void handleFormUpload(HTTPRequest *req, HTTPResponse *res) byte buf[512]; size_t readLength = parser->read(buf, 512); - // LOG_DEBUG("\n\nreadLength - %i\n", readLength); + // LOG_DEBUG("readLength - %i", readLength); // Abort the transfer if there is less than 50k space left on the filesystem. if (FSCom.totalBytes() - FSCom.usedBytes() < 51200) { @@ -553,7 +562,7 @@ void handleFormUpload(HTTPRequest *req, HTTPResponse *res) // if (readLength) { file.write(buf, readLength); fileLength += readLength; - LOG_DEBUG("File Length %i\n", fileLength); + LOG_DEBUG("File Length %i", fileLength); //} } // enableLoopWDT(); @@ -676,7 +685,7 @@ void handleReport(HTTPRequest *req, HTTPResponse *res) */ void handleHotspot(HTTPRequest *req, HTTPResponse *res) { - LOG_INFO("Hotspot Request\n"); + LOG_INFO("Hotspot Request"); /* If we don't do a redirect, be sure to return a "Success" message @@ -690,7 +699,7 @@ void handleHotspot(HTTPRequest *req, HTTPResponse *res) res->setHeader("Access-Control-Allow-Methods", "GET"); // res->println(""); - res->println("\n"); + res->println(""); } void handleDeleteFsContent(HTTPRequest *req, HTTPResponse *res) @@ -699,14 +708,14 @@ void handleDeleteFsContent(HTTPRequest *req, HTTPResponse *res) res->setHeader("Access-Control-Allow-Origin", "*"); res->setHeader("Access-Control-Allow-Methods", "GET"); - res->println("

Meshtastic

\n"); - res->println("Deleting Content in /static/*"); + res->println("

Meshtastic

"); + res->println("Delete Content in /static/*"); - LOG_INFO("Deleting files from /static/* : \n"); + LOG_INFO("Delete files from /static/* : "); htmlDeleteDir("/static"); - res->println("


Back to admin\n"); + res->println("


Back to admin"); } void handleAdmin(HTTPRequest *req, HTTPResponse *res) @@ -715,10 +724,10 @@ void handleAdmin(HTTPRequest *req, HTTPResponse *res) res->setHeader("Access-Control-Allow-Origin", "*"); res->setHeader("Access-Control-Allow-Methods", "GET"); - res->println("

Meshtastic

\n"); - // res->println("Settings
\n"); - // res->println("Manage Web Content
\n"); - res->println("Device Report
\n"); + res->println("

Meshtastic

"); + // res->println("Settings
"); + // res->println("Manage Web Content
"); + res->println("Device Report
"); } void handleAdminSettings(HTTPRequest *req, HTTPResponse *res) @@ -727,20 +736,20 @@ void handleAdminSettings(HTTPRequest *req, HTTPResponse *res) res->setHeader("Access-Control-Allow-Origin", "*"); res->setHeader("Access-Control-Allow-Methods", "GET"); - res->println("

Meshtastic

\n"); - res->println("This isn't done.\n"); - res->println("
\n"); - res->println("\n"); - res->println("\n"); - res->println("\n"); - res->println("\n"); + res->println("

Meshtastic

"); + res->println("This isn't done."); + res->println(""); + res->println("
Set?Settingcurrent valuenew value
WiFi SSIDfalse
WiFi Passwordfalse
"); + res->println(""); + res->println(""); + res->println(""); res->println( - "\n"); - res->println("
Set?Settingcurrent valuenew value
WiFi SSIDfalse
WiFi Passwordfalse
Smart Position Updatefalse
\n"); - res->println("\n"); - res->println("\n"); - res->println("\n"); - res->println("


Back to admin\n"); + "

"); + res->println("
Smart Position Updatefalse
"); + res->println(""); + res->println(""); + res->println(""); + res->println("


Back to admin"); } void handleAdminSettingsApply(HTTPRequest *req, HTTPResponse *res) @@ -748,11 +757,11 @@ void handleAdminSettingsApply(HTTPRequest *req, HTTPResponse *res) res->setHeader("Content-Type", "text/html"); res->setHeader("Access-Control-Allow-Origin", "*"); res->setHeader("Access-Control-Allow-Methods", "POST"); - res->println("

Meshtastic

\n"); + res->println("

Meshtastic

"); res->println( "Settings Applied. "); - res->println("Settings Applied. Please wait.\n"); + res->println("Settings Applied. Please wait."); } void handleFs(HTTPRequest *req, HTTPResponse *res) @@ -761,10 +770,10 @@ void handleFs(HTTPRequest *req, HTTPResponse *res) res->setHeader("Access-Control-Allow-Origin", "*"); res->setHeader("Access-Control-Allow-Methods", "GET"); - res->println("

Meshtastic

\n"); + res->println("

Meshtastic

"); res->println("Delete Web Content

Be patient!"); - res->println("


Back to admin\n"); + res->println("


Back to admin"); } void handleRestart(HTTPRequest *req, HTTPResponse *res) @@ -773,10 +782,10 @@ void handleRestart(HTTPRequest *req, HTTPResponse *res) res->setHeader("Access-Control-Allow-Origin", "*"); res->setHeader("Access-Control-Allow-Methods", "GET"); - res->println("

Meshtastic

\n"); + res->println("

Meshtastic

"); res->println("Restarting"); - LOG_DEBUG("Restarted on HTTP(s) Request\n"); + LOG_DEBUG("Restarted on HTTP(s) Request"); webServerThread->requestRestart = (millis() / 1000) + 5; } diff --git a/src/mesh/http/WebServer.cpp b/src/mesh/http/WebServer.cpp index fc8535257..d9856e157 100644 --- a/src/mesh/http/WebServer.cpp +++ b/src/mesh/http/WebServer.cpp @@ -69,19 +69,19 @@ static void taskCreateCert(void *parameter) #if 0 // Delete the saved certs (used in debugging) - LOG_DEBUG("Deleting any saved SSL keys ...\n"); + LOG_DEBUG("Delete any saved SSL keys"); // prefs.clear(); prefs.remove("PK"); prefs.remove("cert"); #endif - LOG_INFO("Checking if we have a previously saved SSL Certificate.\n"); + LOG_INFO("Checking if we have a saved SSL Certificate"); size_t pkLen = prefs.getBytesLength("PK"); size_t certLen = prefs.getBytesLength("cert"); if (pkLen && certLen) { - LOG_INFO("Existing SSL Certificate found!\n"); + LOG_INFO("Existing SSL Certificate found!"); uint8_t *pkBuffer = new uint8_t[pkLen]; prefs.getBytes("PK", pkBuffer, pkLen); @@ -91,11 +91,11 @@ static void taskCreateCert(void *parameter) cert = new SSLCert(certBuffer, certLen, pkBuffer, pkLen); - LOG_DEBUG("Retrieved Private Key: %d Bytes\n", cert->getPKLength()); - LOG_DEBUG("Retrieved Certificate: %d Bytes\n", cert->getCertLength()); + LOG_DEBUG("Retrieved Private Key: %d Bytes", cert->getPKLength()); + LOG_DEBUG("Retrieved Certificate: %d Bytes", cert->getCertLength()); } else { - LOG_INFO("Creating the certificate. This may take a while. Please wait...\n"); + LOG_INFO("Creating the certificate. This may take a while. Please wait"); yield(); cert = new SSLCert(); yield(); @@ -104,13 +104,13 @@ static void taskCreateCert(void *parameter) yield(); if (createCertResult != 0) { - LOG_ERROR("Creating the certificate failed\n"); + LOG_ERROR("Creating the certificate failed"); } else { - LOG_INFO("Creating the certificate was successful\n"); + LOG_INFO("Creating the certificate was successful"); - LOG_DEBUG("Created Private Key: %d Bytes\n", cert->getPKLength()); + LOG_DEBUG("Created Private Key: %d Bytes", cert->getPKLength()); - LOG_DEBUG("Created Certificate: %d Bytes\n", cert->getCertLength()); + LOG_DEBUG("Created Certificate: %d Bytes", cert->getCertLength()); prefs.putBytes("PK", (uint8_t *)cert->getPKData(), cert->getPKLength()); prefs.putBytes("cert", (uint8_t *)cert->getCertData(), cert->getCertLength()); @@ -139,7 +139,7 @@ void createSSLCert() 16, /* Priority of the task. */ NULL); /* Task handle. */ - LOG_DEBUG("Waiting for SSL Cert to be generated.\n"); + LOG_DEBUG("Waiting for SSL Cert to be generated"); while (!isCertReady) { if ((millis() / 500) % 2) { if (runLoop) { @@ -158,13 +158,13 @@ void createSSLCert() runLoop = true; } } - LOG_INFO("SSL Cert Ready!\n"); + LOG_INFO("SSL Cert Ready!"); } } WebServerThread *webServerThread; -WebServerThread::WebServerThread() : concurrency::OSThread("WebServerThread") +WebServerThread::WebServerThread() : concurrency::OSThread("WebServer") { if (!config.network.wifi_enabled) { disable(); @@ -189,7 +189,7 @@ int32_t WebServerThread::runOnce() void initWebServer() { - LOG_DEBUG("Initializing Web Server ...\n"); + LOG_DEBUG("Init Web Server"); // We can now use the new certificate to setup our server as usual. secureServer = new HTTPSServer(cert); @@ -198,16 +198,16 @@ void initWebServer() registerHandlers(insecureServer, secureServer); if (secureServer) { - LOG_INFO("Starting Secure Web Server...\n"); + LOG_INFO("Start Secure Web Server"); secureServer->start(); } - LOG_INFO("Starting Insecure Web Server...\n"); + LOG_INFO("Start Insecure Web Server"); insecureServer->start(); if (insecureServer->isRunning()) { - LOG_INFO("Web Servers Ready! :-) \n"); + LOG_INFO("Web Servers Ready! :-) "); isWebServerReady = true; } else { - LOG_ERROR("Web Servers Failed! ;-( \n"); + LOG_ERROR("Web Servers Failed! ;-( "); } } -#endif \ No newline at end of file +#endif diff --git a/src/mesh/mesh-pb-constants.cpp b/src/mesh/mesh-pb-constants.cpp index 5b5d669cc..deb93d0a6 100644 --- a/src/mesh/mesh-pb-constants.cpp +++ b/src/mesh/mesh-pb-constants.cpp @@ -3,7 +3,6 @@ #include "FSCommon.h" #include "mesh-pb-constants.h" #include -#include #include #include @@ -13,9 +12,7 @@ size_t pb_encode_to_bytes(uint8_t *destbuf, size_t destbufsize, const pb_msgdesc { pb_ostream_t stream = pb_ostream_from_buffer(destbuf, destbufsize); if (!pb_encode(&stream, fields, src_struct)) { - LOG_ERROR("Panic: can't encode protobuf reason='%s'\n", PB_GET_ERROR(&stream)); - assert( - 0); // If this assert fails it probably means you made a field too large for the max limits specified in mesh.options + LOG_ERROR("Panic: can't encode protobuf reason='%s'", PB_GET_ERROR(&stream)); return 0; } else { return stream.bytes_written; @@ -27,7 +24,7 @@ bool pb_decode_from_bytes(const uint8_t *srcbuf, size_t srcbufsize, const pb_msg { pb_istream_t stream = pb_istream_from_buffer(srcbuf, srcbufsize); if (!pb_decode(&stream, fields, dest_struct)) { - LOG_ERROR("Can't decode protobuf reason='%s', pb_msgdesc %p\n", PB_GET_ERROR(&stream), fields); + LOG_ERROR("Can't decode protobuf reason='%s', pb_msgdesc %p", PB_GET_ERROR(&stream), fields); return false; } else { return true; @@ -59,7 +56,7 @@ bool readcb(pb_istream_t *stream, uint8_t *buf, size_t count) bool writecb(pb_ostream_t *stream, const uint8_t *buf, size_t count) { auto file = (Print *)stream->state; - // LOG_DEBUG("writing %d bytes to protobuf file\n", count); + // LOG_DEBUG("writing %d bytes to protobuf file", count); return file->write(buf, count) == count; } #endif @@ -71,4 +68,4 @@ bool is_in_helper(uint32_t n, const uint32_t *array, pb_size_t count) return true; return false; -} \ No newline at end of file +} diff --git a/src/mesh/mesh-pb-constants.h b/src/mesh/mesh-pb-constants.h index f91c48560..039b36d8d 100644 --- a/src/mesh/mesh-pb-constants.h +++ b/src/mesh/mesh-pb-constants.h @@ -23,6 +23,8 @@ #define MAX_NUM_NODES 100 #endif +#define MAX_NUM_NODES_FS 100 + /// Max number of channels allowed #define MAX_NUM_CHANNELS (member_size(meshtastic_ChannelFile, channels) / member_size(meshtastic_ChannelFile, channels[0])) diff --git a/src/mesh/raspihttp/PiWebServer.cpp b/src/mesh/raspihttp/PiWebServer.cpp index d7e596759..846d70723 100644 --- a/src/mesh/raspihttp/PiWebServer.cpp +++ b/src/mesh/raspihttp/PiWebServer.cpp @@ -178,14 +178,14 @@ int callback_static_file(const struct _u_request *request, struct _u_response *r content_type = u_map_get_case(&configWeb.mime_types, get_filename_ext(file_requested)); if (content_type == NULL) { content_type = u_map_get(&configWeb.mime_types, "*"); - LOG_DEBUG("Static File Server - Unknown mime type for extension %s \n", get_filename_ext(file_requested)); + LOG_DEBUG("Static File Server - Unknown mime type for extension %s ", get_filename_ext(file_requested)); } u_map_put(response->map_header, "Content-Type", content_type); u_map_copy_into(response->map_header, &configWeb.map_header); if (ulfius_set_stream_response(response, 200, callback_static_file_stream, callback_static_file_stream_free, length, STATIC_FILE_CHUNK, f) != U_OK) { - LOG_DEBUG("callback_static_file - Error ulfius_set_stream_response\n "); + LOG_DEBUG("callback_static_file - Error ulfius_set_stream_response"); } } } else { @@ -210,7 +210,7 @@ int callback_static_file(const struct _u_request *request, struct _u_response *r free(real_path); // realpath uses malloc return U_CALLBACK_CONTINUE; } else { - LOG_DEBUG("Static File Server - Error, user_data is NULL or inconsistent\n"); + LOG_DEBUG("Static File Server - Error, user_data is NULL or inconsistent"); return U_CALLBACK_ERROR; } } @@ -223,7 +223,7 @@ static void handleWebResponse() {} */ int handleAPIv1ToRadio(const struct _u_request *req, struct _u_response *res, void *user_data) { - LOG_DEBUG("handleAPIv1ToRadio web -> radio \n"); + LOG_DEBUG("handleAPIv1ToRadio web -> radio "); ulfius_add_header_to_response(res, "Content-Type", "application/x-protobuf"); ulfius_add_header_to_response(res, "Access-Control-Allow-Headers", "Content-Type"); @@ -232,9 +232,9 @@ int handleAPIv1ToRadio(const struct _u_request *req, struct _u_response *res, vo ulfius_add_header_to_response(res, "X-Protobuf-Schema", "https://raw.githubusercontent.com/meshtastic/protobufs/master/meshtastic/mesh.proto"); - if (req->http_verb == "OPTIONS") { + if (strcmp(req->http_verb, "OPTIONS") == 0) { ulfius_set_response_properties(res, U_OPT_STATUS, 204); - return U_CALLBACK_CONTINUE; + return U_CALLBACK_COMPLETE; } byte buffer[MAX_TO_FROM_RADIO_SIZE]; @@ -246,9 +246,9 @@ int handleAPIv1ToRadio(const struct _u_request *req, struct _u_response *res, vo portduinoVFS->mountpoint(configWeb.rootPath); - LOG_DEBUG("Received %d bytes from PUT request\n", s); + LOG_DEBUG("Received %d bytes from PUT request", s); webAPI.handleToRadio(buffer, s); - LOG_DEBUG("end web->radio \n"); + LOG_DEBUG("end web->radio "); return U_CALLBACK_COMPLETE; } @@ -259,7 +259,7 @@ int handleAPIv1ToRadio(const struct _u_request *req, struct _u_response *res, vo int handleAPIv1FromRadio(const struct _u_request *req, struct _u_response *res, void *user_data) { - // LOG_DEBUG("handleAPIv1FromRadio radio -> web\n"); + // LOG_DEBUG("handleAPIv1FromRadio radio -> web"); std::string valueAll; // Status code is 200 OK by default. @@ -269,6 +269,11 @@ int handleAPIv1FromRadio(const struct _u_request *req, struct _u_response *res, ulfius_add_header_to_response(res, "X-Protobuf-Schema", "https://raw.githubusercontent.com/meshtastic/protobufs/master/meshtastic/mesh.proto"); + if (strcmp(req->http_verb, "OPTIONS") == 0) { + ulfius_set_response_properties(res, U_OPT_STATUS, 204); + return U_CALLBACK_COMPLETE; + } + uint8_t txBuf[MAX_STREAM_BUF_SIZE]; uint32_t len = 1; @@ -278,21 +283,21 @@ int handleAPIv1FromRadio(const struct _u_request *req, struct _u_response *res, ulfius_set_response_properties(res, U_OPT_STATUS, 200, U_OPT_BINARY_BODY, txBuf, len); const char *tmpa = (const char *)txBuf; ulfius_set_string_body_response(res, 200, tmpa); - // LOG_DEBUG("\n----webAPI response all:----\n"); + // LOG_DEBUG("\n----webAPI response all:----"); // LOG_DEBUG(tmpa); - // LOG_DEBUG("\n"); + // LOG_DEBUG(""); } // Otherwise, just return one protobuf } else { len = webAPI.getFromRadio(txBuf); const char *tmpa = (const char *)txBuf; ulfius_set_binary_body_response(res, 200, tmpa, len); - // LOG_DEBUG("\n----webAPI response:\n"); + // LOG_DEBUG("\n----webAPI response:"); // LOG_DEBUG(tmpa); - // LOG_DEBUG("\n"); + // LOG_DEBUG(""); } - // LOG_DEBUG("end radio->web\n", len); + // LOG_DEBUG("end radio->web", len); return U_CALLBACK_COMPLETE; } @@ -346,7 +351,7 @@ char *read_file_into_string(const char *filename) { FILE *file = fopen(filename, "rb"); if (file == NULL) { - LOG_ERROR("Error reading File : %s \n", filename); + LOG_ERROR("Error reading File : %s ", filename); return NULL; } @@ -358,7 +363,7 @@ char *read_file_into_string(const char *filename) // reserve mem for file + 1 byte char *buffer = (char *)malloc(filesize + 1); if (buffer == NULL) { - LOG_ERROR("Malloc of mem failed for file : %s \n", filename); + LOG_ERROR("Malloc of mem failed for file : %s ", filename); fclose(file); return NULL; } @@ -366,7 +371,7 @@ char *read_file_into_string(const char *filename) // read content size_t readSize = fread(buffer, 1, filesize, file); if (readSize != filesize) { - LOG_ERROR("Error reading file into buffer\n"); + LOG_ERROR("Error reading file into buffer"); free(buffer); fclose(file); return NULL; @@ -383,13 +388,13 @@ int PiWebServerThread::CheckSSLandLoad() // read certificate cert_pem = read_file_into_string("certificate.pem"); if (cert_pem == NULL) { - LOG_ERROR("ERROR SSL Certificate File can't be loaded or is missing\n"); + LOG_ERROR("ERROR SSL Certificate File can't be loaded or is missing"); return 1; } // read private key key_pem = read_file_into_string("private_key.pem"); if (key_pem == NULL) { - LOG_ERROR("ERROR file private_key can't be loaded or is missing\n"); + LOG_ERROR("ERROR file private_key can't be loaded or is missing"); return 2; } @@ -403,19 +408,19 @@ int PiWebServerThread::CreateSSLCertificate() X509 *x509 = NULL; if (generate_rsa_key(&pkey) != 0) { - LOG_ERROR("Error generating RSA-Key.\n"); + LOG_ERROR("Error generating RSA-Key"); return 1; } if (generate_self_signed_x509(pkey, &x509) != 0) { - LOG_ERROR("Error generating of X509-Certificat.\n"); + LOG_ERROR("Error generating X509-Cert"); return 2; } // Ope file to write private key file FILE *pkey_file = fopen("private_key.pem", "wb"); if (!pkey_file) { - LOG_ERROR("Error opening private key file.\n"); + LOG_ERROR("Error opening private key file"); return 3; } // write private key file @@ -425,7 +430,7 @@ int PiWebServerThread::CreateSSLCertificate() // open Certificate file FILE *x509_file = fopen("certificate.pem", "wb"); if (!x509_file) { - LOG_ERROR("Error opening certificate.\n"); + LOG_ERROR("Error opening cert"); return 4; } // write cirtificate @@ -434,7 +439,7 @@ int PiWebServerThread::CreateSSLCertificate() EVP_PKEY_free(pkey); X509_free(x509); - LOG_INFO("Create SSL Certifictate -certificate.pem- succesfull \n"); + LOG_INFO("Create SSL Cert -certificate.pem- succesfull "); return 0; } @@ -447,24 +452,24 @@ PiWebServerThread::PiWebServerThread() if (CheckSSLandLoad() != 0) { CreateSSLCertificate(); if (CheckSSLandLoad() != 0) { - LOG_ERROR("Major Error Gen & Read SSL Certificate\n"); + LOG_ERROR("Major Error Gen & Read SSL Certificate"); } } if (settingsMap[webserverport] != 0) { webservport = settingsMap[webserverport]; - LOG_INFO("Using webserver port from yaml config. %i \n", webservport); + LOG_INFO("Use webserver port from yaml config %i ", webservport); } else { - LOG_INFO("Webserver port in yaml config set to 0, so defaulting to port 443.\n"); + LOG_INFO("Webserver port in yaml config set to 0, defaulting to port 443"); webservport = 443; } // Web Content Service Instance if (ulfius_init_instance(&instanceWeb, webservport, NULL, DEFAULT_REALM) != U_OK) { - LOG_ERROR("Webserver couldn't be started, abort execution\n"); + LOG_ERROR("Webserver couldn't be started, abort execution"); } else { - LOG_INFO("Webserver started ....\n"); + LOG_INFO("Webserver started"); u_map_init(&configWeb.mime_types); u_map_put(&configWeb.mime_types, "*", "application/octet-stream"); u_map_put(&configWeb.mime_types, ".html", "text/html"); @@ -493,7 +498,9 @@ PiWebServerThread::PiWebServerThread() // Maximum body size sent by the client is 1 Kb instanceWeb.max_post_body_size = 1024; ulfius_add_endpoint_by_val(&instanceWeb, "GET", PREFIX, "/api/v1/fromradio/*", 1, &handleAPIv1FromRadio, NULL); + ulfius_add_endpoint_by_val(&instanceWeb, "OPTIONS", PREFIX, "/api/v1/fromradio/*", 1, &handleAPIv1FromRadio, NULL); ulfius_add_endpoint_by_val(&instanceWeb, "PUT", PREFIX, "/api/v1/toradio/*", 1, &handleAPIv1ToRadio, configWeb.rootPath); + ulfius_add_endpoint_by_val(&instanceWeb, "OPTIONS", PREFIX, "/api/v1/toradio/*", 1, &handleAPIv1ToRadio, NULL); // Add callback function to all endpoints for the Web Server ulfius_add_endpoint_by_val(&instanceWeb, "GET", NULL, "/*", 2, &callback_static_file, &configWeb); @@ -505,10 +512,10 @@ PiWebServerThread::PiWebServerThread() retssl = ulfius_start_secure_framework(&instanceWeb, key_pem, cert_pem); if (retssl == U_OK) { - LOG_INFO("Web Server framework started on port: %i \n", webservport); - LOG_INFO("Web Server root %s\n", (char *)webrootpath.c_str()); + LOG_INFO("Web Server framework started on port: %i ", webservport); + LOG_INFO("Web Server root %s", (char *)webrootpath.c_str()); } else { - LOG_ERROR("Error starting Web Server framework, error number: %d\n", retssl); + LOG_ERROR("Error starting Web Server framework, error number: %d", retssl); } } } @@ -527,4 +534,4 @@ PiWebServerThread::~PiWebServerThread() } #endif -#endif \ No newline at end of file +#endif diff --git a/src/mesh/wifi/WiFiAPClient.cpp b/src/mesh/wifi/WiFiAPClient.cpp index c835878d3..779576d64 100644 --- a/src/mesh/wifi/WiFiAPClient.cpp +++ b/src/mesh/wifi/WiFiAPClient.cpp @@ -20,6 +20,8 @@ #include #include static void WiFiEvent(WiFiEvent_t event); +#elif defined(ARCH_RP2040) +#include #endif #ifndef DISABLE_NTP @@ -57,30 +59,36 @@ static void onNetworkConnected() { if (!APStartupComplete) { // Start web server - LOG_INFO("Starting WiFi network services\n"); + LOG_INFO("Start WiFi network services"); -#ifdef ARCH_ESP32 // start mdns - if (!MDNS.begin("Meshtastic")) { - LOG_ERROR("Error setting up MDNS responder!\n"); + if ( +#ifdef ARCH_RP2040 + !moduleConfig.mqtt.enabled && // MDNS is not supported when MQTT is enabled on ARCH_RP2040 +#endif + !MDNS.begin("Meshtastic")) { + LOG_ERROR("Error setting up MDNS responder!"); } else { - LOG_INFO("mDNS responder started\n"); - LOG_INFO("mDNS Host: Meshtastic.local\n"); + LOG_INFO("mDNS Host: Meshtastic.local"); +#ifdef ARCH_ESP32 MDNS.addService("http", "tcp", 80); MDNS.addService("https", "tcp", 443); - } -#else // ESP32 handles this in WiFiEvent - LOG_INFO("Obtained IP address: %s\n", WiFi.localIP().toString().c_str()); +#elif defined(ARCH_RP2040) + // ARCH_RP2040 does not support HTTPS, create a "meshtastic" service + MDNS.addService("meshtastic", "tcp", 4403); + // ESP32 handles this in WiFiEvent + LOG_INFO("Obtained IP address: %s", WiFi.localIP().toString().c_str()); #endif + } #ifndef DISABLE_NTP - LOG_INFO("Starting NTP time client\n"); + LOG_INFO("Start NTP time client"); timeClient.begin(); timeClient.setUpdateInterval(60 * 60); // Update once an hour #endif if (config.network.rsyslog_server[0]) { - LOG_INFO("Starting Syslog client\n"); + LOG_INFO("Start Syslog client"); // Defaults int serverPort = 514; const char *serverAddr = config.network.rsyslog_server; @@ -129,10 +137,10 @@ static int32_t reconnectWiFi() // Make sure we clear old connection credentials #ifdef ARCH_ESP32 WiFi.disconnect(false, true); -#else +#elif defined(ARCH_RP2040) WiFi.disconnect(false); #endif - LOG_INFO("Reconnecting to WiFi access point %s\n", wifiName); + LOG_INFO("Reconnecting to WiFi access point %s", wifiName); delay(5000); @@ -144,9 +152,9 @@ static int32_t reconnectWiFi() #ifndef DISABLE_NTP if (WiFi.isConnected() && (!Throttle::isWithinTimespanMs(lastrun_ntp, 43200000) || (lastrun_ntp == 0))) { // every 12 hours - LOG_DEBUG("Updating NTP time from %s\n", config.network.ntp_server); + LOG_DEBUG("Update NTP time from %s", config.network.ntp_server); if (timeClient.update()) { - LOG_DEBUG("NTP Request Success - Setting RTCQualityNTP if needed\n"); + LOG_DEBUG("NTP Request Success - Setting RTCQualityNTP if needed"); struct timeval tv; tv.tv_sec = timeClient.getEpochTime(); @@ -155,7 +163,7 @@ static int32_t reconnectWiFi() perhapsSetRTC(RTCQualityNTP, &tv); lastrun_ntp = millis(); } else { - LOG_DEBUG("NTP Update failed\n"); + LOG_DEBUG("NTP Update failed"); } } #endif @@ -188,16 +196,16 @@ bool isWifiAvailable() // Disable WiFi void deinitWifi() { - LOG_INFO("WiFi deinit\n"); + LOG_INFO("WiFi deinit"); if (isWifiAvailable()) { #ifdef ARCH_ESP32 WiFi.disconnect(true, false); -#else +#elif defined(ARCH_RP2040) WiFi.disconnect(true); #endif WiFi.mode(WIFI_OFF); - LOG_INFO("WiFi Turned Off\n"); + LOG_INFO("WiFi Turned Off"); // WiFi.printDiag(Serial); } } @@ -229,15 +237,15 @@ bool initWifi() if (config.network.address_mode == meshtastic_Config_NetworkConfig_AddressMode_STATIC && config.network.ipv4_config.ip != 0) { -#ifndef ARCH_RP2040 +#ifdef ARCH_ESP32 WiFi.config(config.network.ipv4_config.ip, config.network.ipv4_config.gateway, config.network.ipv4_config.subnet, config.network.ipv4_config.dns); -#else +#elif defined(ARCH_RP2040) WiFi.config(config.network.ipv4_config.ip, config.network.ipv4_config.dns, config.network.ipv4_config.gateway, config.network.ipv4_config.subnet); #endif } -#ifndef ARCH_RP2040 +#ifdef ARCH_ESP32 WiFi.onEvent(WiFiEvent); WiFi.setAutoReconnect(true); WiFi.setSleep(false); @@ -247,7 +255,7 @@ bool initWifi() WiFi.onEvent( [](WiFiEvent_t event, WiFiEventInfo_t info) { - LOG_WARN("WiFi lost connection. Reason: %d\n", info.wifi_sta_disconnected.reason); + LOG_WARN("WiFi lost connection. Reason: %d", info.wifi_sta_disconnected.reason); /* If we are disconnected from the AP for some reason, @@ -260,12 +268,12 @@ bool initWifi() }, WiFiEvent_t::ARDUINO_EVENT_WIFI_STA_DISCONNECTED); #endif - LOG_DEBUG("JOINING WIFI soon: ssid=%s\n", wifiName); + LOG_DEBUG("JOINING WIFI soon: ssid=%s", wifiName); wifiReconnect = new Periodic("WifiConnect", reconnectWiFi); } return true; } else { - LOG_INFO("Not using WIFI\n"); + LOG_INFO("Not using WIFI"); return false; } } @@ -278,23 +286,23 @@ static void WiFiEvent(WiFiEvent_t event) switch (event) { case ARDUINO_EVENT_WIFI_READY: - LOG_INFO("WiFi interface ready\n"); + LOG_INFO("WiFi interface ready"); break; case ARDUINO_EVENT_WIFI_SCAN_DONE: - LOG_INFO("Completed scan for access points\n"); + LOG_INFO("Completed scan for access points"); break; case ARDUINO_EVENT_WIFI_STA_START: - LOG_INFO("WiFi station started\n"); + LOG_INFO("WiFi station started"); break; case ARDUINO_EVENT_WIFI_STA_STOP: - LOG_INFO("WiFi station stopped\n"); + LOG_INFO("WiFi station stopped"); syslog.disable(); break; case ARDUINO_EVENT_WIFI_STA_CONNECTED: - LOG_INFO("Connected to access point\n"); + LOG_INFO("Connected to access point"); break; case ARDUINO_EVENT_WIFI_STA_DISCONNECTED: - LOG_INFO("Disconnected from WiFi access point\n"); + LOG_INFO("Disconnected from WiFi access point"); if (!isReconnecting) { WiFi.disconnect(false, true); syslog.disable(); @@ -303,22 +311,22 @@ static void WiFiEvent(WiFiEvent_t event) } break; case ARDUINO_EVENT_WIFI_STA_AUTHMODE_CHANGE: - LOG_INFO("Authentication mode of access point has changed\n"); + LOG_INFO("Authentication mode of access point has changed"); break; case ARDUINO_EVENT_WIFI_STA_GOT_IP: - LOG_INFO("Obtained IP address: %s\n", WiFi.localIP().toString().c_str()); + LOG_INFO("Obtained IP address: %s", WiFi.localIP().toString().c_str()); onNetworkConnected(); break; case ARDUINO_EVENT_WIFI_STA_GOT_IP6: #if ESP_ARDUINO_VERSION >= ESP_ARDUINO_VERSION_VAL(3, 0, 0) - LOG_INFO("Obtained Local IP6 address: %s\n", WiFi.linkLocalIPv6().toString().c_str()); - LOG_INFO("Obtained GlobalIP6 address: %s\n", WiFi.globalIPv6().toString().c_str()); + LOG_INFO("Obtained Local IP6 address: %s", WiFi.linkLocalIPv6().toString().c_str()); + LOG_INFO("Obtained GlobalIP6 address: %s", WiFi.globalIPv6().toString().c_str()); #else - LOG_INFO("Obtained IP6 address: %s\n", WiFi.localIPv6().toString().c_str()); + LOG_INFO("Obtained IP6 address: %s", WiFi.localIPv6().toString().c_str()); #endif break; case ARDUINO_EVENT_WIFI_STA_LOST_IP: - LOG_INFO("Lost IP address and IP address is reset to 0\n"); + LOG_INFO("Lost IP address and IP address is reset to 0"); if (!isReconnecting) { WiFi.disconnect(false, true); syslog.disable(); @@ -327,94 +335,94 @@ static void WiFiEvent(WiFiEvent_t event) } break; case ARDUINO_EVENT_WPS_ER_SUCCESS: - LOG_INFO("WiFi Protected Setup (WPS): succeeded in enrollee mode\n"); + LOG_INFO("WiFi Protected Setup (WPS): succeeded in enrollee mode"); break; case ARDUINO_EVENT_WPS_ER_FAILED: - LOG_INFO("WiFi Protected Setup (WPS): failed in enrollee mode\n"); + LOG_INFO("WiFi Protected Setup (WPS): failed in enrollee mode"); break; case ARDUINO_EVENT_WPS_ER_TIMEOUT: - LOG_INFO("WiFi Protected Setup (WPS): timeout in enrollee mode\n"); + LOG_INFO("WiFi Protected Setup (WPS): timeout in enrollee mode"); break; case ARDUINO_EVENT_WPS_ER_PIN: - LOG_INFO("WiFi Protected Setup (WPS): pin code in enrollee mode\n"); + LOG_INFO("WiFi Protected Setup (WPS): pin code in enrollee mode"); break; case ARDUINO_EVENT_WPS_ER_PBC_OVERLAP: - LOG_INFO("WiFi Protected Setup (WPS): push button overlap in enrollee mode\n"); + LOG_INFO("WiFi Protected Setup (WPS): push button overlap in enrollee mode"); break; case ARDUINO_EVENT_WIFI_AP_START: - LOG_INFO("WiFi access point started\n"); + LOG_INFO("WiFi access point started"); break; case ARDUINO_EVENT_WIFI_AP_STOP: - LOG_INFO("WiFi access point stopped\n"); + LOG_INFO("WiFi access point stopped"); break; case ARDUINO_EVENT_WIFI_AP_STACONNECTED: - LOG_INFO("Client connected\n"); + LOG_INFO("Client connected"); break; case ARDUINO_EVENT_WIFI_AP_STADISCONNECTED: - LOG_INFO("Client disconnected\n"); + LOG_INFO("Client disconnected"); break; case ARDUINO_EVENT_WIFI_AP_STAIPASSIGNED: - LOG_INFO("Assigned IP address to client\n"); + LOG_INFO("Assigned IP address to client"); break; case ARDUINO_EVENT_WIFI_AP_PROBEREQRECVED: - LOG_INFO("Received probe request\n"); + LOG_INFO("Received probe request"); break; case ARDUINO_EVENT_WIFI_AP_GOT_IP6: - LOG_INFO("IPv6 is preferred\n"); + LOG_INFO("IPv6 is preferred"); break; case ARDUINO_EVENT_WIFI_FTM_REPORT: - LOG_INFO("Fast Transition Management report\n"); + LOG_INFO("Fast Transition Management report"); break; case ARDUINO_EVENT_ETH_START: - LOG_INFO("Ethernet started\n"); + LOG_INFO("Ethernet started"); break; case ARDUINO_EVENT_ETH_STOP: - LOG_INFO("Ethernet stopped\n"); + LOG_INFO("Ethernet stopped"); break; case ARDUINO_EVENT_ETH_CONNECTED: - LOG_INFO("Ethernet connected\n"); + LOG_INFO("Ethernet connected"); break; case ARDUINO_EVENT_ETH_DISCONNECTED: - LOG_INFO("Ethernet disconnected\n"); + LOG_INFO("Ethernet disconnected"); break; case ARDUINO_EVENT_ETH_GOT_IP: - LOG_INFO("Obtained IP address (ARDUINO_EVENT_ETH_GOT_IP)\n"); + LOG_INFO("Obtained IP address (ARDUINO_EVENT_ETH_GOT_IP)"); break; case ARDUINO_EVENT_ETH_GOT_IP6: - LOG_INFO("Obtained IP6 address (ARDUINO_EVENT_ETH_GOT_IP6)\n"); + LOG_INFO("Obtained IP6 address (ARDUINO_EVENT_ETH_GOT_IP6)"); break; case ARDUINO_EVENT_SC_SCAN_DONE: - LOG_INFO("SmartConfig: Scan done\n"); + LOG_INFO("SmartConfig: Scan done"); break; case ARDUINO_EVENT_SC_FOUND_CHANNEL: - LOG_INFO("SmartConfig: Found channel\n"); + LOG_INFO("SmartConfig: Found channel"); break; case ARDUINO_EVENT_SC_GOT_SSID_PSWD: - LOG_INFO("SmartConfig: Got SSID and password\n"); + LOG_INFO("SmartConfig: Got SSID and password"); break; case ARDUINO_EVENT_SC_SEND_ACK_DONE: - LOG_INFO("SmartConfig: Send ACK done\n"); + LOG_INFO("SmartConfig: Send ACK done"); break; case ARDUINO_EVENT_PROV_INIT: - LOG_INFO("Provisioning: Init\n"); + LOG_INFO("Provision Init"); break; case ARDUINO_EVENT_PROV_DEINIT: - LOG_INFO("Provisioning: Stopped\n"); + LOG_INFO("Provision Stopped"); break; case ARDUINO_EVENT_PROV_START: - LOG_INFO("Provisioning: Started\n"); + LOG_INFO("Provision Started"); break; case ARDUINO_EVENT_PROV_END: - LOG_INFO("Provisioning: End\n"); + LOG_INFO("Provision End"); break; case ARDUINO_EVENT_PROV_CRED_RECV: - LOG_INFO("Provisioning: Credentials received\n"); + LOG_INFO("Provision Credentials received"); break; case ARDUINO_EVENT_PROV_CRED_FAIL: - LOG_INFO("Provisioning: Credentials failed\n"); + LOG_INFO("Provision Credentials failed"); break; case ARDUINO_EVENT_PROV_CRED_SUCCESS: - LOG_INFO("Provisioning: Credentials success\n"); + LOG_INFO("Provision Credentials success"); break; default: break; diff --git a/src/meshUtils.cpp b/src/meshUtils.cpp index e98a6f1ce..ea2ba641b 100644 --- a/src/meshUtils.cpp +++ b/src/meshUtils.cpp @@ -61,15 +61,13 @@ char *strnstr(const char *s, const char *find, size_t slen) void printBytes(const char *label, const uint8_t *p, size_t numbytes) { int labelSize = strlen(label); - if (labelSize < 100 && numbytes < 64) { - char *messageBuffer = new char[labelSize + (numbytes * 3) + 2]; - strncpy(messageBuffer, label, labelSize); - for (size_t i = 0; i < numbytes; i++) - snprintf(messageBuffer + labelSize + i * 3, 4, " %02x", p[i]); - strcpy(messageBuffer + labelSize + numbytes * 3, "\n"); - LOG_DEBUG(messageBuffer); - delete[] messageBuffer; - } + char *messageBuffer = new char[labelSize + (numbytes * 3) + 2]; + strncpy(messageBuffer, label, labelSize); + for (size_t i = 0; i < numbytes; i++) + snprintf(messageBuffer + labelSize + i * 3, 4, " %02x", p[i]); + strcpy(messageBuffer + labelSize + numbytes * 3, "\n"); + LOG_DEBUG(messageBuffer); + delete[] messageBuffer; } bool memfll(const uint8_t *mem, uint8_t find, size_t numbytes) @@ -94,4 +92,18 @@ bool isOneOf(int item, int count, ...) } va_end(args); return found; +} + +const std::string vformat(const char *const zcFormat, ...) +{ + va_list vaArgs; + va_start(vaArgs, zcFormat); + va_list vaArgsCopy; + va_copy(vaArgsCopy, vaArgs); + const int iLen = std::vsnprintf(NULL, 0, zcFormat, vaArgsCopy); + va_end(vaArgsCopy); + std::vector zc(iLen + 1); + std::vsnprintf(zc.data(), zc.size(), zcFormat, vaArgs); + va_end(vaArgs); + return std::string(zc.data(), iLen); } \ No newline at end of file diff --git a/src/meshUtils.h b/src/meshUtils.h index aff3976f4..47d42b41b 100644 --- a/src/meshUtils.h +++ b/src/meshUtils.h @@ -24,4 +24,6 @@ bool memfll(const uint8_t *mem, uint8_t find, size_t numbytes); bool isOneOf(int item, int count, ...); +const std::string vformat(const char *const zcFormat, ...); + #define IS_ONE_OF(item, ...) isOneOf(item, sizeof((int[]){__VA_ARGS__}) / sizeof(int), __VA_ARGS__) diff --git a/src/modules/AdminModule.cpp b/src/modules/AdminModule.cpp index 79fc67515..c81b6ede4 100644 --- a/src/modules/AdminModule.cpp +++ b/src/modules/AdminModule.cpp @@ -74,15 +74,15 @@ bool AdminModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp, meshta // Could tighten this up further by tracking the last public_key we went an AdminMessage request to // and only allowing responses from that remote. if (messageIsResponse(r)) { - LOG_DEBUG("Allowing admin response message\n"); + LOG_DEBUG("Allow admin response message"); } else if (mp.from == 0) { if (config.security.is_managed) { - LOG_INFO("Ignoring local admin payload because is_managed.\n"); + LOG_INFO("Ignore local admin payload because is_managed"); return handled; } } else if (strcasecmp(ch->settings.name, Channels::adminChannel) == 0) { if (!config.security.admin_channel_enabled) { - LOG_INFO("Ignoring admin channel, as legacy admin is disabled.\n"); + LOG_INFO("Ignore admin channel, legacy admin is disabled"); myReply = allocErrorResponse(meshtastic_Routing_Error_NOT_AUTHORIZED, &mp); return handled; } @@ -93,25 +93,25 @@ bool AdminModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp, meshta memcmp(mp.public_key.bytes, config.security.admin_key[1].bytes, 32) == 0) || (config.security.admin_key[2].size == 32 && memcmp(mp.public_key.bytes, config.security.admin_key[2].bytes, 32) == 0)) { - LOG_INFO("PKC admin payload with authorized sender key.\n"); + LOG_INFO("PKC admin payload with authorized sender key"); } else { myReply = allocErrorResponse(meshtastic_Routing_Error_ADMIN_PUBLIC_KEY_UNAUTHORIZED, &mp); - LOG_INFO("Received PKC admin payload, but the sender public key does not match the admin authorized key!\n"); + LOG_INFO("Received PKC admin payload, but the sender public key does not match the admin authorized key!"); return handled; } } else { - LOG_INFO("Ignoring unauthorized admin payload %i\n", r->which_payload_variant); + LOG_INFO("Ignore unauthorized admin payload %i", r->which_payload_variant); myReply = allocErrorResponse(meshtastic_Routing_Error_NOT_AUTHORIZED, &mp); return handled; } - LOG_INFO("Handling admin payload %i\n", r->which_payload_variant); + LOG_INFO("Handle admin payload %i", r->which_payload_variant); // all of the get and set messages, including those for other modules, flow through here first. // any message that changes state, we want to check the passkey for if (mp.from != 0 && !messageIsRequest(r) && !messageIsResponse(r)) { if (!checkPassKey(r)) { - LOG_WARN("Admin message without session_key!\n"); + LOG_WARN("Admin message without session_key!"); myReply = allocErrorResponse(meshtastic_Routing_Error_ADMIN_BAD_SESSION_KEY, &mp); return handled; } @@ -122,23 +122,23 @@ bool AdminModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp, meshta * Getters */ case meshtastic_AdminMessage_get_owner_request_tag: - LOG_INFO("Client is getting owner\n"); + LOG_INFO("Client got owner"); handleGetOwner(mp); break; case meshtastic_AdminMessage_get_config_request_tag: - LOG_INFO("Client is getting config\n"); + LOG_INFO("Client got config"); handleGetConfig(mp, r->get_config_request); break; case meshtastic_AdminMessage_get_module_config_request_tag: - LOG_INFO("Client is getting module config\n"); + LOG_INFO("Client got module config"); handleGetModuleConfig(mp, r->get_module_config_request); break; case meshtastic_AdminMessage_get_channel_request_tag: { uint32_t i = r->get_channel_request - 1; - LOG_INFO("Client is getting channel %u\n", i); + LOG_INFO("Client got channel %u", i); if (i >= MAX_NUM_CHANNELS) myReply = allocErrorResponse(meshtastic_Routing_Error_BAD_REQUEST, &mp); else @@ -150,29 +150,29 @@ bool AdminModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp, meshta * Setters */ case meshtastic_AdminMessage_set_owner_tag: - LOG_INFO("Client is setting owner\n"); + LOG_INFO("Client set owner"); handleSetOwner(r->set_owner); break; case meshtastic_AdminMessage_set_config_tag: - LOG_INFO("Client is setting the config\n"); + LOG_INFO("Client set config"); handleSetConfig(r->set_config); break; case meshtastic_AdminMessage_set_module_config_tag: - LOG_INFO("Client is setting the module config\n"); + LOG_INFO("Client set module config"); handleSetModuleConfig(r->set_module_config); break; case meshtastic_AdminMessage_set_channel_tag: - LOG_INFO("Client is setting channel %d\n", r->set_channel.index); + LOG_INFO("Client set channel %d", r->set_channel.index); if (r->set_channel.index < 0 || r->set_channel.index >= (int)MAX_NUM_CHANNELS) myReply = allocErrorResponse(meshtastic_Routing_Error_BAD_REQUEST, &mp); else handleSetChannel(r->set_channel); break; case meshtastic_AdminMessage_set_ham_mode_tag: - LOG_INFO("Client is setting ham mode\n"); + LOG_INFO("Client set ham mode"); handleSetHamMode(r->set_ham_mode); break; @@ -187,15 +187,15 @@ bool AdminModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp, meshta int32_t s = r->reboot_ota_seconds; #if defined(ARCH_ESP32) && !MESHTASTIC_EXCLUDE_BLUETOOTH if (BleOta::getOtaAppVersion().isEmpty()) { - LOG_INFO("No OTA firmware available, scheduling regular reboot in %d seconds\n", s); + LOG_INFO("No OTA firmware available, scheduling regular reboot in %d seconds", s); screen->startAlert("Rebooting..."); } else { screen->startFirmwareUpdateScreen(); BleOta::switchToOtaApp(); - LOG_INFO("Rebooting to OTA in %d seconds\n", s); + LOG_INFO("Reboot to OTA in %d seconds", s); } #else - LOG_INFO("Not on ESP32, scheduling regular reboot in %d seconds\n", s); + LOG_INFO("Not on ESP32, scheduling regular reboot in %d seconds", s); screen->startAlert("Rebooting..."); #endif rebootAtMsec = (s < 0) ? 0 : (millis() + s * 1000); @@ -203,56 +203,56 @@ bool AdminModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp, meshta } case meshtastic_AdminMessage_shutdown_seconds_tag: { int32_t s = r->shutdown_seconds; - LOG_INFO("Shutdown in %d seconds\n", s); + LOG_INFO("Shutdown in %d seconds", s); shutdownAtMsec = (s < 0) ? 0 : (millis() + s * 1000); break; } case meshtastic_AdminMessage_get_device_metadata_request_tag: { - LOG_INFO("Client is getting device metadata\n"); + LOG_INFO("Client got device metadata"); handleGetDeviceMetadata(mp); break; } case meshtastic_AdminMessage_factory_reset_config_tag: { disableBluetooth(); - LOG_INFO("Initiating factory config reset\n"); + LOG_INFO("Initiate factory config reset"); nodeDB->factoryReset(); - LOG_INFO("Factory config reset finished, rebooting soon.\n"); + LOG_INFO("Factory config reset finished, rebooting soon"); reboot(DEFAULT_REBOOT_SECONDS); break; } case meshtastic_AdminMessage_factory_reset_device_tag: { disableBluetooth(); - LOG_INFO("Initiating full factory reset\n"); + LOG_INFO("Initiate full factory reset"); nodeDB->factoryReset(true); reboot(DEFAULT_REBOOT_SECONDS); break; } case meshtastic_AdminMessage_nodedb_reset_tag: { disableBluetooth(); - LOG_INFO("Initiating node-db reset\n"); + LOG_INFO("Initiate node-db reset"); nodeDB->resetNodes(); reboot(DEFAULT_REBOOT_SECONDS); break; } case meshtastic_AdminMessage_begin_edit_settings_tag: { - LOG_INFO("Beginning transaction for editing settings\n"); + LOG_INFO("Begin transaction for editing settings"); hasOpenEditTransaction = true; break; } case meshtastic_AdminMessage_commit_edit_settings_tag: { disableBluetooth(); - LOG_INFO("Committing transaction for edited settings\n"); + LOG_INFO("Commit transaction for edited settings"); hasOpenEditTransaction = false; saveChanges(SEGMENT_CONFIG | SEGMENT_MODULECONFIG | SEGMENT_DEVICESTATE | SEGMENT_CHANNELS); break; } case meshtastic_AdminMessage_get_device_connection_status_request_tag: { - LOG_INFO("Client is getting device connection status\n"); + LOG_INFO("Client got device connection status"); handleGetDeviceConnectionStatus(mp); break; } case meshtastic_AdminMessage_get_module_config_response_tag: { - LOG_INFO("Client is receiving a get_module_config response.\n"); + LOG_INFO("Client received a get_module_config response"); if (fromOthers && r->get_module_config_response.which_payload_variant == meshtastic_AdminMessage_ModuleConfigType_REMOTEHARDWARE_CONFIG) { handleGetModuleConfigResponse(mp, r); @@ -260,13 +260,13 @@ bool AdminModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp, meshta break; } case meshtastic_AdminMessage_remove_by_nodenum_tag: { - LOG_INFO("Client is receiving a remove_nodenum command.\n"); + LOG_INFO("Client received remove_nodenum command"); nodeDB->removeNodeByNum(r->remove_by_nodenum); this->notifyObservers(r); // Observed by screen break; } case meshtastic_AdminMessage_set_favorite_node_tag: { - LOG_INFO("Client is receiving a set_favorite_node command.\n"); + LOG_INFO("Client received set_favorite_node command"); meshtastic_NodeInfoLite *node = nodeDB->getMeshNode(r->set_favorite_node); if (node != NULL) { node->is_favorite = true; @@ -275,7 +275,7 @@ bool AdminModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp, meshta break; } case meshtastic_AdminMessage_remove_favorite_node_tag: { - LOG_INFO("Client is receiving a remove_favorite_node command.\n"); + LOG_INFO("Client received remove_favorite_node command"); meshtastic_NodeInfoLite *node = nodeDB->getMeshNode(r->remove_favorite_node); if (node != NULL) { node->is_favorite = false; @@ -283,8 +283,30 @@ bool AdminModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp, meshta } break; } + case meshtastic_AdminMessage_set_ignored_node_tag: { + LOG_INFO("Client received set_ignored_node command"); + meshtastic_NodeInfoLite *node = nodeDB->getMeshNode(r->set_ignored_node); + if (node != NULL) { + node->is_ignored = true; + node->has_device_metrics = false; + node->has_position = false; + node->user.public_key.size = 0; + node->user.public_key.bytes[0] = 0; + saveChanges(SEGMENT_DEVICESTATE, false); + } + break; + } + case meshtastic_AdminMessage_remove_ignored_node_tag: { + LOG_INFO("Client received remove_ignored_node command"); + meshtastic_NodeInfoLite *node = nodeDB->getMeshNode(r->remove_ignored_node); + if (node != NULL) { + node->is_ignored = false; + saveChanges(SEGMENT_DEVICESTATE, false); + } + break; + } case meshtastic_AdminMessage_set_fixed_position_tag: { - LOG_INFO("Client is receiving a set_fixed_position command.\n"); + LOG_INFO("Client received set_fixed_position command"); meshtastic_NodeInfoLite *node = nodeDB->getMeshNode(nodeDB->getNodeNum()); node->has_position = true; node->position = TypeConversions::ConvertToPositionLite(r->set_fixed_position); @@ -300,14 +322,14 @@ bool AdminModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp, meshta break; } case meshtastic_AdminMessage_remove_fixed_position_tag: { - LOG_INFO("Client is receiving a remove_fixed_position command.\n"); + LOG_INFO("Client received remove_fixed_position command"); nodeDB->clearLocalPosition(); config.position.fixed_position = false; saveChanges(SEGMENT_DEVICESTATE | SEGMENT_CONFIG, false); break; } case meshtastic_AdminMessage_set_time_only_tag: { - LOG_INFO("Client is receiving a set_time_only command.\n"); + LOG_INFO("Client received set_time_only command"); struct timeval tv; tv.tv_sec = r->set_time_only; tv.tv_usec = 0; @@ -316,26 +338,26 @@ bool AdminModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp, meshta break; } case meshtastic_AdminMessage_enter_dfu_mode_request_tag: { - LOG_INFO("Client is requesting to enter DFU mode.\n"); + LOG_INFO("Client requesting to enter DFU mode"); #if defined(ARCH_NRF52) || defined(ARCH_RP2040) enterDfuMode(); #endif break; } case meshtastic_AdminMessage_delete_file_request_tag: { - LOG_DEBUG("Client is requesting to delete file: %s\n", r->delete_file_request); + LOG_DEBUG("Client requesting to delete file: %s", r->delete_file_request); #ifdef FSCom if (FSCom.remove(r->delete_file_request)) { - LOG_DEBUG("Successfully deleted file\n"); + LOG_DEBUG("Successfully deleted file"); } else { - LOG_DEBUG("Failed to delete file\n"); + LOG_DEBUG("Failed to delete file"); } #endif break; } #ifdef ARCH_PORTDUINO case meshtastic_AdminMessage_exit_simulator_tag: - LOG_INFO("Exiting simulator\n"); + LOG_INFO("Exiting simulator"); _exit(0); break; #endif @@ -348,10 +370,10 @@ bool AdminModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp, meshta setPassKey(&res); myReply = allocDataProtobuf(res); } else if (mp.decoded.want_response) { - LOG_DEBUG("We did not responded to a request that wanted a respond. req.variant=%d\n", r->which_payload_variant); + LOG_DEBUG("Did not responded to a request that wanted a respond. req.variant=%d", r->which_payload_variant); } else if (handleResult != AdminMessageHandleResult::HANDLED) { // Probably a message sent by us or sent to our local node. FIXME, we should avoid scanning these messages - LOG_INFO("Ignoring nonrelevant admin %d\n", r->which_payload_variant); + LOG_INFO("Ignore irrelevant admin %d", r->which_payload_variant); } break; } @@ -369,7 +391,7 @@ void AdminModule::handleGetModuleConfigResponse(const meshtastic_MeshPacket &mp, // Skip if it's disabled or no pins are exposed if (!r->get_module_config_response.payload_variant.remote_hardware.enabled || r->get_module_config_response.payload_variant.remote_hardware.available_pins_count == 0) { - LOG_DEBUG("Remote hardware module disabled or no available_pins. Skipping...\n"); + LOG_DEBUG("Remote hardware module disabled or no available_pins. Skip"); return; } for (uint8_t i = 0; i < devicestate.node_remote_hardware_pins_count; i++) { @@ -427,7 +449,7 @@ void AdminModule::handleSetConfig(const meshtastic_Config &c) switch (c.which_payload_variant) { case meshtastic_Config_device_tag: - LOG_INFO("Setting config: Device\n"); + LOG_INFO("Set config: Device"); config.has_device = true; #if !defined(ARCH_PORTDUINO) && !defined(ARCH_STM32WL) && !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR if (config.device.double_tap_as_button_press == false && c.payload_variant.device.double_tap_as_button_press == true && @@ -463,7 +485,7 @@ void AdminModule::handleSetConfig(const meshtastic_Config &c) if (existingRole != c.payload_variant.device.role) nodeDB->installRoleDefaults(c.payload_variant.device.role); if (config.device.node_info_broadcast_secs < min_node_info_broadcast_secs) { - LOG_DEBUG("Tried to set node_info_broadcast_secs too low, setting to %d\n", min_node_info_broadcast_secs); + LOG_DEBUG("Tried to set node_info_broadcast_secs too low, setting to %d", min_node_info_broadcast_secs); config.device.node_info_broadcast_secs = min_node_info_broadcast_secs; } // Router Client is deprecated; Set it to client @@ -484,14 +506,14 @@ void AdminModule::handleSetConfig(const meshtastic_Config &c) #endif break; case meshtastic_Config_position_tag: - LOG_INFO("Setting config: Position\n"); + LOG_INFO("Set config: Position"); config.has_position = true; config.position = c.payload_variant.position; // Save nodedb as well in case we got a fixed position packet saveChanges(SEGMENT_DEVICESTATE, false); break; case meshtastic_Config_power_tag: - LOG_INFO("Setting config: Power\n"); + LOG_INFO("Set config: Power"); config.has_power = true; // Really just the adc override is the only thing that can change without a reboot if (config.power.device_battery_ina_address == c.payload_variant.power.device_battery_ina_address && @@ -504,14 +526,19 @@ void AdminModule::handleSetConfig(const meshtastic_Config &c) requiresReboot = false; } config.power = c.payload_variant.power; + if (c.payload_variant.power.on_battery_shutdown_after_secs > 0 && + c.payload_variant.power.on_battery_shutdown_after_secs < 30) { + LOG_WARN("Tried to set on_battery_shutdown_after_secs too low, set to min 30 seconds"); + config.power.on_battery_shutdown_after_secs = 30; + } break; case meshtastic_Config_network_tag: - LOG_INFO("Setting config: WiFi\n"); + LOG_INFO("Set config: WiFi"); config.has_network = true; config.network = c.payload_variant.network; break; case meshtastic_Config_display_tag: - LOG_INFO("Setting config: Display\n"); + LOG_INFO("Set config: Display"); config.has_display = true; if (config.display.screen_on_secs == c.payload_variant.display.screen_on_secs && config.display.flip_screen == c.payload_variant.display.flip_screen && @@ -529,7 +556,7 @@ void AdminModule::handleSetConfig(const meshtastic_Config &c) config.display = c.payload_variant.display; break; case meshtastic_Config_lora_tag: - LOG_INFO("Setting config: LoRa\n"); + LOG_INFO("Set config: LoRa"); config.has_lora = true; // If no lora radio parameters change, don't need to reboot if (config.lora.use_preset == c.payload_variant.lora.use_preset && config.lora.region == c.payload_variant.lora.region && @@ -568,12 +595,12 @@ void AdminModule::handleSetConfig(const meshtastic_Config &c) } break; case meshtastic_Config_bluetooth_tag: - LOG_INFO("Setting config: Bluetooth\n"); + LOG_INFO("Set config: Bluetooth"); config.has_bluetooth = true; config.bluetooth = c.payload_variant.bluetooth; break; case meshtastic_Config_security_tag: - LOG_INFO("Setting config: Security\n"); + LOG_INFO("Set config: Security"); config.security = c.payload_variant.security; #if !(MESHTASTIC_EXCLUDE_PKI_KEYGEN) && !(MESHTASTIC_EXCLUDE_PKI) // We check for a potentially valid private key, and a blank public key, and regen the public key if needed. @@ -608,71 +635,71 @@ void AdminModule::handleSetModuleConfig(const meshtastic_ModuleConfig &c) disableBluetooth(); switch (c.which_payload_variant) { case meshtastic_ModuleConfig_mqtt_tag: - LOG_INFO("Setting module config: MQTT\n"); + LOG_INFO("Set module config: MQTT"); moduleConfig.has_mqtt = true; moduleConfig.mqtt = c.payload_variant.mqtt; break; case meshtastic_ModuleConfig_serial_tag: - LOG_INFO("Setting module config: Serial\n"); + LOG_INFO("Set module config: Serial"); moduleConfig.has_serial = true; moduleConfig.serial = c.payload_variant.serial; break; case meshtastic_ModuleConfig_external_notification_tag: - LOG_INFO("Setting module config: External Notification\n"); + LOG_INFO("Set module config: External Notification"); moduleConfig.has_external_notification = true; moduleConfig.external_notification = c.payload_variant.external_notification; break; case meshtastic_ModuleConfig_store_forward_tag: - LOG_INFO("Setting module config: Store & Forward\n"); + LOG_INFO("Set module config: Store & Forward"); moduleConfig.has_store_forward = true; moduleConfig.store_forward = c.payload_variant.store_forward; break; case meshtastic_ModuleConfig_range_test_tag: - LOG_INFO("Setting module config: Range Test\n"); + LOG_INFO("Set module config: Range Test"); moduleConfig.has_range_test = true; moduleConfig.range_test = c.payload_variant.range_test; break; case meshtastic_ModuleConfig_telemetry_tag: - LOG_INFO("Setting module config: Telemetry\n"); + LOG_INFO("Set module config: Telemetry"); moduleConfig.has_telemetry = true; moduleConfig.telemetry = c.payload_variant.telemetry; break; case meshtastic_ModuleConfig_canned_message_tag: - LOG_INFO("Setting module config: Canned Message\n"); + LOG_INFO("Set module config: Canned Message"); moduleConfig.has_canned_message = true; moduleConfig.canned_message = c.payload_variant.canned_message; break; case meshtastic_ModuleConfig_audio_tag: - LOG_INFO("Setting module config: Audio\n"); + LOG_INFO("Set module config: Audio"); moduleConfig.has_audio = true; moduleConfig.audio = c.payload_variant.audio; break; case meshtastic_ModuleConfig_remote_hardware_tag: - LOG_INFO("Setting module config: Remote Hardware\n"); + LOG_INFO("Set module config: Remote Hardware"); moduleConfig.has_remote_hardware = true; moduleConfig.remote_hardware = c.payload_variant.remote_hardware; break; case meshtastic_ModuleConfig_neighbor_info_tag: - LOG_INFO("Setting module config: Neighbor Info\n"); + LOG_INFO("Set module config: Neighbor Info"); moduleConfig.has_neighbor_info = true; if (moduleConfig.neighbor_info.update_interval < min_neighbor_info_broadcast_secs) { - LOG_DEBUG("Tried to set update_interval too low, setting to %d\n", default_neighbor_info_broadcast_secs); + LOG_DEBUG("Tried to set update_interval too low, setting to %d", default_neighbor_info_broadcast_secs); moduleConfig.neighbor_info.update_interval = default_neighbor_info_broadcast_secs; } moduleConfig.neighbor_info = c.payload_variant.neighbor_info; break; case meshtastic_ModuleConfig_detection_sensor_tag: - LOG_INFO("Setting module config: Detection Sensor\n"); + LOG_INFO("Set module config: Detection Sensor"); moduleConfig.has_detection_sensor = true; moduleConfig.detection_sensor = c.payload_variant.detection_sensor; break; case meshtastic_ModuleConfig_ambient_lighting_tag: - LOG_INFO("Setting module config: Ambient Lighting\n"); + LOG_INFO("Set module config: Ambient Lighting"); moduleConfig.has_ambient_lighting = true; moduleConfig.ambient_lighting = c.payload_variant.ambient_lighting; break; case meshtastic_ModuleConfig_paxcounter_tag: - LOG_INFO("Setting module config: Paxcounter\n"); + LOG_INFO("Set module config: Paxcounter"); moduleConfig.has_paxcounter = true; moduleConfig.paxcounter = c.payload_variant.paxcounter; break; @@ -711,49 +738,49 @@ void AdminModule::handleGetConfig(const meshtastic_MeshPacket &req, const uint32 if (req.decoded.want_response) { switch (configType) { case meshtastic_AdminMessage_ConfigType_DEVICE_CONFIG: - LOG_INFO("Getting config: Device\n"); + LOG_INFO("Get config: Device"); res.get_config_response.which_payload_variant = meshtastic_Config_device_tag; res.get_config_response.payload_variant.device = config.device; break; case meshtastic_AdminMessage_ConfigType_POSITION_CONFIG: - LOG_INFO("Getting config: Position\n"); + LOG_INFO("Get config: Position"); res.get_config_response.which_payload_variant = meshtastic_Config_position_tag; res.get_config_response.payload_variant.position = config.position; break; case meshtastic_AdminMessage_ConfigType_POWER_CONFIG: - LOG_INFO("Getting config: Power\n"); + LOG_INFO("Get config: Power"); res.get_config_response.which_payload_variant = meshtastic_Config_power_tag; res.get_config_response.payload_variant.power = config.power; break; case meshtastic_AdminMessage_ConfigType_NETWORK_CONFIG: - LOG_INFO("Getting config: Network\n"); + LOG_INFO("Get config: Network"); res.get_config_response.which_payload_variant = meshtastic_Config_network_tag; res.get_config_response.payload_variant.network = config.network; writeSecret(res.get_config_response.payload_variant.network.wifi_psk, sizeof(res.get_config_response.payload_variant.network.wifi_psk), config.network.wifi_psk); break; case meshtastic_AdminMessage_ConfigType_DISPLAY_CONFIG: - LOG_INFO("Getting config: Display\n"); + LOG_INFO("Get config: Display"); res.get_config_response.which_payload_variant = meshtastic_Config_display_tag; res.get_config_response.payload_variant.display = config.display; break; case meshtastic_AdminMessage_ConfigType_LORA_CONFIG: - LOG_INFO("Getting config: LoRa\n"); + LOG_INFO("Get config: LoRa"); res.get_config_response.which_payload_variant = meshtastic_Config_lora_tag; res.get_config_response.payload_variant.lora = config.lora; break; case meshtastic_AdminMessage_ConfigType_BLUETOOTH_CONFIG: - LOG_INFO("Getting config: Bluetooth\n"); + LOG_INFO("Get config: Bluetooth"); res.get_config_response.which_payload_variant = meshtastic_Config_bluetooth_tag; res.get_config_response.payload_variant.bluetooth = config.bluetooth; break; case meshtastic_AdminMessage_ConfigType_SECURITY_CONFIG: - LOG_INFO("Getting config: Security\n"); + LOG_INFO("Get config: Security"); res.get_config_response.which_payload_variant = meshtastic_Config_security_tag; res.get_config_response.payload_variant.security = config.security; break; case meshtastic_AdminMessage_ConfigType_SESSIONKEY_CONFIG: - LOG_INFO("Getting config: Sessionkey\n"); + LOG_INFO("Get config: Sessionkey"); res.get_config_response.which_payload_variant = meshtastic_Config_sessionkey_tag; break; } @@ -777,67 +804,67 @@ void AdminModule::handleGetModuleConfig(const meshtastic_MeshPacket &req, const if (req.decoded.want_response) { switch (configType) { case meshtastic_AdminMessage_ModuleConfigType_MQTT_CONFIG: - LOG_INFO("Getting module config: MQTT\n"); + LOG_INFO("Get module config: MQTT"); res.get_module_config_response.which_payload_variant = meshtastic_ModuleConfig_mqtt_tag; res.get_module_config_response.payload_variant.mqtt = moduleConfig.mqtt; break; case meshtastic_AdminMessage_ModuleConfigType_SERIAL_CONFIG: - LOG_INFO("Getting module config: Serial\n"); + LOG_INFO("Get module config: Serial"); res.get_module_config_response.which_payload_variant = meshtastic_ModuleConfig_serial_tag; res.get_module_config_response.payload_variant.serial = moduleConfig.serial; break; case meshtastic_AdminMessage_ModuleConfigType_EXTNOTIF_CONFIG: - LOG_INFO("Getting module config: External Notification\n"); + LOG_INFO("Get module config: External Notification"); res.get_module_config_response.which_payload_variant = meshtastic_ModuleConfig_external_notification_tag; res.get_module_config_response.payload_variant.external_notification = moduleConfig.external_notification; break; case meshtastic_AdminMessage_ModuleConfigType_STOREFORWARD_CONFIG: - LOG_INFO("Getting module config: Store & Forward\n"); + LOG_INFO("Get module config: Store & Forward"); res.get_module_config_response.which_payload_variant = meshtastic_ModuleConfig_store_forward_tag; res.get_module_config_response.payload_variant.store_forward = moduleConfig.store_forward; break; case meshtastic_AdminMessage_ModuleConfigType_RANGETEST_CONFIG: - LOG_INFO("Getting module config: Range Test\n"); + LOG_INFO("Get module config: Range Test"); res.get_module_config_response.which_payload_variant = meshtastic_ModuleConfig_range_test_tag; res.get_module_config_response.payload_variant.range_test = moduleConfig.range_test; break; case meshtastic_AdminMessage_ModuleConfigType_TELEMETRY_CONFIG: - LOG_INFO("Getting module config: Telemetry\n"); + LOG_INFO("Get module config: Telemetry"); res.get_module_config_response.which_payload_variant = meshtastic_ModuleConfig_telemetry_tag; res.get_module_config_response.payload_variant.telemetry = moduleConfig.telemetry; break; case meshtastic_AdminMessage_ModuleConfigType_CANNEDMSG_CONFIG: - LOG_INFO("Getting module config: Canned Message\n"); + LOG_INFO("Get module config: Canned Message"); res.get_module_config_response.which_payload_variant = meshtastic_ModuleConfig_canned_message_tag; res.get_module_config_response.payload_variant.canned_message = moduleConfig.canned_message; break; case meshtastic_AdminMessage_ModuleConfigType_AUDIO_CONFIG: - LOG_INFO("Getting module config: Audio\n"); + LOG_INFO("Get module config: Audio"); res.get_module_config_response.which_payload_variant = meshtastic_ModuleConfig_audio_tag; res.get_module_config_response.payload_variant.audio = moduleConfig.audio; break; case meshtastic_AdminMessage_ModuleConfigType_REMOTEHARDWARE_CONFIG: - LOG_INFO("Getting module config: Remote Hardware\n"); + LOG_INFO("Get module config: Remote Hardware"); res.get_module_config_response.which_payload_variant = meshtastic_ModuleConfig_remote_hardware_tag; res.get_module_config_response.payload_variant.remote_hardware = moduleConfig.remote_hardware; break; case meshtastic_AdminMessage_ModuleConfigType_NEIGHBORINFO_CONFIG: - LOG_INFO("Getting module config: Neighbor Info\n"); + LOG_INFO("Get module config: Neighbor Info"); res.get_module_config_response.which_payload_variant = meshtastic_ModuleConfig_neighbor_info_tag; res.get_module_config_response.payload_variant.neighbor_info = moduleConfig.neighbor_info; break; case meshtastic_AdminMessage_ModuleConfigType_DETECTIONSENSOR_CONFIG: - LOG_INFO("Getting module config: Detection Sensor\n"); + LOG_INFO("Get module config: Detection Sensor"); res.get_module_config_response.which_payload_variant = meshtastic_ModuleConfig_detection_sensor_tag; res.get_module_config_response.payload_variant.detection_sensor = moduleConfig.detection_sensor; break; case meshtastic_AdminMessage_ModuleConfigType_AMBIENTLIGHTING_CONFIG: - LOG_INFO("Getting module config: Ambient Lighting\n"); + LOG_INFO("Get module config: Ambient Lighting"); res.get_module_config_response.which_payload_variant = meshtastic_ModuleConfig_ambient_lighting_tag; res.get_module_config_response.payload_variant.ambient_lighting = moduleConfig.ambient_lighting; break; case meshtastic_AdminMessage_ModuleConfigType_PAXCOUNTER_CONFIG: - LOG_INFO("Getting module config: Paxcounter\n"); + LOG_INFO("Get module config: Paxcounter"); res.get_module_config_response.which_payload_variant = meshtastic_ModuleConfig_paxcounter_tag; res.get_module_config_response.payload_variant.paxcounter = moduleConfig.paxcounter; break; @@ -900,7 +927,7 @@ void AdminModule::handleGetDeviceConnectionStatus(const meshtastic_MeshPacket &r #ifdef ARCH_PORTDUINO conn.wifi.status.is_connected = true; #else - conn.wifi.status.is_connected = WiFi.status() != WL_CONNECTED; + conn.wifi.status.is_connected = WiFi.status() == WL_CONNECTED; #endif strncpy(conn.wifi.ssid, config.network.wifi_ssid, 33); if (conn.wifi.status.is_connected) { @@ -932,10 +959,14 @@ void AdminModule::handleGetDeviceConnectionStatus(const meshtastic_MeshPacket &r conn.has_bluetooth = true; conn.bluetooth.pin = config.bluetooth.fixed_pin; #ifdef ARCH_ESP32 - conn.bluetooth.is_connected = nimbleBluetooth->isConnected(); - conn.bluetooth.rssi = nimbleBluetooth->getRssi(); + if (config.bluetooth.enabled && nimbleBluetooth) { + conn.bluetooth.is_connected = nimbleBluetooth->isConnected(); + conn.bluetooth.rssi = nimbleBluetooth->getRssi(); + } #elif defined(ARCH_NRF52) - conn.bluetooth.is_connected = nrf52Bluetooth->isConnected(); + if (config.bluetooth.enabled && nrf52Bluetooth) { + conn.bluetooth.is_connected = nrf52Bluetooth->isConnected(); + } #endif #endif conn.has_serial = true; // No serial-less devices @@ -966,7 +997,7 @@ void AdminModule::handleGetChannel(const meshtastic_MeshPacket &req, uint32_t ch void AdminModule::reboot(int32_t seconds) { - LOG_INFO("Rebooting in %d seconds\n", seconds); + LOG_INFO("Reboot in %d seconds", seconds); screen->startAlert("Rebooting..."); rebootAtMsec = (seconds < 0) ? 0 : (millis() + seconds * 1000); } @@ -974,10 +1005,10 @@ void AdminModule::reboot(int32_t seconds) void AdminModule::saveChanges(int saveWhat, bool shouldReboot) { if (!hasOpenEditTransaction) { - LOG_INFO("Saving changes to disk\n"); + LOG_INFO("Save changes to disk"); service->reloadConfig(saveWhat); // Calls saveToDisk among other things } else { - LOG_INFO("Delaying save of changes to disk until the open transaction is committed\n"); + LOG_INFO("Delay save of changes to disk until the open transaction is committed"); } if (shouldReboot && !hasOpenEditTransaction) { reboot(DEFAULT_REBOOT_SECONDS); @@ -1026,7 +1057,7 @@ void AdminModule::setPassKey(meshtastic_AdminMessage *res) } memcpy(res->session_passkey.bytes, session_passkey, 8); res->session_passkey.size = 8; - printBytes("Setting admin key to ", res->session_passkey.bytes, 8); + printBytes("Set admin key to ", res->session_passkey.bytes, 8); // if halfway to session_expire, regenerate session_passkey, reset the timeout // set the key in the packet } @@ -1092,4 +1123,4 @@ void disableBluetooth() nrf52Bluetooth->shutdown(); #endif #endif -} \ No newline at end of file +} diff --git a/src/modules/AdminModule.h b/src/modules/AdminModule.h index e54b89af1..b99e86707 100644 --- a/src/modules/AdminModule.h +++ b/src/modules/AdminModule.h @@ -1,3 +1,5 @@ +#include + #pragma once #include "ProtobufModule.h" #if HAS_WIFI diff --git a/src/modules/AtakPluginModule.cpp b/src/modules/AtakPluginModule.cpp index 72d069619..d242e5501 100644 --- a/src/modules/AtakPluginModule.cpp +++ b/src/modules/AtakPluginModule.cpp @@ -11,7 +11,7 @@ AtakPluginModule *atakPluginModule; AtakPluginModule::AtakPluginModule() - : ProtobufModule("atak", meshtastic_PortNum_ATAK_PLUGIN, &meshtastic_TAKPacket_msg), concurrency::OSThread("AtakPluginModule") + : ProtobufModule("atak", meshtastic_PortNum_ATAK_PLUGIN, &meshtastic_TAKPacket_msg), concurrency::OSThread("AtakPlugin") { ourPortNum = meshtastic_PortNum_ATAK_PLUGIN; } @@ -65,7 +65,7 @@ void AtakPluginModule::alterReceivedProtobuf(meshtastic_MeshPacket &mp, meshtast { // From Phone (EUD) if (mp.from == 0) { - LOG_DEBUG("Received uncompressed TAK payload from phone: %d bytes\n", mp.decoded.payload.size); + LOG_DEBUG("Received uncompressed TAK payload from phone: %d bytes", mp.decoded.payload.size); // Compress for LoRA transport auto compressed = cloneTAKPacketData(t); compressed.is_compressed = true; @@ -73,28 +73,28 @@ void AtakPluginModule::alterReceivedProtobuf(meshtastic_MeshPacket &mp, meshtast auto length = unishox2_compress_lines(t->contact.callsign, strlen(t->contact.callsign), compressed.contact.callsign, sizeof(compressed.contact.callsign) - 1, USX_PSET_DFLT, NULL); if (length < 0) { - LOG_WARN("Compression overflowed contact.callsign. Reverting to uncompressed packet\n"); + LOG_WARN("Compress overflow contact.callsign. Revert to uncompressed packet"); return; } - LOG_DEBUG("Compressed callsign: %d bytes\n", length); + LOG_DEBUG("Compressed callsign: %d bytes", length); length = unishox2_compress_lines(t->contact.device_callsign, strlen(t->contact.device_callsign), compressed.contact.device_callsign, sizeof(compressed.contact.device_callsign) - 1, USX_PSET_DFLT, NULL); if (length < 0) { - LOG_WARN("Compression overflowed contact.device_callsign. Reverting to uncompressed packet\n"); + LOG_WARN("Compress overflow contact.device_callsign. Revert to uncompressed packet"); return; } - LOG_DEBUG("Compressed device_callsign: %d bytes\n", length); + LOG_DEBUG("Compressed device_callsign: %d bytes", length); } if (t->which_payload_variant == meshtastic_TAKPacket_chat_tag) { auto length = unishox2_compress_lines(t->payload_variant.chat.message, strlen(t->payload_variant.chat.message), compressed.payload_variant.chat.message, sizeof(compressed.payload_variant.chat.message) - 1, USX_PSET_DFLT, NULL); if (length < 0) { - LOG_WARN("Compression overflowed chat.message. Reverting to uncompressed packet\n"); + LOG_WARN("Compress overflow chat.message. Revert to uncompressed packet"); return; } - LOG_DEBUG("Compressed chat message: %d bytes\n", length); + LOG_DEBUG("Compressed chat message: %d bytes", length); if (t->payload_variant.chat.has_to) { compressed.payload_variant.chat.has_to = true; @@ -102,10 +102,10 @@ void AtakPluginModule::alterReceivedProtobuf(meshtastic_MeshPacket &mp, meshtast compressed.payload_variant.chat.to, sizeof(compressed.payload_variant.chat.to) - 1, USX_PSET_DFLT, NULL); if (length < 0) { - LOG_WARN("Compression overflowed chat.to. Reverting to uncompressed packet\n"); + LOG_WARN("Compress overflow chat.to. Revert to uncompressed packet"); return; } - LOG_DEBUG("Compressed chat to: %d bytes\n", length); + LOG_DEBUG("Compressed chat to: %d bytes", length); } if (t->payload_variant.chat.has_to_callsign) { @@ -114,19 +114,19 @@ void AtakPluginModule::alterReceivedProtobuf(meshtastic_MeshPacket &mp, meshtast compressed.payload_variant.chat.to_callsign, sizeof(compressed.payload_variant.chat.to_callsign) - 1, USX_PSET_DFLT, NULL); if (length < 0) { - LOG_WARN("Compression overflowed chat.to_callsign. Reverting to uncompressed packet\n"); + LOG_WARN("Compress overflow chat.to_callsign. Revert to uncompressed packet"); return; } - LOG_DEBUG("Compressed chat to_callsign: %d bytes\n", length); + LOG_DEBUG("Compressed chat to_callsign: %d bytes", length); } } mp.decoded.payload.size = pb_encode_to_bytes(mp.decoded.payload.bytes, sizeof(mp.decoded.payload.bytes), meshtastic_TAKPacket_fields, &compressed); - LOG_DEBUG("Final payload: %d bytes\n", mp.decoded.payload.size); + LOG_DEBUG("Final payload: %d bytes", mp.decoded.payload.size); } else { if (!t->is_compressed) { // Not compressed. Something is wrong - LOG_WARN("Received uncompressed TAKPacket over radio! Skipping\n"); + LOG_WARN("Received uncompressed TAKPacket over radio! Skip"); return; } @@ -139,29 +139,29 @@ void AtakPluginModule::alterReceivedProtobuf(meshtastic_MeshPacket &mp, meshtast unishox2_decompress_lines(t->contact.callsign, strlen(t->contact.callsign), uncompressed.contact.callsign, sizeof(uncompressed.contact.callsign) - 1, USX_PSET_DFLT, NULL); if (length < 0) { - LOG_WARN("Decompression overflowed contact.callsign. Bailing out\n"); + LOG_WARN("Decompress overflow contact.callsign. Bailing out"); return; } - LOG_DEBUG("Decompressed callsign: %d bytes\n", length); + LOG_DEBUG("Decompressed callsign: %d bytes", length); length = unishox2_decompress_lines(t->contact.device_callsign, strlen(t->contact.device_callsign), uncompressed.contact.device_callsign, sizeof(uncompressed.contact.device_callsign) - 1, USX_PSET_DFLT, NULL); if (length < 0) { - LOG_WARN("Decompression overflowed contact.device_callsign. Bailing out\n"); + LOG_WARN("Decompress overflow contact.device_callsign. Bailing out"); return; } - LOG_DEBUG("Decompressed device_callsign: %d bytes\n", length); + LOG_DEBUG("Decompressed device_callsign: %d bytes", length); } if (uncompressed.which_payload_variant == meshtastic_TAKPacket_chat_tag) { auto length = unishox2_decompress_lines(t->payload_variant.chat.message, strlen(t->payload_variant.chat.message), uncompressed.payload_variant.chat.message, sizeof(uncompressed.payload_variant.chat.message) - 1, USX_PSET_DFLT, NULL); if (length < 0) { - LOG_WARN("Decompression overflowed chat.message. Bailing out\n"); + LOG_WARN("Decompress overflow chat.message. Bailing out"); return; } - LOG_DEBUG("Decompressed chat message: %d bytes\n", length); + LOG_DEBUG("Decompressed chat message: %d bytes", length); if (t->payload_variant.chat.has_to) { uncompressed.payload_variant.chat.has_to = true; @@ -169,10 +169,10 @@ void AtakPluginModule::alterReceivedProtobuf(meshtastic_MeshPacket &mp, meshtast uncompressed.payload_variant.chat.to, sizeof(uncompressed.payload_variant.chat.to) - 1, USX_PSET_DFLT, NULL); if (length < 0) { - LOG_WARN("Decompression overflowed chat.to. Bailing out\n"); + LOG_WARN("Decompress overflow chat.to. Bailing out"); return; } - LOG_DEBUG("Decompressed chat to: %d bytes\n", length); + LOG_DEBUG("Decompressed chat to: %d bytes", length); } if (t->payload_variant.chat.has_to_callsign) { @@ -182,10 +182,10 @@ void AtakPluginModule::alterReceivedProtobuf(meshtastic_MeshPacket &mp, meshtast uncompressed.payload_variant.chat.to_callsign, sizeof(uncompressed.payload_variant.chat.to_callsign) - 1, USX_PSET_DFLT, NULL); if (length < 0) { - LOG_WARN("Decompression overflowed chat.to_callsign. Bailing out\n"); + LOG_WARN("Decompress overflow chat.to_callsign. Bailing out"); return; } - LOG_DEBUG("Decompressed chat to_callsign: %d bytes\n", length); + LOG_DEBUG("Decompressed chat to_callsign: %d bytes", length); } } decompressedCopy->decoded.payload.size = diff --git a/src/modules/CannedMessageModule.cpp b/src/modules/CannedMessageModule.cpp index ed0dce25f..a96fcc080 100644 --- a/src/modules/CannedMessageModule.cpp +++ b/src/modules/CannedMessageModule.cpp @@ -42,20 +42,20 @@ meshtastic_CannedMessageModuleConfig cannedMessageModuleConfig; CannedMessageModule *cannedMessageModule; CannedMessageModule::CannedMessageModule() - : SinglePortModule("canned", meshtastic_PortNum_TEXT_MESSAGE_APP), concurrency::OSThread("CannedMessageModule") + : SinglePortModule("canned", meshtastic_PortNum_TEXT_MESSAGE_APP), concurrency::OSThread("CannedMessage") { if (moduleConfig.canned_message.enabled || CANNED_MESSAGE_MODULE_ENABLE) { this->loadProtoForModule(); if ((this->splitConfiguredMessages() <= 0) && (cardkb_found.address == 0x00) && !INPUTBROKER_MATRIX_TYPE && !CANNED_MESSAGE_MODULE_ENABLE) { - LOG_INFO("CannedMessageModule: No messages are configured. Module is disabled\n"); + LOG_INFO("CannedMessageModule: No messages are configured. Module is disabled"); this->runState = CANNED_MESSAGE_RUN_STATE_DISABLED; disable(); } else { - LOG_INFO("CannedMessageModule is enabled\n"); + LOG_INFO("CannedMessageModule is enabled"); // T-Watch interface currently has no way to select destination type, so default to 'node' -#if defined(T_WATCH_S3) || defined(RAK14014) +#if defined(USE_VIRTUAL_KEYBOARD) this->destSelect = CANNED_MESSAGE_DESTINATION_TYPE_NODE; #endif @@ -81,7 +81,7 @@ int CannedMessageModule::splitConfiguredMessages() String canned_messages = cannedMessageModuleConfig.messages; -#if defined(T_WATCH_S3) || defined(RAK14014) +#if defined(USE_VIRTUAL_KEYBOARD) String separator = canned_messages.length() ? "|" : ""; canned_messages = "[---- Free Text ----]" + separator + canned_messages; @@ -112,7 +112,7 @@ int CannedMessageModule::splitConfiguredMessages() } if (strlen(this->messages[messageIndex - 1]) > 0) { // We have a last message. - LOG_DEBUG("CannedMessage %d is: '%s'\n", messageIndex - 1, this->messages[messageIndex - 1]); + LOG_DEBUG("CannedMessage %d is: '%s'", messageIndex - 1, this->messages[messageIndex - 1]); this->messagesCount = messageIndex; } else { this->messagesCount = messageIndex - 1; @@ -150,7 +150,7 @@ int CannedMessageModule::handleInputEvent(const InputEvent *event) } if (event->inputEvent == static_cast(meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_SELECT)) { -#if defined(T_WATCH_S3) || defined(RAK14014) +#if defined(USE_VIRTUAL_KEYBOARD) if (this->currentMessageIndex == 0) { this->runState = CANNED_MESSAGE_RUN_STATE_FREETEXT; @@ -177,7 +177,7 @@ int CannedMessageModule::handleInputEvent(const InputEvent *event) e.action = UIFrameEvent::Action::REGENERATE_FRAMESET; // We want to change the list of frames shown on-screen this->currentMessageIndex = -1; -#if !defined(T_WATCH_S3) && !defined(RAK14014) +#if !defined(T_WATCH_S3) && !defined(RAK14014) && !defined(USE_VIRTUAL_KEYBOARD) this->freetext = ""; // clear freetext this->cursor = 0; this->destSelect = CANNED_MESSAGE_DESTINATION_TYPE_NONE; @@ -190,7 +190,7 @@ int CannedMessageModule::handleInputEvent(const InputEvent *event) (event->inputEvent == static_cast(meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_LEFT)) || (event->inputEvent == static_cast(meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_RIGHT))) { -#if defined(T_WATCH_S3) || defined(RAK14014) +#if defined(USE_VIRTUAL_KEYBOARD) if (event->inputEvent == static_cast(meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_LEFT)) { this->payload = INPUT_BROKER_MSG_LEFT; } else if (event->inputEvent == static_cast(meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_RIGHT)) { @@ -227,20 +227,20 @@ int CannedMessageModule::handleInputEvent(const InputEvent *event) case INPUT_BROKER_MSG_BRIGHTNESS_UP: // make screen brighter if (screen) screen->increaseBrightness(); - LOG_DEBUG("increasing Screen Brightness\n"); + LOG_DEBUG("Increase Screen Brightness"); break; case INPUT_BROKER_MSG_BRIGHTNESS_DOWN: // make screen dimmer if (screen) screen->decreaseBrightness(); - LOG_DEBUG("Decreasing Screen Brightness\n"); + LOG_DEBUG("Decrease Screen Brightness"); break; - case INPUT_BROKER_MSG_FN_SYMBOL_ON: // draw modifier (function) symbal + case INPUT_BROKER_MSG_FN_SYMBOL_ON: // draw modifier (function) symbol if (screen) - screen->setFunctionSymbal("Fn"); + screen->setFunctionSymbol("Fn"); break; - case INPUT_BROKER_MSG_FN_SYMBOL_OFF: // remove modifier (function) symbal + case INPUT_BROKER_MSG_FN_SYMBOL_OFF: // remove modifier (function) symbol if (screen) - screen->removeFunctionSymbal("Fn"); + screen->removeFunctionSymbol("Fn"); break; // mute (switch off/toggle) external notifications on fn+m case INPUT_BROKER_MSG_MUTE_TOGGLE: @@ -249,13 +249,13 @@ int CannedMessageModule::handleInputEvent(const InputEvent *event) externalNotificationModule->setMute(false); showTemporaryMessage("Notifications \nEnabled"); if (screen) - screen->removeFunctionSymbal("M"); // remove the mute symbol from the bottom right corner + screen->removeFunctionSymbol("M"); // remove the mute symbol from the bottom right corner } else { externalNotificationModule->stopNow(); // this will turn off all GPIO and sounds and idle the loop externalNotificationModule->setMute(true); showTemporaryMessage("Notifications \nDisabled"); if (screen) - screen->setFunctionSymbal("M"); // add the mute symbol to the bottom right corner + screen->setFunctionSymbol("M"); // add the mute symbol to the bottom right corner } } break; @@ -301,18 +301,18 @@ int CannedMessageModule::handleInputEvent(const InputEvent *event) return 0; default: // pass the pressed key - // LOG_DEBUG("Canned message ANYKEY (%x)\n", event->kbchar); + // LOG_DEBUG("Canned message ANYKEY (%x)", event->kbchar); this->payload = event->kbchar; this->lastTouchMillis = millis(); validEvent = true; break; } if (screen && (event->kbchar != INPUT_BROKER_MSG_FN_SYMBOL_ON)) { - screen->removeFunctionSymbal("Fn"); // remove modifier (function) symbal + screen->removeFunctionSymbol("Fn"); // remove modifier (function) symbol } } -#if defined(T_WATCH_S3) || defined(RAK14014) +#if defined(USE_VIRTUAL_KEYBOARD) if (this->runState == CANNED_MESSAGE_RUN_STATE_FREETEXT) { String keyTapped = keyForCoordinates(event->touchX, event->touchY); @@ -325,7 +325,9 @@ int CannedMessageModule::handleInputEvent(const InputEvent *event) this->shift = !this->shift; } else if (keyTapped == "⌫") { +#ifndef RAK14014 this->highlight = keyTapped[0]; +#endif this->payload = 0x08; @@ -341,7 +343,9 @@ int CannedMessageModule::handleInputEvent(const InputEvent *event) validEvent = true; } else if (keyTapped == " ") { +#ifndef RAK14014 this->highlight = keyTapped[0]; +#endif this->payload = keyTapped[0]; @@ -361,7 +365,9 @@ int CannedMessageModule::handleInputEvent(const InputEvent *event) this->shift = false; } else if (keyTapped != "") { +#ifndef RAK14014 this->highlight = keyTapped[0]; +#endif this->payload = this->shift ? keyTapped[0] : std::tolower(keyTapped[0]); @@ -414,7 +420,7 @@ void CannedMessageModule::sendText(NodeNum dest, ChannelIndex channel, const cha // or raising a UIFrameEvent before another module has the chance this->waitingForAck = true; - LOG_INFO("Sending message id=%d, dest=%x, msg=%.*s\n", p->id, p->to, p->decoded.payload.size, p->decoded.payload.bytes); + LOG_INFO("Send message id=%d, dest=%x, msg=%.*s", p->id, p->to, p->decoded.payload.size, p->decoded.payload.bytes); service->sendToMesh( p, RX_SRC_LOCAL, @@ -428,7 +434,7 @@ int32_t CannedMessageModule::runOnce() temporaryMessage = ""; return INT32_MAX; } - // LOG_DEBUG("Check status\n"); + // LOG_DEBUG("Check status"); UIFrameEvent e; if ((this->runState == CANNED_MESSAGE_RUN_STATE_SENDING_ACTIVE) || (this->runState == CANNED_MESSAGE_RUN_STATE_ACK_NACK_RECEIVED) || (this->runState == CANNED_MESSAGE_RUN_STATE_MESSAGE)) { @@ -440,7 +446,7 @@ int32_t CannedMessageModule::runOnce() this->freetext = ""; // clear freetext this->cursor = 0; -#if !defined(T_WATCH_S3) && !defined(RAK14014) +#if !defined(T_WATCH_S3) && !defined(RAK14014) && !defined(SENSECAP_INDICATOR) this->destSelect = CANNED_MESSAGE_DESTINATION_TYPE_NONE; #endif @@ -453,7 +459,7 @@ int32_t CannedMessageModule::runOnce() this->freetext = ""; // clear freetext this->cursor = 0; -#if !defined(T_WATCH_S3) && !defined(RAK14014) +#if !defined(T_WATCH_S3) && !defined(RAK14014) && !defined(USE_VIRTUAL_KEYBOARD) this->destSelect = CANNED_MESSAGE_DESTINATION_TYPE_NONE; #endif @@ -473,7 +479,7 @@ int32_t CannedMessageModule::runOnce() powerFSM.trigger(EVENT_PRESS); return INT32_MAX; } else { -#if defined(T_WATCH_S3) || defined(RAK14014) +#if defined(USE_VIRTUAL_KEYBOARD) sendText(this->dest, indexChannels[this->channel], this->messages[this->currentMessageIndex], true); #else sendText(NODENUM_BROADCAST, channels.getPrimaryIndex(), this->messages[this->currentMessageIndex], true); @@ -481,7 +487,7 @@ int32_t CannedMessageModule::runOnce() } this->runState = CANNED_MESSAGE_RUN_STATE_SENDING_ACTIVE; } else { - // LOG_DEBUG("Reset message is empty.\n"); + // LOG_DEBUG("Reset message is empty"); this->runState = CANNED_MESSAGE_RUN_STATE_INACTIVE; } } @@ -490,7 +496,7 @@ int32_t CannedMessageModule::runOnce() this->freetext = ""; // clear freetext this->cursor = 0; -#if !defined(T_WATCH_S3) && !defined(RAK14014) +#if !defined(T_WATCH_S3) && !defined(RAK14014) && !defined(USE_VIRTUAL_KEYBOARD) this->destSelect = CANNED_MESSAGE_DESTINATION_TYPE_NONE; #endif @@ -498,7 +504,7 @@ int32_t CannedMessageModule::runOnce() return 2000; } else if ((this->runState != CANNED_MESSAGE_RUN_STATE_FREETEXT) && (this->currentMessageIndex == -1)) { this->currentMessageIndex = 0; - LOG_DEBUG("First touch (%d):%s\n", this->currentMessageIndex, this->getCurrentMessage()); + LOG_DEBUG("First touch (%d):%s", this->currentMessageIndex, this->getCurrentMessage()); e.action = UIFrameEvent::Action::REGENERATE_FRAMESET; // We want to change the list of frames shown on-screen this->runState = CANNED_MESSAGE_RUN_STATE_ACTIVE; } else if (this->runState == CANNED_MESSAGE_RUN_STATE_ACTION_UP) { @@ -507,12 +513,12 @@ int32_t CannedMessageModule::runOnce() this->freetext = ""; // clear freetext this->cursor = 0; -#if !defined(T_WATCH_S3) && !defined(RAK14014) +#if !defined(T_WATCH_S3) && !defined(RAK14014) && !defined(USE_VIRTUAL_KEYBOARD) this->destSelect = CANNED_MESSAGE_DESTINATION_TYPE_NONE; #endif this->runState = CANNED_MESSAGE_RUN_STATE_ACTIVE; - LOG_DEBUG("MOVE UP (%d):%s\n", this->currentMessageIndex, this->getCurrentMessage()); + LOG_DEBUG("MOVE UP (%d):%s", this->currentMessageIndex, this->getCurrentMessage()); } } else if (this->runState == CANNED_MESSAGE_RUN_STATE_ACTION_DOWN) { if (this->messagesCount > 0) { @@ -520,12 +526,12 @@ int32_t CannedMessageModule::runOnce() this->freetext = ""; // clear freetext this->cursor = 0; -#if !defined(T_WATCH_S3) && !defined(RAK14014) +#if !defined(T_WATCH_S3) && !defined(RAK14014) && !defined(USE_VIRTUAL_KEYBOARD) this->destSelect = CANNED_MESSAGE_DESTINATION_TYPE_NONE; #endif this->runState = CANNED_MESSAGE_RUN_STATE_ACTIVE; - LOG_DEBUG("MOVE DOWN (%d):%s\n", this->currentMessageIndex, this->getCurrentMessage()); + LOG_DEBUG("MOVE DOWN (%d):%s", this->currentMessageIndex, this->getCurrentMessage()); } } else if (this->runState == CANNED_MESSAGE_RUN_STATE_FREETEXT || this->runState == CANNED_MESSAGE_RUN_STATE_ACTIVE) { switch (this->payload) { @@ -666,7 +672,7 @@ int32_t CannedMessageModule::runOnce() break; } if (screen) - screen->removeFunctionSymbal("Fn"); + screen->removeFunctionSymbol("Fn"); } this->lastTouchMillis = millis(); @@ -763,7 +769,7 @@ void CannedMessageModule::showTemporaryMessage(const String &message) setIntervalFromNow(2000); } -#if defined(T_WATCH_S3) || defined(RAK14014) +#if defined(USE_VIRTUAL_KEYBOARD) String CannedMessageModule::keyForCoordinates(uint x, uint y) { @@ -830,6 +836,11 @@ void CannedMessageModule::drawKeyboard(OLEDDisplay *display, OLEDDisplayUiState Letter updatedLetter = {letter.character, letter.width, xOffset, yOffset, cellWidth, cellHeight}; +#ifdef RAK14014 // Optimize the touch range of the virtual keyboard in the bottom row + if (outerIndex == outerSize - 1) { + updatedLetter.rectHeight = 240 - yOffset; + } +#endif this->keyboard[this->charSet][outerIndex][innerIndex] = updatedLetter; float characterOffset = ((cellWidth / 2) - (letter.width / 2)); @@ -977,7 +988,7 @@ void CannedMessageModule::drawFrame(OLEDDisplay *display, OLEDDisplayUiState *st if (temporaryMessage.length() != 0) { requestFocus(); // Tell Screen::setFrames to move to our module's frame - LOG_DEBUG("Drawing temporary message: %s", temporaryMessage.c_str()); + LOG_DEBUG("Draw temporary message: %s", temporaryMessage.c_str()); display->setTextAlignment(TEXT_ALIGN_CENTER); display->setFont(FONT_MEDIUM); display->drawString(display->getWidth() / 2 + x, 0 + y + 12, temporaryMessage); @@ -1044,7 +1055,7 @@ void CannedMessageModule::drawFrame(OLEDDisplay *display, OLEDDisplayUiState *st display->drawString(10 + x, 0 + y + FONT_HEIGHT_SMALL, "Canned Message\nModule disabled."); } else if (cannedMessageModule->runState == CANNED_MESSAGE_RUN_STATE_FREETEXT) { requestFocus(); // Tell Screen::setFrames to move to our module's frame -#if defined(T_WATCH_S3) || defined(RAK14014) +#if defined(USE_VIRTUAL_KEYBOARD) drawKeyboard(display, state, 0, 0); #else @@ -1206,13 +1217,13 @@ AdminMessageHandleResult CannedMessageModule::handleAdminMessageForModule(const switch (request->which_payload_variant) { case meshtastic_AdminMessage_get_canned_message_module_messages_request_tag: - LOG_DEBUG("Client is getting radio canned messages\n"); + LOG_DEBUG("Client getting radio canned messages"); this->handleGetCannedMessageModuleMessages(mp, response); result = AdminMessageHandleResult::HANDLED_WITH_RESPONSE; break; case meshtastic_AdminMessage_set_canned_message_module_messages_tag: - LOG_DEBUG("Client is setting radio canned messages\n"); + LOG_DEBUG("Client getting radio canned messages"); this->handleSetCannedMessageModuleMessages(request->set_canned_message_module_messages); result = AdminMessageHandleResult::HANDLED; break; @@ -1227,7 +1238,7 @@ AdminMessageHandleResult CannedMessageModule::handleAdminMessageForModule(const void CannedMessageModule::handleGetCannedMessageModuleMessages(const meshtastic_MeshPacket &req, meshtastic_AdminMessage *response) { - LOG_DEBUG("*** handleGetCannedMessageModuleMessages\n"); + LOG_DEBUG("*** handleGetCannedMessageModuleMessages"); if (req.decoded.want_response) { response->which_payload_variant = meshtastic_AdminMessage_get_canned_message_module_messages_response_tag; strncpy(response->get_canned_message_module_messages_response, cannedMessageModuleConfig.messages, @@ -1242,7 +1253,7 @@ void CannedMessageModule::handleSetCannedMessageModuleMessages(const char *from_ if (*from_msg) { changed |= strcmp(cannedMessageModuleConfig.messages, from_msg); strncpy(cannedMessageModuleConfig.messages, from_msg, sizeof(cannedMessageModuleConfig.messages)); - LOG_DEBUG("*** from_msg.text:%s\n", from_msg); + LOG_DEBUG("*** from_msg.text:%s", from_msg); } if (changed) { @@ -1256,4 +1267,4 @@ String CannedMessageModule::drawWithCursor(String text, int cursor) return result; } -#endif \ No newline at end of file +#endif diff --git a/src/modules/CannedMessageModule.h b/src/modules/CannedMessageModule.h index e9dc2bda0..fd9ffc9b6 100644 --- a/src/modules/CannedMessageModule.h +++ b/src/modules/CannedMessageModule.h @@ -68,6 +68,10 @@ class CannedMessageModule : public SinglePortModule, public Observable 0) { pinMode(moduleConfig.detection_sensor.monitor_pin, moduleConfig.detection_sensor.use_pullup ? INPUT_PULLUP : INPUT); } else { - LOG_WARN("Detection Sensor Module: Set to enabled but no monitor pin is set. Disabling module...\n"); + LOG_WARN("Detection Sensor Module: Set to enabled but no monitor pin is set. Disable module"); return disable(); } - LOG_INFO("Detection Sensor Module: Initializing\n"); + LOG_INFO("Detection Sensor Module: init"); return DELAYED_INTERVAL; } - // LOG_DEBUG("Detection Sensor Module: Current pin state: %i\n", digitalRead(moduleConfig.detection_sensor.monitor_pin)); + // LOG_DEBUG("Detection Sensor Module: Current pin state: %i", digitalRead(moduleConfig.detection_sensor.monitor_pin)); if (!Throttle::isWithinTimespanMs(lastSentToMesh, Default::getConfiguredOrDefaultMs(moduleConfig.detection_sensor.minimum_broadcast_secs))) { @@ -118,7 +118,7 @@ int32_t DetectionSensorModule::runOnce() void DetectionSensorModule::sendDetectionMessage() { - LOG_DEBUG("Detected event observed. Sending message\n"); + LOG_DEBUG("Detected event observed. Send message"); char *message = new char[40]; sprintf(message, "%s detected", moduleConfig.detection_sensor.name); meshtastic_MeshPacket *p = allocDataPacket(); @@ -130,9 +130,12 @@ void DetectionSensorModule::sendDetectionMessage() p->decoded.payload.bytes[p->decoded.payload.size + 1] = '\0'; // Bell character p->decoded.payload.size++; } - LOG_INFO("Sending message id=%d, dest=%x, msg=%.*s\n", p->id, p->to, p->decoded.payload.size, p->decoded.payload.bytes); lastSentToMesh = millis(); - service->sendToMesh(p); + if (!channels.isDefaultChannel(0)) { + LOG_INFO("Send message id=%d, dest=%x, msg=%.*s", p->id, p->to, p->decoded.payload.size, p->decoded.payload.bytes); + service->sendToMesh(p); + } else + LOG_ERROR("Message not allow on Public channel"); delete[] message; } @@ -140,20 +143,22 @@ void DetectionSensorModule::sendCurrentStateMessage(bool state) { char *message = new char[40]; sprintf(message, "%s state: %i", moduleConfig.detection_sensor.name, state); - meshtastic_MeshPacket *p = allocDataPacket(); p->want_ack = false; p->decoded.payload.size = strlen(message); memcpy(p->decoded.payload.bytes, message, p->decoded.payload.size); - LOG_INFO("Sending message id=%d, dest=%x, msg=%.*s\n", p->id, p->to, p->decoded.payload.size, p->decoded.payload.bytes); lastSentToMesh = millis(); - service->sendToMesh(p); + if (!channels.isDefaultChannel(0)) { + LOG_INFO("Send message id=%d, dest=%x, msg=%.*s", p->id, p->to, p->decoded.payload.size, p->decoded.payload.bytes); + service->sendToMesh(p); + } else + LOG_ERROR("Message not allow on Public channel"); delete[] message; } bool DetectionSensorModule::hasDetectionEvent() { bool currentState = digitalRead(moduleConfig.detection_sensor.monitor_pin); - // LOG_DEBUG("Detection Sensor Module: Current state: %i\n", currentState); + // LOG_DEBUG("Detection Sensor Module: Current state: %i", currentState); return (moduleConfig.detection_sensor.detection_trigger_type & 1) ? currentState : !currentState; -} \ No newline at end of file +} diff --git a/src/modules/DetectionSensorModule.h b/src/modules/DetectionSensorModule.h index b960c8744..3ba10d329 100644 --- a/src/modules/DetectionSensorModule.h +++ b/src/modules/DetectionSensorModule.h @@ -4,8 +4,7 @@ class DetectionSensorModule : public SinglePortModule, private concurrency::OSThread { public: - DetectionSensorModule() - : SinglePortModule("detection", meshtastic_PortNum_DETECTION_SENSOR_APP), OSThread("DetectionSensorModule") + DetectionSensorModule() : SinglePortModule("detection", meshtastic_PortNum_DETECTION_SENSOR_APP), OSThread("DetectionSensor") { } diff --git a/src/modules/DropzoneModule.cpp b/src/modules/DropzoneModule.cpp index b1ca2af81..6c42af98b 100644 --- a/src/modules/DropzoneModule.cpp +++ b/src/modules/DropzoneModule.cpp @@ -34,13 +34,13 @@ ProcessMessage DropzoneModule::handleReceived(const meshtastic_MeshPacket &mp) auto incomingMessage = reinterpret_cast(p.payload.bytes); sprintf(matchCompare, "%s conditions", owner.short_name); if (strncasecmp(incomingMessage, matchCompare, strlen(matchCompare)) == 0) { - LOG_DEBUG("Received dropzone conditions request\n"); + LOG_DEBUG("Received dropzone conditions request"); startSendConditions = millis(); } sprintf(matchCompare, "%s conditions", owner.long_name); if (strncasecmp(incomingMessage, matchCompare, strlen(matchCompare)) == 0) { - LOG_DEBUG("Received dropzone conditions request\n"); + LOG_DEBUG("Received dropzone conditions request"); startSendConditions = millis(); } return ProcessMessage::CONTINUE; @@ -82,10 +82,10 @@ meshtastic_MeshPacket *DropzoneModule::sendConditions() sprintf(replyStr, "%s @ %02d:%02d:%02dz\nWind %.2f kts @ %d°\nBaro %.2f inHg %.2f°C", dropzoneStatus, hour, min, sec, windSpeed, windDirection, baro, temp); } else { - LOG_ERROR("No sensor found\n"); + LOG_ERROR("No sensor found"); sprintf(replyStr, "%s @ %02d:%02d:%02d\nNo sensor found", dropzoneStatus, hour, min, sec); } - LOG_DEBUG("Conditions reply: %s\n", replyStr); + LOG_DEBUG("Conditions reply: %s", replyStr); reply->decoded.payload.size = strlen(replyStr); // You must specify how many bytes are in the reply memcpy(reply->decoded.payload.bytes, replyStr, reply->decoded.payload.size); diff --git a/src/modules/DropzoneModule.h b/src/modules/DropzoneModule.h index 28f54ee0f..9e79ab447 100644 --- a/src/modules/DropzoneModule.h +++ b/src/modules/DropzoneModule.h @@ -15,7 +15,7 @@ class DropzoneModule : public SinglePortModule, private concurrency::OSThread /** Constructor * name is for debugging output */ - DropzoneModule() : SinglePortModule("dropzone", meshtastic_PortNum_TEXT_MESSAGE_APP), concurrency::OSThread("DropzoneModule") + DropzoneModule() : SinglePortModule("dropzone", meshtastic_PortNum_TEXT_MESSAGE_APP), concurrency::OSThread("Dropzone") { // Set up the analog pin for reading the dropzone status pinMode(PIN_A1, INPUT); diff --git a/src/modules/ExternalNotificationModule.cpp b/src/modules/ExternalNotificationModule.cpp index 2306119b1..bbb3f90e0 100644 --- a/src/modules/ExternalNotificationModule.cpp +++ b/src/modules/ExternalNotificationModule.cpp @@ -93,11 +93,11 @@ int32_t ExternalNotificationModule::runOnce() nagCycleCutoff = UINT32_MAX; LOG_INFO("Turning off external notification: "); for (int i = 0; i < 3; i++) { - setExternalOff(i); + setExternalState(i, false); externalTurnedOn[i] = 0; LOG_INFO("%d ", i); } - LOG_INFO("\n"); + LOG_INFO(""); #ifdef HAS_I2S // GPIO0 is used as mclk for I2S audio and set to OUTPUT by the sound library // T-Deck uses GPIO0 as trackball button, so restore the mode @@ -114,17 +114,19 @@ int32_t ExternalNotificationModule::runOnce() if (externalTurnedOn[0] + (moduleConfig.external_notification.output_ms ? moduleConfig.external_notification.output_ms : EXT_NOTIFICATION_MODULE_OUTPUT_MS) < millis()) { - getExternal(0) ? setExternalOff(0) : setExternalOn(0); + setExternalState(0, !getExternal(0)); } if (externalTurnedOn[1] + (moduleConfig.external_notification.output_ms ? moduleConfig.external_notification.output_ms : EXT_NOTIFICATION_MODULE_OUTPUT_MS) < millis()) { - getExternal(1) ? setExternalOff(1) : setExternalOn(1); + setExternalState(1, !getExternal(1)); } if (externalTurnedOn[2] + (moduleConfig.external_notification.output_ms ? moduleConfig.external_notification.output_ms : EXT_NOTIFICATION_MODULE_OUTPUT_MS) < millis()) { - getExternal(2) ? setExternalOff(2) : setExternalOn(2); + LOG_DEBUG("EXTERNAL 2 %d compared to %d", externalTurnedOn[2] + moduleConfig.external_notification.output_ms, + millis()); + setExternalState(2, !getExternal(2)); } #if defined(HAS_NCP5623) || defined(RGBLED_RED) || defined(HAS_NEOPIXEL) || defined(UNPHONE) red = (colorState & 4) ? brightnessValues[brightnessIndex] : 0; // Red enabled on colorState = 4,5,6,7 @@ -184,7 +186,7 @@ int32_t ExternalNotificationModule::runOnce() } #endif // now let the PWM buzzer play - if (moduleConfig.external_notification.use_pwm) { + if (moduleConfig.external_notification.use_pwm && config.device.buzzer_gpio) { if (rtttl::isPlaying()) { rtttl::play(); } else if (isNagging && (nagCycleCutoff >= millis())) { @@ -203,86 +205,42 @@ bool ExternalNotificationModule::wantPacket(const meshtastic_MeshPacket *p) } /** - * Sets the external notification on for the specified index. + * Sets the external notification for the specified index. * - * @param index The index of the external notification to turn on. + * @param index The index of the external notification to change state. + * @param on Whether we are turning things on (true) or off (false). */ -void ExternalNotificationModule::setExternalOn(uint8_t index) +void ExternalNotificationModule::setExternalState(uint8_t index, bool on) { - externalCurrentState[index] = 1; + externalCurrentState[index] = on; externalTurnedOn[index] = millis(); switch (index) { case 1: #ifdef UNPHONE - unphone.vibe(true); // the unPhone's vibration motor is on a i2c GPIO expander + unphone.vibe(on); // the unPhone's vibration motor is on a i2c GPIO expander #endif if (moduleConfig.external_notification.output_vibra) - digitalWrite(moduleConfig.external_notification.output_vibra, true); + digitalWrite(moduleConfig.external_notification.output_vibra, on); break; case 2: if (moduleConfig.external_notification.output_buzzer) - digitalWrite(moduleConfig.external_notification.output_buzzer, true); + digitalWrite(moduleConfig.external_notification.output_buzzer, on); break; default: if (output > 0) - digitalWrite(output, (moduleConfig.external_notification.active ? true : false)); - break; - } - -#ifdef HAS_NCP5623 - if (rgb_found.type == ScanI2C::NCP5623) { - rgb.setColor(red, green, blue); - } -#endif -#ifdef RGBLED_CA - analogWrite(RGBLED_RED, 255 - red); // CA type needs reverse logic - analogWrite(RGBLED_GREEN, 255 - green); - analogWrite(RGBLED_BLUE, 255 - blue); -#elif defined(RGBLED_RED) - analogWrite(RGBLED_RED, red); - analogWrite(RGBLED_GREEN, green); - analogWrite(RGBLED_BLUE, blue); -#endif -#ifdef HAS_NEOPIXEL - pixels.fill(pixels.Color(red, green, blue), 0, NEOPIXEL_COUNT); - pixels.show(); -#endif -#ifdef UNPHONE - unphone.rgb(red, green, blue); -#endif -#ifdef T_WATCH_S3 - drv.go(); -#endif -} - -void ExternalNotificationModule::setExternalOff(uint8_t index) -{ - externalCurrentState[index] = 0; - externalTurnedOn[index] = millis(); - - switch (index) { - case 1: -#ifdef UNPHONE - unphone.vibe(false); // the unPhone's vibration motor is on a i2c GPIO expander -#endif - if (moduleConfig.external_notification.output_vibra) - digitalWrite(moduleConfig.external_notification.output_vibra, false); - break; - case 2: - if (moduleConfig.external_notification.output_buzzer) - digitalWrite(moduleConfig.external_notification.output_buzzer, false); - break; - default: - if (output > 0) - digitalWrite(output, (moduleConfig.external_notification.active ? false : true)); + digitalWrite(output, (moduleConfig.external_notification.active ? on : !on)); break; } #if defined(HAS_NCP5623) || defined(RGBLED_RED) || defined(HAS_NEOPIXEL) || defined(UNPHONE) - red = 0; - green = 0; - blue = 0; + if (!on) { + red = 0; + green = 0; + blue = 0; + } +#endif + #ifdef HAS_NCP5623 if (rgb_found.type == ScanI2C::NCP5623) { rgb.setColor(red, green, blue); @@ -304,10 +262,12 @@ void ExternalNotificationModule::setExternalOff(uint8_t index) #ifdef UNPHONE unphone.rgb(red, green, blue); #endif -#endif - #ifdef T_WATCH_S3 - drv.stop(); + if (on) { + drv.go(); + } else { + drv.stop(); + } #endif } @@ -333,7 +293,7 @@ void ExternalNotificationModule::stopNow() ExternalNotificationModule::ExternalNotificationModule() : SinglePortModule("ExternalNotificationModule", meshtastic_PortNum_TEXT_MESSAGE_APP), - concurrency::OSThread("ExternalNotificationModule") + concurrency::OSThread("ExternalNotification") { /* Uncomment the preferences below if you want to use the module @@ -369,34 +329,34 @@ ExternalNotificationModule::ExternalNotificationModule() sizeof(rtttlConfig.ringtone)); } - LOG_INFO("Initializing External Notification Module\n"); + LOG_INFO("Init External Notification Module"); output = moduleConfig.external_notification.output ? moduleConfig.external_notification.output : EXT_NOTIFICATION_MODULE_OUTPUT; // Set the direction of a pin if (output > 0) { - LOG_INFO("Using Pin %i in digital mode\n", output); + LOG_INFO("Use Pin %i in digital mode", output); pinMode(output, OUTPUT); } - setExternalOff(0); + setExternalState(0, false); externalTurnedOn[0] = 0; if (moduleConfig.external_notification.output_vibra) { - LOG_INFO("Using Pin %i for vibra motor\n", moduleConfig.external_notification.output_vibra); + LOG_INFO("Use Pin %i for vibra motor", moduleConfig.external_notification.output_vibra); pinMode(moduleConfig.external_notification.output_vibra, OUTPUT); - setExternalOff(1); + setExternalState(1, false); externalTurnedOn[1] = 0; } if (moduleConfig.external_notification.output_buzzer) { if (!moduleConfig.external_notification.use_pwm) { - LOG_INFO("Using Pin %i for buzzer\n", moduleConfig.external_notification.output_buzzer); + LOG_INFO("Use Pin %i for buzzer", moduleConfig.external_notification.output_buzzer); pinMode(moduleConfig.external_notification.output_buzzer, OUTPUT); - setExternalOff(2); + setExternalState(2, false); externalTurnedOn[2] = 0; } else { config.device.buzzer_gpio = config.device.buzzer_gpio ? config.device.buzzer_gpio : PIN_BUZZER; // in PWM Mode we force the buzzer pin if it is set - LOG_INFO("Using Pin %i in PWM mode\n", config.device.buzzer_gpio); + LOG_INFO("Use Pin %i in PWM mode", config.device.buzzer_gpio); } } #ifdef HAS_NCP5623 @@ -421,7 +381,7 @@ ExternalNotificationModule::ExternalNotificationModule() pixels.setBrightness(moduleConfig.ambient_lighting.current); #endif } else { - LOG_INFO("External Notification Module Disabled\n"); + LOG_INFO("External Notification Module Disabled"); disable(); } } @@ -447,9 +407,9 @@ ProcessMessage ExternalNotificationModule::handleReceived(const meshtastic_MeshP if (moduleConfig.external_notification.alert_bell) { if (containsBell) { - LOG_INFO("externalNotificationModule - Notification Bell\n"); + LOG_INFO("externalNotificationModule - Notification Bell"); isNagging = true; - setExternalOn(0); + setExternalState(0, true); if (moduleConfig.external_notification.nag_timeout) { nagCycleCutoff = millis() + moduleConfig.external_notification.nag_timeout * 1000; } else { @@ -460,9 +420,9 @@ ProcessMessage ExternalNotificationModule::handleReceived(const meshtastic_MeshP if (moduleConfig.external_notification.alert_bell_vibra) { if (containsBell) { - LOG_INFO("externalNotificationModule - Notification Bell (Vibra)\n"); + LOG_INFO("externalNotificationModule - Notification Bell (Vibra)"); isNagging = true; - setExternalOn(1); + setExternalState(1, true); if (moduleConfig.external_notification.nag_timeout) { nagCycleCutoff = millis() + moduleConfig.external_notification.nag_timeout * 1000; } else { @@ -473,10 +433,10 @@ ProcessMessage ExternalNotificationModule::handleReceived(const meshtastic_MeshP if (moduleConfig.external_notification.alert_bell_buzzer) { if (containsBell) { - LOG_INFO("externalNotificationModule - Notification Bell (Buzzer)\n"); + LOG_INFO("externalNotificationModule - Notification Bell (Buzzer)"); isNagging = true; if (!moduleConfig.external_notification.use_pwm) { - setExternalOn(2); + setExternalState(2, true); } else { #ifdef HAS_I2S audioThread->beginRttl(rtttlConfig.ringtone, strlen_P(rtttlConfig.ringtone)); @@ -493,9 +453,9 @@ ProcessMessage ExternalNotificationModule::handleReceived(const meshtastic_MeshP } if (moduleConfig.external_notification.alert_message) { - LOG_INFO("externalNotificationModule - Notification Module\n"); + LOG_INFO("externalNotificationModule - Notification Module"); isNagging = true; - setExternalOn(0); + setExternalState(0, true); if (moduleConfig.external_notification.nag_timeout) { nagCycleCutoff = millis() + moduleConfig.external_notification.nag_timeout * 1000; } else { @@ -504,9 +464,9 @@ ProcessMessage ExternalNotificationModule::handleReceived(const meshtastic_MeshP } if (moduleConfig.external_notification.alert_message_vibra) { - LOG_INFO("externalNotificationModule - Notification Module (Vibra)\n"); + LOG_INFO("externalNotificationModule - Notification Module (Vibra)"); isNagging = true; - setExternalOn(1); + setExternalState(1, true); if (moduleConfig.external_notification.nag_timeout) { nagCycleCutoff = millis() + moduleConfig.external_notification.nag_timeout * 1000; } else { @@ -515,10 +475,10 @@ ProcessMessage ExternalNotificationModule::handleReceived(const meshtastic_MeshP } if (moduleConfig.external_notification.alert_message_buzzer) { - LOG_INFO("externalNotificationModule - Notification Module (Buzzer)\n"); + LOG_INFO("externalNotificationModule - Notification Module (Buzzer)"); isNagging = true; if (!moduleConfig.external_notification.use_pwm && !moduleConfig.external_notification.use_i2s_as_buzzer) { - setExternalOn(2); + setExternalState(2, true); } else { #ifdef HAS_I2S if (moduleConfig.external_notification.use_i2s_as_buzzer) { @@ -537,7 +497,7 @@ ProcessMessage ExternalNotificationModule::handleReceived(const meshtastic_MeshP setIntervalFromNow(0); // run once so we know if we should do something } } else { - LOG_INFO("External Notification Module Disabled or muted\n"); + LOG_INFO("External Notification Module Disabled or muted"); } return ProcessMessage::CONTINUE; // Let others look at this message also if they want @@ -560,13 +520,13 @@ AdminMessageHandleResult ExternalNotificationModule::handleAdminMessageForModule switch (request->which_payload_variant) { case meshtastic_AdminMessage_get_ringtone_request_tag: - LOG_INFO("Client is getting ringtone\n"); + LOG_INFO("Client getting ringtone"); this->handleGetRingtone(mp, response); result = AdminMessageHandleResult::HANDLED_WITH_RESPONSE; break; case meshtastic_AdminMessage_set_ringtone_message_tag: - LOG_INFO("Client is setting ringtone\n"); + LOG_INFO("Client setting ringtone"); this->handleSetRingtone(request->set_canned_message_module_messages); result = AdminMessageHandleResult::HANDLED; break; @@ -580,7 +540,7 @@ AdminMessageHandleResult ExternalNotificationModule::handleAdminMessageForModule void ExternalNotificationModule::handleGetRingtone(const meshtastic_MeshPacket &req, meshtastic_AdminMessage *response) { - LOG_INFO("*** handleGetRingtone\n"); + LOG_INFO("*** handleGetRingtone"); if (req.decoded.want_response) { response->which_payload_variant = meshtastic_AdminMessage_get_ringtone_response_tag; strncpy(response->get_ringtone_response, rtttlConfig.ringtone, sizeof(response->get_ringtone_response)); @@ -594,7 +554,7 @@ void ExternalNotificationModule::handleSetRingtone(const char *from_msg) if (*from_msg) { changed |= strcmp(rtttlConfig.ringtone, from_msg); strncpy(rtttlConfig.ringtone, from_msg, sizeof(rtttlConfig.ringtone)); - LOG_INFO("*** from_msg.text:%s\n", from_msg); + LOG_INFO("*** from_msg.text:%s", from_msg); } if (changed) { diff --git a/src/modules/ExternalNotificationModule.h b/src/modules/ExternalNotificationModule.h index a5dff3651..841ca6de9 100644 --- a/src/modules/ExternalNotificationModule.h +++ b/src/modules/ExternalNotificationModule.h @@ -32,10 +32,9 @@ class ExternalNotificationModule : public SinglePortModule, private concurrency: public: ExternalNotificationModule(); - uint32_t nagCycleCutoff = UINT32_MAX; + uint32_t nagCycleCutoff = 1; - void setExternalOn(uint8_t index = 0); - void setExternalOff(uint8_t index = 0); + void setExternalState(uint8_t index = 0, bool on = false); bool getExternal(uint8_t index = 0); void setMute(bool mute) { isMuted = mute; } diff --git a/src/modules/NeighborInfoModule.cpp b/src/modules/NeighborInfoModule.cpp index e4c9b44bd..fb658421d 100644 --- a/src/modules/NeighborInfoModule.cpp +++ b/src/modules/NeighborInfoModule.cpp @@ -14,11 +14,11 @@ NOTE: For debugging only */ void NeighborInfoModule::printNeighborInfo(const char *header, const meshtastic_NeighborInfo *np) { - LOG_DEBUG("%s NEIGHBORINFO PACKET from Node 0x%x to Node 0x%x (last sent by 0x%x)\n", header, np->node_id, - nodeDB->getNodeNum(), np->last_sent_by_id); - LOG_DEBUG("Packet contains %d neighbors\n", np->neighbors_count); + LOG_DEBUG("%s NEIGHBORINFO PACKET from Node 0x%x to Node 0x%x (last sent by 0x%x)", header, np->node_id, nodeDB->getNodeNum(), + np->last_sent_by_id); + LOG_DEBUG("Packet contains %d neighbors", np->neighbors_count); for (int i = 0; i < np->neighbors_count; i++) { - LOG_DEBUG("Neighbor %d: node_id=0x%x, snr=%.2f\n", i, np->neighbors[i].node_id, np->neighbors[i].snr); + LOG_DEBUG("Neighbor %d: node_id=0x%x, snr=%.2f", i, np->neighbors[i].node_id, np->neighbors[i].snr); } } @@ -28,16 +28,16 @@ NOTE: for debugging only */ void NeighborInfoModule::printNodeDBNeighbors() { - LOG_DEBUG("Our NodeDB contains %d neighbors\n", neighbors.size()); + LOG_DEBUG("Our NodeDB contains %d neighbors", neighbors.size()); for (size_t i = 0; i < neighbors.size(); i++) { - LOG_DEBUG("Node %d: node_id=0x%x, snr=%.2f\n", i, neighbors[i].node_id, neighbors[i].snr); + LOG_DEBUG("Node %d: node_id=0x%x, snr=%.2f", i, neighbors[i].node_id, neighbors[i].snr); } } /* Send our initial owner announcement 35 seconds after we start (to give network time to setup) */ NeighborInfoModule::NeighborInfoModule() : ProtobufModule("neighborinfo", meshtastic_PortNum_NEIGHBORINFO_APP, &meshtastic_NeighborInfo_msg), - concurrency::OSThread("NeighborInfoModule") + concurrency::OSThread("NeighborInfo") { ourPortNum = meshtastic_PortNum_NEIGHBORINFO_APP; nodeStatusObserver.observe(&nodeStatus->onNewStatus); @@ -47,7 +47,7 @@ NeighborInfoModule::NeighborInfoModule() setIntervalFromNow(Default::getConfiguredOrDefaultMs(moduleConfig.neighbor_info.update_interval, default_telemetry_broadcast_interval_secs)); } else { - LOG_DEBUG("NeighborInfoModule is disabled\n"); + LOG_DEBUG("NeighborInfoModule is disabled"); disable(); } } @@ -91,7 +91,7 @@ void NeighborInfoModule::cleanUpNeighbors() // We will remove a neighbor if we haven't heard from them in twice the broadcast interval // cannot use isWithinTimespanMs() as it->last_rx_time is seconds since 1970 if ((now - it->last_rx_time > it->node_broadcast_interval_secs * 2) && (it->node_id != my_node_id)) { - LOG_DEBUG("Removing neighbor with node ID 0x%x\n", it->node_id); + LOG_DEBUG("Remove neighbor with node ID 0x%x", it->node_id); it = std::vector::reverse_iterator( neighbors.erase(std::next(it).base())); // Erase the element and update the iterator } else { @@ -121,14 +121,17 @@ Will be used for broadcast. */ int32_t NeighborInfoModule::runOnce() { - if (airTime->isTxAllowedChannelUtil(true) && airTime->isTxAllowedAirUtil()) { + if (moduleConfig.neighbor_info.transmit_over_lora && !channels.isDefaultChannel(channels.getPrimaryIndex()) && + airTime->isTxAllowedChannelUtil(true) && airTime->isTxAllowedAirUtil()) { sendNeighborInfo(NODENUM_BROADCAST, false); + } else { + sendNeighborInfo(NODENUM_BROADCAST_NO_LORA, false); } return Default::getConfiguredOrDefaultMs(moduleConfig.neighbor_info.update_interval, default_neighbor_info_broadcast_secs); } /* -Collect a recieved neighbor info packet from another node +Collect a received neighbor info packet from another node Pass it to an upper client; do not persist this data on the mesh */ bool NeighborInfoModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp, meshtastic_NeighborInfo *np) @@ -205,7 +208,7 @@ meshtastic_Neighbor *NeighborInfoModule::getOrCreateNeighbor(NodeNum originalSen neighbors.push_back(new_nbr); } else { // If we have too many neighbors, replace the oldest one - LOG_WARN("Neighbor DB is full, replacing oldest neighbor\n"); + LOG_WARN("Neighbor DB is full, replace oldest neighbor"); neighbors.erase(neighbors.begin()); neighbors.push_back(new_nbr); } diff --git a/src/modules/NodeInfoModule.cpp b/src/modules/NodeInfoModule.cpp index 61ec375cc..b55d47d5b 100644 --- a/src/modules/NodeInfoModule.cpp +++ b/src/modules/NodeInfoModule.cpp @@ -16,7 +16,7 @@ bool NodeInfoModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp, mes bool hasChanged = nodeDB->updateUser(getFrom(&mp), p, mp.channel); - bool wasBroadcast = mp.to == NODENUM_BROADCAST; + bool wasBroadcast = isBroadcast(mp.to); // Show new nodes on LCD screen if (wasBroadcast) { @@ -29,7 +29,7 @@ bool NodeInfoModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp, mes if (hasChanged && !wasBroadcast && !isToUs(&mp)) service->sendToPhone(packetPool.allocCopy(mp)); - // LOG_DEBUG("did handleReceived\n"); + // LOG_DEBUG("did handleReceived"); return false; // Let others look at this message also if they want } @@ -50,7 +50,7 @@ void NodeInfoModule::sendOurNodeInfo(NodeNum dest, bool wantReplies, uint8_t cha else p->priority = meshtastic_MeshPacket_Priority_BACKGROUND; if (channel > 0) { - LOG_DEBUG("sending ourNodeInfo to channel %d\n", channel); + LOG_DEBUG("Send ourNodeInfo to channel %d", channel); p->channel = channel; } @@ -65,30 +65,36 @@ meshtastic_MeshPacket *NodeInfoModule::allocReply() { if (!airTime->isTxAllowedChannelUtil(false)) { ignoreRequest = true; // Mark it as ignored for MeshModule - LOG_DEBUG("Skip sending NodeInfo due to > 40 percent channel util.\n"); + LOG_DEBUG("Skip send NodeInfo > 40%% ch. util"); return NULL; } // If we sent our NodeInfo less than 5 min. ago, don't send it again as it may be still underway. if (!shorterTimeout && lastSentToMesh && Throttle::isWithinTimespanMs(lastSentToMesh, 5 * 60 * 1000)) { - LOG_DEBUG("Skip sending NodeInfo since we just sent it less than 5 minutes ago.\n"); + LOG_DEBUG("Skip send NodeInfo since we sent it <5min ago"); ignoreRequest = true; // Mark it as ignored for MeshModule return NULL; } else if (shorterTimeout && lastSentToMesh && Throttle::isWithinTimespanMs(lastSentToMesh, 60 * 1000)) { - LOG_DEBUG("Skip sending actively requested NodeInfo since we just sent it less than 60 seconds ago.\n"); + LOG_DEBUG("Skip send NodeInfo since we sent it <60s ago"); ignoreRequest = true; // Mark it as ignored for MeshModule return NULL; } else { ignoreRequest = false; // Don't ignore requests anymore meshtastic_User &u = owner; - LOG_INFO("sending owner %s/%s/%s\n", u.id, u.long_name, u.short_name); + // Strip the public key if the user is licensed + if (u.is_licensed && u.public_key.size > 0) { + u.public_key.bytes[0] = 0; + u.public_key.size = 0; + } + + LOG_INFO("Send owner %s/%s/%s", u.id, u.long_name, u.short_name); lastSentToMesh = millis(); return allocDataProtobuf(u); } } NodeInfoModule::NodeInfoModule() - : ProtobufModule("nodeinfo", meshtastic_PortNum_NODEINFO_APP, &meshtastic_User_msg), concurrency::OSThread("NodeInfoModule") + : ProtobufModule("nodeinfo", meshtastic_PortNum_NODEINFO_APP, &meshtastic_User_msg), concurrency::OSThread("NodeInfo") { isPromiscuous = true; // We always want to update our nodedb, even if we are sniffing on others setIntervalFromNow(30 * @@ -102,8 +108,8 @@ int32_t NodeInfoModule::runOnce() currentGeneration = radioGeneration; if (airTime->isTxAllowedAirUtil() && config.device.role != meshtastic_Config_DeviceConfig_Role_CLIENT_HIDDEN) { - LOG_INFO("Sending our nodeinfo to mesh (wantReplies=%d)\n", requestReplies); + LOG_INFO("Send our nodeinfo to mesh (wantReplies=%d)", requestReplies); sendOurNodeInfo(NODENUM_BROADCAST, requestReplies); // Send our info (don't request replies) } return Default::getConfiguredOrDefaultMs(config.device.node_info_broadcast_secs, default_node_info_broadcast_secs); -} \ No newline at end of file +} diff --git a/src/modules/PositionModule.cpp b/src/modules/PositionModule.cpp index 6ad30f927..6285d7aa5 100644 --- a/src/modules/PositionModule.cpp +++ b/src/modules/PositionModule.cpp @@ -16,16 +16,12 @@ #include "meshtastic/atak.pb.h" #include "sleep.h" #include "target_specific.h" - -extern "C" { #include -} PositionModule *positionModule; PositionModule::PositionModule() - : ProtobufModule("position", meshtastic_PortNum_POSITION_APP, &meshtastic_Position_msg), - concurrency::OSThread("PositionModule") + : ProtobufModule("position", meshtastic_PortNum_POSITION_APP, &meshtastic_Position_msg), concurrency::OSThread("Position") { precision = 0; // safe starting value isPromiscuous = true; // We always want to update our nodedb, even if we are sniffing on others @@ -39,7 +35,7 @@ PositionModule::PositionModule() if ((config.device.role == meshtastic_Config_DeviceConfig_Role_TRACKER || config.device.role == meshtastic_Config_DeviceConfig_Role_TAK_TRACKER) && config.power.is_power_saving) { - LOG_DEBUG("Clearing position on startup for sleepy tracker (ー。ー) zzz\n"); + LOG_DEBUG("Clear position on startup for sleepy tracker (ー。ー) zzz"); nodeDB->clearLocalPosition(); } } @@ -57,7 +53,7 @@ bool PositionModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp, mes if (isFromUs(&mp)) { isLocal = true; if (config.position.fixed_position) { - LOG_DEBUG("Ignore incoming position update from myself except for time, because position.fixed_position is true\n"); + LOG_DEBUG("Ignore incoming position update from myself except for time, because position.fixed_position is true"); #ifdef T_WATCH_S3 // Since we return early if position.fixed_position is true, set the T-Watch's RTC to the time received from the @@ -70,14 +66,14 @@ bool PositionModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp, mes nodeDB->setLocalPosition(p, true); return false; } else { - LOG_DEBUG("Incoming update from MYSELF\n"); + LOG_DEBUG("Incoming update from MYSELF"); nodeDB->setLocalPosition(p); } } // Log packet size and data fields LOG_DEBUG("POSITION node=%08x l=%d lat=%d lon=%d msl=%d hae=%d geo=%d pdop=%d hdop=%d vdop=%d siv=%d fxq=%d fxt=%d pts=%d " - "time=%d\n", + "time=%d", getFrom(&mp), mp.decoded.payload.size, p.latitude_i, p.longitude_i, p.altitude, p.altitude_hae, p.altitude_geoidal_separation, p.PDOP, p.HDOP, p.VDOP, p.sats_in_view, p.fix_quality, p.fix_type, p.timestamp, p.time); @@ -111,7 +107,7 @@ void PositionModule::alterReceivedProtobuf(meshtastic_MeshPacket &mp, meshtastic { // Phone position packets need to be truncated to the channel precision if (isFromUs(&mp) && (precision < 32 && precision > 0)) { - LOG_DEBUG("Truncating phone position to channel precision %i\n", precision); + LOG_DEBUG("Truncate phone position to channel precision %i", precision); p->latitude_i = p->latitude_i & (UINT32_MAX << (32 - precision)); p->longitude_i = p->longitude_i & (UINT32_MAX << (32 - precision)); @@ -127,11 +123,11 @@ void PositionModule::alterReceivedProtobuf(meshtastic_MeshPacket &mp, meshtastic void PositionModule::trySetRtc(meshtastic_Position p, bool isLocal, bool forceUpdate) { if (hasQualityTimesource() && !isLocal) { - LOG_DEBUG("Ignoring time from mesh because we have a GPS, RTC, or Phone/NTP time source in the past day\n"); + LOG_DEBUG("Ignore time from mesh because we have a GPS, RTC, or Phone/NTP time source in the past day"); return; } if (!isLocal && p.location_source < meshtastic_Position_LocSource_LOC_INTERNAL) { - LOG_DEBUG("Ignoring time from mesh because it has a unknown or manual source\n"); + LOG_DEBUG("Ignore time from mesh because it has a unknown or manual source"); return; } struct timeval tv; @@ -150,15 +146,24 @@ bool PositionModule::hasQualityTimesource() #if MESHTASTIC_EXCLUDE_GPS bool hasGpsOrRtc = (rtc_found.address != ScanI2C::ADDRESS_NONE.address); #else - bool hasGpsOrRtc = (gps && gps->isConnected()) || (rtc_found.address != ScanI2C::ADDRESS_NONE.address); + bool hasGpsOrRtc = hasGPS() || (rtc_found.address != ScanI2C::ADDRESS_NONE.address); #endif return hasGpsOrRtc || setFromPhoneOrNtpToday; } +bool PositionModule::hasGPS() +{ +#if MESHTASTIC_EXCLUDE_GPS + return false; +#else + return gps && gps->isConnected(); +#endif +} + meshtastic_MeshPacket *PositionModule::allocReply() { if (precision == 0) { - LOG_DEBUG("Skipping location send because precision is set to 0!\n"); + LOG_DEBUG("Skip location send because precision is set to 0!"); return nullptr; } @@ -178,12 +183,12 @@ meshtastic_MeshPacket *PositionModule::allocReply() localPosition.seq_number++; if (localPosition.latitude_i == 0 && localPosition.longitude_i == 0) { - LOG_WARN("Skipping position send because lat/lon are zero!\n"); + LOG_WARN("Skip position send because lat/lon are zero!"); return nullptr; } // lat/lon are unconditionally included - IF AVAILABLE! - LOG_DEBUG("Sending location with precision %i\n", precision); + LOG_DEBUG("Send location with precision %i", precision); if (precision < 32 && precision > 0) { p.latitude_i = localPosition.latitude_i & (UINT32_MAX << (32 - precision)); p.longitude_i = localPosition.longitude_i & (UINT32_MAX << (32 - precision)); @@ -198,10 +203,21 @@ meshtastic_MeshPacket *PositionModule::allocReply() p.precision_bits = precision; p.has_latitude_i = true; p.has_longitude_i = true; - p.time = getValidTime(RTCQualityNTP) > 0 ? getValidTime(RTCQualityNTP) : localPosition.time; + // Always use NTP / GPS time if available + if (getValidTime(RTCQualityNTP) > 0) { + p.time = getValidTime(RTCQualityNTP); + } else if (rtc_found.address != ScanI2C::ADDRESS_NONE.address) { + LOG_INFO("Use RTC time for position"); + p.time = getValidTime(RTCQualityDevice); + } else if (getRTCQuality() < RTCQualityNTP) { + LOG_INFO("Strip low RTCQuality (%d) time from position", getRTCQuality()); + p.time = 0; + } if (config.position.fixed_position) { p.location_source = meshtastic_Position_LocSource_LOC_MANUAL; + } else { + p.location_source = localPosition.location_source; } if (pos_flags & meshtastic_Config_PositionConfig_PositionFlags_ALTITUDE) { @@ -246,18 +262,7 @@ meshtastic_MeshPacket *PositionModule::allocReply() p.has_ground_speed = true; } - // 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 or NTP to include the time, so that devices - // without can get time. - if (getRTCQuality() < RTCQualityNTP) { - LOG_INFO("Stripping time %u from position send\n", p.time); - p.time = 0; - } else { - p.time = getValidTime(RTCQualityNTP); - LOG_INFO("Providing time to mesh %u\n", p.time); - } - - LOG_INFO("Position reply: time=%i lat=%i lon=%i\n", p.time, p.latitude_i, p.longitude_i); + LOG_INFO("Position reply: time=%i lat=%i lon=%i", p.time, p.latitude_i, p.longitude_i); // TAK Tracker devices should send their position in a TAK packet over the ATAK port if (config.device.role == meshtastic_Config_DeviceConfig_Role_TAK_TRACKER) @@ -268,7 +273,7 @@ meshtastic_MeshPacket *PositionModule::allocReply() meshtastic_MeshPacket *PositionModule::allocAtakPli() { - LOG_INFO("Sending TAK PLI packet\n"); + LOG_INFO("Send TAK PLI packet"); meshtastic_MeshPacket *mp = allocDataPacket(); mp->decoded.portnum = meshtastic_PortNum_ATAK_PLUGIN; @@ -293,8 +298,8 @@ meshtastic_MeshPacket *PositionModule::allocAtakPli() auto length = unishox2_compress_lines(owner.long_name, strlen(owner.long_name), takPacket.contact.device_callsign, sizeof(takPacket.contact.device_callsign) - 1, USX_PSET_DFLT, NULL); - LOG_DEBUG("Uncompressed device_callsign '%s' - %d bytes\n", owner.long_name, strlen(owner.long_name)); - LOG_DEBUG("Compressed device_callsign '%s' - %d bytes\n", takPacket.contact.device_callsign, length); + LOG_DEBUG("Uncompressed device_callsign '%s' - %d bytes", owner.long_name, strlen(owner.long_name)); + LOG_DEBUG("Compressed device_callsign '%s' - %d bytes", takPacket.contact.device_callsign, length); length = unishox2_compress_lines(owner.long_name, strlen(owner.long_name), takPacket.contact.callsign, sizeof(takPacket.contact.callsign) - 1, USX_PSET_DFLT, NULL); mp->decoded.payload.size = @@ -308,7 +313,7 @@ void PositionModule::sendOurPosition() currentGeneration = radioGeneration; // If we changed channels, ask everyone else for their latest info - LOG_INFO("Sending pos@%x:6 to mesh (wantReplies=%d)\n", localPosition.timestamp, requestReplies); + LOG_INFO("Send pos@%x:6 to mesh (wantReplies=%d)", localPosition.timestamp, requestReplies); sendOurPosition(NODENUM_BROADCAST, requestReplies); } @@ -330,7 +335,7 @@ void PositionModule::sendOurPosition(NodeNum dest, bool wantReplies, uint8_t cha meshtastic_MeshPacket *p = allocReply(); if (p == nullptr) { - LOG_DEBUG("allocReply returned a nullptr\n"); + LOG_DEBUG("allocReply returned a nullptr"); return; } @@ -351,7 +356,7 @@ void PositionModule::sendOurPosition(NodeNum dest, bool wantReplies, uint8_t cha if (IS_ONE_OF(config.device.role, meshtastic_Config_DeviceConfig_Role_TRACKER, meshtastic_Config_DeviceConfig_Role_TAK_TRACKER) && config.power.is_power_saving) { - LOG_DEBUG("Starting next execution in 5 seconds and then going to sleep.\n"); + LOG_DEBUG("Start next execution in 5s, then sleep"); sleepOnNextExecution = true; setIntervalFromNow(5000); } @@ -364,8 +369,8 @@ int32_t PositionModule::runOnce() if (sleepOnNextExecution == true) { sleepOnNextExecution = false; uint32_t nightyNightMs = Default::getConfiguredOrDefaultMs(config.position.position_broadcast_secs); - LOG_DEBUG("Sleeping for %ims, then awaking to send position again.\n", nightyNightMs); - doDeepSleep(nightyNightMs, false); + LOG_DEBUG("Sleep for %ims, then awaking to send position again", nightyNightMs); + doDeepSleep(nightyNightMs, false, false); } meshtastic_NodeInfoLite *node = nodeDB->getMeshNode(nodeDB->getNodeNum()); @@ -384,7 +389,7 @@ int32_t PositionModule::runOnce() } if (lastGpsSend == 0 || msSinceLastSend >= intervalMs) { - if (hasValidPosition(node)) { + if (nodeDB->hasValidPosition(node)) { lastGpsSend = now; lastGpsLatitude = node->position.latitude_i; @@ -398,7 +403,7 @@ int32_t PositionModule::runOnce() } else if (config.position.position_broadcast_smart_enabled) { const meshtastic_NodeInfoLite *node2 = service->refreshLocalMeshNode(); // should guarantee there is now a position - if (hasValidPosition(node2)) { + if (nodeDB->hasValidPosition(node2)) { // The minimum time (in seconds) that would pass before we are able to send a new position packet. auto smartPosition = getDistanceTraveledSinceLastSend(node->position); @@ -407,10 +412,10 @@ int32_t PositionModule::runOnce() if (smartPosition.hasTraveledOverThreshold && Throttle::execute( &lastGpsSend, minimumTimeThreshold, []() { positionModule->sendOurPosition(); }, - []() { LOG_DEBUG("Skipping send smart broadcast due to time throttling\n"); })) { + []() { LOG_DEBUG("Skip send smart broadcast due to time throttling"); })) { LOG_DEBUG("Sent smart pos@%x:6 to mesh (distanceTraveled=%fm, minDistanceThreshold=%im, timeElapsed=%ims, " - "minTimeInterval=%ims)\n", + "minTimeInterval=%ims)", localPosition.timestamp, smartPosition.distanceTraveled, smartPosition.distanceThreshold, msSinceLastSend, minimumTimeThreshold); @@ -449,23 +454,6 @@ struct SmartPosition PositionModule::getDistanceTraveledSinceLastSend(meshtastic float distanceTraveledSinceLastSend = GeoCoord::latLongToMeter( lastGpsLatitude * 1e-7, lastGpsLongitude * 1e-7, currentPosition.latitude_i * 1e-7, currentPosition.longitude_i * 1e-7); -#ifdef GPS_EXTRAVERBOSE - LOG_DEBUG("--------LAST POSITION------------------------------------\n"); - LOG_DEBUG("lastGpsLatitude=%i, lastGpsLatitude=%i\n", lastGpsLatitude, lastGpsLongitude); - - LOG_DEBUG("--------CURRENT POSITION---------------------------------\n"); - LOG_DEBUG("currentPosition.latitude_i=%i, currentPosition.longitude_i=%i\n", lastGpsLatitude, lastGpsLongitude); - - LOG_DEBUG("--------SMART POSITION-----------------------------------\n"); - LOG_DEBUG("hasTraveledOverThreshold=%i, distanceTraveled=%f, distanceThreshold=%f\n", - abs(distanceTraveledSinceLastSend) >= distanceTravelThreshold, abs(distanceTraveledSinceLastSend), - distanceTravelThreshold); - - if (abs(distanceTraveledSinceLastSend) >= distanceTravelThreshold) { - LOG_DEBUG("\n\n\nSMART SEEEEEEEEENDING\n\n\n"); - } -#endif - return SmartPosition{.distanceTraveled = abs(distanceTraveledSinceLastSend), .distanceThreshold = distanceTravelThreshold, .hasTraveledOverThreshold = abs(distanceTraveledSinceLastSend) >= distanceTravelThreshold}; @@ -476,15 +464,15 @@ void PositionModule::handleNewPosition() meshtastic_NodeInfoLite *node = nodeDB->getMeshNode(nodeDB->getNodeNum()); const meshtastic_NodeInfoLite *node2 = service->refreshLocalMeshNode(); // should guarantee there is now a position // We limit our GPS broadcasts to a max rate - if (hasValidPosition(node2)) { + if (nodeDB->hasValidPosition(node2)) { auto smartPosition = getDistanceTraveledSinceLastSend(node->position); uint32_t msSinceLastSend = millis() - lastGpsSend; if (smartPosition.hasTraveledOverThreshold && Throttle::execute( &lastGpsSend, minimumTimeThreshold, []() { positionModule->sendOurPosition(); }, - []() { LOG_DEBUG("Skipping send smart broadcast due to time throttling\n"); })) { + []() { LOG_DEBUG("Skip send smart broadcast due to time throttling"); })) { LOG_DEBUG("Sent smart pos@%x:6 to mesh (distanceTraveled=%fm, minDistanceThreshold=%im, timeElapsed=%ims, " - "minTimeInterval=%ims)\n", + "minTimeInterval=%ims)", localPosition.timestamp, smartPosition.distanceTraveled, smartPosition.distanceThreshold, msSinceLastSend, minimumTimeThreshold); diff --git a/src/modules/PositionModule.h b/src/modules/PositionModule.h index 41b86b795..1e4aa5d29 100644 --- a/src/modules/PositionModule.h +++ b/src/modules/PositionModule.h @@ -61,6 +61,7 @@ class PositionModule : public ProtobufModule, private concu uint32_t precision; void sendLostAndFoundText(); bool hasQualityTimesource(); + bool hasGPS(); const uint32_t minimumTimeThreshold = Default::getConfiguredOrDefaultMs(config.position.broadcast_smart_minimum_interval_secs, 30); diff --git a/src/modules/PowerStressModule.cpp b/src/modules/PowerStressModule.cpp index 48159ba54..d487fe6fc 100644 --- a/src/modules/PowerStressModule.cpp +++ b/src/modules/PowerStressModule.cpp @@ -15,7 +15,7 @@ extern void printInfo(); PowerStressModule::PowerStressModule() : ProtobufModule("powerstress", meshtastic_PortNum_POWERSTRESS_APP, &meshtastic_PowerStressMessage_msg), - concurrency::OSThread("PowerStressModule") + concurrency::OSThread("PowerStress") { } @@ -24,12 +24,12 @@ bool PowerStressModule::handleReceivedProtobuf(const meshtastic_MeshPacket &req, // We only respond to messages if powermon debugging is already on if (config.power.powermon_enables) { auto p = *pptr; - LOG_INFO("Received PowerStress cmd=%d\n", p.cmd); + LOG_INFO("Received PowerStress cmd=%d", p.cmd); // Some commands we can handle immediately, anything else gets deferred to be handled by our thread switch (p.cmd) { case meshtastic_PowerStressMessage_Opcode_UNSET: - LOG_ERROR("PowerStress operation unset\n"); + LOG_ERROR("PowerStress operation unset"); break; case meshtastic_PowerStressMessage_Opcode_PRINT_INFO: @@ -42,7 +42,7 @@ bool PowerStressModule::handleReceivedProtobuf(const meshtastic_MeshPacket &req, default: if (currentMessage.cmd != meshtastic_PowerStressMessage_Opcode_UNSET) - LOG_ERROR("PowerStress operation %d already in progress! Can't start new command\n", currentMessage.cmd); + LOG_ERROR("PowerStress operation %d already in progress! Can't start new command", currentMessage.cmd); else currentMessage = p; // copy for use by thread (the message provided to us will be getting freed) break; @@ -67,13 +67,13 @@ int32_t PowerStressModule::runOnce() p.cmd = meshtastic_PowerStressMessage_Opcode_UNSET; p.num_seconds = 0; isRunningCommand = false; - LOG_INFO("S:PS:%u\n", p.cmd); + LOG_INFO("S:PS:%u", p.cmd); } else { if (p.cmd != meshtastic_PowerStressMessage_Opcode_UNSET) { sleep_msec = (int32_t)(p.num_seconds * 1000); isRunningCommand = !!sleep_msec; // if the command wants us to sleep, make sure to mark that we have something running LOG_INFO( - "S:PS:%u\n", + "S:PS:%u", p.cmd); // Emit a structured log saying we are starting a powerstress state (to make it easier to parse later) switch (p.cmd) { @@ -111,7 +111,7 @@ int32_t PowerStressModule::runOnce() setBluetoothEnable(true); break; case meshtastic_PowerStressMessage_Opcode_CPU_DEEPSLEEP: - doDeepSleep(sleep_msec, true); + doDeepSleep(sleep_msec, true, true); break; case meshtastic_PowerStressMessage_Opcode_CPU_FULLON: { uint32_t start_msec = millis(); @@ -124,7 +124,7 @@ int32_t PowerStressModule::runOnce() // FIXME - implement break; default: - LOG_ERROR("PowerStress operation %d not yet implemented!\n", p.cmd); + LOG_ERROR("PowerStress operation %d not yet implemented!", p.cmd); sleep_msec = 0; // Don't do whatever sleep was requested... break; } diff --git a/src/modules/RangeTestModule.cpp b/src/modules/RangeTestModule.cpp index e78b4e68d..bf842ce55 100644 --- a/src/modules/RangeTestModule.cpp +++ b/src/modules/RangeTestModule.cpp @@ -24,7 +24,7 @@ RangeTestModule *rangeTestModule; RangeTestModuleRadio *rangeTestModuleRadio; -RangeTestModule::RangeTestModule() : concurrency::OSThread("RangeTestModule") {} +RangeTestModule::RangeTestModule() : concurrency::OSThread("RangeTest") {} uint32_t packetSequence = 0; @@ -54,11 +54,11 @@ int32_t RangeTestModule::runOnce() firstTime = 0; if (moduleConfig.range_test.sender) { - LOG_INFO("Initializing Range Test Module -- Sender\n"); + LOG_INFO("Init Range Test Module -- Sender"); started = millis(); // make a note of when we started return (5000); // Sending first message 5 seconds after initialization. } else { - LOG_INFO("Initializing Range Test Module -- Receiver\n"); + LOG_INFO("Init Range Test Module -- Receiver"); return disable(); // This thread does not need to run as a receiver } @@ -66,13 +66,13 @@ int32_t RangeTestModule::runOnce() if (moduleConfig.range_test.sender) { // If sender - LOG_INFO("Range Test Module - Sending heartbeat every %d ms\n", (senderHeartbeat)); + LOG_INFO("Range Test Module - Sending heartbeat every %d ms", (senderHeartbeat)); - LOG_INFO("gpsStatus->getLatitude() %d\n", gpsStatus->getLatitude()); - LOG_INFO("gpsStatus->getLongitude() %d\n", gpsStatus->getLongitude()); - LOG_INFO("gpsStatus->getHasLock() %d\n", gpsStatus->getHasLock()); - LOG_INFO("gpsStatus->getDOP() %d\n", gpsStatus->getDOP()); - LOG_INFO("fixed_position() %d\n", config.position.fixed_position); + LOG_INFO("gpsStatus->getLatitude() %d", gpsStatus->getLatitude()); + LOG_INFO("gpsStatus->getLongitude() %d", gpsStatus->getLongitude()); + LOG_INFO("gpsStatus->getHasLock() %d", gpsStatus->getHasLock()); + LOG_INFO("gpsStatus->getDOP() %d", gpsStatus->getDOP()); + LOG_INFO("fixed_position() %d", config.position.fixed_position); // Only send packets if the channel is less than 25% utilized. if (airTime->isTxAllowedChannelUtil(true)) { @@ -81,7 +81,7 @@ int32_t RangeTestModule::runOnce() // If we have been running for more than 8 hours, turn module back off if (!Throttle::isWithinTimespanMs(started, 28800000)) { - LOG_INFO("Range Test Module - Disabling after 8 hours\n"); + LOG_INFO("Range Test Module - Disable after 8 hours"); return disable(); } else { return (senderHeartbeat); @@ -92,7 +92,7 @@ int32_t RangeTestModule::runOnce() } } } else { - LOG_INFO("Range Test Module - Disabled\n"); + LOG_INFO("Range Test Module - Disabled"); } #endif @@ -135,7 +135,7 @@ ProcessMessage RangeTestModuleRadio::handleReceived(const meshtastic_MeshPacket /* auto &p = mp.decoded; - LOG_DEBUG("Received text msg self=0x%0x, from=0x%0x, to=0x%0x, id=%d, msg=%.*s\n", + LOG_DEBUG("Received text msg self=0x%0x, from=0x%0x, to=0x%0x, id=%d, msg=%.*s", LOG_INFO.getNodeNum(), mp.from, mp.to, mp.id, p.payload.size, p.payload.bytes); */ @@ -148,31 +148,31 @@ ProcessMessage RangeTestModuleRadio::handleReceived(const meshtastic_MeshPacket /* NodeInfoLite *n = nodeDB->getMeshNode(getFrom(&mp)); - LOG_DEBUG("-----------------------------------------\n"); - LOG_DEBUG("p.payload.bytes \"%s\"\n", p.payload.bytes); - LOG_DEBUG("p.payload.size %d\n", p.payload.size); - LOG_DEBUG("---- Received Packet:\n"); - LOG_DEBUG("mp.from %d\n", mp.from); - LOG_DEBUG("mp.rx_snr %f\n", mp.rx_snr); - LOG_DEBUG("mp.hop_limit %d\n", mp.hop_limit); - // LOG_DEBUG("mp.decoded.position.latitude_i %d\n", mp.decoded.position.latitude_i); // Deprecated - // LOG_DEBUG("mp.decoded.position.longitude_i %d\n", mp.decoded.position.longitude_i); // Deprecated - LOG_DEBUG("---- Node Information of Received Packet (mp.from):\n"); - LOG_DEBUG("n->user.long_name %s\n", n->user.long_name); - LOG_DEBUG("n->user.short_name %s\n", n->user.short_name); - LOG_DEBUG("n->has_position %d\n", n->has_position); - LOG_DEBUG("n->position.latitude_i %d\n", n->position.latitude_i); - LOG_DEBUG("n->position.longitude_i %d\n", n->position.longitude_i); - LOG_DEBUG("---- Current device location information:\n"); - LOG_DEBUG("gpsStatus->getLatitude() %d\n", gpsStatus->getLatitude()); - LOG_DEBUG("gpsStatus->getLongitude() %d\n", gpsStatus->getLongitude()); - LOG_DEBUG("gpsStatus->getHasLock() %d\n", gpsStatus->getHasLock()); - LOG_DEBUG("gpsStatus->getDOP() %d\n", gpsStatus->getDOP()); - LOG_DEBUG("-----------------------------------------\n"); + LOG_DEBUG("-----------------------------------------"); + LOG_DEBUG("p.payload.bytes \"%s\"", p.payload.bytes); + LOG_DEBUG("p.payload.size %d", p.payload.size); + LOG_DEBUG("---- Received Packet:"); + LOG_DEBUG("mp.from %d", mp.from); + LOG_DEBUG("mp.rx_snr %f", mp.rx_snr); + LOG_DEBUG("mp.hop_limit %d", mp.hop_limit); + // LOG_DEBUG("mp.decoded.position.latitude_i %d", mp.decoded.position.latitude_i); // Deprecated + // LOG_DEBUG("mp.decoded.position.longitude_i %d", mp.decoded.position.longitude_i); // Deprecated + LOG_DEBUG("---- Node Information of Received Packet (mp.from):"); + LOG_DEBUG("n->user.long_name %s", n->user.long_name); + LOG_DEBUG("n->user.short_name %s", n->user.short_name); + LOG_DEBUG("n->has_position %d", n->has_position); + LOG_DEBUG("n->position.latitude_i %d", n->position.latitude_i); + LOG_DEBUG("n->position.longitude_i %d", n->position.longitude_i); + LOG_DEBUG("---- Current device location information:"); + LOG_DEBUG("gpsStatus->getLatitude() %d", gpsStatus->getLatitude()); + LOG_DEBUG("gpsStatus->getLongitude() %d", gpsStatus->getLongitude()); + LOG_DEBUG("gpsStatus->getHasLock() %d", gpsStatus->getHasLock()); + LOG_DEBUG("gpsStatus->getDOP() %d", gpsStatus->getDOP()); + LOG_DEBUG("-----------------------------------------"); */ } } else { - LOG_INFO("Range Test Module Disabled\n"); + LOG_INFO("Range Test Module Disabled"); } #endif @@ -187,35 +187,35 @@ bool RangeTestModuleRadio::appendFile(const meshtastic_MeshPacket &mp) meshtastic_NodeInfoLite *n = nodeDB->getMeshNode(getFrom(&mp)); /* - LOG_DEBUG("-----------------------------------------\n"); - LOG_DEBUG("p.payload.bytes \"%s\"\n", p.payload.bytes); - LOG_DEBUG("p.payload.size %d\n", p.payload.size); - LOG_DEBUG("---- Received Packet:\n"); - LOG_DEBUG("mp.from %d\n", mp.from); - LOG_DEBUG("mp.rx_snr %f\n", mp.rx_snr); - LOG_DEBUG("mp.hop_limit %d\n", mp.hop_limit); - // LOG_DEBUG("mp.decoded.position.latitude_i %d\n", mp.decoded.position.latitude_i); // Deprecated - // LOG_DEBUG("mp.decoded.position.longitude_i %d\n", mp.decoded.position.longitude_i); // Deprecated - LOG_DEBUG("---- Node Information of Received Packet (mp.from):\n"); - LOG_DEBUG("n->user.long_name %s\n", n->user.long_name); - LOG_DEBUG("n->user.short_name %s\n", n->user.short_name); - LOG_DEBUG("n->has_position %d\n", n->has_position); - LOG_DEBUG("n->position.latitude_i %d\n", n->position.latitude_i); - LOG_DEBUG("n->position.longitude_i %d\n", n->position.longitude_i); - LOG_DEBUG("---- Current device location information:\n"); - LOG_DEBUG("gpsStatus->getLatitude() %d\n", gpsStatus->getLatitude()); - LOG_DEBUG("gpsStatus->getLongitude() %d\n", gpsStatus->getLongitude()); - LOG_DEBUG("gpsStatus->getHasLock() %d\n", gpsStatus->getHasLock()); - LOG_DEBUG("gpsStatus->getDOP() %d\n", gpsStatus->getDOP()); - LOG_DEBUG("-----------------------------------------\n"); + LOG_DEBUG("-----------------------------------------"); + LOG_DEBUG("p.payload.bytes \"%s\"", p.payload.bytes); + LOG_DEBUG("p.payload.size %d", p.payload.size); + LOG_DEBUG("---- Received Packet:"); + LOG_DEBUG("mp.from %d", mp.from); + LOG_DEBUG("mp.rx_snr %f", mp.rx_snr); + LOG_DEBUG("mp.hop_limit %d", mp.hop_limit); + // LOG_DEBUG("mp.decoded.position.latitude_i %d", mp.decoded.position.latitude_i); // Deprecated + // LOG_DEBUG("mp.decoded.position.longitude_i %d", mp.decoded.position.longitude_i); // Deprecated + LOG_DEBUG("---- Node Information of Received Packet (mp.from):"); + LOG_DEBUG("n->user.long_name %s", n->user.long_name); + LOG_DEBUG("n->user.short_name %s", n->user.short_name); + LOG_DEBUG("n->has_position %d", n->has_position); + LOG_DEBUG("n->position.latitude_i %d", n->position.latitude_i); + LOG_DEBUG("n->position.longitude_i %d", n->position.longitude_i); + LOG_DEBUG("---- Current device location information:"); + LOG_DEBUG("gpsStatus->getLatitude() %d", gpsStatus->getLatitude()); + LOG_DEBUG("gpsStatus->getLongitude() %d", gpsStatus->getLongitude()); + LOG_DEBUG("gpsStatus->getHasLock() %d", gpsStatus->getHasLock()); + LOG_DEBUG("gpsStatus->getDOP() %d", gpsStatus->getDOP()); + LOG_DEBUG("-----------------------------------------"); */ if (!FSBegin()) { - LOG_DEBUG("An Error has occurred while mounting the filesystem\n"); + LOG_DEBUG("An Error has occurred while mounting the filesystem"); return 0; } if (FSCom.totalBytes() - FSCom.usedBytes() < 51200) { - LOG_DEBUG("Filesystem doesn't have enough free space. Aborting write.\n"); + LOG_DEBUG("Filesystem doesn't have enough free space. Aborting write"); return 0; } @@ -227,16 +227,16 @@ bool RangeTestModuleRadio::appendFile(const meshtastic_MeshPacket &mp) File fileToWrite = FSCom.open("/static/rangetest.csv", FILE_WRITE); if (!fileToWrite) { - LOG_ERROR("There was an error opening the file for writing\n"); + LOG_ERROR("There was an error opening the file for writing"); return 0; } // Print the CSV header if (fileToWrite.println( "time,from,sender name,sender lat,sender long,rx lat,rx long,rx elevation,rx snr,distance,hop limit,payload")) { - LOG_INFO("File was written\n"); + LOG_INFO("File was written"); } else { - LOG_ERROR("File write failed\n"); + LOG_ERROR("File write failed"); } fileToWrite.flush(); fileToWrite.close(); @@ -246,7 +246,7 @@ bool RangeTestModuleRadio::appendFile(const meshtastic_MeshPacket &mp) File fileToAppend = FSCom.open("/static/rangetest.csv", FILE_APPEND); if (!fileToAppend) { - LOG_ERROR("There was an error opening the file for appending\n"); + LOG_ERROR("There was an error opening the file for appending"); return 0; } @@ -292,4 +292,4 @@ bool RangeTestModuleRadio::appendFile(const meshtastic_MeshPacket &mp) #endif return 1; -} \ No newline at end of file +} diff --git a/src/modules/RemoteHardwareModule.cpp b/src/modules/RemoteHardwareModule.cpp index 43612e450..9bc8512b6 100644 --- a/src/modules/RemoteHardwareModule.cpp +++ b/src/modules/RemoteHardwareModule.cpp @@ -64,7 +64,7 @@ static uint64_t digitalReads(uint64_t mask, uint64_t maskAvailable) RemoteHardwareModule::RemoteHardwareModule() : ProtobufModule("remotehardware", meshtastic_PortNum_REMOTE_HARDWARE_APP, &meshtastic_HardwareMessage_msg), - concurrency::OSThread("RemoteHardwareModule") + concurrency::OSThread("RemoteHardware") { // restrict to the gpio channel for rx boundChannel = Channels::gpioChannel; @@ -79,7 +79,7 @@ bool RemoteHardwareModule::handleReceivedProtobuf(const meshtastic_MeshPacket &r { if (moduleConfig.remote_hardware.enabled) { auto p = *pptr; - LOG_INFO("Received RemoteHardware type=%d\n", p.type); + LOG_INFO("Received RemoteHardware type=%d", p.type); switch (p.type) { case meshtastic_HardwareMessage_Type_WRITE_GPIOS: { @@ -122,7 +122,7 @@ bool RemoteHardwareModule::handleReceivedProtobuf(const meshtastic_MeshPacket &r ~watchGpios; // generate a 'previous' value which is guaranteed to not match (to force an initial publish) enabled = true; // Let our thread run at least once setInterval(2000); // Set a new interval so we'll run soon - LOG_INFO("Now watching GPIOs 0x%llx\n", watchGpios); + LOG_INFO("Now watching GPIOs 0x%llx", watchGpios); break; } @@ -131,7 +131,7 @@ bool RemoteHardwareModule::handleReceivedProtobuf(const meshtastic_MeshPacket &r break; // Ignore - we might see our own replies default: - LOG_ERROR("Hardware operation %d not yet implemented! FIXME\n", p.type); + LOG_ERROR("Hardware operation %d not yet implemented! FIXME", p.type); break; } } @@ -149,7 +149,7 @@ int32_t RemoteHardwareModule::runOnce() if (curVal != previousWatch) { previousWatch = curVal; - LOG_INFO("Broadcasting GPIOS 0x%llx changed!\n", curVal); + LOG_INFO("Broadcast GPIOS 0x%llx changed!", curVal); // Something changed! Tell the world with a broadcast message meshtastic_HardwareMessage r = meshtastic_HardwareMessage_init_default; diff --git a/src/modules/ReplyModule.cpp b/src/modules/ReplyModule.cpp index 439f6b7f7..c4f63c6b1 100644 --- a/src/modules/ReplyModule.cpp +++ b/src/modules/ReplyModule.cpp @@ -12,10 +12,10 @@ meshtastic_MeshPacket *ReplyModule::allocReply() auto req = *currentRequest; auto &p = req.decoded; // The incoming message is in p.payload - LOG_INFO("Received message from=0x%0x, id=%d, msg=%.*s\n", req.from, req.id, p.payload.size, p.payload.bytes); + LOG_INFO("Received message from=0x%0x, id=%d, msg=%.*s", req.from, req.id, p.payload.size, p.payload.bytes); #endif - screen->print("Sending reply\n"); + screen->print("Send reply\n"); const char *replyStr = "Message Received"; auto reply = allocDataPacket(); // Allocate a packet for sending diff --git a/src/modules/RoutingModule.cpp b/src/modules/RoutingModule.cpp index 3b7be1ab7..a501e319b 100644 --- a/src/modules/RoutingModule.cpp +++ b/src/modules/RoutingModule.cpp @@ -10,11 +10,7 @@ RoutingModule *routingModule; bool RoutingModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp, meshtastic_Routing *r) { - printPacket("Routing sniffing", &mp); - router->sniffReceived(&mp, r); - - bool maybePKI = - mp.which_payload_variant == meshtastic_MeshPacket_encrypted_tag && mp.channel == 0 && mp.to != NODENUM_BROADCAST; + bool maybePKI = mp.which_payload_variant == meshtastic_MeshPacket_encrypted_tag && mp.channel == 0 && !isBroadcast(mp.to); // Beginning of logic whether to drop the packet based on Rebroadcast mode if (mp.which_payload_variant == meshtastic_MeshPacket_encrypted_tag && (config.device.rebroadcast_mode == meshtastic_Config_DeviceConfig_RebroadcastMode_LOCAL_ONLY || @@ -26,9 +22,12 @@ bool RoutingModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp, mesh return false; } + printPacket("Routing sniffing", &mp); + router->sniffReceived(&mp, r); + // FIXME - move this to a non promsicious PhoneAPI module? // Note: we are careful not to send back packets that started with the phone back to the phone - if ((mp.to == NODENUM_BROADCAST || isToUs(&mp)) && (mp.from != 0)) { + if ((isBroadcast(mp.to) || isToUs(&mp)) && (mp.from != 0)) { printPacket("Delivering rx packet", &mp); service->handleFromRadio(&mp); } @@ -50,10 +49,9 @@ meshtastic_MeshPacket *RoutingModule::allocReply() return NULL; } -void RoutingModule::sendAckNak(meshtastic_Routing_Error err, NodeNum to, PacketId idFrom, ChannelIndex chIndex, uint8_t hopStart, - uint8_t hopLimit) +void RoutingModule::sendAckNak(meshtastic_Routing_Error err, NodeNum to, PacketId idFrom, ChannelIndex chIndex, uint8_t hopLimit) { - auto p = allocAckNak(err, to, idFrom, chIndex, hopStart, hopLimit); + auto p = allocAckNak(err, to, idFrom, chIndex, hopLimit); router->sendLocal(p); // we sometimes send directly to the local node } @@ -79,7 +77,7 @@ RoutingModule::RoutingModule() : ProtobufModule("routing", meshtastic_PortNum_RO { isPromiscuous = true; - // moved the ReboradcastMode logic into handleReceivedProtobuf + // moved the RebroadcastMode logic into handleReceivedProtobuf // LocalOnly requires either the from or to to be a known node // knownOnly specifically requires the from to be a known node. encryptedOk = true; diff --git a/src/modules/RoutingModule.h b/src/modules/RoutingModule.h index f085b307b..7c34c5bc9 100644 --- a/src/modules/RoutingModule.h +++ b/src/modules/RoutingModule.h @@ -13,8 +13,7 @@ class RoutingModule : public ProtobufModule */ RoutingModule(); - void sendAckNak(meshtastic_Routing_Error err, NodeNum to, PacketId idFrom, ChannelIndex chIndex, uint8_t hopStart = 0, - uint8_t hopLimit = 0); + void sendAckNak(meshtastic_Routing_Error err, NodeNum to, PacketId idFrom, ChannelIndex chIndex, uint8_t hopLimit = 0); // Given the hopStart and hopLimit upon reception of a request, return the hop limit to use for the response uint8_t getHopLimitForResponse(uint8_t hopStart, uint8_t hopLimit); @@ -36,4 +35,4 @@ class RoutingModule : public ProtobufModule virtual bool wantPacket(const meshtastic_MeshPacket *p) override { return true; } }; -extern RoutingModule *routingModule; +extern RoutingModule *routingModule; \ No newline at end of file diff --git a/src/modules/SerialModule.cpp b/src/modules/SerialModule.cpp index d40b59345..bf53b1748 100644 --- a/src/modules/SerialModule.cpp +++ b/src/modules/SerialModule.cpp @@ -61,13 +61,13 @@ SerialModule *serialModule; SerialModuleRadio *serialModuleRadio; #if defined(TTGO_T_ECHO) || defined(CANARYONE) -SerialModule::SerialModule() : StreamAPI(&Serial), concurrency::OSThread("SerialModule") {} +SerialModule::SerialModule() : StreamAPI(&Serial), concurrency::OSThread("Serial") {} static Print *serialPrint = &Serial; #elif defined(CONFIG_IDF_TARGET_ESP32C6) -SerialModule::SerialModule() : StreamAPI(&Serial1), concurrency::OSThread("SerialModule") {} +SerialModule::SerialModule() : StreamAPI(&Serial1), concurrency::OSThread("Serial") {} static Print *serialPrint = &Serial1; #else -SerialModule::SerialModule() : StreamAPI(&Serial2), concurrency::OSThread("SerialModule") {} +SerialModule::SerialModule() : StreamAPI(&Serial2), concurrency::OSThread("Serial") {} static Print *serialPrint = &Serial2; #endif @@ -125,7 +125,7 @@ int32_t SerialModule::runOnce() if (moduleConfig.serial.override_console_serial_port || (moduleConfig.serial.rxd && moduleConfig.serial.txd)) { if (firstTime) { // Interface with the serial peripheral from in here. - LOG_INFO("Initializing serial peripheral interface\n"); + LOG_INFO("Init serial peripheral interface"); uint32_t baud = getBaudRate(); @@ -204,9 +204,11 @@ int32_t SerialModule::runOnce() lastNmeaTime = millis(); uint32_t readIndex = 0; const meshtastic_NodeInfoLite *tempNodeInfo = nodeDB->readNextMeshNode(readIndex); - while (tempNodeInfo != NULL && tempNodeInfo->has_user && hasValidPosition(tempNodeInfo)) { - printWPL(outbuf, sizeof(outbuf), tempNodeInfo->position, tempNodeInfo->user.long_name, true); - serialPrint->printf("%s", outbuf); + while (tempNodeInfo != NULL) { + if (tempNodeInfo->has_user && nodeDB->hasValidPosition(tempNodeInfo)) { + printWPL(outbuf, sizeof(outbuf), tempNodeInfo->position, tempNodeInfo->user.long_name, true); + serialPrint->printf("%s", outbuf); + } tempNodeInfo = nodeDB->readNextMeshNode(readIndex); } } @@ -252,7 +254,12 @@ void SerialModule::sendTelemetry(meshtastic_Telemetry m) pb_encode_to_bytes(p->decoded.payload.bytes, sizeof(p->decoded.payload.bytes), &meshtastic_Telemetry_msg, &m); p->to = NODENUM_BROADCAST; p->decoded.want_response = false; - p->priority = meshtastic_MeshPacket_Priority_RELIABLE; + if (config.device.role == meshtastic_Config_DeviceConfig_Role_SENSOR) { + p->want_ack = true; + p->priority = meshtastic_MeshPacket_Priority_HIGH; + } else { + p->priority = meshtastic_MeshPacket_Priority_RELIABLE; + } service->sendToMesh(p, RX_SRC_LOCAL, true); } @@ -307,10 +314,10 @@ ProcessMessage SerialModuleRadio::handleReceived(const meshtastic_MeshPacket &mp } auto &p = mp.decoded; - // LOG_DEBUG("Received text msg self=0x%0x, from=0x%0x, to=0x%0x, id=%d, msg=%.*s\n", + // LOG_DEBUG("Received text msg self=0x%0x, from=0x%0x, to=0x%0x, id=%d, msg=%.*s", // nodeDB->getNodeNum(), mp.from, mp.to, mp.id, p.payload.size, p.payload.bytes); - if (!isFromUs(&mp)) { + if (isFromUs(&mp)) { /* * If moduleConfig.serial.echo is true, then echo the packets that are sent out @@ -322,7 +329,7 @@ ProcessMessage SerialModuleRadio::handleReceived(const meshtastic_MeshPacket &mp // TODO: need to find out why. if (lastRxID != mp.id) { lastRxID = mp.id; - // LOG_DEBUG("* * Message came this device\n"); + // LOG_DEBUG("* * Message came this device"); // serialPrint->println("* * Message came this device"); serialPrint->printf("%s", p.payload.bytes); } @@ -424,8 +431,10 @@ void SerialModule::processWXSerial() static char windGust[5] = "xx.x"; // Assuming windGust is 4 characters long + null terminator static char batVoltage[5] = "0.0V"; static char capVoltage[5] = "0.0V"; + static char temperature[5] = "00.0"; static float batVoltageF = 0; static float capVoltageF = 0; + static float temperatureF = 0; bool gotwind = false; while (Serial2.available()) { @@ -465,7 +474,7 @@ void SerialModule::processWXSerial() if (windDirPos != NULL) { // Extract data after "=" for WindDir strcpy(windDir, windDirPos + 15); // Add 15 to skip "WindDir = " - double radians = toRadians(strtof(windDir, nullptr)); + double radians = GeoCoord::toRadians(strtof(windDir, nullptr)); dir_sum_sin += sin(radians); dir_sum_cos += cos(radians); dirCount++; @@ -499,6 +508,13 @@ void SerialModule::processWXSerial() strcpy(capVoltage, capVoltagePos + 17); // 18 for ws 80, 17 for ws85 capVoltageF = strtof(capVoltage, nullptr); } + // GXTS04Temp = 24.4 + } else if (strstr(line, "GXTS04Temp") != NULL) { // we have a temperature line + char *tempPos = strstr(line, "GXTS04Temp = "); + if (tempPos != NULL) { + strcpy(temperature, tempPos + 15); // 15 spaces for ws85 + temperatureF = strtof(temperature, nullptr); + } } // Update lineStart for the next line @@ -514,18 +530,18 @@ void SerialModule::processWXSerial() } if (gotwind) { - LOG_INFO("WS85 : %i %.1fg%.1f %.1fv %.1fv\n", atoi(windDir), strtof(windVel, nullptr), strtof(windGust, nullptr), - batVoltageF, capVoltageF); + LOG_INFO("WS85 : %i %.1fg%.1f %.1fv %.1fv %.1fC", atoi(windDir), strtof(windVel, nullptr), strtof(windGust, nullptr), + batVoltageF, capVoltageF, temperatureF); } if (gotwind && !Throttle::isWithinTimespanMs(lastAveraged, averageIntervalMillis)) { - // calulate averages and send to the mesh + // calculate averages and send to the mesh float velAvg = 1.0 * velSum / velCount; double avgSin = dir_sum_sin / dirCount; double avgCos = dir_sum_cos / dirCount; double avgRadians = atan2(avgSin, avgCos); - float dirAvg = toDegrees(avgRadians); + float dirAvg = GeoCoord::toDegrees(avgRadians); if (dirAvg < 0) { dirAvg += 360.0; @@ -535,17 +551,32 @@ void SerialModule::processWXSerial() // make a telemetry packet with the data meshtastic_Telemetry m = meshtastic_Telemetry_init_zero; m.which_variant = meshtastic_Telemetry_environment_metrics_tag; + m.variant.environment_metrics.wind_speed = velAvg; + m.variant.environment_metrics.has_wind_speed = true; + m.variant.environment_metrics.wind_direction = dirAvg; - m.variant.environment_metrics.wind_gust = gust; - m.variant.environment_metrics.wind_lull = lull; + m.variant.environment_metrics.has_wind_direction = true; + + m.variant.environment_metrics.temperature = temperatureF; + m.variant.environment_metrics.has_temperature = true; + m.variant.environment_metrics.voltage = capVoltageF > batVoltageF ? capVoltageF : batVoltageF; // send the larger of the two voltage values. + m.variant.environment_metrics.has_voltage = true; - LOG_INFO("WS85 Transmit speed=%fm/s, direction=%d , lull=%f, gust=%f, voltage=%f\n", + m.variant.environment_metrics.wind_gust = gust; + m.variant.environment_metrics.has_wind_gust = true; + + if (lull == -1) + lull = 0; + m.variant.environment_metrics.wind_lull = lull; + m.variant.environment_metrics.has_wind_lull = true; + + LOG_INFO("WS85 Transmit speed=%fm/s, direction=%d , lull=%f, gust=%f, voltage=%f temperature=%f", m.variant.environment_metrics.wind_speed, m.variant.environment_metrics.wind_direction, m.variant.environment_metrics.wind_lull, m.variant.environment_metrics.wind_gust, - m.variant.environment_metrics.voltage); + m.variant.environment_metrics.voltage, m.variant.environment_metrics.temperature); sendTelemetry(m); diff --git a/src/modules/StoreForwardModule.cpp b/src/modules/StoreForwardModule.cpp index e0092839f..4cf06f5d2 100644 --- a/src/modules/StoreForwardModule.cpp +++ b/src/modules/StoreForwardModule.cpp @@ -46,7 +46,7 @@ int32_t StoreForwardModule::runOnce() } else if (this->heartbeat && (!Throttle::isWithinTimespanMs(lastHeartbeat, heartbeatInterval * 1000)) && airTime->isTxAllowedChannelUtil(true)) { lastHeartbeat = millis(); - LOG_INFO("Sending heartbeat\n"); + LOG_INFO("Send heartbeat"); meshtastic_StoreAndForward sf = meshtastic_StoreAndForward_init_zero; sf.rr = meshtastic_StoreAndForward_RequestResponse_ROUTER_HEARTBEAT; sf.which_variant = meshtastic_StoreAndForward_heartbeat_tag; @@ -70,7 +70,7 @@ void StoreForwardModule::populatePSRAM() https://learn.upesy.com/en/programmation/psram.html#psram-tab */ - LOG_DEBUG("Before PSRAM init: heap %d/%d PSRAM %d/%d\n", memGet.getFreeHeap(), memGet.getHeapSize(), memGet.getFreePsram(), + LOG_DEBUG("Before PSRAM init: heap %d/%d PSRAM %d/%d", memGet.getFreeHeap(), memGet.getHeapSize(), memGet.getFreePsram(), memGet.getPsramSize()); /* Use a maximum of 2/3 the available PSRAM unless otherwise specified. @@ -86,9 +86,9 @@ void StoreForwardModule::populatePSRAM() #endif - LOG_DEBUG("After PSRAM init: heap %d/%d PSRAM %d/%d\n", memGet.getFreeHeap(), memGet.getHeapSize(), memGet.getFreePsram(), + LOG_DEBUG("After PSRAM init: heap %d/%d PSRAM %d/%d", memGet.getFreeHeap(), memGet.getHeapSize(), memGet.getFreePsram(), memGet.getPsramSize()); - LOG_DEBUG("numberOfPackets for packetHistory - %u\n", numberOfPackets); + LOG_DEBUG("numberOfPackets for packetHistory - %u", numberOfPackets); } /** @@ -105,11 +105,11 @@ void StoreForwardModule::historySend(uint32_t secAgo, uint32_t to) queueSize = this->historyReturnMax; if (queueSize) { - LOG_INFO("S&F - Sending %u message(s)\n", queueSize); + LOG_INFO("S&F - Send %u message(s)", queueSize); this->busy = true; // runOnce() will pickup the next steps once busy = true. this->busyTo = to; } else { - LOG_INFO("S&F - No history\n"); + LOG_INFO("S&F - No history"); } meshtastic_StoreAndForward sf = meshtastic_StoreAndForward_init_zero; sf.rr = meshtastic_StoreAndForward_RequestResponse_ROUTER_HISTORY; @@ -187,7 +187,7 @@ void StoreForwardModule::historyAdd(const meshtastic_MeshPacket &mp) const auto &p = mp.decoded; if (this->packetHistoryTotalCount == this->records) { - LOG_WARN("S&F - PSRAM Full. Starting overwrite.\n"); + LOG_WARN("S&F - PSRAM Full. Starting overwrite"); this->packetHistoryTotalCount = 0; for (auto &i : lastRequest) { i.second = 0; // Clear the last request index for each client device @@ -215,7 +215,7 @@ bool StoreForwardModule::sendPayload(NodeNum dest, uint32_t last_time) { meshtastic_MeshPacket *p = preparePayload(dest, last_time); if (p) { - LOG_INFO("Sending S&F Payload\n"); + LOG_INFO("Send S&F Payload"); service->sendToMesh(p); this->requestCount++; return true; @@ -335,7 +335,7 @@ void StoreForwardModule::sendErrorTextMessage(NodeNum dest, bool want_response) } else { str = "S&F not permitted on the public channel."; } - LOG_WARN("%s\n", str); + LOG_WARN("%s", str); memcpy(pr->decoded.payload.bytes, str, strlen(str)); pr->decoded.payload.size = strlen(str); if (want_response) { @@ -365,7 +365,7 @@ void StoreForwardModule::statsSend(uint32_t to) sf.variant.stats.return_max = this->historyReturnMax; sf.variant.stats.return_window = this->historyReturnWindow; - LOG_DEBUG("Sending S&F Stats\n"); + LOG_DEBUG("Send S&F Stats"); storeForwardModule->sendMessage(to, sf); } @@ -383,7 +383,7 @@ ProcessMessage StoreForwardModule::handleReceived(const meshtastic_MeshPacket &m if ((mp.decoded.portnum == meshtastic_PortNum_TEXT_MESSAGE_APP) && is_server) { auto &p = mp.decoded; if (isToUs(&mp) && (p.payload.bytes[0] == 'S') && (p.payload.bytes[1] == 'F') && (p.payload.bytes[2] == 0x00)) { - LOG_DEBUG("Legacy Request to send\n"); + LOG_DEBUG("Legacy Request to send"); // Send the last 60 minutes of messages. if (this->busy || channels.isDefaultChannel(mp.channel)) { @@ -393,7 +393,7 @@ ProcessMessage StoreForwardModule::handleReceived(const meshtastic_MeshPacket &m } } else { storeForwardModule->historyAdd(mp); - LOG_INFO("S&F stored. Message history contains %u records now.\n", this->packetHistoryTotalCount); + LOG_INFO("S&F stored. Message history contains %u records now", this->packetHistoryTotalCount); } } else if (!isFromUs(&mp) && mp.decoded.portnum == meshtastic_PortNum_STORE_FORWARD_APP) { auto &p = mp.decoded; @@ -403,7 +403,7 @@ ProcessMessage StoreForwardModule::handleReceived(const meshtastic_MeshPacket &m if (pb_decode_from_bytes(p.payload.bytes, p.payload.size, &meshtastic_StoreAndForward_msg, &scratch)) { decoded = &scratch; } else { - LOG_ERROR("Error decoding protobuf module!\n"); + LOG_ERROR("Error decoding proto module!"); // if we can't decode it, nobody can process it! return ProcessMessage::STOP; } @@ -439,7 +439,7 @@ bool StoreForwardModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp, if (is_server) { // stop sending stuff, the client wants to abort or has another error if ((this->busy) && (this->busyTo == getFrom(&mp))) { - LOG_ERROR("Client in ERROR or ABORT requested\n"); + LOG_ERROR("Client in ERROR or ABORT requested"); this->requestCount = 0; this->busy = false; } @@ -449,7 +449,7 @@ bool StoreForwardModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp, case meshtastic_StoreAndForward_RequestResponse_CLIENT_HISTORY: if (is_server) { requests_history++; - LOG_INFO("Client Request to send HISTORY\n"); + LOG_INFO("Client Request to send HISTORY"); // Send the last 60 minutes of messages. if (this->busy || channels.isDefaultChannel(mp.channel)) { sendErrorTextMessage(getFrom(&mp), mp.decoded.want_response); @@ -479,10 +479,10 @@ bool StoreForwardModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp, case meshtastic_StoreAndForward_RequestResponse_CLIENT_STATS: if (is_server) { - LOG_INFO("Client Request to send STATS\n"); + LOG_INFO("Client Request to send STATS"); if (this->busy) { storeForwardModule->sendMessage(getFrom(&mp), meshtastic_StoreAndForward_RequestResponse_ROUTER_BUSY); - LOG_INFO("S&F - Busy. Try again shortly.\n"); + LOG_INFO("S&F - Busy. Try again shortly"); } else { storeForwardModule->statsSend(getFrom(&mp)); } @@ -492,7 +492,7 @@ bool StoreForwardModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp, case meshtastic_StoreAndForward_RequestResponse_ROUTER_ERROR: case meshtastic_StoreAndForward_RequestResponse_ROUTER_BUSY: if (is_client) { - LOG_DEBUG("StoreAndForward_RequestResponse_ROUTER_BUSY\n"); + LOG_DEBUG("StoreAndForward_RequestResponse_ROUTER_BUSY"); // retry in messages_saved * packetTimeMax ms retry_delay = millis() + getNumAvailablePackets(this->busyTo, this->last_time) * packetTimeMax * (meshtastic_StoreAndForward_RequestResponse_ROUTER_ERROR ? 2 : 1); @@ -508,7 +508,7 @@ bool StoreForwardModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp, heartbeatInterval = p->variant.heartbeat.period; } lastHeartbeat = millis(); - LOG_INFO("StoreAndForward Heartbeat received\n"); + LOG_INFO("StoreAndForward Heartbeat received"); } break; @@ -521,7 +521,7 @@ bool StoreForwardModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp, case meshtastic_StoreAndForward_RequestResponse_ROUTER_STATS: if (is_client) { - LOG_DEBUG("Router Response STATS\n"); + LOG_DEBUG("Router Response STATS"); // These fields only have informational purpose on a client. Fill them to consume later. if (p->which_variant == meshtastic_StoreAndForward_stats_tag) { this->records = p->variant.stats.messages_max; @@ -539,7 +539,7 @@ bool StoreForwardModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp, // These fields only have informational purpose on a client. Fill them to consume later. if (p->which_variant == meshtastic_StoreAndForward_history_tag) { this->historyReturnWindow = p->variant.history.window / 60000; - LOG_INFO("Router Response HISTORY - Sending %d messages from last %d minutes\n", + LOG_INFO("Router Response HISTORY - Sending %d messages from last %d minutes", p->variant.history.history_messages, this->historyReturnWindow); } } @@ -552,7 +552,7 @@ bool StoreForwardModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp, } StoreForwardModule::StoreForwardModule() - : concurrency::OSThread("StoreForwardModule"), + : concurrency::OSThread("StoreForward"), ProtobufModule("StoreForward", meshtastic_PortNum_STORE_FORWARD_APP, &meshtastic_StoreAndForward_msg) { @@ -573,7 +573,7 @@ StoreForwardModule::StoreForwardModule() // Router if ((config.device.role == meshtastic_Config_DeviceConfig_Role_ROUTER || moduleConfig.store_forward.is_server)) { - LOG_INFO("Initializing Store & Forward Module in Server mode\n"); + LOG_INFO("Init Store & Forward Module in Server mode"); if (memGet.getPsramSize() > 0) { if (memGet.getFreePsram() >= 1024 * 1024) { @@ -601,20 +601,20 @@ StoreForwardModule::StoreForwardModule() this->populatePSRAM(); is_server = true; } else { - LOG_INFO(".\n"); - LOG_INFO("S&F: not enough PSRAM free, disabling.\n"); + LOG_INFO("."); + LOG_INFO("S&F: not enough PSRAM free, Disable"); } } else { - LOG_INFO("S&F: device doesn't have PSRAM, disabling.\n"); + LOG_INFO("S&F: device doesn't have PSRAM, Disable"); } // Client } else { is_client = true; - LOG_INFO("Initializing Store & Forward Module in Client mode\n"); + LOG_INFO("Init Store & Forward Module in Client mode"); } } else { disable(); } #endif -} \ No newline at end of file +} diff --git a/src/modules/Telemetry/AirQualityTelemetry.cpp b/src/modules/Telemetry/AirQualityTelemetry.cpp index 0b6be1b7e..362d60252 100644 --- a/src/modules/Telemetry/AirQualityTelemetry.cpp +++ b/src/modules/Telemetry/AirQualityTelemetry.cpp @@ -33,9 +33,10 @@ int32_t AirQualityTelemetryModule::runOnce() firstTime = false; if (moduleConfig.telemetry.air_quality_enabled) { - LOG_INFO("Air quality Telemetry: Initializing\n"); + LOG_INFO("Air quality Telemetry: init"); if (!aqi.begin_I2C()) { - LOG_WARN("Could not establish i2c connection to AQI sensor. Rescanning...\n"); +#ifndef I2C_NO_RESCAN + LOG_WARN("Could not establish i2c connection to AQI sensor. Rescan"); // rescan for late arriving sensors. AQI Module starts about 10 seconds into the boot so this is plenty. uint8_t i2caddr_scan[] = {PMSA0031_ADDR}; uint8_t i2caddr_asize = 1; @@ -51,6 +52,7 @@ int32_t AirQualityTelemetryModule::runOnce() i2cScanner->fetchI2CBus(found.address); return 1000; } +#endif return disable(); } return 1000; @@ -84,11 +86,11 @@ bool AirQualityTelemetryModule::handleReceivedProtobuf(const meshtastic_MeshPack #ifdef DEBUG_PORT const char *sender = getSenderShortName(mp); - LOG_INFO("(Received from %s): pm10_standard=%i, pm25_standard=%i, pm100_standard=%i\n", sender, + LOG_INFO("(Received from %s): pm10_standard=%i, pm25_standard=%i, pm100_standard=%i", sender, t->variant.air_quality_metrics.pm10_standard, t->variant.air_quality_metrics.pm25_standard, t->variant.air_quality_metrics.pm100_standard); - LOG_INFO(" | PM1.0(Environmental)=%i, PM2.5(Environmental)=%i, PM10.0(Environmental)=%i\n", + LOG_INFO(" | PM1.0(Environmental)=%i, PM2.5(Environmental)=%i, PM10.0(Environmental)=%i", t->variant.air_quality_metrics.pm10_environmental, t->variant.air_quality_metrics.pm25_environmental, t->variant.air_quality_metrics.pm100_environmental); #endif @@ -105,7 +107,7 @@ bool AirQualityTelemetryModule::handleReceivedProtobuf(const meshtastic_MeshPack bool AirQualityTelemetryModule::getAirQualityTelemetry(meshtastic_Telemetry *m) { if (!aqi.read(&data)) { - LOG_WARN("Skipping send measurements. Could not read AQIn\n"); + LOG_WARN("Skip send measurements. Could not read AQIn"); return false; } @@ -119,11 +121,10 @@ bool AirQualityTelemetryModule::getAirQualityTelemetry(meshtastic_Telemetry *m) m->variant.air_quality_metrics.pm25_environmental = data.pm25_env; m->variant.air_quality_metrics.pm100_environmental = data.pm100_env; - LOG_INFO("(Sending): PM1.0(Standard)=%i, PM2.5(Standard)=%i, PM10.0(Standard)=%i\n", - m->variant.air_quality_metrics.pm10_standard, m->variant.air_quality_metrics.pm25_standard, - m->variant.air_quality_metrics.pm100_standard); + LOG_INFO("Send: PM1.0(Standard)=%i, PM2.5(Standard)=%i, PM10.0(Standard)=%i", m->variant.air_quality_metrics.pm10_standard, + m->variant.air_quality_metrics.pm25_standard, m->variant.air_quality_metrics.pm100_standard); - LOG_INFO(" | PM1.0(Environmental)=%i, PM2.5(Environmental)=%i, PM10.0(Environmental)=%i\n", + LOG_INFO(" | PM1.0(Environmental)=%i, PM2.5(Environmental)=%i, PM10.0(Environmental)=%i", m->variant.air_quality_metrics.pm10_environmental, m->variant.air_quality_metrics.pm25_environmental, m->variant.air_quality_metrics.pm100_environmental); @@ -141,14 +142,14 @@ meshtastic_MeshPacket *AirQualityTelemetryModule::allocReply() if (pb_decode_from_bytes(p.payload.bytes, p.payload.size, &meshtastic_Telemetry_msg, &scratch)) { decoded = &scratch; } else { - LOG_ERROR("Error decoding AirQualityTelemetry module!\n"); + LOG_ERROR("Error decoding AirQualityTelemetry module!"); return NULL; } // Check for a request for air quality metrics if (decoded->which_variant == meshtastic_Telemetry_air_quality_metrics_tag) { meshtastic_Telemetry m = meshtastic_Telemetry_init_zero; if (getAirQualityTelemetry(&m)) { - LOG_INFO("Air quality telemetry replying to request\n"); + LOG_INFO("Air quality telemetry reply to request"); return allocDataProtobuf(m); } else { return NULL; @@ -176,10 +177,10 @@ bool AirQualityTelemetryModule::sendTelemetry(NodeNum dest, bool phoneOnly) lastMeasurementPacket = packetPool.allocCopy(*p); if (phoneOnly) { - LOG_INFO("Sending packet to phone\n"); + LOG_INFO("Send packet to phone"); service->sendToPhone(p); } else { - LOG_INFO("Sending packet to mesh\n"); + LOG_INFO("Send packet to mesh"); service->sendToMesh(p, RX_SRC_LOCAL, true); } return true; diff --git a/src/modules/Telemetry/AirQualityTelemetry.h b/src/modules/Telemetry/AirQualityTelemetry.h index fb8edd07e..3b983bd56 100644 --- a/src/modules/Telemetry/AirQualityTelemetry.h +++ b/src/modules/Telemetry/AirQualityTelemetry.h @@ -16,7 +16,7 @@ class AirQualityTelemetryModule : private concurrency::OSThread, public Protobuf public: AirQualityTelemetryModule() - : concurrency::OSThread("AirQualityTelemetryModule"), + : concurrency::OSThread("AirQualityTelemetry"), ProtobufModule("AirQualityTelemetry", meshtastic_PortNum_TELEMETRY_APP, &meshtastic_Telemetry_msg) { lastMeasurementPacket = nullptr; diff --git a/src/modules/Telemetry/DeviceTelemetry.cpp b/src/modules/Telemetry/DeviceTelemetry.cpp index eb3f67e96..4989b88e2 100644 --- a/src/modules/Telemetry/DeviceTelemetry.cpp +++ b/src/modules/Telemetry/DeviceTelemetry.cpp @@ -51,7 +51,7 @@ bool DeviceTelemetryModule::handleReceivedProtobuf(const meshtastic_MeshPacket & #ifdef DEBUG_PORT const char *sender = getSenderShortName(mp); - LOG_INFO("(Received from %s): air_util_tx=%f, channel_utilization=%f, battery_level=%i, voltage=%f\n", sender, + LOG_INFO("(Received from %s): air_util_tx=%f, channel_utilization=%f, battery_level=%i, voltage=%f", 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); #endif @@ -71,15 +71,16 @@ meshtastic_MeshPacket *DeviceTelemetryModule::allocReply() if (pb_decode_from_bytes(p.payload.bytes, p.payload.size, &meshtastic_Telemetry_msg, &scratch)) { decoded = &scratch; } else { - LOG_ERROR("Error decoding DeviceTelemetry module!\n"); + LOG_ERROR("Error decoding DeviceTelemetry module!"); return NULL; } // Check for a request for device metrics if (decoded->which_variant == meshtastic_Telemetry_device_metrics_tag) { - LOG_INFO("Device telemetry replying to request\n"); - - meshtastic_Telemetry telemetry = getDeviceTelemetry(); - return allocDataProtobuf(telemetry); + LOG_INFO("Device telemetry reply to request"); + return allocDataProtobuf(getDeviceTelemetry()); + } else if (decoded->which_variant == meshtastic_Telemetry_local_stats_tag) { + LOG_INFO("Device telemetry reply w/ LocalStats to request"); + return allocDataProtobuf(getLocalStatsTelemetry()); } } return NULL; @@ -112,7 +113,7 @@ meshtastic_Telemetry DeviceTelemetryModule::getDeviceTelemetry() return t; } -void DeviceTelemetryModule::sendLocalStatsToPhone() +meshtastic_Telemetry DeviceTelemetryModule::getLocalStatsTelemetry() { meshtastic_Telemetry telemetry = meshtastic_Telemetry_init_zero; telemetry.which_variant = meshtastic_Telemetry_local_stats_tag; @@ -134,16 +135,20 @@ void DeviceTelemetryModule::sendLocalStatsToPhone() telemetry.variant.local_stats.num_tx_relay_canceled = router->txRelayCanceled; } - LOG_INFO( - "(Sending local stats): uptime=%i, channel_utilization=%f, air_util_tx=%f, num_online_nodes=%i, num_total_nodes=%i\n", - telemetry.variant.local_stats.uptime_seconds, telemetry.variant.local_stats.channel_utilization, - telemetry.variant.local_stats.air_util_tx, telemetry.variant.local_stats.num_online_nodes, - telemetry.variant.local_stats.num_total_nodes); + LOG_INFO("Sending local stats: uptime=%i, channel_utilization=%f, air_util_tx=%f, num_online_nodes=%i, num_total_nodes=%i", + telemetry.variant.local_stats.uptime_seconds, telemetry.variant.local_stats.channel_utilization, + telemetry.variant.local_stats.air_util_tx, telemetry.variant.local_stats.num_online_nodes, + telemetry.variant.local_stats.num_total_nodes); - LOG_INFO("num_packets_tx=%i, num_packets_rx=%i, num_packets_rx_bad=%i\n", telemetry.variant.local_stats.num_packets_tx, + LOG_INFO("num_packets_tx=%i, num_packets_rx=%i, num_packets_rx_bad=%i", telemetry.variant.local_stats.num_packets_tx, telemetry.variant.local_stats.num_packets_rx, telemetry.variant.local_stats.num_packets_rx_bad); - meshtastic_MeshPacket *p = allocDataProtobuf(telemetry); + return telemetry; +} + +void DeviceTelemetryModule::sendLocalStatsToPhone() +{ + meshtastic_MeshPacket *p = allocDataProtobuf(getLocalStatsTelemetry()); p->to = NODENUM_BROADCAST; p->decoded.want_response = false; p->priority = meshtastic_MeshPacket_Priority_BACKGROUND; @@ -154,7 +159,7 @@ void DeviceTelemetryModule::sendLocalStatsToPhone() bool DeviceTelemetryModule::sendTelemetry(NodeNum dest, bool phoneOnly) { meshtastic_Telemetry telemetry = getDeviceTelemetry(); - LOG_INFO("(Sending): air_util_tx=%f, channel_utilization=%f, battery_level=%i, voltage=%f, uptime=%i\n", + LOG_INFO("Send: air_util_tx=%f, channel_utilization=%f, battery_level=%i, voltage=%f, uptime=%i", telemetry.variant.device_metrics.air_util_tx, telemetry.variant.device_metrics.channel_utilization, telemetry.variant.device_metrics.battery_level, telemetry.variant.device_metrics.voltage, telemetry.variant.device_metrics.uptime_seconds); @@ -166,10 +171,10 @@ bool DeviceTelemetryModule::sendTelemetry(NodeNum dest, bool phoneOnly) nodeDB->updateTelemetry(nodeDB->getNodeNum(), telemetry, RX_SRC_LOCAL); if (phoneOnly) { - LOG_INFO("Sending packet to phone\n"); + LOG_INFO("Send packet to phone"); service->sendToPhone(p); } else { - LOG_INFO("Sending packet to mesh\n"); + LOG_INFO("Send packet to mesh"); service->sendToMesh(p, RX_SRC_LOCAL, true); } return true; diff --git a/src/modules/Telemetry/DeviceTelemetry.h b/src/modules/Telemetry/DeviceTelemetry.h index 6d7f69891..19b7d5b01 100644 --- a/src/modules/Telemetry/DeviceTelemetry.h +++ b/src/modules/Telemetry/DeviceTelemetry.h @@ -12,7 +12,7 @@ class DeviceTelemetryModule : private concurrency::OSThread, public ProtobufModu public: DeviceTelemetryModule() - : concurrency::OSThread("DeviceTelemetryModule"), + : concurrency::OSThread("DeviceTelemetry"), ProtobufModule("DeviceTelemetry", meshtastic_PortNum_TELEMETRY_APP, &meshtastic_Telemetry_msg) { uptimeWrapCount = 0; @@ -42,6 +42,8 @@ class DeviceTelemetryModule : private concurrency::OSThread, public ProtobufModu private: meshtastic_Telemetry getDeviceTelemetry(); + meshtastic_Telemetry getLocalStatsTelemetry(); + void sendLocalStatsToPhone(); uint32_t sendToPhoneIntervalMs = SECONDS_IN_MINUTE * 1000; // Send to phone every minute uint32_t sendStatsToPhoneIntervalMs = 15 * SECONDS_IN_MINUTE * 1000; // Send stats to phone every 15 minutes diff --git a/src/modules/Telemetry/EnvironmentTelemetry.cpp b/src/modules/Telemetry/EnvironmentTelemetry.cpp index 1ccdedeb7..92d964f7d 100644 --- a/src/modules/Telemetry/EnvironmentTelemetry.cpp +++ b/src/modules/Telemetry/EnvironmentTelemetry.cpp @@ -25,6 +25,7 @@ #include "Sensor/BMP085Sensor.h" #include "Sensor/BMP280Sensor.h" #include "Sensor/BMP3XXSensor.h" +#include "Sensor/CGRadSensSensor.h" #include "Sensor/DFRobotLarkSensor.h" #include "Sensor/LPS22HBSensor.h" #include "Sensor/MCP9808Sensor.h" @@ -60,6 +61,7 @@ BMP3XXSensor bmp3xxSensor; #ifdef T1000X_SENSOR_EN T1000xSensor t1000xSensor; #endif +CGRadSensSensor cgRadSens; #define FAILED_STATE_SENSOR_READ_MULTIPLIER 10 #define DISPLAY_RECEIVEID_MEASUREMENTS_ON_SCREEN true @@ -73,8 +75,8 @@ int32_t EnvironmentTelemetryModule::runOnce() sleepOnNextExecution = false; uint32_t nightyNightMs = Default::getConfiguredOrDefaultMs(moduleConfig.telemetry.environment_update_interval, default_telemetry_broadcast_interval_secs); - LOG_DEBUG("Sleeping for %ims, then awaking to send metrics again.\n", nightyNightMs); - doDeepSleep(nightyNightMs, true); + LOG_DEBUG("Sleep for %ims, then awake to send metrics again", nightyNightMs); + doDeepSleep(nightyNightMs, true, false); } uint32_t result = UINT32_MAX; @@ -97,7 +99,7 @@ int32_t EnvironmentTelemetryModule::runOnce() firstTime = 0; if (moduleConfig.telemetry.environment_measurement_enabled) { - LOG_INFO("Environment Telemetry: Initializing\n"); + LOG_INFO("Environment Telemetry: init"); // it's possible to have this module enabled, only for displaying values on the screen. // therefore, we should only enable the sensor loop if measurement is also enabled #ifdef T1000X_SENSOR_EN @@ -147,6 +149,8 @@ int32_t EnvironmentTelemetryModule::runOnce() result = nau7802Sensor.runOnce(); if (max17048Sensor.hasSensor()) result = max17048Sensor.runOnce(); + if (cgRadSens.hasSensor()) + result = cgRadSens.runOnce(); #endif } return result; @@ -210,16 +214,19 @@ void EnvironmentTelemetryModule::drawFrame(OLEDDisplay *display, OLEDDisplayUiSt // Display "Env. From: ..." on its own display->drawString(x, y, "Env. From: " + String(lastSender) + "(" + String(agoSecs) + "s)"); - String last_temp = String(lastMeasurement.variant.environment_metrics.temperature, 0) + "°C"; - if (moduleConfig.telemetry.environment_display_fahrenheit) { - last_temp = - String(UnitConversions::CelsiusToFahrenheit(lastMeasurement.variant.environment_metrics.temperature), 0) + "°F"; - } + if (lastMeasurement.variant.environment_metrics.has_temperature || + lastMeasurement.variant.environment_metrics.has_relative_humidity) { + String last_temp = String(lastMeasurement.variant.environment_metrics.temperature, 0) + "°C"; + if (moduleConfig.telemetry.environment_display_fahrenheit) { + last_temp = + String(UnitConversions::CelsiusToFahrenheit(lastMeasurement.variant.environment_metrics.temperature), 0) + "°F"; + } - // Continue with the remaining details - display->drawString(x, y += _fontHeight(FONT_SMALL), - "Temp/Hum: " + last_temp + " / " + - String(lastMeasurement.variant.environment_metrics.relative_humidity, 0) + "%"); + // Continue with the remaining details + display->drawString(x, y += _fontHeight(FONT_SMALL), + "Temp/Hum: " + last_temp + " / " + + String(lastMeasurement.variant.environment_metrics.relative_humidity, 0) + "%"); + } if (lastMeasurement.variant.environment_metrics.barometric_pressure != 0) { display->drawString(x, y += _fontHeight(FONT_SMALL), @@ -243,6 +250,10 @@ void EnvironmentTelemetryModule::drawFrame(OLEDDisplay *display, OLEDDisplayUiSt if (lastMeasurement.variant.environment_metrics.weight != 0) display->drawString(x, y += _fontHeight(FONT_SMALL), "Weight: " + String(lastMeasurement.variant.environment_metrics.weight, 0) + "kg"); + + if (lastMeasurement.variant.environment_metrics.radiation != 0) + display->drawString(x, y += _fontHeight(FONT_SMALL), + "Rad: " + String(lastMeasurement.variant.environment_metrics.radiation, 2) + "µR/h"); } bool EnvironmentTelemetryModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp, meshtastic_Telemetry *t) @@ -252,17 +263,19 @@ bool EnvironmentTelemetryModule::handleReceivedProtobuf(const meshtastic_MeshPac const char *sender = getSenderShortName(mp); LOG_INFO("(Received from %s): barometric_pressure=%f, current=%f, gas_resistance=%f, relative_humidity=%f, " - "temperature=%f\n", + "temperature=%f", 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); - LOG_INFO("(Received from %s): voltage=%f, IAQ=%d, distance=%f, lux=%f\n", sender, t->variant.environment_metrics.voltage, + LOG_INFO("(Received from %s): voltage=%f, IAQ=%d, distance=%f, lux=%f", sender, t->variant.environment_metrics.voltage, t->variant.environment_metrics.iaq, t->variant.environment_metrics.distance, t->variant.environment_metrics.lux); - LOG_INFO("(Received from %s): wind speed=%fm/s, direction=%d degrees, weight=%fkg\n", sender, + LOG_INFO("(Received from %s): wind speed=%fm/s, direction=%d degrees, weight=%fkg", sender, t->variant.environment_metrics.wind_speed, t->variant.environment_metrics.wind_direction, t->variant.environment_metrics.weight); + LOG_INFO("(Received from %s): radiation=%fµR/h", sender, t->variant.environment_metrics.radiation); + #endif // release previous packet before occupying a new spot if (lastMeasurementPacket != nullptr) @@ -373,21 +386,27 @@ bool EnvironmentTelemetryModule::getEnvironmentTelemetry(meshtastic_Telemetry *m } else if (bmp280Sensor.hasSensor()) { // prefer bmp280 temp if both sensors are present, fetch only humidity meshtastic_Telemetry m_ahtx = meshtastic_Telemetry_init_zero; - LOG_INFO("AHTX0+BMP280 module detected: using temp from BMP280 and humy from AHTX0\n"); + LOG_INFO("AHTX0+BMP280 module detected: using temp from BMP280 and humy from AHTX0"); aht10Sensor.getMetrics(&m_ahtx); m->variant.environment_metrics.relative_humidity = m_ahtx.variant.environment_metrics.relative_humidity; + m->variant.environment_metrics.has_relative_humidity = m_ahtx.variant.environment_metrics.has_relative_humidity; } else { // prefer bmp3xx temp if both sensors are present, fetch only humidity meshtastic_Telemetry m_ahtx = meshtastic_Telemetry_init_zero; - LOG_INFO("AHTX0+BMP3XX module detected: using temp from BMP3XX and humy from AHTX0\n"); + LOG_INFO("AHTX0+BMP3XX module detected: using temp from BMP3XX and humy from AHTX0"); aht10Sensor.getMetrics(&m_ahtx); m->variant.environment_metrics.relative_humidity = m_ahtx.variant.environment_metrics.relative_humidity; + m->variant.environment_metrics.has_relative_humidity = m_ahtx.variant.environment_metrics.has_relative_humidity; } } if (max17048Sensor.hasSensor()) { valid = valid && max17048Sensor.getMetrics(m); hasSensor = true; } + if (cgRadSens.hasSensor()) { + valid = valid && cgRadSens.getMetrics(m); + hasSensor = true; + } #endif return valid && hasSensor; @@ -404,14 +423,14 @@ meshtastic_MeshPacket *EnvironmentTelemetryModule::allocReply() if (pb_decode_from_bytes(p.payload.bytes, p.payload.size, &meshtastic_Telemetry_msg, &scratch)) { decoded = &scratch; } else { - LOG_ERROR("Error decoding EnvironmentTelemetry module!\n"); + LOG_ERROR("Error decoding EnvironmentTelemetry module!"); return NULL; } // Check for a request for environment metrics if (decoded->which_variant == meshtastic_Telemetry_environment_metrics_tag) { meshtastic_Telemetry m = meshtastic_Telemetry_init_zero; if (getEnvironmentTelemetry(&m)) { - LOG_INFO("Environment telemetry replying to request\n"); + LOG_INFO("Environment telemetry reply to request"); return allocDataProtobuf(m); } else { return NULL; @@ -431,16 +450,18 @@ bool EnvironmentTelemetryModule::sendTelemetry(NodeNum dest, bool phoneOnly) #else if (getEnvironmentTelemetry(&m)) { #endif - LOG_INFO("(Sending): barometric_pressure=%f, current=%f, gas_resistance=%f, relative_humidity=%f, temperature=%f\n", + LOG_INFO("Send: barometric_pressure=%f, current=%f, gas_resistance=%f, relative_humidity=%f, temperature=%f", 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); - LOG_INFO("(Sending): voltage=%f, IAQ=%d, distance=%f, lux=%f\n", m.variant.environment_metrics.voltage, + LOG_INFO("Send: voltage=%f, IAQ=%d, distance=%f, lux=%f", m.variant.environment_metrics.voltage, m.variant.environment_metrics.iaq, m.variant.environment_metrics.distance, m.variant.environment_metrics.lux); - LOG_INFO("(Sending): wind speed=%fm/s, direction=%d degrees, weight=%fkg\n", m.variant.environment_metrics.wind_speed, + LOG_INFO("Send: wind speed=%fm/s, direction=%d degrees, weight=%fkg", m.variant.environment_metrics.wind_speed, m.variant.environment_metrics.wind_direction, m.variant.environment_metrics.weight); + LOG_INFO("Send: radiation=%fµR/h", m.variant.environment_metrics.radiation); + sensor_read_error_count = 0; meshtastic_MeshPacket *p = allocDataProtobuf(m); @@ -456,14 +477,14 @@ bool EnvironmentTelemetryModule::sendTelemetry(NodeNum dest, bool phoneOnly) lastMeasurementPacket = packetPool.allocCopy(*p); if (phoneOnly) { - LOG_INFO("Sending packet to phone\n"); + LOG_INFO("Send packet to phone"); service->sendToPhone(p); } else { - LOG_INFO("Sending packet to mesh\n"); + LOG_INFO("Send packet to mesh"); service->sendToMesh(p, RX_SRC_LOCAL, true); if (config.device.role == meshtastic_Config_DeviceConfig_Role_SENSOR && config.power.is_power_saving) { - LOG_DEBUG("Starting next execution in 5 seconds and then going to sleep.\n"); + LOG_DEBUG("Start next execution in 5s, then sleep"); sleepOnNextExecution = true; setIntervalFromNow(5000); } @@ -583,7 +604,12 @@ AdminMessageHandleResult EnvironmentTelemetryModule::handleAdminMessageForModule if (result != AdminMessageHandleResult::NOT_HANDLED) return result; } + if (cgRadSens.hasSensor()) { + result = cgRadSens.handleAdminMessage(mp, request, response); + if (result != AdminMessageHandleResult::NOT_HANDLED) + return result; + } return result; } -#endif \ No newline at end of file +#endif diff --git a/src/modules/Telemetry/EnvironmentTelemetry.h b/src/modules/Telemetry/EnvironmentTelemetry.h index e680d8bbd..6e0f850ef 100644 --- a/src/modules/Telemetry/EnvironmentTelemetry.h +++ b/src/modules/Telemetry/EnvironmentTelemetry.h @@ -17,7 +17,7 @@ class EnvironmentTelemetryModule : private concurrency::OSThread, public Protobu public: EnvironmentTelemetryModule() - : concurrency::OSThread("EnvironmentTelemetryModule"), + : concurrency::OSThread("EnvironmentTelemetry"), ProtobufModule("EnvironmentTelemetry", meshtastic_PortNum_TELEMETRY_APP, &meshtastic_Telemetry_msg) { lastMeasurementPacket = nullptr; diff --git a/src/modules/Telemetry/HealthTelemetry.cpp b/src/modules/Telemetry/HealthTelemetry.cpp index bcf9d9d57..22534e9f5 100644 --- a/src/modules/Telemetry/HealthTelemetry.cpp +++ b/src/modules/Telemetry/HealthTelemetry.cpp @@ -39,8 +39,8 @@ int32_t HealthTelemetryModule::runOnce() sleepOnNextExecution = false; uint32_t nightyNightMs = Default::getConfiguredOrDefaultMs(moduleConfig.telemetry.health_update_interval, default_telemetry_broadcast_interval_secs); - LOG_DEBUG("Sleeping for %ims, then awaking to send metrics again.\n", nightyNightMs); - doDeepSleep(nightyNightMs, true); + LOG_DEBUG("Sleep for %ims, then awake to send metrics again", nightyNightMs); + doDeepSleep(nightyNightMs, true, false); } uint32_t result = UINT32_MAX; @@ -55,7 +55,7 @@ int32_t HealthTelemetryModule::runOnce() firstTime = false; if (moduleConfig.telemetry.health_measurement_enabled) { - LOG_INFO("Health Telemetry: Initializing\n"); + LOG_INFO("Health Telemetry: init"); // Initialize sensors if (mlx90614Sensor.hasSensor()) result = mlx90614Sensor.runOnce(); @@ -143,7 +143,7 @@ bool HealthTelemetryModule::handleReceivedProtobuf(const meshtastic_MeshPacket & #ifdef DEBUG_PORT const char *sender = getSenderShortName(mp); - LOG_INFO("(Received from %s): temperature=%f, heart_bpm=%d, spO2=%d,\n", sender, t->variant.health_metrics.temperature, + LOG_INFO("(Received from %s): temperature=%f, heart_bpm=%d, spO2=%d,", sender, t->variant.health_metrics.temperature, t->variant.health_metrics.heart_bpm, t->variant.health_metrics.spO2); #endif @@ -188,14 +188,14 @@ meshtastic_MeshPacket *HealthTelemetryModule::allocReply() if (pb_decode_from_bytes(p.payload.bytes, p.payload.size, &meshtastic_Telemetry_msg, &scratch)) { decoded = &scratch; } else { - LOG_ERROR("Error decoding HealthTelemetry module!\n"); + LOG_ERROR("Error decoding HealthTelemetry module!"); return NULL; } // Check for a request for health metrics if (decoded->which_variant == meshtastic_Telemetry_health_metrics_tag) { meshtastic_Telemetry m = meshtastic_Telemetry_init_zero; if (getHealthTelemetry(&m)) { - LOG_INFO("Health telemetry replying to request\n"); + LOG_INFO("Health telemetry reply to request"); return allocDataProtobuf(m); } else { return NULL; @@ -211,7 +211,7 @@ bool HealthTelemetryModule::sendTelemetry(NodeNum dest, bool phoneOnly) m.which_variant = meshtastic_Telemetry_health_metrics_tag; m.time = getTime(); if (getHealthTelemetry(&m)) { - LOG_INFO("(Sending): temperature=%f, heart_bpm=%d, spO2=%d\n", m.variant.health_metrics.temperature, + LOG_INFO("Send: temperature=%f, heart_bpm=%d, spO2=%d", m.variant.health_metrics.temperature, m.variant.health_metrics.heart_bpm, m.variant.health_metrics.spO2); sensor_read_error_count = 0; @@ -229,14 +229,14 @@ bool HealthTelemetryModule::sendTelemetry(NodeNum dest, bool phoneOnly) lastMeasurementPacket = packetPool.allocCopy(*p); if (phoneOnly) { - LOG_INFO("Sending packet to phone\n"); + LOG_INFO("Send packet to phone"); service->sendToPhone(p); } else { - LOG_INFO("Sending packet to mesh\n"); + LOG_INFO("Send packet to mesh"); service->sendToMesh(p, RX_SRC_LOCAL, true); if (config.device.role == meshtastic_Config_DeviceConfig_Role_SENSOR && config.power.is_power_saving) { - LOG_DEBUG("Starting next execution in 5 seconds and then going to sleep.\n"); + LOG_DEBUG("Start next execution in 5s, then sleep"); sleepOnNextExecution = true; setIntervalFromNow(5000); } diff --git a/src/modules/Telemetry/HealthTelemetry.h b/src/modules/Telemetry/HealthTelemetry.h index 4ad0da838..fe84f2d27 100644 --- a/src/modules/Telemetry/HealthTelemetry.h +++ b/src/modules/Telemetry/HealthTelemetry.h @@ -16,7 +16,7 @@ class HealthTelemetryModule : private concurrency::OSThread, public ProtobufModu public: HealthTelemetryModule() - : concurrency::OSThread("HealthTelemetryModule"), + : concurrency::OSThread("HealthTelemetry"), ProtobufModule("HealthTelemetry", meshtastic_PortNum_TELEMETRY_APP, &meshtastic_Telemetry_msg) { lastMeasurementPacket = nullptr; diff --git a/src/modules/Telemetry/PowerTelemetry.cpp b/src/modules/Telemetry/PowerTelemetry.cpp index be3048998..367643849 100644 --- a/src/modules/Telemetry/PowerTelemetry.cpp +++ b/src/modules/Telemetry/PowerTelemetry.cpp @@ -27,8 +27,8 @@ int32_t PowerTelemetryModule::runOnce() sleepOnNextExecution = false; uint32_t nightyNightMs = Default::getConfiguredOrDefaultMs(moduleConfig.telemetry.power_update_interval, default_telemetry_broadcast_interval_secs); - LOG_DEBUG("Sleeping for %ims, then awaking to send metrics again.\n", nightyNightMs); - doDeepSleep(nightyNightMs, true); + LOG_DEBUG("Sleep for %ims, then awake to send metrics again", nightyNightMs); + doDeepSleep(nightyNightMs, true, false); } uint32_t result = UINT32_MAX; @@ -51,7 +51,7 @@ int32_t PowerTelemetryModule::runOnce() firstTime = 0; #if HAS_TELEMETRY && !defined(ARCH_PORTDUINO) if (moduleConfig.telemetry.power_measurement_enabled) { - LOG_INFO("Power Telemetry: Initializing\n"); + LOG_INFO("Power Telemetry: init"); // it's possible to have this module enabled, only for displaying values on the screen. // therefore, we should only enable the sensor loop if measurement is also enabled if (ina219Sensor.hasSensor() && !ina219Sensor.isInitialized()) @@ -145,7 +145,7 @@ bool PowerTelemetryModule::handleReceivedProtobuf(const meshtastic_MeshPacket &m const char *sender = getSenderShortName(mp); LOG_INFO("(Received from %s): ch1_voltage=%.1f, ch1_current=%.1f, ch2_voltage=%.1f, ch2_current=%.1f, " - "ch3_voltage=%.1f, ch3_current=%.1f\n", + "ch3_voltage=%.1f, ch3_current=%.1f", sender, t->variant.power_metrics.ch1_voltage, t->variant.power_metrics.ch1_current, t->variant.power_metrics.ch2_voltage, t->variant.power_metrics.ch2_current, t->variant.power_metrics.ch3_voltage, t->variant.power_metrics.ch3_current); @@ -192,14 +192,14 @@ meshtastic_MeshPacket *PowerTelemetryModule::allocReply() if (pb_decode_from_bytes(p.payload.bytes, p.payload.size, &meshtastic_Telemetry_msg, &scratch)) { decoded = &scratch; } else { - LOG_ERROR("Error decoding PowerTelemetry module!\n"); + LOG_ERROR("Error decoding PowerTelemetry module!"); return NULL; } // Check for a request for power metrics if (decoded->which_variant == meshtastic_Telemetry_power_metrics_tag) { meshtastic_Telemetry m = meshtastic_Telemetry_init_zero; if (getPowerTelemetry(&m)) { - LOG_INFO("Power telemetry replying to request\n"); + LOG_INFO("Power telemetry reply to request"); return allocDataProtobuf(m); } else { return NULL; @@ -216,8 +216,8 @@ bool PowerTelemetryModule::sendTelemetry(NodeNum dest, bool phoneOnly) m.which_variant = meshtastic_Telemetry_power_metrics_tag; m.time = getTime(); if (getPowerTelemetry(&m)) { - LOG_INFO("(Sending): ch1_voltage=%f, ch1_current=%f, ch2_voltage=%f, ch2_current=%f, " - "ch3_voltage=%f, ch3_current=%f\n", + LOG_INFO("Send: ch1_voltage=%f, ch1_current=%f, ch2_voltage=%f, ch2_current=%f, " + "ch3_voltage=%f, ch3_current=%f", m.variant.power_metrics.ch1_voltage, m.variant.power_metrics.ch1_current, m.variant.power_metrics.ch2_voltage, m.variant.power_metrics.ch2_current, m.variant.power_metrics.ch3_voltage, m.variant.power_metrics.ch3_current); @@ -236,14 +236,14 @@ bool PowerTelemetryModule::sendTelemetry(NodeNum dest, bool phoneOnly) lastMeasurementPacket = packetPool.allocCopy(*p); if (phoneOnly) { - LOG_INFO("Sending packet to phone\n"); + LOG_INFO("Send packet to phone"); service->sendToPhone(p); } else { - LOG_INFO("Sending packet to mesh\n"); + LOG_INFO("Send packet to mesh"); service->sendToMesh(p, RX_SRC_LOCAL, true); if (config.device.role == meshtastic_Config_DeviceConfig_Role_SENSOR && config.power.is_power_saving) { - LOG_DEBUG("Starting next execution in 5s then going to sleep.\n"); + LOG_DEBUG("Start next execution in 5s then sleep"); sleepOnNextExecution = true; setIntervalFromNow(5000); } diff --git a/src/modules/Telemetry/PowerTelemetry.h b/src/modules/Telemetry/PowerTelemetry.h index f8248304e..b9ec6edc1 100644 --- a/src/modules/Telemetry/PowerTelemetry.h +++ b/src/modules/Telemetry/PowerTelemetry.h @@ -17,7 +17,7 @@ class PowerTelemetryModule : private concurrency::OSThread, public ProtobufModul public: PowerTelemetryModule() - : concurrency::OSThread("PowerTelemetryModule"), + : concurrency::OSThread("PowerTelemetry"), ProtobufModule("PowerTelemetry", meshtastic_PortNum_TELEMETRY_APP, &meshtastic_Telemetry_msg) { lastMeasurementPacket = nullptr; diff --git a/src/modules/Telemetry/Sensor/AHT10.cpp b/src/modules/Telemetry/Sensor/AHT10.cpp index f9e8ba18a..4d8c80200 100644 --- a/src/modules/Telemetry/Sensor/AHT10.cpp +++ b/src/modules/Telemetry/Sensor/AHT10.cpp @@ -13,7 +13,7 @@ AHT10Sensor::AHT10Sensor() : TelemetrySensor(meshtastic_TelemetrySensorType_AHT1 int32_t AHT10Sensor::runOnce() { - LOG_INFO("Init sensor: %s\n", sensorName); + LOG_INFO("Init sensor: %s", sensorName); if (!hasSensor()) { return DEFAULT_SENSOR_MINIMUM_WAIT_TIME_BETWEEN_READS; } @@ -27,7 +27,7 @@ void AHT10Sensor::setup() {} bool AHT10Sensor::getMetrics(meshtastic_Telemetry *measurement) { - LOG_DEBUG("AHT10Sensor::getMetrics\n"); + LOG_DEBUG("AHT10 getMetrics"); sensors_event_t humidity, temp; aht10.getEvent(&humidity, &temp); diff --git a/src/modules/Telemetry/Sensor/BME280Sensor.cpp b/src/modules/Telemetry/Sensor/BME280Sensor.cpp index 55bc16744..65dab5105 100644 --- a/src/modules/Telemetry/Sensor/BME280Sensor.cpp +++ b/src/modules/Telemetry/Sensor/BME280Sensor.cpp @@ -12,7 +12,7 @@ BME280Sensor::BME280Sensor() : TelemetrySensor(meshtastic_TelemetrySensorType_BM int32_t BME280Sensor::runOnce() { - LOG_INFO("Init sensor: %s\n", sensorName); + LOG_INFO("Init sensor: %s", sensorName); if (!hasSensor()) { return DEFAULT_SENSOR_MINIMUM_WAIT_TIME_BETWEEN_READS; } @@ -35,7 +35,7 @@ bool BME280Sensor::getMetrics(meshtastic_Telemetry *measurement) measurement->variant.environment_metrics.has_relative_humidity = true; measurement->variant.environment_metrics.has_barometric_pressure = true; - LOG_DEBUG("BME280Sensor::getMetrics\n"); + LOG_DEBUG("BME280 getMetrics"); bme280.takeForcedMeasurement(); measurement->variant.environment_metrics.temperature = bme280.readTemperature(); measurement->variant.environment_metrics.relative_humidity = bme280.readHumidity(); diff --git a/src/modules/Telemetry/Sensor/BME680Sensor.cpp b/src/modules/Telemetry/Sensor/BME680Sensor.cpp index 328ec827d..18515d0a8 100644 --- a/src/modules/Telemetry/Sensor/BME680Sensor.cpp +++ b/src/modules/Telemetry/Sensor/BME680Sensor.cpp @@ -37,13 +37,13 @@ int32_t BME680Sensor::runOnce() checkStatus("updateSubscription"); status = 0; } - LOG_INFO("Init sensor: %s with the BSEC Library version %d.%d.%d.%d \n", sensorName, bme680.version.major, + LOG_INFO("Init sensor: %s with the BSEC Library version %d.%d.%d.%d ", sensorName, bme680.version.major, bme680.version.minor, bme680.version.major_bugfix, bme680.version.minor_bugfix); } else { status = 0; } if (status == 0) - LOG_DEBUG("BME680Sensor::runOnce: bme680.status %d\n", bme680.status); + LOG_DEBUG("BME680Sensor::runOnce: bme680.status %d", bme680.status); return initI2CSensor(); } @@ -80,12 +80,12 @@ void BME680Sensor::loadState() file.read((uint8_t *)&bsecState, BSEC_MAX_STATE_BLOB_SIZE); file.close(); bme680.setState(bsecState); - LOG_INFO("%s state read from %s.\n", sensorName, bsecConfigFileName); + LOG_INFO("%s state read from %s", sensorName, bsecConfigFileName); } else { - LOG_INFO("No %s state found (File: %s).\n", sensorName, bsecConfigFileName); + LOG_INFO("No %s state found (File: %s)", sensorName, bsecConfigFileName); } #else - LOG_ERROR("ERROR: Filesystem not implemented\n"); + LOG_ERROR("ERROR: Filesystem not implemented"); #endif } @@ -97,16 +97,16 @@ void BME680Sensor::updateState() /* First state update when IAQ accuracy is >= 3 */ accuracy = bme680.getData(BSEC_OUTPUT_IAQ).accuracy; if (accuracy >= 2) { - LOG_DEBUG("%s state update IAQ accuracy %u >= 2\n", sensorName, accuracy); + LOG_DEBUG("%s state update IAQ accuracy %u >= 2", sensorName, accuracy); update = true; stateUpdateCounter++; } else { - LOG_DEBUG("%s not updated, IAQ accuracy is %u < 2\n", sensorName, accuracy); + LOG_DEBUG("%s not updated, IAQ accuracy is %u < 2", sensorName, accuracy); } } else { /* Update every STATE_SAVE_PERIOD minutes */ if ((stateUpdateCounter * STATE_SAVE_PERIOD) < millis()) { - LOG_DEBUG("%s state update every %d minutes\n", sensorName, STATE_SAVE_PERIOD / 60000); + LOG_DEBUG("%s state update every %d minutes", sensorName, STATE_SAVE_PERIOD / 60000); update = true; stateUpdateCounter++; } @@ -115,34 +115,34 @@ void BME680Sensor::updateState() if (update) { bme680.getState(bsecState); if (FSCom.exists(bsecConfigFileName) && !FSCom.remove(bsecConfigFileName)) { - LOG_WARN("Can't remove old state file\n"); + LOG_WARN("Can't remove old state file"); } auto file = FSCom.open(bsecConfigFileName, FILE_O_WRITE); if (file) { - LOG_INFO("%s state write to %s.\n", sensorName, bsecConfigFileName); + LOG_INFO("%s state write to %s", sensorName, bsecConfigFileName); file.write((uint8_t *)&bsecState, BSEC_MAX_STATE_BLOB_SIZE); file.flush(); file.close(); } else { - LOG_INFO("Can't write %s state (File: %s).\n", sensorName, bsecConfigFileName); + LOG_INFO("Can't write %s state (File: %s)", sensorName, bsecConfigFileName); } } #else - LOG_ERROR("ERROR: Filesystem not implemented\n"); + LOG_ERROR("ERROR: Filesystem not implemented"); #endif } void BME680Sensor::checkStatus(String functionName) { if (bme680.status < BSEC_OK) - LOG_ERROR("%s BSEC2 code: %s\n", functionName.c_str(), String(bme680.status).c_str()); + LOG_ERROR("%s BSEC2 code: %s", functionName.c_str(), String(bme680.status).c_str()); else if (bme680.status > BSEC_OK) - LOG_WARN("%s BSEC2 code: %s\n", functionName.c_str(), String(bme680.status).c_str()); + LOG_WARN("%s BSEC2 code: %s", functionName.c_str(), String(bme680.status).c_str()); if (bme680.sensor.status < BME68X_OK) - LOG_ERROR("%s BME68X code: %s\n", functionName.c_str(), String(bme680.sensor.status).c_str()); + LOG_ERROR("%s BME68X code: %s", functionName.c_str(), String(bme680.sensor.status).c_str()); else if (bme680.sensor.status > BME68X_OK) - LOG_WARN("%s BME68X code: %s\n", functionName.c_str(), String(bme680.sensor.status).c_str()); + LOG_WARN("%s BME68X code: %s", functionName.c_str(), String(bme680.sensor.status).c_str()); } -#endif \ No newline at end of file +#endif diff --git a/src/modules/Telemetry/Sensor/BMP085Sensor.cpp b/src/modules/Telemetry/Sensor/BMP085Sensor.cpp index 15951126f..7f59f14f0 100644 --- a/src/modules/Telemetry/Sensor/BMP085Sensor.cpp +++ b/src/modules/Telemetry/Sensor/BMP085Sensor.cpp @@ -12,7 +12,7 @@ BMP085Sensor::BMP085Sensor() : TelemetrySensor(meshtastic_TelemetrySensorType_BM int32_t BMP085Sensor::runOnce() { - LOG_INFO("Init sensor: %s\n", sensorName); + LOG_INFO("Init sensor: %s", sensorName); if (!hasSensor()) { return DEFAULT_SENSOR_MINIMUM_WAIT_TIME_BETWEEN_READS; } @@ -29,7 +29,7 @@ bool BMP085Sensor::getMetrics(meshtastic_Telemetry *measurement) measurement->variant.environment_metrics.has_temperature = true; measurement->variant.environment_metrics.has_barometric_pressure = true; - LOG_DEBUG("BMP085Sensor::getMetrics\n"); + LOG_DEBUG("BMP085 getMetrics"); measurement->variant.environment_metrics.temperature = bmp085.readTemperature(); measurement->variant.environment_metrics.barometric_pressure = bmp085.readPressure() / 100.0F; diff --git a/src/modules/Telemetry/Sensor/BMP280Sensor.cpp b/src/modules/Telemetry/Sensor/BMP280Sensor.cpp index 6b0743d75..56a8bc080 100644 --- a/src/modules/Telemetry/Sensor/BMP280Sensor.cpp +++ b/src/modules/Telemetry/Sensor/BMP280Sensor.cpp @@ -12,7 +12,7 @@ BMP280Sensor::BMP280Sensor() : TelemetrySensor(meshtastic_TelemetrySensorType_BM int32_t BMP280Sensor::runOnce() { - LOG_INFO("Init sensor: %s\n", sensorName); + LOG_INFO("Init sensor: %s", sensorName); if (!hasSensor()) { return DEFAULT_SENSOR_MINIMUM_WAIT_TIME_BETWEEN_READS; } @@ -34,7 +34,7 @@ bool BMP280Sensor::getMetrics(meshtastic_Telemetry *measurement) measurement->variant.environment_metrics.has_temperature = true; measurement->variant.environment_metrics.has_barometric_pressure = true; - LOG_DEBUG("BMP280Sensor::getMetrics\n"); + LOG_DEBUG("BMP280 getMetrics"); bmp280.takeForcedMeasurement(); measurement->variant.environment_metrics.temperature = bmp280.readTemperature(); measurement->variant.environment_metrics.barometric_pressure = bmp280.readPressure() / 100.0F; diff --git a/src/modules/Telemetry/Sensor/BMP3XXSensor.cpp b/src/modules/Telemetry/Sensor/BMP3XXSensor.cpp index 399610613..69feaf3d9 100644 --- a/src/modules/Telemetry/Sensor/BMP3XXSensor.cpp +++ b/src/modules/Telemetry/Sensor/BMP3XXSensor.cpp @@ -10,7 +10,7 @@ void BMP3XXSensor::setup() {} int32_t BMP3XXSensor::runOnce() { - LOG_INFO("Init sensor: %s\n", sensorName); + LOG_INFO("Init sensor: %s", sensorName); if (!hasSensor()) { return DEFAULT_SENSOR_MINIMUM_WAIT_TIME_BETWEEN_READS; } @@ -50,11 +50,11 @@ bool BMP3XXSensor::getMetrics(meshtastic_Telemetry *measurement) measurement->variant.environment_metrics.barometric_pressure = static_cast(bmp3xx->pressure) / 100.0F; measurement->variant.environment_metrics.relative_humidity = 0.0f; - LOG_DEBUG("BMP3XXSensor::getMetrics id: %i temp: %.1f press %.1f\n", measurement->which_variant, + LOG_DEBUG("BMP3XX getMetrics id: %i temp: %.1f press %.1f", measurement->which_variant, measurement->variant.environment_metrics.temperature, measurement->variant.environment_metrics.barometric_pressure); } else { - LOG_DEBUG("BMP3XXSensor::getMetrics id: %i\n", measurement->which_variant); + LOG_DEBUG("BMP3XX getMetrics id: %i", measurement->which_variant); } return true; } diff --git a/src/modules/Telemetry/Sensor/CGRadSensSensor.cpp b/src/modules/Telemetry/Sensor/CGRadSensSensor.cpp new file mode 100644 index 000000000..5e69cc22f --- /dev/null +++ b/src/modules/Telemetry/Sensor/CGRadSensSensor.cpp @@ -0,0 +1,75 @@ +/* + * Support for the ClimateGuard RadSens Dosimeter + * A fun and educational sensor for Meshtastic; not for safety critical applications. + */ +#include "configuration.h" + +#if !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR + +#include "../mesh/generated/meshtastic/telemetry.pb.h" +#include "CGRadSensSensor.h" +#include "TelemetrySensor.h" +#include +#include + +CGRadSensSensor::CGRadSensSensor() : TelemetrySensor(meshtastic_TelemetrySensorType_RADSENS, "RadSens") {} + +int32_t CGRadSensSensor::runOnce() +{ + // Initialize the sensor following the same pattern as RCWL9620Sensor + LOG_INFO("Init sensor: %s", sensorName); + if (!hasSensor()) { + return DEFAULT_SENSOR_MINIMUM_WAIT_TIME_BETWEEN_READS; + } + + status = true; + begin(nodeTelemetrySensorsMap[sensorType].second, nodeTelemetrySensorsMap[sensorType].first); + + return initI2CSensor(); +} + +void CGRadSensSensor::setup() {} + +void CGRadSensSensor::begin(TwoWire *wire, uint8_t addr) +{ + // Store the Wire and address to the sensor following the same pattern as RCWL9620Sensor + _wire = wire; + _addr = addr; + _wire->begin(); +} + +float CGRadSensSensor::getStaticRadiation() +{ + // Read a register, following the same pattern as the RCWL9620Sensor + uint32_t data; + _wire->beginTransmission(_addr); // Transfer data to addr. + _wire->write(0x06); // Radiation intensity (static period T = 500 sec) + if (_wire->endTransmission() == 0) { + if (_wire->requestFrom(_addr, (uint8_t)3)) { + ; // Request 3 bytes + data = _wire->read(); + data <<= 8; + data |= _wire->read(); + data <<= 8; + data |= _wire->read(); + + // As per the data sheet for the RadSens + // Register 0x06 contains the reading in 0.1 * μR / h + float microRadPerHr = float(data) / 10.0; + return microRadPerHr; + } + } + return -1.0; +} + +bool CGRadSensSensor::getMetrics(meshtastic_Telemetry *measurement) +{ + // Store the meansurement in the the appropriate fields of the protobuf + measurement->variant.environment_metrics.has_radiation = true; + + LOG_DEBUG("CGRADSENS getMetrics"); + measurement->variant.environment_metrics.radiation = getStaticRadiation(); + + return true; +} +#endif \ No newline at end of file diff --git a/src/modules/Telemetry/Sensor/CGRadSensSensor.h b/src/modules/Telemetry/Sensor/CGRadSensSensor.h new file mode 100644 index 000000000..3b15a19a2 --- /dev/null +++ b/src/modules/Telemetry/Sensor/CGRadSensSensor.h @@ -0,0 +1,30 @@ +/* + * Support for the ClimateGuard RadSens Dosimeter + * A fun and educational sensor for Meshtastic; not for safety critical applications. + */ +#include "configuration.h" + +#if !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR + +#include "../mesh/generated/meshtastic/telemetry.pb.h" +#include "TelemetrySensor.h" +#include + +class CGRadSensSensor : public TelemetrySensor +{ + private: + uint8_t _addr = 0x66; + TwoWire *_wire = &Wire; + + protected: + virtual void setup() override; + void begin(TwoWire *wire = &Wire, uint8_t addr = 0x66); + float getStaticRadiation(); + + public: + CGRadSensSensor(); + virtual int32_t runOnce() override; + virtual bool getMetrics(meshtastic_Telemetry *measurement) override; +}; + +#endif \ No newline at end of file diff --git a/src/modules/Telemetry/Sensor/DFRobotLarkSensor.cpp b/src/modules/Telemetry/Sensor/DFRobotLarkSensor.cpp index 4b01eb444..1d143b03b 100644 --- a/src/modules/Telemetry/Sensor/DFRobotLarkSensor.cpp +++ b/src/modules/Telemetry/Sensor/DFRobotLarkSensor.cpp @@ -13,7 +13,7 @@ DFRobotLarkSensor::DFRobotLarkSensor() : TelemetrySensor(meshtastic_TelemetrySen int32_t DFRobotLarkSensor::runOnce() { - LOG_INFO("Init sensor: %s\n", sensorName); + LOG_INFO("Init sensor: %s", sensorName); if (!hasSensor()) { return DEFAULT_SENSOR_MINIMUM_WAIT_TIME_BETWEEN_READS; } @@ -22,10 +22,10 @@ int32_t DFRobotLarkSensor::runOnce() if (lark.begin() == 0) // DFRobotLarkSensor init { - LOG_DEBUG("DFRobotLarkSensor Init Succeed\n"); + LOG_DEBUG("DFRobotLarkSensor Init Succeed"); status = true; } else { - LOG_ERROR("DFRobotLarkSensor Init Failed\n"); + LOG_ERROR("DFRobotLarkSensor Init Failed"); status = false; } return initI2CSensor(); @@ -47,11 +47,11 @@ bool DFRobotLarkSensor::getMetrics(meshtastic_Telemetry *measurement) measurement->variant.environment_metrics.wind_direction = GeoCoord::bearingToDegrees(lark.getValue("Dir").c_str()); measurement->variant.environment_metrics.barometric_pressure = lark.getValue("Pressure").toFloat(); - LOG_INFO("Temperature: %f\n", measurement->variant.environment_metrics.temperature); - LOG_INFO("Humidity: %f\n", measurement->variant.environment_metrics.relative_humidity); - LOG_INFO("Wind Speed: %f\n", measurement->variant.environment_metrics.wind_speed); - LOG_INFO("Wind Direction: %d\n", measurement->variant.environment_metrics.wind_direction); - LOG_INFO("Barometric Pressure: %f\n", measurement->variant.environment_metrics.barometric_pressure); + LOG_INFO("Temperature: %f", measurement->variant.environment_metrics.temperature); + LOG_INFO("Humidity: %f", measurement->variant.environment_metrics.relative_humidity); + LOG_INFO("Wind Speed: %f", measurement->variant.environment_metrics.wind_speed); + LOG_INFO("Wind Direction: %d", measurement->variant.environment_metrics.wind_direction); + LOG_INFO("Barometric Pressure: %f", measurement->variant.environment_metrics.barometric_pressure); return true; } diff --git a/src/modules/Telemetry/Sensor/INA219Sensor.cpp b/src/modules/Telemetry/Sensor/INA219Sensor.cpp index f70d3705e..de69163b4 100644 --- a/src/modules/Telemetry/Sensor/INA219Sensor.cpp +++ b/src/modules/Telemetry/Sensor/INA219Sensor.cpp @@ -15,7 +15,7 @@ INA219Sensor::INA219Sensor() : TelemetrySensor(meshtastic_TelemetrySensorType_IN int32_t INA219Sensor::runOnce() { - LOG_INFO("Init sensor: %s\n", sensorName); + LOG_INFO("Init sensor: %s", sensorName); if (!hasSensor()) { return DEFAULT_SENSOR_MINIMUM_WAIT_TIME_BETWEEN_READS; } diff --git a/src/modules/Telemetry/Sensor/INA260Sensor.cpp b/src/modules/Telemetry/Sensor/INA260Sensor.cpp index 751608c82..24182b336 100644 --- a/src/modules/Telemetry/Sensor/INA260Sensor.cpp +++ b/src/modules/Telemetry/Sensor/INA260Sensor.cpp @@ -11,7 +11,7 @@ INA260Sensor::INA260Sensor() : TelemetrySensor(meshtastic_TelemetrySensorType_IN int32_t INA260Sensor::runOnce() { - LOG_INFO("Init sensor: %s\n", sensorName); + LOG_INFO("Init sensor: %s", sensorName); if (!hasSensor()) { return DEFAULT_SENSOR_MINIMUM_WAIT_TIME_BETWEEN_READS; } diff --git a/src/modules/Telemetry/Sensor/INA3221Sensor.cpp b/src/modules/Telemetry/Sensor/INA3221Sensor.cpp index 549346d72..ed09856e2 100644 --- a/src/modules/Telemetry/Sensor/INA3221Sensor.cpp +++ b/src/modules/Telemetry/Sensor/INA3221Sensor.cpp @@ -11,7 +11,7 @@ INA3221Sensor::INA3221Sensor() : TelemetrySensor(meshtastic_TelemetrySensorType_ int32_t INA3221Sensor::runOnce() { - LOG_INFO("Init sensor: %s\n", sensorName); + LOG_INFO("Init sensor: %s", sensorName); if (!hasSensor()) { return DEFAULT_SENSOR_MINIMUM_WAIT_TIME_BETWEEN_READS; } diff --git a/src/modules/Telemetry/Sensor/LPS22HBSensor.cpp b/src/modules/Telemetry/Sensor/LPS22HBSensor.cpp index 111d86d1a..170fafd39 100644 --- a/src/modules/Telemetry/Sensor/LPS22HBSensor.cpp +++ b/src/modules/Telemetry/Sensor/LPS22HBSensor.cpp @@ -12,7 +12,7 @@ LPS22HBSensor::LPS22HBSensor() : TelemetrySensor(meshtastic_TelemetrySensorType_ int32_t LPS22HBSensor::runOnce() { - LOG_INFO("Init sensor: %s\n", sensorName); + LOG_INFO("Init sensor: %s", sensorName); if (!hasSensor()) { return DEFAULT_SENSOR_MINIMUM_WAIT_TIME_BETWEEN_READS; } diff --git a/src/modules/Telemetry/Sensor/MAX17048Sensor.cpp b/src/modules/Telemetry/Sensor/MAX17048Sensor.cpp index 96dd5ae80..3aacf9cd7 100644 --- a/src/modules/Telemetry/Sensor/MAX17048Sensor.cpp +++ b/src/modules/Telemetry/Sensor/MAX17048Sensor.cpp @@ -19,7 +19,7 @@ MAX17048Singleton *MAX17048Singleton::pinstance{nullptr}; bool MAX17048Singleton::runOnce(TwoWire *theWire) { initialized = begin(theWire); - LOG_DEBUG("%s::runOnce %s\n", sensorStr, initialized ? "began ok" : "begin failed"); + LOG_DEBUG("%s::runOnce %s", sensorStr, initialized ? "began ok" : "begin failed"); return initialized; } @@ -27,7 +27,7 @@ bool MAX17048Singleton::isBatteryCharging() { float volts = cellVoltage(); if (isnan(volts)) { - LOG_DEBUG("%s::isBatteryCharging is not connected\n", sensorStr); + LOG_DEBUG("%s::isBatteryCharging not connected", sensorStr); return 0; } @@ -53,7 +53,7 @@ bool MAX17048Singleton::isBatteryCharging() chargeState = MAX17048ChargeState::IDLE; } - LOG_DEBUG("%s::isBatteryCharging %s volts: %.3f soc: %.3f rate: %.3f\n", sensorStr, chargeLabels[chargeState], volts, + LOG_DEBUG("%s::isBatteryCharging %s volts: %.3f soc: %.3f rate: %.3f", sensorStr, chargeLabels[chargeState], volts, sample.cellPercent, sample.chargeRate); return chargeState == MAX17048ChargeState::IMPORT; } @@ -62,17 +62,17 @@ uint16_t MAX17048Singleton::getBusVoltageMv() { float volts = cellVoltage(); if (isnan(volts)) { - LOG_DEBUG("%s::getBusVoltageMv is not connected\n", sensorStr); + LOG_DEBUG("%s::getBusVoltageMv is not connected", sensorStr); return 0; } - LOG_DEBUG("%s::getBusVoltageMv %.3fmV\n", sensorStr, volts); + LOG_DEBUG("%s::getBusVoltageMv %.3fmV", sensorStr, volts); return (uint16_t)(volts * 1000.0f); } uint8_t MAX17048Singleton::getBusBatteryPercent() { float soc = cellPercent(); - LOG_DEBUG("%s::getBusBatteryPercent %.1f%%\n", sensorStr, soc); + LOG_DEBUG("%s::getBusBatteryPercent %.1f%%", sensorStr, soc); return clamp(static_cast(round(soc)), static_cast(0), static_cast(100)); } @@ -82,7 +82,7 @@ uint16_t MAX17048Singleton::getTimeToGoSecs() float soc = cellPercent(); // state of charge in percent 0 to 100 soc = clamp(soc, 0.0f, 100.0f); // clamp soc between 0 and 100% float ttg = ((100.0f - soc) / rate) * 3600.0f; // calculate seconds to charge/discharge - LOG_DEBUG("%s::getTimeToGoSecs %.0f seconds\n", sensorStr, ttg); + LOG_DEBUG("%s::getTimeToGoSecs %.0f seconds", sensorStr, ttg); return (uint16_t)ttg; } @@ -90,7 +90,7 @@ bool MAX17048Singleton::isBatteryConnected() { float volts = cellVoltage(); if (isnan(volts)) { - LOG_DEBUG("%s::isBatteryConnected is not connected\n", sensorStr); + LOG_DEBUG("%s::isBatteryConnected is not connected", sensorStr); return false; } @@ -103,12 +103,12 @@ bool MAX17048Singleton::isExternallyPowered() float volts = cellVoltage(); if (isnan(volts)) { // if the battery is not connected then there must be external power - LOG_DEBUG("%s::isExternallyPowered battery is\n", sensorStr); + LOG_DEBUG("%s::isExternallyPowered battery is", sensorStr); return true; } // if the bus voltage is over MAX17048_BUS_POWER_VOLTS, then the external power // is assumed to be connected - LOG_DEBUG("%s::isExternallyPowered %s connected\n", sensorStr, volts >= MAX17048_BUS_POWER_VOLTS ? "is" : "is not"); + LOG_DEBUG("%s::isExternallyPowered %s connected", sensorStr, volts >= MAX17048_BUS_POWER_VOLTS ? "is" : "is not"); return volts >= MAX17048_BUS_POWER_VOLTS; } @@ -119,11 +119,11 @@ MAX17048Sensor::MAX17048Sensor() : TelemetrySensor(meshtastic_TelemetrySensorTyp int32_t MAX17048Sensor::runOnce() { if (isInitialized()) { - LOG_INFO("Init sensor: %s is already initialised\n", sensorName); + LOG_INFO("Init sensor: %s is already initialised", sensorName); return true; } - LOG_INFO("Init sensor: %s\n", sensorName); + LOG_INFO("Init sensor: %s", sensorName); if (!hasSensor()) { return DEFAULT_SENSOR_MINIMUM_WAIT_TIME_BETWEEN_READS; } @@ -140,11 +140,11 @@ void MAX17048Sensor::setup() {} bool MAX17048Sensor::getMetrics(meshtastic_Telemetry *measurement) { - LOG_DEBUG("MAX17048Sensor::getMetrics id: %i\n", measurement->which_variant); + LOG_DEBUG("MAX17048 getMetrics id: %i", measurement->which_variant); float volts = max17048->cellVoltage(); if (isnan(volts)) { - LOG_DEBUG("MAX17048Sensor::getMetrics battery is not connected\n"); + LOG_DEBUG("MAX17048 getMetrics battery is not connected"); return false; } @@ -153,7 +153,7 @@ bool MAX17048Sensor::getMetrics(meshtastic_Telemetry *measurement) soc = clamp(soc, 0.0f, 100.0f); // clamp soc between 0 and 100% float ttg = (100.0f - soc) / rate; // calculate hours to charge/discharge - LOG_DEBUG("MAX17048Sensor::getMetrics volts: %.3fV soc: %.1f%% ttg: %.1f hours\n", volts, soc, ttg); + LOG_DEBUG("MAX17048 getMetrics volts: %.3fV soc: %.1f%% ttg: %.1f hours", volts, soc, ttg); if ((int)measurement->which_variant == meshtastic_Telemetry_power_metrics_tag) { measurement->variant.power_metrics.has_ch1_voltage = true; measurement->variant.power_metrics.ch1_voltage = volts; diff --git a/src/modules/Telemetry/Sensor/MAX30102Sensor.cpp b/src/modules/Telemetry/Sensor/MAX30102Sensor.cpp index b3b20e5f2..88128a6db 100644 --- a/src/modules/Telemetry/Sensor/MAX30102Sensor.cpp +++ b/src/modules/Telemetry/Sensor/MAX30102Sensor.cpp @@ -11,7 +11,7 @@ MAX30102Sensor::MAX30102Sensor() : TelemetrySensor(meshtastic_TelemetrySensorTyp int32_t MAX30102Sensor::runOnce() { - LOG_INFO("Init sensor: %s\n", sensorName); + LOG_INFO("Init sensor: %s", sensorName); if (!hasSensor()) { return DEFAULT_SENSOR_MINIMUM_WAIT_TIME_BETWEEN_READS; } @@ -28,10 +28,10 @@ int32_t MAX30102Sensor::runOnce() max30102.enableDIETEMPRDY(); // Enable the temperature ready interrupt max30102.setup(brightness, sampleAverage, leds, sampleRate, pulseWidth, adcRange); - LOG_DEBUG("MAX30102 Init Succeed\n"); + LOG_DEBUG("MAX30102 Init Succeed"); status = true; } else { - LOG_ERROR("MAX30102 Init Failed\n"); + LOG_ERROR("MAX30102 Init Failed"); status = false; } return initI2CSensor(); diff --git a/src/modules/Telemetry/Sensor/MCP9808Sensor.cpp b/src/modules/Telemetry/Sensor/MCP9808Sensor.cpp index c1cda7227..58ce29cd2 100644 --- a/src/modules/Telemetry/Sensor/MCP9808Sensor.cpp +++ b/src/modules/Telemetry/Sensor/MCP9808Sensor.cpp @@ -11,7 +11,7 @@ MCP9808Sensor::MCP9808Sensor() : TelemetrySensor(meshtastic_TelemetrySensorType_ int32_t MCP9808Sensor::runOnce() { - LOG_INFO("Init sensor: %s\n", sensorName); + LOG_INFO("Init sensor: %s", sensorName); if (!hasSensor()) { return DEFAULT_SENSOR_MINIMUM_WAIT_TIME_BETWEEN_READS; } @@ -28,7 +28,7 @@ bool MCP9808Sensor::getMetrics(meshtastic_Telemetry *measurement) { measurement->variant.environment_metrics.has_temperature = true; - LOG_DEBUG("MCP9808Sensor::getMetrics\n"); + LOG_DEBUG("MCP9808 getMetrics"); measurement->variant.environment_metrics.temperature = mcp9808.readTempC(); return true; } diff --git a/src/modules/Telemetry/Sensor/MLX90614Sensor.cpp b/src/modules/Telemetry/Sensor/MLX90614Sensor.cpp index 92c22bf21..d9908fce3 100644 --- a/src/modules/Telemetry/Sensor/MLX90614Sensor.cpp +++ b/src/modules/Telemetry/Sensor/MLX90614Sensor.cpp @@ -9,7 +9,7 @@ MLX90614Sensor::MLX90614Sensor() : TelemetrySensor(meshtastic_TelemetrySensorTyp int32_t MLX90614Sensor::runOnce() { - LOG_INFO("Init sensor: %s\n", sensorName); + LOG_INFO("Init sensor: %s", sensorName); if (!hasSensor()) { return DEFAULT_SENSOR_MINIMUM_WAIT_TIME_BETWEEN_READS; } @@ -19,12 +19,12 @@ int32_t MLX90614Sensor::runOnce() LOG_DEBUG("MLX90614 emissivity: %f", mlx.readEmissivity()); if (fabs(MLX90614_EMISSIVITY - mlx.readEmissivity()) > 0.001) { mlx.writeEmissivity(MLX90614_EMISSIVITY); - LOG_INFO("MLX90614 emissivity updated. In case of weird data, power cycle."); + LOG_INFO("MLX90614 emissivity updated. In case of weird data, power cycle"); } - LOG_DEBUG("MLX90614 Init Succeed\n"); + LOG_DEBUG("MLX90614 Init Succeed"); status = true; } else { - LOG_ERROR("MLX90614 Init Failed\n"); + LOG_ERROR("MLX90614 Init Failed"); status = false; } return initI2CSensor(); diff --git a/src/modules/Telemetry/Sensor/MLX90632Sensor.cpp b/src/modules/Telemetry/Sensor/MLX90632Sensor.cpp index 0568a4652..b7bd6ae61 100644 --- a/src/modules/Telemetry/Sensor/MLX90632Sensor.cpp +++ b/src/modules/Telemetry/Sensor/MLX90632Sensor.cpp @@ -10,7 +10,7 @@ MLX90632Sensor::MLX90632Sensor() : TelemetrySensor(meshtastic_TelemetrySensorTyp int32_t MLX90632Sensor::runOnce() { - LOG_INFO("Init sensor: %s\n", sensorName); + LOG_INFO("Init sensor: %s", sensorName); if (!hasSensor()) { return DEFAULT_SENSOR_MINIMUM_WAIT_TIME_BETWEEN_READS; } @@ -19,10 +19,10 @@ int32_t MLX90632Sensor::runOnce() if (mlx.begin(nodeTelemetrySensorsMap[sensorType].first, *nodeTelemetrySensorsMap[sensorType].second, returnError) == true) // MLX90632 init { - LOG_DEBUG("MLX90632 Init Succeed\n"); + LOG_DEBUG("MLX90632 Init Succeed"); status = true; } else { - LOG_ERROR("MLX90632 Init Failed\n"); + LOG_ERROR("MLX90632 Init Failed"); status = false; } return initI2CSensor(); diff --git a/src/modules/Telemetry/Sensor/NAU7802Sensor.cpp b/src/modules/Telemetry/Sensor/NAU7802Sensor.cpp index 59f310a24..65f616686 100644 --- a/src/modules/Telemetry/Sensor/NAU7802Sensor.cpp +++ b/src/modules/Telemetry/Sensor/NAU7802Sensor.cpp @@ -17,17 +17,17 @@ NAU7802Sensor::NAU7802Sensor() : TelemetrySensor(meshtastic_TelemetrySensorType_ int32_t NAU7802Sensor::runOnce() { - LOG_INFO("Init sensor: %s\n", sensorName); + LOG_INFO("Init sensor: %s", sensorName); if (!hasSensor()) { return DEFAULT_SENSOR_MINIMUM_WAIT_TIME_BETWEEN_READS; } status = nau7802.begin(*nodeTelemetrySensorsMap[sensorType].second); nau7802.setSampleRate(NAU7802_SPS_320); if (!loadCalibrationData()) { - LOG_ERROR("Failed to load calibration data\n"); + LOG_ERROR("Failed to load calibration data"); } nau7802.calibrateAFE(); - LOG_INFO("Offset: %d, Calibration factor: %.2f\n", nau7802.getZeroOffset(), nau7802.getCalibrationFactor()); + LOG_INFO("Offset: %d, Calibration factor: %.2f", nau7802.getZeroOffset(), nau7802.getCalibrationFactor()); return initI2CSensor(); } @@ -35,7 +35,7 @@ void NAU7802Sensor::setup() {} bool NAU7802Sensor::getMetrics(meshtastic_Telemetry *measurement) { - LOG_DEBUG("NAU7802Sensor::getMetrics\n"); + LOG_DEBUG("NAU7802 getMetrics"); nau7802.powerUp(); // Wait for the sensor to become ready for one second max uint32_t start = millis(); @@ -48,7 +48,7 @@ bool NAU7802Sensor::getMetrics(meshtastic_Telemetry *measurement) } measurement->variant.environment_metrics.has_weight = true; // Check if we have correct calibration values after powerup - LOG_DEBUG("Offset: %d, Calibration factor: %.2f\n", nau7802.getZeroOffset(), nau7802.getCalibrationFactor()); + LOG_DEBUG("Offset: %d, Calibration factor: %.2f", nau7802.getZeroOffset(), nau7802.getCalibrationFactor()); measurement->variant.environment_metrics.weight = nau7802.getWeight() / 1000; // sample is in kg nau7802.powerDown(); return true; @@ -58,9 +58,9 @@ void NAU7802Sensor::calibrate(float weight) { nau7802.calculateCalibrationFactor(weight * 1000, 64); // internal sample is in grams if (!saveCalibrationData()) { - LOG_WARN("Failed to save calibration data\n"); + LOG_WARN("Failed to save calibration data"); } - LOG_INFO("Offset: %d, Calibration factor: %.2f\n", nau7802.getZeroOffset(), nau7802.getCalibrationFactor()); + LOG_INFO("Offset: %d, Calibration factor: %.2f", nau7802.getZeroOffset(), nau7802.getCalibrationFactor()); } AdminMessageHandleResult NAU7802Sensor::handleAdminMessage(const meshtastic_MeshPacket &mp, meshtastic_AdminMessage *request, @@ -72,10 +72,10 @@ AdminMessageHandleResult NAU7802Sensor::handleAdminMessage(const meshtastic_Mesh case meshtastic_AdminMessage_set_scale_tag: if (request->set_scale == 0) { this->tare(); - LOG_DEBUG("Client requested to tare scale\n"); + LOG_DEBUG("Client requested to tare scale"); } else { this->calibrate(request->set_scale); - LOG_DEBUG("Client requested to calibrate to %d kg\n", request->set_scale); + LOG_DEBUG("Client requested to calibrate to %d kg", request->set_scale); } result = AdminMessageHandleResult::HANDLED; break; @@ -91,9 +91,9 @@ void NAU7802Sensor::tare() { nau7802.calculateZeroOffset(64); if (!saveCalibrationData()) { - LOG_WARN("Failed to save calibration data\n"); + LOG_WARN("Failed to save calibration data"); } - LOG_INFO("Offset: %d, Calibration factor: %.2f\n", nau7802.getZeroOffset(), nau7802.getCalibrationFactor()); + LOG_INFO("Offset: %d, Calibration factor: %.2f", nau7802.getZeroOffset(), nau7802.getCalibrationFactor()); } bool NAU7802Sensor::saveCalibrationData() @@ -103,11 +103,11 @@ bool NAU7802Sensor::saveCalibrationData() nau7802config.calibrationFactor = nau7802.getCalibrationFactor(); bool okay = false; - LOG_INFO("%s state write to %s.\n", sensorName, nau7802ConfigFileName); + LOG_INFO("%s state write to %s", sensorName, nau7802ConfigFileName); pb_ostream_t stream = {&writecb, static_cast(&file), meshtastic_Nau7802Config_size}; if (!pb_encode(&stream, &meshtastic_Nau7802Config_msg, &nau7802config)) { - LOG_ERROR("Error: can't encode protobuf %s\n", PB_GET_ERROR(&stream)); + LOG_ERROR("Error: can't encode protobuf %s", PB_GET_ERROR(&stream)); } else { okay = true; } @@ -121,10 +121,10 @@ bool NAU7802Sensor::loadCalibrationData() auto file = FSCom.open(nau7802ConfigFileName, FILE_O_READ); bool okay = false; if (file) { - LOG_INFO("%s state read from %s.\n", sensorName, nau7802ConfigFileName); + LOG_INFO("%s state read from %s", sensorName, nau7802ConfigFileName); pb_istream_t stream = {&readcb, &file, meshtastic_Nau7802Config_size}; if (!pb_decode(&stream, &meshtastic_Nau7802Config_msg, &nau7802config)) { - LOG_ERROR("Error: can't decode protobuf %s\n", PB_GET_ERROR(&stream)); + LOG_ERROR("Error: can't decode protobuf %s", PB_GET_ERROR(&stream)); } else { nau7802.setZeroOffset(nau7802config.zeroOffset); nau7802.setCalibrationFactor(nau7802config.calibrationFactor); @@ -132,9 +132,9 @@ bool NAU7802Sensor::loadCalibrationData() } file.close(); } else { - LOG_INFO("No %s state found (File: %s).\n", sensorName, nau7802ConfigFileName); + LOG_INFO("No %s state found (File: %s)", sensorName, nau7802ConfigFileName); } return okay; } -#endif \ No newline at end of file +#endif diff --git a/src/modules/Telemetry/Sensor/OPT3001Sensor.cpp b/src/modules/Telemetry/Sensor/OPT3001Sensor.cpp index 3e4376d55..75c6cd41a 100644 --- a/src/modules/Telemetry/Sensor/OPT3001Sensor.cpp +++ b/src/modules/Telemetry/Sensor/OPT3001Sensor.cpp @@ -11,7 +11,7 @@ OPT3001Sensor::OPT3001Sensor() : TelemetrySensor(meshtastic_TelemetrySensorType_ int32_t OPT3001Sensor::runOnce() { - LOG_INFO("Init sensor: %s\n", sensorName); + LOG_INFO("Init sensor: %s", sensorName); if (!hasSensor()) { return DEFAULT_SENSOR_MINIMUM_WAIT_TIME_BETWEEN_READS; } @@ -42,7 +42,7 @@ bool OPT3001Sensor::getMetrics(meshtastic_Telemetry *measurement) OPT3001 result = opt3001.readResult(); measurement->variant.environment_metrics.lux = result.lux; - LOG_INFO("Lux: %f\n", measurement->variant.environment_metrics.lux); + LOG_INFO("Lux: %f", measurement->variant.environment_metrics.lux); return true; } diff --git a/src/modules/Telemetry/Sensor/RCWL9620Sensor.cpp b/src/modules/Telemetry/Sensor/RCWL9620Sensor.cpp index b9a29ab7d..e352dda8d 100644 --- a/src/modules/Telemetry/Sensor/RCWL9620Sensor.cpp +++ b/src/modules/Telemetry/Sensor/RCWL9620Sensor.cpp @@ -10,7 +10,7 @@ RCWL9620Sensor::RCWL9620Sensor() : TelemetrySensor(meshtastic_TelemetrySensorTyp int32_t RCWL9620Sensor::runOnce() { - LOG_INFO("Init sensor: %s\n", sensorName); + LOG_INFO("Init sensor: %s", sensorName); if (!hasSensor()) { return DEFAULT_SENSOR_MINIMUM_WAIT_TIME_BETWEEN_READS; } @@ -24,7 +24,7 @@ void RCWL9620Sensor::setup() {} bool RCWL9620Sensor::getMetrics(meshtastic_Telemetry *measurement) { measurement->variant.environment_metrics.has_distance = true; - LOG_DEBUG("RCWL9620Sensor::getMetrics\n"); + LOG_DEBUG("RCWL9620 getMetrics"); measurement->variant.environment_metrics.distance = getDistance(); return true; } diff --git a/src/modules/Telemetry/Sensor/SHT31Sensor.cpp b/src/modules/Telemetry/Sensor/SHT31Sensor.cpp index c372f7986..b96b94fa8 100644 --- a/src/modules/Telemetry/Sensor/SHT31Sensor.cpp +++ b/src/modules/Telemetry/Sensor/SHT31Sensor.cpp @@ -11,7 +11,7 @@ SHT31Sensor::SHT31Sensor() : TelemetrySensor(meshtastic_TelemetrySensorType_SHT3 int32_t SHT31Sensor::runOnce() { - LOG_INFO("Init sensor: %s\n", sensorName); + LOG_INFO("Init sensor: %s", sensorName); if (!hasSensor()) { return DEFAULT_SENSOR_MINIMUM_WAIT_TIME_BETWEEN_READS; } diff --git a/src/modules/Telemetry/Sensor/SHT4XSensor.cpp b/src/modules/Telemetry/Sensor/SHT4XSensor.cpp index 94367cba4..0fa6021dc 100644 --- a/src/modules/Telemetry/Sensor/SHT4XSensor.cpp +++ b/src/modules/Telemetry/Sensor/SHT4XSensor.cpp @@ -11,7 +11,7 @@ SHT4XSensor::SHT4XSensor() : TelemetrySensor(meshtastic_TelemetrySensorType_SHT4 int32_t SHT4XSensor::runOnce() { - LOG_INFO("Init sensor: %s\n", sensorName); + LOG_INFO("Init sensor: %s", sensorName); if (!hasSensor()) { return DEFAULT_SENSOR_MINIMUM_WAIT_TIME_BETWEEN_READS; } @@ -22,7 +22,7 @@ int32_t SHT4XSensor::runOnce() serialNumber = sht4x.readSerial(); if (serialNumber != 0) { - LOG_DEBUG("serialNumber : %x\n", serialNumber); + LOG_DEBUG("serialNumber : %x", serialNumber); status = 1; } else { LOG_DEBUG("Error trying to execute readSerial(): "); diff --git a/src/modules/Telemetry/Sensor/SHTC3Sensor.cpp b/src/modules/Telemetry/Sensor/SHTC3Sensor.cpp index 64ebfb472..3a7cc48d2 100644 --- a/src/modules/Telemetry/Sensor/SHTC3Sensor.cpp +++ b/src/modules/Telemetry/Sensor/SHTC3Sensor.cpp @@ -11,7 +11,7 @@ SHTC3Sensor::SHTC3Sensor() : TelemetrySensor(meshtastic_TelemetrySensorType_SHTC int32_t SHTC3Sensor::runOnce() { - LOG_INFO("Init sensor: %s\n", sensorName); + LOG_INFO("Init sensor: %s", sensorName); if (!hasSensor()) { return DEFAULT_SENSOR_MINIMUM_WAIT_TIME_BETWEEN_READS; } diff --git a/src/modules/Telemetry/Sensor/T1000xSensor.cpp b/src/modules/Telemetry/Sensor/T1000xSensor.cpp index 4772aeb9e..068969e8e 100644 --- a/src/modules/Telemetry/Sensor/T1000xSensor.cpp +++ b/src/modules/Telemetry/Sensor/T1000xSensor.cpp @@ -40,7 +40,7 @@ T1000xSensor::T1000xSensor() : TelemetrySensor(meshtastic_TelemetrySensorType_SE int32_t T1000xSensor::runOnce() { - LOG_INFO("Init sensor: %s\n", sensorName); + LOG_INFO("Init sensor: %s", sensorName); if (!hasSensor()) { return DEFAULT_SENSOR_MINIMUM_WAIT_TIME_BETWEEN_READS; } diff --git a/src/modules/Telemetry/Sensor/TSL2591Sensor.cpp b/src/modules/Telemetry/Sensor/TSL2591Sensor.cpp index 9002874b3..add475d5b 100644 --- a/src/modules/Telemetry/Sensor/TSL2591Sensor.cpp +++ b/src/modules/Telemetry/Sensor/TSL2591Sensor.cpp @@ -12,7 +12,7 @@ TSL2591Sensor::TSL2591Sensor() : TelemetrySensor(meshtastic_TelemetrySensorType_ int32_t TSL2591Sensor::runOnce() { - LOG_INFO("Init sensor: %s\n", sensorName); + LOG_INFO("Init sensor: %s", sensorName); if (!hasSensor()) { return DEFAULT_SENSOR_MINIMUM_WAIT_TIME_BETWEEN_READS; } @@ -36,7 +36,7 @@ bool TSL2591Sensor::getMetrics(meshtastic_Telemetry *measurement) full = lum & 0xFFFF; measurement->variant.environment_metrics.lux = tsl.calculateLux(full, ir); - LOG_INFO("Lux: %f\n", measurement->variant.environment_metrics.lux); + LOG_INFO("Lux: %f", measurement->variant.environment_metrics.lux); return true; } diff --git a/src/modules/Telemetry/Sensor/TelemetrySensor.h b/src/modules/Telemetry/Sensor/TelemetrySensor.h index da376ad31..08cc1125d 100644 --- a/src/modules/Telemetry/Sensor/TelemetrySensor.h +++ b/src/modules/Telemetry/Sensor/TelemetrySensor.h @@ -31,10 +31,10 @@ class TelemetrySensor int32_t initI2CSensor() { if (!status) { - LOG_WARN("Could not connect to detected %s sensor.\n Removing from nodeTelemetrySensorsMap.\n", sensorName); + LOG_WARN("Can't connect to detected %s sensor. Remove from nodeTelemetrySensorsMap", sensorName); nodeTelemetrySensorsMap[sensorType].first = 0; } else { - LOG_INFO("Opened %s sensor on i2c bus\n", sensorName); + LOG_INFO("Opened %s sensor on i2c bus", sensorName); setup(); } initialized = true; diff --git a/src/modules/Telemetry/Sensor/VEML7700Sensor.cpp b/src/modules/Telemetry/Sensor/VEML7700Sensor.cpp index c176ed21b..496b49aeb 100644 --- a/src/modules/Telemetry/Sensor/VEML7700Sensor.cpp +++ b/src/modules/Telemetry/Sensor/VEML7700Sensor.cpp @@ -13,7 +13,7 @@ VEML7700Sensor::VEML7700Sensor() : TelemetrySensor(meshtastic_TelemetrySensorTyp int32_t VEML7700Sensor::runOnce() { - LOG_INFO("Init sensor: %s\n", sensorName); + LOG_INFO("Init sensor: %s", sensorName); if (!hasSensor()) { return DEFAULT_SENSOR_MINIMUM_WAIT_TIME_BETWEEN_READS; } @@ -60,7 +60,7 @@ bool VEML7700Sensor::getMetrics(meshtastic_Telemetry *measurement) measurement->variant.environment_metrics.lux = veml7700.readLux(VEML_LUX_AUTO); white = veml7700.readWhite(true); measurement->variant.environment_metrics.white_lux = computeLux(white, white > 100); - LOG_INFO("white lux %f, als lux %f\n", measurement->variant.environment_metrics.white_lux, + LOG_INFO("white lux %f, als lux %f", measurement->variant.environment_metrics.white_lux, measurement->variant.environment_metrics.lux); return true; diff --git a/src/modules/Telemetry/UnitConversions.cpp b/src/modules/Telemetry/UnitConversions.cpp index 9f40de40f..fff1ee3d2 100644 --- a/src/modules/Telemetry/UnitConversions.cpp +++ b/src/modules/Telemetry/UnitConversions.cpp @@ -1,8 +1,8 @@ #include "UnitConversions.h" -float UnitConversions::CelsiusToFahrenheit(float celcius) +float UnitConversions::CelsiusToFahrenheit(float celsius) { - return (celcius * 9) / 5 + 32; + return (celsius * 9) / 5 + 32; } float UnitConversions::MetersPerSecondToKnots(float metersPerSecond) diff --git a/src/modules/Telemetry/UnitConversions.h b/src/modules/Telemetry/UnitConversions.h index 60f9b664a..638476315 100644 --- a/src/modules/Telemetry/UnitConversions.h +++ b/src/modules/Telemetry/UnitConversions.h @@ -3,7 +3,7 @@ class UnitConversions { public: - static float CelsiusToFahrenheit(float celcius); + static float CelsiusToFahrenheit(float celsius); static float MetersPerSecondToKnots(float metersPerSecond); static float MetersPerSecondToMilesPerHour(float metersPerSecond); static float HectoPascalToInchesOfMercury(float hectoPascal); diff --git a/src/modules/TextMessageModule.cpp b/src/modules/TextMessageModule.cpp index 2933718af..f1d01ad16 100644 --- a/src/modules/TextMessageModule.cpp +++ b/src/modules/TextMessageModule.cpp @@ -10,7 +10,7 @@ ProcessMessage TextMessageModule::handleReceived(const meshtastic_MeshPacket &mp { #ifdef DEBUG_PORT auto &p = mp.decoded; - LOG_INFO("Received text msg from=0x%0x, id=0x%x, msg=%.*s\n", mp.from, mp.id, p.payload.size, p.payload.bytes); + LOG_INFO("Received text msg from=0x%0x, id=0x%x, msg=%.*s", mp.from, mp.id, p.payload.size, p.payload.bytes); #endif // We only store/display messages destined for us. // Keep a copy of the most recent text message. diff --git a/src/modules/TraceRouteModule.cpp b/src/modules/TraceRouteModule.cpp index 955098c28..79b14de0a 100644 --- a/src/modules/TraceRouteModule.cpp +++ b/src/modules/TraceRouteModule.cpp @@ -1,5 +1,6 @@ #include "TraceRouteModule.h" #include "MeshService.h" +#include "meshUtils.h" TraceRouteModule *traceRouteModule; @@ -101,45 +102,47 @@ void TraceRouteModule::appendMyIDandSNR(meshtastic_RouteDiscovery *updated, floa route[*route_count] = myNodeInfo.my_node_num; *route_count += 1; } else { - LOG_WARN("Route exceeded maximum hop limit!\n"); // Are you bridging networks? + LOG_WARN("Route exceeded maximum hop limit!"); // Are you bridging networks? } } void TraceRouteModule::printRoute(meshtastic_RouteDiscovery *r, uint32_t origin, uint32_t dest, bool isTowardsDestination) { #ifdef DEBUG_PORT - LOG_INFO("Route traced:\n"); - LOG_INFO("0x%x --> ", origin); + std::string route = "Route traced:"; + route += vformat("0x%x --> ", origin); for (uint8_t i = 0; i < r->route_count; i++) { if (i < r->snr_towards_count && r->snr_towards[i] != INT8_MIN) - LOG_INFO("0x%x (%.2fdB) --> ", r->route[i], (float)r->snr_towards[i] / 4); + route += vformat("0x%x (%.2fdB) --> ", r->route[i], (float)r->snr_towards[i] / 4); else - LOG_INFO("0x%x (?dB) --> ", r->route[i]); + route += vformat("0x%x (?dB) --> ", r->route[i]); } // If we are the destination, or it has already reached the destination, print it if (dest == nodeDB->getNodeNum() || !isTowardsDestination) { if (r->snr_towards_count > 0 && r->snr_towards[r->snr_towards_count - 1] != INT8_MIN) - LOG_INFO("0x%x (%.2fdB)\n", dest, (float)r->snr_towards[r->snr_towards_count - 1] / 4); + route += vformat("0x%x (%.2fdB)", dest, (float)r->snr_towards[r->snr_towards_count - 1] / 4); + else - LOG_INFO("0x%x (?dB)\n", dest); + route += vformat("0x%x (?dB)", dest); } else - LOG_INFO("...\n"); + route += "..."; // If there's a route back (or we are the destination as then the route is complete), print it if (r->route_back_count > 0 || origin == nodeDB->getNodeNum()) { if (r->snr_towards_count > 0 && origin == nodeDB->getNodeNum()) - LOG_INFO("(%.2fdB) 0x%x <-- ", (float)r->snr_back[r->snr_back_count - 1] / 4, origin); + route += vformat("(%.2fdB) 0x%x <-- ", (float)r->snr_back[r->snr_back_count - 1] / 4, origin); else - LOG_INFO("..."); + route += "..."; for (int8_t i = r->route_back_count - 1; i >= 0; i--) { if (i < r->snr_back_count && r->snr_back[i] != INT8_MIN) - LOG_INFO("(%.2fdB) 0x%x <-- ", (float)r->snr_back[i] / 4, r->route_back[i]); + route += vformat("(%.2fdB) 0x%x <-- ", (float)r->snr_back[i] / 4, r->route_back[i]); else - LOG_INFO("(?dB) 0x%x <-- ", r->route_back[i]); + route += vformat("(?dB) 0x%x <-- ", r->route_back[i]); } - LOG_INFO("0x%x\n", dest); + route += vformat("0x%x", dest); } + LOG_INFO(route.c_str()); #endif } diff --git a/src/modules/WaypointModule.cpp b/src/modules/WaypointModule.cpp index a4611e780..b8b738309 100644 --- a/src/modules/WaypointModule.cpp +++ b/src/modules/WaypointModule.cpp @@ -14,7 +14,7 @@ ProcessMessage WaypointModule::handleReceived(const meshtastic_MeshPacket &mp) { #ifdef DEBUG_PORT auto &p = mp.decoded; - LOG_INFO("Received waypoint msg from=0x%0x, id=0x%x, msg=%.*s\n", mp.from, mp.id, p.payload.size, p.payload.bytes); + LOG_INFO("Received waypoint msg from=0x%0x, id=0x%x, msg=%.*s", mp.from, mp.id, p.payload.size, p.payload.bytes); #endif // We only store/display messages destined for us. // Keep a copy of the most recent text message. @@ -68,7 +68,7 @@ bool WaypointModule::shouldDraw() } // If decoding failed - LOG_ERROR("Failed to decode waypoint\n"); + LOG_ERROR("Failed to decode waypoint"); devicestate.has_rx_waypoint = false; return false; #else @@ -126,7 +126,7 @@ void WaypointModule::drawFrame(OLEDDisplay *display, OLEDDisplayUiState *state, } // If our node has a position: - if (ourNode && (hasValidPosition(ourNode) || screen->hasHeading())) { + if (ourNode && (nodeDB->hasValidPosition(ourNode) || screen->hasHeading())) { const meshtastic_PositionLite &op = ourNode->position; float myHeading; if (screen->hasHeading()) diff --git a/src/modules/esp32/AudioModule.cpp b/src/modules/esp32/AudioModule.cpp index 89e4b4e25..77cc94359 100644 --- a/src/modules/esp32/AudioModule.cpp +++ b/src/modules/esp32/AudioModule.cpp @@ -46,7 +46,7 @@ void run_codec2(void *parameter) // 4 bytes of header in each frame hex c0 de c2 plus the bitrate memcpy(audioModule->tx_encode_frame, &audioModule->tx_header, sizeof(audioModule->tx_header)); - LOG_INFO("Starting codec2 task\n"); + LOG_INFO("Start codec2 task"); while (true) { uint32_t tcount = ulTaskNotifyTake(pdFALSE, pdMS_TO_TICKS(10000)); @@ -61,7 +61,7 @@ void run_codec2(void *parameter) audioModule->tx_encode_frame_index += audioModule->encode_codec_size; if (audioModule->tx_encode_frame_index == (audioModule->encode_frame_size + sizeof(audioModule->tx_header))) { - LOG_INFO("Sending %d codec2 bytes\n", audioModule->encode_frame_size); + LOG_INFO("Send %d codec2 bytes", audioModule->encode_frame_size); audioModule->sendPayload(); audioModule->tx_encode_frame_index = sizeof(audioModule->tx_header); } @@ -91,7 +91,7 @@ void run_codec2(void *parameter) } } -AudioModule::AudioModule() : SinglePortModule("AudioModule", meshtastic_PortNum_AUDIO_APP), concurrency::OSThread("AudioModule") +AudioModule::AudioModule() : SinglePortModule("Audio", meshtastic_PortNum_AUDIO_APP), concurrency::OSThread("Audio") { // moduleConfig.audio.codec2_enabled = true; // moduleConfig.audio.i2s_ws = 13; @@ -101,8 +101,7 @@ AudioModule::AudioModule() : SinglePortModule("AudioModule", meshtastic_PortNum_ // moduleConfig.audio.ptt_pin = 39; if ((moduleConfig.audio.codec2_enabled) && (myRegion->audioPermitted)) { - LOG_INFO("Setting up codec2 in mode %u", - (moduleConfig.audio.bitrate ? moduleConfig.audio.bitrate : AUDIO_MODULE_MODE) - 1); + LOG_INFO("Set up codec2 in mode %u", (moduleConfig.audio.bitrate ? moduleConfig.audio.bitrate : AUDIO_MODULE_MODE) - 1); codec2 = codec2_create((moduleConfig.audio.bitrate ? moduleConfig.audio.bitrate : AUDIO_MODULE_MODE) - 1); memcpy(tx_header.magic, c2_magic, sizeof(c2_magic)); tx_header.mode = (moduleConfig.audio.bitrate ? moduleConfig.audio.bitrate : AUDIO_MODULE_MODE) - 1; @@ -111,7 +110,7 @@ AudioModule::AudioModule() : SinglePortModule("AudioModule", meshtastic_PortNum_ encode_frame_num = (meshtastic_Constants_DATA_PAYLOAD_LEN - sizeof(tx_header)) / encode_codec_size; encode_frame_size = encode_frame_num * encode_codec_size; // max 233 bytes + 4 header bytes adc_buffer_size = codec2_samples_per_frame(codec2); - LOG_INFO("using %d frames of %d bytes for a total payload length of %d bytes\n", encode_frame_num, encode_codec_size, + LOG_INFO("Use %d frames of %d bytes for a total payload length of %d bytes", encode_frame_num, encode_codec_size, encode_frame_size); xTaskCreate(&run_codec2, "codec2_task", 30000, NULL, 5, &codec2HandlerTask); } else { @@ -148,7 +147,7 @@ int32_t AudioModule::runOnce() esp_err_t res; if (firstTime) { // Set up I2S Processor configuration. This will produce 16bit samples at 8 kHz instead of 12 from the ADC - LOG_INFO("Initializing I2S SD: %d DIN: %d WS: %d SCK: %d\n", moduleConfig.audio.i2s_sd, moduleConfig.audio.i2s_din, + LOG_INFO("Init I2S SD: %d DIN: %d WS: %d SCK: %d", moduleConfig.audio.i2s_sd, moduleConfig.audio.i2s_din, moduleConfig.audio.i2s_ws, moduleConfig.audio.i2s_sck); i2s_config_t i2s_config = {.mode = (i2s_mode_t)(I2S_MODE_MASTER | (moduleConfig.audio.i2s_sd ? I2S_MODE_RX : 0) | (moduleConfig.audio.i2s_din ? I2S_MODE_TX : 0)), @@ -164,7 +163,7 @@ int32_t AudioModule::runOnce() .fixed_mclk = 0}; res = i2s_driver_install(I2S_PORT, &i2s_config, 0, NULL); if (res != ESP_OK) { - LOG_ERROR("Failed to install I2S driver: %d\n", res); + LOG_ERROR("Failed to install I2S driver: %d", res); } const i2s_pin_config_t pin_config = { @@ -174,18 +173,18 @@ int32_t AudioModule::runOnce() .data_in_num = moduleConfig.audio.i2s_sd ? moduleConfig.audio.i2s_sd : I2S_PIN_NO_CHANGE}; res = i2s_set_pin(I2S_PORT, &pin_config); if (res != ESP_OK) { - LOG_ERROR("Failed to set I2S pin config: %d\n", res); + LOG_ERROR("Failed to set I2S pin config: %d", res); } res = i2s_start(I2S_PORT); if (res != ESP_OK) { - LOG_ERROR("Failed to start I2S: %d\n", res); + LOG_ERROR("Failed to start I2S: %d", res); } radio_state = RadioState::rx; // Configure PTT input - LOG_INFO("Initializing PTT on Pin %u\n", moduleConfig.audio.ptt_pin ? moduleConfig.audio.ptt_pin : PTT_PIN); + LOG_INFO("Init PTT on Pin %u", moduleConfig.audio.ptt_pin ? moduleConfig.audio.ptt_pin : PTT_PIN); pinMode(moduleConfig.audio.ptt_pin ? moduleConfig.audio.ptt_pin : PTT_PIN, INPUT); firstTime = false; @@ -194,17 +193,17 @@ int32_t AudioModule::runOnce() // Check if PTT is pressed. TODO hook that into Onebutton/Interrupt drive. if (digitalRead(moduleConfig.audio.ptt_pin ? moduleConfig.audio.ptt_pin : PTT_PIN) == HIGH) { if (radio_state == RadioState::rx) { - LOG_INFO("PTT pressed, switching to TX\n"); + LOG_INFO("PTT pressed, switching to TX"); radio_state = RadioState::tx; e.action = UIFrameEvent::Action::REGENERATE_FRAMESET; // We want to change the list of frames shown on-screen this->notifyObservers(&e); } } else { if (radio_state == RadioState::tx) { - LOG_INFO("PTT released, switching to RX\n"); + LOG_INFO("PTT released, switching to RX"); if (tx_encode_frame_index > sizeof(tx_header)) { // Send the incomplete frame - LOG_INFO("Sending %d codec2 bytes (incomplete)\n", tx_encode_frame_index); + LOG_INFO("Send %d codec2 bytes (incomplete)", tx_encode_frame_index); sendPayload(); } tx_encode_frame_index = sizeof(tx_header); diff --git a/src/modules/esp32/PaxcounterModule.cpp b/src/modules/esp32/PaxcounterModule.cpp index 71810df07..b27586771 100644 --- a/src/modules/esp32/PaxcounterModule.cpp +++ b/src/modules/esp32/PaxcounterModule.cpp @@ -15,14 +15,14 @@ PaxcounterModule *paxcounterModule; void PaxcounterModule::handlePaxCounterReportRequest() { // The libpax library already updated our data structure, just before invoking this callback. - LOG_INFO("PaxcounterModule: libpax reported new data: wifi=%d; ble=%d; uptime=%lu\n", + LOG_INFO("PaxcounterModule: libpax reported new data: wifi=%d; ble=%d; uptime=%lu", paxcounterModule->count_from_libpax.wifi_count, paxcounterModule->count_from_libpax.ble_count, millis() / 1000); paxcounterModule->reportedDataSent = false; paxcounterModule->setIntervalFromNow(0); } PaxcounterModule::PaxcounterModule() - : concurrency::OSThread("PaxcounterModule"), + : concurrency::OSThread("Paxcounter"), ProtobufModule("paxcounter", meshtastic_PortNum_PAXCOUNTER_APP, &meshtastic_Paxcount_msg) { } @@ -39,7 +39,7 @@ bool PaxcounterModule::sendInfo(NodeNum dest) if (paxcounterModule->reportedDataSent) return false; - LOG_INFO("PaxcounterModule: sending pax info wifi=%d; ble=%d; uptime=%lu\n", count_from_libpax.wifi_count, + LOG_INFO("PaxcounterModule: send pax info wifi=%d; ble=%d; uptime=%lu", count_from_libpax.wifi_count, count_from_libpax.ble_count, millis() / 1000); meshtastic_Paxcount pl = meshtastic_Paxcount_init_default; @@ -78,7 +78,7 @@ int32_t PaxcounterModule::runOnce() if (isActive()) { if (firstTime) { firstTime = false; - LOG_DEBUG("Paxcounter starting up with interval of %d seconds\n", + LOG_DEBUG("Paxcounter starting up with interval of %d seconds", Default::getConfiguredOrDefault(moduleConfig.paxcounter.paxcounter_update_interval, default_telemetry_broadcast_interval_secs)); struct libpax_config_t configuration; @@ -95,7 +95,9 @@ int32_t PaxcounterModule::runOnce() // internal processing initialization libpax_counter_init(handlePaxCounterReportRequest, &count_from_libpax, - moduleConfig.paxcounter.paxcounter_update_interval, 0); + Default::getConfiguredOrDefault(moduleConfig.paxcounter.paxcounter_update_interval, + default_telemetry_broadcast_interval_secs), + 0); libpax_counter_start(); } else { sendInfo(NODENUM_BROADCAST); diff --git a/src/motion/AccelerometerThread.h b/src/motion/AccelerometerThread.h index f9cbb49a1..95f09910f 100755 --- a/src/motion/AccelerometerThread.h +++ b/src/motion/AccelerometerThread.h @@ -7,14 +7,21 @@ #if !defined(ARCH_PORTDUINO) && !defined(ARCH_STM32WL) && !MESHTASTIC_EXCLUDE_I2C #include "../concurrency/OSThread.h" +#ifdef HAS_BMA423 #include "BMA423Sensor.h" +#endif #include "BMX160Sensor.h" #include "ICM20948Sensor.h" #include "LIS3DHSensor.h" #include "LSM6DS3Sensor.h" #include "MPU6050Sensor.h" #include "MotionSensor.h" +#ifdef HAS_QMA6100P +#include "QMA6100PSensor.h" +#endif +#ifdef HAS_STK8XXX #include "STK8XXXSensor.h" +#endif extern ScanI2C::DeviceAddress accelerometer_found; @@ -25,7 +32,7 @@ class AccelerometerThread : public concurrency::OSThread bool isInitialised = false; public: - explicit AccelerometerThread(ScanI2C::FoundDevice foundDevice) : OSThread("AccelerometerThread") + explicit AccelerometerThread(ScanI2C::FoundDevice foundDevice) : OSThread("Accelerometer") { device = foundDevice; init(); @@ -62,23 +69,25 @@ class AccelerometerThread : public concurrency::OSThread return; if (device.address.port == ScanI2C::I2CPort::NO_I2C || device.address.address == 0 || device.type == ScanI2C::NONE) { - LOG_DEBUG("AccelerometerThread disabling due to no sensors found\n"); + LOG_DEBUG("AccelerometerThread Disable due to no sensors found"); disable(); return; } #ifndef RAK_4631 if (!config.display.wake_on_tap_or_motion && !config.device.double_tap_as_button_press) { - LOG_DEBUG("AccelerometerThread disabling due to no interested configurations\n"); + LOG_DEBUG("AccelerometerThread Disable due to no interested configurations"); disable(); return; } #endif switch (device.type) { +#ifdef HAS_BMA423 case ScanI2C::DeviceType::BMA423: sensor = new BMA423Sensor(device); break; +#endif case ScanI2C::DeviceType::MPU6050: sensor = new MPU6050Sensor(device); break; @@ -91,12 +100,19 @@ class AccelerometerThread : public concurrency::OSThread case ScanI2C::DeviceType::LSM6DS3: sensor = new LSM6DS3Sensor(device); break; +#ifdef HAS_STK8XXX case ScanI2C::DeviceType::STK8BAXX: sensor = new STK8XXXSensor(device); break; +#endif case ScanI2C::DeviceType::ICM20948: sensor = new ICM20948Sensor(device); break; +#ifdef HAS_QMA6100P + case ScanI2C::DeviceType::QMA6100P: + sensor = new QMA6100PSensor(device); + break; +#endif default: disable(); return; @@ -106,11 +122,11 @@ class AccelerometerThread : public concurrency::OSThread if (!isInitialised) { clean(); } - LOG_DEBUG("AccelerometerThread::init %s\n", isInitialised ? "ok" : "failed"); + LOG_DEBUG("AccelerometerThread::init %s", isInitialised ? "ok" : "failed"); } // Copy constructor (not implemented / included to avoid cppcheck warnings) - AccelerometerThread(const AccelerometerThread &other) : OSThread::OSThread("AccelerometerThread") { this->copy(other); } + AccelerometerThread(const AccelerometerThread &other) : OSThread::OSThread("Accelerometer") { this->copy(other); } // Destructor (included to avoid cppcheck warnings) virtual ~AccelerometerThread() { clean(); } diff --git a/src/motion/BMA423Sensor.cpp b/src/motion/BMA423Sensor.cpp index ec07b7c40..d7058bab0 100755 --- a/src/motion/BMA423Sensor.cpp +++ b/src/motion/BMA423Sensor.cpp @@ -1,6 +1,6 @@ #include "BMA423Sensor.h" -#if !defined(ARCH_PORTDUINO) && !defined(ARCH_STM32WL) && !MESHTASTIC_EXCLUDE_I2C +#if !defined(ARCH_PORTDUINO) && !defined(ARCH_STM32WL) && !MESHTASTIC_EXCLUDE_I2C && defined(HAS_BMA423) using namespace MotionSensorI2C; @@ -41,10 +41,10 @@ bool BMA423Sensor::init() // It corresponds to isDoubleClick interrupt sensor.enableWakeupIRQ(); - LOG_DEBUG("BMA423Sensor::init ok\n"); + LOG_DEBUG("BMA423 init ok"); return true; } - LOG_DEBUG("BMA423Sensor::init failed\n"); + LOG_DEBUG("BMA423 init failed"); return false; } diff --git a/src/motion/BMA423Sensor.h b/src/motion/BMA423Sensor.h index 0bc0ddf8c..455315aa9 100755 --- a/src/motion/BMA423Sensor.h +++ b/src/motion/BMA423Sensor.h @@ -4,7 +4,7 @@ #include "MotionSensor.h" -#if !defined(ARCH_PORTDUINO) && !defined(ARCH_STM32WL) && !MESHTASTIC_EXCLUDE_I2C +#if !defined(ARCH_PORTDUINO) && !defined(ARCH_STM32WL) && !MESHTASTIC_EXCLUDE_I2C && defined(HAS_BMA423) #include #include diff --git a/src/motion/BMX160Sensor.cpp b/src/motion/BMX160Sensor.cpp index 3f68a4a25..6562a651c 100755 --- a/src/motion/BMX160Sensor.cpp +++ b/src/motion/BMX160Sensor.cpp @@ -16,10 +16,10 @@ bool BMX160Sensor::init() if (sensor.begin()) { // set output data rate sensor.ODR_Config(BMX160_ACCEL_ODR_100HZ, BMX160_GYRO_ODR_100HZ); - LOG_DEBUG("BMX160Sensor::init ok\n"); + LOG_DEBUG("BMX160 init ok"); return true; } - LOG_DEBUG("BMX160Sensor::init failed\n"); + LOG_DEBUG("BMX160 init failed"); return false; } diff --git a/src/motion/ICM20948Sensor.cpp b/src/motion/ICM20948Sensor.cpp index d16968e06..338a4fc5f 100755 --- a/src/motion/ICM20948Sensor.cpp +++ b/src/motion/ICM20948Sensor.cpp @@ -44,14 +44,14 @@ int32_t ICM20948Sensor::runOnce() // Wake on motion using polling - this is not as efficient as using hardware interrupt pin (see above) auto status = sensor->setBank(0); if (sensor->status != ICM_20948_Stat_Ok) { - LOG_DEBUG("ICM20948Sensor::isWakeOnMotion failed to set bank - %s\n", sensor->statusString()); + LOG_DEBUG("ICM20948 isWakeOnMotion failed to set bank - %s", sensor->statusString()); return MOTION_SENSOR_CHECK_INTERVAL_MS; } ICM_20948_INT_STATUS_t int_stat; status = sensor->read(AGB0_REG_INT_STATUS, (uint8_t *)&int_stat, sizeof(ICM_20948_INT_STATUS_t)); if (status != ICM_20948_Stat_Ok) { - LOG_DEBUG("ICM20948Sensor::isWakeOnMotion failed to read interrupts - %s\n", sensor->statusString()); + LOG_DEBUG("ICM20948 isWakeOnMotion failed to read interrupts - %s", sensor->statusString()); return MOTION_SENSOR_CHECK_INTERVAL_MS; } @@ -99,25 +99,25 @@ bool ICM20948Singleton::init(ScanI2C::FoundDevice device) ICM_20948_Status_e status = begin(Wire, device.address.address == ICM20948_ADDR ? 1 : 0); #endif if (status != ICM_20948_Stat_Ok) { - LOG_DEBUG("ICM20948Sensor::init begin - %s\n", statusString()); + LOG_DEBUG("ICM20948 init begin - %s", statusString()); return false; } // SW reset to make sure the device starts in a known state if (swReset() != ICM_20948_Stat_Ok) { - LOG_DEBUG("ICM20948Sensor::init reset - %s\n", statusString()); + LOG_DEBUG("ICM20948 init reset - %s", statusString()); return false; } delay(200); // Now wake the sensor up if (sleep(false) != ICM_20948_Stat_Ok) { - LOG_DEBUG("ICM20948Sensor::init wake - %s\n", statusString()); + LOG_DEBUG("ICM20948 init wake - %s", statusString()); return false; } if (lowPower(false) != ICM_20948_Stat_Ok) { - LOG_DEBUG("ICM20948Sensor::init high power - %s\n", statusString()); + LOG_DEBUG("ICM20948 init high power - %s", statusString()); return false; } @@ -125,19 +125,19 @@ bool ICM20948Singleton::init(ScanI2C::FoundDevice device) // Active low cfgIntActiveLow(true); - LOG_DEBUG("ICM20948Sensor::init set cfgIntActiveLow - %s\n", statusString()); + LOG_DEBUG("ICM20948 init set cfgIntActiveLow - %s", statusString()); // Push-pull cfgIntOpenDrain(false); - LOG_DEBUG("ICM20948Sensor::init set cfgIntOpenDrain - %s\n", statusString()); + LOG_DEBUG("ICM20948 init set cfgIntOpenDrain - %s", statusString()); // If enabled, *ANY* read will clear the INT_STATUS register. cfgIntAnyReadToClear(true); - LOG_DEBUG("ICM20948Sensor::init set cfgIntAnyReadToClear - %s\n", statusString()); + LOG_DEBUG("ICM20948 init set cfgIntAnyReadToClear - %s", statusString()); // Latch the interrupt until cleared cfgIntLatch(true); - LOG_DEBUG("ICM20948Sensor::init set cfgIntLatch - %s\n", statusString()); + LOG_DEBUG("ICM20948 init set cfgIntLatch - %s", statusString()); // Set up an interrupt pin with an internal pullup for active low pinMode(ICM_20948_INT_PIN, INPUT_PULLUP); @@ -168,13 +168,13 @@ bool ICM20948Singleton::setWakeOnMotion() // Enable WoM Logic mode 1 = Compare the current sample with the previous sample status = WOMLogic(true, 1); - LOG_DEBUG("ICM20948Sensor::init set WOMLogic - %s\n", statusString()); + LOG_DEBUG("ICM20948 init set WOMLogic - %s", statusString()); if (status != ICM_20948_Stat_Ok) return false; // Enable interrupts on WakeOnMotion status = intEnableWOM(true); - LOG_DEBUG("ICM20948Sensor::init set intEnableWOM - %s\n", statusString()); + LOG_DEBUG("ICM20948 init set intEnableWOM - %s", statusString()); return status == ICM_20948_Stat_Ok; // Clear any current interrupts diff --git a/src/motion/LIS3DHSensor.cpp b/src/motion/LIS3DHSensor.cpp index e2df60b1c..f3f5a62d1 100755 --- a/src/motion/LIS3DHSensor.cpp +++ b/src/motion/LIS3DHSensor.cpp @@ -1,4 +1,5 @@ #include "LIS3DHSensor.h" +#include "NodeDB.h" #if !defined(ARCH_PORTDUINO) && !defined(ARCH_STM32WL) && !MESHTASTIC_EXCLUDE_I2C @@ -10,10 +11,10 @@ bool LIS3DHSensor::init() sensor.setRange(LIS3DH_RANGE_2_G); // Adjust threshold, higher numbers are less sensitive sensor.setClick(config.device.double_tap_as_button_press ? 2 : 1, MOTION_SENSOR_CHECK_INTERVAL_MS); - LOG_DEBUG("LIS3DHSensor::init ok\n"); + LOG_DEBUG("LIS3DH init ok"); return true; } - LOG_DEBUG("LIS3DHSensor::init failed\n"); + LOG_DEBUG("LIS3DH init failed"); return false; } diff --git a/src/motion/LSM6DS3Sensor.cpp b/src/motion/LSM6DS3Sensor.cpp index 64ef9a23b..2dcb4d663 100755 --- a/src/motion/LSM6DS3Sensor.cpp +++ b/src/motion/LSM6DS3Sensor.cpp @@ -1,4 +1,5 @@ #include "LSM6DS3Sensor.h" +#include "NodeDB.h" #if !defined(ARCH_PORTDUINO) && !defined(ARCH_STM32WL) && !MESHTASTIC_EXCLUDE_I2C @@ -11,13 +12,13 @@ bool LSM6DS3Sensor::init() // Default threshold of 2G, less sensitive options are 4, 8 or 16G sensor.setAccelRange(LSM6DS_ACCEL_RANGE_2_G); - // Duration is number of occurances needed to trigger, higher threshold is less sensitive + // Duration is number of occurrences needed to trigger, higher threshold is less sensitive sensor.enableWakeup(config.display.wake_on_tap_or_motion, 1, LSM6DS3_WAKE_THRESH); - LOG_DEBUG("LSM6DS3Sensor::init ok\n"); + LOG_DEBUG("LSM6DS3 init ok"); return true; } - LOG_DEBUG("LSM6DS3Sensor::init failed\n"); + LOG_DEBUG("LSM6DS3 init failed"); return false; } diff --git a/src/motion/MPU6050Sensor.cpp b/src/motion/MPU6050Sensor.cpp index 77aaca46d..c3f2d0b7c 100755 --- a/src/motion/MPU6050Sensor.cpp +++ b/src/motion/MPU6050Sensor.cpp @@ -13,10 +13,10 @@ bool MPU6050Sensor::init() sensor.setMotionDetectionDuration(20); sensor.setInterruptPinLatch(true); // Keep it latched. Will turn off when reinitialized. sensor.setInterruptPinPolarity(true); - LOG_DEBUG("MPU6050Sensor::init ok\n"); + LOG_DEBUG("MPU6050 init ok"); return true; } - LOG_DEBUG("MPU6050Sensor::init failed\n"); + LOG_DEBUG("MPU6050 init failed"); return false; } diff --git a/src/motion/MotionSensor.cpp b/src/motion/MotionSensor.cpp index f1c0ba85e..242e3709f 100755 --- a/src/motion/MotionSensor.cpp +++ b/src/motion/MotionSensor.cpp @@ -10,8 +10,8 @@ MotionSensor::MotionSensor(ScanI2C::FoundDevice foundDevice) device.address.address = foundDevice.address.address; device.address.port = foundDevice.address.port; device.type = foundDevice.type; - LOG_DEBUG("MotionSensor::MotionSensor port: %s address: 0x%x type: %d\n", - devicePort() == ScanI2C::I2CPort::WIRE1 ? "Wire1" : "Wire", (uint8_t)deviceAddress(), deviceType()); + LOG_DEBUG("Motion MotionSensor port: %s address: 0x%x type: %d", devicePort() == ScanI2C::I2CPort::WIRE1 ? "Wire1" : "Wire", + (uint8_t)deviceAddress(), deviceType()); } ScanI2C::DeviceType MotionSensor::deviceType() @@ -57,14 +57,14 @@ void MotionSensor::drawFrameCalibration(OLEDDisplay *display, OLEDDisplayUiState void MotionSensor::wakeScreen() { if (powerFSM.getState() == &stateDARK) { - LOG_DEBUG("MotionSensor::wakeScreen detected\n"); + LOG_DEBUG("Motion wakeScreen detected"); powerFSM.trigger(EVENT_INPUT); } } void MotionSensor::buttonPress() { - LOG_DEBUG("MotionSensor::buttonPress detected\n"); + LOG_DEBUG("Motion buttonPress detected"); powerFSM.trigger(EVENT_PRESS); } diff --git a/src/motion/MotionSensor.h b/src/motion/MotionSensor.h index 4da23cc70..78eec54ce 100755 --- a/src/motion/MotionSensor.h +++ b/src/motion/MotionSensor.h @@ -14,6 +14,7 @@ #include "../graphics/Screen.h" #include "../graphics/ScreenFonts.h" #include "../power.h" +#include "Wire.h" // Base class for motion processing class MotionSensor diff --git a/src/motion/QMA6100PSensor.cpp b/src/motion/QMA6100PSensor.cpp new file mode 100644 index 000000000..4c5bc14d2 --- /dev/null +++ b/src/motion/QMA6100PSensor.cpp @@ -0,0 +1,183 @@ +#include "QMA6100PSensor.h" + +#if !defined(ARCH_PORTDUINO) && !defined(ARCH_STM32WL) && !MESHTASTIC_EXCLUDE_I2C && defined(HAS_QMA6100P) + +// Flag when an interrupt has been detected +volatile static bool QMA6100P_IRQ = false; + +// Interrupt service routine +void QMA6100PSetInterrupt() +{ + QMA6100P_IRQ = true; +} + +QMA6100PSensor::QMA6100PSensor(ScanI2C::FoundDevice foundDevice) : MotionSensor::MotionSensor(foundDevice) {} + +bool QMA6100PSensor::init() +{ + // Initialise the sensor + sensor = QMA6100PSingleton::GetInstance(); + if (!sensor->init(device)) + return false; + + // Enable simple Wake on Motion + return sensor->setWakeOnMotion(); +} + +#ifdef QMA_6100P_INT_PIN + +int32_t QMA6100PSensor::runOnce() +{ + // Wake on motion using hardware interrupts - this is the most efficient way to check for motion + if (QMA6100P_IRQ) { + QMA6100P_IRQ = false; + wakeScreen(); + } + return MOTION_SENSOR_CHECK_INTERVAL_MS; +} + +#else + +int32_t QMA6100PSensor::runOnce() +{ + // Wake on motion using polling - this is not as efficient as using hardware interrupt pin (see above) + + uint8_t tempVal; + if (!sensor->readRegisterRegion(SFE_QMA6100P_INT_ST0, &tempVal, 1)) { + LOG_DEBUG("QMA6100PS isWakeOnMotion failed to read interrupts"); + return MOTION_SENSOR_CHECK_INTERVAL_MS; + } + + if ((tempVal & 7) != 0) { + // Wake up! + wakeScreen(); + } + return MOTION_SENSOR_CHECK_INTERVAL_MS; +} + +#endif + +// ---------------------------------------------------------------------- +// QMA6100PSingleton +// ---------------------------------------------------------------------- + +// Get a singleton wrapper for an Sparkfun QMA_6100P_I2C +QMA6100PSingleton *QMA6100PSingleton::GetInstance() +{ + if (pinstance == nullptr) { + pinstance = new QMA6100PSingleton(); + } + return pinstance; +} + +QMA6100PSingleton::QMA6100PSingleton() {} + +QMA6100PSingleton::~QMA6100PSingleton() {} + +QMA6100PSingleton *QMA6100PSingleton::pinstance{nullptr}; + +// Initialise the QMA6100P Sensor +bool QMA6100PSingleton::init(ScanI2C::FoundDevice device) +{ + +// startup +#ifdef Wire1 + bool status = begin(device.address.address, device.address.port == ScanI2C::I2CPort::WIRE1 ? &Wire1 : &Wire); +#else + // check chip id + bool status = begin(device.address.address, &Wire); +#endif + if (status != true) { + LOG_WARN("QMA6100P init begin failed\n"); + return false; + } + delay(20); + // SW reset to make sure the device starts in a known state + if (softwareReset() != true) { + LOG_WARN("QMA6100P init reset failed\n"); + return false; + } + delay(20); + // Set range + if (!setRange(QMA_6100P_MPU_ACCEL_SCALE)) { + LOG_WARN("QMA6100P init range failed"); + return false; + } + // set active mode + if (!enableAccel()) { + LOG_WARN("ERROR QMA6100P active mode set failed"); + } + // set calibrateoffsets + if (!calibrateOffsets()) { + LOG_WARN("ERROR QMA6100P calibration failed"); + } +#ifdef QMA_6100P_INT_PIN + + // Active low & Open Drain + uint8_t tempVal; + if (!readRegisterRegion(SFE_QMA6100P_INTPINT_CONF, &tempVal, 1)) { + LOG_WARN("QMA6100P init failed to read interrupt pin config"); + return false; + } + + tempVal |= 0b00000010; // Active low & Open Drain + + if (!writeRegisterByte(SFE_QMA6100P_INTPINT_CONF, tempVal)) { + LOG_WARN("QMA6100P init failed to write interrupt pin config"); + return false; + } + + // Latch until cleared, all reads clear the latch + if (!readRegisterRegion(SFE_QMA6100P_INT_CFG, &tempVal, 1)) { + LOG_WARN("QMA6100P init failed to read interrupt config"); + return false; + } + + tempVal |= 0b10000001; // Latch until cleared, INT_RD_CLR1 + + if (!writeRegisterByte(SFE_QMA6100P_INT_CFG, tempVal)) { + LOG_WARN("QMA6100P init failed to write interrupt config"); + return false; + } + // Set up an interrupt pin with an internal pullup for active low + pinMode(QMA_6100P_INT_PIN, INPUT_PULLUP); + + // Set up an interrupt service routine + attachInterrupt(QMA_6100P_INT_PIN, QMA6100PSetInterrupt, FALLING); + +#endif + return true; +} + +bool QMA6100PSingleton::setWakeOnMotion() +{ + // Enable 'Any Motion' interrupt + if (!writeRegisterByte(SFE_QMA6100P_INT_EN2, 0b00000111)) { + LOG_WARN("QMA6100P :setWakeOnMotion failed to write interrupt enable"); + return false; + } + + // Set 'Significant Motion' interrupt map to INT1 + uint8_t tempVal; + + if (!readRegisterRegion(SFE_QMA6100P_INT_MAP1, &tempVal, 1)) { + LOG_WARN("QMA6100P setWakeOnMotion failed to read interrupt map"); + return false; + } + + sfe_qma6100p_int_map1_bitfield_t int_map1; + int_map1.all = tempVal; + int_map1.bits.int1_any_mot = 1; // any motion interrupt to INT1 + tempVal = int_map1.all; + + if (!writeRegisterByte(SFE_QMA6100P_INT_MAP1, tempVal)) { + LOG_WARN("QMA6100P setWakeOnMotion failed to write interrupt map"); + return false; + } + + // Clear any current interrupts + QMA6100P_IRQ = false; + return true; +} + +#endif \ No newline at end of file diff --git a/src/motion/QMA6100PSensor.h b/src/motion/QMA6100PSensor.h new file mode 100644 index 000000000..7ba00149c --- /dev/null +++ b/src/motion/QMA6100PSensor.h @@ -0,0 +1,63 @@ +#pragma once +#ifndef _QMA_6100P_SENSOR_H_ +#define _QMA_6100P_SENSOR_H_ + +#include "MotionSensor.h" + +#if !defined(ARCH_PORTDUINO) && !defined(ARCH_STM32WL) && !MESHTASTIC_EXCLUDE_I2C && defined(HAS_QMA6100P) + +#include + +// Set the default accelerometer scale - gpm2, gpm4, gpm8, gpm16 +#ifndef QMA_6100P_MPU_ACCEL_SCALE +#define QMA_6100P_MPU_ACCEL_SCALE SFE_QMA6100P_RANGE32G +#endif + +// The I2C address of the Accelerometer (if found) from main.cpp +extern ScanI2C::DeviceAddress accelerometer_found; + +// Singleton wrapper for the Sparkfun QMA_6100P_I2C class +class QMA6100PSingleton : public QMA6100P +{ + private: + static QMA6100PSingleton *pinstance; + + protected: + QMA6100PSingleton(); + ~QMA6100PSingleton(); + + public: + // Create a singleton instance (not thread safe) + static QMA6100PSingleton *GetInstance(); + + // Singletons should not be cloneable. + QMA6100PSingleton(QMA6100PSingleton &other) = delete; + + // Singletons should not be assignable. + void operator=(const QMA6100PSingleton &) = delete; + + // Initialise the motion sensor singleton for normal operation + bool init(ScanI2C::FoundDevice device); + + // Enable Wake on Motion interrupts (sensor must be initialised first) + bool setWakeOnMotion(); +}; + +class QMA6100PSensor : public MotionSensor +{ + private: + QMA6100PSingleton *sensor = nullptr; + + public: + explicit QMA6100PSensor(ScanI2C::FoundDevice foundDevice); + + // Initialise the motion sensor + virtual bool init() override; + + // Called each time our sensor gets a chance to run + virtual int32_t runOnce() override; +}; + +#endif + +#endif \ No newline at end of file diff --git a/src/motion/STK8XXXSensor.cpp b/src/motion/STK8XXXSensor.cpp index d4d69ef99..377ee3c37 100755 --- a/src/motion/STK8XXXSensor.cpp +++ b/src/motion/STK8XXXSensor.cpp @@ -1,6 +1,6 @@ #include "STK8XXXSensor.h" -#if !defined(ARCH_PORTDUINO) && !defined(ARCH_STM32WL) && !MESHTASTIC_EXCLUDE_I2C +#if !defined(ARCH_PORTDUINO) && !defined(ARCH_STM32WL) && !MESHTASTIC_EXCLUDE_I2C && defined(HAS_STK8XXX) STK8XXXSensor::STK8XXXSensor(ScanI2C::FoundDevice foundDevice) : MotionSensor::MotionSensor(foundDevice) {} @@ -17,10 +17,10 @@ bool STK8XXXSensor::init() attachInterrupt( digitalPinToInterrupt(STK8XXX_INT), [] { STK_IRQ = true; }, RISING); - LOG_DEBUG("STK8XXXSensor::init ok\n"); + LOG_DEBUG("STK8XXX init ok"); return true; } - LOG_DEBUG("STK8XXXSensor::init failed\n"); + LOG_DEBUG("STK8XXX init failed"); return false; } diff --git a/src/motion/STK8XXXSensor.h b/src/motion/STK8XXXSensor.h index 190b916b4..cff98d87d 100755 --- a/src/motion/STK8XXXSensor.h +++ b/src/motion/STK8XXXSensor.h @@ -4,7 +4,7 @@ #include "MotionSensor.h" -#if !defined(ARCH_PORTDUINO) && !defined(ARCH_STM32WL) && !MESHTASTIC_EXCLUDE_I2C +#if !defined(ARCH_PORTDUINO) && !defined(ARCH_STM32WL) && !MESHTASTIC_EXCLUDE_I2C && defined(HAS_STK8XXX) #ifdef STK8XXX_INT diff --git a/src/mqtt/MQTT.cpp b/src/mqtt/MQTT.cpp index dd00a336f..967db04d6 100644 --- a/src/mqtt/MQTT.cpp +++ b/src/mqtt/MQTT.cpp @@ -35,6 +35,8 @@ Allocator &mqttPool = staticMqttPool; // FIXME - this size calculation is super sloppy, but it will go away once we dynamically alloc meshpackets static uint8_t bytes[meshtastic_MqttClientProxyMessage_size + 30]; // 12 for channel name and 16 for nodeid +static bool isMqttServerAddressPrivate = false; + void MQTT::mqttCallback(char *topic, byte *payload, unsigned int length) { mqtt->onReceive(topic, payload, length); @@ -72,7 +74,7 @@ void MQTT::onReceive(char *topic, byte *payload, size_t length) // this is a valid envelope if (json["type"]->AsString().compare("sendtext") == 0 && json["payload"]->IsString()) { std::string jsonPayloadStr = json["payload"]->AsString(); - LOG_INFO("JSON payload %s, length %u\n", jsonPayloadStr.c_str(), jsonPayloadStr.length()); + LOG_INFO("JSON payload %s, length %u", jsonPayloadStr.c_str(), jsonPayloadStr.length()); // construct protobuf data packet using TEXT_MESSAGE, send it to the mesh meshtastic_MeshPacket *p = router->allocForSending(); @@ -89,7 +91,7 @@ void MQTT::onReceive(char *topic, byte *payload, size_t length) p->decoded.payload.size = jsonPayloadStr.length(); service->sendToMesh(p, RX_SRC_LOCAL); } else { - LOG_WARN("Received MQTT json payload too long, dropping\n"); + LOG_WARN("Received MQTT json payload too long, drop"); } } else if (json["type"]->AsString().compare("sendposition") == 0 && json["payload"]->IsObject()) { // invent the "sendposition" type for a valid envelope @@ -120,29 +122,29 @@ void MQTT::onReceive(char *topic, byte *payload, size_t length) &meshtastic_Position_msg, &pos); // make the Data protobuf from position service->sendToMesh(p, RX_SRC_LOCAL); } else { - LOG_DEBUG("JSON Ignoring downlink message with unsupported type.\n"); + LOG_DEBUG("JSON ignore downlink message with unsupported type"); } } else { - LOG_ERROR("JSON Received payload on MQTT but not a valid envelope.\n"); + LOG_ERROR("JSON received payload on MQTT but not a valid envelope"); } } else { - LOG_WARN("JSON downlink received on channel not called 'mqtt' or without downlink enabled.\n"); + LOG_WARN("JSON downlink received on channel not called 'mqtt' or without downlink enabled"); } } else { // no json, this is an invalid payload - LOG_ERROR("JSON Received payload on MQTT but not a valid JSON\n"); + LOG_ERROR("JSON received payload on MQTT but not a valid JSON"); } delete json_value; } else { if (length == 0) { - LOG_WARN("Empty MQTT payload received, topic %s!\n", topic); + LOG_WARN("Empty MQTT payload received, topic %s!", topic); return; } else if (!pb_decode_from_bytes(payload, length, &meshtastic_ServiceEnvelope_msg, &e)) { - LOG_ERROR("Invalid MQTT service envelope, topic %s, len %u!\n", topic, length); + LOG_ERROR("Invalid MQTT service envelope, topic %s, len %u!", topic, length); return; } else { if (e.channel_id == NULL || e.gateway_id == NULL) { - LOG_ERROR("Invalid MQTT service envelope, topic %s, len %u!\n", topic, length); + LOG_ERROR("Invalid MQTT service envelope, topic %s, len %u!", topic, length); return; } meshtastic_Channel ch = channels.getByName(e.channel_id); @@ -153,29 +155,38 @@ void MQTT::onReceive(char *topic, byte *payload, size_t length) if (e.packet && isFromUs(e.packet)) routingModule->sendAckNak(meshtastic_Routing_Error_NONE, getFrom(e.packet), e.packet->id, ch.index); else - LOG_INFO("Ignoring downlink message we originally sent.\n"); + LOG_INFO("Ignore downlink message we originally sent"); } else { // Find channel by channel_id and check downlink_enabled if ((strcmp(e.channel_id, "PKI") == 0 && e.packet) || (strcmp(e.channel_id, channels.getGlobalId(ch.index)) == 0 && e.packet && ch.settings.downlink_enabled)) { - LOG_INFO("Received MQTT topic %s, len=%u\n", topic, length); + LOG_INFO("Received MQTT topic %s, len=%u", topic, length); meshtastic_MeshPacket *p = packetPool.allocCopy(*e.packet); p->via_mqtt = true; // Mark that the packet was received via MQTT if (isFromUs(p)) { - LOG_INFO("Ignoring downlink message we originally sent.\n"); + LOG_INFO("Ignore downlink message we originally sent"); packetPool.release(p); + free(e.channel_id); + free(e.gateway_id); + free(e.packet); return; } if (p->which_payload_variant == meshtastic_MeshPacket_decoded_tag) { if (moduleConfig.mqtt.encryption_enabled) { - LOG_INFO("Ignoring decoded message on MQTT, encryption is enabled.\n"); + LOG_INFO("Ignore decoded message on MQTT, encryption is enabled"); packetPool.release(p); + free(e.channel_id); + free(e.gateway_id); + free(e.packet); return; } if (p->decoded.portnum == meshtastic_PortNum_ADMIN_APP) { - LOG_INFO("Ignoring decoded admin packet.\n"); + LOG_INFO("Ignore decoded admin packet"); packetPool.release(p); + free(e.channel_id); + free(e.gateway_id); + free(e.packet); return; } p->channel = ch.index; @@ -216,7 +227,7 @@ MQTT::MQTT() : concurrency::OSThread("mqtt"), mqttQueue(MAX_MQTT_QUEUE) #endif { if (moduleConfig.mqtt.enabled) { - LOG_DEBUG("Initializing MQTT\n"); + LOG_DEBUG("Init MQTT"); assert(!mqtt); mqtt = this; @@ -238,13 +249,18 @@ MQTT::MQTT() : concurrency::OSThread("mqtt"), mqttQueue(MAX_MQTT_QUEUE) moduleConfig.mqtt.map_report_settings.publish_interval_secs, default_map_publish_interval_secs); } + isMqttServerAddressPrivate = isPrivateIpAddress(moduleConfig.mqtt.address); + if (isMqttServerAddressPrivate) { + LOG_INFO("MQTT server on a private IP"); + } + #if HAS_NETWORKING if (!moduleConfig.mqtt.proxy_to_client_enabled) pubSub.setCallback(mqttCallback); #endif if (moduleConfig.mqtt.proxy_to_client_enabled) { - LOG_INFO("MQTT configured to use client proxy...\n"); + LOG_INFO("MQTT configured to use client proxy"); enabled = true; runASAP = true; reconnectCount = 0; @@ -308,7 +324,7 @@ void MQTT::reconnect() { if (wantsLink()) { if (moduleConfig.mqtt.proxy_to_client_enabled) { - LOG_INFO("MQTT connecting via client proxy instead...\n"); + LOG_INFO("MQTT connect via client proxy instead"); enabled = true; runASAP = true; reconnectCount = 0; @@ -329,7 +345,7 @@ void MQTT::reconnect() mqttPassword = moduleConfig.mqtt.password; } #if HAS_WIFI && !defined(ARCH_PORTDUINO) -#if !defined(CONFIG_IDF_TARGET_ESP32C6) && !defined(RPI_PICO) +#if !defined(CONFIG_IDF_TARGET_ESP32C6) if (moduleConfig.mqtt.tls_enabled) { // change default for encrypted to 8883 try { @@ -337,12 +353,12 @@ void MQTT::reconnect() wifiSecureClient.setInsecure(); pubSub.setClient(wifiSecureClient); - LOG_INFO("Using TLS-encrypted session\n"); + LOG_INFO("Use TLS-encrypted session"); } catch (const std::exception &e) { - LOG_ERROR("MQTT ERROR: %s\n", e.what()); + LOG_ERROR("MQTT ERROR: %s", e.what()); } } else { - LOG_INFO("Using non-TLS-encrypted session\n"); + LOG_INFO("Use non-TLS-encrypted session"); pubSub.setClient(mqttClient); } #else @@ -363,12 +379,12 @@ void MQTT::reconnect() pubSub.setServer(serverAddr, serverPort); pubSub.setBufferSize(512); - LOG_INFO("Attempting to connect directly to MQTT server %s, port: %d, username: %s, password: %s\n", serverAddr, - serverPort, mqttUsername, mqttPassword); + LOG_INFO("Connect directly to MQTT server %s, port: %d, username: %s, password: %s", serverAddr, serverPort, mqttUsername, + mqttPassword); bool connected = pubSub.connect(owner.id, mqttUsername, mqttPassword); if (connected) { - LOG_INFO("MQTT connected\n"); + LOG_INFO("MQTT connected"); enabled = true; // Start running background process again runASAP = true; reconnectCount = 0; @@ -378,7 +394,7 @@ void MQTT::reconnect() } else { #if HAS_WIFI && !defined(ARCH_PORTDUINO) reconnectCount++; - LOG_ERROR("Failed to contact MQTT server directly (%d/%d)...\n", reconnectCount, reconnectMax); + LOG_ERROR("Failed to contact MQTT server directly (%d/%d)", reconnectCount, reconnectMax); if (reconnectCount >= reconnectMax) { needReconnect = true; wifiReconnect->setIntervalFromNow(0); @@ -400,13 +416,13 @@ void MQTT::sendSubscriptions() if (ch.settings.downlink_enabled) { hasDownlink = true; std::string topic = cryptTopic + channels.getGlobalId(i) + "/+"; - LOG_INFO("Subscribing to %s\n", topic.c_str()); + LOG_INFO("Subscribe to %s", topic.c_str()); pubSub.subscribe(topic.c_str(), 1); // FIXME, is QOS 1 right? #if !defined(ARCH_NRF52) || \ defined(NRF52_USE_JSON) // JSON is not supported on nRF52, see issue #2804 ### Fixed by using ArduinoJSON ### if (moduleConfig.mqtt.json_enabled == true) { std::string topicDecoded = jsonTopic + channels.getGlobalId(i) + "/+"; - LOG_INFO("Subscribing to %s\n", topicDecoded.c_str()); + LOG_INFO("Subscribe to %s", topicDecoded.c_str()); pubSub.subscribe(topicDecoded.c_str(), 1); // FIXME, is QOS 1 right? } #endif // ARCH_NRF52 NRF52_USE_JSON @@ -415,7 +431,7 @@ void MQTT::sendSubscriptions() #if !MESHTASTIC_EXCLUDE_PKI if (hasDownlink) { std::string topic = cryptTopic + "PKI/+"; - LOG_INFO("Subscribing to %s\n", topic.c_str()); + LOG_INFO("Subscribe to %s", topic.c_str()); pubSub.subscribe(topic.c_str(), 1); } #endif @@ -471,7 +487,7 @@ int32_t MQTT::runOnce() } else { // we are connected to server, check often for new requests on the TCP port if (!wantConnection) { - LOG_INFO("MQTT link not needed, dropping\n"); + LOG_INFO("MQTT link not needed, drop"); pubSub.disconnect(); } @@ -489,7 +505,7 @@ void MQTT::publishNodeInfo() void MQTT::publishQueuedMessages() { if (!mqttQueue.isEmpty()) { - LOG_DEBUG("Publishing enqueued MQTT message\n"); + LOG_DEBUG("Publish enqueued MQTT message"); meshtastic_ServiceEnvelope *env = mqttQueue.dequeuePtr(0); size_t numBytes = pb_encode_to_bytes(bytes, sizeof(bytes), &meshtastic_ServiceEnvelope_msg, env); std::string topic; @@ -498,7 +514,7 @@ void MQTT::publishQueuedMessages() } else { topic = cryptTopic + env->channel_id + "/" + owner.id; } - LOG_INFO("publish %s, %u bytes from queue\n", topic.c_str(), numBytes); + LOG_INFO("publish %s, %u bytes from queue", topic.c_str(), numBytes); publish(topic.c_str(), bytes, numBytes, false); @@ -514,8 +530,7 @@ void MQTT::publishQueuedMessages() } else { topicJson = jsonTopic + env->channel_id + "/" + owner.id; } - LOG_INFO("JSON publish message to %s, %u bytes: %s\n", topicJson.c_str(), jsonString.length(), - jsonString.c_str()); + LOG_INFO("JSON publish message to %s, %u bytes: %s", topicJson.c_str(), jsonString.length(), jsonString.c_str()); publish(topicJson.c_str(), jsonString.c_str(), false); } } @@ -539,19 +554,21 @@ void MQTT::onSend(const meshtastic_MeshPacket &mp_encrypted, const meshtastic_Me // mp_decoded will not be decoded when it's PKI encrypted and not directed to us if (mp_decoded.which_payload_variant == meshtastic_MeshPacket_decoded_tag) { + // For uplinking other's packets, check if it's not OK to MQTT or if it's an older packet without the bitfield + bool dontUplink = !mp_decoded.decoded.has_bitfield || + (mp_decoded.decoded.has_bitfield && !(mp_decoded.decoded.bitfield & BITFIELD_OK_TO_MQTT_MASK)); // check for the lowest bit of the data bitfield set false, and the use of one of the default keys. - if (!isFromUs(&mp_decoded) && strcmp(moduleConfig.mqtt.address, "127.0.0.1") != 0 && mp_decoded.decoded.has_bitfield && - !(mp_decoded.decoded.bitfield & BITFIELD_OK_TO_MQTT_MASK) && + if (!isFromUs(&mp_decoded) && !isMqttServerAddressPrivate && dontUplink && (ch.settings.psk.size < 2 || (ch.settings.psk.size == 16 && memcmp(ch.settings.psk.bytes, defaultpsk, 16)) || (ch.settings.psk.size == 32 && memcmp(ch.settings.psk.bytes, eventpsk, 32)))) { - LOG_INFO("MQTT onSend - Not forwarding packet due to DontMqttMeBro flag\n"); + LOG_INFO("MQTT onSend - Not forwarding packet due to DontMqttMeBro flag"); return; } if (strcmp(moduleConfig.mqtt.address, default_mqtt_address) == 0 && (mp_decoded.decoded.portnum == meshtastic_PortNum_RANGE_TEST_APP || mp_decoded.decoded.portnum == meshtastic_PortNum_DETECTION_SENSOR_APP)) { - LOG_DEBUG("MQTT onSend - Ignoring range test or detection sensor message on public mqtt\n"); + LOG_DEBUG("MQTT onSend - Ignoring range test or detection sensor message on public mqtt"); return; } } @@ -565,22 +582,23 @@ void MQTT::onSend(const meshtastic_MeshPacket &mp_encrypted, const meshtastic_Me env->channel_id = (char *)channelId; env->gateway_id = owner.id; - LOG_DEBUG("MQTT onSend - Publishing "); + LOG_DEBUG("MQTT onSend - Publish "); if (moduleConfig.mqtt.encryption_enabled) { env->packet = (meshtastic_MeshPacket *)&mp_encrypted; - LOG_DEBUG("encrypted message\n"); + LOG_DEBUG("encrypted message"); } else if (mp_decoded.which_payload_variant == meshtastic_MeshPacket_decoded_tag) { env->packet = (meshtastic_MeshPacket *)&mp_decoded; - LOG_DEBUG("portnum %i message\n", env->packet->decoded.portnum); + LOG_DEBUG("portnum %i message", env->packet->decoded.portnum); } else { - LOG_DEBUG("nothing, pkt not decrypted\n"); + LOG_DEBUG("nothing, pkt not decrypted"); + mqttPool.release(env); return; // Don't upload a still-encrypted PKI packet if not encryption_enabled } if (moduleConfig.mqtt.proxy_to_client_enabled || this->isConnectedDirectly()) { size_t numBytes = pb_encode_to_bytes(bytes, sizeof(bytes), &meshtastic_ServiceEnvelope_msg, env); std::string topic = cryptTopic + channelId + "/" + owner.id; - LOG_DEBUG("MQTT Publish %s, %u bytes\n", topic.c_str(), numBytes); + LOG_DEBUG("MQTT Publish %s, %u bytes", topic.c_str(), numBytes); publish(topic.c_str(), bytes, numBytes, false); @@ -591,16 +609,16 @@ void MQTT::onSend(const meshtastic_MeshPacket &mp_encrypted, const meshtastic_Me auto jsonString = MeshPacketSerializer::JsonSerialize((meshtastic_MeshPacket *)&mp_decoded); if (jsonString.length() != 0) { std::string topicJson = jsonTopic + channelId + "/" + owner.id; - LOG_INFO("JSON publish message to %s, %u bytes: %s\n", topicJson.c_str(), jsonString.length(), + LOG_INFO("JSON publish message to %s, %u bytes: %s", topicJson.c_str(), jsonString.length(), jsonString.c_str()); publish(topicJson.c_str(), jsonString.c_str(), false); } } #endif // ARCH_NRF52 NRF52_USE_JSON } else { - LOG_INFO("MQTT not connected, queueing packet\n"); + LOG_INFO("MQTT not connected, queue packet"); if (mqttQueue.numFree() == 0) { - LOG_WARN("NOTE: MQTT queue is full, discarding oldest\n"); + LOG_WARN("MQTT queue is full, discard oldest"); meshtastic_ServiceEnvelope *d = mqttQueue.dequeuePtr(0); if (d) mqttPool.release(d); @@ -624,9 +642,9 @@ void MQTT::perhapsReportToMap() if (map_position_precision == 0 || (localPosition.latitude_i == 0 && localPosition.longitude_i == 0)) { last_report_to_map = millis(); if (map_position_precision == 0) - LOG_WARN("MQTT Map reporting is enabled, but precision is 0\n"); + LOG_WARN("MQTT Map report enabled, but precision is 0"); if (localPosition.latitude_i == 0 && localPosition.longitude_i == 0) - LOG_WARN("MQTT Map reporting is enabled, but no position available.\n"); + LOG_WARN("MQTT Map report enabled, but no position available"); return; } @@ -675,7 +693,7 @@ void MQTT::perhapsReportToMap() size_t numBytes = pb_encode_to_bytes(bytes, sizeof(bytes), &meshtastic_ServiceEnvelope_msg, se); - LOG_INFO("MQTT Publish map report to %s\n", mapTopic.c_str()); + LOG_INFO("MQTT Publish map report to %s", mapTopic.c_str()); publish(mapTopic.c_str(), bytes, numBytes, false); // Release the allocated memory for ServiceEnvelope and MeshPacket @@ -696,4 +714,70 @@ bool MQTT::isValidJsonEnvelope(JSONObject &json) (json["from"]->AsNumber() == nodeDB->getNodeNum()) && // only accept message if the "from" is us (json.find("type") != json.end()) && json["type"]->IsString() && // should specify a type (json.find("payload") != json.end()); // should have a payload -} \ No newline at end of file +} + +bool MQTT::isPrivateIpAddress(const char address[]) +{ + // Min. length like 10.0.0.0 (8), max like 192.168.255.255:65535 (21) + size_t length = strlen(address); + if (length < 8 || length > 21) { + return false; + } + + // Ensure the address contains only digits and dots and maybe a colon. + // Some limited validation is done. + // Even if it's not a valid IP address, we will know it's not a domain. + bool hasColon = false; + int numDots = 0; + for (size_t i = 0; i < length; i++) { + if (!isdigit(address[i]) && address[i] != '.' && address[i] != ':') { + return false; + } + + // Dots can't be the first character, immediately follow another dot, + // occur more than 3 times, or occur after a colon. + if (address[i] == '.') { + if (++numDots > 3 || i == 0 || address[i - 1] == '.' || hasColon) { + return false; + } + } + // There can only be a single colon, and it can only occur after 3 dots + else if (address[i] == ':') { + if (hasColon || numDots < 3) { + return false; + } + + hasColon = true; + } + } + + // Final validation for IPv4 address and port format. + // Note that the values of octets haven't been tested, only the address format. + if (numDots != 3) { + return false; + } + + // Check the easy ones first. + if (strcmp(address, "127.0.0.1") == 0 || strncmp(address, "10.", 3) == 0 || strncmp(address, "192.168", 7) == 0 || + strncmp(address, "169.254", 7) == 0) { + return true; + } + + // See if it's definitely not a 172 address. + if (strncmp(address, "172", 3) != 0) { + return false; + } + + // We know it's a 172 address, now see if the second octet is 2 digits. + if (address[6] != '.') { + return false; + } + + // Copy the second octet into a secondary buffer we can null-terminate and parse. + char octet2[3]; + strncpy(octet2, address + 4, 2); + octet2[2] = 0; + + int octet2Num = atoi(octet2); + return octet2Num >= 16 && octet2Num <= 31; +} diff --git a/src/mqtt/MQTT.h b/src/mqtt/MQTT.h index 83adc8fd2..7e0378238 100644 --- a/src/mqtt/MQTT.h +++ b/src/mqtt/MQTT.h @@ -35,7 +35,7 @@ class MQTT : private concurrency::OSThread #if HAS_WIFI WiFiClient mqttClient; #if !defined(ARCH_PORTDUINO) -#if defined(ESP_ARDUINO_VERSION_MAJOR) && ESP_ARDUINO_VERSION_MAJOR < 3 +#if (defined(ESP_ARDUINO_VERSION_MAJOR) && ESP_ARDUINO_VERSION_MAJOR < 3) || defined(RPI_PICO) WiFiClientSecure wifiSecureClient; #endif #endif @@ -120,10 +120,14 @@ class MQTT : private concurrency::OSThread // returns true if this is a valid JSON envelope which we accept on downlink bool isValidJsonEnvelope(JSONObject &json); + /// Determines if the given address is a private IPv4 address, i.e. not routable on the public internet. + /// These are the ranges: 127.0.0.1, 10.0.0.0-10.255.255.255, 172.16.0.0-172.31.255.255, 192.168.0.0-192.168.255.255. + bool isPrivateIpAddress(const char address[]); + /// 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; } }; void mqttInit(); -extern MQTT *mqtt; \ No newline at end of file +extern MQTT *mqtt; diff --git a/src/nimble/NimbleBluetooth.cpp b/src/nimble/NimbleBluetooth.cpp index eedfe1a71..2662ef0bc 100644 --- a/src/nimble/NimbleBluetooth.cpp +++ b/src/nimble/NimbleBluetooth.cpp @@ -26,7 +26,7 @@ class BluetoothPhoneAPI : public PhoneAPI { PhoneAPI::onNowHasData(fromRadioNum); - LOG_INFO("BLE notify fromNum\n"); + LOG_INFO("BLE notify fromNum"); uint8_t val[4]; put_le32(val, fromRadioNum); @@ -51,15 +51,15 @@ class NimbleBluetoothToRadioCallback : public NimBLECharacteristicCallbacks { virtual void onWrite(NimBLECharacteristic *pCharacteristic) { - LOG_INFO("To Radio onwrite\n"); + LOG_INFO("To Radio onwrite"); auto val = pCharacteristic->getValue(); if (memcmp(lastToRadio, val.data(), val.length()) != 0) { - LOG_DEBUG("New ToRadio packet\n"); + LOG_DEBUG("New ToRadio packet"); memcpy(lastToRadio, val.data(), val.length()); bluetoothPhoneAPI->handleToRadio(val.data(), val.length()); } else { - LOG_DEBUG("Dropping duplicate ToRadio packet we just saw\n"); + LOG_DEBUG("Drop dup ToRadio packet we just saw"); } } }; @@ -84,11 +84,11 @@ class NimbleBluetoothServerCallback : public NimBLEServerCallbacks uint32_t passkey = config.bluetooth.fixed_pin; if (config.bluetooth.mode == meshtastic_Config_BluetoothConfig_PairingMode_RANDOM_PIN) { - LOG_INFO("Using random passkey\n"); + LOG_INFO("Use random passkey"); // This is the passkey to be entered on peer - we pick a number >100,000 to ensure 6 digits passkey = random(100000, 999999); } - LOG_INFO("*** Enter passkey %d on the peer side ***\n", passkey); + LOG_INFO("*** Enter passkey %d on the peer side ***", passkey); powerFSM.trigger(EVENT_BLUETOOTH_PAIR); #if HAS_SCREEN @@ -125,7 +125,7 @@ class NimbleBluetoothServerCallback : public NimBLEServerCallbacks virtual void onAuthenticationComplete(ble_gap_conn_desc *desc) { - LOG_INFO("BLE authentication complete\n"); + LOG_INFO("BLE authentication complete"); if (passkeyShowing) { passkeyShowing = false; @@ -135,7 +135,7 @@ class NimbleBluetoothServerCallback : public NimBLEServerCallbacks virtual void onDisconnect(NimBLEServer *pServer, ble_gap_conn_desc *desc) { - LOG_INFO("BLE disconnect\n"); + LOG_INFO("BLE disconnect"); if (bluetoothPhoneAPI) { bluetoothPhoneAPI->close(); @@ -151,7 +151,7 @@ void NimbleBluetooth::shutdown() // No measurable power saving for ESP32 during light-sleep(?) #ifndef ARCH_ESP32 // Shutdown bluetooth for minimum power draw - LOG_INFO("Disable bluetooth\n"); + LOG_INFO("Disable bluetooth"); NimBLEAdvertising *pAdvertising = NimBLEDevice::getAdvertising(); pAdvertising->reset(); pAdvertising->stop(); @@ -162,7 +162,7 @@ void NimbleBluetooth::shutdown() void NimbleBluetooth::deinit() { #ifdef ARCH_ESP32 - LOG_INFO("Disable bluetooth until reboot\n"); + LOG_INFO("Disable bluetooth until reboot"); NimBLEDevice::deinit(); #endif } @@ -193,7 +193,7 @@ void NimbleBluetooth::setup() // Uncomment for testing // NimbleBluetooth::clearBonds(); - LOG_INFO("Initialise the NimBLE bluetooth module\n"); + LOG_INFO("Init the NimBLE bluetooth module"); NimBLEDevice::init(getDeviceName()); NimBLEDevice::setPower(ESP_PWR_LVL_P9); @@ -279,7 +279,7 @@ void updateBatteryLevel(uint8_t level) void NimbleBluetooth::clearBonds() { - LOG_INFO("Clearing bluetooth bonds!\n"); + LOG_INFO("Clearing bluetooth bonds!"); NimBLEDevice::deleteAllBonds(); } diff --git a/src/platform/esp32/ESP32CryptoEngine.cpp b/src/platform/esp32/ESP32CryptoEngine.cpp index 230139036..b554a3de4 100644 --- a/src/platform/esp32/ESP32CryptoEngine.cpp +++ b/src/platform/esp32/ESP32CryptoEngine.cpp @@ -32,7 +32,7 @@ class ESP32CryptoEngine : public CryptoEngine sizeof(scratch) - numBytes); // Fill rest of buffer with zero (in case cypher looks at it) mbedtls_aes_crypt_ctr(&aes, numBytes, &nc_off, _nonce, stream_block, scratch, bytes); } else { - LOG_ERROR("Packet too large for crypto engine: %d. noop encryption!\n", numBytes); + LOG_ERROR("Packet too large for crypto engine: %d. noop encryption!", numBytes); } } } diff --git a/src/platform/esp32/SimpleAllocator.cpp b/src/platform/esp32/SimpleAllocator.cpp index 04ce35eb3..d482a3f1c 100644 --- a/src/platform/esp32/SimpleAllocator.cpp +++ b/src/platform/esp32/SimpleAllocator.cpp @@ -12,7 +12,7 @@ void *SimpleAllocator::alloc(size_t size) assert(nextFree + size <= sizeof(bytes)); void *res = &bytes[nextFree]; nextFree += size; - LOG_DEBUG("Total simple allocs %u\n", nextFree); + LOG_DEBUG("Total simple allocs %u", nextFree); return res; } diff --git a/src/platform/esp32/architecture.h b/src/platform/esp32/architecture.h index 01f416629..1a274aa28 100644 --- a/src/platform/esp32/architecture.h +++ b/src/platform/esp32/architecture.h @@ -104,6 +104,8 @@ #define HW_VENDOR meshtastic_HardwareModel_NANO_G1 #elif defined(M5STACK) #define HW_VENDOR meshtastic_HardwareModel_M5STACK +#elif defined(M5STACK_CORES3) +#define HW_VENDOR meshtastic_HardwareModel_M5STACK_CORES3 #elif defined(STATION_G1) #define HW_VENDOR meshtastic_HardwareModel_STATION_G1 #elif defined(DR_DEV) @@ -168,8 +170,6 @@ #define HW_VENDOR meshtastic_HardwareModel_HELTEC_VISION_MASTER_E213 #elif defined(HELTEC_VISION_MASTER_E290) #define HW_VENDOR meshtastic_HardwareModel_HELTEC_VISION_MASTER_E290 -#elif defined(HELTEC_MESH_NODE_T114) -#define HW_VENDOR meshtastic_HardwareModel_HELTEC_MESH_NODE_T114 #elif defined(SENSECAP_INDICATOR) #define HW_VENDOR meshtastic_HardwareModel_SENSECAP_INDICATOR #elif defined(SEEED_XIAO_S3) diff --git a/src/platform/esp32/main-esp32.cpp b/src/platform/esp32/main-esp32.cpp index f16accab6..679222af5 100644 --- a/src/platform/esp32/main-esp32.cpp +++ b/src/platform/esp32/main-esp32.cpp @@ -51,7 +51,11 @@ void updateBatteryLevel(uint8_t level) {} void getMacAddr(uint8_t *dmac) { +#if defined(CONFIG_IDF_TARGET_ESP32C6) && defined(CONFIG_SOC_IEEE802154_SUPPORTED) + assert(esp_base_mac_addr_get(dmac) == ESP_OK); +#else assert(esp_efuse_mac_get_default(dmac) == ESP_OK); +#endif } #ifdef HAS_32768HZ @@ -76,17 +80,17 @@ void enableSlowCLK() uint32_t cal_32k = CALIBRATE_ONE(RTC_CAL_32K_XTAL); if (cal_32k == 0) { - LOG_DEBUG("32K XTAL OSC has not started up\n"); + LOG_DEBUG("32K XTAL OSC has not started up"); } else { rtc_clk_slow_freq_set(RTC_SLOW_FREQ_32K_XTAL); - LOG_DEBUG("Switching RTC Source to 32.768Khz succeeded, using 32K XTAL\n"); + LOG_DEBUG("Switch RTC Source to 32.768Khz succeeded, using 32K XTAL"); CALIBRATE_ONE(RTC_CAL_RTC_MUX); CALIBRATE_ONE(RTC_CAL_32K_XTAL); } CALIBRATE_ONE(RTC_CAL_RTC_MUX); CALIBRATE_ONE(RTC_CAL_32K_XTAL); if (rtc_clk_slow_freq_get() != RTC_SLOW_FREQ_32K_XTAL) { - LOG_WARN("Failed to switch 32K XTAL RTC source to 32.768Khz !!! \n"); + LOG_WARN("Failed to switch 32K XTAL RTC source to 32.768Khz !!! "); return; } } @@ -97,22 +101,22 @@ void esp32Setup() /* We explicitly don't want to do call randomSeed, // as that triggers the esp32 core to use a less secure pseudorandom function. uint32_t seed = esp_random(); - LOG_DEBUG("Setting random seed %u\n", seed); + LOG_DEBUG("Set random seed %u", seed); randomSeed(seed); */ - LOG_DEBUG("Total heap: %d\n", ESP.getHeapSize()); - LOG_DEBUG("Free heap: %d\n", ESP.getFreeHeap()); - LOG_DEBUG("Total PSRAM: %d\n", ESP.getPsramSize()); - LOG_DEBUG("Free PSRAM: %d\n", ESP.getFreePsram()); + LOG_DEBUG("Total heap: %d", ESP.getHeapSize()); + LOG_DEBUG("Free heap: %d", ESP.getFreeHeap()); + LOG_DEBUG("Total PSRAM: %d", ESP.getPsramSize()); + LOG_DEBUG("Free PSRAM: %d", ESP.getFreePsram()); nvs_stats_t nvs_stats; auto res = nvs_get_stats(NULL, &nvs_stats); assert(res == ESP_OK); - LOG_DEBUG("NVS: UsedEntries %d, FreeEntries %d, AllEntries %d, NameSpaces %d\n", nvs_stats.used_entries, - nvs_stats.free_entries, nvs_stats.total_entries, nvs_stats.namespace_count); + LOG_DEBUG("NVS: UsedEntries %d, FreeEntries %d, AllEntries %d, NameSpaces %d", nvs_stats.used_entries, nvs_stats.free_entries, + nvs_stats.total_entries, nvs_stats.namespace_count); - LOG_DEBUG("Setup Preferences in Flash Storage\n"); + LOG_DEBUG("Setup Preferences in Flash Storage"); // Create object to store our persistent data Preferences preferences; @@ -129,16 +133,16 @@ void esp32Setup() if (hwven != HW_VENDOR) preferences.putUInt("hwVendor", HW_VENDOR); preferences.end(); - LOG_DEBUG("Number of Device Reboots: %d\n", rebootCounter); + LOG_DEBUG("Number of Device Reboots: %d", rebootCounter); #if !MESHTASTIC_EXCLUDE_BLUETOOTH String BLEOTA = BleOta::getOtaAppVersion(); if (BLEOTA.isEmpty()) { - LOG_INFO("No OTA firmware available\n"); + LOG_INFO("No OTA firmware available"); } else { - LOG_INFO("OTA firmware version %s\n", BLEOTA.c_str()); + LOG_INFO("OTA firmware version %s", BLEOTA.c_str()); } #else - LOG_INFO("No OTA firmware available\n"); + LOG_INFO("No OTA firmware available"); #endif // enableModemSleep(); @@ -166,26 +170,6 @@ void esp32Setup() #endif } -#if 0 -// Turn off for now - -uint32_t axpDebugRead() -{ - axp.debugCharging(); - LOG_DEBUG("vbus current %f\n", axp.getVbusCurrent()); - LOG_DEBUG("charge current %f\n", axp.getBattChargeCurrent()); - LOG_DEBUG("bat voltage %f\n", axp.getBattVoltage()); - LOG_DEBUG("batt pct %d\n", axp.getBattPercentage()); - LOG_DEBUG("is battery connected %d\n", axp.isBatteryConnect()); - LOG_DEBUG("is USB connected %d\n", axp.isVBUSPlug()); - LOG_DEBUG("is charging %d\n", axp.isChargeing()); - - return 30 * 1000; -} - -Periodic axpDebugOutput(axpDebugRead); -#endif - /// loop code specific to ESP32 targets void esp32Loop() { @@ -263,4 +247,4 @@ void cpuDeepSleep(uint32_t msecToWake) esp_sleep_enable_timer_wakeup(msecToWake * 1000ULL); // call expects usecs esp_deep_sleep_start(); // TBD mA sleep current (battery) -} \ No newline at end of file +} diff --git a/src/platform/extra_variants/heltec_wireless_tracker/variant.cpp b/src/platform/extra_variants/heltec_wireless_tracker/variant.cpp index 0a19a9c3b..12960e2d9 100644 --- a/src/platform/extra_variants/heltec_wireless_tracker/variant.cpp +++ b/src/platform/extra_variants/heltec_wireless_tracker/variant.cpp @@ -9,7 +9,7 @@ // Heltec tracker specific init void lateInitVariant() { - // LOG_DEBUG("Heltec tracker initVariant\n"); + // LOG_DEBUG("Heltec tracker initVariant"); #ifndef MESHTASTIC_EXCLUDE_GPS GpioVirtPin *virtGpsEnable = gps ? gps->enablePin : new GpioVirtPin(); diff --git a/src/platform/nrf52/NRF52Bluetooth.cpp b/src/platform/nrf52/NRF52Bluetooth.cpp index 63d9331ad..31bbc7fa9 100644 --- a/src/platform/nrf52/NRF52Bluetooth.cpp +++ b/src/platform/nrf52/NRF52Bluetooth.cpp @@ -39,7 +39,7 @@ class BluetoothPhoneAPI : public PhoneAPI { PhoneAPI::onNowHasData(fromRadioNum); - LOG_INFO("BLE notify fromNum\n"); + LOG_INFO("BLE notify fromNum"); fromNum.notify32(fromRadioNum); } @@ -60,7 +60,7 @@ void onConnect(uint16_t conn_handle) connectionHandle = conn_handle; char central_name[32] = {0}; connection->getPeerName(central_name, sizeof(central_name)); - LOG_INFO("BLE Connected to %s\n", central_name); + LOG_INFO("BLE Connected to %s", central_name); } /** * Callback invoked when a connection is dropped @@ -69,7 +69,7 @@ void onConnect(uint16_t conn_handle) */ void onDisconnect(uint16_t conn_handle, uint8_t reason) { - LOG_INFO("BLE Disconnected, reason = 0x%x\n", reason); + LOG_INFO("BLE Disconnected, reason = 0x%x", reason); if (bluetoothPhoneAPI) { bluetoothPhoneAPI->close(); } @@ -77,7 +77,7 @@ void onDisconnect(uint16_t conn_handle, uint8_t reason) void onCccd(uint16_t conn_hdl, BLECharacteristic *chr, uint16_t cccd_value) { // Display the raw request packet - LOG_INFO("CCCD Updated: %u\n", cccd_value); + LOG_INFO("CCCD Updated: %u", cccd_value); // Check the characteristic this CCCD update is associated with in case // this handler is used for multiple CCCD records. @@ -87,9 +87,9 @@ void onCccd(uint16_t conn_hdl, BLECharacteristic *chr, uint16_t cccd_value) if (chr->uuid == fromNum.uuid || chr->uuid == logRadio.uuid) { auto result = cccd_value == 2 ? chr->indicateEnabled(conn_hdl) : chr->notifyEnabled(conn_hdl); if (result) { - LOG_INFO("Notify/Indicate enabled\n"); + LOG_INFO("Notify/Indicate enabled"); } else { - LOG_INFO("Notify/Indicate disabled\n"); + LOG_INFO("Notify/Indicate disabled"); } } } @@ -137,7 +137,7 @@ void onFromRadioAuthorize(uint16_t conn_hdl, BLECharacteristic *chr, ble_gatts_e // or make empty if the queue is empty fromRadio.write(fromRadioBytes, numBytes); } else { - // LOG_INFO("Ignoring successor read\n"); + // LOG_INFO("Ignore successor read"); } authorizeRead(conn_hdl); } @@ -146,13 +146,13 @@ static uint8_t lastToRadio[MAX_TO_FROM_RADIO_SIZE]; void onToRadioWrite(uint16_t conn_hdl, BLECharacteristic *chr, uint8_t *data, uint16_t len) { - LOG_INFO("toRadioWriteCb data %p, len %u\n", data, len); + LOG_INFO("toRadioWriteCb data %p, len %u", data, len); if (memcmp(lastToRadio, data, len) != 0) { - LOG_DEBUG("New ToRadio packet\n"); + LOG_DEBUG("New ToRadio packet"); memcpy(lastToRadio, data, len); bluetoothPhoneAPI->handleToRadio(data, len); } else { - LOG_DEBUG("Dropping duplicate ToRadio packet we just saw\n"); + LOG_DEBUG("Drop dup ToRadio packet we just saw"); } } @@ -207,11 +207,11 @@ static uint32_t configuredPasskey; void NRF52Bluetooth::shutdown() { // Shutdown bluetooth for minimum power draw - LOG_INFO("Disable NRF52 bluetooth\n"); + LOG_INFO("Disable NRF52 bluetooth"); uint8_t connection_num = Bluefruit.connected(); if (connection_num) { for (uint8_t i = 0; i < connection_num; i++) { - LOG_INFO("NRF52 bluetooth disconnecting handle %d\n", i); + LOG_INFO("NRF52 bluetooth disconnecting handle %d", i); Bluefruit.disconnect(i); } delay(100); // wait for ondisconnect; @@ -225,7 +225,7 @@ void NRF52Bluetooth::startDisabled() // Shutdown bluetooth for minimum power draw Bluefruit.Advertising.stop(); Bluefruit.setTxPower(-40); // Minimum power - LOG_INFO("Disabling NRF52 Bluetooth. (Workaround: tx power min, advertising stopped)\n"); + LOG_INFO("Disable NRF52 Bluetooth. (Workaround: tx power min, advertise stopped)"); } bool NRF52Bluetooth::isConnected() { @@ -238,7 +238,7 @@ int NRF52Bluetooth::getRssi() void NRF52Bluetooth::setup() { // Initialise the Bluefruit module - LOG_INFO("Initialize the Bluefruit nRF52 module\n"); + LOG_INFO("Init the Bluefruit nRF52 module"); Bluefruit.autoConnLed(false); Bluefruit.configPrphBandwidth(BANDWIDTH_MAX); Bluefruit.begin(); @@ -251,7 +251,7 @@ void NRF52Bluetooth::setup() ? config.bluetooth.fixed_pin : random(100000, 999999); auto pinString = std::to_string(configuredPasskey); - LOG_INFO("Bluetooth pin set to '%i'\n", configuredPasskey); + LOG_INFO("Bluetooth pin set to '%i'", configuredPasskey); Bluefruit.Security.setPIN(pinString.c_str()); Bluefruit.Security.setIOCaps(true, false, false); Bluefruit.Security.setPairPasskeyCallback(NRF52Bluetooth::onPairingPasskey); @@ -275,22 +275,22 @@ void NRF52Bluetooth::setup() bledfusecure.begin(); // Install the DFU helper #endif // Configure and Start the Device Information Service - LOG_INFO("Configuring the Device Information Service\n"); + LOG_INFO("Init the Device Information Service"); bledis.setModel(optstr(HW_VERSION)); bledis.setFirmwareRev(optstr(APP_VERSION)); bledis.begin(); // Start the BLE Battery Service and set it to 100% - LOG_INFO("Configuring the Battery Service\n"); + LOG_INFO("Init the Battery Service"); blebas.begin(); blebas.write(0); // Unknown battery level for now // Setup the Heart Rate Monitor service using // BLEService and BLECharacteristic classes - LOG_INFO("Configuring the Mesh bluetooth service\n"); + LOG_INFO("Init the Mesh bluetooth service"); setupMeshService(); // Setup the advertising packet(s) - LOG_INFO("Setting up the advertising payload(s)\n"); + LOG_INFO("Set up the advertising payload(s)"); startAdv(); - LOG_INFO("Advertising\n"); + LOG_INFO("Advertise"); } void NRF52Bluetooth::resumeAdvertising() { @@ -306,7 +306,7 @@ void updateBatteryLevel(uint8_t level) } void NRF52Bluetooth::clearBonds() { - LOG_INFO("Clearing bluetooth bonds!\n"); + LOG_INFO("Clear bluetooth bonds!"); bond_print_list(BLE_GAP_ROLE_PERIPH); bond_print_list(BLE_GAP_ROLE_CENTRAL); Bluefruit.Periph.clearBonds(); @@ -314,11 +314,11 @@ void NRF52Bluetooth::clearBonds() } void NRF52Bluetooth::onConnectionSecured(uint16_t conn_handle) { - LOG_INFO("BLE connection secured\n"); + LOG_INFO("BLE connection secured"); } bool NRF52Bluetooth::onPairingPasskey(uint16_t conn_handle, uint8_t const passkey[6], bool match_request) { - LOG_INFO("BLE pairing process started with passkey %.3s %.3s\n", passkey, passkey + 3); + LOG_INFO("BLE pair process started with passkey %.3s %.3s", passkey, passkey + 3); powerFSM.trigger(EVENT_BLUETOOTH_PAIR); #if !defined(MESHTASTIC_EXCLUDE_SCREEN) screen->startAlert([](OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y) -> void { @@ -354,15 +354,15 @@ bool NRF52Bluetooth::onPairingPasskey(uint16_t conn_handle, uint8_t const passke break; } } - LOG_INFO("BLE passkey pairing: match_request=%i\n", match_request); + LOG_INFO("BLE passkey pair: match_request=%i", match_request); return true; } void NRF52Bluetooth::onPairingCompleted(uint16_t conn_handle, uint8_t auth_status) { if (auth_status == BLE_GAP_SEC_STATUS_SUCCESS) - LOG_INFO("BLE pairing success\n"); + LOG_INFO("BLE pair success"); else - LOG_INFO("BLE pairing failed\n"); + LOG_INFO("BLE pair failed"); screen->endAlert(); } diff --git a/src/platform/nrf52/architecture.h b/src/platform/nrf52/architecture.h index b2b7b5a20..ce99244ba 100644 --- a/src/platform/nrf52/architecture.h +++ b/src/platform/nrf52/architecture.h @@ -47,6 +47,8 @@ #define HW_VENDOR meshtastic_HardwareModel_PPR #elif defined(RAK2560) #define HW_VENDOR meshtastic_HardwareModel_RAK2560 +#elif defined(WISMESH_TAP) +#define HW_VENDOR meshtastic_HardwareModel_WISMESH_TAP #elif defined(RAK4630) #define HW_VENDOR meshtastic_HardwareModel_RAK4631 #elif defined(TTGO_T_ECHO) diff --git a/src/platform/nrf52/main-nrf52.cpp b/src/platform/nrf52/main-nrf52.cpp index f9329d875..7ca047654 100644 --- a/src/platform/nrf52/main-nrf52.cpp +++ b/src/platform/nrf52/main-nrf52.cpp @@ -35,7 +35,7 @@ bool loopCanSleep() // handle standard gcc assert failures void __attribute__((noreturn)) __assert_func(const char *file, int line, const char *func, const char *failedexpr) { - LOG_ERROR("assert failed %s: %d, %s, test=%s\n", file, line, func, failedexpr); + LOG_ERROR("assert failed %s: %d, %s, test=%s", file, line, func, failedexpr); // debugger_break(); FIXME doesn't work, possibly not for segger // Reboot cpu NVIC_SystemReset(); @@ -74,7 +74,7 @@ void setBluetoothEnable(bool enable) // For debugging use: don't use bluetooth if (!useSoftDevice) { if (enable) - LOG_INFO("DISABLING NRF52 BLUETOOTH WHILE DEBUGGING\n"); + LOG_INFO("Disable NRF52 BLUETOOTH WHILE DEBUGGING"); return; } @@ -97,7 +97,7 @@ void setBluetoothEnable(bool enable) // If not yet set-up if (!nrf52Bluetooth) { - LOG_DEBUG("Initializing NRF52 Bluetooth\n"); + LOG_DEBUG("Init NRF52 Bluetooth"); nrf52Bluetooth = new NRF52Bluetooth(); nrf52Bluetooth->setup(); @@ -141,7 +141,7 @@ void checkSDEvents() break; default: - LOG_DEBUG("Unexpected SDevt %d\n", evt); + LOG_DEBUG("Unexpected SDevt %d", evt); break; } } @@ -188,7 +188,7 @@ void nrf52Setup() uint32_t why = NRF_POWER->RESETREAS; // per // https://infocenter.nordicsemi.com/index.jsp?topic=%2Fcom.nordic.infocenter.nrf52832.ps.v1.1%2Fpower.html - LOG_DEBUG("Reset reason: 0x%x\n", why); + LOG_DEBUG("Reset reason: 0x%x", why); #ifdef USE_SEMIHOSTING nrf52InitSemiHosting(); @@ -202,7 +202,7 @@ void nrf52Setup() #ifdef BQ25703A_ADDR auto *bq = new BQ25713(); if (!bq->setup()) - LOG_ERROR("ERROR! Charge controller init failed\n"); + LOG_ERROR("ERROR! Charge controller init failed"); #endif // Init random seed @@ -212,7 +212,7 @@ void nrf52Setup() } seed; nRFCrypto.begin(); nRFCrypto.Random.generate(seed.seed8, sizeof(seed.seed8)); - LOG_DEBUG("Setting random seed %u\n", seed.seed32); + LOG_DEBUG("Set random seed %u", seed.seed32); randomSeed(seed.seed32); nRFCrypto.end(); } @@ -280,7 +280,7 @@ void cpuDeepSleep(uint32_t msecToWake) // https://devzone.nordicsemi.com/f/nordic-q-a/48919/ram-retention-settings-with-softdevice-enabled auto ok = sd_power_system_off(); if (ok != NRF_SUCCESS) { - LOG_ERROR("FIXME: Ignoring soft device (EasyDMA pending?) and forcing system-off!\n"); + LOG_ERROR("FIXME: Ignoring soft device (EasyDMA pending?) and forcing system-off!"); NRF_POWER->SYSTEMOFF = 1; } } @@ -309,4 +309,4 @@ void enterDfuMode() #else enterUf2Dfu(); #endif -} \ No newline at end of file +} diff --git a/src/platform/portduino/PortduinoGlue.cpp b/src/platform/portduino/PortduinoGlue.cpp index 92bf7c86e..5d8ac0e60 100644 --- a/src/platform/portduino/PortduinoGlue.cpp +++ b/src/platform/portduino/PortduinoGlue.cpp @@ -11,6 +11,7 @@ #include "PortduinoGlue.h" #include "linux/gpio/LinuxGPIOPin.h" #include "yaml-cpp/yaml.h" +#include #include #include #include @@ -74,7 +75,7 @@ void portduinoCustomInit() */ void portduinoSetup() { - printf("Setting up Meshtastic on Portduino...\n"); + printf("Set up Meshtastic on Portduino...\n"); int max_GPIO = 0; const configNames GPIO_lines[] = {cs, irq, @@ -100,33 +101,22 @@ void portduinoSetup() settingsStrings[displayspidev] = ""; settingsMap[spiSpeed] = 2000000; settingsMap[ascii_logs] = !isatty(1); + settingsMap[displayPanel] = no_screen; + settingsMap[touchscreenModule] = no_touchscreen; YAML::Node yamlConfig; if (configPath != nullptr) { - std::cout << "Using " << configPath << " as config file" << std::endl; - try { - yamlConfig = YAML::LoadFile(configPath); - } catch (YAML::Exception &e) { - std::cout << "Could not open " << configPath << " because of error: " << e.what() << std::endl; + if (loadConfig(configPath)) { + std::cout << "Using " << configPath << " as config file" << std::endl; + } else { + std::cout << "Unable to use " << configPath << " as config file" << std::endl; exit(EXIT_FAILURE); } - } else if (access("config.yaml", R_OK) == 0) { + } else if (access("config.yaml", R_OK) == 0 && loadConfig("config.yaml")) { std::cout << "Using local config.yaml as config file" << std::endl; - try { - yamlConfig = YAML::LoadFile("config.yaml"); - } catch (YAML::Exception &e) { - std::cout << "*** Exception " << e.what() << std::endl; - exit(EXIT_FAILURE); - } - } else if (access("/etc/meshtasticd/config.yaml", R_OK) == 0) { + } else if (access("/etc/meshtasticd/config.yaml", R_OK) == 0 && loadConfig("/etc/meshtasticd/config.yaml")) { std::cout << "Using /etc/meshtasticd/config.yaml as config file" << std::endl; - try { - yamlConfig = YAML::LoadFile("/etc/meshtasticd/config.yaml"); - } catch (YAML::Exception &e) { - std::cout << "*** Exception " << e.what() << std::endl; - exit(EXIT_FAILURE); - } } else { std::cout << "No 'config.yaml' found, running simulated." << std::endl; settingsMap[maxnodes] = 200; // Default to 200 nodes @@ -136,170 +126,21 @@ void portduinoSetup() return; } + if (settingsStrings[config_directory] != "") { + std::string filetype = ".yaml"; + for (const std::filesystem::directory_entry &entry : + std::filesystem::directory_iterator{settingsStrings[config_directory]}) { + if (ends_with(entry.path().string(), ".yaml")) { + std::cout << "Also using " << entry << " as additional config file" << std::endl; + loadConfig(entry.path().c_str()); + } + } + } + // Rather important to set this, if not running simulated. randomSeed(time(NULL)); - try { - if (yamlConfig["Logging"]) { - if (yamlConfig["Logging"]["LogLevel"].as("info") == "trace") { - settingsMap[logoutputlevel] = level_trace; - } else if (yamlConfig["Logging"]["LogLevel"].as("info") == "debug") { - settingsMap[logoutputlevel] = level_debug; - } else if (yamlConfig["Logging"]["LogLevel"].as("info") == "info") { - settingsMap[logoutputlevel] = level_info; - } else if (yamlConfig["Logging"]["LogLevel"].as("info") == "warn") { - settingsMap[logoutputlevel] = level_warn; - } else if (yamlConfig["Logging"]["LogLevel"].as("info") == "error") { - settingsMap[logoutputlevel] = level_error; - } - settingsStrings[traceFilename] = yamlConfig["Logging"]["TraceFile"].as(""); - if (yamlConfig["Logging"]["AsciiLogs"]) { - // Default is !isatty(1) but can be set explicitly in config.yaml - settingsMap[ascii_logs] = yamlConfig["Logging"]["AsciiLogs"].as(); - } - } - if (yamlConfig["Lora"]) { - settingsMap[use_sx1262] = false; - settingsMap[use_rf95] = false; - settingsMap[use_sx1280] = false; - settingsMap[use_sx1268] = false; - - if (yamlConfig["Lora"]["Module"] && yamlConfig["Lora"]["Module"].as("") == "sx1262") { - settingsMap[use_sx1262] = true; - } else if (yamlConfig["Lora"]["Module"] && yamlConfig["Lora"]["Module"].as("") == "RF95") { - settingsMap[use_rf95] = true; - } else if (yamlConfig["Lora"]["Module"] && yamlConfig["Lora"]["Module"].as("") == "sx1280") { - settingsMap[use_sx1280] = true; - } else if (yamlConfig["Lora"]["Module"] && yamlConfig["Lora"]["Module"].as("") == "sx1268") { - settingsMap[use_sx1268] = true; - } - settingsMap[dio2_as_rf_switch] = yamlConfig["Lora"]["DIO2_AS_RF_SWITCH"].as(false); - settingsMap[dio3_tcxo_voltage] = yamlConfig["Lora"]["DIO3_TCXO_VOLTAGE"].as(false); - settingsMap[cs] = yamlConfig["Lora"]["CS"].as(RADIOLIB_NC); - settingsMap[irq] = yamlConfig["Lora"]["IRQ"].as(RADIOLIB_NC); - settingsMap[busy] = yamlConfig["Lora"]["Busy"].as(RADIOLIB_NC); - settingsMap[reset] = yamlConfig["Lora"]["Reset"].as(RADIOLIB_NC); - settingsMap[txen] = yamlConfig["Lora"]["TXen"].as(RADIOLIB_NC); - settingsMap[rxen] = yamlConfig["Lora"]["RXen"].as(RADIOLIB_NC); - settingsMap[sx126x_ant_sw] = yamlConfig["Lora"]["SX126X_ANT_SW"].as(RADIOLIB_NC); - settingsMap[gpiochip] = yamlConfig["Lora"]["gpiochip"].as(0); - settingsMap[ch341Quirk] = yamlConfig["Lora"]["ch341_quirk"].as(false); - settingsMap[spiSpeed] = yamlConfig["Lora"]["spiSpeed"].as(2000000); - gpioChipName += std::to_string(settingsMap[gpiochip]); - - settingsStrings[spidev] = "/dev/" + yamlConfig["Lora"]["spidev"].as("spidev0.0"); - if (settingsStrings[spidev].length() == 14) { - int x = settingsStrings[spidev].at(11) - '0'; - int y = settingsStrings[spidev].at(13) - '0'; - if (x >= 0 && x < 10 && y >= 0 && y < 10) { - settingsMap[spidev] = x + y << 4; - settingsMap[displayspidev] = settingsMap[spidev]; - settingsMap[touchscreenspidev] = settingsMap[spidev]; - } - } - } - if (yamlConfig["GPIO"]) { - settingsMap[user] = yamlConfig["GPIO"]["User"].as(RADIOLIB_NC); - } - if (yamlConfig["GPS"]) { - std::string serialPath = yamlConfig["GPS"]["SerialPath"].as(""); - if (serialPath != "") { - Serial1.setPath(serialPath); - settingsMap[has_gps] = 1; - } - } - if (yamlConfig["I2C"]) { - settingsStrings[i2cdev] = yamlConfig["I2C"]["I2CDevice"].as(""); - } - settingsMap[displayPanel] = no_screen; - if (yamlConfig["Display"]) { - if (yamlConfig["Display"]["Panel"].as("") == "ST7789") - settingsMap[displayPanel] = st7789; - else if (yamlConfig["Display"]["Panel"].as("") == "ST7735") - settingsMap[displayPanel] = st7735; - else if (yamlConfig["Display"]["Panel"].as("") == "ST7735S") - settingsMap[displayPanel] = st7735s; - else if (yamlConfig["Display"]["Panel"].as("") == "ST7796") - settingsMap[displayPanel] = st7796; - else if (yamlConfig["Display"]["Panel"].as("") == "ILI9341") - settingsMap[displayPanel] = ili9341; - else if (yamlConfig["Display"]["Panel"].as("") == "ILI9342") - settingsMap[displayPanel] = ili9342; - else if (yamlConfig["Display"]["Panel"].as("") == "ILI9488") - settingsMap[displayPanel] = ili9488; - else if (yamlConfig["Display"]["Panel"].as("") == "HX8357D") - settingsMap[displayPanel] = hx8357d; - else if (yamlConfig["Display"]["Panel"].as("") == "X11") - settingsMap[displayPanel] = x11; - settingsMap[displayHeight] = yamlConfig["Display"]["Height"].as(0); - settingsMap[displayWidth] = yamlConfig["Display"]["Width"].as(0); - settingsMap[displayDC] = yamlConfig["Display"]["DC"].as(-1); - settingsMap[displayCS] = yamlConfig["Display"]["CS"].as(-1); - settingsMap[displayRGBOrder] = yamlConfig["Display"]["RGBOrder"].as(false); - settingsMap[displayBacklight] = yamlConfig["Display"]["Backlight"].as(-1); - settingsMap[displayBacklightInvert] = yamlConfig["Display"]["BacklightInvert"].as(false); - settingsMap[displayBacklightPWMChannel] = yamlConfig["Display"]["BacklightPWMChannel"].as(-1); - settingsMap[displayReset] = yamlConfig["Display"]["Reset"].as(-1); - settingsMap[displayOffsetX] = yamlConfig["Display"]["OffsetX"].as(0); - settingsMap[displayOffsetY] = yamlConfig["Display"]["OffsetY"].as(0); - settingsMap[displayRotate] = yamlConfig["Display"]["Rotate"].as(false); - settingsMap[displayOffsetRotate] = yamlConfig["Display"]["OffsetRotate"].as(1); - settingsMap[displayInvert] = yamlConfig["Display"]["Invert"].as(false); - settingsMap[displayBusFrequency] = yamlConfig["Display"]["BusFrequency"].as(40000000); - if (yamlConfig["Display"]["spidev"]) { - settingsStrings[displayspidev] = "/dev/" + yamlConfig["Display"]["spidev"].as("spidev0.1"); - if (settingsStrings[displayspidev].length() == 14) { - int x = settingsStrings[displayspidev].at(11) - '0'; - int y = settingsStrings[displayspidev].at(13) - '0'; - if (x >= 0 && x < 10 && y >= 0 && y < 10) { - settingsMap[displayspidev] = x + y << 4; - settingsMap[touchscreenspidev] = settingsMap[displayspidev]; - } - } - } - } - settingsMap[touchscreenModule] = no_touchscreen; - if (yamlConfig["Touchscreen"]) { - if (yamlConfig["Touchscreen"]["Module"].as("") == "XPT2046") - settingsMap[touchscreenModule] = xpt2046; - else if (yamlConfig["Touchscreen"]["Module"].as("") == "STMPE610") - settingsMap[touchscreenModule] = stmpe610; - else if (yamlConfig["Touchscreen"]["Module"].as("") == "GT911") - settingsMap[touchscreenModule] = gt911; - else if (yamlConfig["Touchscreen"]["Module"].as("") == "FT5x06") - settingsMap[touchscreenModule] = ft5x06; - settingsMap[touchscreenCS] = yamlConfig["Touchscreen"]["CS"].as(-1); - settingsMap[touchscreenIRQ] = yamlConfig["Touchscreen"]["IRQ"].as(-1); - settingsMap[touchscreenBusFrequency] = yamlConfig["Touchscreen"]["BusFrequency"].as(1000000); - settingsMap[touchscreenRotate] = yamlConfig["Touchscreen"]["Rotate"].as(-1); - settingsMap[touchscreenI2CAddr] = yamlConfig["Touchscreen"]["I2CAddr"].as(-1); - if (yamlConfig["Touchscreen"]["spidev"]) { - settingsStrings[touchscreenspidev] = "/dev/" + yamlConfig["Touchscreen"]["spidev"].as(""); - if (settingsStrings[touchscreenspidev].length() == 14) { - int x = settingsStrings[touchscreenspidev].at(11) - '0'; - int y = settingsStrings[touchscreenspidev].at(13) - '0'; - if (x >= 0 && x < 10 && y >= 0 && y < 10) { - settingsMap[touchscreenspidev] = x + y << 4; - } - } - } - } - if (yamlConfig["Input"]) { - settingsStrings[keyboardDevice] = (yamlConfig["Input"]["KeyboardDevice"]).as(""); - } - - if (yamlConfig["Webserver"]) { - settingsMap[webserverport] = (yamlConfig["Webserver"]["Port"]).as(-1); - settingsStrings[webserverrootpath] = (yamlConfig["Webserver"]["RootPath"]).as(""); - } - - settingsMap[maxnodes] = (yamlConfig["General"]["MaxNodes"]).as(200); - settingsMap[maxtophone] = (yamlConfig["General"]["MaxMessageQueue"]).as(100); - - } catch (YAML::Exception &e) { - std::cout << "*** Exception " << e.what() << std::endl; - exit(EXIT_FAILURE); - } + gpioChipName += std::to_string(settingsMap[gpiochip]); for (configNames i : GPIO_lines) { if (settingsMap.count(i) && settingsMap[i] > max_GPIO) @@ -402,4 +243,178 @@ int initGPIOPin(int pinNum, const std::string gpioChipName) #else return ERRNO_OK; #endif +} + +bool loadConfig(const char *configPath) +{ + YAML::Node yamlConfig; + try { + yamlConfig = YAML::LoadFile(configPath); + if (yamlConfig["Logging"]) { + if (yamlConfig["Logging"]["LogLevel"].as("info") == "trace") { + settingsMap[logoutputlevel] = level_trace; + } else if (yamlConfig["Logging"]["LogLevel"].as("info") == "debug") { + settingsMap[logoutputlevel] = level_debug; + } else if (yamlConfig["Logging"]["LogLevel"].as("info") == "info") { + settingsMap[logoutputlevel] = level_info; + } else if (yamlConfig["Logging"]["LogLevel"].as("info") == "warn") { + settingsMap[logoutputlevel] = level_warn; + } else if (yamlConfig["Logging"]["LogLevel"].as("info") == "error") { + settingsMap[logoutputlevel] = level_error; + } + settingsStrings[traceFilename] = yamlConfig["Logging"]["TraceFile"].as(""); + if (yamlConfig["Logging"]["AsciiLogs"]) { + // Default is !isatty(1) but can be set explicitly in config.yaml + settingsMap[ascii_logs] = yamlConfig["Logging"]["AsciiLogs"].as(); + } + } + if (yamlConfig["Lora"]) { + settingsMap[use_sx1262] = false; + settingsMap[use_rf95] = false; + settingsMap[use_sx1280] = false; + settingsMap[use_sx1268] = false; + + if (yamlConfig["Lora"]["Module"] && yamlConfig["Lora"]["Module"].as("") == "sx1262") { + settingsMap[use_sx1262] = true; + } else if (yamlConfig["Lora"]["Module"] && yamlConfig["Lora"]["Module"].as("") == "RF95") { + settingsMap[use_rf95] = true; + } else if (yamlConfig["Lora"]["Module"] && yamlConfig["Lora"]["Module"].as("") == "sx1280") { + settingsMap[use_sx1280] = true; + } else if (yamlConfig["Lora"]["Module"] && yamlConfig["Lora"]["Module"].as("") == "sx1268") { + settingsMap[use_sx1268] = true; + } + settingsMap[dio2_as_rf_switch] = yamlConfig["Lora"]["DIO2_AS_RF_SWITCH"].as(false); + settingsMap[dio3_tcxo_voltage] = yamlConfig["Lora"]["DIO3_TCXO_VOLTAGE"].as(false); + settingsMap[cs] = yamlConfig["Lora"]["CS"].as(RADIOLIB_NC); + settingsMap[irq] = yamlConfig["Lora"]["IRQ"].as(RADIOLIB_NC); + settingsMap[busy] = yamlConfig["Lora"]["Busy"].as(RADIOLIB_NC); + settingsMap[reset] = yamlConfig["Lora"]["Reset"].as(RADIOLIB_NC); + settingsMap[txen] = yamlConfig["Lora"]["TXen"].as(RADIOLIB_NC); + settingsMap[rxen] = yamlConfig["Lora"]["RXen"].as(RADIOLIB_NC); + settingsMap[sx126x_ant_sw] = yamlConfig["Lora"]["SX126X_ANT_SW"].as(RADIOLIB_NC); + settingsMap[gpiochip] = yamlConfig["Lora"]["gpiochip"].as(0); + settingsMap[ch341Quirk] = yamlConfig["Lora"]["ch341_quirk"].as(false); + settingsMap[spiSpeed] = yamlConfig["Lora"]["spiSpeed"].as(2000000); + + settingsStrings[spidev] = "/dev/" + yamlConfig["Lora"]["spidev"].as("spidev0.0"); + if (settingsStrings[spidev].length() == 14) { + int x = settingsStrings[spidev].at(11) - '0'; + int y = settingsStrings[spidev].at(13) - '0'; + if (x >= 0 && x < 10 && y >= 0 && y < 10) { + settingsMap[spidev] = x + y << 4; + settingsMap[displayspidev] = settingsMap[spidev]; + settingsMap[touchscreenspidev] = settingsMap[spidev]; + } + } + } + if (yamlConfig["GPIO"]) { + settingsMap[user] = yamlConfig["GPIO"]["User"].as(RADIOLIB_NC); + } + if (yamlConfig["GPS"]) { + std::string serialPath = yamlConfig["GPS"]["SerialPath"].as(""); + if (serialPath != "") { + Serial1.setPath(serialPath); + settingsMap[has_gps] = 1; + } + } + if (yamlConfig["I2C"]) { + settingsStrings[i2cdev] = yamlConfig["I2C"]["I2CDevice"].as(""); + } + if (yamlConfig["Display"]) { + if (yamlConfig["Display"]["Panel"].as("") == "ST7789") + settingsMap[displayPanel] = st7789; + else if (yamlConfig["Display"]["Panel"].as("") == "ST7735") + settingsMap[displayPanel] = st7735; + else if (yamlConfig["Display"]["Panel"].as("") == "ST7735S") + settingsMap[displayPanel] = st7735s; + else if (yamlConfig["Display"]["Panel"].as("") == "ST7796") + settingsMap[displayPanel] = st7796; + else if (yamlConfig["Display"]["Panel"].as("") == "ILI9341") + settingsMap[displayPanel] = ili9341; + else if (yamlConfig["Display"]["Panel"].as("") == "ILI9342") + settingsMap[displayPanel] = ili9342; + else if (yamlConfig["Display"]["Panel"].as("") == "ILI9488") + settingsMap[displayPanel] = ili9488; + else if (yamlConfig["Display"]["Panel"].as("") == "HX8357D") + settingsMap[displayPanel] = hx8357d; + else if (yamlConfig["Display"]["Panel"].as("") == "X11") + settingsMap[displayPanel] = x11; + settingsMap[displayHeight] = yamlConfig["Display"]["Height"].as(0); + settingsMap[displayWidth] = yamlConfig["Display"]["Width"].as(0); + settingsMap[displayDC] = yamlConfig["Display"]["DC"].as(-1); + settingsMap[displayCS] = yamlConfig["Display"]["CS"].as(-1); + settingsMap[displayRGBOrder] = yamlConfig["Display"]["RGBOrder"].as(false); + settingsMap[displayBacklight] = yamlConfig["Display"]["Backlight"].as(-1); + settingsMap[displayBacklightInvert] = yamlConfig["Display"]["BacklightInvert"].as(false); + settingsMap[displayBacklightPWMChannel] = yamlConfig["Display"]["BacklightPWMChannel"].as(-1); + settingsMap[displayReset] = yamlConfig["Display"]["Reset"].as(-1); + settingsMap[displayOffsetX] = yamlConfig["Display"]["OffsetX"].as(0); + settingsMap[displayOffsetY] = yamlConfig["Display"]["OffsetY"].as(0); + settingsMap[displayRotate] = yamlConfig["Display"]["Rotate"].as(false); + settingsMap[displayOffsetRotate] = yamlConfig["Display"]["OffsetRotate"].as(1); + settingsMap[displayInvert] = yamlConfig["Display"]["Invert"].as(false); + settingsMap[displayBusFrequency] = yamlConfig["Display"]["BusFrequency"].as(40000000); + if (yamlConfig["Display"]["spidev"]) { + settingsStrings[displayspidev] = "/dev/" + yamlConfig["Display"]["spidev"].as("spidev0.1"); + if (settingsStrings[displayspidev].length() == 14) { + int x = settingsStrings[displayspidev].at(11) - '0'; + int y = settingsStrings[displayspidev].at(13) - '0'; + if (x >= 0 && x < 10 && y >= 0 && y < 10) { + settingsMap[displayspidev] = x + y << 4; + settingsMap[touchscreenspidev] = settingsMap[displayspidev]; + } + } + } + } + if (yamlConfig["Touchscreen"]) { + if (yamlConfig["Touchscreen"]["Module"].as("") == "XPT2046") + settingsMap[touchscreenModule] = xpt2046; + else if (yamlConfig["Touchscreen"]["Module"].as("") == "STMPE610") + settingsMap[touchscreenModule] = stmpe610; + else if (yamlConfig["Touchscreen"]["Module"].as("") == "GT911") + settingsMap[touchscreenModule] = gt911; + else if (yamlConfig["Touchscreen"]["Module"].as("") == "FT5x06") + settingsMap[touchscreenModule] = ft5x06; + settingsMap[touchscreenCS] = yamlConfig["Touchscreen"]["CS"].as(-1); + settingsMap[touchscreenIRQ] = yamlConfig["Touchscreen"]["IRQ"].as(-1); + settingsMap[touchscreenBusFrequency] = yamlConfig["Touchscreen"]["BusFrequency"].as(1000000); + settingsMap[touchscreenRotate] = yamlConfig["Touchscreen"]["Rotate"].as(-1); + settingsMap[touchscreenI2CAddr] = yamlConfig["Touchscreen"]["I2CAddr"].as(-1); + if (yamlConfig["Touchscreen"]["spidev"]) { + settingsStrings[touchscreenspidev] = "/dev/" + yamlConfig["Touchscreen"]["spidev"].as(""); + if (settingsStrings[touchscreenspidev].length() == 14) { + int x = settingsStrings[touchscreenspidev].at(11) - '0'; + int y = settingsStrings[touchscreenspidev].at(13) - '0'; + if (x >= 0 && x < 10 && y >= 0 && y < 10) { + settingsMap[touchscreenspidev] = x + y << 4; + } + } + } + } + if (yamlConfig["Input"]) { + settingsStrings[keyboardDevice] = (yamlConfig["Input"]["KeyboardDevice"]).as(""); + } + + if (yamlConfig["Webserver"]) { + settingsMap[webserverport] = (yamlConfig["Webserver"]["Port"]).as(-1); + settingsStrings[webserverrootpath] = (yamlConfig["Webserver"]["RootPath"]).as(""); + } + + if (yamlConfig["General"]) { + settingsMap[maxnodes] = (yamlConfig["General"]["MaxNodes"]).as(200); + settingsMap[maxtophone] = (yamlConfig["General"]["MaxMessageQueue"]).as(100); + settingsStrings[config_directory] = (yamlConfig["General"]["ConfigDirectory"]).as(""); + } + + } catch (YAML::Exception &e) { + std::cout << "*** Exception " << e.what() << std::endl; + return false; + } + return true; +} + +// https://stackoverflow.com/questions/874134/find-out-if-string-ends-with-another-string-in-c +static bool ends_with(std::string_view str, std::string_view suffix) +{ + return str.size() >= suffix.size() && str.compare(str.size() - suffix.size(), suffix.size(), suffix) == 0; } \ No newline at end of file diff --git a/src/platform/portduino/PortduinoGlue.h b/src/platform/portduino/PortduinoGlue.h index 8ee96717e..95d82c1a2 100644 --- a/src/platform/portduino/PortduinoGlue.h +++ b/src/platform/portduino/PortduinoGlue.h @@ -55,7 +55,8 @@ enum configNames { webserverrootpath, maxtophone, maxnodes, - ascii_logs + ascii_logs, + config_directory }; enum { no_screen, x11, st7789, st7735, st7735s, st7796, ili9341, ili9342, ili9488, hx8357d }; enum { no_touchscreen, xpt2046, stmpe610, gt911, ft5x06 }; @@ -64,4 +65,6 @@ enum { level_error, level_warn, level_info, level_debug, level_trace }; extern std::map settingsMap; extern std::map settingsStrings; extern std::ofstream traceFile; -int initGPIOPin(int pinNum, std::string gpioChipname); \ No newline at end of file +int initGPIOPin(int pinNum, std::string gpioChipname); +bool loadConfig(const char *configPath); +static bool ends_with(std::string_view str, std::string_view suffix); \ No newline at end of file diff --git a/src/platform/portduino/SimRadio.cpp b/src/platform/portduino/SimRadio.cpp index 12757fe6b..0a77b6088 100644 --- a/src/platform/portduino/SimRadio.cpp +++ b/src/platform/portduino/SimRadio.cpp @@ -22,7 +22,7 @@ ErrorCode SimRadio::send(meshtastic_MeshPacket *p) // set (random) transmit delay to let others reconfigure their radio, // to avoid collisions and implement timing-based flooding - LOG_DEBUG("Set random delay before transmitting.\n"); + LOG_DEBUG("Set random delay before tx"); setTransmitDelay(); return res; } @@ -42,7 +42,7 @@ void SimRadio::setTransmitDelay() startTransmitTimer(true); } else { // If there is a SNR, start a timer scaled based on that SNR. - LOG_DEBUG("rx_snr found. hop_limit:%d rx_snr:%f\n", p->hop_limit, p->rx_snr); + LOG_DEBUG("rx_snr found. hop_limit:%d rx_snr:%f", p->hop_limit, p->rx_snr); startTransmitTimerSNR(p->rx_snr); } } @@ -52,7 +52,7 @@ void SimRadio::startTransmitTimer(bool withDelay) // If we have work to do and the timer wasn't already scheduled, schedule it now if (!txQueue.empty()) { uint32_t delayMsec = !withDelay ? 1 : getTxDelayMsec(); - // LOG_DEBUG("xmit timer %d\n", delay); + // LOG_DEBUG("xmit timer %d", delay); notifyLater(delayMsec, TRANSMIT_DELAY_COMPLETED, false); } } @@ -62,7 +62,7 @@ void SimRadio::startTransmitTimerSNR(float snr) // If we have work to do and the timer wasn't already scheduled, schedule it now if (!txQueue.empty()) { uint32_t delayMsec = getTxDelayMsecWeighted(snr); - // LOG_DEBUG("xmit timer %d\n", delay); + // LOG_DEBUG("xmit timer %d", delay); notifyLater(delayMsec, TRANSMIT_DELAY_COMPLETED, false); } } @@ -88,7 +88,7 @@ void SimRadio::completeSending() // We are done sending that packet, release it packetPool.release(p); - // LOG_DEBUG("Done with send\n"); + // LOG_DEBUG("Done with send"); } } @@ -103,9 +103,9 @@ bool SimRadio::canSendImmediately() if (busyTx || busyRx) { if (busyTx) - LOG_WARN("Can not send yet, busyTx\n"); + LOG_WARN("Can not send yet, busyTx"); if (busyRx) - LOG_WARN("Can not send yet, busyRx\n"); + LOG_WARN("Can not send yet, busyRx"); return false; } else return true; @@ -129,7 +129,7 @@ bool SimRadio::cancelSending(NodeNum from, PacketId id) packetPool.release(p); // free the packet we just removed bool result = (p != NULL); - LOG_DEBUG("cancelSending id=0x%x, removed=%d\n", id, result); + LOG_DEBUG("cancelSending id=0x%x, removed=%d", id, result); return result; } @@ -138,25 +138,25 @@ void SimRadio::onNotify(uint32_t notification) switch (notification) { case ISR_TX: handleTransmitInterrupt(); - // LOG_DEBUG("tx complete - starting timer\n"); + // LOG_DEBUG("tx complete - starting timer"); startTransmitTimer(); break; case ISR_RX: - // LOG_DEBUG("rx complete - starting timer\n"); + // LOG_DEBUG("rx complete - starting timer"); startTransmitTimer(); break; case TRANSMIT_DELAY_COMPLETED: - LOG_DEBUG("delay done\n"); + LOG_DEBUG("delay done"); // If we are not currently in receive mode, then restart the random delay (this can happen if the main thread // has placed the unit into standby) FIXME, how will this work if the chipset is in sleep mode? if (!txQueue.empty()) { if (!canSendImmediately()) { - // LOG_DEBUG("Currently Rx/Tx-ing: set random delay\n"); + // LOG_DEBUG("Currently Rx/Tx-ing: set random delay"); setTransmitDelay(); // currently Rx/Tx-ing: reset random delay } else { if (isChannelActive()) { // check if there is currently a LoRa packet on the channel - // LOG_DEBUG("Channel is active: set random delay\n"); + // LOG_DEBUG("Channel is active: set random delay"); setTransmitDelay(); // reset random delay } else { // Send any outgoing packets we have ready @@ -171,7 +171,7 @@ void SimRadio::onNotify(uint32_t notification) } } } else { - // LOG_DEBUG("done with txqueue\n"); + // LOG_DEBUG("done with txqueue"); } break; default: @@ -182,18 +182,18 @@ void SimRadio::onNotify(uint32_t notification) /** start an immediate transmit */ void SimRadio::startSend(meshtastic_MeshPacket *txp) { - printPacket("Starting low level send", txp); + printPacket("Start low level send", txp); size_t numbytes = beginSending(txp); meshtastic_MeshPacket *p = packetPool.allocCopy(*txp); perhapsDecode(p); meshtastic_Compressed c = meshtastic_Compressed_init_default; c.portnum = p->decoded.portnum; - // LOG_DEBUG("Sending back to simulator with portNum %d\n", p->decoded.portnum); + // LOG_DEBUG("Send back to simulator with portNum %d", p->decoded.portnum); if (p->decoded.payload.size <= sizeof(c.data.bytes)) { memcpy(&c.data.bytes, p->decoded.payload.bytes, p->decoded.payload.size); c.data.size = p->decoded.payload.size; } else { - LOG_WARN("Payload size is larger than compressed message allows! Sending empty payload.\n"); + LOG_WARN("Payload size larger than compressed message allows! Send empty payload"); } p->decoded.payload.size = pb_encode_to_bytes(p->decoded.payload.bytes, sizeof(p->decoded.payload.bytes), &meshtastic_Compressed_msg, &c); @@ -225,11 +225,11 @@ meshtastic_QueueStatus SimRadio::getQueueStatus() void SimRadio::handleReceiveInterrupt(meshtastic_MeshPacket *p) { - LOG_DEBUG("HANDLE RECEIVE INTERRUPT\n"); + LOG_DEBUG("HANDLE RECEIVE INTERRUPT"); uint32_t xmitMsec; if (!isReceiving) { - LOG_DEBUG("*** WAS_ASSERT *** handleReceiveInterrupt called when not in receive mode\n"); + LOG_DEBUG("*** WAS_ASSERT *** handleReceiveInterrupt called when not in receive mode"); return; } @@ -238,7 +238,7 @@ void SimRadio::handleReceiveInterrupt(meshtastic_MeshPacket *p) // read the number of actually received bytes size_t length = getPacketLength(p); xmitMsec = getPacketTime(length); - // LOG_DEBUG("Payload size %d vs length (includes header) %d\n", p->decoded.payload.size, length); + // LOG_DEBUG("Payload size %d vs length (includes header) %d", p->decoded.payload.size, length); meshtastic_MeshPacket *mp = packetPool.allocCopy(*p); // keep a copy in packetPool @@ -265,4 +265,4 @@ int16_t SimRadio::readData(uint8_t *data, size_t len) } return state; -} \ No newline at end of file +} diff --git a/src/platform/rp2xx0/main-rp2xx0.cpp b/src/platform/rp2xx0/main-rp2xx0.cpp index 67bf1eb08..a46b0face 100644 --- a/src/platform/rp2xx0/main-rp2xx0.cpp +++ b/src/platform/rp2xx0/main-rp2xx0.cpp @@ -34,7 +34,7 @@ void epoch_to_datetime(time_t epoch, datetime_t *dt) void debug_date(datetime_t t) { - LOG_DEBUG("%d %d %d %d %d %d %d\n", t.year, t.month, t.day, t.hour, t.min, t.sec, t.dotw); + LOG_DEBUG("%d %d %d %d %d %d %d", t.year, t.month, t.day, t.hour, t.min, t.sec, t.dotw); uart_default_tx_wait_blocking(); } @@ -103,15 +103,15 @@ void rp2040Setup() uint f_clk_adc = frequency_count_khz(CLOCKS_FC0_SRC_VALUE_CLK_ADC); uint f_clk_rtc = frequency_count_khz(CLOCKS_FC0_SRC_VALUE_CLK_RTC); - LOG_INFO("Clock speed:\n"); - LOG_INFO("pll_sys = %dkHz\n", f_pll_sys); - LOG_INFO("pll_usb = %dkHz\n", f_pll_usb); - LOG_INFO("rosc = %dkHz\n", f_rosc); - LOG_INFO("clk_sys = %dkHz\n", f_clk_sys); - LOG_INFO("clk_peri = %dkHz\n", f_clk_peri); - LOG_INFO("clk_usb = %dkHz\n", f_clk_usb); - LOG_INFO("clk_adc = %dkHz\n", f_clk_adc); - LOG_INFO("clk_rtc = %dkHz\n", f_clk_rtc); + LOG_INFO("Clock speed:"); + LOG_INFO("pll_sys = %dkHz", f_pll_sys); + LOG_INFO("pll_usb = %dkHz", f_pll_usb); + LOG_INFO("rosc = %dkHz", f_rosc); + LOG_INFO("clk_sys = %dkHz", f_clk_sys); + LOG_INFO("clk_peri = %dkHz", f_clk_peri); + LOG_INFO("clk_usb = %dkHz", f_clk_usb); + LOG_INFO("clk_adc = %dkHz", f_clk_adc); + LOG_INFO("clk_rtc = %dkHz", f_clk_rtc); #endif } diff --git a/src/serialization/MeshPacketSerializer.cpp b/src/serialization/MeshPacketSerializer.cpp index ebcfaecab..b4603186b 100644 --- a/src/serialization/MeshPacketSerializer.cpp +++ b/src/serialization/MeshPacketSerializer.cpp @@ -11,6 +11,7 @@ #include "../mesh/generated/meshtastic/paxcount.pb.h" #endif #include "mesh/generated/meshtastic/remote_hardware.pb.h" +#include std::string MeshPacketSerializer::JsonSerialize(const meshtastic_MeshPacket *mp, bool shouldLog) { @@ -26,7 +27,7 @@ std::string MeshPacketSerializer::JsonSerialize(const meshtastic_MeshPacket *mp, msgType = "text"; // convert bytes to string if (shouldLog) - LOG_DEBUG("got text message of size %u\n", mp->decoded.payload.size); + LOG_DEBUG("got text message of size %u", mp->decoded.payload.size); char payloadStr[(mp->decoded.payload.size) + 1]; memcpy(payloadStr, mp->decoded.payload.bytes, mp->decoded.payload.size); @@ -35,7 +36,7 @@ std::string MeshPacketSerializer::JsonSerialize(const meshtastic_MeshPacket *mp, JSONValue *json_value = JSON::Parse(payloadStr); if (json_value != NULL) { if (shouldLog) - LOG_INFO("text message payload is of type json\n"); + LOG_INFO("text message payload is of type json"); // if it is, then we can just use the json object jsonObj["payload"] = json_value; @@ -43,7 +44,7 @@ std::string MeshPacketSerializer::JsonSerialize(const meshtastic_MeshPacket *mp, // if it isn't, then we need to create a json object // with the string as the value if (shouldLog) - LOG_INFO("text message payload is of type plaintext\n"); + LOG_INFO("text message payload is of type plaintext"); msgPayload["text"] = new JSONValue(payloadStr); jsonObj["payload"] = new JSONValue(msgPayload); @@ -77,6 +78,7 @@ std::string MeshPacketSerializer::JsonSerialize(const meshtastic_MeshPacket *mp, msgPayload["wind_direction"] = new JSONValue((uint)decoded->variant.environment_metrics.wind_direction); msgPayload["wind_gust"] = new JSONValue(decoded->variant.environment_metrics.wind_gust); msgPayload["wind_lull"] = new JSONValue(decoded->variant.environment_metrics.wind_lull); + msgPayload["radiation"] = new JSONValue(decoded->variant.environment_metrics.radiation); } else if (decoded->which_variant == meshtastic_Telemetry_air_quality_metrics_tag) { msgPayload["pm10"] = new JSONValue((unsigned int)decoded->variant.air_quality_metrics.pm10_standard); msgPayload["pm25"] = new JSONValue((unsigned int)decoded->variant.air_quality_metrics.pm25_standard); @@ -294,7 +296,7 @@ std::string MeshPacketSerializer::JsonSerialize(const meshtastic_MeshPacket *mp, break; } } else if (shouldLog) { - LOG_WARN("Couldn't convert encrypted payload of MeshPacket to JSON\n"); + LOG_WARN("Couldn't convert encrypted payload of MeshPacket to JSON"); } jsonObj["id"] = new JSONValue((unsigned int)mp->id); @@ -318,7 +320,7 @@ std::string MeshPacketSerializer::JsonSerialize(const meshtastic_MeshPacket *mp, std::string jsonStr = value->Stringify(); if (shouldLog) - LOG_INFO("serialized json message: %s\n", jsonStr.c_str()); + LOG_INFO("serialized json message: %s", jsonStr.c_str()); delete value; return jsonStr; diff --git a/src/serialization/MeshPacketSerializer.h b/src/serialization/MeshPacketSerializer.h index 989f30fc0..12efccb43 100644 --- a/src/serialization/MeshPacketSerializer.h +++ b/src/serialization/MeshPacketSerializer.h @@ -2,7 +2,7 @@ #include static const char hexChars[16] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'}; -static const char *errStr = "Error decoding protobuf for %s message!\n"; +static const char *errStr = "Error decoding proto for %s message!"; class MeshPacketSerializer { diff --git a/src/serialization/MeshPacketSerializer_nRF52.cpp b/src/serialization/MeshPacketSerializer_nRF52.cpp index cd3aa1630..89ecddfad 100644 --- a/src/serialization/MeshPacketSerializer_nRF52.cpp +++ b/src/serialization/MeshPacketSerializer_nRF52.cpp @@ -28,7 +28,7 @@ std::string MeshPacketSerializer::JsonSerialize(const meshtastic_MeshPacket *mp, msgType = "text"; // convert bytes to string if (shouldLog) - LOG_DEBUG("got text message of size %u\n", mp->decoded.payload.size); + LOG_DEBUG("got text message of size %u", mp->decoded.payload.size); char payloadStr[(mp->decoded.payload.size) + 1]; memcpy(payloadStr, mp->decoded.payload.bytes, mp->decoded.payload.size); @@ -40,12 +40,12 @@ std::string MeshPacketSerializer::JsonSerialize(const meshtastic_MeshPacket *mp, // if it isn't, then we need to create a json object // with the string as the value if (shouldLog) - LOG_INFO("text message payload is of type plaintext\n"); + LOG_INFO("text message payload is of type plaintext"); jsonObj["payload"]["text"] = payloadStr; } else { // if it is, then we can just use the json object if (shouldLog) - LOG_INFO("text message payload is of type json\n"); + LOG_INFO("text message payload is of type json"); jsonObj["payload"] = text_doc; } break; @@ -77,6 +77,7 @@ std::string MeshPacketSerializer::JsonSerialize(const meshtastic_MeshPacket *mp, jsonObj["payload"]["wind_direction"] = (uint)decoded->variant.environment_metrics.wind_direction; jsonObj["payload"]["wind_gust"] = decoded->variant.environment_metrics.wind_gust; jsonObj["payload"]["wind_lull"] = decoded->variant.environment_metrics.wind_lull; + jsonObj["payload"]["radiation"] = decoded->variant.environment_metrics.radiation; } else if (decoded->which_variant == meshtastic_Telemetry_air_quality_metrics_tag) { jsonObj["payload"]["pm10"] = (unsigned int)decoded->variant.air_quality_metrics.pm10_standard; jsonObj["payload"]["pm25"] = (unsigned int)decoded->variant.air_quality_metrics.pm25_standard; @@ -93,7 +94,7 @@ std::string MeshPacketSerializer::JsonSerialize(const meshtastic_MeshPacket *mp, jsonObj["payload"]["current_ch3"] = decoded->variant.power_metrics.ch3_current; } } else if (shouldLog) { - LOG_ERROR("Error decoding protobuf for telemetry message!\n"); + LOG_ERROR("Error decoding proto for telemetry message!"); return ""; } break; @@ -111,7 +112,7 @@ std::string MeshPacketSerializer::JsonSerialize(const meshtastic_MeshPacket *mp, jsonObj["payload"]["hardware"] = decoded->hw_model; jsonObj["payload"]["role"] = (int)decoded->role; } else if (shouldLog) { - LOG_ERROR("Error decoding protobuf for nodeinfo message!\n"); + LOG_ERROR("Error decoding proto for nodeinfo message!"); return ""; } break; @@ -156,7 +157,7 @@ std::string MeshPacketSerializer::JsonSerialize(const meshtastic_MeshPacket *mp, jsonObj["payload"]["precision_bits"] = (int)decoded->precision_bits; } } else if (shouldLog) { - LOG_ERROR("Error decoding protobuf for position message!\n"); + LOG_ERROR("Error decoding proto for position message!"); return ""; } break; @@ -176,7 +177,7 @@ std::string MeshPacketSerializer::JsonSerialize(const meshtastic_MeshPacket *mp, jsonObj["payload"]["latitude_i"] = (int)decoded->latitude_i; jsonObj["payload"]["longitude_i"] = (int)decoded->longitude_i; } else if (shouldLog) { - LOG_ERROR("Error decoding protobuf for position message!\n"); + LOG_ERROR("Error decoding proto for position message!"); return ""; } break; @@ -207,7 +208,7 @@ std::string MeshPacketSerializer::JsonSerialize(const meshtastic_MeshPacket *mp, neighbors.remove(0); jsonObj["payload"]["neighbors"] = neighbors; } else if (shouldLog) { - LOG_ERROR("Error decoding protobuf for neighborinfo message!\n"); + LOG_ERROR("Error decoding proto for neighborinfo message!"); return ""; } break; @@ -241,7 +242,7 @@ std::string MeshPacketSerializer::JsonSerialize(const meshtastic_MeshPacket *mp, jsonObj["payload"]["route"] = route; } else if (shouldLog) { - LOG_ERROR("Error decoding protobuf for traceroute message!\n"); + LOG_ERROR("Error decoding proto for traceroute message!"); return ""; } } else { @@ -274,19 +275,19 @@ std::string MeshPacketSerializer::JsonSerialize(const meshtastic_MeshPacket *mp, jsonObj["payload"]["gpio_mask"] = (unsigned int)decoded->gpio_mask; } } else if (shouldLog) { - LOG_ERROR("Error decoding protobuf for RemoteHardware message!\n"); + LOG_ERROR("Error decoding proto for RemoteHardware message!"); return ""; } break; } // add more packet types here if needed default: - LOG_WARN("Unsupported packet type %d\n", mp->decoded.portnum); + LOG_WARN("Unsupported packet type %d", mp->decoded.portnum); return ""; break; } } else if (shouldLog) { - LOG_WARN("Couldn't convert encrypted payload of MeshPacket to JSON\n"); + LOG_WARN("Couldn't convert encrypted payload of MeshPacket to JSON"); return ""; } @@ -308,7 +309,7 @@ std::string MeshPacketSerializer::JsonSerialize(const meshtastic_MeshPacket *mp, // serialize and write it to the stream - // Serial.printf("serialized json message: \r\n"); + // Serial.printf("serialized json message: \r"); // serializeJson(jsonObj, Serial); // Serial.println(""); @@ -316,7 +317,7 @@ std::string MeshPacketSerializer::JsonSerialize(const meshtastic_MeshPacket *mp, serializeJson(jsonObj, jsonStr); if (shouldLog) - LOG_INFO("serialized json message: %s\n", jsonStr.c_str()); + LOG_INFO("serialized json message: %s", jsonStr.c_str()); return jsonStr; } diff --git a/src/shutdown.h b/src/shutdown.h index 481e7778d..9e30e772c 100644 --- a/src/shutdown.h +++ b/src/shutdown.h @@ -12,7 +12,7 @@ void powerCommandsCheck() { if (rebootAtMsec && millis() > rebootAtMsec) { - LOG_INFO("Rebooting\n"); + LOG_INFO("Rebooting"); #if defined(ARCH_ESP32) ESP.restart(); #elif defined(ARCH_NRF52) @@ -28,11 +28,11 @@ void powerCommandsCheck() Serial1.end(); if (screen) delete screen; - LOG_DEBUG("final reboot!\n"); + LOG_DEBUG("final reboot!"); reboot(); #else rebootAtMsec = -1; - LOG_WARN("FIXME implement reboot for this platform. Note that some settings require a restart to be applied.\n"); + LOG_WARN("FIXME implement reboot for this platform. Note that some settings require a restart to be applied"); #endif } @@ -43,7 +43,7 @@ void powerCommandsCheck() #endif if (shutdownAtMsec && millis() > shutdownAtMsec) { - LOG_INFO("Shutting down from admin command\n"); + LOG_INFO("Shut down from admin command"); #if defined(ARCH_NRF52) || defined(ARCH_ESP32) || defined(ARCH_RP2040) playShutdownMelody(); power->shutdown(); diff --git a/src/sleep.cpp b/src/sleep.cpp index 60f83f537..69eb0349a 100644 --- a/src/sleep.cpp +++ b/src/sleep.cpp @@ -71,7 +71,7 @@ void setCPUFast(bool on) * (Added: Dec 23, 2021 by Jm Casler) */ #ifndef CONFIG_IDF_TARGET_ESP32C3 - LOG_DEBUG("Setting CPU to 240MHz because WiFi is in use.\n"); + LOG_DEBUG("Set CPU to 240MHz because WiFi is in use"); setCpuFrequencyMhz(240); #endif return; @@ -134,13 +134,13 @@ void initDeepSleep() if (hwReason == TG1WDT_SYS_RESET) reason = "intWatchdog"; - LOG_INFO("Booted, wake cause %d (boot count %d), reset_reason=%s\n", wakeCause, bootCount, reason); + LOG_INFO("Booted, wake cause %d (boot count %d), reset_reason=%s", wakeCause, bootCount, reason); #endif #if SOC_RTCIO_HOLD_SUPPORTED // If waking from sleep, release any and all RTC GPIOs if (wakeCause != ESP_SLEEP_WAKEUP_UNDEFINED) { - LOG_DEBUG("Disabling any holds on RTC IO pads\n"); + LOG_DEBUG("Disable any holds on RTC IO pads"); for (uint8_t i = 0; i <= GPIO_NUM_MAX; i++) { if (rtc_gpio_is_valid_gpio((gpio_num_t)i)) rtc_gpio_hold_dis((gpio_num_t)i); @@ -187,12 +187,12 @@ static void waitEnterSleep(bool skipPreflight = false) notifySleep.notifyObservers(NULL); } -void doDeepSleep(uint32_t msecToWake, bool skipPreflight = false) +void doDeepSleep(uint32_t msecToWake, bool skipPreflight = false, bool skipSaveNodeDb = false) { if (INCLUDE_vTaskSuspend && (msecToWake == portMAX_DELAY)) { - LOG_INFO("Entering deep sleep forever\n"); + LOG_INFO("Enter deep sleep forever"); } else { - LOG_INFO("Entering deep sleep for %u seconds\n", msecToWake / 1000); + LOG_INFO("Enter deep sleep for %u seconds", msecToWake / 1000); } // not using wifi yet, but once we are this is needed to shutoff the radio hw @@ -219,7 +219,9 @@ void doDeepSleep(uint32_t msecToWake, bool skipPreflight = false) screen->doDeepSleep(); // datasheet says this will draw only 10ua - nodeDB->saveToDisk(); + if (!skipSaveNodeDb) { + nodeDB->saveToDisk(); + } #ifdef PIN_POWER_EN pinMode(PIN_POWER_EN, INPUT); // power off peripherals @@ -287,7 +289,7 @@ void doDeepSleep(uint32_t msecToWake, bool skipPreflight = false) // No need to turn this off if the power draw in sleep mode really is just 0.2uA and turning it off would // leave floating input for the IRQ line // If we want to leave the radio receiving in would be 11.5mA current draw, but most of the time it is just waiting - // in its sequencer (true?) so the average power draw should be much lower even if we were listinging for packets + // in its sequencer (true?) so the average power draw should be much lower even if we were listening for packets // all the time. PMU->setChargingLedMode(XPOWERS_CHG_LED_OFF); @@ -305,7 +307,7 @@ void doDeepSleep(uint32_t msecToWake, bool skipPreflight = false) PMU->disablePowerOutput(XPOWERS_LDO2); // lora radio power channel } if (msecToWake == portMAX_DELAY) { - LOG_INFO("PMU shutdown.\n"); + LOG_INFO("PMU shutdown"); console->flush(); PMU->shutdown(); } @@ -332,7 +334,13 @@ void doDeepSleep(uint32_t msecToWake, bool skipPreflight = false) */ esp_sleep_wakeup_cause_t doLightSleep(uint64_t sleepMsec) // FIXME, use a more reasonable default { - // LOG_DEBUG("Enter light sleep\n"); + // LOG_DEBUG("Enter light sleep"); + + // LORA_DIO1 is an extended IO pin. Setting it as a wake-up pin will cause problems, such as the indicator device not entering + // LightSleep. +#if defined(SENSECAP_INDICATOR) + return ESP_SLEEP_WAKEUP_TIMER; +#endif waitEnterSleep(false); @@ -359,7 +367,7 @@ esp_sleep_wakeup_cause_t doLightSleep(uint64_t sleepMsec) // FIXME, use a more r // never tries to go to sleep if the user is using the API // gpio_wakeup_enable((gpio_num_t)SERIAL0_RX_GPIO, GPIO_INTR_LOW_LEVEL); - // doesn't help - I think the USB-UART chip losing power is pulling the signal llow + // doesn't help - I think the USB-UART chip losing power is pulling the signal low // gpio_pullup_en((gpio_num_t)SERIAL0_RX_GPIO); // alas - can only work if using the refclock, which is limited to about 9600 bps @@ -387,19 +395,19 @@ esp_sleep_wakeup_cause_t doLightSleep(uint64_t sleepMsec) // FIXME, use a more r #endif auto res = esp_sleep_enable_gpio_wakeup(); if (res != ESP_OK) { - LOG_ERROR("esp_sleep_enable_gpio_wakeup result %d\n", res); + LOG_ERROR("esp_sleep_enable_gpio_wakeup result %d", res); } assert(res == ESP_OK); res = esp_sleep_enable_timer_wakeup(sleepUsec); if (res != ESP_OK) { - LOG_ERROR("esp_sleep_enable_timer_wakeup result %d\n", res); + LOG_ERROR("esp_sleep_enable_timer_wakeup result %d", res); } assert(res == ESP_OK); console->flush(); res = esp_light_sleep_start(); if (res != ESP_OK) { - LOG_ERROR("esp_light_sleep_start result %d\n", res); + LOG_ERROR("esp_light_sleep_start result %d", res); } // commented out because it's not that crucial; // if it sporadically happens the node will go into light sleep during the next round @@ -429,12 +437,12 @@ esp_sleep_wakeup_cause_t doLightSleep(uint64_t sleepMsec) // FIXME, use a more r esp_sleep_wakeup_cause_t cause = esp_sleep_get_wakeup_cause(); #ifdef BUTTON_PIN if (cause == ESP_SLEEP_WAKEUP_GPIO) { - LOG_INFO("Exit light sleep gpio: btn=%d\n", + LOG_INFO("Exit light sleep gpio: btn=%d", !digitalRead(config.device.button_gpio ? config.device.button_gpio : BUTTON_PIN)); } else #endif { - LOG_INFO("Exit light sleep cause: %d\n", cause); + LOG_INFO("Exit light sleep cause: %d", cause); } return cause; @@ -470,7 +478,7 @@ void enableModemSleep() esp32_config.min_freq_mhz = 20; // 10Mhz is minimum recommended esp32_config.light_sleep_enable = false; int rv = esp_pm_configure(&esp32_config); - LOG_DEBUG("Sleep request result %x\n", rv); + LOG_DEBUG("Sleep request result %x", rv); } bool shouldLoraWake(uint32_t msecToWake) @@ -492,24 +500,24 @@ void enableLoraInterrupt() if (rtc_gpio_is_valid_gpio((gpio_num_t)LORA_DIO1)) { // Setup light/deep sleep with wakeup by external source - LOG_INFO("setup LORA_DIO1 (GPIO%02d) with wakeup by external source\n", LORA_DIO1); + LOG_INFO("setup LORA_DIO1 (GPIO%02d) with wakeup by external source", LORA_DIO1); esp_sleep_enable_ext0_wakeup((gpio_num_t)LORA_DIO1, HIGH); } else { - LOG_INFO("setup LORA_DIO1 (GPIO%02d) with wakeup by gpio interrupt\n", LORA_DIO1); + LOG_INFO("setup LORA_DIO1 (GPIO%02d) with wakeup by gpio interrupt", LORA_DIO1); gpio_wakeup_enable((gpio_num_t)LORA_DIO1, GPIO_INTR_HIGH_LEVEL); } #elif defined(LORA_DIO1) && (LORA_DIO1 != RADIOLIB_NC) if (radioType != RF95_RADIO) { - LOG_INFO("setup LORA_DIO1 (GPIO%02d) with wakeup by gpio interrupt\n", LORA_DIO1); + LOG_INFO("setup LORA_DIO1 (GPIO%02d) with wakeup by gpio interrupt", LORA_DIO1); gpio_wakeup_enable((gpio_num_t)LORA_DIO1, GPIO_INTR_HIGH_LEVEL); // SX126x/SX128x interrupt, active high } #endif #if defined(RF95_IRQ) && (RF95_IRQ != RADIOLIB_NC) if (radioType == RF95_RADIO) { - LOG_INFO("setup RF95_IRQ (GPIO%02d) with wakeup by gpio interrupt\n", RF95_IRQ); + LOG_INFO("setup RF95_IRQ (GPIO%02d) with wakeup by gpio interrupt", RF95_IRQ); gpio_wakeup_enable((gpio_num_t)RF95_IRQ, GPIO_INTR_HIGH_LEVEL); // RF95 interrupt, active high } #endif } -#endif \ No newline at end of file +#endif diff --git a/src/sleep.h b/src/sleep.h index 6ac420769..8d3cb17e8 100644 --- a/src/sleep.h +++ b/src/sleep.h @@ -4,7 +4,7 @@ #include "Observer.h" #include "configuration.h" -void doDeepSleep(uint32_t msecToWake, bool skipPreflight), cpuDeepSleep(uint32_t msecToWake); +void doDeepSleep(uint32_t msecToWake, bool skipPreflight, bool skipSaveNodeDb), cpuDeepSleep(uint32_t msecToWake); #ifdef ARCH_ESP32 #include "esp_sleep.h" diff --git a/src/xmodem.cpp b/src/xmodem.cpp index de73e8668..bf25e2da7 100644 --- a/src/xmodem.cpp +++ b/src/xmodem.cpp @@ -97,7 +97,7 @@ void XModemAdapter::sendControl(meshtastic_XModem_Control c) { xmodemStore = meshtastic_XModem_init_zero; xmodemStore.control = c; - LOG_DEBUG("XModem: Notify Sending control %d.\n", c); + LOG_DEBUG("XModem: Notify Send control %d", c); packetReady.notifyObservers(packetno); } @@ -131,7 +131,7 @@ void XModemAdapter::handlePacket(meshtastic_XModem xmodemPacket) isReceiving = false; break; } else { // Transmit this file from Flash - LOG_INFO("XModem: Transmitting file %s\n", filename); + LOG_INFO("XModem: Transmit file %s", filename); file = FSCom.open(filename, FILE_O_READ); if (file) { packetno = 1; @@ -141,7 +141,7 @@ void XModemAdapter::handlePacket(meshtastic_XModem xmodemPacket) xmodemStore.seq = packetno; xmodemStore.buffer.size = file.read(xmodemStore.buffer.bytes, sizeof(meshtastic_XModem_buffer_t::bytes)); xmodemStore.crc16 = crc16_ccitt(xmodemStore.buffer.bytes, xmodemStore.buffer.size); - LOG_DEBUG("XModem: STX Notify Sending packet %d, %d Bytes.\n", packetno, xmodemStore.buffer.size); + LOG_DEBUG("XModem: STX Notify Send packet %d, %d Bytes", packetno, xmodemStore.buffer.size); if (xmodemStore.buffer.size < sizeof(meshtastic_XModem_buffer_t::bytes)) { isEOT = true; // send EOT on next Ack @@ -196,7 +196,7 @@ void XModemAdapter::handlePacket(meshtastic_XModem xmodemPacket) if (isEOT) { sendControl(meshtastic_XModem_Control_EOT); file.close(); - LOG_INFO("XModem: Finished sending file %s\n", filename); + LOG_INFO("XModem: Finished send file %s", filename); isTransmitting = false; isEOT = false; break; @@ -208,7 +208,7 @@ void XModemAdapter::handlePacket(meshtastic_XModem xmodemPacket) xmodemStore.seq = packetno; xmodemStore.buffer.size = file.read(xmodemStore.buffer.bytes, sizeof(meshtastic_XModem_buffer_t::bytes)); xmodemStore.crc16 = crc16_ccitt(xmodemStore.buffer.bytes, xmodemStore.buffer.size); - LOG_DEBUG("XModem: ACK Notify Sending packet %d, %d Bytes.\n", packetno, xmodemStore.buffer.size); + LOG_DEBUG("XModem: ACK Notify Send packet %d, %d Bytes", packetno, xmodemStore.buffer.size); if (xmodemStore.buffer.size < sizeof(meshtastic_XModem_buffer_t::bytes)) { isEOT = true; // send EOT on next Ack @@ -225,7 +225,7 @@ void XModemAdapter::handlePacket(meshtastic_XModem xmodemPacket) if (--retrans <= 0) { sendControl(meshtastic_XModem_Control_CAN); file.close(); - LOG_INFO("XModem: Retransmit timeout, cancelling file %s\n", filename); + LOG_INFO("XModem: Retransmit timeout, cancel file %s", filename); isTransmitting = false; break; } @@ -235,7 +235,7 @@ void XModemAdapter::handlePacket(meshtastic_XModem xmodemPacket) file.seek((packetno - 1) * sizeof(meshtastic_XModem_buffer_t::bytes)); xmodemStore.buffer.size = file.read(xmodemStore.buffer.bytes, sizeof(meshtastic_XModem_buffer_t::bytes)); xmodemStore.crc16 = crc16_ccitt(xmodemStore.buffer.bytes, xmodemStore.buffer.size); - LOG_DEBUG("XModem: NAK Notify Sending packet %d, %d Bytes.\n", packetno, xmodemStore.buffer.size); + LOG_DEBUG("XModem: NAK Notify Send packet %d, %d Bytes", packetno, xmodemStore.buffer.size); if (xmodemStore.buffer.size < sizeof(meshtastic_XModem_buffer_t::bytes)) { isEOT = true; // send EOT on next Ack @@ -251,4 +251,4 @@ void XModemAdapter::handlePacket(meshtastic_XModem xmodemPacket) break; } } -#endif \ No newline at end of file +#endif diff --git a/userPrefs.h b/userPrefs.h index ed622bcfb..00f04149a 100644 --- a/userPrefs.h +++ b/userPrefs.h @@ -26,6 +26,8 @@ */ // #define USERPREFS_CHANNEL_0_NAME "DEFCONnect" // #define USERPREFS_CHANNEL_0_PRECISION 14 +// #define USERPREFS_CHANNEL_0_UPLINK_ENABLED true +// #define USERPREFS_CHANNEL_0_DOWNLINK_ENABLED true /* #define USERPREFS_CHANNEL_1_PSK \ { \ @@ -35,6 +37,8 @@ */ // #define USERPREFS_CHANNEL_1_NAME "REPLACEME" // #define USERPREFS_CHANNEL_1_PRECISION 14 +// #define USERPREFS_CHANNEL_1_UPLINK_ENABLED true +// #define USERPREFS_CHANNEL_1_DOWNLINK_ENABLED true /* #define USERPREFS_CHANNEL_2_PSK \ { \ @@ -44,6 +48,8 @@ */ // #define USERPREFS_CHANNEL_2_NAME "REPLACEME" // #define USERPREFS_CHANNEL_2_PRECISION 14 +// #define USERPREFS_CHANNEL_2_UPLINK_ENABLED true +// #define USERPREFS_CHANNEL_2_DOWNLINK_ENABLED true // #define USERPREFS_CONFIG_OWNER_LONG_NAME "My Long Name" // #define USERPREFS_CONFIG_OWNER_SHORT_NAME "MLN" @@ -62,10 +68,40 @@ static unsigned char icon_bits[] = { 0x98, 0x3F, 0xF0, 0x23, 0x00, 0xFC, 0x0F, 0xE0, 0x7F, 0x00, 0xFC, 0x03, 0x80, 0xFF, 0x01, 0xFC, 0x00, 0x00, 0x3E, 0x00, 0x70, 0x00, 0x00, 0x1C, 0x00, 0x70, 0x00, 0x00, 0x1C, 0x00, 0x70, 0x00, 0x00, 0x1C, 0x00, 0x70, 0x00, 0x00, 0x1C, 0x00}; */ + /* -#define USERPREFS_USE_ADMIN_KEY 1 -static unsigned char USERPREFS_ADMIN_KEY[] = {0xcd, 0xc0, 0xb4, 0x3c, 0x53, 0x24, 0xdf, 0x13, 0xca, 0x5a, 0xa6, - 0x0c, 0x0d, 0xec, 0x85, 0x5a, 0x4c, 0xf6, 0x1a, 0x96, 0x04, 0x1a, - 0x3e, 0xfc, 0xbb, 0x8e, 0x33, 0x71, 0xe5, 0xfc, 0xff, 0x3c}; + * PKI Admin keys. + * If a Admin key is set with '{};' + * then it will be ignored, a PKI key must have a size of 32 byte. + */ +/* +#define USERPREFS_USE_ADMIN_KEY_0 \ + { \ + 0xcd, 0xc0, 0xb4, 0x3c, 0x53, 0x24, 0xdf, 0x13, 0xca, 0x5a, 0xa6, 0x0c, 0x0d, 0xec, 0x85, 0x5a, 0x4c, 0xf6, 0x1a, 0x96, \ + 0x04, 0x1a, 0x3e, 0xfc, 0xbb, 0x8e, 0x33, 0x71, 0xe5, 0xfc, 0xff, 0x3c \ + }; */ +// #define USERPREFS_USE_ADMIN_KEY_1 {}; +// #define USERPREFS_USE_ADMIN_KEY_2 {}; + +/* + * USERPREF_FIXED_GPS_LAT and USERPREF_FIXED_GPS_LON must be set, USERPREF_FIXED_GPS_ALT is optional + * + * Fixed GPS is Eiffel Tower, Paris, France + */ +// #define USERPREFS_FIXED_GPS +// #define USERPREFS_FIXED_GPS_LAT 48.85873920 +// #define USERPREFS_FIXED_GPS_LON 2.294508368 +// #define USERPREFS_FIXED_GPS_ALT 0 + +/* + * Set Fixed Bluetooth paring code + */ +// #define USERPREFS_FIXED_BLUETOOTH 121212 + +/* + * Will overwrite BUTTON_PIN if set + */ +// #define USERPREFS_BUTTON_PIN 36 + #endif \ No newline at end of file diff --git a/variants/betafpv_2400_tx_micro/variant.h b/variants/betafpv_2400_tx_micro/variant.h index fd06183ee..67699e7c8 100644 --- a/variants/betafpv_2400_tx_micro/variant.h +++ b/variants/betafpv_2400_tx_micro/variant.h @@ -34,4 +34,4 @@ #define SX128X_TXEN 26 #define SX128X_RXEN 27 #define SX128X_RESET LORA_RESET -#define SX128X_MAX_POWER 13 \ No newline at end of file +#define SX128X_MAX_POWER 3 diff --git a/variants/chatter2/variant.h b/variants/chatter2/variant.h index 5c27e2fb5..ff4f87bbe 100644 --- a/variants/chatter2/variant.h +++ b/variants/chatter2/variant.h @@ -6,7 +6,6 @@ // Debugging // #define GPS_DEBUG -// #define GPS_EXTRAVERBOSE // Lora #define USE_LLCC68 // Original Chatter2 with LLCC68 module diff --git a/variants/diy/mesh-tab/variant.h b/variants/diy/mesh-tab/variant.h new file mode 100644 index 000000000..0a23a3c36 --- /dev/null +++ b/variants/diy/mesh-tab/variant.h @@ -0,0 +1,49 @@ +#ifndef _VARIANT_MESHTAB_DIY_ +#define _VARIANT_MESHTAB_DIY_ + +#define HAS_TOUCHSCREEN 1 + +#define SLEEP_TIME 120 + +// Analog pins +#define BATTERY_PIN 4 // A battery voltage measurement pin, voltage divider connected here to measure battery voltage +// ratio of voltage divider = 2.0 +#define ADC_MULTIPLIER 2.11 // 2.0 + 10% for correction of display undervoltage. +#define ADC_CHANNEL ADC1_GPIO4_CHANNEL + +// LED +#define LED_PIN 21 + +// Button +#define BUTTON_PIN 0 + +// GPS +#define GPS_RX_PIN 18 +#define GPS_TX_PIN 17 + +// #define HAS_SDCARD 1 +#define SPI_MOSI 13 +#define SPI_SCK 12 +#define SPI_MISO 11 +#define SPI_CS 10 +#define SDCARD_CS 6 + +// LORA SPI +#define LORA_SCK 36 +#define LORA_MISO 37 +#define LORA_MOSI 35 +#define LORA_CS 39 + +// LORA MODULES +#define USE_SX1262 + +// LORA CONFIG +#define SX126X_CS LORA_CS +#define SX126X_DIO1 15 +#define SX126X_DIO2_AS_RF_SWITCH +#define SX126X_BUSY 40 +#define SX126X_RESET 14 +#define SX126X_RXEN 47 +#define SX126X_TXEN RADIOLIB_NC // Assuming that DIO2 is connected to TXEN pin + +#endif diff --git a/variants/diy/nrf52_promicro_diy_tcxo/variant.h b/variants/diy/nrf52_promicro_diy_tcxo/variant.h index 05d4a088c..5c535ba1e 100644 --- a/variants/diy/nrf52_promicro_diy_tcxo/variant.h +++ b/variants/diy/nrf52_promicro_diy_tcxo/variant.h @@ -22,26 +22,26 @@ extern "C" { /* NRF52 PRO MICRO PIN ASSIGNMENT -| Pin | Function | | Pin | Function | -|-------|------------|---|---------|-------------| -| Gnd | | | vbat | | -| P0.06 | Serial2 RX | | vbat | | -| P0.08 | Serial2 TX | | Gnd | | -| Gnd | | | reset | | -| Gnd | | | ext_vcc | *see 0.13 | -| P0.17 | RXEN | | P0.31 | BATTERY_PIN | -| P0.20 | GPS_RX | | P0.29 | BUSY | -| P0.22 | GPS_TX | | P0.02 | MISO | -| P0.24 | GPS_EN | | P1.15 | MOSI | -| P1.00 | BUTTON_PIN | | P1.13 | CS | -| P0.11 | SCL | | P1.11 | SCK | -| P1.04 | SDA | | P0.10 | DIO1/IRQ | -| P1.06 | Free pin | | P0.09 | RESET | -| | | | | | -| | Mid board | | | Internal | -| P1.01 | Free pin | | 0.15 | LED | -| P1.02 | Free pin | | 0.13 | 3V3_EN | -| P1.07 | Free pin | | | | +| Pin   | Function   |   | Pin     | Function     | RF95 | +| ----- | ----------- | --- | -------- | ------------ | ----- | +| Gnd   |             |   | vbat     |             | | +| P0.06 | Serial2 RX |   | vbat     |             | | +| P0.08 | Serial2 TX |   | Gnd     |             | | +| Gnd   |             |   | reset   |             | | +| Gnd   |             |   | ext_vcc | *see 0.13   | | +| P0.17 | RXEN       |   | P0.31   | BATTERY_PIN | | +| P0.20 | GPS_RX     |   | P0.29   | BUSY         | DIO0 | +| P0.22 | GPS_TX     |   | P0.02   | MISO | MISO | +| P0.24 | GPS_EN     |   | P1.15   | MOSI         | MOSI | +| P1.00 | BUTTON_PIN |   | P1.13   | CS           | CS   | +| P0.11 | SCL         |   | P1.11   | SCK         | SCK | +| P1.04 | SDA         |   | P0.10   | DIO1/IRQ     | DIO1 | +| P1.06 | Free pin   |   | P0.09   | RESET       | RST | +|       |             |   |         |             | | +|       | Mid board   |   |         | Internal     | | +| P1.01 | Free pin   |   | 0.15     | LED         | | +| P1.02 | Free pin   |   | 0.13     | 3V3_EN       | | +| P1.07 | Free pin   |   |         |             | | */ // Number of pins defined in PinDescription array @@ -112,13 +112,28 @@ NRF52 PRO MICRO PIN ASSIGNMENT #define PIN_SPI_MOSI (32 + 15) // P1.15 #define PIN_SPI_SCK (32 + 11) // P1.11 +#define LORA_MISO PIN_SPI_MISO +#define LORA_MOSI PIN_SPI_MOSI +#define LORA_SCK PIN_SPI_SCK +#define LORA_CS (32 + 13) // P1.13 + // LORA MODULES #define USE_LLCC68 #define USE_SX1262 -// #define USE_RF95 +#define USE_RF95 #define USE_SX1268 -// LORA CONFIG +// RF95 CONFIG + +#define LORA_DIO0 (0 + 29) // P0.10 IRQ +#define LORA_DIO1 (0 + 10) // P0.10 IRQ +#define LORA_RESET (0 + 9) // P0.09 + +// RX/TX for RFM95/SX127x +#define RF95_RXEN (0 + 17) // P0.17 +#define RF95_TXEN RADIOLIB_NC // Assuming that DIO2 is connected to TXEN pin. If not, TXEN must be connected. + +// SX126X CONFIG #define SX126X_CS (32 + 13) // P1.13 FIXME - we really should define LORA_CS instead #define SX126X_DIO1 (0 + 10) // P0.10 IRQ #define SX126X_DIO2_AS_RF_SWITCH // Note for E22 modules: DIO2 is not attached internally to TXEN for automatic TX/RX switching, @@ -134,18 +149,21 @@ NRF52 PRO MICRO PIN ASSIGNMENT On the SX1262, DIO3 sets the voltage for an external TCXO, if one is present. If one is not present, use TCXO_OPTIONAL to try both settings. -| Mfr | Module | TCXO | RF Switch | Notes | -| ---------- | ---------------- | ---- | --------- | -------------------------------------------- | -| Ebyte | E22-900M22S | Yes | Ext | | -| Ebyte | E22-900MM22S | No | Ext | | -| Ebyte | E22-900M30S | Yes | Ext | | -| Ebyte | E22-900M33S | Yes | Ext | MAX_POWER must be set to 8 for this | -| Ebyte | E220-900M22S | No | Ext | LLCC68, looks like DIO3 not connected at all | -| AI-Thinker | RA-01SH | No | Int | | -| Heltec | HT-RA62 | Yes | Int | | -| NiceRF | Lora1262 | yes | Int | | -| Waveshare | Core1262-HF | yes | Ext | | -| Waveshare | LoRa Node Module | yes | Int | | +| Mfr | Module | TCXO | RF Switch | Notes | +| ------------ | ---------------- | ---- | --------- | ------------------------------------- | +| Ebyte | E22-900M22S | Yes | Ext | | +| Ebyte | E22-900MM22S | No | Ext | | +| Ebyte | E22-900M30S | Yes | Ext | | +| Ebyte | E22-900M33S | Yes | Ext | MAX_POWER must be set to 8 for this | +| Ebyte | E220-900M22S | No | Ext | LLCC68, looks like DIO3 not connected | +| AI-Thinker | RA-01SH | No | Int | SX1262 | +| Heltec | HT-RA62 | Yes | Int | | +| NiceRF | Lora1262 | yes | Int | | +| Waveshare | Core1262-HF | yes | Ext | | +| Waveshare | LoRa Node Module | yes | Int | | +| Seeed | Wio-SX1262 | yes | Int | Sooooo cute! | +| AI-Thinker | RA-02 | No | Int | SX1278 **433mhz band only** | +| RF Solutions | RFM95 | No | Int | Untested | */ diff --git a/variants/diy/platformio.ini b/variants/diy/platformio.ini index f3c22b7a8..00ff88da2 100644 --- a/variants/diy/platformio.ini +++ b/variants/diy/platformio.ini @@ -88,3 +88,73 @@ build_flags = -D ARDUINO_USB_MODE=0 -D ARDUINO_USB_CDC_ON_BOOT=1 -I variants/diy/t-energy-s3_e22 + +; esp32-s3 + ra-sh01 lora + 3.2" ILI9143 +[env:mesh-tab] +extends = esp32s3_base +board = um_feathers3 +board_level = extra +board_upload.flash_size = 16MB +board_build.partitions = default_16MB.csv +upload_protocol = esptool +build_flags = ${esp32s3_base.build_flags} + -D MESH_TAB + -D PRIVATE_HW + -D CONFIG_ARDUHAL_ESP_LOG + -D CONFIG_ARDUHAL_LOG_COLORS=1 + -D CONFIG_DISABLE_HAL_LOCKS=1 ; "feels" to be a bit more stable without locks + -D MESHTASTIC_EXCLUDE_CANNEDMESSAGES=1 + -D MESHTASTIC_EXCLUDE_INPUTBROKER=1 + -D MESHTASTIC_EXCLUDE_BLUETOOTH=1 + -D MESHTASTIC_EXCLUDE_WEBSERVER=1 + -D LV_LVGL_H_INCLUDE_SIMPLE + -D LV_CONF_INCLUDE_SIMPLE + -D LV_COMP_CONF_INCLUDE_SIMPLE + -D LV_USE_SYSMON=0 + -D LV_USE_PROFILER=0 + -D LV_USE_PERF_MONITOR=0 + -D LV_USE_MEM_MONITOR=0 + -D LV_USE_LOG=0 + -D LV_BUILD_TEST=0 + -D USE_LOG_DEBUG + -D LOG_DEBUG_INC=\"DebugConfiguration.h\" + -D RADIOLIB_SPI_PARANOID=0 + -D MAX_NUM_NODES=250 + -D MAX_THREADS=40 + -D HAS_SCREEN=0 + -D HAS_TFT=1 + -D RAM_SIZE=1024 + -D LGFX_DRIVER_TEMPLATE + -D LGFX_DRIVER=LGFX_GENERIC + -D LGFX_PANEL=ILI9341 + -D LGFX_OFFSET_ROTATION=1 + -D LGFX_TOUCH=XPT2046 + -D LGFX_PIN_SCK=12 + -D LGFX_PIN_MOSI=13 + -D LGFX_PIN_MISO=11 + -D LGFX_PIN_DC=16 + -D LGFX_PIN_CS=10 + -D LGFX_PIN_RST=-1 + -D LGFX_PIN_BL=42 + -D LGFX_TOUCH_INT=41 + -D LGFX_TOUCH_CS=7 + -D LGFX_TOUCH_CLK=12 + -D LGFX_TOUCH_DO=11 + -D LGFX_TOUCH_DIN=13 + -D LGFX_TOUCH_X_MIN=300 + -D LGFX_TOUCH_X_MAX=3900 + -D LGFX_TOUCH_Y_MIN=400 + -D LGFX_TOUCH_Y_MAX=3900 + -D VIEW_320x240 + -D USE_PACKET_API + -I lib/device-ui/generated/ui_320x240 + -I variants/diy/mesh-tab +build_src_filter = ${esp32_base.build_src_filter} + +<../lib/device-ui/generated/ui_320x240> + +<../lib/device-ui/resources> + +<../lib/device-ui/locale> + +<../lib/device-ui/source> +lib_deps = ${esp32_base.lib_deps} + lovyan03/LovyanGFX@^1.1.16 + earlephilhower/ESP8266Audio@^1.9.7 + earlephilhower/ESP8266SAM@^1.0.1 diff --git a/variants/dreamcatcher/platformio.ini b/variants/dreamcatcher/platformio.ini index 46f9b9871..c57849d96 100644 --- a/variants/dreamcatcher/platformio.ini +++ b/variants/dreamcatcher/platformio.ini @@ -11,7 +11,7 @@ build_flags = -DARDUINO_USB_CDC_ON_BOOT=1 lib_deps = ${esp32s3_base.lib_deps} - earlephilhower/ESP8266Audio@^1.9.7 + earlephilhower/ESP8266Audio@^1.9.9 earlephilhower/ESP8266SAM@^1.0.1 [env:dreamcatcher-2206] diff --git a/variants/dreamcatcher/variant.h b/variants/dreamcatcher/variant.h index eb95a95dd..7835979e1 100644 --- a/variants/dreamcatcher/variant.h +++ b/variants/dreamcatcher/variant.h @@ -60,6 +60,7 @@ #define DAC_I2S_BCK 21 #define DAC_I2S_WS 9 #define DAC_I2S_DOUT 48 +#define DAC_I2S_MCLK 44 #define BIAS_T_ENABLE 7 // needs to be low #define BIAS_T_VALUE 0 diff --git a/variants/heltec_capsule_sensor_v3/platformio.ini b/variants/heltec_capsule_sensor_v3/platformio.ini deleted file mode 100644 index f1aef925d..000000000 --- a/variants/heltec_capsule_sensor_v3/platformio.ini +++ /dev/null @@ -1,11 +0,0 @@ -[env:heltec_capsule_sensor_v3] -extends = esp32s3_base -board = heltec_wifi_lora_32_V3 -board_check = true - -build_flags = - ${esp32s3_base.build_flags} -I variants/heltec_capsule_sensor_v3 - -D HELTEC_CAPSULE_SENSOR_V3 - -D GPS_POWER_TOGGLE ; comment this line to disable triple press function on the user button to turn off gps entirely. - ;-D DEBUG_DISABLED ; uncomment this line to disable DEBUG output - diff --git a/variants/heltec_capsule_sensor_v3/variant.h b/variants/heltec_capsule_sensor_v3/variant.h deleted file mode 100644 index 415de0559..000000000 --- a/variants/heltec_capsule_sensor_v3/variant.h +++ /dev/null @@ -1,53 +0,0 @@ -#define LED_PIN 33 -#define LED_PIN2 34 -#define EXT_PWR_DETECT 35 - -#define BUTTON_PIN 18 - -#define BATTERY_PIN 7 // A battery voltage measurement pin, voltage divider connected here to measure battery voltage -#define ADC_CHANNEL ADC1_GPIO7_CHANNEL -#define ADC_ATTENUATION ADC_ATTEN_DB_2_5 // lower dB for high resistance voltage divider -#define ADC_MULTIPLIER (4.9 * 1.045) -#define ADC_CTRL 36 // active HIGH, powers the voltage divider. Only on 1.1 -#define ADC_CTRL_ENABLED HIGH - -#undef GPS_RX_PIN -#undef GPS_TX_PIN -#define GPS_RX_PIN 5 -#define GPS_TX_PIN 4 -#define PIN_GPS_RESET 3 -#define GPS_RESET_MODE LOW -#define PIN_GPS_PPS 1 -#define PIN_GPS_EN 21 -#define GPS_EN_ACTIVE HIGH - -#define USE_SX1262 -#define LORA_DIO0 -1 // a No connect on the SX1262 module -#define LORA_RESET 12 -#define LORA_DIO1 14 // SX1262 IRQ -#define LORA_DIO2 13 // SX1262 BUSY -#define LORA_DIO3 // Not connected on PCB, but internally on the TTGO SX1262, if DIO3 is high the TXCO is enabled - -#define LORA_SCK 9 -#define LORA_MISO 11 -#define LORA_MOSI 10 -#define LORA_CS 8 - -#define SX126X_CS LORA_CS -#define SX126X_DIO1 LORA_DIO1 -#define SX126X_BUSY LORA_DIO2 -#define SX126X_RESET LORA_RESET - -#define SX126X_DIO2_AS_RF_SWITCH -#define SX126X_DIO3_TCXO_VOLTAGE 1.8 - -#define I2C_SDA 1 -#define I2C_SCL 2 -#define HAS_SCREEN 0 -#define SENSOR_POWER_CTRL_PIN 21 -#define SENSOR_POWER_ON 1 - -#define PERIPHERAL_WARMUP_MS 100 -#define SENSOR_GPS_CONFLICT - -#define ESP32S3_WAKE_TYPE ESP_EXT1_WAKEUP_ANY_HIGH \ No newline at end of file diff --git a/variants/heltec_v2.1/platformio.ini b/variants/heltec_v2.1/platformio.ini index 5aa04fc58..ea2281911 100644 --- a/variants/heltec_v2.1/platformio.ini +++ b/variants/heltec_v2.1/platformio.ini @@ -1,4 +1,5 @@ [env:heltec-v2_1] +board_level = extra ;build_type = debug ; to make it possible to step through our jtag debugger extends = esp32_base board = heltec_wifi_lora_32_V2 diff --git a/variants/heltec_v2/platformio.ini b/variants/heltec_v2/platformio.ini index cee1537d0..c81bca8ba 100644 --- a/variants/heltec_v2/platformio.ini +++ b/variants/heltec_v2/platformio.ini @@ -1,5 +1,6 @@ [env:heltec-v2_0] -;build_type = debug ; to make it possible to step through our jtag debugger +;build_type = debug ; to make it possible to step through our jtag debugger +board_level = extra extends = esp32_base board = heltec_wifi_lora_32_V2 build_flags = diff --git a/variants/heltec_wireless_paper/variant.h b/variants/heltec_wireless_paper/variant.h index 520dcec9b..fe8f391df 100644 --- a/variants/heltec_wireless_paper/variant.h +++ b/variants/heltec_wireless_paper/variant.h @@ -32,6 +32,9 @@ #define HAS_32768HZ #define ADC_CTRL_ENABLED LOW +#define NO_EXT_GPIO 1 +#define NO_GPS 1 + // LoRa #define USE_SX1262 diff --git a/variants/heltec_wireless_paper_v1/platformio.ini b/variants/heltec_wireless_paper_v1/platformio.ini index 999f1586a..c94bcacca 100644 --- a/variants/heltec_wireless_paper_v1/platformio.ini +++ b/variants/heltec_wireless_paper_v1/platformio.ini @@ -1,5 +1,6 @@ [env:heltec-wireless-paper-v1_0] extends = esp32s3_base +board_level = extra board = heltec_wifi_lora_32_V3 build_flags = ${esp32s3_base.build_flags} diff --git a/variants/heltec_wireless_paper_v1/variant.h b/variants/heltec_wireless_paper_v1/variant.h index 520dcec9b..fe8f391df 100644 --- a/variants/heltec_wireless_paper_v1/variant.h +++ b/variants/heltec_wireless_paper_v1/variant.h @@ -32,6 +32,9 @@ #define HAS_32768HZ #define ADC_CTRL_ENABLED LOW +#define NO_EXT_GPIO 1 +#define NO_GPS 1 + // LoRa #define USE_SX1262 diff --git a/variants/heltec_wireless_tracker_V1_0/platformio.ini b/variants/heltec_wireless_tracker_V1_0/platformio.ini index 303e27dbf..0e48c72f2 100644 --- a/variants/heltec_wireless_tracker_V1_0/platformio.ini +++ b/variants/heltec_wireless_tracker_V1_0/platformio.ini @@ -1,14 +1,13 @@ [env:heltec-wireless-tracker-V1-0] extends = esp32s3_base +board_level = extra board = heltec_wireless_tracker upload_protocol = esptool - build_flags = ${esp32s3_base.build_flags} -I variants/heltec_wireless_tracker_V1_0 -D HELTEC_TRACKER_V1_0 -D GPS_POWER_TOGGLE ; comment this line to disable triple press function on the user button to turn off gps entirely. ;-D DEBUG_DISABLED ; uncomment this line to disable DEBUG output - lib_deps = ${esp32s3_base.lib_deps} lovyan03/LovyanGFX@^1.1.8 \ No newline at end of file diff --git a/variants/icarus/pins_arduino.h b/variants/icarus/pins_arduino.h new file mode 100644 index 000000000..12d72d6dd --- /dev/null +++ b/variants/icarus/pins_arduino.h @@ -0,0 +1,21 @@ +#ifndef Pins_Arduino_h +#define Pins_Arduino_h + +#include + +#define USB_VID 0x2886 +#define USB_PID 0x0059 + +// GPIO48 Reference: https://github.com/espressif/arduino-esp32/pull/8600 + +// The default Wire will be mapped to Screen and Sensors +static const uint8_t SDA = 8; +static const uint8_t SCL = 9; + +// Default SPI will be mapped to Radio +static const uint8_t MISO = 39; +static const uint8_t SCK = 21; +static const uint8_t MOSI = 38; +static const uint8_t SS = 17; + +#endif /* Pins_Arduino_h */ diff --git a/variants/icarus/platformio.ini b/variants/icarus/platformio.ini new file mode 100644 index 000000000..11f09cab4 --- /dev/null +++ b/variants/icarus/platformio.ini @@ -0,0 +1,19 @@ +[env:icarus] +extends = esp32s3_base +board = icarus +board_level = extra +board_check = true +board_build.mcu = esp32s3 +upload_protocol = esptool +upload_speed = 921600 +platform_packages = framework-arduinoespressif32@https://github.com/PowerFeather/powerfeather-meshtastic-arduino-lib/releases/download/2.0.16a/esp32-2.0.16.zip +lib_deps = + ${esp32s3_base.lib_deps} +build_unflags = + ${esp32s3_base.build_unflags} + -DARDUINO_USB_MODE=1 +build_flags = + ${esp32s3_base.build_flags} -D PRIVATE_HW -I variants/icarus + -DBOARD_HAS_PSRAM + + -DARDUINO_USB_MODE=0 diff --git a/variants/icarus/variant.h b/variants/icarus/variant.h new file mode 100644 index 000000000..c9c74b45a --- /dev/null +++ b/variants/icarus/variant.h @@ -0,0 +1,31 @@ +// Icarus has a 1.3 inch OLED Screen +#define SCREEN_SSD106 + +#define I2C_SDA 8 +#define I2C_SCL 9 + +#define I2C_SDA1 18 +#define I2C_SCL1 6 + +#define BUTTON_PIN 7 // Selection button + +// RA-01SH/HT-RA62 LORA module +#define USE_SX1262 + +#define LORA_MISO 39 +#define LORA_SCK 21 +#define LORA_MOSI 38 +#define LORA_CS 17 + +#define LORA_RESET 42 +#define LORA_DIO1 5 + +#ifdef USE_SX1262 +#define SX126X_CS LORA_CS +#define SX126X_DIO1 LORA_DIO1 +#define SX126X_BUSY 47 +#define SX126X_RESET LORA_RESET + +// DIO2 controlls an antenna switch +#define SX126X_DIO2_AS_RF_SWITCH +#endif diff --git a/variants/m5stack_cores3/pins_arduino.h b/variants/m5stack_cores3/pins_arduino.h new file mode 100644 index 000000000..78e936990 --- /dev/null +++ b/variants/m5stack_cores3/pins_arduino.h @@ -0,0 +1,63 @@ +#ifndef Pins_Arduino_h +#define Pins_Arduino_h + +#include "soc/soc_caps.h" +#include + +#define USB_VID 0x303a +#define USB_PID 0x1001 + +// Some boards have too low voltage on this pin (board design bug) +// Use different pin with 3V and connect with 48 +// and change this setup for the chosen pin (for example 38) +static const uint8_t LED_BUILTIN = SOC_GPIO_PIN_COUNT + 48; +#define BUILTIN_LED LED_BUILTIN // backward compatibility +#define LED_BUILTIN LED_BUILTIN +#define RGB_BUILTIN LED_BUILTIN +#define RGB_BRIGHTNESS 64 + +static const uint8_t TX = 43; +static const uint8_t RX = 44; + +static const uint8_t TXD2 = 17; +static const uint8_t RXD2 = 18; + +static const uint8_t SDA = 12; +static const uint8_t SCL = 11; + +static const uint8_t SS = 15; +static const uint8_t MOSI = 37; +static const uint8_t MISO = 35; +static const uint8_t SCK = 36; + +static const uint8_t G0 = 0; +static const uint8_t G1 = 1; +static const uint8_t G2 = 2; +static const uint8_t G3 = 3; +static const uint8_t G4 = 4; +static const uint8_t G5 = 5; +static const uint8_t G6 = 6; +static const uint8_t G7 = 7; +static const uint8_t G8 = 8; +static const uint8_t G9 = 9; +static const uint8_t G11 = 11; +static const uint8_t G12 = 12; +static const uint8_t G13 = 13; +static const uint8_t G14 = 14; +static const uint8_t G17 = 17; +static const uint8_t G18 = 18; +static const uint8_t G19 = 19; +static const uint8_t G20 = 20; +static const uint8_t G21 = 21; +static const uint8_t G33 = 33; +static const uint8_t G34 = 34; +static const uint8_t G35 = 35; +static const uint8_t G36 = 36; +static const uint8_t G37 = 37; +static const uint8_t G38 = 38; +static const uint8_t G45 = 45; +static const uint8_t G46 = 46; + +static const uint8_t ADC = 10; + +#endif /* Pins_Arduino_h */ diff --git a/variants/m5stack_cores3/platformio.ini b/variants/m5stack_cores3/platformio.ini new file mode 100644 index 000000000..fc73fabae --- /dev/null +++ b/variants/m5stack_cores3/platformio.ini @@ -0,0 +1,14 @@ +; M5stack CoreS3 +[env:m5stack-cores3] +extends = esp32s3_base +board = m5stack-cores3 +board_check = true +upload_protocol = esptool + +build_flags = ${esp32_base.build_flags} + -DPRIVATE_HW + -DM5STACK_CORES3 + -Ivariants/m5stack_cores3 + +lib_deps = + ${esp32_base.lib_deps} diff --git a/variants/m5stack_cores3/variant.h b/variants/m5stack_cores3/variant.h new file mode 100644 index 000000000..2ad4fcbdd --- /dev/null +++ b/variants/m5stack_cores3/variant.h @@ -0,0 +1,22 @@ +#define I2C_SDA 12 +#define I2C_SCL 11 + +#undef LORA_SCK +#undef LORA_MISO +#undef LORA_MOSI +#undef LORA_CS + +#define LORA_SCK 36 +#define LORA_MISO 35 +#define LORA_MOSI 37 +#define LORA_CS 6 // NSS + +#define USE_RF95 +#define LORA_DIO0 14 // IRQ +#define LORA_RESET 5 // RESET +#define LORA_RST 5 // RESET +#define LORA_IRQ 14 // DIO0 +#define LORA_DIO1 RADIOLIB_NC // Not really used +#define LORA_DIO2 RADIOLIB_NC // Not really used + +#define HAS_AXP2101 diff --git a/variants/monteops_hw1/platformio.ini b/variants/monteops_hw1/platformio.ini index 4b42dce39..eaa246526 100644 --- a/variants/monteops_hw1/platformio.ini +++ b/variants/monteops_hw1/platformio.ini @@ -12,4 +12,4 @@ lib_deps = https://github.com/RAKWireless/RAK13800-W5100S.git#1.0.2 debug_tool = jlink ; If not set we will default to uploading over serial (first it forces bootloader entry by talking 1200bps to cdcacm) -;upload_protocol = jlink \ No newline at end of file +;upload_protocol = jlink diff --git a/variants/portduino/platformio.ini b/variants/portduino/platformio.ini index 46417e388..aa11142f7 100644 --- a/variants/portduino/platformio.ini +++ b/variants/portduino/platformio.ini @@ -2,7 +2,8 @@ extends = portduino_base ; The pkg-config commands below optionally add link flags. ; the || : is just a "or run the null command" to avoid returning an error code -build_flags = ${portduino_base.build_flags} -O0 -I variants/portduino -I /usr/include +build_flags = ${portduino_base.build_flags} -O0 -I variants/portduino + -I /usr/include !pkg-config --libs libulfius --silence-errors || : !pkg-config --libs openssl --silence-errors || : board = cross_platform diff --git a/variants/radiomaster_900_bandit/platformio.ini b/variants/radiomaster_900_bandit/platformio.ini index 4ff8a6ea2..010791d8a 100644 --- a/variants/radiomaster_900_bandit/platformio.ini +++ b/variants/radiomaster_900_bandit/platformio.ini @@ -6,9 +6,11 @@ build_flags = -DRADIOMASTER_900_BANDIT -DVTABLES_IN_FLASH=1 -DCONFIG_DISABLE_HAL_LOCKS=1 + -DHAS_STK8XXX=1 -O2 -Ivariants/radiomaster_900_bandit board_build.f_cpu = 240000000L upload_protocol = esptool lib_deps = - ${esp32_base.lib_deps} \ No newline at end of file + ${esp32_base.lib_deps} + https://github.com/gjelsoe/STK8xxx-Accelerometer.git#v0.1.1 diff --git a/variants/rak11310/platformio.ini b/variants/rak11310/platformio.ini index c7b3504fe..923cedaa3 100644 --- a/variants/rak11310/platformio.ini +++ b/variants/rak11310/platformio.ini @@ -3,6 +3,7 @@ extends = rp2040_base board = wiscore_rak11300 upload_protocol = picotool # keep an old SDK to use less memory. +platform = https://github.com/maxgerhardt/platform-raspberrypi.git#v1.2.0-gcc12 platform_packages = framework-arduinopico@https://github.com/earlephilhower/arduino-pico.git#3.7.2 # add our variants files to the include and src paths @@ -13,5 +14,5 @@ build_flags = ${rp2040_base.build_flags} -L "${platformio.libdeps_dir}/${this.__env__}/bsec2/src/cortex-m0plus" lib_deps = ${rp2040_base.lib_deps} -debug_build_flags = ${rp2040_base.build_flags} +debug_build_flags = ${rp2040_base.build_flags}, -g debug_tool = cmsis-dap ; for e.g. Picotool \ No newline at end of file diff --git a/variants/rak10701/platformio.ini b/variants/rak_wismeshtap/platformio.ini similarity index 73% rename from variants/rak10701/platformio.ini rename to variants/rak_wismeshtap/platformio.ini index 4c9bf3b20..bcf46b90d 100644 --- a/variants/rak10701/platformio.ini +++ b/variants/rak_wismeshtap/platformio.ini @@ -1,15 +1,19 @@ ; The very slick RAK wireless RAK10701 Field Tester device. Note you will have to flash to Arduino bootloader to use this firmware. Be aware touch is not currently working. -[env:rak10701] +[env:rak_wismeshtap] extends = nrf52840_base -board_level = extra board = wiscore_rak4631 -build_flags = ${nrf52840_base.build_flags} -Ivariants/rak10701 -D RAK_4631 +build_flags = ${nrf52840_base.build_flags} -Ivariants/rak_wismeshtap -DWISMESH_TAP -DRAK_4631 -L "${platformio.libdeps_dir}/${this.__env__}/bsec2/src/cortex-m4/fpv4-sp-d16-hard" -DGPS_POWER_TOGGLE ; comment this line to disable triple press function on the user button to turn off gps entirely. -DEINK_DISPLAY_MODEL=GxEPD2_213_BN -DEINK_WIDTH=250 -DEINK_HEIGHT=122 -build_src_filter = ${nrf52_base.build_src_filter} +<../variants/rak10701> + + + + -DMESHTASTIC_EXCLUDE_WIFI=1 + -DMESHTASTIC_EXCLUDE_DETECTIONSENSOR=1 + -DMESHTASTIC_EXCLUDE_STOREFORWARD=1 + -DMESHTASTIC_EXCLUDE_POWER_TELEMETRY=1 + -DMESHTASTIC_EXCLUDE_ATAK=1 +build_src_filter = ${nrf52_base.build_src_filter} +<../variants/rak_wismeshtap> + + + lib_deps = ${nrf52840_base.lib_deps} ${networking_base.lib_deps} @@ -21,4 +25,4 @@ lib_deps = beegee-tokyo/RAK14014-FT6336U @ 1.0.1 debug_tool = jlink ; If not set we will default to uploading over serial (first it forces bootloader entry by talking 1200bps to cdcacm) -;upload_protocol = jlink \ No newline at end of file +;upload_protocol = jlink diff --git a/variants/rak10701/variant.cpp b/variants/rak_wismeshtap/variant.cpp similarity index 100% rename from variants/rak10701/variant.cpp rename to variants/rak_wismeshtap/variant.cpp diff --git a/variants/rak10701/variant.h b/variants/rak_wismeshtap/variant.h similarity index 98% rename from variants/rak10701/variant.h rename to variants/rak_wismeshtap/variant.h index c263796ee..c21a11ac1 100644 --- a/variants/rak10701/variant.h +++ b/variants/rak_wismeshtap/variant.h @@ -243,7 +243,7 @@ SO GPIO 39/TXEN MAY NOT BE DEFINED FOR SUCCESSFUL OPERATION OF THE SX1262 - TG // Therefore must be 1 to keep peripherals powered // Power is on the controllable 3V3_S rail // #define PIN_GPS_RESET (34) -#define PIN_GPS_EN PIN_3V3_EN +// #define PIN_GPS_EN PIN_3V3_EN #define PIN_GPS_PPS (17) // Pulse per second input from the GPS #define GPS_RX_PIN PIN_SERIAL1_RX @@ -271,13 +271,8 @@ SO GPIO 39/TXEN MAY NOT BE DEFINED FOR SUCCESSFUL OPERATION OF THE SX1262 - TG #define HAS_RTC 1 -#define HAS_ETHERNET 1 - #define RAK_4631 1 -#define PIN_ETHERNET_RESET 21 -#define PIN_ETHERNET_SS PIN_EINK_CS -#define ETH_SPI_PORT SPI1 #define AQ_SET_PIN 10 #ifdef __cplusplus @@ -311,7 +306,7 @@ SO GPIO 39/TXEN MAY NOT BE DEFINED FOR SUCCESSFUL OPERATION OF THE SX1262 - TG #define SCREEN_TOUCH_INT WB_IO6 #define CANNED_MESSAGE_MODULE_ENABLE 1 - +#define USE_VIRTUAL_KEYBOARD 1 /*---------------------------------------------------------------------------- * Arduino objects - C++ only *----------------------------------------------------------------------------*/ diff --git a/variants/rp2040-lora/platformio.ini b/variants/rp2040-lora/platformio.ini index 8499f6f3c..4c578fb2b 100644 --- a/variants/rp2040-lora/platformio.ini +++ b/variants/rp2040-lora/platformio.ini @@ -12,5 +12,5 @@ build_flags = ${rp2040_base.build_flags} -L "${platformio.libdeps_dir}/${this.__env__}/bsec2/src/cortex-m0plus" lib_deps = ${rp2040_base.lib_deps} -debug_build_flags = ${rp2040_base.build_flags} +debug_build_flags = ${rp2040_base.build_flags}, -g debug_tool = cmsis-dap ; for e.g. Picotool \ No newline at end of file diff --git a/variants/rp2040-lora/variant.h b/variants/rp2040-lora/variant.h index c93105f0e..f1826605f 100644 --- a/variants/rp2040-lora/variant.h +++ b/variants/rp2040-lora/variant.h @@ -54,7 +54,7 @@ #define SX126X_DIO1 LORA_DIO1 #define SX126X_BUSY LORA_BUSY #define SX126X_RESET LORA_RESET -#define SX126X_DIO2_AS_RF_SWITCH // Antenna switch CTRL -#define SX126X_POWER_EN LORA_DIO4 // Antenna switch !CTRL via GPIO17 +#define SX126X_DIO2_AS_RF_SWITCH // Antenna switch CTRL +#define SX126X_RXEN LORA_DIO4 // Antenna switch !CTRL via GPIO17 // #define SX126X_DIO3_TCXO_VOLTAGE 1.8 -#endif +#endif \ No newline at end of file diff --git a/variants/rpipico/platformio.ini b/variants/rpipico/platformio.ini index e4b9e479f..9c62ebcb5 100644 --- a/variants/rpipico/platformio.ini +++ b/variants/rpipico/platformio.ini @@ -12,5 +12,5 @@ build_flags = ${rp2040_base.build_flags} -L "${platformio.libdeps_dir}/${this.__env__}/bsec2/src/cortex-m0plus" lib_deps = ${rp2040_base.lib_deps} -debug_build_flags = ${rp2040_base.build_flags} +debug_build_flags = ${rp2040_base.build_flags}, -g debug_tool = cmsis-dap ; for e.g. Picotool \ No newline at end of file diff --git a/variants/rpipico2/platformio.ini b/variants/rpipico2/platformio.ini index a63414418..24714efd5 100644 --- a/variants/rpipico2/platformio.ini +++ b/variants/rpipico2/platformio.ini @@ -12,5 +12,5 @@ build_flags = ${rp2350_base.build_flags} -L "${platformio.libdeps_dir}/${this.__env__}/bsec2/src/cortex-m0plus" lib_deps = ${rp2350_base.lib_deps} -debug_build_flags = ${rp2350_base.build_flags} +debug_build_flags = ${rp2350_base.build_flags}, -g debug_tool = cmsis-dap ; for e.g. Picotool \ No newline at end of file diff --git a/variants/rpipicow/platformio.ini b/variants/rpipicow/platformio.ini index 2600b4b38..7a43ece3b 100644 --- a/variants/rpipicow/platformio.ini +++ b/variants/rpipicow/platformio.ini @@ -14,5 +14,5 @@ build_src_filter = ${rp2040_base.build_src_filter} + lib_deps = ${rp2040_base.lib_deps} ${networking_base.lib_deps} -debug_build_flags = ${rp2040_base.build_flags} +debug_build_flags = ${rp2040_base.build_flags}, -g debug_tool = cmsis-dap ; for e.g. Picotool \ No newline at end of file diff --git a/variants/seeed-sensecap-indicator/variant.h b/variants/seeed-sensecap-indicator/variant.h index d7ed329eb..55895f353 100644 --- a/variants/seeed-sensecap-indicator/variant.h +++ b/variants/seeed-sensecap-indicator/variant.h @@ -36,12 +36,13 @@ #define TOUCH_I2C_PORT 0 #define TOUCH_SLAVE_ADDRESS 0x48 -// Buzzer -#define PIN_BUZZER 19 +// in future, we may want to add a buzzer and add all sensors to the indicator via a data protocol for now only GPS is supported +// // Buzzer +// #define PIN_BUZZER 19 -#define HAS_GPS 0 -#undef GPS_RX_PIN -#undef GPS_TX_PIN +#define GPS_RX_PIN 20 +#define GPS_TX_PIN 19 +#define HAS_GPS 1 #define USE_SX1262 #define USE_SX1268 @@ -62,3 +63,6 @@ #define SX126X_BUSY LORA_DIO2 #define SX126X_RESET LORA_RESET #define SX126X_DIO2_AS_RF_SWITCH + +#define USE_VIRTUAL_KEYBOARD 1 +#define DISPLAY_CLOCK_FRAME 1 diff --git a/variants/t-deck/platformio.ini b/variants/t-deck/platformio.ini index a63ff57a7..16769e2f2 100644 --- a/variants/t-deck/platformio.ini +++ b/variants/t-deck/platformio.ini @@ -15,5 +15,5 @@ build_flags = ${esp32_base.build_flags} lib_deps = ${esp32s3_base.lib_deps} lovyan03/LovyanGFX@^1.1.9 - earlephilhower/ESP8266Audio@^1.9.7 + earlephilhower/ESP8266Audio@^1.9.9 earlephilhower/ESP8266SAM@^1.0.1 \ No newline at end of file diff --git a/variants/t-deck/variant.h b/variants/t-deck/variant.h index 9860d608f..6d398391e 100644 --- a/variants/t-deck/variant.h +++ b/variants/t-deck/variant.h @@ -73,6 +73,7 @@ #define DAC_I2S_BCK 7 #define DAC_I2S_WS 5 #define DAC_I2S_DOUT 6 +#define DAC_I2S_MCLK 21 // GPIO lrck mic // LoRa #define USE_SX1262 diff --git a/variants/t-echo/variant.h b/variants/t-echo/variant.h index 9abb4ea69..365dfd804 100644 --- a/variants/t-echo/variant.h +++ b/variants/t-echo/variant.h @@ -216,6 +216,8 @@ External serial flash WP25R1635FZUIL0 #define VBAT_AR_INTERNAL AR_INTERNAL_3_0 #define ADC_MULTIPLIER (2.0F) +#define NO_EXT_GPIO 1 + #define HAS_RTC 1 #ifdef __cplusplus diff --git a/variants/t-watch-s3/platformio.ini b/variants/t-watch-s3/platformio.ini index 1f5fc278b..005c4d021 100644 --- a/variants/t-watch-s3/platformio.ini +++ b/variants/t-watch-s3/platformio.ini @@ -9,10 +9,12 @@ build_flags = ${esp32_base.build_flags} -DT_WATCH_S3 -Ivariants/t-watch-s3 -DPCF8563_RTC=0x51 + -DHAS_BMA423=1 lib_deps = ${esp32s3_base.lib_deps} lovyan03/LovyanGFX@^1.1.9 lewisxhe/PCF8563_Library@1.0.1 adafruit/Adafruit DRV2605 Library@^1.2.2 - earlephilhower/ESP8266Audio@^1.9.7 - earlephilhower/ESP8266SAM@^1.0.1 \ No newline at end of file + earlephilhower/ESP8266Audio@^1.9.9 + earlephilhower/ESP8266SAM@^1.0.1 + lewisxhe/SensorLib@0.2.0 diff --git a/variants/t-watch-s3/variant.h b/variants/t-watch-s3/variant.h index 9f939d859..5a6aebfa2 100644 --- a/variants/t-watch-s3/variant.h +++ b/variants/t-watch-s3/variant.h @@ -34,6 +34,7 @@ #define DAC_I2S_BCK 48 #define DAC_I2S_WS 15 #define DAC_I2S_DOUT 46 +#define DAC_I2S_MCLK 0 #define HAS_AXP2101 @@ -71,3 +72,6 @@ #define SX126X_DIO3_TCXO_VOLTAGE 1.8 // Internally the TTGO module hooks the SX1262-DIO2 in to control the TX/RX switch (which is the default for // the sx1262interface code) + +#define USE_VIRTUAL_KEYBOARD 1 +#define DISPLAY_CLOCK_FRAME 1 diff --git a/variants/tlora_v1/platformio.ini b/variants/tlora_v1/platformio.ini index c90daed90..65ec4bcdc 100644 --- a/variants/tlora_v1/platformio.ini +++ b/variants/tlora_v1/platformio.ini @@ -1,4 +1,5 @@ [env:tlora-v1] +board_level = extra extends = esp32_base board = ttgo-lora32-v1 build_flags = diff --git a/variants/tlora_v1_3/platformio.ini b/variants/tlora_v1_3/platformio.ini index 9d9f41a7c..99df28e56 100644 --- a/variants/tlora_v1_3/platformio.ini +++ b/variants/tlora_v1_3/platformio.ini @@ -1,4 +1,5 @@ [env:tlora_v1_3] +board_level = extra extends = esp32_base board = ttgo-lora32-v1 build_flags = diff --git a/variants/tlora_v2/platformio.ini b/variants/tlora_v2/platformio.ini index 8710068af..8087a30e3 100644 --- a/variants/tlora_v2/platformio.ini +++ b/variants/tlora_v2/platformio.ini @@ -1,4 +1,5 @@ [env:tlora-v2] +board_level = extra extends = esp32_base board = ttgo-lora32-v1 build_flags = diff --git a/variants/tracker-t1000-e/platformio.ini b/variants/tracker-t1000-e/platformio.ini index dfc72f3f0..075811610 100644 --- a/variants/tracker-t1000-e/platformio.ini +++ b/variants/tracker-t1000-e/platformio.ini @@ -1,16 +1,14 @@ -; tracker-t1000-e v0.9.1 [env:tracker-t1000-e] extends = nrf52840_base board = tracker-t1000-e -; board_level = extra -; platform = https://github.com/maxgerhardt/platform-nordicnrf52#cac6fcf943a41accd2aeb4f3659ae297a73f422e build_flags = ${nrf52840_base.build_flags} -Ivariants/tracker-t1000-e -Isrc/platform/nrf52/softdevice -Isrc/platform/nrf52/softdevice/nrf52 -DTRACKER_T1000_E -L "${platformio.libdeps_dir}/${this.__env__}/bsec2/src/cortex-m4/fpv4-sp-d16-hard" - -DGPS_POWER_TOGGLE ; comment this line to disable triple press function on the user button to turn off gps entirely. + -DGPS_POWER_TOGGLE board_build.ldscript = src/platform/nrf52/nrf52840_s140_v7.ld build_src_filter = ${nrf52_base.build_src_filter} +<../variants/tracker-t1000-e> lib_deps = ${nrf52840_base.lib_deps} + https://github.com/meshtastic/QMA6100P_Arduino_Library.git#14c900b8b2e4feaac5007a7e41e0c1b7f0841136 debug_tool = jlink ; If not set we will default to uploading over serial (first it forces bootloader entry by talking 1200bps to cdcacm) upload_protocol = nrfutil diff --git a/variants/tracker-t1000-e/variant.cpp b/variants/tracker-t1000-e/variant.cpp index 85e0c44f3..8096705d0 100644 --- a/variants/tracker-t1000-e/variant.cpp +++ b/variants/tracker-t1000-e/variant.cpp @@ -40,7 +40,7 @@ void initVariant() digitalWrite(PIN_3V3_EN, HIGH); pinMode(PIN_3V3_ACC_EN, OUTPUT); - digitalWrite(PIN_3V3_ACC_EN, LOW); + digitalWrite(PIN_3V3_ACC_EN, HIGH); pinMode(BUZZER_EN_PIN, OUTPUT); digitalWrite(BUZZER_EN_PIN, HIGH); diff --git a/variants/tracker-t1000-e/variant.h b/variants/tracker-t1000-e/variant.h index b8eb59b31..6a1f99600 100644 --- a/variants/tracker-t1000-e/variant.h +++ b/variants/tracker-t1000-e/variant.h @@ -52,7 +52,7 @@ extern "C" { #define LED_BLUE -1 // Actually green #define LED_STATE_ON 1 // State when LED is lit -#define BUTTON_PIN (0 + 6) // P0.6 +#define BUTTON_PIN (0 + 6) // P0.06 #define BUTTON_ACTIVE_LOW false #define BUTTON_ACTIVE_PULLUP false #define BUTTON_SENSE_TYPE 0x6 @@ -61,9 +61,11 @@ extern "C" { #define WIRE_INTERFACES_COUNT 1 -// unused pins -#define PIN_WIRE_SDA (0 + 9) // P0.26 -#define PIN_WIRE_SCL (0 + 10) // P0.27 +#define PIN_WIRE_SDA (0 + 26) // P0.26 +#define PIN_WIRE_SCL (0 + 27) // P0.27 +#define I2C_NO_RESCAN // I2C is a bit finicky, don't scan too much +#define HAS_QMA6100P // very rare beast, only on this board. +#define QMA_6100P_INT_PIN (32 + 2) // P1.02 /* * Serial interfaces @@ -116,14 +118,22 @@ extern "C" { #define PIN_GPS_RESET (32 + 15) // P1.15 #define GPS_RESET_MODE HIGH -#define GPS_VRTC_EN (0 + 8) // P0.8, awlays high -#define GPS_SLEEP_INT (32 + 12) // P1.12, awlays high +#define GPS_VRTC_EN (0 + 8) // P0.8, always high +#define GPS_SLEEP_INT (32 + 12) // P1.12, always high #define GPS_RTC_INT (0 + 15) // P0.15, normal is LOW, wake by HIGH -#define GPS_RESETB_OUT (32 + 14) // P1.14, awlays input pull_up +#define GPS_RESETB_OUT (32 + 14) // P1.14, always input pull_up #define GPS_FIX_HOLD_TIME 15000 // ms -#define BATTERY_PIN 2 +#define BATTERY_PIN 2 // P0.02/AIN0, BAT_ADC +#define BATTERY_IMMUTABLE #define ADC_MULTIPLIER (2.0F) +// P0.04/AIN2 is VCC_ADC, P0.05/AIN3 is CHARGER_DET, P1.03 is CHARGE_STA, P1.04 is CHARGE_DONE + +#define EXT_CHRG_DETECT (32 + 3) // P1.03 +#define EXT_CHRG_DETECT_VALUE LOW +// #define EXT_IS_CHRGD (32 + 4) // P1.04 +// #define EXT_IS_CHRGD_VALUE LOW +#define EXT_PWR_DETECT (0 + 5) // P0.05 #define ADC_RESOLUTION 14 #define BATTERY_SENSE_RESOLUTION_BITS 12 @@ -133,13 +143,13 @@ extern "C" { #define VBAT_AR_INTERNAL AR_INTERNAL_3_0 // Buzzer -#define BUZZER_EN_PIN (32 + 5) // P1.05, awlays high +#define BUZZER_EN_PIN (32 + 5) // P1.05, always high #define PIN_BUZZER (0 + 25) // P0.25, pwm output #define T1000X_SENSOR_EN #define T1000X_VCC_PIN (0 + 4) // P0.4 -#define T1000X_NTC_PIN (0 + 31) // P0.31 -#define T1000X_LUX_PIN (0 + 29) // P0.29 +#define T1000X_NTC_PIN (0 + 31) // P0.31/AIN7 +#define T1000X_LUX_PIN (0 + 29) // P0.29/AIN5 #ifdef __cplusplus } diff --git a/variants/unphone/pins_arduino.h b/variants/unphone/pins_arduino.h new file mode 100644 index 000000000..c4e9add1c --- /dev/null +++ b/variants/unphone/pins_arduino.h @@ -0,0 +1,67 @@ +#ifndef Pins_Arduino_h +#define Pins_Arduino_h + +#include + +#define USB_VID 0x16D0 +#define USB_PID 0x1178 + +#define EXTERNAL_NUM_INTERRUPTS 46 +#define NUM_DIGITAL_PINS 48 +#define NUM_ANALOG_INPUTS 20 + +#define analogInputToDigitalPin(p) (((p) < 20) ? (analogChannelToDigitalPin(p)) : -1) +#define digitalPinToInterrupt(p) (((p) < 48) ? (p) : -1) +#define digitalPinHasPWM(p) (p < 46) + +#define LED_BUILTIN 13 +#define BUILTIN_LED LED_BUILTIN // backward compatibility + +static const uint8_t TX = 43; +static const uint8_t RX = 44; + +static const uint8_t SDA = 3; +static const uint8_t SCL = 4; + +static const uint8_t SS = 13; +static const uint8_t MOSI = 40; +static const uint8_t MISO = 41; +static const uint8_t SCK = 39; + +static const uint8_t A0 = 1; +static const uint8_t A1 = 2; +static const uint8_t A2 = 8; +static const uint8_t A3 = 9; +static const uint8_t A4 = 5; +static const uint8_t A5 = 6; +static const uint8_t A6 = 14; +static const uint8_t A7 = 7; +static const uint8_t A8 = 15; +static const uint8_t A9 = 33; +static const uint8_t A10 = 27; +static const uint8_t A11 = 12; +static const uint8_t A12 = 13; +static const uint8_t A13 = 14; +static const uint8_t A14 = 15; +static const uint8_t A15 = 16; +static const uint8_t A16 = 17; +static const uint8_t A17 = 18; +static const uint8_t A18 = 19; +static const uint8_t A19 = 20; + +static const uint8_t T1 = 2; +static const uint8_t T2 = 8; +static const uint8_t T3 = 9; +static const uint8_t T4 = 5; +static const uint8_t T5 = 6; +static const uint8_t T6 = 14; +static const uint8_t T7 = 7; +static const uint8_t T8 = 15; +static const uint8_t T9 = 33; +static const uint8_t T10 = 27; +static const uint8_t T11 = 12; +static const uint8_t T12 = 13; +static const uint8_t T13 = 14; +static const uint8_t T14 = 15; + +#endif /* Pins_Arduino_h */ diff --git a/variants/unphone/platformio.ini b/variants/unphone/platformio.ini index dbfa0599d..489c70f99 100644 --- a/variants/unphone/platformio.ini +++ b/variants/unphone/platformio.ini @@ -13,7 +13,6 @@ build_unflags = -D ARDUINO_USB_MODE build_flags = ${esp32_base.build_flags} - ;-D BOARD_HAS_PSRAM // what's up with this - doesn't seem to be recognised at boot -D UNPHONE -I variants/unphone -D ARDUINO_USB_MODE=0 @@ -27,6 +26,52 @@ build_flags = ${esp32_base.build_flags} build_src_filter = ${esp32_base.build_src_filter} +<../variants/unphone> lib_deps = ${esp32s3_base.lib_deps} - lovyan03/LovyanGFX @ ^1.1.8 - https://gitlab.com/hamishcunningham/unphonelibrary#meshtastic @ ^9.0.0 - adafruit/Adafruit NeoPixel @ ^1.12.0 \ No newline at end of file + lovyan03/LovyanGFX@ 1.1.12 + https://gitlab.com/hamishcunningham/unphonelibrary#meshtastic@9.0.0 + adafruit/Adafruit NeoPixel @ ^1.12.0 + + +[env:unphone-tft] +extends = esp32s3_base +board_level = extra +board = unphone +board_build.partitions = default_8MB.csv +monitor_speed = 115200 +monitor_filters = esp32_exception_decoder +build_flags = ${esp32_base.build_flags} + -D UNPHONE + -D UNPHONE_ACCEL=0 + -D UNPHONE_TOUCHS=0 + -D UNPHONE_SDCARD=0 + -D UNPHONE_UI0=0 + -D UNPHONE_LORA=0 + -D UNPHONE_FACTORY_MODE=0 + -D MAX_THREADS=40 + -D HAS_SCREEN=0 + -D HAS_TFT=1 + -D RAM_SIZE=512 + -D LV_LVGL_H_INCLUDE_SIMPLE + -D LV_CONF_INCLUDE_SIMPLE + -D LV_COMP_CONF_INCLUDE_SIMPLE + -D LV_BUILD_TEST=0 + -D LV_USE_PERF_MONITOR=0 + -D LV_USE_MEM_MONITOR=0 + -D USE_LOG_DEBUG + -D LOG_DEBUG_INC=\"DebugConfiguration.h\" +; -D CALIBRATE_TOUCH=0 + -D LGFX_DRIVER=LGFX_UNPHONE_V9 + -D VIEW_320x240 +; -D USE_DOUBLE_BUFFER + -D USE_PACKET_API + -I lib/device-ui/generated/ui_320x240 + -I variants/unphone + +build_src_filter = ${esp32_base.build_src_filter} +<../variants/unphone> + +<../lib/device-ui/generated/ui_320x240> + +<../lib/device-ui/resources> + +<../lib/device-ui/source> + +lib_deps = ${esp32s3_base.lib_deps} + lovyan03/LovyanGFX@^1.1.12 + https://gitlab.com/hamishcunningham/unphonelibrary#meshtastic@9.0.0 + adafruit/Adafruit NeoPixel@1.12.0 \ No newline at end of file diff --git a/version.properties b/version.properties index be97dacb5..bed14bad5 100644 --- a/version.properties +++ b/version.properties @@ -1,4 +1,4 @@ [VERSION] major = 2 minor = 5 -build = 7 +build = 14