From b62bdbc46a9bcdaf10b8eeb04f00a7996ed2f397 Mon Sep 17 00:00:00 2001 From: Austin Date: Fri, 10 Jan 2025 20:03:29 -0500 Subject: [PATCH 01/89] meshtasticd-debian: Auto-Publish to OBS (#5791) --- .github/workflows/main_matrix.yml | 26 ++++++++- .github/workflows/nightly_debian.yml | 4 +- .github/workflows/package_obs.yml | 73 ++++++++++++++------------ .github/workflows/package_ppa.yml | 2 +- .github/workflows/release_channels.yml | 16 +++--- 5 files changed, 77 insertions(+), 44 deletions(-) diff --git a/.github/workflows/main_matrix.yml b/.github/workflows/main_matrix.yml index 20ffecb76..b55696301 100644 --- a/.github/workflows/main_matrix.yml +++ b/.github/workflows/main_matrix.yml @@ -137,6 +137,13 @@ jobs: package-native: uses: ./.github/workflows/package_amd64.yml + build-debian-src: + uses: ./.github/workflows/build_debian_src.yml + with: + series: UNRELEASED + build_location: local + secrets: inherit + test-native: uses: ./.github/workflows/test_native.yml @@ -260,6 +267,7 @@ jobs: package-raspbian, package-raspbian-armv7l, package-native, + build-debian-src, ] steps: - name: Checkout @@ -271,8 +279,12 @@ jobs: python-version: 3.x - name: Get release version string - run: echo "long=$(./bin/buildinfo.py long)" >> $GITHUB_OUTPUT + run: | + echo "long=$(./bin/buildinfo.py long)" >> $GITHUB_OUTPUT + echo "deb=$(./bin/buildinfo.py deb)" >> $GITHUB_OUTPUT id: version + env: + BUILD_LOCATION: local - name: Create release uses: softprops/action-gh-release@v2 @@ -292,6 +304,17 @@ jobs: merge-multiple: true path: ./output + - name: Download source deb + uses: actions/download-artifact@v4 + with: + pattern: firmware-debian-${{ steps.version.outputs.deb }}~UNRELEASED-src + merge-multiple: true + path: ./output/debian-src + + - name: Zip source deb + working-directory: output + run: zip -j -9 -r ./meshtasticd-${{ steps.version.outputs.deb }}-src.zip ./debian-src + # For diagnostics - name: Display structure of downloaded files run: ls -lR @@ -304,6 +327,7 @@ jobs: ./output/meshtasticd_${{ steps.version.outputs.long }}_arm64.deb ./output/meshtasticd_${{ steps.version.outputs.long }}_armhf.deb ./output/meshtasticd_${{ steps.version.outputs.long }}_amd64.deb + ./output/meshtasticd-${{ steps.version.outputs.deb }}-src.zip - name: Bump version.properties run: >- diff --git a/.github/workflows/nightly_debian.yml b/.github/workflows/nightly_debian.yml index 63d977beb..aa001854e 100644 --- a/.github/workflows/nightly_debian.yml +++ b/.github/workflows/nightly_debian.yml @@ -25,13 +25,13 @@ jobs: series: [plucky, oracular, noble, jammy] uses: ./.github/workflows/package_ppa.yml with: - ppa_repo: daily + ppa_repo: ppa:meshtastic/daily series: ${{ matrix.series }} secrets: inherit package-obs: uses: ./.github/workflows/package_obs.yml with: - obs_repo: meshtasticd + obs_project: home:meshtastic:daily series: unstable secrets: inherit diff --git a/.github/workflows/package_obs.yml b/.github/workflows/package_obs.yml index 11f2b87bf..336c27f4b 100644 --- a/.github/workflows/package_obs.yml +++ b/.github/workflows/package_obs.yml @@ -3,11 +3,15 @@ name: Package for OpenSUSE Build Service on: workflow_call: secrets: + OBS_USERNAME: + required: true + OBS_PASSWORD: + required: true PPA_GPG_PRIVATE_KEY: required: true inputs: - obs_repo: - description: Meshtastic OBS repo to target + obs_project: + description: Meshtastic OBS project to target required: true type: string series: @@ -64,40 +68,43 @@ jobs: - name: Display structure of downloaded files run: ls -lah - # - name: Configure osc - # shell: bash - # env: - # OBS_USERNAME: ${{ secrets.OBS_USERNAME }} - # OBS_PASSWORD: ${{ secrets.OBS_PASSWORD }} - # run: | - # mkdir -p ~/.config/osc - # echo -e "[https://api.opensuse.org]\n" > ~/.config/osc/oscrc - # echo -e "user = $OBS_USERNAME" >> ~/.config/osc/oscrc - # echo -e "pass = $OBS_PASSWORD" >> ~/.config/osc/oscrc - # echo -e "aliases = obs" >> ~/.config/osc/oscrc - # # Authenticate to OBS - # osc meta prj -v + - name: Configure osc + run: | + # Setup OpenSUSE Build Service credentials + mkdir -p ~/.config/osc + echo "[general]" > ~/.config/osc/oscrc + echo "apiurl=https://api.opensuse.org" >> ~/.config/osc/oscrc + echo "[https://api.opensuse.org]" >> ~/.config/osc/oscrc + echo "user=${{ secrets.OBS_USERNAME }}" >> ~/.config/osc/oscrc + echo "pass=${{ secrets.OBS_PASSWORD }}" >> ~/.config/osc/oscrc + echo "credentials_mgr_class=osc.credentials.PlaintextConfigFileCredentialsManager" >> ~/.config/osc/oscrc + # Create a temporary directory for osc checkout + mkdir -p osc - # - name: Upload Package to OBS - # shell: bash - # run: | - # # Define your OBS project and repository - # OBS_PROJECT="application:meshtastic" - # OBS_REPO="${{ inputs.obs_repo }}" + # Intentionally fail if credentials are invalid + # Update secrets if this returns `401` + - name: Verify OBS authentication + run: osc token - # # Create a temporary directory for osc - # mkdir -p /tmp/osc/$OBS_PROJECT/$OBS_REPO - # cd /tmp/osc/$OBS_PROJECT/$OBS_REPO + - name: Upload package to OBS + shell: bash + working-directory: osc + env: + OBS_PROJECT: ${{ inputs.obs_project }} + OBS_PACKAGE: meshtasticd + run: | + # Initialize the package in the current directory + osc checkout --output-dir . $OBS_PROJECT $OBS_PACKAGE - # # Initialize the package directory - # osc checkout $OBS_PROJECT $OBS_REPO + # Remove the existing package files + rm -rf *.dsc *.tar.xz - # # Copy package files to the osc directory - # cp $GITHUB_WORKSPACE/*.dsc . - # cp $GITHUB_WORKSPACE/*.tar.xz . + # Copy new package files to the directory + cp $GITHUB_WORKSPACE/*.dsc . + cp $GITHUB_WORKSPACE/*.tar.xz . - # # Add files to osc - # osc addremove + # Add/Remove the files + osc addremove - # # Commit and push the changes - # osc commit -m "Automated upload from GitHub Actions" + # Commit changes and push to OpenSUSE Build Service + osc commit -m "GitHub Actions: ${{ steps.version.outputs.deb }}~${{ inputs.series }}" diff --git a/.github/workflows/package_ppa.yml b/.github/workflows/package_ppa.yml index 340087d1d..a54b0bd36 100644 --- a/.github/workflows/package_ppa.yml +++ b/.github/workflows/package_ppa.yml @@ -71,4 +71,4 @@ jobs: - name: Publish with dput if: ${{ github.event_name != 'pull_request_target' && github.event_name != 'pull_request' }} run: | - dput ppa:meshtastic/${{ inputs.ppa_repo }} meshtasticd_${{ steps.version.outputs.deb }}~${{ inputs.series }}_source.changes + dput ${{ inputs.ppa_repo }} meshtasticd_${{ steps.version.outputs.deb }}~${{ inputs.series }}_source.changes diff --git a/.github/workflows/release_channels.yml b/.github/workflows/release_channels.yml index cdde74b2e..34673f0c7 100644 --- a/.github/workflows/release_channels.yml +++ b/.github/workflows/release_channels.yml @@ -15,13 +15,15 @@ jobs: uses: ./.github/workflows/package_ppa.yml with: ppa_repo: |- - ${{ contains(github.event.release.name, 'Beta') && 'beta' || contains(github.event.release.name, 'Alpha') && 'alpha' }} + ppa:meshtastic/${{ contains(github.event.release.name, 'Beta') && 'beta' || contains(github.event.release.name, 'Alpha') && 'alpha' }} series: ${{ matrix.series }} secrets: inherit - # package-obs: - # uses: ./.github/workflows/package_obs.yml - # with: - # obs_repo: meshtasticd - # series: ${{ contains(github.event.release.name, 'Beta') && 'beta' || contains(github.event.release.name, 'Alpha') && 'alpha' }} - # secrets: inherit + package-obs: + uses: ./.github/workflows/package_obs.yml + with: + obs_project: |- + home:meshtastic:${{ contains(github.event.release.name, 'Beta') && 'beta' || contains(github.event.release.name, 'Alpha') && 'alpha' }} + series: |- + ${{ contains(github.event.release.name, 'Beta') && 'beta' || contains(github.event.release.name, 'Alpha') && 'alpha' }} + secrets: inherit From c144ee77a7de8c7462128fba0f271bf10a108199 Mon Sep 17 00:00:00 2001 From: Austin Date: Fri, 10 Jan 2025 20:46:12 -0500 Subject: [PATCH 02/89] GitHub Actions: Fix `meshtastic` display issue in logs (#5811) --- .github/workflows/package_obs.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/package_obs.yml b/.github/workflows/package_obs.yml index 336c27f4b..275ffce0e 100644 --- a/.github/workflows/package_obs.yml +++ b/.github/workflows/package_obs.yml @@ -3,8 +3,6 @@ name: Package for OpenSUSE Build Service on: workflow_call: secrets: - OBS_USERNAME: - required: true OBS_PASSWORD: required: true PPA_GPG_PRIVATE_KEY: @@ -69,13 +67,15 @@ jobs: run: ls -lah - name: Configure osc + env: + OBS_USERNAME: meshtastic run: | # Setup OpenSUSE Build Service credentials mkdir -p ~/.config/osc echo "[general]" > ~/.config/osc/oscrc echo "apiurl=https://api.opensuse.org" >> ~/.config/osc/oscrc echo "[https://api.opensuse.org]" >> ~/.config/osc/oscrc - echo "user=${{ secrets.OBS_USERNAME }}" >> ~/.config/osc/oscrc + echo "user=${{ env.OBS_USERNAME }}" >> ~/.config/osc/oscrc echo "pass=${{ secrets.OBS_PASSWORD }}" >> ~/.config/osc/oscrc echo "credentials_mgr_class=osc.credentials.PlaintextConfigFileCredentialsManager" >> ~/.config/osc/oscrc # Create a temporary directory for osc checkout From 25a5f178e197dd0dede16063a7002c7768ad9318 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20G=C3=B6ttgens?= Date: Sat, 11 Jan 2025 03:43:48 +0100 Subject: [PATCH 03/89] remove ethernet code from this variant, remove unused radio chip code (#5810) * remove ethernet code from this variant, remove unused radio chip code * remove ethernet lib too, pin onewire library to release hash --- variants/rak2560/platformio.ini | 10 ++++++---- variants/rak2560/variant.h | 5 ----- 2 files changed, 6 insertions(+), 9 deletions(-) diff --git a/variants/rak2560/platformio.ini b/variants/rak2560/platformio.ini index 96c1bfa92..cd5225a22 100644 --- a/variants/rak2560/platformio.ini +++ b/variants/rak2560/platformio.ini @@ -1,4 +1,4 @@ -; The very slick RAK wireless RAK 4631 / 4630 board - Unified firmware for 5005/19003, with or without OLED RAK 1921 +; Firmware for the WisMesh HUB RAK2560, including a onewire module to talk to the RAK 9154 solar battery. [env:rak2560] extends = nrf52840_base board = wiscore_rak4631 @@ -6,16 +6,18 @@ board_check = true build_flags = ${nrf52840_base.build_flags} -Ivariants/rak2560 -D RAK_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. + -DRADIOLIB_EXCLUDE_SX128X=1 + -DRADIOLIB_EXCLUDE_SX127X=1 + -DRADIOLIB_EXCLUDE_LR11X0=1 -DHAS_RAKPROT=1 ; Define if RAk OneWireSerial is used (disables GPS) -build_src_filter = ${nrf52_base.build_src_filter} +<../variants/rak2560> + + + +build_src_filter = ${nrf52_base.build_src_filter} +<../variants/rak2560> + + lib_deps = ${nrf52840_base.lib_deps} ${networking_base.lib_deps} melopero/Melopero RV3028@^1.1.0 - https://github.com/RAKWireless/RAK13800-W5100S.git#1.0.2 rakwireless/RAKwireless NCP5623 RGB LED library@^1.0.2 beegee-tokyo/RAKwireless RAK12034@^1.0.0 - https://github.com/beegee-tokyo/RAK-OneWireSerial.git + https://github.com/beegee-tokyo/RAK-OneWireSerial.git#0.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 diff --git a/variants/rak2560/variant.h b/variants/rak2560/variant.h index 8e5d90553..a03fc3933 100644 --- a/variants/rak2560/variant.h +++ b/variants/rak2560/variant.h @@ -250,8 +250,6 @@ 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 HALF_UART_PIN PIN_SERIAL1_RX @@ -265,9 +263,6 @@ SO GPIO 39/TXEN MAY NOT BE DEFINED FOR SUCCESSFUL OPERATION OF THE SX1262 - TG #error pin 15 collision #endif -#define PIN_ETHERNET_RESET 21 -#define PIN_ETHERNET_SS PIN_EINK_CS -#define ETH_SPI_PORT SPI1 #define AQ_SET_PIN 10 #ifdef __cplusplus From 46ea39af4511e6b13fc2662e7c0a445ea966ceae Mon Sep 17 00:00:00 2001 From: Tom Fifield Date: Sat, 11 Jan 2025 17:10:04 +0800 Subject: [PATCH 04/89] Quote filename in device-install.sh (#5814) Without these quotes, a filename with spaces would be interpreted as different arguments. Thanks to @Hnikar-az for the report. fixes https://github.com/meshtastic/firmware/issues/5795 --- bin/device-install.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bin/device-install.sh b/bin/device-install.sh index e09c61ba6..4698b88e5 100755 --- a/bin/device-install.sh +++ b/bin/device-install.sh @@ -73,7 +73,7 @@ shift "$((OPTIND - 1))" if [ -f "${FILENAME}" ] && [ -n "${FILENAME##*"update"*}" ]; then echo "Trying to flash ${FILENAME}, but first erasing and writing system information" $ESPTOOL_CMD erase_flash - $ESPTOOL_CMD write_flash 0x00 ${FILENAME} + $ESPTOOL_CMD write_flash 0x00 "${FILENAME}" # Account for S3 board's different OTA partition if [ -n "${FILENAME##*"s3"*}" ] && [ -n "${FILENAME##*"-v3"*}" ] && [ -n "${FILENAME##*"t-deck"*}" ] && [ -n "${FILENAME##*"wireless-paper"*}" ] && [ -n "${FILENAME##*"wireless-tracker"*}" ] && [ -n "${FILENAME##*"station-g2"*}" ] && [ -n "${FILENAME##*"unphone"*}" ]; then if [ -n "${FILENAME##*"esp32c3"*}" ]; then From 077ee024265289449692b9430371424e4fb2b1c1 Mon Sep 17 00:00:00 2001 From: Tom Fifield Date: Sat, 11 Jan 2025 19:52:28 +0800 Subject: [PATCH 05/89] Cherry-pick: Meshtab streamline and rotation fixes (#5812) * mesh-tab: streamline target definitions * mesh-tab: fix touch rotation 4.0 inch display * Mesh-Tab platformio: 4.0inch: increase SPI frequency to max * mesh-tab: fix rotation for 3.5 IPS capacitive display * mesh-tab: fix rotation for 3.2 IPS capacitive display --------- Co-authored-by: mverch67 --- variants/mesh-tab/platformio.ini | 155 ++++++++++--------------------- 1 file changed, 50 insertions(+), 105 deletions(-) diff --git a/variants/mesh-tab/platformio.ini b/variants/mesh-tab/platformio.ini index a1007a7f4..30be7dbb8 100644 --- a/variants/mesh-tab/platformio.ini +++ b/variants/mesh-tab/platformio.ini @@ -56,18 +56,10 @@ build_src_filter = ${esp32_base.build_src_filter} lib_deps = ${esp32_base.lib_deps} lovyan03/LovyanGFX@^1.1.16 -; 3.2" TN TFT ST7789 / XPT2046: https://vi.aliexpress.com/item/1005005933490544.html -[env:mesh-tab-3-2-TN-resistive] +[mesh_tab_xpt2046] extends = mesh_tab_base build_flags = ${mesh_tab_base.build_flags} - -D LGFX_SCREEN_WIDTH=240 - -D LGFX_SCREEN_HEIGHT=320 - -D LGFX_PANEL=ST7789 - -D LGFX_INVERT_COLOR=false - -D LGFX_RGB_ORDER=false - -D LGFX_ROTATION=3 -D LGFX_TOUCH=XPT2046 - -D SPI_FREQUENCY=60000000 -D LGFX_TOUCH_SPI_FREQ=2500000 -D LGFX_TOUCH_SPI_HOST=2 -D LGFX_TOUCH_CS=7 @@ -78,156 +70,109 @@ build_flags = ${mesh_tab_base.build_flags} -D LGFX_TOUCH_X_MAX=3900 -D LGFX_TOUCH_Y_MIN=400 -D LGFX_TOUCH_Y_MAX=3900 + +[mesh_tab_ft5x06] +extends = mesh_tab_base +build_flags = ${mesh_tab_base.build_flags} + -D LGFX_TOUCH=FT5x06 + -D LGFX_TOUCH_I2C_FREQ=400000 + -D LGFX_TOUCH_I2C_PORT=0 + -D LGFX_TOUCH_I2C_ADDR=0x38 + -D LGFX_TOUCH_I2C_SDA=8 + -D LGFX_TOUCH_I2C_SCL=9 + -D LGFX_TOUCH_RST=7 + +; 3.2" TN TFT ST7789 / XPT2046: https://vi.aliexpress.com/item/1005005933490544.html +[env:mesh-tab-3-2-TN-resistive] +extends = mesh_tab_base +build_flags = ${mesh_tab_xpt2046.build_flags} + -D SPI_FREQUENCY=60000000 + -D LGFX_SCREEN_WIDTH=240 + -D LGFX_SCREEN_HEIGHT=320 + -D LGFX_PANEL=ST7789 + -D LGFX_INVERT_COLOR=false + -D LGFX_ROTATION=3 -D LGFX_TOUCH_ROTATION=4 ; 3.2" IPS TFT ILI9341 / XPT2046: https://www.aliexpress.com/item/1005006258575617.html [env:mesh-tab-3-2-IPS-resistive] extends = mesh_tab_base -build_flags = ${mesh_tab_base.build_flags} +build_flags = ${mesh_tab_xpt2046.build_flags} + -D SPI_FREQUENCY=60000000 ; if image is distorted then lower to 40 MHz -D LGFX_SCREEN_WIDTH=240 -D LGFX_SCREEN_HEIGHT=320 -D LGFX_PANEL=ILI9341 - -D LGFX_INVERT_COLOR=true - -D LGFX_RGB_ORDER=false -D LGFX_ROTATION=1 - -D LGFX_TOUCH=XPT2046 - -D SPI_FREQUENCY=60000000 ; if image is distorted then lower to 40 MHz - -D LGFX_TOUCH_SPI_FREQ=2500000 - -D LGFX_TOUCH_SPI_HOST=2 - -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 LGFX_TOUCH_ROTATION=4 ; 3.5" IPS TFT ILI9488 / XPT2046: https://vi.aliexpress.com/item/1005006333922639.html [env:mesh-tab-3-5-IPS-resistive] extends = mesh_tab_base -build_flags = ${mesh_tab_base.build_flags} +build_flags = ${mesh_tab_xpt2046.build_flags} + -D SPI_FREQUENCY=60000000 ; may go higher upto 40/60/80 MHz -D DISPLAY_SET_RESOLUTION -D LGFX_SCREEN_WIDTH=320 -D LGFX_SCREEN_HEIGHT=480 -D LGFX_PANEL=ILI9488 - -D LGFX_INVERT_COLOR=true - -D LGFX_RGB_ORDER=false - -D LGFX_DLEN_16BITS=false -D LGFX_ROTATION=0 - -D LGFX_TOUCH=XPT2046 - -D SPI_FREQUENCY=40000000 ; may go higher upto 40/60/80 MHz - -D LGFX_TOUCH_SPI_FREQ=2500000 - -D LGFX_TOUCH_SPI_HOST=2 - -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 LGFX_TOUCH_ROTATION=0 ; 3.5" TN TFT ILI9488 / XPT2046: https://vi.aliexpress.com/item/32985467436.html [env:mesh-tab-3-5-TN-resistive] extends = mesh_tab_base -build_flags = ${mesh_tab_base.build_flags} +build_flags = ${mesh_tab_xpt2046.build_flags} + -D SPI_FREQUENCY=60000000 -D DISPLAY_SET_RESOLUTION -D LGFX_SCREEN_WIDTH=320 -D LGFX_SCREEN_HEIGHT=480 -D LGFX_PANEL=HX8357B - -D SPI_FREQUENCY=60000000 -D LGFX_INVERT_COLOR=false - -D LGFX_RGB_ORDER=false - -D LGFX_DLEN_16BITS=false -D LGFX_ROTATION=4 - -D LGFX_TOUCH=XPT2046 - -D LGFX_TOUCH_SPI_FREQ=2500000 - -D LGFX_TOUCH_SPI_HOST=2 - -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 LGFX_TOUCH_ROTATION=2 ; 3.2" IPS TFT ILI9341 / FT6236: https://vi.aliexpress.com/item/1005006624072350.html [env:mesh-tab-3-2-IPS-capacitive] extends = mesh_tab_base -build_flags = ${mesh_tab_base.build_flags} +build_flags = ${mesh_tab_ft5x06.build_flags} + -D SPI_FREQUENCY=75000000 ; may go higher upto 60/80 MHz -D LGFX_SCREEN_WIDTH=240 -D LGFX_SCREEN_HEIGHT=320 -D LGFX_PANEL=ILI9341 - -D LGFX_INVERT_COLOR=true - -D LGFX_RGB_ORDER=false -D LGFX_ROTATION=1 - -D LGFX_TOUCH=FT5x06 - -D SPI_FREQUENCY=40000000 ; may go higher upto 60/80 MHz - -D LGFX_TOUCH_I2C_PORT=0 - -D LGFX_TOUCH_I2C_ADDR=0x38 - -D LGFX_TOUCH_I2C_SDA=8 - -D LGFX_TOUCH_I2C_SCL=9 - -D LGFX_TOUCH_RST=7 + -D LGFX_TOUCH_X_MIN=0 + -D LGFX_TOUCH_X_MAX=239 + -D LGFX_TOUCH_Y_MIN=0 + -D LGFX_TOUCH_Y_MAX=319 + -D LGFX_TOUCH_ROTATION=2 + +; 3.5" IPS TFT ILI9488 / FT6236: https://vi.aliexpress.com/item/1005006893699919.html +[env:mesh-tab-3-5-IPS-capacitive] +extends = mesh_tab_base +build_flags = ${mesh_tab_ft5x06.build_flags} + -D SPI_FREQUENCY=75000000 ; may go higher upto 40/60/80 MHz + -D DISPLAY_SET_RESOLUTION + -D LGFX_SCREEN_WIDTH=320 + -D LGFX_SCREEN_HEIGHT=480 + -D LGFX_PANEL=ILI9488 + -D LGFX_ROTATION=2 -D LGFX_TOUCH_X_MIN=0 -D LGFX_TOUCH_X_MAX=319 -D LGFX_TOUCH_Y_MIN=0 -D LGFX_TOUCH_Y_MAX=479 -D LGFX_TOUCH_ROTATION=0 - -D LGFX_TOUCH_I2C_FREQ=400000 - -; 3.5" IPS TFT ILI9488 / FT6236: https://vi.aliexpress.com/item/1005006893699919.html -[env:mesh-tab-3-5-IPS-capacitive] -extends = mesh_tab_base -build_flags = ${mesh_tab_base.build_flags} - -D DISPLAY_SET_RESOLUTION - -D LGFX_SCREEN_WIDTH=320 - -D LGFX_SCREEN_HEIGHT=480 - -D LGFX_PANEL=ILI9488 - -D LGFX_INVERT_COLOR=true - -D LGFX_RGB_ORDER=false - -D LGFX_DLEN_16BITS=false - -D LGFX_ROTATION=1 - -D LGFX_TOUCH=FT5x06 - -D SPI_FREQUENCY=30000000 ; may go higher upto 40/60/80 MHz - -D LGFX_TOUCH_I2C_PORT=0 - -D LGFX_TOUCH_I2C_ADDR=0x38 - -D LGFX_TOUCH_I2C_SDA=8 - -D LGFX_TOUCH_I2C_SCL=9 - -D LGFX_TOUCH_RST=7 - -D LGFX_TOUCH_X_MIN=0 - -D LGFX_TOUCH_X_MAX=319 - -D LGFX_TOUCH_Y_MIN=0 - -D LGFX_TOUCH_Y_MAX=479 - -D LGFX_TOUCH_ROTATION=1 - -D LGFX_TOUCH_I2C_FREQ=400000 ; 4.0" IPS TFT ILI9488 / FT6236: https://vi.aliexpress.com/item/1005007082906950.html [env:mesh-tab-4-0-IPS-capacitive] extends = mesh_tab_base -build_flags = ${mesh_tab_base.build_flags} +build_flags = ${mesh_tab_ft5x06.build_flags} + -D SPI_FREQUENCY=75000000 -D DISPLAY_SET_RESOLUTION -D LGFX_SCREEN_WIDTH=320 -D LGFX_SCREEN_HEIGHT=480 -D LGFX_PANEL=HX8357B - -D LGFX_INVERT_COLOR=true - -D LGFX_RGB_ORDER=false - -D LGFX_DLEN_16BITS=false -D LGFX_ROTATION=4 - -D LGFX_TOUCH=FT5x06 - -D SPI_FREQUENCY=30000000 ; may go higher upto 40/60/80 MHz - -D LGFX_TOUCH_I2C_PORT=0 - -D LGFX_TOUCH_I2C_ADDR=0x38 - -D LGFX_TOUCH_I2C_SDA=8 - -D LGFX_TOUCH_I2C_SCL=9 - -D LGFX_TOUCH_RST=7 -D LGFX_TOUCH_X_MIN=0 -D LGFX_TOUCH_X_MAX=319 -D LGFX_TOUCH_Y_MIN=0 -D LGFX_TOUCH_Y_MAX=479 - -D LGFX_TOUCH_ROTATION=1 - -D LGFX_TOUCH_I2C_FREQ=400000 \ No newline at end of file + -D LGFX_TOUCH_ROTATION=6 From e7802d960fac813e58e6b0e8ed085d91801e5dd8 Mon Sep 17 00:00:00 2001 From: Eric Severance Date: Sat, 11 Jan 2025 04:15:50 -0800 Subject: [PATCH 06/89] Manage when destructor is called for native HttpAPI (#5807) --- src/main.cpp | 4 +++- src/mesh/raspihttp/PiWebServer.cpp | 23 +++++++++---------- src/mesh/raspihttp/PiWebServer.h | 36 +++++++++++++++--------------- 3 files changed, 31 insertions(+), 32 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index c2b20b1c1..4a642ef6d 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -92,6 +92,7 @@ NRF52Bluetooth *nrf52Bluetooth = nullptr; #include "mesh/raspihttp/PiWebServer.h" #include "platform/portduino/PortduinoGlue.h" #include "platform/portduino/USBHal.h" +#include #include #include #include @@ -1159,6 +1160,7 @@ void setup() #if __has_include() if (settingsMap[webserverport] != -1) { piwebServerThread = new PiWebServerThread(); + std::atexit([] { delete piwebServerThread; }); } #endif initApiServer(TCPPort); @@ -1278,4 +1280,4 @@ void loop() mainDelay.delay(delayMsec); } } -#endif +#endif \ No newline at end of file diff --git a/src/mesh/raspihttp/PiWebServer.cpp b/src/mesh/raspihttp/PiWebServer.cpp index 846d70723..9d2625410 100644 --- a/src/mesh/raspihttp/PiWebServer.cpp +++ b/src/mesh/raspihttp/PiWebServer.cpp @@ -82,8 +82,6 @@ char contentTypes[][2][32] = {{".txt", "text/plain"}, {".html", "text/html" volatile bool isWebServerReady; volatile bool isCertReady; -HttpAPI webAPI; - PiWebServerThread *piwebServerThread; /** @@ -247,7 +245,7 @@ int handleAPIv1ToRadio(const struct _u_request *req, struct _u_response *res, vo portduinoVFS->mountpoint(configWeb.rootPath); LOG_DEBUG("Received %d bytes from PUT request", s); - webAPI.handleToRadio(buffer, s); + static_cast(user_data)->handleToRadio(buffer, s); LOG_DEBUG("end web->radio "); return U_CALLBACK_COMPLETE; } @@ -279,7 +277,7 @@ int handleAPIv1FromRadio(const struct _u_request *req, struct _u_response *res, if (valueAll == "true") { while (len) { - len = webAPI.getFromRadio(txBuf); + len = static_cast(user_data)->getFromRadio(txBuf); 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); @@ -289,7 +287,7 @@ int handleAPIv1FromRadio(const struct _u_request *req, struct _u_response *res, } // Otherwise, just return one protobuf } else { - len = webAPI.getFromRadio(txBuf); + len = static_cast(user_data)->getFromRadio(txBuf); const char *tmpa = (const char *)txBuf; ulfius_set_binary_body_response(res, 200, tmpa, len); // LOG_DEBUG("\n----webAPI response:"); @@ -497,10 +495,10 @@ PiWebServerThread::PiWebServerThread() u_map_put(instanceWeb.default_headers, "Access-Control-Allow-Origin", "*"); // 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); + ulfius_add_endpoint_by_val(&instanceWeb, "GET", PREFIX, "/api/v1/fromradio/*", 1, &handleAPIv1FromRadio, &webAPI); + ulfius_add_endpoint_by_val(&instanceWeb, "OPTIONS", PREFIX, "/api/v1/fromradio/*", 1, &handleAPIv1FromRadio, &webAPI); + ulfius_add_endpoint_by_val(&instanceWeb, "PUT", PREFIX, "/api/v1/toradio/*", 1, &handleAPIv1ToRadio, &webAPI); + ulfius_add_endpoint_by_val(&instanceWeb, "OPTIONS", PREFIX, "/api/v1/toradio/*", 1, &handleAPIv1ToRadio, &webAPI); // Add callback function to all endpoints for the Web Server ulfius_add_endpoint_by_val(&instanceWeb, "GET", NULL, "/*", 2, &callback_static_file, &configWeb); @@ -525,13 +523,12 @@ PiWebServerThread::~PiWebServerThread() u_map_clean(&configWeb.mime_types); ulfius_stop_framework(&instanceWeb); - ulfius_stop_framework(&instanceWeb); + ulfius_clean_instance(&instanceWeb); free(configWeb.rootPath); - ulfius_clean_instance(&instanceService); - ulfius_clean_instance(&instanceService); + free(key_pem); free(cert_pem); LOG_INFO("End framework"); } #endif -#endif +#endif \ No newline at end of file diff --git a/src/mesh/raspihttp/PiWebServer.h b/src/mesh/raspihttp/PiWebServer.h index c4c49e919..b45348cf3 100644 --- a/src/mesh/raspihttp/PiWebServer.h +++ b/src/mesh/raspihttp/PiWebServer.h @@ -23,24 +23,6 @@ struct _file_config { char *rootPath; }; -class PiWebServerThread -{ - private: - char *key_pem = NULL; - char *cert_pem = NULL; - // struct _u_map mime_types; - std::string webrootpath; - - public: - PiWebServerThread(); - ~PiWebServerThread(); - int CreateSSLCertificate(); - int CheckSSLandLoad(); - uint32_t requestRestart = 0; - struct _u_instance instanceWeb; - struct _u_instance instanceService; -}; - class HttpAPI : public PhoneAPI { @@ -55,6 +37,24 @@ class HttpAPI : public PhoneAPI virtual bool checkIsConnected() override { return true; } // FIXME, be smarter about this }; +class PiWebServerThread +{ + private: + char *key_pem = NULL; + char *cert_pem = NULL; + // struct _u_map mime_types; + std::string webrootpath; + HttpAPI webAPI; + + public: + PiWebServerThread(); + ~PiWebServerThread(); + int CreateSSLCertificate(); + int CheckSSLandLoad(); + uint32_t requestRestart = 0; + struct _u_instance instanceWeb; +}; + extern PiWebServerThread *piwebServerThread; #endif From 812aa35f096c0102bdcf246a335810596873c558 Mon Sep 17 00:00:00 2001 From: GUVWAF <78759985+GUVWAF@users.noreply.github.com> Date: Sat, 11 Jan 2025 14:19:17 +0100 Subject: [PATCH 07/89] Enable Tx interrupt immediately after `startTransmit()` (#5820) --- src/mesh/RadioLibInterface.cpp | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/mesh/RadioLibInterface.cpp b/src/mesh/RadioLibInterface.cpp index 09266b334..0a047a660 100644 --- a/src/mesh/RadioLibInterface.cpp +++ b/src/mesh/RadioLibInterface.cpp @@ -501,14 +501,13 @@ bool RadioLibInterface::startSend(meshtastic_MeshPacket *txp) powerMon->clearState(meshtastic_PowerMon_State_Lora_TXOn); // Transmitter off now startReceive(); // Restart receive mode (because startTransmit failed to put us in xmit mode) } else { + // Must be done AFTER, starting transmit, because startTransmit clears (possibly stale) interrupt pending register + // bits + enableInterrupt(isrTxLevel0); 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 From 6b8cf164e9a5af84c40d403c5102102c0c73db42 Mon Sep 17 00:00:00 2001 From: GUVWAF <78759985+GUVWAF@users.noreply.github.com> Date: Sat, 11 Jan 2025 14:20:32 +0100 Subject: [PATCH 08/89] Save some flash usage on STM32WL (#5819) --- src/mesh/RadioInterface.cpp | 4 ++-- variants/rak3172/platformio.ini | 2 ++ variants/wio-e5/platformio.ini | 2 ++ 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/mesh/RadioInterface.cpp b/src/mesh/RadioInterface.cpp index b1403f3b6..d91cba116 100644 --- a/src/mesh/RadioInterface.cpp +++ b/src/mesh/RadioInterface.cpp @@ -297,7 +297,7 @@ uint32_t RadioInterface::getTxDelayMsecWeighted(float snr) void printPacket(const char *prefix, const meshtastic_MeshPacket *p) { -#ifdef DEBUG_PORT +#if defined(DEBUG_PORT) && !defined(DEBUG_MUTE) std::string out = DEBUG_PORT.mt_sprintf("%s (id=0x%08x fr=0x%08x to=0x%08x, WantAck=%d, HopLim=%d Ch=0x%x", prefix, p->id, p->from, p->to, p->want_ack, p->hop_limit, p->channel); if (p->which_payload_variant == meshtastic_MeshPacket_decoded_tag) { @@ -637,4 +637,4 @@ size_t RadioInterface::beginSending(meshtastic_MeshPacket *p) sendingPacket = p; return p->encrypted.size + sizeof(PacketHeader); -} +} \ No newline at end of file diff --git a/variants/rak3172/platformio.ini b/variants/rak3172/platformio.ini index 9e617e01e..58ea32088 100644 --- a/variants/rak3172/platformio.ini +++ b/variants/rak3172/platformio.ini @@ -28,6 +28,8 @@ build_flags = -DHAL_TIM_MODULE_DISABLED -DHAL_WWDG_MODULE_DISABLED -DHAL_EXTI_MODULE_DISABLED + -DHAL_SAI_MODULE_DISABLED + -DHAL_ICACHE_MODULE_DISABLED -DRADIOLIB_EXCLUDE_SX128X=1 -DRADIOLIB_EXCLUDE_SX127X=1 -DRADIOLIB_EXCLUDE_LR11X0=1 diff --git a/variants/wio-e5/platformio.ini b/variants/wio-e5/platformio.ini index 29c4a0a37..e9d4ca946 100644 --- a/variants/wio-e5/platformio.ini +++ b/variants/wio-e5/platformio.ini @@ -28,6 +28,8 @@ build_flags = -DHAL_TIM_MODULE_DISABLED -DHAL_WWDG_MODULE_DISABLED -DHAL_EXTI_MODULE_DISABLED + -DHAL_SAI_MODULE_DISABLED + -DHAL_ICACHE_MODULE_DISABLED -DRADIOLIB_EXCLUDE_SX128X=1 -DRADIOLIB_EXCLUDE_SX127X=1 -DRADIOLIB_EXCLUDE_LR11X0=1 From b4a4d2db4e9a482d778ebd7232dd0fc821df95bb Mon Sep 17 00:00:00 2001 From: Eric Severance Date: Sat, 11 Jan 2025 17:40:39 -0800 Subject: [PATCH 09/89] Cache Python & PlatformIO dependencies (#5822) --- .github/actions/build-variant/action.yml | 6 ++++++ .github/actions/setup-base/action.yml | 11 ++++------- 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/.github/actions/build-variant/action.yml b/.github/actions/build-variant/action.yml index d003459d6..b24a5fc12 100644 --- a/.github/actions/build-variant/action.yml +++ b/.github/actions/build-variant/action.yml @@ -68,6 +68,12 @@ runs: sed -i '/DDEBUG_HEAP/d' ${INI_FILE} done + - name: PlatformIO ${{ inputs.arch }} download cache + uses: actions/cache@v4 + with: + path: ~/.platformio/.cache + key: pio-cache-${{ inputs.arch }}-${{ hashFiles('.github/actions/**', '**.ini') }} + - name: Build ${{ inputs.board }} shell: bash run: ${{ inputs.build-script-path }} ${{ inputs.board }} diff --git a/.github/actions/setup-base/action.yml b/.github/actions/setup-base/action.yml index c0f6c4e66..77c8f0e10 100644 --- a/.github/actions/setup-base/action.yml +++ b/.github/actions/setup-base/action.yml @@ -26,13 +26,10 @@ runs: uses: actions/setup-python@v5 with: python-version: 3.x - - # - name: Cache python libs - # uses: actions/cache@v4 - # id: cache-pip # needed in if test - # with: - # path: ~/.cache/pip - # key: ${{ runner.os }}-pip + cache: pip + cache-dependency-path: | + .github/actions/** + **.ini - name: Upgrade python tools shell: bash From 253ab458efaf2a60cfda7d57a6b5196ad06c6028 Mon Sep 17 00:00:00 2001 From: Eric Severance Date: Sat, 11 Jan 2025 20:29:24 -0800 Subject: [PATCH 10/89] Add lsb-release on the github runner (#5825) --- .github/actions/setup-base/action.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/actions/setup-base/action.yml b/.github/actions/setup-base/action.yml index 77c8f0e10..7364c4ddb 100644 --- a/.github/actions/setup-base/action.yml +++ b/.github/actions/setup-base/action.yml @@ -20,7 +20,7 @@ runs: shell: bash run: | sudo apt-get -y update --fix-missing - sudo apt-get install -y cppcheck libbluetooth-dev libgpiod-dev libyaml-cpp-dev + sudo apt-get install -y cppcheck libbluetooth-dev libgpiod-dev libyaml-cpp-dev lsb-release - name: Setup Python uses: actions/setup-python@v5 From 00fdf2c9aadf5c45e77ae98e0b476ca9d9f4fd10 Mon Sep 17 00:00:00 2001 From: DarkZeros Date: Sun, 12 Jan 2025 05:17:40 +0000 Subject: [PATCH 11/89] Heltec Wireless Stick Lite V1/V2 support (#5808) * I only have the V2.1 version, not sure if the HW is same with V1, or V2. Given the few resources I could find I think it is. * ADC / Battery measure and TX/RX are working --- variants/heltec_wsl_v2.1/platformio.ini | 7 ++++++ variants/heltec_wsl_v2.1/variant.h | 29 +++++++++++++++++++++++++ 2 files changed, 36 insertions(+) create mode 100644 variants/heltec_wsl_v2.1/platformio.ini create mode 100644 variants/heltec_wsl_v2.1/variant.h diff --git a/variants/heltec_wsl_v2.1/platformio.ini b/variants/heltec_wsl_v2.1/platformio.ini new file mode 100644 index 000000000..f4fff9698 --- /dev/null +++ b/variants/heltec_wsl_v2.1/platformio.ini @@ -0,0 +1,7 @@ +[env:heltec-wsl-v2_1] +extends = esp32_base +board = heltec_wireless_stick_lite +board_level = extra +build_flags = + ${esp32_base.build_flags} -D PRIVATE_HW -I variants/heltec_wsl_v2.1 + -DGPS_POWER_TOGGLE ; comment this line to disable triple press function on the user button to turn off gps entirely. \ No newline at end of file diff --git a/variants/heltec_wsl_v2.1/variant.h b/variants/heltec_wsl_v2.1/variant.h new file mode 100644 index 000000000..3927a89d6 --- /dev/null +++ b/variants/heltec_wsl_v2.1/variant.h @@ -0,0 +1,29 @@ +#define I2C_SCL SCL +#define I2C_SDA SDA + +#define LED_PIN LED + +// active low, powers the Battery reader, but no lora antenna boost (?) +// #define VEXT_ENABLE Vext +// #define VEXT_ON_VALUE LOW + +#define BUTTON_PIN 0 + +#define ADC_CTRL 21 +#define ADC_CTRL_ENABLED LOW +#define BATTERY_PIN 37 // A battery voltage measurement pin, voltage divider connected here to measure battery voltage +#define ADC_CHANNEL ADC1_CHANNEL_1 +// ratio of voltage divider = 3.20 (R1=100k, R2=220k) +#define ADC_MULTIPLIER 3.2 + +#define USE_RF95 // RFM95/SX127x + +#define LORA_DIO0 26 +#define LORA_RESET 14 +#define LORA_DIO1 35 +#define LORA_DIO2 34 + +#define LORA_SCK 5 +#define LORA_MISO 19 +#define LORA_MOSI 27 +#define LORA_CS 18 From 0fe8d4ccc7687195cf93401c2914c16de7d9ab9d Mon Sep 17 00:00:00 2001 From: Eric Severance Date: Sat, 11 Jan 2025 21:51:43 -0800 Subject: [PATCH 12/89] Run the AddressSanitizer during tests (#5815) * Run the AddressSanitizer during tests * Show details for test failures --- .github/workflows/test_native.yml | 2 +- variants/portduino/platformio.ini | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/test_native.yml b/.github/workflows/test_native.yml index 8e8ff68e4..c7b0ef34c 100644 --- a/.github/workflows/test_native.yml +++ b/.github/workflows/test_native.yml @@ -90,7 +90,7 @@ jobs: run: sed -i 's/-DBUILD_EPOCH=$UNIX_TIME/#-DBUILD_EPOCH=$UNIX_TIME/' platformio.ini - name: PlatformIO Tests - run: platformio test -e coverage --junit-output-path testreport.xml + run: platformio test -e coverage -v --junit-output-path testreport.xml - name: Save test results if: always() # run this step even if previous step failed diff --git a/variants/portduino/platformio.ini b/variants/portduino/platformio.ini index cad87ea8c..2c7030b5b 100644 --- a/variants/portduino/platformio.ini +++ b/variants/portduino/platformio.ini @@ -12,4 +12,4 @@ build_src_filter = ${portduino_base.build_src_filter} [env:coverage] extends = env:native -build_flags = -lgcov --coverage -fprofile-abs-path ${env:native.build_flags} +build_flags = -lgcov --coverage -fprofile-abs-path -fsanitize=address ${env:native.build_flags} From a0a4c5bc7944764af1ba7154eb9476f80e3b956c Mon Sep 17 00:00:00 2001 From: And137 <137andrew137@gmail.com> Date: Sun, 12 Jan 2025 07:30:58 +0100 Subject: [PATCH 13/89] Support for Polish fonts on E-Ink devices, Polish fonts retouch, fixed Czech/Slovak OLED/E-Ink double space bug (#5821) * Added support for Polish fonts for E-Ink devices * Added support for Polish fonts for E-Ink devices FIX * Polilsh E-Ink/OLED font retouch, fixed Czech/Slovak font double space bug * Fixed platformio.ini uncommented flag --- platformio.ini | 2 +- src/graphics/ScreenFonts.h | 14 + src/graphics/fonts/OLEDDisplayFontsCS.cpp | 6 +- src/graphics/fonts/OLEDDisplayFontsPL.cpp | 1742 ++++++++++++++++----- src/graphics/fonts/OLEDDisplayFontsPL.h | 3 +- 5 files changed, 1327 insertions(+), 440 deletions(-) diff --git a/platformio.ini b/platformio.ini index 6a4466c01..e6735ec23 100644 --- a/platformio.ini +++ b/platformio.ini @@ -49,7 +49,7 @@ build_flags = -Wno-missing-field-initializers -DMESHTASTIC_EXCLUDE_HEALTH_TELEMETRY=1 -DMESHTASTIC_EXCLUDE_POWERSTRESS=1 ; exclude power stress test module from main firmware #-DBUILD_EPOCH=$UNIX_TIME - ;-D OLED_PL + #-D OLED_PL=1 monitor_speed = 115200 monitor_filters = direct diff --git a/src/graphics/ScreenFonts.h b/src/graphics/ScreenFonts.h index 81eb717cd..7881b464f 100644 --- a/src/graphics/ScreenFonts.h +++ b/src/graphics/ScreenFonts.h @@ -20,9 +20,15 @@ defined(ST7789_CS) || defined(USE_ST7789) || defined(HX8357_CS)) && \ !defined(DISPLAY_FORCE_SMALL_FONTS) // The screen is bigger so use bigger fonts +#ifdef OLED_PL +#define FONT_SMALL ArialMT_Plain_16_PL // Height: 19 +#define FONT_MEDIUM ArialMT_Plain_24_PL // Height: 28 +#define FONT_LARGE ArialMT_Plain_24_PL // Height: 28 +#else #define FONT_SMALL ArialMT_Plain_16 // Height: 19 #define FONT_MEDIUM ArialMT_Plain_24 // Height: 28 #define FONT_LARGE ArialMT_Plain_24 // Height: 28 +#endif #else #ifdef OLED_PL #define FONT_SMALL ArialMT_Plain_10_PL @@ -41,6 +47,9 @@ #endif #endif #endif +#ifdef OLED_PL +#define FONT_MEDIUM ArialMT_Plain_16_PL // Height: 19 +#else #ifdef OLED_UA #define FONT_MEDIUM ArialMT_Plain_16_UA // Height: 19 #else @@ -50,6 +59,10 @@ #define FONT_MEDIUM ArialMT_Plain_16 // Height: 19 #endif #endif +#endif +#ifdef OLED_PL +#define FONT_LARGE ArialMT_Plain_24_PL // Height: 28 +#else #ifdef OLED_UA #define FONT_LARGE ArialMT_Plain_24_UA // Height: 28 #else @@ -60,6 +73,7 @@ #endif #endif #endif +#endif #define _fontHeight(font) ((font)[1] + 1) // height is position 1 diff --git a/src/graphics/fonts/OLEDDisplayFontsCS.cpp b/src/graphics/fonts/OLEDDisplayFontsCS.cpp index 5c17e9177..67208b4d9 100644 --- a/src/graphics/fonts/OLEDDisplayFontsCS.cpp +++ b/src/graphics/fonts/OLEDDisplayFontsCS.cpp @@ -7,7 +7,7 @@ const uint8_t ArialMT_Plain_10_CS[] PROGMEM = { 0x20, // First char: 32 0xE0, // Number of chars: 224 // Jump Table: - 0xFF, 0xFF, 0x00, 0x0A, // 32 + 0xFF, 0xFF, 0x00, 0x03, // 32 0x00, 0x00, 0x04, 0x03, // 33 0x00, 0x04, 0x05, 0x04, // 34 0x00, 0x09, 0x09, 0x06, // 35 @@ -453,7 +453,7 @@ const uint8_t ArialMT_Plain_16_CS[] PROGMEM = { 0x20, // First char: 32 0xE0, // Number of chars: 224 // Jump Table: - 0xFF, 0xFF, 0x00, 0x10, // 32 + 0xFF, 0xFF, 0x00, 0x04, // 32 0x00, 0x00, 0x08, 0x04, // 33 0x00, 0x08, 0x0D, 0x06, // 34 0x00, 0x15, 0x1A, 0x0A, // 35 @@ -1036,7 +1036,7 @@ const uint8_t ArialMT_Plain_24_CS[] PROGMEM = { 0x20, // First char: 32 0xE0, // Number of chars: 224 // Jump Table: - 0xFF, 0xFF, 0x00, 0x18, // 32 + 0xFF, 0xFF, 0x00, 0x06, // 32 0x00, 0x00, 0x13, 0x06, // 33 0x00, 0x13, 0x1A, 0x08, // 34 0x00, 0x2D, 0x33, 0x0E, // 35 diff --git a/src/graphics/fonts/OLEDDisplayFontsPL.cpp b/src/graphics/fonts/OLEDDisplayFontsPL.cpp index 03fdab5fa..1f43967aa 100644 --- a/src/graphics/fonts/OLEDDisplayFontsPL.cpp +++ b/src/graphics/fonts/OLEDDisplayFontsPL.cpp @@ -1,440 +1,1312 @@ #include "OLEDDisplayFontsPL.h" -// Font generated or edited with the glyphEditor const uint8_t ArialMT_Plain_10_PL[] PROGMEM = { - 0x0A, // Width: 10 - 0x0D, // Height: 13 - 0x20, // First char: 32 - 0xE0, // Number of chars: 224 +0x0A, // Width: 10 +0x0D, // Height: 13 +0x20, // First char: 32 +0xE0, // Number of chars: 224 +// Jump Table: +0xFF, 0xFF, 0x00, 0x03, // 32 +0x00, 0x00, 0x04, 0x03, // 33 +0x00, 0x04, 0x05, 0x04, // 34 +0x00, 0x09, 0x09, 0x06, // 35 +0x00, 0x12, 0x0A, 0x06, // 36 +0x00, 0x1C, 0x10, 0x09, // 37 +0x00, 0x2C, 0x0E, 0x08, // 38 +0x00, 0x3A, 0x01, 0x02, // 39 +0x00, 0x3B, 0x06, 0x04, // 40 +0x00, 0x41, 0x06, 0x04, // 41 +0x00, 0x47, 0x05, 0x04, // 42 +0x00, 0x4C, 0x09, 0x06, // 43 +0x00, 0x55, 0x04, 0x03, // 44 +0x00, 0x59, 0x03, 0x03, // 45 +0x00, 0x5C, 0x04, 0x03, // 46 +0x00, 0x60, 0x05, 0x04, // 47 +0x00, 0x65, 0x0A, 0x06, // 48 +0x00, 0x6F, 0x08, 0x05, // 49 +0x00, 0x77, 0x0A, 0x06, // 50 +0x00, 0x81, 0x0A, 0x06, // 51 +0x00, 0x8B, 0x0B, 0x07, // 52 +0x00, 0x96, 0x0A, 0x06, // 53 +0x00, 0xA0, 0x0A, 0x06, // 54 +0x00, 0xAA, 0x09, 0x06, // 55 +0x00, 0xB3, 0x0A, 0x06, // 56 +0x00, 0xBD, 0x0A, 0x06, // 57 +0x00, 0xC7, 0x04, 0x03, // 58 +0x00, 0xCB, 0x04, 0x03, // 59 +0x00, 0xCF, 0x0A, 0x06, // 60 +0x00, 0xD9, 0x09, 0x06, // 61 +0x00, 0xE2, 0x09, 0x06, // 62 +0x00, 0xEB, 0x0B, 0x07, // 63 +0x00, 0xF6, 0x14, 0x0B, // 64 +0x01, 0x0A, 0x0E, 0x08, // 65 +0x01, 0x18, 0x0C, 0x07, // 66 +0x01, 0x24, 0x0C, 0x07, // 67 +0x01, 0x30, 0x0B, 0x07, // 68 +0x01, 0x3B, 0x0C, 0x07, // 69 +0x01, 0x47, 0x09, 0x06, // 70 +0x01, 0x50, 0x0D, 0x08, // 71 +0x01, 0x5D, 0x0C, 0x07, // 72 +0x01, 0x69, 0x04, 0x03, // 73 +0x01, 0x6D, 0x08, 0x05, // 74 +0x01, 0x75, 0x0E, 0x08, // 75 +0x01, 0x83, 0x0C, 0x07, // 76 +0x01, 0x8F, 0x10, 0x09, // 77 +0x01, 0x9F, 0x0C, 0x07, // 78 +0x01, 0xAB, 0x0E, 0x08, // 79 +0x01, 0xB9, 0x0B, 0x07, // 80 +0x01, 0xC4, 0x0E, 0x08, // 81 +0x01, 0xD2, 0x0C, 0x07, // 82 +0x01, 0xDE, 0x0C, 0x07, // 83 +0x01, 0xEA, 0x0B, 0x07, // 84 +0x01, 0xF5, 0x0C, 0x07, // 85 +0x02, 0x01, 0x0D, 0x08, // 86 +0x02, 0x0E, 0x11, 0x0A, // 87 +0x02, 0x1F, 0x0E, 0x08, // 88 +0x02, 0x2D, 0x0D, 0x08, // 89 +0x02, 0x3A, 0x0C, 0x07, // 90 +0x02, 0x46, 0x06, 0x04, // 91 +0x02, 0x4C, 0x06, 0x04, // 92 +0x02, 0x52, 0x04, 0x03, // 93 +0x02, 0x56, 0x09, 0x06, // 94 +0x02, 0x5F, 0x0C, 0x07, // 95 +0x02, 0x6B, 0x03, 0x03, // 96 +0x02, 0x6E, 0x0A, 0x06, // 97 +0x02, 0x78, 0x0A, 0x06, // 98 +0x02, 0x82, 0x0A, 0x06, // 99 +0x02, 0x8C, 0x0A, 0x06, // 100 +0x02, 0x96, 0x0A, 0x06, // 101 +0x02, 0xA0, 0x05, 0x04, // 102 +0x02, 0xA5, 0x0A, 0x06, // 103 +0x02, 0xAF, 0x0A, 0x06, // 104 +0x02, 0xB9, 0x04, 0x03, // 105 +0x02, 0xBD, 0x04, 0x03, // 106 +0x02, 0xC1, 0x08, 0x05, // 107 +0x02, 0xC9, 0x04, 0x03, // 108 +0x02, 0xCD, 0x10, 0x09, // 109 +0x02, 0xDD, 0x0A, 0x06, // 110 +0x02, 0xE7, 0x0A, 0x06, // 111 +0x02, 0xF1, 0x0A, 0x06, // 112 +0x02, 0xFB, 0x0A, 0x06, // 113 +0x03, 0x05, 0x05, 0x04, // 114 +0x03, 0x0A, 0x08, 0x05, // 115 +0x03, 0x12, 0x06, 0x04, // 116 +0x03, 0x18, 0x0A, 0x06, // 117 +0x03, 0x22, 0x09, 0x06, // 118 +0x03, 0x2B, 0x0E, 0x08, // 119 +0x03, 0x39, 0x0A, 0x06, // 120 +0x03, 0x43, 0x09, 0x06, // 121 +0x03, 0x4C, 0x0A, 0x06, // 122 +0x03, 0x56, 0x06, 0x04, // 123 +0x03, 0x5C, 0x04, 0x03, // 124 +0x03, 0x60, 0x05, 0x04, // 125 +0x03, 0x65, 0x09, 0x06, // 126 +0xFF, 0xFF, 0x00, 0x0A, // 127 +0xFF, 0xFF, 0x00, 0x0A, // 128 +0x03, 0x6E, 0x0C, 0x07, // 129 +0x03, 0x7A, 0x05, 0x04, // 130 +0x03, 0x7F, 0x0C, 0x07, // 131 +0x03, 0x8B, 0x0E, 0x08, // 132 +0x03, 0x99, 0x0A, 0x06, // 133 +0x03, 0xA3, 0x0C, 0x07, // 134 +0x03, 0xAF, 0x0A, 0x06, // 135 +0x03, 0xB9, 0x0A, 0x06, // 136 +0x03, 0xC3, 0x0A, 0x06, // 137 +0xFF, 0xFF, 0x00, 0x0A, // 138 +0xFF, 0xFF, 0x00, 0x0A, // 139 +0xFF, 0xFF, 0x00, 0x0A, // 140 +0xFF, 0xFF, 0x00, 0x0A, // 141 +0xFF, 0xFF, 0x00, 0x0A, // 142 +0xFF, 0xFF, 0x00, 0x0A, // 143 +0xFF, 0xFF, 0x00, 0x0A, // 144 +0xFF, 0xFF, 0x00, 0x0A, // 145 +0xFF, 0xFF, 0x00, 0x0A, // 146 +0x03, 0xCD, 0x0E, 0x08, // 147 +0x03, 0xDB, 0x0A, 0x06, // 148 +0xFF, 0xFF, 0x00, 0x0A, // 149 +0xFF, 0xFF, 0x00, 0x0A, // 150 +0xFF, 0xFF, 0x00, 0x0A, // 151 +0x03, 0xE5, 0x0C, 0x07, // 152 +0x03, 0xF1, 0x0A, 0x06, // 153 +0x03, 0xFB, 0x0C, 0x07, // 154 +0x04, 0x07, 0x08, 0x05, // 155 +0xFF, 0xFF, 0x00, 0x0A, // 156 +0xFF, 0xFF, 0x00, 0x0A, // 157 +0xFF, 0xFF, 0x00, 0x0A, // 158 +0xFF, 0xFF, 0x00, 0x0A, // 159 +0xFF, 0xFF, 0x00, 0x0A, // 160 +0x04, 0x0F, 0x04, 0x03, // 161 +0x04, 0x13, 0x0A, 0x06, // 162 +0x04, 0x1D, 0x0C, 0x07, // 163 +0x04, 0x29, 0x0A, 0x06, // 164 +0x04, 0x33, 0x0A, 0x06, // 165 +0x04, 0x3D, 0x04, 0x03, // 166 +0x04, 0x41, 0x0A, 0x06, // 167 +0x04, 0x4B, 0x05, 0x04, // 168 +0x04, 0x50, 0x0D, 0x08, // 169 +0x04, 0x5D, 0x07, 0x05, // 170 +0x04, 0x64, 0x0A, 0x06, // 171 +0x04, 0x6E, 0x09, 0x06, // 172 +0x04, 0x77, 0x03, 0x03, // 173 +0x04, 0x7A, 0x0D, 0x08, // 174 +0x04, 0x87, 0x0B, 0x07, // 175 +0x04, 0x92, 0x07, 0x05, // 176 +0x04, 0x99, 0x0A, 0x06, // 177 +0x04, 0xA3, 0x05, 0x04, // 178 +0x04, 0xA8, 0x05, 0x04, // 179 +0x04, 0xAD, 0x05, 0x04, // 180 +0x04, 0xB2, 0x0A, 0x06, // 181 +0x04, 0xBC, 0x09, 0x06, // 182 +0x04, 0xC5, 0x03, 0x03, // 183 +0x04, 0xC8, 0x06, 0x04, // 184 +0x04, 0xCE, 0x0C, 0x07, // 185 +0x04, 0xDA, 0x07, 0x05, // 186 +0x04, 0xE1, 0x0C, 0x07, // 187 +0x04, 0xED, 0x0A, 0x06, // 188 +0x04, 0xF7, 0x10, 0x09, // 189 +0x05, 0x07, 0x10, 0x09, // 190 +0x05, 0x17, 0x0A, 0x06, // 191 +0x05, 0x21, 0x0E, 0x08, // 192 +0x05, 0x2F, 0x0E, 0x08, // 193 +0x05, 0x3D, 0x0E, 0x08, // 194 +0x05, 0x4B, 0x0E, 0x08, // 195 +0x05, 0x59, 0x0E, 0x08, // 196 +0x05, 0x67, 0x0E, 0x08, // 197 +0x05, 0x75, 0x12, 0x0A, // 198 +0x05, 0x87, 0x0C, 0x07, // 199 +0x05, 0x93, 0x0C, 0x07, // 200 +0x05, 0x9F, 0x0C, 0x07, // 201 +0x05, 0xAB, 0x0C, 0x07, // 202 +0x05, 0xB7, 0x0C, 0x07, // 203 +0x05, 0xC3, 0x05, 0x04, // 204 +0x05, 0xC8, 0x04, 0x03, // 205 +0x05, 0xCC, 0x04, 0x03, // 206 +0x05, 0xD0, 0x05, 0x04, // 207 +0x05, 0xD5, 0x0B, 0x07, // 208 +0x05, 0xE0, 0x0C, 0x07, // 209 +0x05, 0xEC, 0x0E, 0x08, // 210 +0x05, 0xFA, 0x0E, 0x08, // 211 +0x06, 0x08, 0x0E, 0x08, // 212 +0x06, 0x16, 0x0E, 0x08, // 213 +0x06, 0x24, 0x0E, 0x08, // 214 +0x06, 0x32, 0x0A, 0x06, // 215 +0x06, 0x3C, 0x0D, 0x08, // 216 +0x06, 0x49, 0x0C, 0x07, // 217 +0x06, 0x55, 0x0C, 0x07, // 218 +0x06, 0x61, 0x0C, 0x07, // 219 +0x06, 0x6D, 0x0C, 0x07, // 220 +0x06, 0x79, 0x0D, 0x08, // 221 +0x06, 0x86, 0x0B, 0x07, // 222 +0x06, 0x91, 0x0C, 0x07, // 223 +0x06, 0x9D, 0x0A, 0x06, // 224 +0x06, 0xA7, 0x0A, 0x06, // 225 +0x06, 0xB1, 0x0A, 0x06, // 226 +0x06, 0xBB, 0x0A, 0x06, // 227 +0x06, 0xC5, 0x0A, 0x06, // 228 +0x06, 0xCF, 0x0A, 0x06, // 229 +0x06, 0xD9, 0x10, 0x09, // 230 +0x06, 0xE9, 0x0A, 0x06, // 231 +0x06, 0xF3, 0x0A, 0x06, // 232 +0x06, 0xFD, 0x0A, 0x06, // 233 +0x07, 0x07, 0x0A, 0x06, // 234 +0x07, 0x11, 0x0A, 0x06, // 235 +0x07, 0x1B, 0x05, 0x04, // 236 +0x07, 0x20, 0x04, 0x03, // 237 +0x07, 0x24, 0x05, 0x04, // 238 +0x07, 0x29, 0x05, 0x04, // 239 +0x07, 0x2E, 0x0A, 0x06, // 240 +0x07, 0x38, 0x0A, 0x06, // 241 +0x07, 0x42, 0x0A, 0x06, // 242 +0x07, 0x4C, 0x0A, 0x06, // 243 +0x07, 0x56, 0x0A, 0x06, // 244 +0x07, 0x60, 0x0A, 0x06, // 245 +0x07, 0x6A, 0x0A, 0x06, // 246 +0x07, 0x74, 0x09, 0x06, // 247 +0x07, 0x7D, 0x0A, 0x06, // 248 +0x07, 0x87, 0x0A, 0x06, // 249 +0x07, 0x91, 0x0A, 0x06, // 250 +0x07, 0x9B, 0x0A, 0x06, // 251 +0x07, 0xA5, 0x0A, 0x06, // 252 +0x07, 0xAF, 0x09, 0x06, // 253 +0x07, 0xB8, 0x0A, 0x06, // 254 +0x07, 0xC2, 0x09, 0x06, // 255 +// Font Data: +0x00, 0x00, 0xF8, 0x02, // 33 +0x38, 0x00, 0x00, 0x00, 0x38, // 34 +0xA0, 0x03, 0xE0, 0x00, 0xB8, 0x03, 0xE0, 0x00, 0xB8, // 35 +0x30, 0x01, 0x28, 0x02, 0xF8, 0x07, 0x48, 0x02, 0x90, 0x01, // 36 +0x00, 0x00, 0x30, 0x00, 0x48, 0x00, 0x30, 0x03, 0xC0, 0x00, 0xB0, 0x01, 0x48, 0x02, 0x80, 0x01, // 37 +0x80, 0x01, 0x50, 0x02, 0x68, 0x02, 0xA8, 0x02, 0x18, 0x01, 0x80, 0x03, 0x80, 0x02, // 38 +0x38, // 39 +0xE0, 0x03, 0x10, 0x04, 0x08, 0x08, // 40 +0x08, 0x08, 0x10, 0x04, 0xE0, 0x03, // 41 +0x28, 0x00, 0x18, 0x00, 0x28, // 42 +0x40, 0x00, 0x40, 0x00, 0xF0, 0x01, 0x40, 0x00, 0x40, // 43 +0x00, 0x00, 0x00, 0x06, // 44 +0x80, 0x00, 0x80, // 45 +0x00, 0x00, 0x00, 0x02, // 46 +0x00, 0x03, 0xE0, 0x00, 0x18, // 47 +0xF0, 0x01, 0x08, 0x02, 0x08, 0x02, 0x08, 0x02, 0xF0, 0x01, // 48 +0x00, 0x00, 0x20, 0x00, 0x10, 0x00, 0xF8, 0x03, // 49 +0x10, 0x02, 0x08, 0x03, 0x88, 0x02, 0x48, 0x02, 0x30, 0x02, // 50 +0x10, 0x01, 0x08, 0x02, 0x48, 0x02, 0x48, 0x02, 0xB0, 0x01, // 51 +0xC0, 0x00, 0xA0, 0x00, 0x90, 0x00, 0x88, 0x00, 0xF8, 0x03, 0x80, // 52 +0x60, 0x01, 0x38, 0x02, 0x28, 0x02, 0x28, 0x02, 0xC8, 0x01, // 53 +0xF0, 0x01, 0x28, 0x02, 0x28, 0x02, 0x28, 0x02, 0xD0, 0x01, // 54 +0x08, 0x00, 0x08, 0x03, 0xC8, 0x00, 0x38, 0x00, 0x08, // 55 +0xB0, 0x01, 0x48, 0x02, 0x48, 0x02, 0x48, 0x02, 0xB0, 0x01, // 56 +0x70, 0x01, 0x88, 0x02, 0x88, 0x02, 0x88, 0x02, 0xF0, 0x01, // 57 +0x00, 0x00, 0x20, 0x02, // 58 +0x00, 0x00, 0x20, 0x06, // 59 +0x00, 0x00, 0x40, 0x00, 0xA0, 0x00, 0xA0, 0x00, 0x10, 0x01, // 60 +0xA0, 0x00, 0xA0, 0x00, 0xA0, 0x00, 0xA0, 0x00, 0xA0, // 61 +0x00, 0x00, 0x10, 0x01, 0xA0, 0x00, 0xA0, 0x00, 0x40, // 62 +0x10, 0x00, 0x08, 0x00, 0x08, 0x00, 0xC8, 0x02, 0x48, 0x00, 0x30, // 63 +0x00, 0x00, 0xC0, 0x03, 0x30, 0x04, 0xD0, 0x09, 0x28, 0x0A, 0x28, 0x0A, 0xC8, 0x0B, 0x68, 0x0A, 0x10, 0x05, 0xE0, 0x04, // 64 +0x00, 0x02, 0xC0, 0x01, 0xB0, 0x00, 0x88, 0x00, 0xB0, 0x00, 0xC0, 0x01, 0x00, 0x02, // 65 +0x00, 0x00, 0xF8, 0x03, 0x48, 0x02, 0x48, 0x02, 0x48, 0x02, 0xF0, 0x01, // 66 +0x00, 0x00, 0xF0, 0x01, 0x08, 0x02, 0x08, 0x02, 0x08, 0x02, 0x10, 0x01, // 67 +0x00, 0x00, 0xF8, 0x03, 0x08, 0x02, 0x08, 0x02, 0x10, 0x01, 0xE0, // 68 +0x00, 0x00, 0xF8, 0x03, 0x48, 0x02, 0x48, 0x02, 0x48, 0x02, 0x48, 0x02, // 69 +0x00, 0x00, 0xF8, 0x03, 0x48, 0x00, 0x48, 0x00, 0x08, // 70 +0x00, 0x00, 0xE0, 0x00, 0x10, 0x01, 0x08, 0x02, 0x48, 0x02, 0x50, 0x01, 0xC0, // 71 +0x00, 0x00, 0xF8, 0x03, 0x40, 0x00, 0x40, 0x00, 0x40, 0x00, 0xF8, 0x03, // 72 +0x00, 0x00, 0xF8, 0x03, // 73 +0x00, 0x03, 0x00, 0x02, 0x00, 0x02, 0xF8, 0x01, // 74 +0x00, 0x00, 0xF8, 0x03, 0x80, 0x00, 0x60, 0x00, 0x90, 0x00, 0x08, 0x01, 0x00, 0x02, // 75 +0x00, 0x00, 0xF8, 0x03, 0x00, 0x02, 0x00, 0x02, 0x00, 0x02, 0x00, 0x02, // 76 +0x00, 0x00, 0xF8, 0x03, 0x30, 0x00, 0xC0, 0x01, 0x00, 0x02, 0xC0, 0x01, 0x30, 0x00, 0xF8, 0x03, // 77 +0x00, 0x00, 0xF8, 0x03, 0x30, 0x00, 0x40, 0x00, 0x80, 0x01, 0xF8, 0x03, // 78 +0x00, 0x00, 0xF0, 0x01, 0x08, 0x02, 0x08, 0x02, 0x08, 0x02, 0x08, 0x02, 0xF0, 0x01, // 79 +0x00, 0x00, 0xF8, 0x03, 0x48, 0x00, 0x48, 0x00, 0x48, 0x00, 0x30, // 80 +0x00, 0x00, 0xF0, 0x01, 0x08, 0x02, 0x08, 0x02, 0x08, 0x03, 0x08, 0x03, 0xF0, 0x02, // 81 +0x00, 0x00, 0xF8, 0x03, 0x48, 0x00, 0x48, 0x00, 0xC8, 0x00, 0x30, 0x03, // 82 +0x00, 0x00, 0x30, 0x01, 0x48, 0x02, 0x48, 0x02, 0x48, 0x02, 0x90, 0x01, // 83 +0x00, 0x00, 0x08, 0x00, 0x08, 0x00, 0xF8, 0x03, 0x08, 0x00, 0x08, // 84 +0x00, 0x00, 0xF8, 0x01, 0x00, 0x02, 0x00, 0x02, 0x00, 0x02, 0xF8, 0x01, // 85 +0x08, 0x00, 0x70, 0x00, 0x80, 0x01, 0x00, 0x02, 0x80, 0x01, 0x70, 0x00, 0x08, // 86 +0x18, 0x00, 0xE0, 0x01, 0x00, 0x02, 0xF0, 0x01, 0x08, 0x00, 0xF0, 0x01, 0x00, 0x02, 0xE0, 0x01, 0x18, // 87 +0x00, 0x02, 0x08, 0x01, 0x90, 0x00, 0x60, 0x00, 0x90, 0x00, 0x08, 0x01, 0x00, 0x02, // 88 +0x08, 0x00, 0x10, 0x00, 0x20, 0x00, 0xC0, 0x03, 0x20, 0x00, 0x10, 0x00, 0x08, // 89 +0x08, 0x03, 0x88, 0x02, 0xC8, 0x02, 0x68, 0x02, 0x38, 0x02, 0x18, 0x02, // 90 +0x00, 0x00, 0xF8, 0x0F, 0x08, 0x08, // 91 +0x18, 0x00, 0xE0, 0x00, 0x00, 0x03, // 92 +0x08, 0x08, 0xF8, 0x0F, // 93 +0x40, 0x00, 0x30, 0x00, 0x08, 0x00, 0x30, 0x00, 0x40, // 94 +0x00, 0x08, 0x00, 0x08, 0x00, 0x08, 0x00, 0x08, 0x00, 0x08, 0x00, 0x08, // 95 +0x08, 0x00, 0x10, // 96 +0x00, 0x00, 0x00, 0x03, 0xA0, 0x02, 0xA0, 0x02, 0xE0, 0x03, // 97 +0x00, 0x00, 0xF8, 0x03, 0x20, 0x02, 0x20, 0x02, 0xC0, 0x01, // 98 +0x00, 0x00, 0xC0, 0x01, 0x20, 0x02, 0x20, 0x02, 0x40, 0x01, // 99 +0x00, 0x00, 0xC0, 0x01, 0x20, 0x02, 0x20, 0x02, 0xF8, 0x03, // 100 +0x00, 0x00, 0xC0, 0x01, 0xA0, 0x02, 0xA0, 0x02, 0xC0, 0x02, // 101 +0x20, 0x00, 0xF0, 0x03, 0x28, // 102 +0x00, 0x00, 0xC0, 0x05, 0x20, 0x0A, 0x20, 0x0A, 0xE0, 0x07, // 103 +0x00, 0x00, 0xF8, 0x03, 0x20, 0x00, 0x20, 0x00, 0xC0, 0x03, // 104 +0x00, 0x00, 0xE8, 0x03, // 105 +0x00, 0x08, 0xE8, 0x07, // 106 +0xF8, 0x03, 0x80, 0x00, 0xC0, 0x01, 0x20, 0x02, // 107 +0x00, 0x00, 0xF8, 0x03, // 108 +0x00, 0x00, 0xE0, 0x03, 0x20, 0x00, 0x20, 0x00, 0xE0, 0x03, 0x20, 0x00, 0x20, 0x00, 0xC0, 0x03, // 109 +0x00, 0x00, 0xE0, 0x03, 0x20, 0x00, 0x20, 0x00, 0xC0, 0x03, // 110 +0x00, 0x00, 0xC0, 0x01, 0x20, 0x02, 0x20, 0x02, 0xC0, 0x01, // 111 +0x00, 0x00, 0xE0, 0x0F, 0x20, 0x02, 0x20, 0x02, 0xC0, 0x01, // 112 +0x00, 0x00, 0xC0, 0x01, 0x20, 0x02, 0x20, 0x02, 0xE0, 0x0F, // 113 +0x00, 0x00, 0xE0, 0x03, 0x20, // 114 +0x40, 0x02, 0xA0, 0x02, 0xA0, 0x02, 0x20, 0x01, // 115 +0x20, 0x00, 0xF8, 0x03, 0x20, 0x02, // 116 +0x00, 0x00, 0xE0, 0x01, 0x00, 0x02, 0x00, 0x02, 0xE0, 0x03, // 117 +0x20, 0x00, 0xC0, 0x01, 0x00, 0x02, 0xC0, 0x01, 0x20, // 118 +0xE0, 0x01, 0x00, 0x02, 0xC0, 0x01, 0x20, 0x00, 0xC0, 0x01, 0x00, 0x02, 0xE0, 0x01, // 119 +0x20, 0x02, 0x40, 0x01, 0x80, 0x00, 0x40, 0x01, 0x20, 0x02, // 120 +0x20, 0x00, 0xC0, 0x09, 0x00, 0x06, 0xC0, 0x01, 0x20, // 121 +0x20, 0x02, 0x20, 0x03, 0xA0, 0x02, 0x60, 0x02, 0x20, 0x02, // 122 +0x80, 0x00, 0x78, 0x0F, 0x08, 0x08, // 123 +0x00, 0x00, 0xF8, 0x0F, // 124 +0x08, 0x08, 0x78, 0x0F, 0x80, // 125 +0xC0, 0x00, 0x40, 0x00, 0xC0, 0x00, 0x80, 0x00, 0xC0, // 126 +0x00, 0x00, 0xF8, 0x03, 0x40, 0x02, 0x20, 0x02, 0x00, 0x02, 0x00, 0x02, // 129 +0x40, 0x00, 0xF8, 0x03, 0x20, // 130 +0x00, 0x00, 0xF8, 0x03, 0x30, 0x00, 0x44, 0x00, 0x82, 0x01, 0xF8, 0x03, // 131 +0x00, 0x02, 0xC0, 0x01, 0xB0, 0x00, 0x88, 0x00, 0xB0, 0x00, 0xC0, 0x0D, 0x00, 0x0A, // 132 +0x00, 0x00, 0x00, 0x03, 0xA0, 0x02, 0xA0, 0x0E, 0xE0, 0x0B, // 133 +0x00, 0x00, 0xF0, 0x01, 0x08, 0x02, 0x0A, 0x02, 0x09, 0x02, 0x10, 0x01, // 134 +0x00, 0x00, 0xC0, 0x01, 0x20, 0x02, 0x28, 0x02, 0x44, 0x01, // 135 +0x00, 0x00, 0xE0, 0x03, 0x20, 0x00, 0x28, 0x00, 0xC4, 0x03, // 136 +0x20, 0x02, 0x20, 0x03, 0xA8, 0x02, 0x64, 0x02, 0x20, 0x02, // 137 +0x00, 0x00, 0xF0, 0x01, 0x08, 0x02, 0x08, 0x02, 0x0A, 0x02, 0x09, 0x02, 0xF0, 0x01, // 147 +0x00, 0x00, 0xC0, 0x01, 0x20, 0x02, 0x28, 0x02, 0xC4, 0x01, // 148 +0x00, 0x00, 0xF8, 0x03, 0x48, 0x02, 0x48, 0x0E, 0x48, 0x0A, 0x48, 0x02, // 152 +0x00, 0x00, 0xC0, 0x01, 0xA0, 0x02, 0xA0, 0x0E, 0xC0, 0x0A, // 153 +0x00, 0x00, 0x30, 0x01, 0x48, 0x02, 0x4A, 0x02, 0x49, 0x02, 0x90, 0x01, // 154 +0x40, 0x02, 0xA0, 0x02, 0xA8, 0x02, 0x24, 0x01, // 155 +0x00, 0x00, 0xA0, 0x0F, // 161 +0x00, 0x00, 0xC0, 0x01, 0xA0, 0x0F, 0x78, 0x02, 0x40, 0x01, // 162 +0x40, 0x02, 0x70, 0x03, 0xC8, 0x02, 0x48, 0x02, 0x08, 0x02, 0x10, 0x02, // 163 +0x00, 0x00, 0xE0, 0x01, 0x20, 0x01, 0x20, 0x01, 0xE0, 0x01, // 164 +0x48, 0x01, 0x70, 0x01, 0xC0, 0x03, 0x70, 0x01, 0x48, 0x01, // 165 +0x00, 0x00, 0x38, 0x0F, // 166 +0xD0, 0x04, 0x28, 0x09, 0x48, 0x09, 0x48, 0x0A, 0x90, 0x05, // 167 +0x08, 0x00, 0x00, 0x00, 0x08, // 168 +0xE0, 0x00, 0x10, 0x01, 0x48, 0x02, 0xA8, 0x02, 0xA8, 0x02, 0x10, 0x01, 0xE0, // 169 +0x68, 0x00, 0x68, 0x00, 0x68, 0x00, 0x78, // 170 +0x00, 0x00, 0x80, 0x01, 0x40, 0x02, 0x80, 0x01, 0x40, 0x02, // 171 +0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0xE0, // 172 +0x80, 0x00, 0x80, // 173 +0xE0, 0x00, 0x10, 0x01, 0xE8, 0x02, 0x68, 0x02, 0xC8, 0x02, 0x10, 0x01, 0xE0, // 174 +0x02, 0x00, 0x02, 0x00, 0x02, 0x00, 0x02, 0x00, 0x02, 0x00, 0x02, // 175 +0x00, 0x00, 0x38, 0x00, 0x28, 0x00, 0x38, // 176 +0x40, 0x02, 0x40, 0x02, 0xF0, 0x03, 0x40, 0x02, 0x40, 0x02, // 177 +0x48, 0x00, 0x68, 0x00, 0x58, // 178 +0x48, 0x00, 0x58, 0x00, 0x68, // 179 +0x00, 0x00, 0x10, 0x00, 0x08, // 180 +0x00, 0x00, 0xE0, 0x0F, 0x00, 0x02, 0x00, 0x02, 0xE0, 0x03, // 181 +0x70, 0x00, 0xF8, 0x0F, 0x08, 0x00, 0xF8, 0x0F, 0x08, // 182 +0x00, 0x00, 0x40, // 183 +0x00, 0x00, 0x00, 0x14, 0x00, 0x18, // 184 +0x08, 0x03, 0x88, 0x02, 0xCA, 0x02, 0x69, 0x02, 0x38, 0x02, 0x18, 0x02, // 185 +0x30, 0x00, 0x48, 0x00, 0x48, 0x00, 0x30, // 186 +0x08, 0x03, 0x88, 0x02, 0xC8, 0x02, 0x6A, 0x02, 0x38, 0x02, 0x18, 0x02, // 187 +0x20, 0x02, 0x20, 0x03, 0xA8, 0x02, 0x60, 0x02, 0x20, 0x02, // 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, 0x06, 0x00, 0x09, 0xA0, 0x09, 0x00, 0x04, // 191 +0x00, 0x02, 0xC0, 0x01, 0xB0, 0x00, 0x89, 0x00, 0xB2, 0x00, 0xC0, 0x01, 0x00, 0x02, // 192 +0x00, 0x02, 0xC0, 0x01, 0xB0, 0x00, 0x8A, 0x00, 0xB1, 0x00, 0xC0, 0x01, 0x00, 0x02, // 193 +0x00, 0x02, 0xC0, 0x01, 0xB2, 0x00, 0x89, 0x00, 0xB2, 0x00, 0xC0, 0x01, 0x00, 0x02, // 194 +0x00, 0x02, 0xC2, 0x01, 0xB1, 0x00, 0x8A, 0x00, 0xB1, 0x00, 0xC0, 0x01, 0x00, 0x02, // 195 +0x00, 0x02, 0xC0, 0x01, 0xB2, 0x00, 0x88, 0x00, 0xB2, 0x00, 0xC0, 0x01, 0x00, 0x02, // 196 +0x00, 0x02, 0xC0, 0x01, 0xBE, 0x00, 0x8A, 0x00, 0xBE, 0x00, 0xC0, 0x01, 0x00, 0x02, // 197 +0x00, 0x03, 0xC0, 0x00, 0xE0, 0x00, 0x98, 0x00, 0x88, 0x00, 0xF8, 0x03, 0x48, 0x02, 0x48, 0x02, 0x48, 0x02, // 198 +0x00, 0x00, 0xF0, 0x01, 0x08, 0x02, 0x08, 0x16, 0x08, 0x1A, 0x10, 0x01, // 199 +0x00, 0x00, 0xF8, 0x03, 0x49, 0x02, 0x4A, 0x02, 0x48, 0x02, 0x48, 0x02, // 200 +0x00, 0x00, 0xF8, 0x03, 0x48, 0x02, 0x4A, 0x02, 0x49, 0x02, 0x48, 0x02, // 201 +0x00, 0x00, 0xFA, 0x03, 0x49, 0x02, 0x4A, 0x02, 0x48, 0x02, 0x48, 0x02, // 202 +0x00, 0x00, 0xF8, 0x03, 0x4A, 0x02, 0x48, 0x02, 0x4A, 0x02, 0x48, 0x02, // 203 +0x00, 0x00, 0xF9, 0x03, 0x02, // 204 +0x02, 0x00, 0xF9, 0x03, // 205 +0x01, 0x00, 0xFA, 0x03, // 206 +0x02, 0x00, 0xF8, 0x03, 0x02, // 207 +0x40, 0x00, 0xF8, 0x03, 0x48, 0x02, 0x48, 0x02, 0x10, 0x01, 0xE0, // 208 +0x00, 0x00, 0xFA, 0x03, 0x31, 0x00, 0x42, 0x00, 0x81, 0x01, 0xF8, 0x03, // 209 +0x00, 0x00, 0xF0, 0x01, 0x08, 0x02, 0x09, 0x02, 0x0A, 0x02, 0x08, 0x02, 0xF0, 0x01, // 210 +0x00, 0x00, 0xF0, 0x01, 0x08, 0x02, 0x0A, 0x02, 0x09, 0x02, 0x08, 0x02, 0xF0, 0x01, // 211 +0x00, 0x00, 0xF0, 0x01, 0x08, 0x02, 0x0A, 0x02, 0x09, 0x02, 0x0A, 0x02, 0xF0, 0x01, // 212 +0x00, 0x00, 0xF0, 0x01, 0x0A, 0x02, 0x09, 0x02, 0x0A, 0x02, 0x09, 0x02, 0xF0, 0x01, // 213 +0x00, 0x00, 0xF0, 0x01, 0x0A, 0x02, 0x08, 0x02, 0x0A, 0x02, 0x08, 0x02, 0xF0, 0x01, // 214 +0x10, 0x01, 0xA0, 0x00, 0xE0, 0x00, 0xA0, 0x00, 0x10, 0x01, // 215 +0x00, 0x00, 0xF0, 0x02, 0x08, 0x03, 0xC8, 0x02, 0x28, 0x02, 0x18, 0x03, 0xE8, // 216 +0x00, 0x00, 0xF8, 0x01, 0x01, 0x02, 0x02, 0x02, 0x00, 0x02, 0xF8, 0x01, // 217 +0x00, 0x00, 0xF8, 0x01, 0x02, 0x02, 0x01, 0x02, 0x00, 0x02, 0xF8, 0x01, // 218 +0x00, 0x00, 0xF8, 0x01, 0x02, 0x02, 0x01, 0x02, 0x02, 0x02, 0xF8, 0x01, // 219 +0x00, 0x00, 0xF8, 0x01, 0x02, 0x02, 0x00, 0x02, 0x02, 0x02, 0xF8, 0x01, // 220 +0x08, 0x00, 0x10, 0x00, 0x20, 0x00, 0xC2, 0x03, 0x21, 0x00, 0x10, 0x00, 0x08, // 221 +0x00, 0x00, 0xF8, 0x03, 0x10, 0x01, 0x10, 0x01, 0x10, 0x01, 0xE0, // 222 +0x00, 0x00, 0xF0, 0x03, 0x08, 0x01, 0x48, 0x02, 0xB0, 0x02, 0x80, 0x01, // 223 +0x00, 0x00, 0x00, 0x03, 0xA4, 0x02, 0xA8, 0x02, 0xE0, 0x03, // 224 +0x00, 0x00, 0x00, 0x03, 0xA8, 0x02, 0xA4, 0x02, 0xE0, 0x03, // 225 +0x00, 0x00, 0x00, 0x03, 0xA8, 0x02, 0xA4, 0x02, 0xE8, 0x03, // 226 +0x00, 0x00, 0x08, 0x03, 0xA4, 0x02, 0xA8, 0x02, 0xE4, 0x03, // 227 +0x00, 0x00, 0x00, 0x03, 0xA8, 0x02, 0xA0, 0x02, 0xE8, 0x03, // 228 +0x00, 0x00, 0x00, 0x03, 0xAE, 0x02, 0xAA, 0x02, 0xEE, 0x03, // 229 +0x00, 0x00, 0x40, 0x03, 0xA0, 0x02, 0xA0, 0x02, 0xC0, 0x01, 0xA0, 0x02, 0xA0, 0x02, 0xC0, 0x02, // 230 +0x00, 0x00, 0xC0, 0x01, 0x20, 0x16, 0x20, 0x1A, 0x40, 0x01, // 231 +0x00, 0x00, 0xC0, 0x01, 0xA4, 0x02, 0xA8, 0x02, 0xC0, 0x02, // 232 +0x00, 0x00, 0xC0, 0x01, 0xA8, 0x02, 0xA4, 0x02, 0xC0, 0x02, // 233 +0x00, 0x00, 0xC0, 0x01, 0xA8, 0x02, 0xA4, 0x02, 0xC8, 0x02, // 234 +0x00, 0x00, 0xC0, 0x01, 0xA8, 0x02, 0xA0, 0x02, 0xC8, 0x02, // 235 +0x00, 0x00, 0xE4, 0x03, 0x08, // 236 +0x08, 0x00, 0xE4, 0x03, // 237 +0x08, 0x00, 0xE4, 0x03, 0x08, // 238 +0x08, 0x00, 0xE0, 0x03, 0x08, // 239 +0x00, 0x00, 0xC0, 0x01, 0x28, 0x02, 0x38, 0x02, 0xE0, 0x01, // 240 +0x00, 0x00, 0xE8, 0x03, 0x24, 0x00, 0x28, 0x00, 0xC4, 0x03, // 241 +0x00, 0x00, 0xC0, 0x01, 0x24, 0x02, 0x28, 0x02, 0xC0, 0x01, // 242 +0x00, 0x00, 0xC0, 0x01, 0x28, 0x02, 0x24, 0x02, 0xC0, 0x01, // 243 +0x00, 0x00, 0xC0, 0x01, 0x28, 0x02, 0x24, 0x02, 0xC8, 0x01, // 244 +0x00, 0x00, 0xC8, 0x01, 0x24, 0x02, 0x28, 0x02, 0xC4, 0x01, // 245 +0x00, 0x00, 0xC0, 0x01, 0x28, 0x02, 0x20, 0x02, 0xC8, 0x01, // 246 +0x40, 0x00, 0x40, 0x00, 0x50, 0x01, 0x40, 0x00, 0x40, // 247 +0x00, 0x00, 0xC0, 0x02, 0xA0, 0x03, 0x60, 0x02, 0xA0, 0x01, // 248 +0x00, 0x00, 0xE0, 0x01, 0x04, 0x02, 0x08, 0x02, 0xE0, 0x03, // 249 +0x00, 0x00, 0xE0, 0x01, 0x08, 0x02, 0x04, 0x02, 0xE0, 0x03, // 250 +0x00, 0x00, 0xE8, 0x01, 0x04, 0x02, 0x08, 0x02, 0xE0, 0x03, // 251 +0x00, 0x00, 0xE0, 0x01, 0x08, 0x02, 0x00, 0x02, 0xE8, 0x03, // 252 +0x20, 0x00, 0xC0, 0x09, 0x08, 0x06, 0xC4, 0x01, 0x20, // 253 +0x00, 0x00, 0xF8, 0x0F, 0x20, 0x02, 0x20, 0x02, 0xC0, 0x01, // 254 +0x20, 0x00, 0xC8, 0x09, 0x00, 0x06, 0xC8, 0x01, 0x20, // 255 +}; - // Jump Table: - 0xFF, 0xFF, 0x00, 0x03, // 32:65535 - 0x00, 0x00, 0x04, 0x03, // 33 - 0x00, 0x04, 0x05, 0x04, // 34 - 0x00, 0x09, 0x09, 0x06, // 35 - 0x00, 0x12, 0x0A, 0x06, // 36 - 0x00, 0x1C, 0x10, 0x09, // 37 - 0x00, 0x2C, 0x0E, 0x08, // 38 - 0x00, 0x3A, 0x01, 0x02, // 39 - 0x00, 0x3B, 0x06, 0x04, // 40 - 0x00, 0x41, 0x06, 0x04, // 41 - 0x00, 0x47, 0x05, 0x04, // 42 - 0x00, 0x4C, 0x09, 0x06, // 43 - 0x00, 0x55, 0x04, 0x03, // 44 - 0x00, 0x59, 0x03, 0x03, // 45 - 0x00, 0x5C, 0x04, 0x03, // 46 - 0x00, 0x60, 0x05, 0x04, // 47 - 0x00, 0x65, 0x0A, 0x06, // 48 - 0x00, 0x6F, 0x08, 0x05, // 49 - 0x00, 0x77, 0x0A, 0x06, // 50 - 0x00, 0x81, 0x0A, 0x06, // 51 - 0x00, 0x8B, 0x0B, 0x07, // 52 - 0x00, 0x96, 0x0A, 0x06, // 53 - 0x00, 0xA0, 0x0A, 0x06, // 54 - 0x00, 0xAA, 0x09, 0x06, // 55 - 0x00, 0xB3, 0x0A, 0x06, // 56 - 0x00, 0xBD, 0x0A, 0x06, // 57 - 0x00, 0xC7, 0x04, 0x03, // 58 - 0x00, 0xCB, 0x04, 0x03, // 59 - 0x00, 0xCF, 0x0A, 0x06, // 60 - 0x00, 0xD9, 0x09, 0x06, // 61 - 0x00, 0xE2, 0x09, 0x06, // 62 - 0x00, 0xEB, 0x0B, 0x07, // 63 - 0x00, 0xF6, 0x14, 0x0B, // 64 - 0x01, 0x0A, 0x0E, 0x08, // 65 - 0x01, 0x18, 0x0C, 0x07, // 66 - 0x01, 0x24, 0x0C, 0x07, // 67 - 0x01, 0x30, 0x0B, 0x07, // 68 - 0x01, 0x3B, 0x0C, 0x07, // 69 - 0x01, 0x47, 0x09, 0x06, // 70 - 0x01, 0x50, 0x0D, 0x08, // 71 - 0x01, 0x5D, 0x0C, 0x07, // 72 - 0x01, 0x69, 0x04, 0x03, // 73 - 0x01, 0x6D, 0x08, 0x05, // 74 - 0x01, 0x75, 0x0E, 0x08, // 75 - 0x01, 0x83, 0x0C, 0x07, // 76 - 0x01, 0x8F, 0x10, 0x09, // 77 - 0x01, 0x9F, 0x0C, 0x07, // 78 - 0x01, 0xAB, 0x0E, 0x08, // 79 - 0x01, 0xB9, 0x0B, 0x07, // 80 - 0x01, 0xC4, 0x0E, 0x08, // 81 - 0x01, 0xD2, 0x0C, 0x07, // 82 - 0x01, 0xDE, 0x0C, 0x07, // 83 - 0x01, 0xEA, 0x0B, 0x07, // 84 - 0x01, 0xF5, 0x0C, 0x07, // 85 - 0x02, 0x01, 0x0D, 0x08, // 86 - 0x02, 0x0E, 0x11, 0x0A, // 87 - 0x02, 0x1F, 0x0E, 0x08, // 88 - 0x02, 0x2D, 0x0D, 0x08, // 89 - 0x02, 0x3A, 0x0C, 0x07, // 90 - 0x02, 0x46, 0x06, 0x04, // 91 - 0x02, 0x4C, 0x06, 0x04, // 92 - 0x02, 0x52, 0x04, 0x03, // 93 - 0x02, 0x56, 0x09, 0x06, // 94 - 0x02, 0x5F, 0x0C, 0x07, // 95 - 0x02, 0x6B, 0x03, 0x03, // 96 - 0x02, 0x6E, 0x0A, 0x06, // 97 - 0x02, 0x78, 0x0A, 0x06, // 98 - 0x02, 0x82, 0x0A, 0x06, // 99 - 0x02, 0x8C, 0x0A, 0x06, // 100 - 0x02, 0x96, 0x0A, 0x06, // 101 - 0x02, 0xA0, 0x05, 0x04, // 102 - 0x02, 0xA5, 0x0A, 0x06, // 103 - 0x02, 0xAF, 0x0A, 0x06, // 104 - 0x02, 0xB9, 0x04, 0x03, // 105 - 0x02, 0xBD, 0x04, 0x03, // 106 - 0x02, 0xC1, 0x08, 0x05, // 107 - 0x02, 0xC9, 0x04, 0x03, // 108 - 0x02, 0xCD, 0x10, 0x09, // 109 - 0x02, 0xDD, 0x0A, 0x06, // 110 - 0x02, 0xE7, 0x0A, 0x06, // 111 - 0x02, 0xF1, 0x0A, 0x06, // 112 - 0x02, 0xFB, 0x0A, 0x06, // 113 - 0x03, 0x05, 0x05, 0x04, // 114 - 0x03, 0x0A, 0x08, 0x05, // 115 - 0x03, 0x12, 0x06, 0x04, // 116 - 0x03, 0x18, 0x0A, 0x06, // 117 - 0x03, 0x22, 0x09, 0x06, // 118 - 0x03, 0x2B, 0x0E, 0x08, // 119 - 0x03, 0x39, 0x0A, 0x06, // 120 - 0x03, 0x43, 0x09, 0x06, // 121 - 0x03, 0x4C, 0x0A, 0x06, // 122 - 0x03, 0x56, 0x06, 0x04, // 123 - 0x03, 0x5C, 0x04, 0x03, // 124 - 0x03, 0x60, 0x05, 0x04, // 125 - 0x03, 0x65, 0x09, 0x06, // 126 - 0xFF, 0xFF, 0x00, 0x0A, // 127 - 0xFF, 0xFF, 0x00, 0x0A, // 128 - 0x03, 0x6E, 0x0C, 0x07, // 129 - 0x03, 0x7A, 0x05, 0x04, // 130 - 0x03, 0x7F, 0x0C, 0x07, // 131 - 0x03, 0x8B, 0x0E, 0x08, // 132 - 0x03, 0x99, 0x0C, 0x07, // 133 - 0x03, 0xA5, 0x0C, 0x07, // 134 - 0x03, 0xB1, 0x0A, 0x06, // 135 - 0x03, 0xBB, 0x0A, 0x06, // 136 - 0x03, 0xC5, 0x0A, 0x06, // 137 - 0xFF, 0xFF, 0x00, 0x0A, // 138 - 0xFF, 0xFF, 0x00, 0x0A, // 139 - 0xFF, 0xFF, 0x00, 0x0A, // 140 - 0xFF, 0xFF, 0x00, 0x0A, // 141 - 0xFF, 0xFF, 0x00, 0x0A, // 142 - 0xFF, 0xFF, 0x00, 0x0A, // 143 - 0xFF, 0xFF, 0x00, 0x0A, // 144 - 0xFF, 0xFF, 0x00, 0x0A, // 145 - 0xFF, 0xFF, 0x00, 0x0A, // 146 - 0x03, 0xCF, 0x0E, 0x08, // 147 - 0x03, 0xDD, 0x0A, 0x06, // 148 - 0xFF, 0xFF, 0x00, 0x0A, // 149 - 0xFF, 0xFF, 0x00, 0x0A, // 150 - 0xFF, 0xFF, 0x00, 0x0A, // 151 - 0x03, 0xE7, 0x0C, 0x07, // 152 - 0x03, 0xF3, 0x0C, 0x07, // 153 - 0x03, 0xFF, 0x0C, 0x07, // 154 - 0x04, 0x0B, 0x08, 0x05, // 155 - 0xFF, 0xFF, 0x00, 0x0A, // 156 - 0xFF, 0xFF, 0x00, 0x0A, // 157 - 0xFF, 0xFF, 0x00, 0x0A, // 158 - 0xFF, 0xFF, 0x00, 0x0A, // 159 - 0xFF, 0xFF, 0x00, 0x0A, // 160 - 0x04, 0x13, 0x04, 0x03, // 161 - 0x04, 0x17, 0x0A, 0x06, // 162 - 0x04, 0x21, 0x0C, 0x07, // 163 - 0x04, 0x2D, 0x0A, 0x06, // 164 - 0x04, 0x37, 0x0A, 0x06, // 165 - 0x04, 0x41, 0x04, 0x03, // 166 - 0x04, 0x45, 0x0A, 0x06, // 167 - 0x04, 0x4F, 0x05, 0x04, // 168 - 0x04, 0x54, 0x0D, 0x08, // 169 - 0x04, 0x61, 0x07, 0x05, // 170 - 0x04, 0x68, 0x0A, 0x06, // 171 - 0x04, 0x72, 0x09, 0x06, // 172 - 0x04, 0x7B, 0x03, 0x03, // 173 - 0x04, 0x7E, 0x0D, 0x08, // 174 - 0x04, 0x8B, 0x0B, 0x07, // 175 - 0x04, 0x96, 0x07, 0x05, // 176 - 0x04, 0x9D, 0x0A, 0x06, // 177 - 0x04, 0xA7, 0x05, 0x04, // 178 - 0x04, 0xAC, 0x05, 0x04, // 179 - 0x04, 0xB1, 0x05, 0x04, // 180 - 0x04, 0xB6, 0x0A, 0x06, // 181 - 0x04, 0xC0, 0x09, 0x06, // 182 - 0x04, 0xC9, 0x03, 0x03, // 183 - 0x04, 0xCC, 0x06, 0x04, // 184 - 0x04, 0xD2, 0x0C, 0x07, // 185 - 0x04, 0xDE, 0x07, 0x05, // 186 - 0x04, 0xE5, 0x0C, 0x07, // 187 - 0x04, 0xF1, 0x0A, 0x06, // 188 - 0x04, 0xFB, 0x10, 0x09, // 189 - 0x05, 0x0B, 0x10, 0x09, // 190 - 0x05, 0x1B, 0x0A, 0x06, // 191 - 0x05, 0x25, 0x0E, 0x08, // 192 - 0x05, 0x33, 0x0E, 0x08, // 193 - 0x05, 0x41, 0x0E, 0x08, // 194 - 0x05, 0x4F, 0x0E, 0x08, // 195 - 0x05, 0x5D, 0x0E, 0x08, // 196 - 0x05, 0x6B, 0x0E, 0x08, // 197 - 0x05, 0x79, 0x12, 0x0A, // 198 - 0x05, 0x8B, 0x0C, 0x07, // 199 - 0x05, 0x97, 0x0C, 0x07, // 200 - 0x05, 0xA3, 0x0C, 0x07, // 201 - 0x05, 0xAF, 0x0C, 0x07, // 202 - 0x05, 0xBB, 0x0C, 0x07, // 203 - 0x05, 0xC7, 0x05, 0x04, // 204 - 0x05, 0xCC, 0x04, 0x03, // 205 - 0x05, 0xD0, 0x04, 0x03, // 206 - 0x05, 0xD4, 0x05, 0x04, // 207 - 0x05, 0xD9, 0x0B, 0x07, // 208 - 0x05, 0xE4, 0x0C, 0x07, // 209 - 0x05, 0xF0, 0x0E, 0x08, // 210 - 0x05, 0xFE, 0x0E, 0x08, // 211 - 0x06, 0x0C, 0x0E, 0x08, // 212 - 0x06, 0x1A, 0x0E, 0x08, // 213 - 0x06, 0x28, 0x0E, 0x08, // 214 - 0x06, 0x36, 0x0A, 0x06, // 215 - 0x06, 0x40, 0x0D, 0x08, // 216 - 0x06, 0x4D, 0x0C, 0x07, // 217 - 0x06, 0x59, 0x0C, 0x07, // 218 - 0x06, 0x65, 0x0C, 0x07, // 219 - 0x06, 0x71, 0x0C, 0x07, // 220 - 0x06, 0x7D, 0x0D, 0x08, // 221 - 0x06, 0x8A, 0x0B, 0x07, // 222 - 0x06, 0x95, 0x0C, 0x07, // 223 - 0x06, 0xA1, 0x0A, 0x06, // 224 - 0x06, 0xAB, 0x0A, 0x06, // 225 - 0x06, 0xB5, 0x0A, 0x06, // 226 - 0x06, 0xBF, 0x0A, 0x06, // 227 - 0x06, 0xC9, 0x0A, 0x06, // 228 - 0x06, 0xD3, 0x0A, 0x06, // 229 - 0x06, 0xDD, 0x10, 0x09, // 230 - 0x06, 0xED, 0x0A, 0x06, // 231 - 0x06, 0xF7, 0x0A, 0x06, // 232 - 0x07, 0x01, 0x0A, 0x06, // 233 - 0x07, 0x0B, 0x0A, 0x06, // 234 - 0x07, 0x15, 0x0A, 0x06, // 235 - 0x07, 0x1F, 0x05, 0x04, // 236 - 0x07, 0x24, 0x04, 0x03, // 237 - 0x07, 0x28, 0x05, 0x04, // 238 - 0x07, 0x2D, 0x05, 0x04, // 239 - 0x07, 0x32, 0x0A, 0x06, // 240 - 0x07, 0x3C, 0x0A, 0x06, // 241 - 0x07, 0x46, 0x0A, 0x06, // 242 - 0x07, 0x50, 0x0A, 0x06, // 243 - 0x07, 0x5A, 0x0A, 0x06, // 244 - 0x07, 0x64, 0x0A, 0x06, // 245 - 0x07, 0x6E, 0x0A, 0x06, // 246 - 0x07, 0x78, 0x09, 0x06, // 247 - 0x07, 0x81, 0x0A, 0x06, // 248 - 0x07, 0x8B, 0x0A, 0x06, // 249 - 0x07, 0x95, 0x0A, 0x06, // 250 - 0x07, 0x9F, 0x0A, 0x06, // 251 - 0x07, 0xA9, 0x0A, 0x06, // 252 - 0x07, 0xB3, 0x09, 0x06, // 253 - 0x07, 0xBC, 0x0A, 0x06, // 254 - 0x07, 0xC6, 0x09, 0x06, // 255 - // Font Data: - 0x00, 0x00, 0xF8, 0x02, // 33 - 0x38, 0x00, 0x00, 0x00, 0x38, // 34 - 0xA0, 0x03, 0xE0, 0x00, 0xB8, 0x03, 0xE0, 0x00, 0xB8, // 35 - 0x30, 0x01, 0x28, 0x02, 0xF8, 0x07, 0x48, 0x02, 0x90, 0x01, // 36 - 0x00, 0x00, 0x30, 0x00, 0x48, 0x00, 0x30, 0x03, 0xC0, 0x00, 0xB0, 0x01, 0x48, 0x02, 0x80, 0x01, // 37 - 0x80, 0x01, 0x50, 0x02, 0x68, 0x02, 0xA8, 0x02, 0x18, 0x01, 0x80, 0x03, 0x80, 0x02, // 38 - 0x38, // 39 - 0xE0, 0x03, 0x10, 0x04, 0x08, 0x08, // 40 - 0x08, 0x08, 0x10, 0x04, 0xE0, 0x03, // 41 - 0x28, 0x00, 0x18, 0x00, 0x28, // 42 - 0x40, 0x00, 0x40, 0x00, 0xF0, 0x01, 0x40, 0x00, 0x40, // 43 - 0x00, 0x00, 0x00, 0x06, // 44 - 0x80, 0x00, 0x80, // 45 - 0x00, 0x00, 0x00, 0x02, // 46 - 0x00, 0x03, 0xE0, 0x00, 0x18, // 47 - 0xF0, 0x01, 0x08, 0x02, 0x08, 0x02, 0x08, 0x02, 0xF0, 0x01, // 48 - 0x00, 0x00, 0x20, 0x00, 0x10, 0x00, 0xF8, 0x03, // 49 - 0x10, 0x02, 0x08, 0x03, 0x88, 0x02, 0x48, 0x02, 0x30, 0x02, // 50 - 0x10, 0x01, 0x08, 0x02, 0x48, 0x02, 0x48, 0x02, 0xB0, 0x01, // 51 - 0xC0, 0x00, 0xA0, 0x00, 0x90, 0x00, 0x88, 0x00, 0xF8, 0x03, 0x80, // 52 - 0x60, 0x01, 0x38, 0x02, 0x28, 0x02, 0x28, 0x02, 0xC8, 0x01, // 53 - 0xF0, 0x01, 0x28, 0x02, 0x28, 0x02, 0x28, 0x02, 0xD0, 0x01, // 54 - 0x08, 0x00, 0x08, 0x03, 0xC8, 0x00, 0x38, 0x00, 0x08, // 55 - 0xB0, 0x01, 0x48, 0x02, 0x48, 0x02, 0x48, 0x02, 0xB0, 0x01, // 56 - 0x70, 0x01, 0x88, 0x02, 0x88, 0x02, 0x88, 0x02, 0xF0, 0x01, // 57 - 0x00, 0x00, 0x20, 0x02, // 58 - 0x00, 0x00, 0x20, 0x06, // 59 - 0x00, 0x00, 0x40, 0x00, 0xA0, 0x00, 0xA0, 0x00, 0x10, 0x01, // 60 - 0xA0, 0x00, 0xA0, 0x00, 0xA0, 0x00, 0xA0, 0x00, 0xA0, // 61 - 0x00, 0x00, 0x10, 0x01, 0xA0, 0x00, 0xA0, 0x00, 0x40, // 62 - 0x10, 0x00, 0x08, 0x00, 0x08, 0x00, 0xC8, 0x02, 0x48, 0x00, 0x30, // 63 - 0x00, 0x00, 0xC0, 0x03, 0x30, 0x04, 0xD0, 0x09, 0x28, 0x0A, 0x28, 0x0A, 0xC8, 0x0B, 0x68, 0x0A, 0x10, 0x05, 0xE0, 0x04, // 64 - 0x00, 0x02, 0xC0, 0x01, 0xB0, 0x00, 0x88, 0x00, 0xB0, 0x00, 0xC0, 0x01, 0x00, 0x02, // 65 - 0x00, 0x00, 0xF8, 0x03, 0x48, 0x02, 0x48, 0x02, 0x48, 0x02, 0xF0, 0x01, // 66 - 0x00, 0x00, 0xF0, 0x01, 0x08, 0x02, 0x08, 0x02, 0x08, 0x02, 0x10, 0x01, // 67 - 0x00, 0x00, 0xF8, 0x03, 0x08, 0x02, 0x08, 0x02, 0x10, 0x01, 0xE0, // 68 - 0x00, 0x00, 0xF8, 0x03, 0x48, 0x02, 0x48, 0x02, 0x48, 0x02, 0x48, 0x02, // 69 - 0x00, 0x00, 0xF8, 0x03, 0x48, 0x00, 0x48, 0x00, 0x08, // 70 - 0x00, 0x00, 0xE0, 0x00, 0x10, 0x01, 0x08, 0x02, 0x48, 0x02, 0x50, 0x01, 0xC0, // 71 - 0x00, 0x00, 0xF8, 0x03, 0x40, 0x00, 0x40, 0x00, 0x40, 0x00, 0xF8, 0x03, // 72 - 0x00, 0x00, 0xF8, 0x03, // 73 - 0x00, 0x03, 0x00, 0x02, 0x00, 0x02, 0xF8, 0x01, // 74 - 0x00, 0x00, 0xF8, 0x03, 0x80, 0x00, 0x60, 0x00, 0x90, 0x00, 0x08, 0x01, 0x00, 0x02, // 75 - 0x00, 0x00, 0xF8, 0x03, 0x00, 0x02, 0x00, 0x02, 0x00, 0x02, 0x00, 0x02, // 76 - 0x00, 0x00, 0xF8, 0x03, 0x30, 0x00, 0xC0, 0x01, 0x00, 0x02, 0xC0, 0x01, 0x30, 0x00, 0xF8, 0x03, // 77 - 0x00, 0x00, 0xF8, 0x03, 0x30, 0x00, 0x40, 0x00, 0x80, 0x01, 0xF8, 0x03, // 78 - 0x00, 0x00, 0xF0, 0x01, 0x08, 0x02, 0x08, 0x02, 0x08, 0x02, 0x08, 0x02, 0xF0, 0x01, // 79 - 0x00, 0x00, 0xF8, 0x03, 0x48, 0x00, 0x48, 0x00, 0x48, 0x00, 0x30, // 80 - 0x00, 0x00, 0xF0, 0x01, 0x08, 0x02, 0x08, 0x02, 0x08, 0x03, 0x08, 0x03, 0xF0, 0x02, // 81 - 0x00, 0x00, 0xF8, 0x03, 0x48, 0x00, 0x48, 0x00, 0xC8, 0x00, 0x30, 0x03, // 82 - 0x00, 0x00, 0x30, 0x01, 0x48, 0x02, 0x48, 0x02, 0x48, 0x02, 0x90, 0x01, // 83 - 0x00, 0x00, 0x08, 0x00, 0x08, 0x00, 0xF8, 0x03, 0x08, 0x00, 0x08, // 84 - 0x00, 0x00, 0xF8, 0x01, 0x00, 0x02, 0x00, 0x02, 0x00, 0x02, 0xF8, 0x01, // 85 - 0x08, 0x00, 0x70, 0x00, 0x80, 0x01, 0x00, 0x02, 0x80, 0x01, 0x70, 0x00, 0x08, // 86 - 0x18, 0x00, 0xE0, 0x01, 0x00, 0x02, 0xF0, 0x01, 0x08, 0x00, 0xF0, 0x01, 0x00, 0x02, 0xE0, 0x01, 0x18, // 87 - 0x00, 0x02, 0x08, 0x01, 0x90, 0x00, 0x60, 0x00, 0x90, 0x00, 0x08, 0x01, 0x00, 0x02, // 88 - 0x08, 0x00, 0x10, 0x00, 0x20, 0x00, 0xC0, 0x03, 0x20, 0x00, 0x10, 0x00, 0x08, // 89 - 0x08, 0x03, 0x88, 0x02, 0xC8, 0x02, 0x68, 0x02, 0x38, 0x02, 0x18, 0x02, // 90 - 0x00, 0x00, 0xF8, 0x0F, 0x08, 0x08, // 91 - 0x18, 0x00, 0xE0, 0x00, 0x00, 0x03, // 92 - 0x08, 0x08, 0xF8, 0x0F, // 93 - 0x40, 0x00, 0x30, 0x00, 0x08, 0x00, 0x30, 0x00, 0x40, // 94 - 0x00, 0x08, 0x00, 0x08, 0x00, 0x08, 0x00, 0x08, 0x00, 0x08, 0x00, 0x08, // 95 - 0x08, 0x00, 0x10, // 96 - 0x00, 0x00, 0x00, 0x03, 0xA0, 0x02, 0xA0, 0x02, 0xE0, 0x03, // 97 - 0x00, 0x00, 0xF8, 0x03, 0x20, 0x02, 0x20, 0x02, 0xC0, 0x01, // 98 - 0x00, 0x00, 0xC0, 0x01, 0x20, 0x02, 0x20, 0x02, 0x40, 0x01, // 99 - 0x00, 0x00, 0xC0, 0x01, 0x20, 0x02, 0x20, 0x02, 0xF8, 0x03, // 100 - 0x00, 0x00, 0xC0, 0x01, 0xA0, 0x02, 0xA0, 0x02, 0xC0, 0x02, // 101 - 0x20, 0x00, 0xF0, 0x03, 0x28, // 102 - 0x00, 0x00, 0xC0, 0x05, 0x20, 0x0A, 0x20, 0x0A, 0xE0, 0x07, // 103 - 0x00, 0x00, 0xF8, 0x03, 0x20, 0x00, 0x20, 0x00, 0xC0, 0x03, // 104 - 0x00, 0x00, 0xE8, 0x03, // 105 - 0x00, 0x08, 0xE8, 0x07, // 106 - 0xF8, 0x03, 0x80, 0x00, 0xC0, 0x01, 0x20, 0x02, // 107 - 0x00, 0x00, 0xF8, 0x03, // 108 - 0x00, 0x00, 0xE0, 0x03, 0x20, 0x00, 0x20, 0x00, 0xE0, 0x03, 0x20, 0x00, 0x20, 0x00, 0xC0, 0x03, // 109 - 0x00, 0x00, 0xE0, 0x03, 0x20, 0x00, 0x20, 0x00, 0xC0, 0x03, // 110 - 0x00, 0x00, 0xC0, 0x01, 0x20, 0x02, 0x20, 0x02, 0xC0, 0x01, // 111 - 0x00, 0x00, 0xE0, 0x0F, 0x20, 0x02, 0x20, 0x02, 0xC0, 0x01, // 112 - 0x00, 0x00, 0xC0, 0x01, 0x20, 0x02, 0x20, 0x02, 0xE0, 0x0F, // 113 - 0x00, 0x00, 0xE0, 0x03, 0x20, // 114 - 0x40, 0x02, 0xA0, 0x02, 0xA0, 0x02, 0x20, 0x01, // 115 - 0x20, 0x00, 0xF8, 0x03, 0x20, 0x02, // 116 - 0x00, 0x00, 0xE0, 0x01, 0x00, 0x02, 0x00, 0x02, 0xE0, 0x03, // 117 - 0x20, 0x00, 0xC0, 0x01, 0x00, 0x02, 0xC0, 0x01, 0x20, // 118 - 0xE0, 0x01, 0x00, 0x02, 0xC0, 0x01, 0x20, 0x00, 0xC0, 0x01, 0x00, 0x02, 0xE0, 0x01, // 119 - 0x20, 0x02, 0x40, 0x01, 0x80, 0x00, 0x40, 0x01, 0x20, 0x02, // 120 - 0x20, 0x00, 0xC0, 0x09, 0x00, 0x06, 0xC0, 0x01, 0x20, // 121 - 0x20, 0x02, 0x20, 0x03, 0xA0, 0x02, 0x60, 0x02, 0x20, 0x02, // 122 - 0x80, 0x00, 0x78, 0x0F, 0x08, 0x08, // 123 - 0x00, 0x00, 0xF8, 0x0F, // 124 - 0x08, 0x08, 0x78, 0x0F, 0x80, // 125 - 0xC0, 0x00, 0x40, 0x00, 0xC0, 0x00, 0x80, 0x00, 0xC0, // 126 - 0x00, 0x00, 0xF8, 0x03, 0x40, 0x02, 0x20, 0x02, 0x00, 0x02, 0x00, 0x02, // 129 - 0x40, 0x00, 0xF8, 0x03, 0x20, // 130 - 0x00, 0x00, 0xF8, 0x03, 0x30, 0x00, 0x44, 0x00, 0x82, 0x01, 0xF8, 0x03, // 131 - 0x00, 0x02, 0xC0, 0x01, 0xB0, 0x00, 0x88, 0x00, 0xB0, 0x00, 0xC0, 0x05, 0x00, 0x0A, // 132 - 0x00, 0x00, 0x00, 0x03, 0xA0, 0x02, 0xA0, 0x02, 0xE0, 0x07, 0x00, 0x08, // 133 - 0x00, 0x00, 0xF0, 0x01, 0x08, 0x02, 0x0A, 0x02, 0x09, 0x02, 0x10, 0x01, // 134 - 0x00, 0x00, 0xC0, 0x01, 0x20, 0x02, 0x28, 0x02, 0x44, 0x01, // 135 - 0x00, 0x00, 0xE0, 0x03, 0x20, 0x00, 0x28, 0x00, 0xC4, 0x03, // 136 - 0x20, 0x02, 0x20, 0x03, 0xA8, 0x02, 0x64, 0x02, 0x20, 0x02, // 137 - 0x00, 0x00, 0xF0, 0x01, 0x08, 0x02, 0x08, 0x02, 0x0A, 0x02, 0x09, 0x02, 0xF0, 0x01, // 147 - 0x00, 0x00, 0xC0, 0x01, 0x20, 0x02, 0x28, 0x02, 0xC4, 0x01, // 148 - 0x00, 0x00, 0xF8, 0x03, 0x48, 0x02, 0x48, 0x02, 0x48, 0x06, 0x48, 0x0A, // 152 - 0x00, 0x00, 0xC0, 0x01, 0xA0, 0x02, 0xA0, 0x02, 0xC0, 0x06, 0x00, 0x08, // 153 - 0x00, 0x00, 0x30, 0x01, 0x48, 0x02, 0x4A, 0x02, 0x49, 0x02, 0x90, 0x01, // 154 - 0x40, 0x02, 0xA0, 0x02, 0xA8, 0x02, 0x24, 0x01, // 155 - 0x00, 0x00, 0xA0, 0x0F, // 161 - 0x00, 0x00, 0xC0, 0x01, 0xA0, 0x0F, 0x78, 0x02, 0x40, 0x01, // 162 - 0x40, 0x02, 0x70, 0x03, 0xC8, 0x02, 0x48, 0x02, 0x08, 0x02, 0x10, 0x02, // 163 - 0x00, 0x00, 0xE0, 0x01, 0x20, 0x01, 0x20, 0x01, 0xE0, 0x01, // 164 - 0x48, 0x01, 0x70, 0x01, 0xC0, 0x03, 0x70, 0x01, 0x48, 0x01, // 165 - 0x00, 0x00, 0x38, 0x0F, // 166 - 0xD0, 0x04, 0x28, 0x09, 0x48, 0x09, 0x48, 0x0A, 0x90, 0x05, // 167 - 0x08, 0x00, 0x00, 0x00, 0x08, // 168 - 0xE0, 0x00, 0x10, 0x01, 0x48, 0x02, 0xA8, 0x02, 0xA8, 0x02, 0x10, 0x01, 0xE0, // 169 - 0x68, 0x00, 0x68, 0x00, 0x68, 0x00, 0x78, // 170 - 0x00, 0x00, 0x80, 0x01, 0x40, 0x02, 0x80, 0x01, 0x40, 0x02, // 171 - 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0xE0, // 172 - 0x80, 0x00, 0x80, // 173 - 0xE0, 0x00, 0x10, 0x01, 0xE8, 0x02, 0x68, 0x02, 0xC8, 0x02, 0x10, 0x01, 0xE0, // 174 - 0x02, 0x00, 0x02, 0x00, 0x02, 0x00, 0x02, 0x00, 0x02, 0x00, 0x02, // 175 - 0x00, 0x00, 0x38, 0x00, 0x28, 0x00, 0x38, // 176 - 0x40, 0x02, 0x40, 0x02, 0xF0, 0x03, 0x40, 0x02, 0x40, 0x02, // 177 - 0x48, 0x00, 0x68, 0x00, 0x58, // 178 - 0x48, 0x00, 0x58, 0x00, 0x68, // 179 - 0x00, 0x00, 0x10, 0x00, 0x08, // 180 - 0x00, 0x00, 0xE0, 0x0F, 0x00, 0x02, 0x00, 0x02, 0xE0, 0x03, // 181 - 0x70, 0x00, 0xF8, 0x0F, 0x08, 0x00, 0xF8, 0x0F, 0x08, // 182 - 0x00, 0x00, 0x40, // 183 - 0x00, 0x00, 0x00, 0x14, 0x00, 0x18, // 184 - 0x08, 0x03, 0x88, 0x02, 0xCA, 0x02, 0x69, 0x02, 0x38, 0x02, 0x18, 0x02, // 185 - 0x30, 0x00, 0x48, 0x00, 0x48, 0x00, 0x30, // 186 - 0x08, 0x03, 0x88, 0x02, 0xC8, 0x02, 0x6A, 0x02, 0x38, 0x02, 0x18, 0x02, // 187 - 0x20, 0x02, 0x20, 0x03, 0xA8, 0x02, 0x60, 0x02, 0x20, 0x02, // 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, 0x06, 0x00, 0x09, 0xA0, 0x09, 0x00, 0x04, // 191 - 0x00, 0x02, 0xC0, 0x01, 0xB0, 0x00, 0x89, 0x00, 0xB2, 0x00, 0xC0, 0x01, 0x00, 0x02, // 192 - 0x00, 0x02, 0xC0, 0x01, 0xB0, 0x00, 0x8A, 0x00, 0xB1, 0x00, 0xC0, 0x01, 0x00, 0x02, // 193 - 0x00, 0x02, 0xC0, 0x01, 0xB2, 0x00, 0x89, 0x00, 0xB2, 0x00, 0xC0, 0x01, 0x00, 0x02, // 194 - 0x00, 0x02, 0xC2, 0x01, 0xB1, 0x00, 0x8A, 0x00, 0xB1, 0x00, 0xC0, 0x01, 0x00, 0x02, // 195 - 0x00, 0x02, 0xC0, 0x01, 0xB2, 0x00, 0x88, 0x00, 0xB2, 0x00, 0xC0, 0x01, 0x00, 0x02, // 196 - 0x00, 0x02, 0xC0, 0x01, 0xBE, 0x00, 0x8A, 0x00, 0xBE, 0x00, 0xC0, 0x01, 0x00, 0x02, // 197 - 0x00, 0x03, 0xC0, 0x00, 0xE0, 0x00, 0x98, 0x00, 0x88, 0x00, 0xF8, 0x03, 0x48, 0x02, 0x48, 0x02, 0x48, 0x02, // 198 - 0x00, 0x00, 0xF0, 0x01, 0x08, 0x02, 0x08, 0x16, 0x08, 0x1A, 0x10, 0x01, // 199 - 0x00, 0x00, 0xF8, 0x03, 0x49, 0x02, 0x4A, 0x02, 0x48, 0x02, 0x48, 0x02, // 200 - 0x00, 0x00, 0xF8, 0x03, 0x48, 0x02, 0x4A, 0x02, 0x49, 0x02, 0x48, 0x02, // 201 - 0x00, 0x00, 0xFA, 0x03, 0x49, 0x02, 0x4A, 0x02, 0x48, 0x02, 0x48, 0x02, // 202 - 0x00, 0x00, 0xF8, 0x03, 0x4A, 0x02, 0x48, 0x02, 0x4A, 0x02, 0x48, 0x02, // 203 - 0x00, 0x00, 0xF9, 0x03, 0x02, // 204 - 0x02, 0x00, 0xF9, 0x03, // 205 - 0x01, 0x00, 0xFA, 0x03, // 206 - 0x02, 0x00, 0xF8, 0x03, 0x02, // 207 - 0x40, 0x00, 0xF8, 0x03, 0x48, 0x02, 0x48, 0x02, 0x10, 0x01, 0xE0, // 208 - 0x00, 0x00, 0xFA, 0x03, 0x31, 0x00, 0x42, 0x00, 0x81, 0x01, 0xF8, 0x03, // 209 - 0x00, 0x00, 0xF0, 0x01, 0x08, 0x02, 0x09, 0x02, 0x0A, 0x02, 0x08, 0x02, 0xF0, 0x01, // 210 - 0x00, 0x00, 0xF0, 0x01, 0x08, 0x02, 0x0A, 0x02, 0x09, 0x02, 0x08, 0x02, 0xF0, 0x01, // 211 - 0x00, 0x00, 0xF0, 0x01, 0x08, 0x02, 0x0A, 0x02, 0x09, 0x02, 0x0A, 0x02, 0xF0, 0x01, // 212 - 0x00, 0x00, 0xF0, 0x01, 0x0A, 0x02, 0x09, 0x02, 0x0A, 0x02, 0x09, 0x02, 0xF0, 0x01, // 213 - 0x00, 0x00, 0xF0, 0x01, 0x0A, 0x02, 0x08, 0x02, 0x0A, 0x02, 0x08, 0x02, 0xF0, 0x01, // 214 - 0x10, 0x01, 0xA0, 0x00, 0xE0, 0x00, 0xA0, 0x00, 0x10, 0x01, // 215 - 0x00, 0x00, 0xF0, 0x02, 0x08, 0x03, 0xC8, 0x02, 0x28, 0x02, 0x18, 0x03, 0xE8, // 216 - 0x00, 0x00, 0xF8, 0x01, 0x01, 0x02, 0x02, 0x02, 0x00, 0x02, 0xF8, 0x01, // 217 - 0x00, 0x00, 0xF8, 0x01, 0x02, 0x02, 0x01, 0x02, 0x00, 0x02, 0xF8, 0x01, // 218 - 0x00, 0x00, 0xF8, 0x01, 0x02, 0x02, 0x01, 0x02, 0x02, 0x02, 0xF8, 0x01, // 219 - 0x00, 0x00, 0xF8, 0x01, 0x02, 0x02, 0x00, 0x02, 0x02, 0x02, 0xF8, 0x01, // 220 - 0x08, 0x00, 0x10, 0x00, 0x20, 0x00, 0xC2, 0x03, 0x21, 0x00, 0x10, 0x00, 0x08, // 221 - 0x00, 0x00, 0xF8, 0x03, 0x10, 0x01, 0x10, 0x01, 0x10, 0x01, 0xE0, // 222 - 0x00, 0x00, 0xF0, 0x03, 0x08, 0x01, 0x48, 0x02, 0xB0, 0x02, 0x80, 0x01, // 223 - 0x00, 0x00, 0x00, 0x03, 0xA4, 0x02, 0xA8, 0x02, 0xE0, 0x03, // 224 - 0x00, 0x00, 0x00, 0x03, 0xA8, 0x02, 0xA4, 0x02, 0xE0, 0x03, // 225 - 0x00, 0x00, 0x00, 0x03, 0xA8, 0x02, 0xA4, 0x02, 0xE8, 0x03, // 226 - 0x00, 0x00, 0x08, 0x03, 0xA4, 0x02, 0xA8, 0x02, 0xE4, 0x03, // 227 - 0x00, 0x00, 0x00, 0x03, 0xA8, 0x02, 0xA0, 0x02, 0xE8, 0x03, // 228 - 0x00, 0x00, 0x00, 0x03, 0xAE, 0x02, 0xAA, 0x02, 0xEE, 0x03, // 229 - 0x00, 0x00, 0x40, 0x03, 0xA0, 0x02, 0xA0, 0x02, 0xC0, 0x01, 0xA0, 0x02, 0xA0, 0x02, 0xC0, 0x02, // 230 - 0x00, 0x00, 0xC0, 0x01, 0x20, 0x16, 0x20, 0x1A, 0x40, 0x01, // 231 - 0x00, 0x00, 0xC0, 0x01, 0xA4, 0x02, 0xA8, 0x02, 0xC0, 0x02, // 232 - 0x00, 0x00, 0xC0, 0x01, 0xA8, 0x02, 0xA4, 0x02, 0xC0, 0x02, // 233 - 0x00, 0x00, 0xC0, 0x01, 0xA8, 0x02, 0xA4, 0x02, 0xC8, 0x02, // 234 - 0x00, 0x00, 0xC0, 0x01, 0xA8, 0x02, 0xA0, 0x02, 0xC8, 0x02, // 235 - 0x00, 0x00, 0xE4, 0x03, 0x08, // 236 - 0x08, 0x00, 0xE4, 0x03, // 237 - 0x08, 0x00, 0xE4, 0x03, 0x08, // 238 - 0x08, 0x00, 0xE0, 0x03, 0x08, // 239 - 0x00, 0x00, 0xC0, 0x01, 0x28, 0x02, 0x38, 0x02, 0xE0, 0x01, // 240 - 0x00, 0x00, 0xE8, 0x03, 0x24, 0x00, 0x28, 0x00, 0xC4, 0x03, // 241 - 0x00, 0x00, 0xC0, 0x01, 0x24, 0x02, 0x28, 0x02, 0xC0, 0x01, // 242 - 0x00, 0x00, 0xC0, 0x01, 0x28, 0x02, 0x24, 0x02, 0xC0, 0x01, // 243 - 0x00, 0x00, 0xC0, 0x01, 0x28, 0x02, 0x24, 0x02, 0xC8, 0x01, // 244 - 0x00, 0x00, 0xC8, 0x01, 0x24, 0x02, 0x28, 0x02, 0xC4, 0x01, // 245 - 0x00, 0x00, 0xC0, 0x01, 0x28, 0x02, 0x20, 0x02, 0xC8, 0x01, // 246 - 0x40, 0x00, 0x40, 0x00, 0x50, 0x01, 0x40, 0x00, 0x40, // 247 - 0x00, 0x00, 0xC0, 0x02, 0xA0, 0x03, 0x60, 0x02, 0xA0, 0x01, // 248 - 0x00, 0x00, 0xE0, 0x01, 0x04, 0x02, 0x08, 0x02, 0xE0, 0x03, // 249 - 0x00, 0x00, 0xE0, 0x01, 0x08, 0x02, 0x04, 0x02, 0xE0, 0x03, // 250 - 0x00, 0x00, 0xE8, 0x01, 0x04, 0x02, 0x08, 0x02, 0xE0, 0x03, // 251 - 0x00, 0x00, 0xE0, 0x01, 0x08, 0x02, 0x00, 0x02, 0xE8, 0x03, // 252 - 0x20, 0x00, 0xC0, 0x09, 0x08, 0x06, 0xC4, 0x01, 0x20, // 253 - 0x00, 0x00, 0xF8, 0x0F, 0x20, 0x02, 0x20, 0x02, 0xC0, 0x01, // 254 - 0x20, 0x00, 0xC8, 0x09, 0x00, 0x06, 0xC8, 0x01, 0x20, // 255 +const uint8_t ArialMT_Plain_16_PL[] PROGMEM = { +0x10, // Width: 16 +0x13, // Height: 19 +0x20, // First char: 32 +0xE0, // Number of chars: 224 +// Jump Table: +0xFF, 0xFF, 0x00, 0x04, // 32 +0x00, 0x00, 0x08, 0x04, // 33 +0x00, 0x08, 0x0D, 0x06, // 34 +0x00, 0x15, 0x1A, 0x0A, // 35 +0x00, 0x2F, 0x17, 0x09, // 36 +0x00, 0x46, 0x26, 0x0E, // 37 +0x00, 0x6C, 0x1D, 0x0B, // 38 +0x00, 0x89, 0x04, 0x03, // 39 +0x00, 0x8D, 0x0C, 0x05, // 40 +0x00, 0x99, 0x0B, 0x05, // 41 +0x00, 0xA4, 0x0D, 0x06, // 42 +0x00, 0xB1, 0x17, 0x09, // 43 +0x00, 0xC8, 0x09, 0x04, // 44 +0x00, 0xD1, 0x0B, 0x05, // 45 +0x00, 0xDC, 0x08, 0x04, // 46 +0x00, 0xE4, 0x0A, 0x05, // 47 +0x00, 0xEE, 0x17, 0x09, // 48 +0x01, 0x05, 0x11, 0x07, // 49 +0x01, 0x16, 0x17, 0x09, // 50 +0x01, 0x2D, 0x17, 0x09, // 51 +0x01, 0x44, 0x17, 0x09, // 52 +0x01, 0x5B, 0x17, 0x09, // 53 +0x01, 0x72, 0x17, 0x09, // 54 +0x01, 0x89, 0x16, 0x09, // 55 +0x01, 0x9F, 0x17, 0x09, // 56 +0x01, 0xB6, 0x17, 0x09, // 57 +0x01, 0xCD, 0x05, 0x03, // 58 +0x01, 0xD2, 0x06, 0x03, // 59 +0x01, 0xD8, 0x17, 0x09, // 60 +0x01, 0xEF, 0x17, 0x09, // 61 +0x02, 0x06, 0x17, 0x09, // 62 +0x02, 0x1D, 0x16, 0x09, // 63 +0x02, 0x33, 0x2F, 0x11, // 64 +0x02, 0x62, 0x1D, 0x0B, // 65 +0x02, 0x7F, 0x1D, 0x0B, // 66 +0x02, 0x9C, 0x20, 0x0C, // 67 +0x02, 0xBC, 0x20, 0x0C, // 68 +0x02, 0xDC, 0x1D, 0x0B, // 69 +0x02, 0xF9, 0x19, 0x0A, // 70 +0x03, 0x12, 0x20, 0x0C, // 71 +0x03, 0x32, 0x1D, 0x0B, // 72 +0x03, 0x4F, 0x05, 0x03, // 73 +0x03, 0x54, 0x14, 0x08, // 74 +0x03, 0x68, 0x1D, 0x0B, // 75 +0x03, 0x85, 0x17, 0x09, // 76 +0x03, 0x9C, 0x23, 0x0D, // 77 +0x03, 0xBF, 0x1D, 0x0B, // 78 +0x03, 0xDC, 0x20, 0x0C, // 79 +0x03, 0xFC, 0x1C, 0x0B, // 80 +0x04, 0x18, 0x20, 0x0C, // 81 +0x04, 0x38, 0x1D, 0x0B, // 82 +0x04, 0x55, 0x1D, 0x0B, // 83 +0x04, 0x72, 0x19, 0x0A, // 84 +0x04, 0x8B, 0x1D, 0x0B, // 85 +0x04, 0xA8, 0x1C, 0x0B, // 86 +0x04, 0xC4, 0x2B, 0x10, // 87 +0x04, 0xEF, 0x20, 0x0C, // 88 +0x05, 0x0F, 0x19, 0x0A, // 89 +0x05, 0x28, 0x1A, 0x0A, // 90 +0x05, 0x42, 0x0C, 0x05, // 91 +0x05, 0x4E, 0x0B, 0x05, // 92 +0x05, 0x59, 0x09, 0x04, // 93 +0x05, 0x62, 0x14, 0x08, // 94 +0x05, 0x76, 0x1B, 0x0A, // 95 +0x05, 0x91, 0x07, 0x04, // 96 +0x05, 0x98, 0x17, 0x09, // 97 +0x05, 0xAF, 0x17, 0x09, // 98 +0x05, 0xC6, 0x14, 0x08, // 99 +0x05, 0xDA, 0x17, 0x09, // 100 +0x05, 0xF1, 0x17, 0x09, // 101 +0x06, 0x08, 0x0A, 0x05, // 102 +0x06, 0x12, 0x17, 0x09, // 103 +0x06, 0x29, 0x14, 0x08, // 104 +0x06, 0x3D, 0x05, 0x03, // 105 +0x06, 0x42, 0x06, 0x03, // 106 +0x06, 0x48, 0x17, 0x09, // 107 +0x06, 0x5F, 0x05, 0x03, // 108 +0x06, 0x64, 0x23, 0x0D, // 109 +0x06, 0x87, 0x14, 0x08, // 110 +0x06, 0x9B, 0x17, 0x09, // 111 +0x06, 0xB2, 0x17, 0x09, // 112 +0x06, 0xC9, 0x18, 0x09, // 113 +0x06, 0xE1, 0x0D, 0x06, // 114 +0x06, 0xEE, 0x14, 0x08, // 115 +0x07, 0x02, 0x0B, 0x05, // 116 +0x07, 0x0D, 0x14, 0x08, // 117 +0x07, 0x21, 0x13, 0x08, // 118 +0x07, 0x34, 0x1F, 0x0C, // 119 +0x07, 0x53, 0x14, 0x08, // 120 +0x07, 0x67, 0x13, 0x08, // 121 +0x07, 0x7A, 0x14, 0x08, // 122 +0x07, 0x8E, 0x0F, 0x06, // 123 +0x07, 0x9D, 0x06, 0x03, // 124 +0x07, 0xA3, 0x0E, 0x06, // 125 +0x07, 0xB1, 0x17, 0x09, // 126 +0xFF, 0xFF, 0x00, 0x10, // 127 +0xFF, 0xFF, 0x00, 0x10, // 128 +0x07, 0xC8, 0x17, 0x09, // 129 +0x07, 0xDF, 0x07, 0x04, // 130 +0x07, 0xE6, 0x1D, 0x0B, // 131 +0x08, 0x03, 0x1E, 0x0B, // 132 +0x08, 0x21, 0x1B, 0x0A, // 133 +0x08, 0x3C, 0x20, 0x0C, // 134 +0x08, 0x5C, 0x14, 0x08, // 135 +0x08, 0x70, 0x14, 0x08, // 136 +0x08, 0x84, 0x14, 0x08, // 137 +0xFF, 0xFF, 0x00, 0x10, // 138 +0xFF, 0xFF, 0x00, 0x10, // 139 +0xFF, 0xFF, 0x00, 0x10, // 140 +0xFF, 0xFF, 0x00, 0x10, // 141 +0xFF, 0xFF, 0x00, 0x10, // 142 +0xFF, 0xFF, 0x00, 0x10, // 143 +0xFF, 0xFF, 0x00, 0x10, // 144 +0xFF, 0xFF, 0x00, 0x10, // 145 +0xFF, 0xFF, 0x00, 0x10, // 146 +0x08, 0x98, 0x20, 0x0C, // 147 +0x08, 0xB8, 0x17, 0x09, // 148 +0xFF, 0xFF, 0x00, 0x10, // 149 +0xFF, 0xFF, 0x00, 0x10, // 150 +0xFF, 0xFF, 0x00, 0x10, // 151 +0x08, 0xCF, 0x1D, 0x0B, // 152 +0x08, 0xEC, 0x17, 0x09, // 153 +0x09, 0x03, 0x1D, 0x0B, // 154 +0x09, 0x20, 0x14, 0x08, // 155 +0xFF, 0xFF, 0x00, 0x10, // 156 +0xFF, 0xFF, 0x00, 0x10, // 157 +0xFF, 0xFF, 0x00, 0x10, // 158 +0xFF, 0xFF, 0x00, 0x10, // 159 +0xFF, 0xFF, 0x00, 0x10, // 160 +0x09, 0x34, 0x09, 0x04, // 161 +0x09, 0x3D, 0x17, 0x09, // 162 +0x09, 0x54, 0x17, 0x09, // 163 +0x09, 0x6B, 0x14, 0x08, // 164 +0x09, 0x7F, 0x1A, 0x0A, // 165 +0x09, 0x99, 0x06, 0x03, // 166 +0x09, 0x9F, 0x17, 0x09, // 167 +0x09, 0xB6, 0x07, 0x04, // 168 +0x09, 0xBD, 0x23, 0x0D, // 169 +0x09, 0xE0, 0x0E, 0x06, // 170 +0x09, 0xEE, 0x14, 0x08, // 171 +0x0A, 0x02, 0x17, 0x09, // 172 +0x0A, 0x19, 0x0B, 0x05, // 173 +0x0A, 0x24, 0x23, 0x0D, // 174 +0x0A, 0x47, 0x19, 0x0A, // 175 +0x0A, 0x60, 0x0D, 0x06, // 176 +0x0A, 0x6D, 0x17, 0x09, // 177 +0x0A, 0x84, 0x0E, 0x06, // 178 +0x0A, 0x92, 0x0D, 0x06, // 179 +0x0A, 0x9F, 0x0A, 0x05, // 180 +0x0A, 0xA9, 0x17, 0x09, // 181 +0x0A, 0xC0, 0x19, 0x0A, // 182 +0x0A, 0xD9, 0x08, 0x04, // 183 +0x0A, 0xE1, 0x0C, 0x05, // 184 +0x0A, 0xED, 0x1A, 0x0A, // 185 +0x0B, 0x07, 0x0D, 0x06, // 186 +0x0B, 0x14, 0x1A, 0x0A, // 187 +0x0B, 0x2E, 0x14, 0x08, // 188 +0x0B, 0x42, 0x26, 0x0E, // 189 +0x0B, 0x68, 0x26, 0x0E, // 190 +0x0B, 0x8E, 0x1A, 0x0A, // 191 +0x0B, 0xA8, 0x1D, 0x0B, // 192 +0x0B, 0xC5, 0x1D, 0x0B, // 193 +0x0B, 0xE2, 0x1D, 0x0B, // 194 +0x0B, 0xFF, 0x1D, 0x0B, // 195 +0x0C, 0x1C, 0x1D, 0x0B, // 196 +0x0C, 0x39, 0x1D, 0x0B, // 197 +0x0C, 0x56, 0x2C, 0x10, // 198 +0x0C, 0x82, 0x20, 0x0C, // 199 +0x0C, 0xA2, 0x1D, 0x0B, // 200 +0x0C, 0xBF, 0x1D, 0x0B, // 201 +0x0C, 0xDC, 0x1D, 0x0B, // 202 +0x0C, 0xF9, 0x1D, 0x0B, // 203 +0x0D, 0x16, 0x05, 0x03, // 204 +0x0D, 0x1B, 0x07, 0x04, // 205 +0x0D, 0x22, 0x0A, 0x05, // 206 +0x0D, 0x2C, 0x07, 0x04, // 207 +0x0D, 0x33, 0x20, 0x0C, // 208 +0x0D, 0x53, 0x1D, 0x0B, // 209 +0x0D, 0x70, 0x20, 0x0C, // 210 +0x0D, 0x90, 0x20, 0x0C, // 211 +0x0D, 0xB0, 0x20, 0x0C, // 212 +0x0D, 0xD0, 0x20, 0x0C, // 213 +0x0D, 0xF0, 0x20, 0x0C, // 214 +0x0E, 0x10, 0x17, 0x09, // 215 +0x0E, 0x27, 0x20, 0x0C, // 216 +0x0E, 0x47, 0x1D, 0x0B, // 217 +0x0E, 0x64, 0x1D, 0x0B, // 218 +0x0E, 0x81, 0x1D, 0x0B, // 219 +0x0E, 0x9E, 0x1D, 0x0B, // 220 +0x0E, 0xBB, 0x19, 0x0A, // 221 +0x0E, 0xD4, 0x1D, 0x0B, // 222 +0x0E, 0xF1, 0x17, 0x09, // 223 +0x0F, 0x08, 0x17, 0x09, // 224 +0x0F, 0x1F, 0x17, 0x09, // 225 +0x0F, 0x36, 0x17, 0x09, // 226 +0x0F, 0x4D, 0x17, 0x09, // 227 +0x0F, 0x64, 0x17, 0x09, // 228 +0x0F, 0x7B, 0x17, 0x09, // 229 +0x0F, 0x92, 0x29, 0x0F, // 230 +0x0F, 0xBB, 0x14, 0x08, // 231 +0x0F, 0xCF, 0x17, 0x09, // 232 +0x0F, 0xE6, 0x17, 0x09, // 233 +0x0F, 0xFD, 0x17, 0x09, // 234 +0x10, 0x14, 0x17, 0x09, // 235 +0x10, 0x2B, 0x05, 0x03, // 236 +0x10, 0x30, 0x07, 0x04, // 237 +0x10, 0x37, 0x0A, 0x05, // 238 +0x10, 0x41, 0x07, 0x04, // 239 +0x10, 0x48, 0x17, 0x09, // 240 +0x10, 0x5F, 0x14, 0x08, // 241 +0x10, 0x73, 0x17, 0x09, // 242 +0x10, 0x8A, 0x17, 0x09, // 243 +0x10, 0xA1, 0x17, 0x09, // 244 +0x10, 0xB8, 0x17, 0x09, // 245 +0x10, 0xCF, 0x17, 0x09, // 246 +0x10, 0xE6, 0x17, 0x09, // 247 +0x10, 0xFD, 0x17, 0x09, // 248 +0x11, 0x14, 0x14, 0x08, // 249 +0x11, 0x28, 0x14, 0x08, // 250 +0x11, 0x3C, 0x14, 0x08, // 251 +0x11, 0x50, 0x14, 0x08, // 252 +0x11, 0x64, 0x13, 0x08, // 253 +0x11, 0x77, 0x17, 0x09, // 254 +0x11, 0x8E, 0x13, 0x08, // 255 +// 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, 0x41, 0x00, 0xFC, 0xFF, 0x00, 0x08, 0x42, 0x00, 0x10, 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, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x78, 0x00, 0x08, 0x07, 0x00, 0xC8, 0x00, 0x00, 0x28, 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, 0x06, 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, 0x10, 0x00, 0x60, 0x0C, 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, 0xE0, 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, 0xF8, 0x7F, 0x00, 0x00, 0x42, 0x00, 0x00, 0x41, 0x00, 0x80, 0x40, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, // 129 +0x00, 0x01, 0x00, 0xF8, 0x7F, 0x00, 0x80, // 130 +0x00, 0x00, 0x00, 0xF8, 0x7F, 0x00, 0x10, 0x00, 0x00, 0x60, 0x00, 0x00, 0x80, 0x00, 0x00, 0x08, 0x03, 0x00, 0x04, 0x04, 0x00, 0x02, 0x18, 0x00, 0x00, 0x20, 0x00, 0xF8, 0x7F, // 131 +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, 0x9C, 0x01, 0x00, 0x60, 0x02, // 132 +0x00, 0x00, 0x00, 0x00, 0x39, 0x00, 0x80, 0x44, 0x00, 0x40, 0x44, 0x00, 0x40, 0x44, 0x00, 0x40, 0x42, 0x00, 0x40, 0x22, 0x00, 0x80, 0x7F, 0x03, 0x00, 0x80, 0x04, // 133 +0x00, 0x00, 0x00, 0xC0, 0x0F, 0x00, 0x20, 0x10, 0x00, 0x10, 0x20, 0x00, 0x08, 0x40, 0x00, 0x08, 0x40, 0x00, 0x08, 0x40, 0x00, 0x0A, 0x40, 0x00, 0x09, 0x40, 0x00, 0x10, 0x20, 0x00, 0x20, 0x10, // 134 +0x00, 0x00, 0x00, 0x00, 0x1F, 0x00, 0x80, 0x20, 0x00, 0x40, 0x40, 0x00, 0x50, 0x40, 0x00, 0x48, 0x40, 0x00, 0x80, 0x20, // 135 +0x00, 0x00, 0x00, 0xC0, 0x7F, 0x00, 0x80, 0x00, 0x00, 0x40, 0x00, 0x00, 0x50, 0x00, 0x00, 0x48, 0x00, 0x00, 0x80, 0x7F, // 136 +0x40, 0x40, 0x00, 0x40, 0x60, 0x00, 0x40, 0x58, 0x00, 0x50, 0x44, 0x00, 0x48, 0x43, 0x00, 0xC0, 0x40, 0x00, 0x40, 0x40, // 137 +0x00, 0x00, 0x00, 0xC0, 0x0F, 0x00, 0x20, 0x10, 0x00, 0x10, 0x20, 0x00, 0x08, 0x40, 0x00, 0x08, 0x40, 0x00, 0x0A, 0x40, 0x00, 0x09, 0x40, 0x00, 0x10, 0x20, 0x00, 0x20, 0x10, 0x00, 0xC0, 0x0F, // 147 +0x00, 0x00, 0x00, 0x00, 0x1F, 0x00, 0x80, 0x20, 0x00, 0x40, 0x40, 0x00, 0x50, 0x40, 0x00, 0x48, 0x40, 0x00, 0x80, 0x20, 0x00, 0x00, 0x1F, // 148 +0x00, 0x00, 0x00, 0xF8, 0x7F, 0x00, 0x08, 0x41, 0x00, 0x08, 0x41, 0x00, 0x08, 0x41, 0x00, 0x08, 0x41, 0x00, 0x08, 0xC1, 0x01, 0x08, 0x41, 0x02, 0x08, 0x41, 0x00, 0x08, 0x40, // 152 +0x00, 0x00, 0x00, 0x00, 0x1F, 0x00, 0x80, 0x24, 0x00, 0x40, 0x44, 0x00, 0x40, 0x44, 0x03, 0x40, 0xC4, 0x04, 0x80, 0x24, 0x00, 0x00, 0x17, // 153 +0x00, 0x00, 0x00, 0x60, 0x10, 0x00, 0x90, 0x20, 0x00, 0x08, 0x41, 0x00, 0x08, 0x41, 0x00, 0x0A, 0x41, 0x00, 0x09, 0x42, 0x00, 0x08, 0x42, 0x00, 0x10, 0x22, 0x00, 0x20, 0x1C, // 154 +0x00, 0x00, 0x00, 0x80, 0x23, 0x00, 0x40, 0x44, 0x00, 0x40, 0x44, 0x00, 0x50, 0x44, 0x00, 0x48, 0x44, 0x00, 0x80, 0x38, // 155 +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0xFF, 0x03, // 161 +0x00, 0x00, 0x00, 0x00, 0x1F, 0x00, 0x80, 0x20, 0x03, 0x40, 0xF0, 0x00, 0x40, 0x4E, 0x00, 0xC0, 0x41, 0x00, 0xB8, 0x20, 0x00, 0x00, 0x11, // 162 +0x00, 0x41, 0x00, 0xE0, 0x31, 0x00, 0x10, 0x2F, 0x00, 0x08, 0x21, 0x00, 0x08, 0x21, 0x00, 0x08, 0x40, 0x00, 0x10, 0x40, 0x00, 0x20, 0x20, // 163 +0x00, 0x00, 0x00, 0x40, 0x0B, 0x00, 0x80, 0x04, 0x00, 0x40, 0x08, 0x00, 0x40, 0x08, 0x00, 0x80, 0x04, 0x00, 0x40, 0x0B, // 164 +0x08, 0x0A, 0x00, 0x10, 0x0A, 0x00, 0x60, 0x0A, 0x00, 0x80, 0x0B, 0x00, 0x00, 0x7E, 0x00, 0x80, 0x0B, 0x00, 0x60, 0x0A, 0x00, 0x10, 0x0A, 0x00, 0x08, 0x0A, // 165 +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 +0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, // 168 +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 +0xD0, 0x00, 0x00, 0x48, 0x01, 0x00, 0x28, 0x01, 0x00, 0x28, 0x01, 0x00, 0xF0, 0x01, // 170 +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 +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, // 175 +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 +0x10, 0x01, 0x00, 0x88, 0x01, 0x00, 0x48, 0x01, 0x00, 0x48, 0x01, 0x00, 0x30, 0x01, // 178 +0x90, 0x00, 0x00, 0x08, 0x01, 0x00, 0x08, 0x01, 0x00, 0x28, 0x01, 0x00, 0xD8, // 179 +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x08, // 180 +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, 0x00, 0x02, 0x00, 0x80, 0x02, 0x00, 0x00, 0x03, // 184 +0x00, 0x40, 0x00, 0x08, 0x60, 0x00, 0x08, 0x58, 0x00, 0x08, 0x44, 0x00, 0x0A, 0x43, 0x00, 0x89, 0x40, 0x00, 0x68, 0x40, 0x00, 0x18, 0x40, 0x00, 0x08, 0x40, // 185 +0xF0, 0x00, 0x00, 0x08, 0x01, 0x00, 0x08, 0x01, 0x00, 0x08, 0x01, 0x00, 0xF0, // 186 +0x00, 0x40, 0x00, 0x08, 0x60, 0x00, 0x08, 0x58, 0x00, 0x08, 0x44, 0x00, 0x0A, 0x43, 0x00, 0x88, 0x40, 0x00, 0x68, 0x40, 0x00, 0x18, 0x40, 0x00, 0x08, 0x40, // 187 +0x40, 0x40, 0x00, 0x40, 0x60, 0x00, 0x40, 0x58, 0x00, 0x50, 0x44, 0x00, 0x40, 0x43, 0x00, 0xC0, 0x40, 0x00, 0x40, 0x40, // 188 +0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x08, 0x40, 0x00, 0xF8, 0x31, 0x00, 0x00, 0x08, 0x00, 0x00, 0x04, 0x00, 0x00, 0x03, 0x00, 0x80, 0x00, 0x00, 0x60, 0x44, 0x00, 0x10, 0x62, 0x00, 0x08, 0x52, 0x00, 0x00, 0x52, 0x00, 0x00, 0x4C, // 189 +0x90, 0x00, 0x00, 0x08, 0x01, 0x00, 0x08, 0x41, 0x00, 0x28, 0x21, 0x00, 0xD8, 0x18, 0x00, 0x00, 0x04, 0x00, 0x00, 0x03, 0x00, 0x80, 0x00, 0x00, 0x40, 0x30, 0x00, 0x30, 0x28, 0x00, 0x08, 0x24, 0x00, 0x00, 0x7E, 0x00, 0x00, 0x20, // 190 +0x00, 0x00, 0x00, 0x00, 0xE0, 0x00, 0x00, 0x10, 0x01, 0x00, 0x08, 0x02, 0x40, 0x07, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x01, 0x00, 0xC0, // 191 +0x00, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x1C, 0x00, 0x80, 0x07, 0x00, 0x71, 0x04, 0x00, 0x0A, 0x04, 0x00, 0x70, 0x04, 0x00, 0x80, 0x07, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x60, // 192 +0x00, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x1C, 0x00, 0x80, 0x07, 0x00, 0x70, 0x04, 0x00, 0x0A, 0x04, 0x00, 0x71, 0x04, 0x00, 0x80, 0x07, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x60, // 193 +0x00, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x1C, 0x00, 0x80, 0x07, 0x00, 0x72, 0x04, 0x00, 0x09, 0x04, 0x00, 0x71, 0x04, 0x00, 0x82, 0x07, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x60, // 194 +0x00, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x1C, 0x00, 0x80, 0x07, 0x00, 0x72, 0x04, 0x00, 0x09, 0x04, 0x00, 0x72, 0x04, 0x00, 0x81, 0x07, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x60, // 195 +0x00, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x1C, 0x00, 0x80, 0x07, 0x00, 0x72, 0x04, 0x00, 0x08, 0x04, 0x00, 0x72, 0x04, 0x00, 0x80, 0x07, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x60, // 196 +0x00, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x1C, 0x00, 0x80, 0x07, 0x00, 0x7E, 0x04, 0x00, 0x0A, 0x04, 0x00, 0x7E, 0x04, 0x00, 0x80, 0x07, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x60, // 197 +0x00, 0x60, 0x00, 0x00, 0x18, 0x00, 0x00, 0x06, 0x00, 0x80, 0x05, 0x00, 0x60, 0x04, 0x00, 0x18, 0x04, 0x00, 0x08, 0x04, 0x00, 0x08, 0x04, 0x00, 0xF8, 0x7F, 0x00, 0x08, 0x41, 0x00, 0x08, 0x41, 0x00, 0x08, 0x41, 0x00, 0x08, 0x41, 0x00, 0x08, 0x41, 0x00, 0x08, 0x41, // 198 +0x00, 0x00, 0x00, 0xC0, 0x0F, 0x00, 0x20, 0x10, 0x00, 0x10, 0x20, 0x00, 0x08, 0x40, 0x00, 0x08, 0x40, 0x02, 0x08, 0xC0, 0x02, 0x08, 0x40, 0x03, 0x08, 0x40, 0x00, 0x10, 0x20, 0x00, 0x20, 0x10, // 199 +0x00, 0x00, 0x00, 0xF8, 0x7F, 0x00, 0x08, 0x41, 0x00, 0x08, 0x41, 0x00, 0x08, 0x41, 0x00, 0x09, 0x41, 0x00, 0x0A, 0x41, 0x00, 0x08, 0x41, 0x00, 0x08, 0x41, 0x00, 0x08, 0x40, // 200 +0x00, 0x00, 0x00, 0xF8, 0x7F, 0x00, 0x08, 0x41, 0x00, 0x08, 0x41, 0x00, 0x08, 0x41, 0x00, 0x0A, 0x41, 0x00, 0x09, 0x41, 0x00, 0x08, 0x41, 0x00, 0x08, 0x41, 0x00, 0x08, 0x40, // 201 +0x00, 0x00, 0x00, 0xF8, 0x7F, 0x00, 0x08, 0x41, 0x00, 0x08, 0x41, 0x00, 0x0A, 0x41, 0x00, 0x09, 0x41, 0x00, 0x09, 0x41, 0x00, 0x0A, 0x41, 0x00, 0x08, 0x41, 0x00, 0x08, 0x40, // 202 +0x00, 0x00, 0x00, 0xF8, 0x7F, 0x00, 0x08, 0x41, 0x00, 0x08, 0x41, 0x00, 0x0A, 0x41, 0x00, 0x08, 0x41, 0x00, 0x0A, 0x41, 0x00, 0x08, 0x41, 0x00, 0x08, 0x41, 0x00, 0x08, 0x40, // 203 +0x01, 0x00, 0x00, 0xFA, 0x7F, // 204 +0x00, 0x00, 0x00, 0xFA, 0x7F, 0x00, 0x01, // 205 +0x02, 0x00, 0x00, 0xF9, 0x7F, 0x00, 0x01, 0x00, 0x00, 0x02, // 206 +0x02, 0x00, 0x00, 0xF8, 0x7F, 0x00, 0x02, // 207 +0x00, 0x02, 0x00, 0xF8, 0x7F, 0x00, 0x08, 0x42, 0x00, 0x08, 0x42, 0x00, 0x08, 0x42, 0x00, 0x08, 0x42, 0x00, 0x08, 0x40, 0x00, 0x08, 0x40, 0x00, 0x10, 0x20, 0x00, 0x20, 0x10, 0x00, 0xC0, 0x0F, // 208 +0x00, 0x00, 0x00, 0xF8, 0x7F, 0x00, 0x10, 0x00, 0x00, 0x60, 0x00, 0x00, 0x82, 0x00, 0x00, 0x01, 0x03, 0x00, 0x02, 0x04, 0x00, 0x01, 0x18, 0x00, 0x00, 0x20, 0x00, 0xF8, 0x7F, // 209 +0x00, 0x00, 0x00, 0xC0, 0x0F, 0x00, 0x20, 0x10, 0x00, 0x10, 0x20, 0x00, 0x08, 0x40, 0x00, 0x09, 0x40, 0x00, 0x0A, 0x40, 0x00, 0x08, 0x40, 0x00, 0x10, 0x20, 0x00, 0x20, 0x10, 0x00, 0xC0, 0x0F, // 210 +0x00, 0x00, 0x00, 0xC0, 0x0F, 0x00, 0x20, 0x10, 0x00, 0x10, 0x20, 0x00, 0x08, 0x40, 0x00, 0x0A, 0x40, 0x00, 0x09, 0x40, 0x00, 0x08, 0x40, 0x00, 0x10, 0x20, 0x00, 0x20, 0x10, 0x00, 0xC0, 0x0F, // 211 +0x00, 0x00, 0x00, 0xC0, 0x0F, 0x00, 0x20, 0x10, 0x00, 0x10, 0x20, 0x00, 0x0A, 0x40, 0x00, 0x09, 0x40, 0x00, 0x09, 0x40, 0x00, 0x0A, 0x40, 0x00, 0x10, 0x20, 0x00, 0x20, 0x10, 0x00, 0xC0, 0x0F, // 212 +0x00, 0x00, 0x00, 0xC0, 0x0F, 0x00, 0x20, 0x10, 0x00, 0x10, 0x20, 0x00, 0x0A, 0x40, 0x00, 0x09, 0x40, 0x00, 0x0A, 0x40, 0x00, 0x09, 0x40, 0x00, 0x10, 0x20, 0x00, 0x20, 0x10, 0x00, 0xC0, 0x0F, // 213 +0x00, 0x00, 0x00, 0xC0, 0x0F, 0x00, 0x20, 0x10, 0x00, 0x10, 0x20, 0x00, 0x08, 0x40, 0x00, 0x0A, 0x40, 0x00, 0x08, 0x40, 0x00, 0x0A, 0x40, 0x00, 0x10, 0x20, 0x00, 0x20, 0x10, 0x00, 0xC0, 0x0F, // 214 +0x00, 0x00, 0x00, 0x40, 0x10, 0x00, 0x80, 0x08, 0x00, 0x00, 0x05, 0x00, 0x00, 0x07, 0x00, 0x00, 0x05, 0x00, 0x80, 0x08, 0x00, 0x40, 0x10, // 215 +0x00, 0x00, 0x00, 0xC0, 0x4F, 0x00, 0x20, 0x30, 0x00, 0x10, 0x30, 0x00, 0x08, 0x4C, 0x00, 0x08, 0x42, 0x00, 0x08, 0x41, 0x00, 0xC8, 0x40, 0x00, 0x30, 0x20, 0x00, 0x30, 0x10, 0x00, 0xC8, 0x0F, // 216 +0x00, 0x00, 0x00, 0xF8, 0x1F, 0x00, 0x00, 0x20, 0x00, 0x00, 0x40, 0x00, 0x01, 0x40, 0x00, 0x02, 0x40, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, 0x00, 0x20, 0x00, 0xF8, 0x1F, // 217 +0x00, 0x00, 0x00, 0xF8, 0x1F, 0x00, 0x00, 0x20, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, 0x02, 0x40, 0x00, 0x01, 0x40, 0x00, 0x00, 0x40, 0x00, 0x00, 0x20, 0x00, 0xF8, 0x1F, // 218 +0x00, 0x00, 0x00, 0xF8, 0x1F, 0x00, 0x00, 0x20, 0x00, 0x00, 0x40, 0x00, 0x02, 0x40, 0x00, 0x01, 0x40, 0x00, 0x01, 0x40, 0x00, 0x02, 0x40, 0x00, 0x00, 0x20, 0x00, 0xF8, 0x1F, // 219 +0x00, 0x00, 0x00, 0xF8, 0x1F, 0x00, 0x00, 0x20, 0x00, 0x00, 0x40, 0x00, 0x02, 0x40, 0x00, 0x00, 0x40, 0x00, 0x02, 0x40, 0x00, 0x00, 0x40, 0x00, 0x00, 0x20, 0x00, 0xF8, 0x1F, // 220 +0x08, 0x00, 0x00, 0x30, 0x00, 0x00, 0x40, 0x00, 0x00, 0x80, 0x01, 0x00, 0x02, 0x7E, 0x00, 0x81, 0x01, 0x00, 0x40, 0x00, 0x00, 0x30, 0x00, 0x00, 0x08, // 221 +0x00, 0x00, 0x00, 0xF8, 0x7F, 0x00, 0x20, 0x10, 0x00, 0x20, 0x10, 0x00, 0x20, 0x10, 0x00, 0x20, 0x10, 0x00, 0x20, 0x10, 0x00, 0x20, 0x10, 0x00, 0x40, 0x08, 0x00, 0x80, 0x07, // 222 +0x00, 0x00, 0x00, 0xE0, 0x7F, 0x00, 0x10, 0x00, 0x00, 0x08, 0x20, 0x00, 0x88, 0x43, 0x00, 0x70, 0x42, 0x00, 0x00, 0x44, 0x00, 0x00, 0x38, // 223 +0x00, 0x00, 0x00, 0x00, 0x39, 0x00, 0x80, 0x44, 0x00, 0x40, 0x44, 0x00, 0x48, 0x44, 0x00, 0x50, 0x42, 0x00, 0x40, 0x22, 0x00, 0x80, 0x7F, // 224 +0x00, 0x00, 0x00, 0x00, 0x39, 0x00, 0x80, 0x44, 0x00, 0x40, 0x44, 0x00, 0x50, 0x44, 0x00, 0x48, 0x42, 0x00, 0x40, 0x22, 0x00, 0x80, 0x7F, // 225 +0x00, 0x00, 0x00, 0x00, 0x39, 0x00, 0x80, 0x44, 0x00, 0x50, 0x44, 0x00, 0x48, 0x44, 0x00, 0x48, 0x42, 0x00, 0x50, 0x22, 0x00, 0x80, 0x7F, // 226 +0x00, 0x00, 0x00, 0x00, 0x39, 0x00, 0x80, 0x44, 0x00, 0x50, 0x44, 0x00, 0x48, 0x44, 0x00, 0x50, 0x42, 0x00, 0x48, 0x22, 0x00, 0x80, 0x7F, // 227 +0x00, 0x00, 0x00, 0x00, 0x39, 0x00, 0x80, 0x44, 0x00, 0x50, 0x44, 0x00, 0x40, 0x44, 0x00, 0x50, 0x42, 0x00, 0x40, 0x22, 0x00, 0x80, 0x7F, // 228 +0x00, 0x00, 0x00, 0x00, 0x39, 0x00, 0x80, 0x44, 0x00, 0x5C, 0x44, 0x00, 0x54, 0x44, 0x00, 0x5C, 0x42, 0x00, 0x40, 0x22, 0x00, 0x80, 0x7F, // 229 +0x00, 0x00, 0x00, 0x00, 0x39, 0x00, 0x80, 0x44, 0x00, 0x40, 0x44, 0x00, 0x40, 0x44, 0x00, 0x40, 0x42, 0x00, 0x40, 0x22, 0x00, 0x80, 0x3F, 0x00, 0x80, 0x24, 0x00, 0x40, 0x44, 0x00, 0x40, 0x44, 0x00, 0x40, 0x44, 0x00, 0x80, 0x24, 0x00, 0x00, 0x17, // 230 +0x00, 0x00, 0x00, 0x00, 0x1F, 0x00, 0x80, 0x20, 0x00, 0x40, 0x40, 0x02, 0x40, 0xC0, 0x02, 0x40, 0x40, 0x03, 0x80, 0x20, // 231 +0x00, 0x00, 0x00, 0x00, 0x1F, 0x00, 0x80, 0x24, 0x00, 0x48, 0x44, 0x00, 0x50, 0x44, 0x00, 0x40, 0x44, 0x00, 0x80, 0x24, 0x00, 0x00, 0x17, // 232 +0x00, 0x00, 0x00, 0x00, 0x1F, 0x00, 0x80, 0x24, 0x00, 0x40, 0x44, 0x00, 0x50, 0x44, 0x00, 0x48, 0x44, 0x00, 0x80, 0x24, 0x00, 0x00, 0x17, // 233 +0x00, 0x00, 0x00, 0x00, 0x1F, 0x00, 0x80, 0x24, 0x00, 0x50, 0x44, 0x00, 0x48, 0x44, 0x00, 0x48, 0x44, 0x00, 0x90, 0x24, 0x00, 0x00, 0x17, // 234 +0x00, 0x00, 0x00, 0x00, 0x1F, 0x00, 0x80, 0x24, 0x00, 0x50, 0x44, 0x00, 0x40, 0x44, 0x00, 0x50, 0x44, 0x00, 0x80, 0x24, 0x00, 0x00, 0x17, // 235 +0x08, 0x00, 0x00, 0xD0, 0x7F, // 236 +0x00, 0x00, 0x00, 0xD0, 0x7F, 0x00, 0x08, // 237 +0x10, 0x00, 0x00, 0xC8, 0x7F, 0x00, 0x08, 0x00, 0x00, 0x10, // 238 +0x10, 0x00, 0x00, 0xC0, 0x7F, 0x00, 0x10, // 239 +0x00, 0x00, 0x00, 0x00, 0x1F, 0x00, 0xA0, 0x20, 0x00, 0x68, 0x40, 0x00, 0x58, 0x40, 0x00, 0x70, 0x40, 0x00, 0xE8, 0x20, 0x00, 0x00, 0x1F, // 240 +0x00, 0x00, 0x00, 0xC0, 0x7F, 0x00, 0x90, 0x00, 0x00, 0x48, 0x00, 0x00, 0x50, 0x00, 0x00, 0x48, 0x00, 0x00, 0x80, 0x7F, // 241 +0x00, 0x00, 0x00, 0x00, 0x1F, 0x00, 0x80, 0x20, 0x00, 0x48, 0x40, 0x00, 0x50, 0x40, 0x00, 0x40, 0x40, 0x00, 0x80, 0x20, 0x00, 0x00, 0x1F, // 242 +0x00, 0x00, 0x00, 0x00, 0x1F, 0x00, 0x80, 0x20, 0x00, 0x40, 0x40, 0x00, 0x50, 0x40, 0x00, 0x48, 0x40, 0x00, 0x80, 0x20, 0x00, 0x00, 0x1F, // 243 +0x00, 0x00, 0x00, 0x00, 0x1F, 0x00, 0x80, 0x20, 0x00, 0x50, 0x40, 0x00, 0x48, 0x40, 0x00, 0x48, 0x40, 0x00, 0x90, 0x20, 0x00, 0x00, 0x1F, // 244 +0x00, 0x00, 0x00, 0x00, 0x1F, 0x00, 0x80, 0x20, 0x00, 0x50, 0x40, 0x00, 0x48, 0x40, 0x00, 0x50, 0x40, 0x00, 0x88, 0x20, 0x00, 0x00, 0x1F, // 245 +0x00, 0x00, 0x00, 0x00, 0x1F, 0x00, 0x80, 0x20, 0x00, 0x50, 0x40, 0x00, 0x40, 0x40, 0x00, 0x50, 0x40, 0x00, 0x80, 0x20, 0x00, 0x00, 0x1F, // 246 +0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x80, 0x0A, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, // 247 +0x00, 0x00, 0x00, 0x00, 0x5F, 0x00, 0x80, 0x30, 0x00, 0x40, 0x48, 0x00, 0x40, 0x44, 0x00, 0x40, 0x42, 0x00, 0x80, 0x21, 0x00, 0x40, 0x1F, // 248 +0x00, 0x00, 0x00, 0xC0, 0x3F, 0x00, 0x00, 0x40, 0x00, 0x08, 0x40, 0x00, 0x10, 0x40, 0x00, 0x00, 0x20, 0x00, 0xC0, 0x7F, // 249 +0x00, 0x00, 0x00, 0xC0, 0x3F, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, 0x10, 0x40, 0x00, 0x08, 0x20, 0x00, 0xC0, 0x7F, // 250 +0x00, 0x00, 0x00, 0xC0, 0x3F, 0x00, 0x10, 0x40, 0x00, 0x08, 0x40, 0x00, 0x08, 0x40, 0x00, 0x10, 0x20, 0x00, 0xC0, 0x7F, // 251 +0x00, 0x00, 0x00, 0xD0, 0x3F, 0x00, 0x00, 0x40, 0x00, 0x10, 0x40, 0x00, 0x00, 0x40, 0x00, 0x00, 0x20, 0x00, 0xC0, 0x7F, // 252 +0xC0, 0x01, 0x00, 0x00, 0x06, 0x02, 0x00, 0x38, 0x02, 0x10, 0xE0, 0x01, 0x08, 0x38, 0x00, 0x00, 0x07, 0x00, 0xC0, // 253 +0x00, 0x00, 0x00, 0xF8, 0xFF, 0x03, 0x80, 0x20, 0x00, 0x40, 0x40, 0x00, 0x40, 0x40, 0x00, 0x40, 0x40, 0x00, 0x80, 0x20, 0x00, 0x00, 0x1F, // 254 +0xC0, 0x01, 0x00, 0x00, 0x06, 0x02, 0x10, 0x38, 0x02, 0x00, 0xE0, 0x01, 0x10, 0x38, 0x00, 0x00, 0x07, 0x00, 0xC0, // 255 +}; + +const uint8_t ArialMT_Plain_24_PL[] PROGMEM = { +0x18, // Width: 24 +0x1C, // Height: 28 +0x20, // First char: 32 +0xE0, // Number of chars: 224 +// Jump Table: +0xFF, 0xFF, 0x00, 0x06, // 32 +0x00, 0x00, 0x13, 0x06, // 33 +0x00, 0x13, 0x1A, 0x08, // 34 +0x00, 0x2D, 0x33, 0x0E, // 35 +0x00, 0x60, 0x2F, 0x0D, // 36 +0x00, 0x8F, 0x4F, 0x15, // 37 +0x00, 0xDE, 0x3B, 0x10, // 38 +0x01, 0x19, 0x0A, 0x04, // 39 +0x01, 0x23, 0x1C, 0x08, // 40 +0x01, 0x3F, 0x1B, 0x08, // 41 +0x01, 0x5A, 0x21, 0x0A, // 42 +0x01, 0x7B, 0x32, 0x0E, // 43 +0x01, 0xAD, 0x10, 0x05, // 44 +0x01, 0xBD, 0x1B, 0x08, // 45 +0x01, 0xD8, 0x0F, 0x05, // 46 +0x01, 0xE7, 0x19, 0x08, // 47 +0x02, 0x00, 0x2F, 0x0D, // 48 +0x02, 0x2F, 0x23, 0x0A, // 49 +0x02, 0x52, 0x2F, 0x0D, // 50 +0x02, 0x81, 0x2F, 0x0D, // 51 +0x02, 0xB0, 0x2F, 0x0D, // 52 +0x02, 0xDF, 0x2F, 0x0D, // 53 +0x03, 0x0E, 0x2F, 0x0D, // 54 +0x03, 0x3D, 0x2D, 0x0D, // 55 +0x03, 0x6A, 0x2F, 0x0D, // 56 +0x03, 0x99, 0x2F, 0x0D, // 57 +0x03, 0xC8, 0x0F, 0x05, // 58 +0x03, 0xD7, 0x10, 0x05, // 59 +0x03, 0xE7, 0x2F, 0x0D, // 60 +0x04, 0x16, 0x2F, 0x0D, // 61 +0x04, 0x45, 0x2E, 0x0D, // 62 +0x04, 0x73, 0x2E, 0x0D, // 63 +0x04, 0xA1, 0x5B, 0x18, // 64 +0x04, 0xFC, 0x3B, 0x10, // 65 +0x05, 0x37, 0x3B, 0x10, // 66 +0x05, 0x72, 0x3F, 0x11, // 67 +0x05, 0xB1, 0x3F, 0x11, // 68 +0x05, 0xF0, 0x3B, 0x10, // 69 +0x06, 0x2B, 0x35, 0x0F, // 70 +0x06, 0x60, 0x43, 0x12, // 71 +0x06, 0xA3, 0x3B, 0x10, // 72 +0x06, 0xDE, 0x0F, 0x05, // 73 +0x06, 0xED, 0x27, 0x0B, // 74 +0x07, 0x14, 0x3F, 0x11, // 75 +0x07, 0x53, 0x2F, 0x0D, // 76 +0x07, 0x82, 0x43, 0x12, // 77 +0x07, 0xC5, 0x3B, 0x10, // 78 +0x08, 0x00, 0x47, 0x13, // 79 +0x08, 0x47, 0x3A, 0x10, // 80 +0x08, 0x81, 0x47, 0x13, // 81 +0x08, 0xC8, 0x3F, 0x11, // 82 +0x09, 0x07, 0x3B, 0x10, // 83 +0x09, 0x42, 0x35, 0x0F, // 84 +0x09, 0x77, 0x3B, 0x10, // 85 +0x09, 0xB2, 0x39, 0x10, // 86 +0x09, 0xEB, 0x59, 0x18, // 87 +0x0A, 0x44, 0x3B, 0x10, // 88 +0x0A, 0x7F, 0x3D, 0x11, // 89 +0x0A, 0xBC, 0x37, 0x0F, // 90 +0x0A, 0xF3, 0x14, 0x06, // 91 +0x0B, 0x07, 0x1B, 0x08, // 92 +0x0B, 0x22, 0x18, 0x07, // 93 +0x0B, 0x3A, 0x2A, 0x0C, // 94 +0x0B, 0x64, 0x34, 0x0E, // 95 +0x0B, 0x98, 0x11, 0x06, // 96 +0x0B, 0xA9, 0x2F, 0x0D, // 97 +0x0B, 0xD8, 0x33, 0x0E, // 98 +0x0C, 0x0B, 0x2B, 0x0C, // 99 +0x0C, 0x36, 0x2F, 0x0D, // 100 +0x0C, 0x65, 0x2F, 0x0D, // 101 +0x0C, 0x94, 0x1A, 0x08, // 102 +0x0C, 0xAE, 0x2F, 0x0D, // 103 +0x0C, 0xDD, 0x2F, 0x0D, // 104 +0x0D, 0x0C, 0x0F, 0x05, // 105 +0x0D, 0x1B, 0x10, 0x05, // 106 +0x0D, 0x2B, 0x2F, 0x0D, // 107 +0x0D, 0x5A, 0x0F, 0x05, // 108 +0x0D, 0x69, 0x47, 0x13, // 109 +0x0D, 0xB0, 0x2F, 0x0D, // 110 +0x0D, 0xDF, 0x2F, 0x0D, // 111 +0x0E, 0x0E, 0x33, 0x0E, // 112 +0x0E, 0x41, 0x30, 0x0D, // 113 +0x0E, 0x71, 0x1E, 0x09, // 114 +0x0E, 0x8F, 0x2B, 0x0C, // 115 +0x0E, 0xBA, 0x1B, 0x08, // 116 +0x0E, 0xD5, 0x2F, 0x0D, // 117 +0x0F, 0x04, 0x2A, 0x0C, // 118 +0x0F, 0x2E, 0x42, 0x12, // 119 +0x0F, 0x70, 0x2B, 0x0C, // 120 +0x0F, 0x9B, 0x2A, 0x0C, // 121 +0x0F, 0xC5, 0x2B, 0x0C, // 122 +0x0F, 0xF0, 0x1C, 0x08, // 123 +0x10, 0x0C, 0x10, 0x05, // 124 +0x10, 0x1C, 0x1B, 0x08, // 125 +0x10, 0x37, 0x32, 0x0E, // 126 +0xFF, 0xFF, 0x00, 0x18, // 127 +0xFF, 0xFF, 0x00, 0x18, // 128 +0x10, 0x69, 0x2F, 0x0D, // 129 +0x10, 0x98, 0x16, 0x07, // 130 +0x10, 0xAE, 0x3B, 0x10, // 131 +0x10, 0xE9, 0x40, 0x11, // 132 +0x11, 0x29, 0x34, 0x0E, // 133 +0x11, 0x5D, 0x3F, 0x11, // 134 +0x11, 0x9C, 0x2B, 0x0C, // 135 +0x11, 0xC7, 0x2F, 0x0D, // 136 +0x11, 0xF6, 0x2B, 0x0C, // 137 +0xFF, 0xFF, 0x00, 0x18, // 138 +0xFF, 0xFF, 0x00, 0x18, // 139 +0xFF, 0xFF, 0x00, 0x18, // 140 +0xFF, 0xFF, 0x00, 0x18, // 141 +0xFF, 0xFF, 0x00, 0x18, // 142 +0xFF, 0xFF, 0x00, 0x18, // 143 +0xFF, 0xFF, 0x00, 0x18, // 144 +0xFF, 0xFF, 0x00, 0x18, // 145 +0xFF, 0xFF, 0x00, 0x18, // 146 +0x12, 0x21, 0x47, 0x13, // 147 +0x12, 0x68, 0x2F, 0x0D, // 148 +0xFF, 0xFF, 0x00, 0x18, // 149 +0xFF, 0xFF, 0x00, 0x18, // 150 +0xFF, 0xFF, 0x00, 0x18, // 151 +0x12, 0x97, 0x3B, 0x10, // 152 +0x12, 0xD2, 0x2F, 0x0D, // 153 +0x13, 0x01, 0x3B, 0x10, // 154 +0x13, 0x3C, 0x2B, 0x0C, // 155 +0xFF, 0xFF, 0x00, 0x18, // 156 +0xFF, 0xFF, 0x00, 0x18, // 157 +0xFF, 0xFF, 0x00, 0x18, // 158 +0xFF, 0xFF, 0x00, 0x18, // 159 +0xFF, 0xFF, 0x00, 0x18, // 160 +0x13, 0x67, 0x14, 0x06, // 161 +0x13, 0x7B, 0x2B, 0x0C, // 162 +0x13, 0xA6, 0x2F, 0x0D, // 163 +0x13, 0xD5, 0x33, 0x0E, // 164 +0x14, 0x08, 0x31, 0x0E, // 165 +0x14, 0x39, 0x10, 0x05, // 166 +0x14, 0x49, 0x2F, 0x0D, // 167 +0x14, 0x78, 0x19, 0x08, // 168 +0x14, 0x91, 0x46, 0x13, // 169 +0x14, 0xD7, 0x1A, 0x08, // 170 +0x14, 0xF1, 0x27, 0x0B, // 171 +0x15, 0x18, 0x2F, 0x0D, // 172 +0x15, 0x47, 0x1B, 0x08, // 173 +0x15, 0x62, 0x46, 0x13, // 174 +0x15, 0xA8, 0x31, 0x0E, // 175 +0x15, 0xD9, 0x1E, 0x09, // 176 +0x15, 0xF7, 0x33, 0x0E, // 177 +0x16, 0x2A, 0x1A, 0x08, // 178 +0x16, 0x44, 0x1A, 0x08, // 179 +0x16, 0x5E, 0x19, 0x08, // 180 +0x16, 0x77, 0x2F, 0x0D, // 181 +0x16, 0xA6, 0x31, 0x0E, // 182 +0x16, 0xD7, 0x12, 0x06, // 183 +0x16, 0xE9, 0x18, 0x07, // 184 +0x17, 0x01, 0x37, 0x0F, // 185 +0x17, 0x38, 0x1E, 0x09, // 186 +0x17, 0x56, 0x37, 0x0F, // 187 +0x17, 0x8D, 0x2B, 0x0C, // 188 +0x17, 0xB8, 0x4B, 0x14, // 189 +0x18, 0x03, 0x4B, 0x14, // 190 +0x18, 0x4E, 0x33, 0x0E, // 191 +0x18, 0x81, 0x3B, 0x10, // 192 +0x18, 0xBC, 0x3B, 0x10, // 193 +0x18, 0xF7, 0x3B, 0x10, // 194 +0x19, 0x32, 0x3B, 0x10, // 195 +0x19, 0x6D, 0x3B, 0x10, // 196 +0x19, 0xA8, 0x3B, 0x10, // 197 +0x19, 0xE3, 0x5B, 0x18, // 198 +0x1A, 0x3E, 0x3F, 0x11, // 199 +0x1A, 0x7D, 0x3B, 0x10, // 200 +0x1A, 0xB8, 0x3B, 0x10, // 201 +0x1A, 0xF3, 0x3B, 0x10, // 202 +0x1B, 0x2E, 0x3B, 0x10, // 203 +0x1B, 0x69, 0x11, 0x06, // 204 +0x1B, 0x7A, 0x11, 0x06, // 205 +0x1B, 0x8B, 0x15, 0x07, // 206 +0x1B, 0xA0, 0x15, 0x07, // 207 +0x1B, 0xB5, 0x3F, 0x11, // 208 +0x1B, 0xF4, 0x3B, 0x10, // 209 +0x1C, 0x2F, 0x47, 0x13, // 210 +0x1C, 0x76, 0x47, 0x13, // 211 +0x1C, 0xBD, 0x47, 0x13, // 212 +0x1D, 0x04, 0x47, 0x13, // 213 +0x1D, 0x4B, 0x47, 0x13, // 214 +0x1D, 0x92, 0x2B, 0x0C, // 215 +0x1D, 0xBD, 0x47, 0x13, // 216 +0x1E, 0x04, 0x3B, 0x10, // 217 +0x1E, 0x3F, 0x3B, 0x10, // 218 +0x1E, 0x7A, 0x3B, 0x10, // 219 +0x1E, 0xB5, 0x3B, 0x10, // 220 +0x1E, 0xF0, 0x3D, 0x11, // 221 +0x1F, 0x2D, 0x3A, 0x10, // 222 +0x1F, 0x67, 0x37, 0x0F, // 223 +0x1F, 0x9E, 0x2F, 0x0D, // 224 +0x1F, 0xCD, 0x2F, 0x0D, // 225 +0x1F, 0xFC, 0x2F, 0x0D, // 226 +0x20, 0x2B, 0x2F, 0x0D, // 227 +0x20, 0x5A, 0x2F, 0x0D, // 228 +0x20, 0x89, 0x2F, 0x0D, // 229 +0x20, 0xB8, 0x53, 0x16, // 230 +0x21, 0x0B, 0x2B, 0x0C, // 231 +0x21, 0x36, 0x2F, 0x0D, // 232 +0x21, 0x65, 0x2F, 0x0D, // 233 +0x21, 0x94, 0x2F, 0x0D, // 234 +0x21, 0xC3, 0x2F, 0x0D, // 235 +0x21, 0xF2, 0x11, 0x06, // 236 +0x22, 0x03, 0x11, 0x06, // 237 +0x22, 0x14, 0x15, 0x07, // 238 +0x22, 0x29, 0x15, 0x07, // 239 +0x22, 0x3E, 0x2F, 0x0D, // 240 +0x22, 0x6D, 0x2F, 0x0D, // 241 +0x22, 0x9C, 0x2F, 0x0D, // 242 +0x22, 0xCB, 0x2F, 0x0D, // 243 +0x22, 0xFA, 0x2F, 0x0D, // 244 +0x23, 0x29, 0x2F, 0x0D, // 245 +0x23, 0x58, 0x2F, 0x0D, // 246 +0x23, 0x87, 0x32, 0x0E, // 247 +0x23, 0xB9, 0x33, 0x0E, // 248 +0x23, 0xEC, 0x2F, 0x0D, // 249 +0x24, 0x1B, 0x2F, 0x0D, // 250 +0x24, 0x4A, 0x2F, 0x0D, // 251 +0x24, 0x79, 0x2F, 0x0D, // 252 +0x24, 0xA8, 0x2A, 0x0C, // 253 +0x24, 0xD2, 0x2F, 0x0D, // 254 +0x25, 0x01, 0x2A, 0x0C, // 255 +// Font Data: +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0xFF, 0x33, 0x00, 0xE0, 0xFF, 0x33, // 33 +0x00, 0x00, 0x00, 0x00, 0xE0, 0x07, 0x00, 0x00, 0xE0, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0x07, 0x00, 0x00, 0xE0, 0x07, // 34 +0x00, 0x0C, 0x03, 0x00, 0x00, 0x0C, 0x33, 0x00, 0x00, 0x0C, 0x3F, 0x00, 0x00, 0xFC, 0x0F, 0x00, 0x80, 0xFF, 0x03, 0x00, 0xE0, 0x0F, 0x03, 0x00, 0x60, 0x0C, 0x33, 0x00, 0x00, 0x0C, 0x3F, 0x00, 0x00, 0xFC, 0x0F, 0x00, 0x80, 0xFF, 0x03, 0x00, 0xE0, 0x0F, 0x03, 0x00, 0x60, 0x0C, 0x03, 0x00, 0x00, 0x0C, 0x03, // 35 +0x00, 0x00, 0x00, 0x00, 0x80, 0x07, 0x06, 0x00, 0xC0, 0x0F, 0x1E, 0x00, 0xC0, 0x18, 0x1C, 0x00, 0x60, 0x18, 0x38, 0x00, 0x60, 0x30, 0x30, 0x00, 0xF0, 0xFF, 0xFF, 0x00, 0x60, 0x30, 0x30, 0x00, 0x60, 0x60, 0x38, 0x00, 0xC0, 0x60, 0x18, 0x00, 0xC0, 0xC1, 0x1F, 0x00, 0x00, 0x81, 0x07, // 36 +0x00, 0x00, 0x00, 0x00, 0x80, 0x0F, 0x00, 0x00, 0xC0, 0x1F, 0x00, 0x00, 0x60, 0x30, 0x00, 0x00, 0x20, 0x20, 0x00, 0x00, 0x20, 0x20, 0x20, 0x00, 0x60, 0x30, 0x38, 0x00, 0xC0, 0x1F, 0x1E, 0x00, 0x80, 0x8F, 0x0F, 0x00, 0x00, 0xC0, 0x03, 0x00, 0x00, 0xF0, 0x00, 0x00, 0x00, 0x3C, 0x00, 0x00, 0x00, 0x8F, 0x0F, 0x00, 0xC0, 0xC3, 0x1F, 0x00, 0xE0, 0x60, 0x30, 0x00, 0x20, 0x20, 0x20, 0x00, 0x00, 0x20, 0x20, 0x00, 0x00, 0x60, 0x30, 0x00, 0x00, 0xC0, 0x1F, 0x00, 0x00, 0x80, 0x0F, // 37 +0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x07, 0x00, 0x00, 0xC0, 0x0F, 0x00, 0x80, 0xE3, 0x1C, 0x00, 0xC0, 0x77, 0x38, 0x00, 0xE0, 0x3C, 0x30, 0x00, 0x60, 0x38, 0x30, 0x00, 0x60, 0x78, 0x30, 0x00, 0xE0, 0xEC, 0x38, 0x00, 0xC0, 0x8F, 0x1B, 0x00, 0x80, 0x03, 0x1F, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0xC0, 0x1F, 0x00, 0x00, 0xC0, 0x38, 0x00, 0x00, 0x00, 0x10, // 38 +0x00, 0x00, 0x00, 0x00, 0xE0, 0x07, 0x00, 0x00, 0xE0, 0x07, // 39 +0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0x0F, 0x00, 0x00, 0xFE, 0x7F, 0x00, 0x80, 0x0F, 0xF0, 0x01, 0xC0, 0x01, 0x80, 0x03, 0x60, 0x00, 0x00, 0x06, 0x20, 0x00, 0x00, 0x04, // 40 +0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x04, 0x60, 0x00, 0x00, 0x06, 0xC0, 0x01, 0x80, 0x03, 0x80, 0x0F, 0xF0, 0x01, 0x00, 0xFE, 0x7F, 0x00, 0x00, 0xF0, 0x0F, // 41 +0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x04, 0x00, 0x00, 0x80, 0x0F, 0x00, 0x00, 0xE0, 0x03, 0x00, 0x00, 0xE0, 0x03, 0x00, 0x00, 0x80, 0x0F, 0x00, 0x00, 0x80, 0x04, 0x00, 0x00, 0x80, // 42 +0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0xFF, 0x0F, 0x00, 0x00, 0xFF, 0x0F, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x60, // 43 +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x03, 0x00, 0x00, 0xF0, 0x01, // 44 +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, // 45 +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x30, // 46 +0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x00, 0xE0, 0x0F, 0x00, 0x00, 0xFC, 0x01, 0x00, 0x80, 0x3F, 0x00, 0x00, 0xE0, 0x03, 0x00, 0x00, 0x60, // 47 +0x00, 0x00, 0x00, 0x00, 0x00, 0xFE, 0x03, 0x00, 0x80, 0xFF, 0x0F, 0x00, 0xC0, 0x01, 0x1C, 0x00, 0xE0, 0x00, 0x38, 0x00, 0x60, 0x00, 0x30, 0x00, 0x60, 0x00, 0x30, 0x00, 0x60, 0x00, 0x30, 0x00, 0xE0, 0x00, 0x38, 0x00, 0xC0, 0x01, 0x1C, 0x00, 0x80, 0xFF, 0x0F, 0x00, 0x00, 0xFE, 0x03, // 48 +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0xE0, 0xFF, 0x3F, 0x00, 0xE0, 0xFF, 0x3F, // 49 +0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x30, 0x00, 0xC0, 0x03, 0x38, 0x00, 0xC0, 0x00, 0x3C, 0x00, 0x60, 0x00, 0x36, 0x00, 0x60, 0x00, 0x33, 0x00, 0x60, 0x80, 0x31, 0x00, 0x60, 0xC0, 0x30, 0x00, 0x60, 0x60, 0x30, 0x00, 0xC0, 0x30, 0x30, 0x00, 0xC0, 0x1F, 0x30, 0x00, 0x00, 0x0F, 0x30, // 50 +0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x06, 0x00, 0xC0, 0x01, 0x0E, 0x00, 0xC0, 0x00, 0x1C, 0x00, 0x60, 0x00, 0x30, 0x00, 0x60, 0x30, 0x30, 0x00, 0x60, 0x30, 0x30, 0x00, 0x60, 0x30, 0x30, 0x00, 0xC0, 0x38, 0x30, 0x00, 0xC0, 0x6F, 0x18, 0x00, 0x80, 0xC7, 0x0F, 0x00, 0x00, 0x80, 0x07, // 51 +0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x03, 0x00, 0x00, 0xC0, 0x03, 0x00, 0x00, 0xF0, 0x03, 0x00, 0x00, 0x3C, 0x03, 0x00, 0x00, 0x0E, 0x03, 0x00, 0x80, 0x07, 0x03, 0x00, 0xC0, 0x01, 0x03, 0x00, 0xE0, 0xFF, 0x3F, 0x00, 0xE0, 0xFF, 0x3F, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, // 52 +0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x06, 0x00, 0x80, 0x3F, 0x0E, 0x00, 0xE0, 0x1F, 0x18, 0x00, 0x60, 0x08, 0x30, 0x00, 0x60, 0x0C, 0x30, 0x00, 0x60, 0x0C, 0x30, 0x00, 0x60, 0x0C, 0x30, 0x00, 0x60, 0x0C, 0x30, 0x00, 0x60, 0x18, 0x1C, 0x00, 0x60, 0xF0, 0x0F, 0x00, 0x00, 0xE0, 0x03, // 53 +0x00, 0x00, 0x00, 0x00, 0x00, 0xFC, 0x03, 0x00, 0x80, 0xFF, 0x0F, 0x00, 0xC0, 0x63, 0x1C, 0x00, 0xC0, 0x30, 0x38, 0x00, 0x60, 0x18, 0x30, 0x00, 0x60, 0x18, 0x30, 0x00, 0x60, 0x18, 0x30, 0x00, 0x60, 0x18, 0x30, 0x00, 0xE0, 0x30, 0x18, 0x00, 0xC0, 0xF1, 0x0F, 0x00, 0x80, 0xC1, 0x07, // 54 +0x00, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x60, 0x00, 0x3C, 0x00, 0x60, 0x80, 0x3F, 0x00, 0x60, 0xE0, 0x03, 0x00, 0x60, 0x78, 0x00, 0x00, 0x60, 0x0E, 0x00, 0x00, 0x60, 0x03, 0x00, 0x00, 0xE0, 0x01, 0x00, 0x00, 0x60, // 55 +0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x07, 0x00, 0x80, 0xC7, 0x1F, 0x00, 0xC0, 0x6F, 0x18, 0x00, 0xE0, 0x38, 0x30, 0x00, 0x60, 0x30, 0x30, 0x00, 0x60, 0x30, 0x30, 0x00, 0x60, 0x30, 0x30, 0x00, 0xE0, 0x38, 0x30, 0x00, 0xC0, 0x6F, 0x18, 0x00, 0x80, 0xC7, 0x1F, 0x00, 0x00, 0x80, 0x07, // 56 +0x00, 0x00, 0x00, 0x00, 0x00, 0x1F, 0x0C, 0x00, 0x80, 0x7F, 0x1C, 0x00, 0xC0, 0x61, 0x38, 0x00, 0x60, 0xC0, 0x30, 0x00, 0x60, 0xC0, 0x30, 0x00, 0x60, 0xC0, 0x30, 0x00, 0x60, 0xC0, 0x30, 0x00, 0x60, 0x60, 0x18, 0x00, 0xC0, 0x31, 0x1E, 0x00, 0x80, 0xFF, 0x0F, 0x00, 0x00, 0xFE, 0x01, // 57 +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x30, 0x00, 0x00, 0x06, 0x30, // 58 +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x30, 0x03, 0x00, 0x06, 0xF0, 0x01, // 59 +0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00, 0xD8, 0x00, 0x00, 0x00, 0xD8, 0x00, 0x00, 0x00, 0x8C, 0x01, 0x00, 0x00, 0x8C, 0x01, 0x00, 0x00, 0x04, 0x01, 0x00, 0x00, 0x06, 0x03, 0x00, 0x00, 0x06, 0x03, 0x00, 0x00, 0x03, 0x06, // 60 +0x00, 0x00, 0x00, 0x00, 0x00, 0x8C, 0x01, 0x00, 0x00, 0x8C, 0x01, 0x00, 0x00, 0x8C, 0x01, 0x00, 0x00, 0x8C, 0x01, 0x00, 0x00, 0x8C, 0x01, 0x00, 0x00, 0x8C, 0x01, 0x00, 0x00, 0x8C, 0x01, 0x00, 0x00, 0x8C, 0x01, 0x00, 0x00, 0x8C, 0x01, 0x00, 0x00, 0x8C, 0x01, 0x00, 0x00, 0x8C, 0x01, // 61 +0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x06, 0x00, 0x00, 0x06, 0x03, 0x00, 0x00, 0x06, 0x03, 0x00, 0x00, 0x04, 0x01, 0x00, 0x00, 0x8C, 0x01, 0x00, 0x00, 0x8C, 0x01, 0x00, 0x00, 0xD8, 0x00, 0x00, 0x00, 0xD8, 0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x20, // 62 +0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x80, 0x03, 0x00, 0x00, 0xC0, 0x01, 0x00, 0x00, 0xE0, 0x00, 0x00, 0x00, 0x60, 0x80, 0x33, 0x00, 0x60, 0xC0, 0x33, 0x00, 0x60, 0xE0, 0x00, 0x00, 0x60, 0x30, 0x00, 0x00, 0xC0, 0x38, 0x00, 0x00, 0xC0, 0x1F, 0x00, 0x00, 0x00, 0x07, // 63 +0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0x0F, 0x00, 0x00, 0xF8, 0x3F, 0x00, 0x00, 0x1E, 0xF0, 0x00, 0x00, 0x07, 0xC0, 0x01, 0x80, 0xC3, 0x87, 0x01, 0xC0, 0xF1, 0x9F, 0x03, 0xC0, 0x38, 0x18, 0x03, 0xC0, 0x0C, 0x30, 0x03, 0x60, 0x0E, 0x30, 0x06, 0x60, 0x06, 0x30, 0x06, 0x60, 0x06, 0x18, 0x06, 0x60, 0x06, 0x0C, 0x06, 0x60, 0x0C, 0x1E, 0x06, 0x60, 0xF8, 0x3F, 0x06, 0xE0, 0xFE, 0x31, 0x06, 0xC0, 0x0E, 0x30, 0x06, 0xC0, 0x01, 0x18, 0x03, 0x80, 0x03, 0x1C, 0x03, 0x00, 0x07, 0x8F, 0x01, 0x00, 0xFE, 0x87, 0x01, 0x00, 0xF8, 0xC1, 0x00, 0x00, 0x00, 0x40, // 64 +0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x80, 0x0F, 0x00, 0x00, 0xF0, 0x03, 0x00, 0x00, 0xFE, 0x01, 0x00, 0x80, 0x8F, 0x01, 0x00, 0xE0, 0x83, 0x01, 0x00, 0x60, 0x80, 0x01, 0x00, 0xE0, 0x83, 0x01, 0x00, 0x80, 0x8F, 0x01, 0x00, 0x00, 0xFE, 0x01, 0x00, 0x00, 0xF0, 0x03, 0x00, 0x00, 0x80, 0x0F, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x00, 0x30, // 65 +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0xFF, 0x3F, 0x00, 0xE0, 0xFF, 0x3F, 0x00, 0x60, 0x30, 0x30, 0x00, 0x60, 0x30, 0x30, 0x00, 0x60, 0x30, 0x30, 0x00, 0x60, 0x30, 0x30, 0x00, 0x60, 0x30, 0x30, 0x00, 0x60, 0x30, 0x30, 0x00, 0x60, 0x30, 0x30, 0x00, 0xC0, 0x78, 0x30, 0x00, 0xC0, 0xFF, 0x18, 0x00, 0x80, 0xC7, 0x1F, 0x00, 0x00, 0x80, 0x07, // 66 +0x00, 0x00, 0x00, 0x00, 0x00, 0xFC, 0x01, 0x00, 0x00, 0xFF, 0x07, 0x00, 0x80, 0x07, 0x0F, 0x00, 0xC0, 0x01, 0x1C, 0x00, 0xC0, 0x00, 0x18, 0x00, 0x60, 0x00, 0x30, 0x00, 0x60, 0x00, 0x30, 0x00, 0x60, 0x00, 0x30, 0x00, 0x60, 0x00, 0x30, 0x00, 0x60, 0x00, 0x30, 0x00, 0x60, 0x00, 0x30, 0x00, 0xC0, 0x00, 0x18, 0x00, 0xC0, 0x01, 0x1C, 0x00, 0x80, 0x03, 0x0F, 0x00, 0x00, 0x02, 0x03, // 67 +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0xFF, 0x3F, 0x00, 0xE0, 0xFF, 0x3F, 0x00, 0x60, 0x00, 0x30, 0x00, 0x60, 0x00, 0x30, 0x00, 0x60, 0x00, 0x30, 0x00, 0x60, 0x00, 0x30, 0x00, 0x60, 0x00, 0x30, 0x00, 0x60, 0x00, 0x30, 0x00, 0x60, 0x00, 0x30, 0x00, 0xE0, 0x00, 0x18, 0x00, 0xC0, 0x01, 0x1C, 0x00, 0x80, 0x03, 0x0E, 0x00, 0x00, 0xFF, 0x07, 0x00, 0x00, 0xFC, 0x01, // 68 +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0xFF, 0x3F, 0x00, 0xE0, 0xFF, 0x3F, 0x00, 0x60, 0x30, 0x30, 0x00, 0x60, 0x30, 0x30, 0x00, 0x60, 0x30, 0x30, 0x00, 0x60, 0x30, 0x30, 0x00, 0x60, 0x30, 0x30, 0x00, 0x60, 0x30, 0x30, 0x00, 0x60, 0x30, 0x30, 0x00, 0x60, 0x30, 0x30, 0x00, 0x60, 0x30, 0x30, 0x00, 0x60, 0x30, 0x30, 0x00, 0x60, 0x00, 0x30, // 69 +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0xFF, 0x3F, 0x00, 0xE0, 0xFF, 0x3F, 0x00, 0x60, 0x30, 0x00, 0x00, 0x60, 0x30, 0x00, 0x00, 0x60, 0x30, 0x00, 0x00, 0x60, 0x30, 0x00, 0x00, 0x60, 0x30, 0x00, 0x00, 0x60, 0x30, 0x00, 0x00, 0x60, 0x30, 0x00, 0x00, 0x60, 0x30, 0x00, 0x00, 0x60, 0x30, 0x00, 0x00, 0x60, // 70 +0x00, 0x00, 0x00, 0x00, 0x00, 0xFC, 0x01, 0x00, 0x00, 0xFF, 0x07, 0x00, 0x80, 0x07, 0x0F, 0x00, 0xC0, 0x01, 0x1C, 0x00, 0xC0, 0x00, 0x18, 0x00, 0xE0, 0x00, 0x18, 0x00, 0x60, 0x00, 0x30, 0x00, 0x60, 0x00, 0x30, 0x00, 0x60, 0x00, 0x30, 0x00, 0x60, 0x60, 0x30, 0x00, 0x60, 0x60, 0x30, 0x00, 0xE0, 0x60, 0x38, 0x00, 0xC0, 0x60, 0x18, 0x00, 0xC0, 0x61, 0x18, 0x00, 0x80, 0xE3, 0x0F, 0x00, 0x00, 0xE2, 0x0F, // 71 +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0xFF, 0x3F, 0x00, 0xE0, 0xFF, 0x3F, 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, 0xE0, 0xFF, 0x3F, 0x00, 0xE0, 0xFF, 0x3F, // 72 +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0xFF, 0x3F, 0x00, 0xE0, 0xFF, 0x3F, // 73 +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x38, 0x00, 0xE0, 0xFF, 0x1F, 0x00, 0xE0, 0xFF, 0x0F, // 74 +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0xFF, 0x3F, 0x00, 0xE0, 0xFF, 0x3F, 0x00, 0x00, 0xE0, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0x7C, 0x00, 0x00, 0x00, 0xFE, 0x00, 0x00, 0x00, 0xE7, 0x01, 0x00, 0x80, 0x83, 0x07, 0x00, 0xC0, 0x01, 0x0F, 0x00, 0xE0, 0x00, 0x1E, 0x00, 0x60, 0x00, 0x38, 0x00, 0x20, 0x00, 0x30, 0x00, 0x00, 0x00, 0x20, // 75 +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0xFF, 0x3F, 0x00, 0xE0, 0xFF, 0x3F, 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, // 76 +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0xFF, 0x3F, 0x00, 0xE0, 0xFF, 0x3F, 0x00, 0xE0, 0x01, 0x00, 0x00, 0xC0, 0x0F, 0x00, 0x00, 0x00, 0xFE, 0x00, 0x00, 0x00, 0xE0, 0x07, 0x00, 0x00, 0x00, 0x3F, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x3F, 0x00, 0x00, 0xE0, 0x07, 0x00, 0x00, 0xFE, 0x00, 0x00, 0xC0, 0x0F, 0x00, 0x00, 0xE0, 0x01, 0x00, 0x00, 0xE0, 0xFF, 0x3F, 0x00, 0xE0, 0xFF, 0x3F, // 77 +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0xFF, 0x3F, 0x00, 0xE0, 0xFF, 0x3F, 0x00, 0xC0, 0x01, 0x00, 0x00, 0x80, 0x03, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x3C, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0xE0, 0x01, 0x00, 0x00, 0x80, 0x03, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x1C, 0x00, 0xE0, 0xFF, 0x3F, 0x00, 0xE0, 0xFF, 0x3F, // 78 +0x00, 0x00, 0x00, 0x00, 0x00, 0xFC, 0x01, 0x00, 0x00, 0xFF, 0x07, 0x00, 0x80, 0x07, 0x0F, 0x00, 0xC0, 0x01, 0x1C, 0x00, 0xC0, 0x00, 0x18, 0x00, 0xE0, 0x00, 0x38, 0x00, 0x60, 0x00, 0x30, 0x00, 0x60, 0x00, 0x30, 0x00, 0x60, 0x00, 0x30, 0x00, 0x60, 0x00, 0x30, 0x00, 0x60, 0x00, 0x30, 0x00, 0xE0, 0x00, 0x38, 0x00, 0xC0, 0x00, 0x18, 0x00, 0xC0, 0x01, 0x1C, 0x00, 0x80, 0x07, 0x0F, 0x00, 0x00, 0xFF, 0x07, 0x00, 0x00, 0xFC, 0x01, // 79 +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0xFF, 0x3F, 0x00, 0xE0, 0xFF, 0x3F, 0x00, 0x60, 0x60, 0x00, 0x00, 0x60, 0x60, 0x00, 0x00, 0x60, 0x60, 0x00, 0x00, 0x60, 0x60, 0x00, 0x00, 0x60, 0x60, 0x00, 0x00, 0x60, 0x60, 0x00, 0x00, 0x60, 0x60, 0x00, 0x00, 0x60, 0x60, 0x00, 0x00, 0xC0, 0x30, 0x00, 0x00, 0xC0, 0x3F, 0x00, 0x00, 0x00, 0x0F, // 80 +0x00, 0x00, 0x00, 0x00, 0x00, 0xFC, 0x01, 0x00, 0x00, 0xFF, 0x07, 0x00, 0x80, 0x07, 0x0F, 0x00, 0xC0, 0x01, 0x0C, 0x00, 0xC0, 0x00, 0x18, 0x00, 0xE0, 0x00, 0x18, 0x00, 0x60, 0x00, 0x30, 0x00, 0x60, 0x00, 0x30, 0x00, 0x60, 0x00, 0x30, 0x00, 0x60, 0x00, 0x36, 0x00, 0x60, 0x00, 0x36, 0x00, 0xE0, 0x00, 0x3C, 0x00, 0xC0, 0x00, 0x1C, 0x00, 0xC0, 0x01, 0x1C, 0x00, 0x80, 0x07, 0x3F, 0x00, 0x00, 0xFF, 0x77, 0x00, 0x00, 0xFC, 0x61, // 81 +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0xFF, 0x3F, 0x00, 0xE0, 0xFF, 0x3F, 0x00, 0x60, 0x30, 0x00, 0x00, 0x60, 0x30, 0x00, 0x00, 0x60, 0x30, 0x00, 0x00, 0x60, 0x30, 0x00, 0x00, 0x60, 0x70, 0x00, 0x00, 0x60, 0xF0, 0x00, 0x00, 0x60, 0xF0, 0x03, 0x00, 0x60, 0xB0, 0x07, 0x00, 0xE0, 0x18, 0x1F, 0x00, 0xC0, 0x1F, 0x3C, 0x00, 0x80, 0x0F, 0x30, 0x00, 0x00, 0x00, 0x20, // 82 +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x07, 0x0F, 0x00, 0xC0, 0x1F, 0x1C, 0x00, 0xC0, 0x18, 0x18, 0x00, 0x60, 0x38, 0x38, 0x00, 0x60, 0x30, 0x30, 0x00, 0x60, 0x30, 0x30, 0x00, 0x60, 0x30, 0x30, 0x00, 0x60, 0x30, 0x30, 0x00, 0x60, 0x70, 0x30, 0x00, 0xC0, 0x60, 0x18, 0x00, 0xC0, 0xE1, 0x18, 0x00, 0x80, 0xC3, 0x0F, 0x00, 0x00, 0x83, 0x07, // 83 +0x60, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0xE0, 0xFF, 0x3F, 0x00, 0xE0, 0xFF, 0x3F, 0x00, 0x60, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x60, // 84 +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0xFF, 0x03, 0x00, 0xE0, 0xFF, 0x0F, 0x00, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0x1C, 0x00, 0xE0, 0xFF, 0x0F, 0x00, 0xE0, 0xFF, 0x03, // 85 +0x20, 0x00, 0x00, 0x00, 0xE0, 0x01, 0x00, 0x00, 0xC0, 0x0F, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x00, 0xF8, 0x01, 0x00, 0x00, 0xC0, 0x0F, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x00, 0xC0, 0x0F, 0x00, 0x00, 0xF8, 0x01, 0x00, 0x00, 0x3E, 0x00, 0x00, 0xC0, 0x0F, 0x00, 0x00, 0xE0, 0x01, 0x00, 0x00, 0x20, // 86 +0x60, 0x00, 0x00, 0x00, 0xE0, 0x07, 0x00, 0x00, 0x80, 0xFF, 0x00, 0x00, 0x00, 0xF8, 0x0F, 0x00, 0x00, 0x80, 0x3F, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x3F, 0x00, 0x00, 0xE0, 0x0F, 0x00, 0x00, 0xFC, 0x01, 0x00, 0x80, 0x1F, 0x00, 0x00, 0xE0, 0x03, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0xE0, 0x03, 0x00, 0x00, 0x80, 0x1F, 0x00, 0x00, 0x00, 0xFC, 0x01, 0x00, 0x00, 0xE0, 0x0F, 0x00, 0x00, 0x00, 0x3F, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x80, 0x3F, 0x00, 0x00, 0xF8, 0x0F, 0x00, 0x80, 0xFF, 0x00, 0x00, 0xE0, 0x07, 0x00, 0x00, 0x60, // 87 +0x00, 0x00, 0x20, 0x00, 0x20, 0x00, 0x30, 0x00, 0x60, 0x00, 0x3C, 0x00, 0xE0, 0x01, 0x1E, 0x00, 0xC0, 0x83, 0x07, 0x00, 0x00, 0xCF, 0x03, 0x00, 0x00, 0xFE, 0x01, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0xFE, 0x01, 0x00, 0x00, 0xCF, 0x03, 0x00, 0xC0, 0x03, 0x07, 0x00, 0xE0, 0x01, 0x1E, 0x00, 0x60, 0x00, 0x3C, 0x00, 0x20, 0x00, 0x30, 0x00, 0x00, 0x00, 0x20, // 88 +0x20, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0xC0, 0x01, 0x00, 0x00, 0x80, 0x03, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x00, 0x3C, 0x00, 0x00, 0x00, 0xF0, 0x3F, 0x00, 0x00, 0xF0, 0x3F, 0x00, 0x00, 0x3C, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0xC0, 0x03, 0x00, 0x00, 0xE0, 0x01, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x20, // 89 +0x00, 0x00, 0x30, 0x00, 0x60, 0x00, 0x38, 0x00, 0x60, 0x00, 0x3C, 0x00, 0x60, 0x00, 0x37, 0x00, 0x60, 0x80, 0x33, 0x00, 0x60, 0xC0, 0x31, 0x00, 0x60, 0xE0, 0x30, 0x00, 0x60, 0x38, 0x30, 0x00, 0x60, 0x1C, 0x30, 0x00, 0x60, 0x0E, 0x30, 0x00, 0x60, 0x07, 0x30, 0x00, 0xE0, 0x01, 0x30, 0x00, 0xE0, 0x00, 0x30, 0x00, 0x60, 0x00, 0x30, // 90 +0x00, 0x00, 0x00, 0x00, 0xE0, 0xFF, 0xFF, 0x07, 0xE0, 0xFF, 0xFF, 0x07, 0x60, 0x00, 0x00, 0x06, 0x60, 0x00, 0x00, 0x06, // 91 +0x60, 0x00, 0x00, 0x00, 0xE0, 0x03, 0x00, 0x00, 0x80, 0x3F, 0x00, 0x00, 0x00, 0xFC, 0x01, 0x00, 0x00, 0xE0, 0x0F, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x00, 0x30, // 92 +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x06, 0x60, 0x00, 0x00, 0x06, 0xE0, 0xFF, 0xFF, 0x07, 0xE0, 0xFF, 0xFF, 0x07, // 93 +0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0x1F, 0x00, 0x00, 0xC0, 0x07, 0x00, 0x00, 0xE0, 0x00, 0x00, 0x00, 0xE0, 0x00, 0x00, 0x00, 0xC0, 0x07, 0x00, 0x00, 0x00, 0x1F, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0x20, // 94 +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, // 95 +0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0xE0, 0x00, 0x00, 0x00, 0x80, // 96 +0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x0E, 0x00, 0x00, 0x1C, 0x1F, 0x00, 0x00, 0x8C, 0x39, 0x00, 0x00, 0x86, 0x31, 0x00, 0x00, 0x86, 0x31, 0x00, 0x00, 0xC6, 0x30, 0x00, 0x00, 0xC6, 0x18, 0x00, 0x00, 0xCE, 0x0C, 0x00, 0x00, 0xFC, 0x1F, 0x00, 0x00, 0xF8, 0x3F, 0x00, 0x00, 0x00, 0x20, // 97 +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0xFF, 0x3F, 0x00, 0xE0, 0xFF, 0x3F, 0x00, 0x00, 0x18, 0x0C, 0x00, 0x00, 0x0C, 0x18, 0x00, 0x00, 0x06, 0x30, 0x00, 0x00, 0x06, 0x30, 0x00, 0x00, 0x06, 0x30, 0x00, 0x00, 0x0E, 0x38, 0x00, 0x00, 0x1C, 0x1C, 0x00, 0x00, 0xF8, 0x0F, 0x00, 0x00, 0xE0, 0x03, // 98 +0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0x07, 0x00, 0x00, 0xF8, 0x0F, 0x00, 0x00, 0x1C, 0x1C, 0x00, 0x00, 0x0E, 0x38, 0x00, 0x00, 0x06, 0x30, 0x00, 0x00, 0x06, 0x30, 0x00, 0x00, 0x06, 0x30, 0x00, 0x00, 0x0E, 0x38, 0x00, 0x00, 0x1C, 0x1C, 0x00, 0x00, 0x18, 0x0C, // 99 +0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0x03, 0x00, 0x00, 0xF8, 0x0F, 0x00, 0x00, 0x1C, 0x1C, 0x00, 0x00, 0x0E, 0x38, 0x00, 0x00, 0x06, 0x30, 0x00, 0x00, 0x06, 0x30, 0x00, 0x00, 0x06, 0x30, 0x00, 0x00, 0x0C, 0x18, 0x00, 0x00, 0x18, 0x0C, 0x00, 0xE0, 0xFF, 0x3F, 0x00, 0xE0, 0xFF, 0x3F, // 100 +0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0x07, 0x00, 0x00, 0xF8, 0x0F, 0x00, 0x00, 0xDC, 0x1C, 0x00, 0x00, 0xCE, 0x38, 0x00, 0x00, 0xC6, 0x30, 0x00, 0x00, 0xC6, 0x30, 0x00, 0x00, 0xC6, 0x30, 0x00, 0x00, 0xCE, 0x38, 0x00, 0x00, 0xDC, 0x18, 0x00, 0x00, 0xF8, 0x0C, 0x00, 0x00, 0xF0, 0x04, // 101 +0x00, 0x06, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0xC0, 0xFF, 0x3F, 0x00, 0xE0, 0xFF, 0x3F, 0x00, 0x60, 0x06, 0x00, 0x00, 0x60, 0x06, 0x00, 0x00, 0x60, 0x06, // 102 +0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0x83, 0x01, 0x00, 0xF8, 0x8F, 0x03, 0x00, 0x1C, 0x1C, 0x07, 0x00, 0x0E, 0x38, 0x06, 0x00, 0x06, 0x30, 0x06, 0x00, 0x06, 0x30, 0x06, 0x00, 0x06, 0x30, 0x06, 0x00, 0x0C, 0x18, 0x07, 0x00, 0x18, 0x8C, 0x03, 0x00, 0xFE, 0xFF, 0x01, 0x00, 0xFE, 0xFF, // 103 +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0xFF, 0x3F, 0x00, 0xE0, 0xFF, 0x3F, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x00, 0xFC, 0x3F, 0x00, 0x00, 0xF8, 0x3F, // 104 +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0xFE, 0x3F, 0x00, 0x60, 0xFE, 0x3F, // 105 +0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x06, 0x60, 0xFE, 0xFF, 0x07, 0x60, 0xFE, 0xFF, 0x03, // 106 +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0xFF, 0x3F, 0x00, 0xE0, 0xFF, 0x3F, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0xE0, 0x00, 0x00, 0x00, 0xF0, 0x01, 0x00, 0x00, 0x98, 0x07, 0x00, 0x00, 0x0C, 0x0E, 0x00, 0x00, 0x06, 0x3C, 0x00, 0x00, 0x02, 0x30, 0x00, 0x00, 0x00, 0x20, // 107 +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0xFF, 0x3F, 0x00, 0xE0, 0xFF, 0x3F, // 108 +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFE, 0x3F, 0x00, 0x00, 0xFE, 0x3F, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x00, 0xFC, 0x3F, 0x00, 0x00, 0xF8, 0x3F, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x00, 0xFC, 0x3F, 0x00, 0x00, 0xF8, 0x3F, // 109 +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFE, 0x3F, 0x00, 0x00, 0xFE, 0x3F, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x00, 0xFC, 0x3F, 0x00, 0x00, 0xF8, 0x3F, // 110 +0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0x07, 0x00, 0x00, 0xF8, 0x0F, 0x00, 0x00, 0x1C, 0x1C, 0x00, 0x00, 0x0E, 0x38, 0x00, 0x00, 0x06, 0x30, 0x00, 0x00, 0x06, 0x30, 0x00, 0x00, 0x06, 0x30, 0x00, 0x00, 0x0E, 0x38, 0x00, 0x00, 0x1C, 0x1C, 0x00, 0x00, 0xF8, 0x0F, 0x00, 0x00, 0xF0, 0x07, // 111 +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFE, 0xFF, 0x07, 0x00, 0xFE, 0xFF, 0x07, 0x00, 0x18, 0x0C, 0x00, 0x00, 0x0C, 0x18, 0x00, 0x00, 0x06, 0x30, 0x00, 0x00, 0x06, 0x30, 0x00, 0x00, 0x06, 0x30, 0x00, 0x00, 0x0E, 0x38, 0x00, 0x00, 0x1C, 0x1C, 0x00, 0x00, 0xF8, 0x0F, 0x00, 0x00, 0xE0, 0x03, // 112 +0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0x03, 0x00, 0x00, 0xF8, 0x0F, 0x00, 0x00, 0x1C, 0x1C, 0x00, 0x00, 0x0E, 0x38, 0x00, 0x00, 0x06, 0x30, 0x00, 0x00, 0x06, 0x30, 0x00, 0x00, 0x06, 0x30, 0x00, 0x00, 0x0C, 0x18, 0x00, 0x00, 0x18, 0x0C, 0x00, 0x00, 0xFE, 0xFF, 0x07, 0x00, 0xFE, 0xFF, 0x07, // 113 +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFE, 0x3F, 0x00, 0x00, 0xFE, 0x3F, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x06, // 114 +0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x0C, 0x00, 0x00, 0x7C, 0x1C, 0x00, 0x00, 0xEE, 0x38, 0x00, 0x00, 0xC6, 0x30, 0x00, 0x00, 0xC6, 0x30, 0x00, 0x00, 0xC6, 0x31, 0x00, 0x00, 0xC6, 0x31, 0x00, 0x00, 0x8E, 0x39, 0x00, 0x00, 0x9C, 0x1F, 0x00, 0x00, 0x18, 0x0F, // 115 +0x00, 0x06, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0xC0, 0xFF, 0x1F, 0x00, 0xE0, 0xFF, 0x3F, 0x00, 0x00, 0x06, 0x30, 0x00, 0x00, 0x06, 0x30, 0x00, 0x00, 0x06, 0x30, // 116 +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFE, 0x0F, 0x00, 0x00, 0xFE, 0x1F, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0xFE, 0x3F, 0x00, 0x00, 0xFE, 0x3F, // 117 +0x00, 0x06, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x00, 0x00, 0xC0, 0x07, 0x00, 0x00, 0x00, 0x1F, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0x1F, 0x00, 0x00, 0xC0, 0x07, 0x00, 0x00, 0xF8, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x00, 0x06, // 118 +0x00, 0x0E, 0x00, 0x00, 0x00, 0x7E, 0x00, 0x00, 0x00, 0xF0, 0x03, 0x00, 0x00, 0x80, 0x1F, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x80, 0x1F, 0x00, 0x00, 0xE0, 0x03, 0x00, 0x00, 0x7C, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x7C, 0x00, 0x00, 0x00, 0xE0, 0x03, 0x00, 0x00, 0x80, 0x1F, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x80, 0x1F, 0x00, 0x00, 0xF0, 0x03, 0x00, 0x00, 0x7E, 0x00, 0x00, 0x00, 0x0E, // 119 +0x00, 0x02, 0x20, 0x00, 0x00, 0x06, 0x30, 0x00, 0x00, 0x1E, 0x3C, 0x00, 0x00, 0x38, 0x0E, 0x00, 0x00, 0xF0, 0x07, 0x00, 0x00, 0xC0, 0x01, 0x00, 0x00, 0xE0, 0x07, 0x00, 0x00, 0x38, 0x0E, 0x00, 0x00, 0x1C, 0x3C, 0x00, 0x00, 0x0E, 0x30, 0x00, 0x00, 0x02, 0x20, // 120 +0x00, 0x00, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x7E, 0x00, 0x06, 0x00, 0xF0, 0x01, 0x06, 0x00, 0x80, 0x0F, 0x07, 0x00, 0x00, 0xFE, 0x03, 0x00, 0x00, 0xFC, 0x00, 0x00, 0xC0, 0x1F, 0x00, 0x00, 0xF8, 0x03, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x00, 0x06, // 121 +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x06, 0x3C, 0x00, 0x00, 0x06, 0x3E, 0x00, 0x00, 0x06, 0x37, 0x00, 0x00, 0xC6, 0x33, 0x00, 0x00, 0xE6, 0x30, 0x00, 0x00, 0x76, 0x30, 0x00, 0x00, 0x3E, 0x30, 0x00, 0x00, 0x1E, 0x30, 0x00, 0x00, 0x06, 0x30, // 122 +0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0xC0, 0x03, 0x00, 0xC0, 0x7F, 0xFE, 0x03, 0xE0, 0x3F, 0xFC, 0x07, 0x60, 0x00, 0x00, 0x06, 0x60, 0x00, 0x00, 0x06, // 123 +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0xFF, 0xFF, 0x0F, 0xE0, 0xFF, 0xFF, 0x0F, // 124 +0x00, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x06, 0x60, 0x00, 0x00, 0x06, 0xE0, 0x3F, 0xFC, 0x07, 0xC0, 0x7F, 0xFF, 0x03, 0x00, 0xC0, 0x03, 0x00, 0x00, 0x80, 0x01, // 125 +0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0xE0, 0x00, 0x00, 0x00, 0x60, // 126 +0x00, 0x60, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0xE0, 0xFF, 0x3F, 0x00, 0xE0, 0xFF, 0x3F, 0x00, 0x00, 0x0C, 0x30, 0x00, 0x00, 0x06, 0x30, 0x00, 0x00, 0x03, 0x30, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x30, // 129 +0x00, 0x60, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0xE0, 0xFF, 0x3F, 0x00, 0xE0, 0xFF, 0x3F, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x06, // 130 +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0xFF, 0x3F, 0x00, 0xE0, 0xFF, 0x3F, 0x00, 0xC0, 0x01, 0x00, 0x00, 0x80, 0x03, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x10, 0x3C, 0x00, 0x00, 0x1C, 0x70, 0x00, 0x00, 0x0C, 0xE0, 0x01, 0x00, 0x04, 0x80, 0x03, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x1C, 0x00, 0xE0, 0xFF, 0x3F, 0x00, 0xE0, 0xFF, 0x3F, // 131 +0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x80, 0x0F, 0x00, 0x00, 0xF0, 0x03, 0x00, 0x00, 0xFE, 0x01, 0x00, 0x80, 0x8F, 0x01, 0x00, 0xE0, 0x83, 0x01, 0x00, 0x60, 0x80, 0x01, 0x00, 0xE0, 0x83, 0x01, 0x00, 0x80, 0x8F, 0x01, 0x00, 0x00, 0xFE, 0x01, 0x00, 0x00, 0xF0, 0x03, 0x00, 0x00, 0x80, 0x0F, 0x00, 0x00, 0x00, 0xFE, 0x01, 0x00, 0x00, 0xB0, 0x03, 0x00, 0x00, 0x00, 0x03, // 132 +0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x0E, 0x00, 0x00, 0x1C, 0x1F, 0x00, 0x00, 0x8C, 0x39, 0x00, 0x00, 0x86, 0x31, 0x00, 0x00, 0x86, 0x31, 0x00, 0x00, 0xC6, 0x30, 0x00, 0x00, 0xC6, 0x18, 0x00, 0x00, 0xCE, 0x0C, 0x00, 0x00, 0xFC, 0x1F, 0x00, 0x00, 0xF8, 0xFF, 0x01, 0x00, 0x00, 0xA0, 0x03, 0x00, 0x00, 0x00, 0x03, // 133 +0x00, 0x00, 0x00, 0x00, 0x00, 0xFC, 0x01, 0x00, 0x00, 0xFF, 0x07, 0x00, 0x80, 0x07, 0x0F, 0x00, 0xC0, 0x01, 0x1C, 0x00, 0xC0, 0x00, 0x18, 0x00, 0x60, 0x00, 0x30, 0x00, 0x60, 0x00, 0x30, 0x00, 0x68, 0x00, 0x30, 0x00, 0x6E, 0x00, 0x30, 0x00, 0x66, 0x00, 0x30, 0x00, 0x62, 0x00, 0x30, 0x00, 0xC0, 0x00, 0x18, 0x00, 0xC0, 0x01, 0x1C, 0x00, 0x80, 0x03, 0x0F, 0x00, 0x00, 0x02, 0x03, // 134 +0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0x07, 0x00, 0x00, 0xF8, 0x0F, 0x00, 0x00, 0x1C, 0x1C, 0x00, 0x00, 0x0E, 0x38, 0x00, 0x80, 0x06, 0x30, 0x00, 0xE0, 0x06, 0x30, 0x00, 0x60, 0x06, 0x30, 0x00, 0x20, 0x0E, 0x38, 0x00, 0x00, 0x1C, 0x1C, 0x00, 0x00, 0x18, 0x0C, // 135 +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFE, 0x3F, 0x00, 0x00, 0xFE, 0x3F, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x80, 0x06, 0x00, 0x00, 0xE0, 0x06, 0x00, 0x00, 0x60, 0x06, 0x00, 0x00, 0x20, 0x0E, 0x00, 0x00, 0x00, 0xFC, 0x3F, 0x00, 0x00, 0xF8, 0x3F, // 136 +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x06, 0x3C, 0x00, 0x00, 0x06, 0x3E, 0x00, 0x00, 0x06, 0x37, 0x00, 0x80, 0xC6, 0x33, 0x00, 0xE0, 0xE6, 0x30, 0x00, 0x60, 0x76, 0x30, 0x00, 0x20, 0x3E, 0x30, 0x00, 0x00, 0x1E, 0x30, 0x00, 0x00, 0x06, 0x30, // 137 +0x00, 0x00, 0x00, 0x00, 0x00, 0xFC, 0x01, 0x00, 0x00, 0xFF, 0x07, 0x00, 0x80, 0x07, 0x0F, 0x00, 0xC0, 0x01, 0x1C, 0x00, 0xC0, 0x00, 0x18, 0x00, 0xE0, 0x00, 0x38, 0x00, 0x60, 0x00, 0x30, 0x00, 0x60, 0x00, 0x30, 0x00, 0x68, 0x00, 0x30, 0x00, 0x6E, 0x00, 0x30, 0x00, 0x66, 0x00, 0x30, 0x00, 0xE2, 0x00, 0x38, 0x00, 0xC0, 0x00, 0x18, 0x00, 0xC0, 0x01, 0x1C, 0x00, 0x80, 0x07, 0x0F, 0x00, 0x00, 0xFF, 0x07, 0x00, 0x00, 0xFC, 0x01, // 147 +0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0x07, 0x00, 0x00, 0xF8, 0x0F, 0x00, 0x00, 0x1C, 0x1C, 0x00, 0x00, 0x0E, 0x38, 0x00, 0x00, 0x06, 0x30, 0x00, 0x80, 0x06, 0x30, 0x00, 0xE0, 0x06, 0x30, 0x00, 0x60, 0x0E, 0x38, 0x00, 0x20, 0x1C, 0x1C, 0x00, 0x00, 0xF8, 0x0F, 0x00, 0x00, 0xF0, 0x07, // 148 +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0xFF, 0x3F, 0x00, 0xE0, 0xFF, 0x3F, 0x00, 0x60, 0x30, 0x30, 0x00, 0x60, 0x30, 0x30, 0x00, 0x60, 0x30, 0x30, 0x00, 0x60, 0x30, 0x30, 0x00, 0x60, 0x30, 0xF0, 0x01, 0x60, 0x30, 0xB0, 0x03, 0x60, 0x30, 0x30, 0x03, 0x60, 0x30, 0x30, 0x00, 0x60, 0x30, 0x30, 0x00, 0x60, 0x30, 0x30, 0x00, 0x60, 0x00, 0x30, // 152 +0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0x07, 0x00, 0x00, 0xF8, 0x0F, 0x00, 0x00, 0xDC, 0x1C, 0x00, 0x00, 0xCE, 0x38, 0x00, 0x00, 0xC6, 0x30, 0x00, 0x00, 0xC6, 0xF0, 0x01, 0x00, 0xC6, 0xB0, 0x03, 0x00, 0xCE, 0x38, 0x03, 0x00, 0xDC, 0x18, 0x00, 0x00, 0xF8, 0x0C, 0x00, 0x00, 0xF0, 0x04, // 153 +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x07, 0x0F, 0x00, 0xC0, 0x1F, 0x1C, 0x00, 0xC0, 0x18, 0x18, 0x00, 0x60, 0x38, 0x38, 0x00, 0x60, 0x30, 0x30, 0x00, 0x68, 0x30, 0x30, 0x00, 0x6E, 0x30, 0x30, 0x00, 0x66, 0x30, 0x30, 0x00, 0x62, 0x70, 0x30, 0x00, 0xC0, 0x60, 0x18, 0x00, 0xC0, 0xE1, 0x18, 0x00, 0x80, 0xC3, 0x0F, 0x00, 0x00, 0x83, 0x07, // 154 +0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x0C, 0x00, 0x00, 0x7C, 0x1C, 0x00, 0x00, 0xEE, 0x38, 0x00, 0x00, 0xC6, 0x30, 0x00, 0x80, 0xC6, 0x30, 0x00, 0xE0, 0xC6, 0x31, 0x00, 0x60, 0xC6, 0x31, 0x00, 0x20, 0x8E, 0x39, 0x00, 0x00, 0x9C, 0x1F, 0x00, 0x00, 0x18, 0x0F, // 155 +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE6, 0xFF, 0x07, 0x00, 0xE6, 0xFF, 0x07, // 161 +0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0x07, 0x00, 0x00, 0xF8, 0x0F, 0x00, 0x00, 0x1C, 0x9C, 0x07, 0x00, 0x0E, 0x78, 0x00, 0x00, 0x06, 0x3F, 0x00, 0x00, 0xF6, 0x30, 0x00, 0x00, 0x0E, 0x30, 0x00, 0xE0, 0x0D, 0x1C, 0x00, 0x00, 0x1C, 0x0E, 0x00, 0x00, 0x10, 0x06, // 162 +0x00, 0x60, 0x10, 0x00, 0x00, 0x60, 0x38, 0x00, 0x00, 0x7F, 0x1C, 0x00, 0xC0, 0xFF, 0x1F, 0x00, 0xE0, 0xE0, 0x19, 0x00, 0x60, 0x60, 0x18, 0x00, 0x60, 0x60, 0x18, 0x00, 0x60, 0x60, 0x30, 0x00, 0xE0, 0x00, 0x30, 0x00, 0xC0, 0x01, 0x30, 0x00, 0x80, 0x01, 0x38, 0x00, 0x00, 0x00, 0x10, // 163 +0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x04, 0x00, 0x00, 0xF7, 0x0E, 0x00, 0x00, 0xFE, 0x07, 0x00, 0x00, 0x0C, 0x03, 0x00, 0x00, 0x06, 0x06, 0x00, 0x00, 0x06, 0x06, 0x00, 0x00, 0x06, 0x06, 0x00, 0x00, 0x06, 0x06, 0x00, 0x00, 0x0C, 0x03, 0x00, 0x00, 0xFE, 0x07, 0x00, 0x00, 0xF7, 0x0E, 0x00, 0x00, 0x02, 0x04, // 164 +0xE0, 0x60, 0x06, 0x00, 0xC0, 0x61, 0x06, 0x00, 0x80, 0x67, 0x06, 0x00, 0x00, 0x7E, 0x06, 0x00, 0x00, 0x7C, 0x06, 0x00, 0x00, 0xF0, 0x3F, 0x00, 0x00, 0xF0, 0x3F, 0x00, 0x00, 0x7C, 0x06, 0x00, 0x00, 0x7E, 0x06, 0x00, 0x80, 0x67, 0x06, 0x00, 0xC0, 0x61, 0x06, 0x00, 0xE0, 0x60, 0x06, 0x00, 0x20, // 165 +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0x7F, 0xF8, 0x0F, 0xE0, 0x7F, 0xF8, 0x0F, // 166 +0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0x00, 0x00, 0x80, 0xF3, 0xC1, 0x00, 0xC0, 0x1F, 0xC3, 0x03, 0xE0, 0x0C, 0x07, 0x03, 0x60, 0x1C, 0x06, 0x06, 0x60, 0x18, 0x0C, 0x06, 0x60, 0x30, 0x1C, 0x06, 0xE0, 0x70, 0x38, 0x07, 0xC0, 0xE1, 0xF4, 0x03, 0x80, 0xC1, 0xE7, 0x01, 0x00, 0x80, 0x03, // 167 +0x00, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x60, // 168 +0x00, 0xF8, 0x00, 0x00, 0x00, 0xFE, 0x03, 0x00, 0x00, 0x07, 0x07, 0x00, 0x80, 0x01, 0x0C, 0x00, 0xC0, 0x79, 0x1C, 0x00, 0xC0, 0xFE, 0x19, 0x00, 0x60, 0x86, 0x31, 0x00, 0x60, 0x03, 0x33, 0x00, 0x60, 0x03, 0x33, 0x00, 0x60, 0x03, 0x33, 0x00, 0x60, 0x03, 0x33, 0x00, 0x60, 0x87, 0x33, 0x00, 0xC0, 0x86, 0x19, 0x00, 0xC0, 0x85, 0x1C, 0x00, 0x80, 0x01, 0x0C, 0x00, 0x00, 0x07, 0x07, 0x00, 0x00, 0xFE, 0x03, 0x00, 0x00, 0xF8, // 169 +0x00, 0x00, 0x00, 0x00, 0xC0, 0x1C, 0x00, 0x00, 0xE0, 0x3E, 0x00, 0x00, 0x60, 0x32, 0x00, 0x00, 0x60, 0x32, 0x00, 0x00, 0xE0, 0x3F, 0x00, 0x00, 0xC0, 0x3F, // 170 +0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0xE0, 0x03, 0x00, 0x00, 0x78, 0x0F, 0x00, 0x00, 0x1C, 0x1C, 0x00, 0x00, 0x84, 0x10, 0x00, 0x00, 0xE0, 0x03, 0x00, 0x00, 0x78, 0x0F, 0x00, 0x00, 0x1C, 0x1C, 0x00, 0x00, 0x04, 0x10, // 171 +0x00, 0x00, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0xFC, 0x01, 0x00, 0x00, 0xFC, 0x01, // 172 +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, // 173 +0x00, 0xF8, 0x00, 0x00, 0x00, 0xFE, 0x03, 0x00, 0x00, 0x07, 0x07, 0x00, 0x80, 0x01, 0x0C, 0x00, 0xC0, 0x01, 0x1C, 0x00, 0xC0, 0xFE, 0x1B, 0x00, 0x60, 0xFE, 0x33, 0x00, 0x60, 0x66, 0x30, 0x00, 0x60, 0x66, 0x30, 0x00, 0x60, 0xE6, 0x30, 0x00, 0x60, 0xFE, 0x31, 0x00, 0x60, 0x3C, 0x33, 0x00, 0xC0, 0x00, 0x1A, 0x00, 0xC0, 0x01, 0x1C, 0x00, 0x80, 0x01, 0x0C, 0x00, 0x00, 0x07, 0x07, 0x00, 0x00, 0xFE, 0x03, 0x00, 0x00, 0xF8, // 174 +0x0C, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x0C, // 175 +0x00, 0x00, 0x00, 0x00, 0x80, 0x03, 0x00, 0x00, 0x40, 0x04, 0x00, 0x00, 0x20, 0x08, 0x00, 0x00, 0x20, 0x08, 0x00, 0x00, 0x20, 0x08, 0x00, 0x00, 0x40, 0x04, 0x00, 0x00, 0x80, 0x03, // 176 +0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x30, 0x00, 0x00, 0x60, 0x30, 0x00, 0x00, 0x60, 0x30, 0x00, 0x00, 0x60, 0x30, 0x00, 0x00, 0x60, 0x30, 0x00, 0x00, 0xFF, 0x3F, 0x00, 0x00, 0xFF, 0x3F, 0x00, 0x00, 0x60, 0x30, 0x00, 0x00, 0x60, 0x30, 0x00, 0x00, 0x60, 0x30, 0x00, 0x00, 0x60, 0x30, 0x00, 0x00, 0x60, 0x30, // 177 +0x40, 0x20, 0x00, 0x00, 0x60, 0x30, 0x00, 0x00, 0x20, 0x38, 0x00, 0x00, 0x20, 0x2C, 0x00, 0x00, 0x20, 0x26, 0x00, 0x00, 0xE0, 0x23, 0x00, 0x00, 0xC0, 0x21, // 178 +0x40, 0x10, 0x00, 0x00, 0x60, 0x30, 0x00, 0x00, 0x20, 0x20, 0x00, 0x00, 0x20, 0x22, 0x00, 0x00, 0x20, 0x22, 0x00, 0x00, 0xE0, 0x3D, 0x00, 0x00, 0xC0, 0x1D, // 179 +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0xE0, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x20, // 180 +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFE, 0xFF, 0x07, 0x00, 0xFE, 0xFF, 0x07, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x1C, 0x00, 0x00, 0xFE, 0x3F, 0x00, 0x00, 0xFE, 0x3F, // 181 +0x00, 0x0F, 0x00, 0x00, 0xC0, 0x3F, 0x00, 0x00, 0xC0, 0x3F, 0x00, 0x00, 0xE0, 0x7F, 0x00, 0x00, 0xE0, 0x7F, 0x00, 0x00, 0xE0, 0xFF, 0xFF, 0x07, 0xE0, 0xFF, 0xFF, 0x07, 0x60, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0xE0, 0xFF, 0xFF, 0x07, 0xE0, 0xFF, 0xFF, 0x07, 0x60, 0x00, 0x00, 0x00, 0x60, // 182 +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x60, // 183 +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0xC0, 0x02, 0x00, 0x00, 0x80, 0x03, 0x00, 0x00, 0x00, 0x01, // 184 +0x00, 0x00, 0x30, 0x00, 0x60, 0x00, 0x38, 0x00, 0x60, 0x00, 0x3C, 0x00, 0x60, 0x00, 0x37, 0x00, 0x60, 0x80, 0x33, 0x00, 0x60, 0xC0, 0x31, 0x00, 0x68, 0xE0, 0x30, 0x00, 0x6E, 0x38, 0x30, 0x00, 0x66, 0x1C, 0x30, 0x00, 0x62, 0x0E, 0x30, 0x00, 0x60, 0x07, 0x30, 0x00, 0xE0, 0x01, 0x30, 0x00, 0xE0, 0x00, 0x30, 0x00, 0x60, 0x00, 0x30, // 185 +0x00, 0x00, 0x00, 0x00, 0x80, 0x0F, 0x00, 0x00, 0xC0, 0x1F, 0x00, 0x00, 0xE0, 0x38, 0x00, 0x00, 0x60, 0x30, 0x00, 0x00, 0xE0, 0x38, 0x00, 0x00, 0xC0, 0x1F, 0x00, 0x00, 0x80, 0x0F, // 186 +0x00, 0x00, 0x30, 0x00, 0x60, 0x00, 0x38, 0x00, 0x60, 0x00, 0x3C, 0x00, 0x60, 0x00, 0x37, 0x00, 0x60, 0x80, 0x33, 0x00, 0x60, 0xC0, 0x31, 0x00, 0x6C, 0xE0, 0x30, 0x00, 0x6C, 0x38, 0x30, 0x00, 0x60, 0x1C, 0x30, 0x00, 0x60, 0x0E, 0x30, 0x00, 0x60, 0x07, 0x30, 0x00, 0xE0, 0x01, 0x30, 0x00, 0xE0, 0x00, 0x30, 0x00, 0x60, 0x00, 0x30, // 187 +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x06, 0x3C, 0x00, 0x00, 0x06, 0x3E, 0x00, 0x00, 0x06, 0x37, 0x00, 0xC0, 0xC6, 0x33, 0x00, 0xC0, 0xE6, 0x30, 0x00, 0x00, 0x76, 0x30, 0x00, 0x00, 0x3E, 0x30, 0x00, 0x00, 0x1E, 0x30, 0x00, 0x00, 0x06, 0x30, // 188 +0x00, 0x00, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x20, 0x00, 0xE0, 0x3F, 0x30, 0x00, 0xE0, 0x3F, 0x1C, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0xC0, 0x01, 0x00, 0x00, 0xE0, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x00, 0x4E, 0x20, 0x00, 0x00, 0x67, 0x30, 0x00, 0xC0, 0x21, 0x38, 0x00, 0xE0, 0x20, 0x2C, 0x00, 0x60, 0x20, 0x26, 0x00, 0x00, 0xE0, 0x27, 0x00, 0x00, 0xC0, 0x21, // 189 +0x40, 0x10, 0x00, 0x00, 0x60, 0x30, 0x00, 0x00, 0x20, 0x20, 0x00, 0x00, 0x20, 0x22, 0x20, 0x00, 0x20, 0x22, 0x30, 0x00, 0xE0, 0x3D, 0x38, 0x00, 0xC0, 0x1D, 0x0E, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x80, 0x03, 0x00, 0x00, 0xE0, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0x0E, 0x0C, 0x00, 0x00, 0x07, 0x0E, 0x00, 0x80, 0x83, 0x0B, 0x00, 0xE0, 0xC0, 0x08, 0x00, 0x60, 0xE0, 0x3F, 0x00, 0x20, 0xE0, 0x3F, 0x00, 0x00, 0x00, 0x08, // 190 +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0x00, 0x00, 0x00, 0xF8, 0x03, 0x00, 0x00, 0x1E, 0x03, 0x00, 0x00, 0x07, 0x07, 0x00, 0xE6, 0x03, 0x06, 0x00, 0xE6, 0x01, 0x06, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x80, 0x03, 0x00, 0x00, 0xC0, 0x01, 0x00, 0x00, 0xC0, // 191 +0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x80, 0x0F, 0x00, 0x00, 0xF0, 0x03, 0x00, 0x00, 0xFE, 0x01, 0x00, 0x82, 0x8F, 0x01, 0x00, 0xE6, 0x83, 0x01, 0x00, 0x6E, 0x80, 0x01, 0x00, 0xE8, 0x83, 0x01, 0x00, 0x80, 0x8F, 0x01, 0x00, 0x00, 0xFE, 0x01, 0x00, 0x00, 0xF0, 0x03, 0x00, 0x00, 0x80, 0x0F, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x00, 0x30, // 192 +0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x80, 0x0F, 0x00, 0x00, 0xF0, 0x03, 0x00, 0x00, 0xFE, 0x01, 0x00, 0x80, 0x8F, 0x01, 0x00, 0xE8, 0x83, 0x01, 0x00, 0x6E, 0x80, 0x01, 0x00, 0xE6, 0x83, 0x01, 0x00, 0x82, 0x8F, 0x01, 0x00, 0x00, 0xFE, 0x01, 0x00, 0x00, 0xF0, 0x03, 0x00, 0x00, 0x80, 0x0F, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x00, 0x30, // 193 +0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x80, 0x0F, 0x00, 0x00, 0xF0, 0x03, 0x00, 0x00, 0xFE, 0x01, 0x00, 0x88, 0x8F, 0x01, 0x00, 0xEC, 0x83, 0x01, 0x00, 0x66, 0x80, 0x01, 0x00, 0xE6, 0x83, 0x01, 0x00, 0x8C, 0x8F, 0x01, 0x00, 0x08, 0xFE, 0x01, 0x00, 0x00, 0xF0, 0x03, 0x00, 0x00, 0x80, 0x0F, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x00, 0x30, // 194 +0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x80, 0x0F, 0x00, 0x00, 0xF0, 0x03, 0x00, 0x0C, 0xFE, 0x01, 0x00, 0x8E, 0x8F, 0x01, 0x00, 0xE6, 0x83, 0x01, 0x00, 0x66, 0x80, 0x01, 0x00, 0xEC, 0x83, 0x01, 0x00, 0x8C, 0x8F, 0x01, 0x00, 0x0E, 0xFE, 0x01, 0x00, 0x06, 0xF0, 0x03, 0x00, 0x00, 0x80, 0x0F, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x00, 0x30, // 195 +0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x80, 0x0F, 0x00, 0x00, 0xF0, 0x03, 0x00, 0x00, 0xFE, 0x01, 0x00, 0x8C, 0x8F, 0x01, 0x00, 0xEC, 0x83, 0x01, 0x00, 0x60, 0x80, 0x01, 0x00, 0xE0, 0x83, 0x01, 0x00, 0x8C, 0x8F, 0x01, 0x00, 0x0C, 0xFE, 0x01, 0x00, 0x00, 0xF0, 0x03, 0x00, 0x00, 0x80, 0x0F, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x00, 0x30, // 196 +0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x80, 0x0F, 0x00, 0x00, 0xF0, 0x03, 0x00, 0x00, 0xFE, 0x01, 0x00, 0x9C, 0x8F, 0x01, 0x00, 0xE2, 0x83, 0x01, 0x00, 0x62, 0x80, 0x01, 0x00, 0xE2, 0x83, 0x01, 0x00, 0x9C, 0x8F, 0x01, 0x00, 0x00, 0xFE, 0x01, 0x00, 0x00, 0xF0, 0x03, 0x00, 0x00, 0x80, 0x0F, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x00, 0x30, // 197 +0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x3C, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0xC0, 0x03, 0x00, 0x00, 0xF0, 0x01, 0x00, 0x00, 0xBC, 0x01, 0x00, 0x00, 0x8F, 0x01, 0x00, 0xC0, 0x83, 0x01, 0x00, 0xE0, 0x80, 0x01, 0x00, 0x60, 0x80, 0x01, 0x00, 0x60, 0x80, 0x01, 0x00, 0xE0, 0xFF, 0x3F, 0x00, 0xE0, 0xFF, 0x3F, 0x00, 0x60, 0x30, 0x30, 0x00, 0x60, 0x30, 0x30, 0x00, 0x60, 0x30, 0x30, 0x00, 0x60, 0x30, 0x30, 0x00, 0x60, 0x30, 0x30, 0x00, 0x60, 0x30, 0x30, 0x00, 0x60, 0x30, 0x30, 0x00, 0x60, 0x30, 0x30, 0x00, 0x60, 0x30, 0x30, 0x00, 0x60, 0x00, 0x30, // 198 +0x00, 0x00, 0x00, 0x00, 0x00, 0xFC, 0x01, 0x00, 0x00, 0xFF, 0x07, 0x00, 0x80, 0x07, 0x0F, 0x00, 0xC0, 0x01, 0x1C, 0x00, 0xC0, 0x00, 0x18, 0x00, 0x60, 0x00, 0x30, 0x02, 0x60, 0x00, 0x30, 0x02, 0x60, 0x00, 0xF0, 0x02, 0x60, 0x00, 0xB0, 0x03, 0x60, 0x00, 0x30, 0x01, 0x60, 0x00, 0x30, 0x00, 0xC0, 0x00, 0x18, 0x00, 0xC0, 0x01, 0x1C, 0x00, 0x80, 0x03, 0x0F, 0x00, 0x00, 0x02, 0x03, // 199 +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0xFF, 0x3F, 0x00, 0xE0, 0xFF, 0x3F, 0x00, 0x60, 0x30, 0x30, 0x00, 0x60, 0x30, 0x30, 0x00, 0x62, 0x30, 0x30, 0x00, 0x66, 0x30, 0x30, 0x00, 0x6E, 0x30, 0x30, 0x00, 0x68, 0x30, 0x30, 0x00, 0x60, 0x30, 0x30, 0x00, 0x60, 0x30, 0x30, 0x00, 0x60, 0x30, 0x30, 0x00, 0x60, 0x30, 0x30, 0x00, 0x60, 0x00, 0x30, // 200 +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0xFF, 0x3F, 0x00, 0xE0, 0xFF, 0x3F, 0x00, 0x60, 0x30, 0x30, 0x00, 0x60, 0x30, 0x30, 0x00, 0x60, 0x30, 0x30, 0x00, 0x68, 0x30, 0x30, 0x00, 0x6E, 0x30, 0x30, 0x00, 0x66, 0x30, 0x30, 0x00, 0x62, 0x30, 0x30, 0x00, 0x60, 0x30, 0x30, 0x00, 0x60, 0x30, 0x30, 0x00, 0x60, 0x30, 0x30, 0x00, 0x60, 0x00, 0x30, // 201 +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0xFF, 0x3F, 0x00, 0xE0, 0xFF, 0x3F, 0x00, 0x60, 0x30, 0x30, 0x00, 0x60, 0x30, 0x30, 0x00, 0x68, 0x30, 0x30, 0x00, 0x6C, 0x30, 0x30, 0x00, 0x66, 0x30, 0x30, 0x00, 0x66, 0x30, 0x30, 0x00, 0x6C, 0x30, 0x30, 0x00, 0x68, 0x30, 0x30, 0x00, 0x60, 0x30, 0x30, 0x00, 0x60, 0x30, 0x30, 0x00, 0x60, 0x00, 0x30, // 202 +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0xFF, 0x3F, 0x00, 0xE0, 0xFF, 0x3F, 0x00, 0x60, 0x30, 0x30, 0x00, 0x60, 0x30, 0x30, 0x00, 0x6C, 0x30, 0x30, 0x00, 0x6C, 0x30, 0x30, 0x00, 0x60, 0x30, 0x30, 0x00, 0x60, 0x30, 0x30, 0x00, 0x6C, 0x30, 0x30, 0x00, 0x6C, 0x30, 0x30, 0x00, 0x60, 0x30, 0x30, 0x00, 0x60, 0x30, 0x30, 0x00, 0x60, 0x00, 0x30, // 203 +0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0xE6, 0xFF, 0x3F, 0x00, 0xEE, 0xFF, 0x3F, 0x00, 0x08, // 204 +0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0xEE, 0xFF, 0x3F, 0x00, 0xE6, 0xFF, 0x3F, 0x00, 0x02, // 205 +0x08, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0xE6, 0xFF, 0x3F, 0x00, 0xE6, 0xFF, 0x3F, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x08, // 206 +0x0C, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0xE0, 0xFF, 0x3F, 0x00, 0xE0, 0xFF, 0x3F, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x0C, // 207 +0x00, 0x30, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0xE0, 0xFF, 0x3F, 0x00, 0xE0, 0xFF, 0x3F, 0x00, 0x60, 0x30, 0x30, 0x00, 0x60, 0x30, 0x30, 0x00, 0x60, 0x30, 0x30, 0x00, 0x60, 0x30, 0x30, 0x00, 0x60, 0x30, 0x30, 0x00, 0x60, 0x00, 0x30, 0x00, 0x60, 0x00, 0x30, 0x00, 0xE0, 0x00, 0x18, 0x00, 0xC0, 0x01, 0x1C, 0x00, 0x80, 0x03, 0x0E, 0x00, 0x00, 0xFF, 0x07, 0x00, 0x00, 0xFC, 0x01, // 208 +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0xFF, 0x3F, 0x00, 0xE0, 0xFF, 0x3F, 0x00, 0xC0, 0x01, 0x00, 0x00, 0x8C, 0x03, 0x00, 0x00, 0x0E, 0x0E, 0x00, 0x00, 0x06, 0x3C, 0x00, 0x00, 0x06, 0x70, 0x00, 0x00, 0x0C, 0xE0, 0x01, 0x00, 0x0C, 0x80, 0x03, 0x00, 0x0E, 0x00, 0x0F, 0x00, 0x06, 0x00, 0x1C, 0x00, 0xE0, 0xFF, 0x3F, 0x00, 0xE0, 0xFF, 0x3F, // 209 +0x00, 0x00, 0x00, 0x00, 0x00, 0xFC, 0x01, 0x00, 0x00, 0xFF, 0x07, 0x00, 0x80, 0x07, 0x0F, 0x00, 0xC0, 0x01, 0x1C, 0x00, 0xC0, 0x00, 0x18, 0x00, 0xE0, 0x00, 0x38, 0x00, 0x62, 0x00, 0x30, 0x00, 0x66, 0x00, 0x30, 0x00, 0x6E, 0x00, 0x30, 0x00, 0x68, 0x00, 0x30, 0x00, 0x60, 0x00, 0x30, 0x00, 0xE0, 0x00, 0x38, 0x00, 0xC0, 0x00, 0x18, 0x00, 0xC0, 0x01, 0x1C, 0x00, 0x80, 0x07, 0x0F, 0x00, 0x00, 0xFF, 0x07, 0x00, 0x00, 0xFC, 0x01, // 210 +0x00, 0x00, 0x00, 0x00, 0x00, 0xFC, 0x01, 0x00, 0x00, 0xFF, 0x07, 0x00, 0x80, 0x07, 0x0F, 0x00, 0xC0, 0x01, 0x1C, 0x00, 0xC0, 0x00, 0x18, 0x00, 0xE0, 0x00, 0x38, 0x00, 0x60, 0x00, 0x30, 0x00, 0x68, 0x00, 0x30, 0x00, 0x6E, 0x00, 0x30, 0x00, 0x66, 0x00, 0x30, 0x00, 0x62, 0x00, 0x30, 0x00, 0xE0, 0x00, 0x38, 0x00, 0xC0, 0x00, 0x18, 0x00, 0xC0, 0x01, 0x1C, 0x00, 0x80, 0x07, 0x0F, 0x00, 0x00, 0xFF, 0x07, 0x00, 0x00, 0xFC, 0x01, // 211 +0x00, 0x00, 0x00, 0x00, 0x00, 0xFC, 0x01, 0x00, 0x00, 0xFF, 0x07, 0x00, 0x80, 0x07, 0x0F, 0x00, 0xC0, 0x01, 0x1C, 0x00, 0xC0, 0x00, 0x18, 0x00, 0xE0, 0x00, 0x38, 0x00, 0x68, 0x00, 0x30, 0x00, 0x6C, 0x00, 0x30, 0x00, 0x66, 0x00, 0x30, 0x00, 0x66, 0x00, 0x30, 0x00, 0x6C, 0x00, 0x30, 0x00, 0xE8, 0x00, 0x38, 0x00, 0xC0, 0x00, 0x18, 0x00, 0xC0, 0x01, 0x1C, 0x00, 0x80, 0x07, 0x0F, 0x00, 0x00, 0xFF, 0x07, 0x00, 0x00, 0xFC, 0x01, // 212 +0x00, 0x00, 0x00, 0x00, 0x00, 0xFC, 0x01, 0x00, 0x00, 0xFF, 0x07, 0x00, 0x80, 0x07, 0x0F, 0x00, 0xC0, 0x01, 0x1C, 0x00, 0xCC, 0x00, 0x18, 0x00, 0xEE, 0x00, 0x38, 0x00, 0x66, 0x00, 0x30, 0x00, 0x66, 0x00, 0x30, 0x00, 0x6C, 0x00, 0x30, 0x00, 0x6C, 0x00, 0x30, 0x00, 0x6E, 0x00, 0x30, 0x00, 0xE6, 0x00, 0x38, 0x00, 0xC0, 0x00, 0x18, 0x00, 0xC0, 0x01, 0x1C, 0x00, 0x80, 0x07, 0x0F, 0x00, 0x00, 0xFF, 0x07, 0x00, 0x00, 0xFC, 0x01, // 213 +0x00, 0x00, 0x00, 0x00, 0x00, 0xFC, 0x01, 0x00, 0x00, 0xFF, 0x07, 0x00, 0x80, 0x07, 0x0F, 0x00, 0xC0, 0x01, 0x1C, 0x00, 0xC0, 0x00, 0x18, 0x00, 0xE0, 0x00, 0x38, 0x00, 0x6C, 0x00, 0x30, 0x00, 0x6C, 0x00, 0x30, 0x00, 0x60, 0x00, 0x30, 0x00, 0x60, 0x00, 0x30, 0x00, 0x6C, 0x00, 0x30, 0x00, 0xEC, 0x00, 0x38, 0x00, 0xC0, 0x00, 0x18, 0x00, 0xC0, 0x01, 0x1C, 0x00, 0x80, 0x07, 0x0F, 0x00, 0x00, 0xFF, 0x07, 0x00, 0x00, 0xFC, 0x01, // 214 +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x03, 0x00, 0x00, 0x8E, 0x03, 0x00, 0x00, 0xDC, 0x01, 0x00, 0x00, 0xF8, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x00, 0x00, 0xDC, 0x01, 0x00, 0x00, 0x8E, 0x03, 0x00, 0x00, 0x06, 0x03, // 215 +0x00, 0x00, 0x00, 0x00, 0x00, 0xFC, 0x21, 0x00, 0x00, 0xFF, 0x77, 0x00, 0x80, 0x07, 0x3F, 0x00, 0xC0, 0x01, 0x1E, 0x00, 0xC0, 0x00, 0x1F, 0x00, 0xE0, 0x80, 0x3B, 0x00, 0x60, 0xC0, 0x31, 0x00, 0x60, 0xE0, 0x30, 0x00, 0x60, 0x70, 0x30, 0x00, 0x60, 0x38, 0x30, 0x00, 0x60, 0x1C, 0x30, 0x00, 0xE0, 0x0E, 0x38, 0x00, 0xC0, 0x07, 0x18, 0x00, 0xC0, 0x03, 0x1C, 0x00, 0xE0, 0x07, 0x0F, 0x00, 0x70, 0xFF, 0x07, 0x00, 0x20, 0xFC, 0x01, // 216 +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0xFF, 0x03, 0x00, 0xE0, 0xFF, 0x0F, 0x00, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x00, 0x38, 0x00, 0x02, 0x00, 0x30, 0x00, 0x06, 0x00, 0x30, 0x00, 0x0E, 0x00, 0x30, 0x00, 0x08, 0x00, 0x30, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0x1C, 0x00, 0xE0, 0xFF, 0x0F, 0x00, 0xE0, 0xFF, 0x03, // 217 +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0xFF, 0x03, 0x00, 0xE0, 0xFF, 0x0F, 0x00, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0x30, 0x00, 0x08, 0x00, 0x30, 0x00, 0x0E, 0x00, 0x30, 0x00, 0x06, 0x00, 0x30, 0x00, 0x02, 0x00, 0x30, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0x1C, 0x00, 0xE0, 0xFF, 0x0F, 0x00, 0xE0, 0xFF, 0x03, // 218 +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0xFF, 0x03, 0x00, 0xE0, 0xFF, 0x0F, 0x00, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x00, 0x38, 0x00, 0x08, 0x00, 0x30, 0x00, 0x0C, 0x00, 0x30, 0x00, 0x06, 0x00, 0x30, 0x00, 0x06, 0x00, 0x30, 0x00, 0x0C, 0x00, 0x30, 0x00, 0x08, 0x00, 0x38, 0x00, 0x00, 0x00, 0x1C, 0x00, 0xE0, 0xFF, 0x0F, 0x00, 0xE0, 0xFF, 0x03, // 219 +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0xFF, 0x03, 0x00, 0xE0, 0xFF, 0x0F, 0x00, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x00, 0x38, 0x00, 0x0C, 0x00, 0x30, 0x00, 0x0C, 0x00, 0x30, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x30, 0x00, 0x0C, 0x00, 0x30, 0x00, 0x0C, 0x00, 0x38, 0x00, 0x00, 0x00, 0x1C, 0x00, 0xE0, 0xFF, 0x0F, 0x00, 0xE0, 0xFF, 0x03, // 220 +0x20, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0xC0, 0x01, 0x00, 0x00, 0x80, 0x03, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x00, 0x3C, 0x00, 0x00, 0x08, 0xF0, 0x3F, 0x00, 0x0E, 0xF0, 0x3F, 0x00, 0x06, 0x3C, 0x00, 0x00, 0x02, 0x1E, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0xC0, 0x03, 0x00, 0x00, 0xE0, 0x01, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x20, // 221 +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0xFF, 0x3F, 0x00, 0xE0, 0xFF, 0x3F, 0x00, 0x00, 0x03, 0x06, 0x00, 0x00, 0x03, 0x06, 0x00, 0x00, 0x03, 0x06, 0x00, 0x00, 0x03, 0x06, 0x00, 0x00, 0x03, 0x06, 0x00, 0x00, 0x03, 0x06, 0x00, 0x00, 0x03, 0x06, 0x00, 0x00, 0x03, 0x07, 0x00, 0x00, 0x86, 0x03, 0x00, 0x00, 0xFE, 0x01, 0x00, 0x00, 0xF8, // 222 +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xFF, 0x3F, 0x00, 0xC0, 0xFF, 0x3F, 0x00, 0xC0, 0x00, 0x00, 0x00, 0x60, 0x00, 0x08, 0x00, 0x60, 0x00, 0x1C, 0x00, 0x60, 0x00, 0x38, 0x00, 0xE0, 0x78, 0x30, 0x00, 0xC0, 0x7F, 0x30, 0x00, 0x80, 0xC7, 0x30, 0x00, 0x00, 0x80, 0x39, 0x00, 0x00, 0x80, 0x1F, 0x00, 0x00, 0x00, 0x0F, // 223 +0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x0E, 0x00, 0x00, 0x1C, 0x1F, 0x00, 0x00, 0x8C, 0x39, 0x00, 0x20, 0x86, 0x31, 0x00, 0x60, 0x86, 0x31, 0x00, 0xE0, 0xC6, 0x30, 0x00, 0x80, 0xC6, 0x18, 0x00, 0x00, 0xCE, 0x0C, 0x00, 0x00, 0xFC, 0x1F, 0x00, 0x00, 0xF8, 0x3F, 0x00, 0x00, 0x00, 0x20, // 224 +0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x0E, 0x00, 0x00, 0x1C, 0x1F, 0x00, 0x00, 0x8C, 0x39, 0x00, 0x00, 0x86, 0x31, 0x00, 0x80, 0x86, 0x31, 0x00, 0xE0, 0xC6, 0x30, 0x00, 0x60, 0xC6, 0x18, 0x00, 0x20, 0xCE, 0x0C, 0x00, 0x00, 0xFC, 0x1F, 0x00, 0x00, 0xF8, 0x3F, 0x00, 0x00, 0x00, 0x20, // 225 +0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x0E, 0x00, 0x00, 0x1C, 0x1F, 0x00, 0x80, 0x8C, 0x39, 0x00, 0xC0, 0x86, 0x31, 0x00, 0x60, 0x86, 0x31, 0x00, 0x60, 0xC6, 0x30, 0x00, 0xC0, 0xC6, 0x18, 0x00, 0x80, 0xCE, 0x0C, 0x00, 0x00, 0xFC, 0x1F, 0x00, 0x00, 0xF8, 0x3F, 0x00, 0x00, 0x00, 0x20, // 226 +0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x0E, 0x00, 0xC0, 0x1C, 0x1F, 0x00, 0xE0, 0x8C, 0x39, 0x00, 0x60, 0x86, 0x31, 0x00, 0x60, 0x86, 0x31, 0x00, 0xC0, 0xC6, 0x30, 0x00, 0xC0, 0xC6, 0x18, 0x00, 0xE0, 0xCE, 0x0C, 0x00, 0x60, 0xFC, 0x1F, 0x00, 0x00, 0xF8, 0x3F, 0x00, 0x00, 0x00, 0x20, // 227 +0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x0E, 0x00, 0x00, 0x1C, 0x1F, 0x00, 0xC0, 0x8C, 0x39, 0x00, 0xC0, 0x86, 0x31, 0x00, 0x00, 0x86, 0x31, 0x00, 0x00, 0xC6, 0x30, 0x00, 0xC0, 0xC6, 0x18, 0x00, 0xC0, 0xCE, 0x0C, 0x00, 0x00, 0xFC, 0x1F, 0x00, 0x00, 0xF8, 0x3F, 0x00, 0x00, 0x00, 0x20, // 228 +0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x0E, 0x00, 0x00, 0x1C, 0x1F, 0x00, 0x00, 0x8C, 0x39, 0x00, 0x70, 0x86, 0x31, 0x00, 0x88, 0x86, 0x31, 0x00, 0x88, 0xC6, 0x30, 0x00, 0x88, 0xC6, 0x18, 0x00, 0x70, 0xCE, 0x0C, 0x00, 0x00, 0xFC, 0x1F, 0x00, 0x00, 0xF8, 0x3F, 0x00, 0x00, 0x00, 0x20, // 229 +0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x0F, 0x00, 0x00, 0x9C, 0x1F, 0x00, 0x00, 0xCC, 0x39, 0x00, 0x00, 0xC6, 0x30, 0x00, 0x00, 0xC6, 0x30, 0x00, 0x00, 0xC6, 0x30, 0x00, 0x00, 0xC6, 0x30, 0x00, 0x00, 0x66, 0x18, 0x00, 0x00, 0x6E, 0x1C, 0x00, 0x00, 0xFC, 0x0F, 0x00, 0x00, 0xFC, 0x1F, 0x00, 0x00, 0xCC, 0x1C, 0x00, 0x00, 0xCE, 0x38, 0x00, 0x00, 0xC6, 0x30, 0x00, 0x00, 0xC6, 0x30, 0x00, 0x00, 0xC6, 0x30, 0x00, 0x00, 0xC6, 0x30, 0x00, 0x00, 0xCC, 0x18, 0x00, 0x00, 0xF8, 0x0C, 0x00, 0x00, 0xE0, 0x04, // 230 +0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0x07, 0x00, 0x00, 0xF8, 0x0F, 0x00, 0x00, 0x1C, 0x1C, 0x00, 0x00, 0x0E, 0x38, 0x02, 0x00, 0x06, 0x30, 0x02, 0x00, 0x06, 0xF0, 0x02, 0x00, 0x06, 0xB0, 0x03, 0x00, 0x0E, 0x38, 0x01, 0x00, 0x1C, 0x1C, 0x00, 0x00, 0x18, 0x0C, // 231 +0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0x07, 0x00, 0x00, 0xF8, 0x0F, 0x00, 0x00, 0xDC, 0x1C, 0x00, 0x20, 0xCE, 0x38, 0x00, 0x60, 0xC6, 0x30, 0x00, 0xE0, 0xC6, 0x30, 0x00, 0x80, 0xC6, 0x30, 0x00, 0x00, 0xCE, 0x38, 0x00, 0x00, 0xDC, 0x18, 0x00, 0x00, 0xF8, 0x0C, 0x00, 0x00, 0xF0, 0x04, // 232 +0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0x07, 0x00, 0x00, 0xF8, 0x0F, 0x00, 0x00, 0xDC, 0x1C, 0x00, 0x00, 0xCE, 0x38, 0x00, 0x80, 0xC6, 0x30, 0x00, 0xE0, 0xC6, 0x30, 0x00, 0x60, 0xC6, 0x30, 0x00, 0x20, 0xCE, 0x38, 0x00, 0x00, 0xDC, 0x18, 0x00, 0x00, 0xF8, 0x0C, 0x00, 0x00, 0xF0, 0x04, // 233 +0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0x07, 0x00, 0x00, 0xF8, 0x0F, 0x00, 0x00, 0xDC, 0x1C, 0x00, 0x80, 0xCE, 0x38, 0x00, 0xC0, 0xC6, 0x30, 0x00, 0x60, 0xC6, 0x30, 0x00, 0x60, 0xC6, 0x30, 0x00, 0xC0, 0xCE, 0x38, 0x00, 0x80, 0xDC, 0x18, 0x00, 0x00, 0xF8, 0x0C, 0x00, 0x00, 0xF0, 0x04, // 234 +0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0x07, 0x00, 0x00, 0xF8, 0x0F, 0x00, 0x00, 0xDC, 0x1C, 0x00, 0xC0, 0xCE, 0x38, 0x00, 0xC0, 0xC6, 0x30, 0x00, 0x00, 0xC6, 0x30, 0x00, 0x00, 0xC6, 0x30, 0x00, 0xC0, 0xCE, 0x38, 0x00, 0xC0, 0xDC, 0x18, 0x00, 0x00, 0xF8, 0x0C, 0x00, 0x00, 0xF0, 0x04, // 235 +0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x60, 0xFE, 0x3F, 0x00, 0xE0, 0xFE, 0x3F, 0x00, 0x80, // 236 +0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0xE0, 0xFE, 0x3F, 0x00, 0x60, 0xFE, 0x3F, 0x00, 0x20, // 237 +0x80, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0x60, 0xFE, 0x3F, 0x00, 0x60, 0xFE, 0x3F, 0x00, 0xC0, 0x00, 0x00, 0x00, 0x80, // 238 +0xC0, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0x00, 0xFE, 0x3F, 0x00, 0x00, 0xFE, 0x3F, 0x00, 0xC0, 0x00, 0x00, 0x00, 0xC0, // 239 +0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0x07, 0x00, 0x00, 0xF8, 0x0F, 0x00, 0x00, 0x1D, 0x1C, 0x00, 0xA0, 0x0F, 0x38, 0x00, 0xA0, 0x06, 0x30, 0x00, 0xE0, 0x06, 0x30, 0x00, 0xC0, 0x06, 0x30, 0x00, 0xC0, 0x0F, 0x38, 0x00, 0x20, 0x1F, 0x1C, 0x00, 0x00, 0xFC, 0x0F, 0x00, 0x00, 0xE0, 0x07, // 240 +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFE, 0x3F, 0x00, 0xC0, 0xFE, 0x3F, 0x00, 0xE0, 0x18, 0x00, 0x00, 0x60, 0x0C, 0x00, 0x00, 0x60, 0x06, 0x00, 0x00, 0xC0, 0x06, 0x00, 0x00, 0xC0, 0x06, 0x00, 0x00, 0xE0, 0x0E, 0x00, 0x00, 0x60, 0xFC, 0x3F, 0x00, 0x00, 0xF8, 0x3F, // 241 +0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0x07, 0x00, 0x00, 0xF8, 0x0F, 0x00, 0x00, 0x1C, 0x1C, 0x00, 0x20, 0x0E, 0x38, 0x00, 0x60, 0x06, 0x30, 0x00, 0xE0, 0x06, 0x30, 0x00, 0x80, 0x06, 0x30, 0x00, 0x00, 0x0E, 0x38, 0x00, 0x00, 0x1C, 0x1C, 0x00, 0x00, 0xF8, 0x0F, 0x00, 0x00, 0xF0, 0x07, // 242 +0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0x07, 0x00, 0x00, 0xF8, 0x0F, 0x00, 0x00, 0x1C, 0x1C, 0x00, 0x00, 0x0E, 0x38, 0x00, 0x80, 0x06, 0x30, 0x00, 0xE0, 0x06, 0x30, 0x00, 0x60, 0x06, 0x30, 0x00, 0x20, 0x0E, 0x38, 0x00, 0x00, 0x1C, 0x1C, 0x00, 0x00, 0xF8, 0x0F, 0x00, 0x00, 0xF0, 0x07, // 243 +0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0x07, 0x00, 0x00, 0xF8, 0x0F, 0x00, 0x00, 0x1C, 0x1C, 0x00, 0x80, 0x0E, 0x38, 0x00, 0xC0, 0x06, 0x30, 0x00, 0x60, 0x06, 0x30, 0x00, 0x60, 0x06, 0x30, 0x00, 0xC0, 0x0E, 0x38, 0x00, 0x80, 0x1C, 0x1C, 0x00, 0x00, 0xF8, 0x0F, 0x00, 0x00, 0xF0, 0x07, // 244 +0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0x07, 0x00, 0x00, 0xF8, 0x0F, 0x00, 0xC0, 0x1C, 0x1C, 0x00, 0xE0, 0x0E, 0x38, 0x00, 0x60, 0x06, 0x30, 0x00, 0x60, 0x06, 0x30, 0x00, 0xC0, 0x06, 0x30, 0x00, 0xC0, 0x0E, 0x38, 0x00, 0xE0, 0x1C, 0x1C, 0x00, 0x60, 0xF8, 0x0F, 0x00, 0x00, 0xF0, 0x07, // 245 +0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0x07, 0x00, 0x00, 0xF8, 0x0F, 0x00, 0x00, 0x1C, 0x1C, 0x00, 0xC0, 0x0E, 0x38, 0x00, 0xC0, 0x06, 0x30, 0x00, 0x00, 0x06, 0x30, 0x00, 0x00, 0x06, 0x30, 0x00, 0xC0, 0x0E, 0x38, 0x00, 0xC0, 0x1C, 0x1C, 0x00, 0x00, 0xF8, 0x0F, 0x00, 0x00, 0xF0, 0x07, // 246 +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, 0xB6, 0x01, 0x00, 0x00, 0xB6, 0x01, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x30, // 247 +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0x67, 0x00, 0x00, 0xF8, 0x7F, 0x00, 0x00, 0x1C, 0x1C, 0x00, 0x00, 0x0E, 0x3F, 0x00, 0x00, 0x86, 0x33, 0x00, 0x00, 0xE6, 0x31, 0x00, 0x00, 0x76, 0x30, 0x00, 0x00, 0x3E, 0x38, 0x00, 0x00, 0x1C, 0x1C, 0x00, 0x00, 0xFF, 0x0F, 0x00, 0x00, 0xF3, 0x07, // 248 +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFE, 0x0F, 0x00, 0x00, 0xFE, 0x1F, 0x00, 0x20, 0x00, 0x38, 0x00, 0x60, 0x00, 0x30, 0x00, 0xE0, 0x00, 0x30, 0x00, 0x80, 0x00, 0x30, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0xFE, 0x3F, 0x00, 0x00, 0xFE, 0x3F, // 249 +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFE, 0x0F, 0x00, 0x00, 0xFE, 0x1F, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0x30, 0x00, 0x80, 0x00, 0x30, 0x00, 0xE0, 0x00, 0x30, 0x00, 0x60, 0x00, 0x18, 0x00, 0x20, 0x00, 0x0C, 0x00, 0x00, 0xFE, 0x3F, 0x00, 0x00, 0xFE, 0x3F, // 250 +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFE, 0x0F, 0x00, 0x00, 0xFE, 0x1F, 0x00, 0x80, 0x00, 0x38, 0x00, 0xC0, 0x00, 0x30, 0x00, 0x60, 0x00, 0x30, 0x00, 0x60, 0x00, 0x30, 0x00, 0xC0, 0x00, 0x18, 0x00, 0x80, 0x00, 0x0C, 0x00, 0x00, 0xFE, 0x3F, 0x00, 0x00, 0xFE, 0x3F, // 251 +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFE, 0x0F, 0x00, 0x00, 0xFE, 0x1F, 0x00, 0xC0, 0x00, 0x38, 0x00, 0xC0, 0x00, 0x30, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x30, 0x00, 0xC0, 0x00, 0x18, 0x00, 0xC0, 0x00, 0x0C, 0x00, 0x00, 0xFE, 0x3F, 0x00, 0x00, 0xFE, 0x3F, // 252 +0x00, 0x00, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x7E, 0x00, 0x06, 0x00, 0xF0, 0x01, 0x06, 0x00, 0x80, 0x0F, 0x07, 0x80, 0x00, 0xFE, 0x03, 0xE0, 0x00, 0xFC, 0x00, 0x60, 0xC0, 0x1F, 0x00, 0x20, 0xF8, 0x03, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x00, 0x06, // 253 +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0xFF, 0xFF, 0x07, 0xE0, 0xFF, 0xFF, 0x07, 0x00, 0x1C, 0x18, 0x00, 0x00, 0x06, 0x30, 0x00, 0x00, 0x06, 0x30, 0x00, 0x00, 0x06, 0x30, 0x00, 0x00, 0x0E, 0x38, 0x00, 0x00, 0x1C, 0x1C, 0x00, 0x00, 0xF8, 0x0F, 0x00, 0x00, 0xF0, 0x03, // 254 +0x00, 0x00, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x7E, 0x00, 0x06, 0xC0, 0xF0, 0x01, 0x06, 0xC0, 0x80, 0x0F, 0x07, 0x00, 0x00, 0xFE, 0x03, 0x00, 0x00, 0xFC, 0x00, 0xC0, 0xC0, 0x1F, 0x00, 0xC0, 0xF8, 0x03, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x00, 0x06, // 255 }; \ No newline at end of file diff --git a/src/graphics/fonts/OLEDDisplayFontsPL.h b/src/graphics/fonts/OLEDDisplayFontsPL.h index 59dd92c41..1e6790e07 100644 --- a/src/graphics/fonts/OLEDDisplayFontsPL.h +++ b/src/graphics/fonts/OLEDDisplayFontsPL.h @@ -6,6 +6,7 @@ #elif __MBED__ #define PROGMEM #endif - extern const uint8_t ArialMT_Plain_10_PL[] PROGMEM; +extern const uint8_t ArialMT_Plain_16_PL[] PROGMEM; +extern const uint8_t ArialMT_Plain_24_PL[] PROGMEM; #endif \ No newline at end of file From fd60c9b3be6ef285ab38866d2d6a9543d44519f7 Mon Sep 17 00:00:00 2001 From: Tom Fifield Date: Sun, 12 Jan 2025 15:16:26 +0800 Subject: [PATCH 14/89] Upgrade to LovyanGFX 1.2 (#5677) * [WIP] Attempt upgrade to LovyanGFX 1.1.16 This is the version most used by the TFT branch. I wonder if this will work with our existing code? :) * Update Portduino to LovyanGFX 1.20.0 Manuel says it's good to go. * Update unPhone platformio.ini --------- Co-authored-by: Manuel <71137295+mverch67@users.noreply.github.com> --- arch/portduino/portduino.ini | 4 ++-- variants/chatter2/platformio.ini | 2 +- variants/heltec_wireless_tracker/platformio.ini | 2 +- variants/heltec_wireless_tracker_V1_0/platformio.ini | 2 +- variants/m5stack_core/platformio.ini | 2 +- variants/mesh-tab/platformio.ini | 2 +- variants/picomputer-s3/platformio.ini | 2 +- variants/t-deck/platformio.ini | 2 +- variants/t-watch-s3/platformio.ini | 2 +- variants/tracksenger/platformio.ini | 4 ++-- variants/unphone/platformio.ini | 6 +++--- variants/wiphone/platformio.ini | 4 ++-- 12 files changed, 17 insertions(+), 17 deletions(-) diff --git a/arch/portduino/portduino.ini b/arch/portduino/portduino.ini index aa1150e9a..7ea6a77a2 100644 --- a/arch/portduino/portduino.ini +++ b/arch/portduino/portduino.ini @@ -25,7 +25,7 @@ lib_deps = ${networking_base.lib_deps} ${radiolib_base.lib_deps} rweather/Crypto@^0.4.0 - https://github.com/lovyan03/LovyanGFX.git#1401c28a47646fe00538d487adcb2eb3c72de805 + lovyan03/LovyanGFX@^1.2.0 https://github.com/pine64/libch341-spi-userspace#a9b17e3452f7fb747000d9b4ad4409155b39f6ef build_flags = @@ -40,4 +40,4 @@ build_flags = -lgpiod -lyaml-cpp -li2c - -std=c++17 \ No newline at end of file + -std=c++17 diff --git a/variants/chatter2/platformio.ini b/variants/chatter2/platformio.ini index 1f086cf07..83e00d0c4 100644 --- a/variants/chatter2/platformio.ini +++ b/variants/chatter2/platformio.ini @@ -9,4 +9,4 @@ build_flags = lib_deps = ${esp32_base.lib_deps} - lovyan03/LovyanGFX@^1.1.8 \ No newline at end of file + lovyan03/LovyanGFX@^1.2.0 diff --git a/variants/heltec_wireless_tracker/platformio.ini b/variants/heltec_wireless_tracker/platformio.ini index c7ecce8ea..4f686d289 100644 --- a/variants/heltec_wireless_tracker/platformio.ini +++ b/variants/heltec_wireless_tracker/platformio.ini @@ -11,4 +11,4 @@ build_flags = lib_deps = ${esp32s3_base.lib_deps} - lovyan03/LovyanGFX@^1.1.8 + lovyan03/LovyanGFX@^1.2.0 diff --git a/variants/heltec_wireless_tracker_V1_0/platformio.ini b/variants/heltec_wireless_tracker_V1_0/platformio.ini index 0e48c72f2..5f512b816 100644 --- a/variants/heltec_wireless_tracker_V1_0/platformio.ini +++ b/variants/heltec_wireless_tracker_V1_0/platformio.ini @@ -10,4 +10,4 @@ build_flags = ;-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 + lovyan03/LovyanGFX@^1.2.0 diff --git a/variants/m5stack_core/platformio.ini b/variants/m5stack_core/platformio.ini index 95f5aea9f..7418d9e17 100644 --- a/variants/m5stack_core/platformio.ini +++ b/variants/m5stack_core/platformio.ini @@ -25,4 +25,4 @@ lib_ignore = m5stack-core lib_deps = ${esp32_base.lib_deps} - lovyan03/LovyanGFX@^1.1.8 \ No newline at end of file + lovyan03/LovyanGFX@^1.2.0 diff --git a/variants/mesh-tab/platformio.ini b/variants/mesh-tab/platformio.ini index 30be7dbb8..d6fd1a3ac 100644 --- a/variants/mesh-tab/platformio.ini +++ b/variants/mesh-tab/platformio.ini @@ -54,7 +54,7 @@ build_src_filter = ${esp32_base.build_src_filter} +<../lib/device-ui/locale> +<../lib/device-ui/source> lib_deps = ${esp32_base.lib_deps} - lovyan03/LovyanGFX@^1.1.16 + lovyan03/LovyanGFX@^1.2.0 [mesh_tab_xpt2046] extends = mesh_tab_base diff --git a/variants/picomputer-s3/platformio.ini b/variants/picomputer-s3/platformio.ini index 202cd05e7..5a05d7b90 100644 --- a/variants/picomputer-s3/platformio.ini +++ b/variants/picomputer-s3/platformio.ini @@ -14,4 +14,4 @@ build_flags = lib_deps = ${esp32s3_base.lib_deps} - lovyan03/LovyanGFX@^1.1.8 + lovyan03/LovyanGFX@^1.2.0 diff --git a/variants/t-deck/platformio.ini b/variants/t-deck/platformio.ini index d2f09f50b..003dd184d 100644 --- a/variants/t-deck/platformio.ini +++ b/variants/t-deck/platformio.ini @@ -14,6 +14,6 @@ build_flags = ${esp32s3_base.build_flags} -Ivariants/t-deck lib_deps = ${esp32s3_base.lib_deps} - lovyan03/LovyanGFX@^1.1.9 + lovyan03/LovyanGFX@^1.2.0 earlephilhower/ESP8266Audio@^1.9.9 earlephilhower/ESP8266SAM@^1.0.1 diff --git a/variants/t-watch-s3/platformio.ini b/variants/t-watch-s3/platformio.ini index 005c4d021..8f48cf6c4 100644 --- a/variants/t-watch-s3/platformio.ini +++ b/variants/t-watch-s3/platformio.ini @@ -12,7 +12,7 @@ build_flags = ${esp32_base.build_flags} -DHAS_BMA423=1 lib_deps = ${esp32s3_base.lib_deps} - lovyan03/LovyanGFX@^1.1.9 + lovyan03/LovyanGFX@^1.2.0 lewisxhe/PCF8563_Library@1.0.1 adafruit/Adafruit DRV2605 Library@^1.2.2 earlephilhower/ESP8266Audio@^1.9.9 diff --git a/variants/tracksenger/platformio.ini b/variants/tracksenger/platformio.ini index d3e31264f..796a3b7d5 100644 --- a/variants/tracksenger/platformio.ini +++ b/variants/tracksenger/platformio.ini @@ -11,7 +11,7 @@ build_flags = lib_deps = ${esp32s3_base.lib_deps} - lovyan03/LovyanGFX@^1.1.8 + lovyan03/LovyanGFX@^1.2.0 [env:tracksenger-lcd] extends = esp32s3_base @@ -26,7 +26,7 @@ build_flags = lib_deps = ${esp32s3_base.lib_deps} - lovyan03/LovyanGFX@^1.1.8 + lovyan03/LovyanGFX@^1.2.0 [env:tracksenger-oled] extends = esp32s3_base diff --git a/variants/unphone/platformio.ini b/variants/unphone/platformio.ini index 489c70f99..e21e9ed77 100644 --- a/variants/unphone/platformio.ini +++ b/variants/unphone/platformio.ini @@ -26,7 +26,7 @@ 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.12 + lovyan03/LovyanGFX@ 1.2.0 https://gitlab.com/hamishcunningham/unphonelibrary#meshtastic@9.0.0 adafruit/Adafruit NeoPixel @ ^1.12.0 @@ -72,6 +72,6 @@ build_src_filter = ${esp32_base.build_src_filter} +<../variants/unphone> +<../lib/device-ui/source> lib_deps = ${esp32s3_base.lib_deps} - lovyan03/LovyanGFX@^1.1.12 + lovyan03/LovyanGFX@^1.2.0 https://gitlab.com/hamishcunningham/unphonelibrary#meshtastic@9.0.0 - adafruit/Adafruit NeoPixel@1.12.0 \ No newline at end of file + adafruit/Adafruit NeoPixel@1.12.0 diff --git a/variants/wiphone/platformio.ini b/variants/wiphone/platformio.ini index 0218f8930..362102731 100644 --- a/variants/wiphone/platformio.ini +++ b/variants/wiphone/platformio.ini @@ -8,6 +8,6 @@ build_flags = ${esp32_base.build_flags} -D WIPHONE -I variants/wiphone lib_deps = ${esp32_base.lib_deps} - lovyan03/LovyanGFX@^1.1.8 + lovyan03/LovyanGFX@^1.2.0 sparkfun/SX1509 IO Expander@^3.0.5 - pololu/APA102@^3.0.0 \ No newline at end of file + pololu/APA102@^3.0.0 From 124936b6cfc848483df69d0eacd0dcfd7fb34a12 Mon Sep 17 00:00:00 2001 From: Eric Severance Date: Sun, 12 Jan 2025 10:05:04 -0800 Subject: [PATCH 15/89] Avoid a potential NULL pointer reference in nrf52/BluetoothPhoneAPI (#5830) --- src/platform/nrf52/NRF52Bluetooth.cpp | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/platform/nrf52/NRF52Bluetooth.cpp b/src/platform/nrf52/NRF52Bluetooth.cpp index 31bbc7fa9..541e90ab2 100644 --- a/src/platform/nrf52/NRF52Bluetooth.cpp +++ b/src/platform/nrf52/NRF52Bluetooth.cpp @@ -44,11 +44,7 @@ class BluetoothPhoneAPI : public PhoneAPI } /// Check the current underlying physical link to see if the client is currently connected - virtual bool checkIsConnected() override - { - BLEConnection *connection = Bluefruit.Connection(connectionHandle); - return connection->connected(); - } + virtual bool checkIsConnected() override { return Bluefruit.connected(connectionHandle); } }; static BluetoothPhoneAPI *bluetoothPhoneAPI; From 0cf4a2951a7eccf390c52c5adf49e6196b2cae82 Mon Sep 17 00:00:00 2001 From: Erayd Date: Mon, 13 Jan 2025 07:05:51 +1300 Subject: [PATCH 16/89] Bugfix for low-priority packet replacement when TX queue is full (#5827) * Correct function comment * Enqueue the intended packet, not the pointer to what we just dropped! * Add some log output when we drop packets due to a full queue * Make it clear when a non-late packet is dropped * Remove from queue before release, not after * Erase dropped packet from queue * Declared type * Log TX queue length after every send * Fix operand order * Add worst-case cap on TX delay vs current time --- protobufs | 2 +- src/mesh/MeshPacketQueue.cpp | 22 +++++++++++++++++----- src/mesh/RadioLibInterface.cpp | 5 +++-- 3 files changed, 21 insertions(+), 8 deletions(-) diff --git a/protobufs b/protobufs index 76f806e1b..c55f120a9 160000 --- a/protobufs +++ b/protobufs @@ -1 +1 @@ -Subproject commit 76f806e1bb1e2a7b157a14fadd095775f63db5e4 +Subproject commit c55f120a9c1ce90c85e4826907a0b9bcb2d5f5a2 diff --git a/src/mesh/MeshPacketQueue.cpp b/src/mesh/MeshPacketQueue.cpp index d7ee65800..7dd84639d 100644 --- a/src/mesh/MeshPacketQueue.cpp +++ b/src/mesh/MeshPacketQueue.cpp @@ -69,7 +69,11 @@ bool MeshPacketQueue::enqueue(meshtastic_MeshPacket *p) { // no space - try to replace a lower priority packet in the queue if (queue.size() >= maxLen) { - return replaceLowerPriorityPacket(p); + bool replaced = replaceLowerPriorityPacket(p); + if (!replaced) { + LOG_WARN("TX queue is full, and there is no lower-priority packet available to evict in favour of 0x%08x", p->id); + } + return replaced; } // Find the correct position using upper_bound to maintain a stable order @@ -113,7 +117,10 @@ meshtastic_MeshPacket *MeshPacketQueue::remove(NodeNum from, PacketId id, bool t return NULL; } -/** Attempt to find and remove a packet from this queue. Returns the packet which was removed from the queue */ +/** + * Attempt to find a lower-priority packet in the queue and replace it with the provided one. + * @return True if the replacement succeeded, false otherwise + */ bool MeshPacketQueue::replaceLowerPriorityPacket(meshtastic_MeshPacket *p) { @@ -122,11 +129,12 @@ bool MeshPacketQueue::replaceLowerPriorityPacket(meshtastic_MeshPacket *p) } // Check if the packet at the back has a lower priority than the new packet - auto &backPacket = queue.back(); + auto *backPacket = queue.back(); if (!backPacket->tx_after && backPacket->priority < p->priority) { + LOG_WARN("Dropping packet 0x%08x to make room in the TX queue for higher-priority packet 0x%08x", backPacket->id, p->id); // Remove the back packet - packetPool.release(backPacket); queue.pop_back(); + packetPool.release(backPacket); // Insert the new packet in the correct order enqueue(p); return true; @@ -139,8 +147,12 @@ bool MeshPacketQueue::replaceLowerPriorityPacket(meshtastic_MeshPacket *p) for (; refPacket->tx_after && it != queue.begin(); refPacket = *--it) ; if (!refPacket->tx_after && refPacket->priority < p->priority) { + LOG_WARN("Dropping non-late packet 0x%08x to make room in the TX queue for higher-priority packet 0x%08x", + refPacket->id, p->id); + queue.erase(it); packetPool.release(refPacket); - enqueue(refPacket); + // Insert the new packet in the correct order + enqueue(p); return true; } } diff --git a/src/mesh/RadioLibInterface.cpp b/src/mesh/RadioLibInterface.cpp index 0a047a660..e31f0b3e2 100644 --- a/src/mesh/RadioLibInterface.cpp +++ b/src/mesh/RadioLibInterface.cpp @@ -271,6 +271,7 @@ void RadioLibInterface::onNotify(uint32_t notification) uint32_t xmitMsec = getPacketTime(txp); airTime->logAirtime(TX_LOG, xmitMsec); } + LOG_DEBUG("%d packets remain in the TX queue", txQueue.getMaxLen() - txQueue.getFree()); } } } @@ -297,8 +298,8 @@ void RadioLibInterface::setTransmitDelay() if (p->tx_after) { unsigned long add_delay = p->rx_rssi ? getTxDelayMsecWeighted(p->rx_snr) : getTxDelayMsec(); unsigned long now = millis(); - p->tx_after = max(p->tx_after + add_delay, now + add_delay); - notifyLater(now - p->tx_after, TRANSMIT_DELAY_COMPLETED, false); + p->tx_after = min(max(p->tx_after + add_delay, now + add_delay), now + 2 * getTxDelayMsecWeightedWorst(p->rx_snr)); + notifyLater(p->tx_after - now, TRANSMIT_DELAY_COMPLETED, false); } else if (p->rx_snr == 0 && p->rx_rssi == 0) { /* We assume if rx_snr = 0 and rx_rssi = 0, the packet was generated locally. * This assumption is valid because of the offset generated by the radio to account for the noise From 70296b47bc9b4a026f3aceb73d8afb8448249160 Mon Sep 17 00:00:00 2001 From: Patrick Siegl <3261314+psiegl@users.noreply.github.com> Date: Sun, 12 Jan 2025 20:40:25 +0100 Subject: [PATCH 17/89] Multi gpiochip support for native environment (#5743) * For each GPIO PIN, allow to specify gpiochip and line * Added support for LLCC68 in native env. * Removed one if by employing && * Fix for log, as std::string and not const char* * Remove CH341 flag, enabling it for all LoRa chips * Provide a default example --------- Co-authored-by: Ben Meadors Co-authored-by: Jonathan Bennett --- bin/config-dist.yaml | 45 +++++- src/main.cpp | 141 ++++++------------- src/mesh/RF95Interface.cpp | 22 +-- src/mesh/SX126xInterface.cpp | 12 +- src/mesh/SX128xInterface.cpp | 42 +++--- src/platform/portduino/PortduinoGlue.cpp | 167 ++++++++++++----------- src/platform/portduino/PortduinoGlue.h | 37 +++-- 7 files changed, 237 insertions(+), 229 deletions(-) diff --git a/bin/config-dist.yaml b/bin/config-dist.yaml index e68b01ba3..c8f181308 100644 --- a/bin/config-dist.yaml +++ b/bin/config-dist.yaml @@ -23,6 +23,47 @@ Lora: # Busy: 20 # Reset: 18 +### The Radxa Zero 3E/W employs multiple gpio chips. +### Each gpio pin must be unique, but can be assigned to a specific gpio chip and line. +### In case solely a no. is given, the default gpio chip and pin == line will be employed. +### +# Module: sx1262 # Radxa Zero 3E/W + Ebyte E22-900M30S +# DIO2_AS_RF_SWITCH: true +# DIO3_TCXO_VOLTAGE: 1.8 +# CS: # NSS PIN_24 -> chip 4, line 22 +# pin: 24 +# gpiochip: 4 +# line: 22 +# SCK: # SCK PIN_23 -> chip 4, line 18 +# pin: 23 +# gpiochip: 4 +# line: 18 +# Busy: # BUSY PIN_29 -> chip 3!, line 11 +# pin: 29 +# gpiochip: 3 +# line: 11 +# MOSI: # MOSI PIN_19 -> chip 4, line 19 +# pin: 19 +# gpiochip: 4 +# line: 19 +# MISO: # MISO PIN_21 -> chip 4, line 21 +# pin: 21 +# gpiochip: 4 +# line: 21 +# Reset: # NRST PIN_27 -> chip 4, line 10 +# pin: 27 +# gpiochip: 4 +# line: 10 +# IRQ: # DIO1 PIN_28 -> chip 4, line 11 +# pin: 28 +# gpiochip: 4 +# line: 11 +# RXen: # RXEN PIN_22 -> chip 3!, line 17 +# pin: 22 +# gpiochip: 3 +# line: 17 +# TXen: RADIOLIB_NC # TXEN no PIN, no line, fallback to default gpio chip + # Module: sx1268 # SX1268-based modules, tested with Ebyte E22 400M33S # CS: 21 # IRQ: 16 @@ -39,7 +80,7 @@ Lora: # spiSpeed: 2000000 -### Set gpio chip to use in /dev/. Defaults to 0. +### Set default/fallback gpio chip to use in /dev/. Defaults to 0. ### Notably the Raspberry Pi 5 puts the GPIO header on gpiochip4 # gpiochip: 4 @@ -147,4 +188,4 @@ General: MaxMessageQueue: 100 ConfigDirectory: /etc/meshtasticd/config.d/ # MACAddress: AA:BB:CC:DD:EE:FF -# MACAddressSource: eth0 \ No newline at end of file +# MACAddressSource: eth0 diff --git a/src/main.cpp b/src/main.cpp index 4a642ef6d..338fca5c1 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -826,116 +826,61 @@ void setup() #endif #ifdef ARCH_PORTDUINO - if (settingsMap[use_sx1262]) { - if (!rIf) { - LOG_DEBUG("Activate sx1262 radio on SPI port %s", settingsStrings[spidev].c_str()); + const struct { configNames cfgName; + std::string strName; + } loraModules[] = { + { use_rf95, "RF95" }, + { use_sx1262, "sx1262" }, + { use_sx1268, "sx1268" }, + { use_sx1280, "sx1280" }, + { use_lr1110, "lr1110" }, + { use_lr1120, "lr1120" }, + { use_lr1121, "lr1121" }, + { use_llcc68, "LLCC68" } + }; + // as one can't use a function pointer to the class constructor: + auto loraModuleInterface = [](configNames cfgName, LockingArduinoHal *hal, RADIOLIB_PIN_TYPE cs, RADIOLIB_PIN_TYPE irq, RADIOLIB_PIN_TYPE rst, RADIOLIB_PIN_TYPE busy) { + switch (cfgName) { + case use_rf95: + return (RadioInterface*)new RF95Interface(hal, cs, irq, rst, busy); + case use_sx1262: + return (RadioInterface*)new SX1262Interface(hal, cs, irq, rst, busy); + case use_sx1268: + return (RadioInterface*)new SX1268Interface(hal, cs, irq, rst, busy); + case use_sx1280: + return (RadioInterface*)new SX1280Interface(hal, cs, irq, rst, busy); + case use_lr1110: + return (RadioInterface*)new LR1110Interface(hal, cs, irq, rst, busy); + case use_lr1120: + return (RadioInterface*)new LR1120Interface(hal, cs, irq, rst, busy); + case use_lr1121: + return (RadioInterface*)new LR1121Interface(hal, cs, irq, rst, busy); + case use_llcc68: + return (RadioInterface*)new LLCC68Interface(hal, cs, irq, rst, busy); + default: + assert(0); // shouldn't happen + return (RadioInterface*)nullptr; + } + }; + for (auto& loraModule : loraModules) { + if (settingsMap[loraModule.cfgName] && !rIf) { + LOG_DEBUG("Activate %s radio on SPI port %s", loraModule.strName.c_str(), settingsStrings[spidev].c_str()); if (settingsStrings[spidev] == "ch341") { RadioLibHAL = ch341Hal; } else { RadioLibHAL = new LockingArduinoHal(SPI, spiSettings); } - rIf = new SX1262Interface((LockingArduinoHal *)RadioLibHAL, settingsMap[cs], settingsMap[irq], settingsMap[reset], - settingsMap[busy]); + rIf = loraModuleInterface(loraModule.cfgName, (LockingArduinoHal *)RadioLibHAL, settingsMap[cs_pin], settingsMap[irq_pin], settingsMap[reset_pin], settingsMap[busy_pin]); if (!rIf->init()) { - LOG_WARN("No SX1262 radio"); - delete rIf; - exit(EXIT_FAILURE); - } else { - LOG_INFO("SX1262 init success"); - } - } - } else if (settingsMap[use_rf95]) { - if (!rIf) { - LOG_DEBUG("Activate rf95 radio on SPI port %s", settingsStrings[spidev].c_str()); - RadioLibHAL = new LockingArduinoHal(SPI, spiSettings); - rIf = new RF95Interface((LockingArduinoHal *)RadioLibHAL, settingsMap[cs], settingsMap[irq], settingsMap[reset], - settingsMap[busy]); - if (!rIf->init()) { - LOG_WARN("No RF95 radio"); + LOG_WARN("No %s radio", loraModule.strName.c_str()); delete rIf; rIf = NULL; exit(EXIT_FAILURE); } else { - LOG_INFO("RF95 init success"); - } - } - } else if (settingsMap[use_sx1280]) { - if (!rIf) { - LOG_DEBUG("Activate sx1280 radio on SPI port %s", settingsStrings[spidev].c_str()); - RadioLibHAL = new LockingArduinoHal(SPI, spiSettings); - rIf = new SX1280Interface((LockingArduinoHal *)RadioLibHAL, settingsMap[cs], settingsMap[irq], settingsMap[reset], - settingsMap[busy]); - if (!rIf->init()) { - LOG_WARN("No SX1280 radio"); - delete rIf; - rIf = NULL; - exit(EXIT_FAILURE); - } else { - LOG_INFO("SX1280 init success"); - } - } - } else if (settingsMap[use_lr1110]) { - if (!rIf) { - LOG_DEBUG("Activate lr1110 radio on SPI port %s", settingsStrings[spidev].c_str()); - LockingArduinoHal *RadioLibHAL = new LockingArduinoHal(SPI, spiSettings); - rIf = new LR1110Interface((LockingArduinoHal *)RadioLibHAL, settingsMap[cs], settingsMap[irq], settingsMap[reset], - settingsMap[busy]); - if (!rIf->init()) { - LOG_WARN("No LR1110 radio"); - delete rIf; - rIf = NULL; - exit(EXIT_FAILURE); - } else { - LOG_INFO("LR1110 init success"); - } - } - } else if (settingsMap[use_lr1120]) { - if (!rIf) { - LOG_DEBUG("Activate lr1120 radio on SPI port %s", settingsStrings[spidev].c_str()); - LockingArduinoHal *RadioLibHAL = new LockingArduinoHal(SPI, spiSettings); - rIf = new LR1120Interface((LockingArduinoHal *)RadioLibHAL, settingsMap[cs], settingsMap[irq], settingsMap[reset], - settingsMap[busy]); - if (!rIf->init()) { - LOG_WARN("No LR1120 radio"); - delete rIf; - rIf = NULL; - exit(EXIT_FAILURE); - } else { - LOG_INFO("LR1120 init success"); - } - } - } else if (settingsMap[use_lr1121]) { - if (!rIf) { - LOG_DEBUG("Activate lr1121 radio on SPI port %s", settingsStrings[spidev].c_str()); - LockingArduinoHal *RadioLibHAL = new LockingArduinoHal(SPI, spiSettings); - rIf = new LR1121Interface((LockingArduinoHal *)RadioLibHAL, settingsMap[cs], settingsMap[irq], settingsMap[reset], - settingsMap[busy]); - if (!rIf->init()) { - LOG_WARN("No LR1121 radio"); - delete rIf; - rIf = NULL; - exit(EXIT_FAILURE); - } else { - LOG_INFO("LR1121 init success"); - } - } - } else if (settingsMap[use_sx1268]) { - if (!rIf) { - LOG_DEBUG("Activate sx1268 radio on SPI port %s", settingsStrings[spidev].c_str()); - RadioLibHAL = new LockingArduinoHal(SPI, spiSettings); - rIf = new SX1268Interface((LockingArduinoHal *)RadioLibHAL, settingsMap[cs], settingsMap[irq], settingsMap[reset], - settingsMap[busy]); - if (!rIf->init()) { - LOG_WARN("No SX1268 radio"); - delete rIf; - rIf = NULL; - exit(EXIT_FAILURE); - } else { - LOG_INFO("SX1268 init success"); + LOG_INFO("%s init success", loraModule.strName.c_str()); } } } - #elif defined(HW_SPI1_DEVICE) LockingArduinoHal *RadioLibHAL = new LockingArduinoHal(SPI1, spiSettings); #else // HW_SPI1_DEVICE @@ -1280,4 +1225,4 @@ void loop() mainDelay.delay(delayMsec); } } -#endif \ No newline at end of file +#endif diff --git a/src/mesh/RF95Interface.cpp b/src/mesh/RF95Interface.cpp index 9ef045099..d4d9ad23c 100644 --- a/src/mesh/RF95Interface.cpp +++ b/src/mesh/RF95Interface.cpp @@ -91,16 +91,16 @@ void RF95Interface::setTransmitEnable(bool txon) #ifdef RF95_TXEN digitalWrite(RF95_TXEN, txon ? 1 : 0); #elif ARCH_PORTDUINO - if (settingsMap[txen] != RADIOLIB_NC) { - digitalWrite(settingsMap[txen], txon ? 1 : 0); + if (settingsMap[txen_pin] != RADIOLIB_NC) { + digitalWrite(settingsMap[txen_pin], txon ? 1 : 0); } #endif #ifdef RF95_RXEN digitalWrite(RF95_RXEN, txon ? 0 : 1); #elif ARCH_PORTDUINO - if (settingsMap[rxen] != RADIOLIB_NC) { - digitalWrite(settingsMap[rxen], txon ? 0 : 1); + if (settingsMap[rxen_pin] != RADIOLIB_NC) { + digitalWrite(settingsMap[rxen_pin], txon ? 0 : 1); } #endif } @@ -164,13 +164,13 @@ bool RF95Interface::init() digitalWrite(RF95_RXEN, 1); #endif #if ARCH_PORTDUINO - if (settingsMap[txen] != RADIOLIB_NC) { - pinMode(settingsMap[txen], OUTPUT); - digitalWrite(settingsMap[txen], 0); + if (settingsMap[txen_pin] != RADIOLIB_NC) { + pinMode(settingsMap[txen_pin], OUTPUT); + digitalWrite(settingsMap[txen_pin], 0); } - if (settingsMap[rxen] != RADIOLIB_NC) { - pinMode(settingsMap[rxen], OUTPUT); - digitalWrite(settingsMap[rxen], 0); + if (settingsMap[rxen_pin] != RADIOLIB_NC) { + pinMode(settingsMap[rxen_pin], OUTPUT); + digitalWrite(settingsMap[rxen_pin], 0); } #endif setTransmitEnable(false); @@ -337,4 +337,4 @@ bool RF95Interface::sleep() return true; } -#endif \ No newline at end of file +#endif diff --git a/src/mesh/SX126xInterface.cpp b/src/mesh/SX126xInterface.cpp index ed0267c5b..b13bb6faf 100644 --- a/src/mesh/SX126xInterface.cpp +++ b/src/mesh/SX126xInterface.cpp @@ -51,9 +51,9 @@ template bool SX126xInterface::init() #if ARCH_PORTDUINO float tcxoVoltage = (float)settingsMap[dio3_tcxo_voltage] / 1000; - if (settingsMap[sx126x_ant_sw] != RADIOLIB_NC) { - digitalWrite(settingsMap[sx126x_ant_sw], HIGH); - pinMode(settingsMap[sx126x_ant_sw], OUTPUT); + if (settingsMap[sx126x_ant_sw_pin] != RADIOLIB_NC) { + digitalWrite(settingsMap[sx126x_ant_sw_pin], HIGH); + pinMode(settingsMap[sx126x_ant_sw_pin], OUTPUT); } // FIXME: correct logic to default to not using TCXO if no voltage is specified for SX126X_DIO3_TCXO_VOLTAGE #elif !defined(SX126X_DIO3_TCXO_VOLTAGE) @@ -121,8 +121,8 @@ template bool SX126xInterface::init() // no effect #if ARCH_PORTDUINO if (res == RADIOLIB_ERR_NONE) { - LOG_DEBUG("Use MCU pin %i as RXEN and pin %i as TXEN to control RF switching", settingsMap[rxen], settingsMap[txen]); - lora.setRfSwitchPins(settingsMap[rxen], settingsMap[txen]); + LOG_DEBUG("Use MCU pin %i as RXEN and pin %i as TXEN to control RF switching", settingsMap[rxen_pin], settingsMap[txen_pin]); + lora.setRfSwitchPins(settingsMap[rxen_pin], settingsMap[txen_pin]); } #else #ifndef SX126X_RXEN @@ -341,4 +341,4 @@ template bool SX126xInterface::sleep() return true; } -#endif \ No newline at end of file +#endif diff --git a/src/mesh/SX128xInterface.cpp b/src/mesh/SX128xInterface.cpp index 013164bca..ee3408456 100644 --- a/src/mesh/SX128xInterface.cpp +++ b/src/mesh/SX128xInterface.cpp @@ -38,13 +38,13 @@ template bool SX128xInterface::init() #endif #if ARCH_PORTDUINO - if (settingsMap[rxen] != RADIOLIB_NC) { - pinMode(settingsMap[rxen], OUTPUT); - digitalWrite(settingsMap[rxen], LOW); // Set low before becoming an output + if (settingsMap[rxen_pin] != RADIOLIB_NC) { + pinMode(settingsMap[rxen_pin], OUTPUT); + digitalWrite(settingsMap[rxen_pin], LOW); // Set low before becoming an output } - if (settingsMap[txen] != RADIOLIB_NC) { - pinMode(settingsMap[txen], OUTPUT); - digitalWrite(settingsMap[txen], LOW); // Set low before becoming an output + if (settingsMap[txen_pin] != RADIOLIB_NC) { + pinMode(settingsMap[txen_pin], OUTPUT); + digitalWrite(settingsMap[txen_pin], LOW); // Set low before becoming an output } #else #if defined(SX128X_RXEN) && (SX128X_RXEN != RADIOLIB_NC) // set not rx or tx mode @@ -93,8 +93,8 @@ template bool SX128xInterface::init() lora.setRfSwitchPins(SX128X_RXEN, SX128X_TXEN); } #elif ARCH_PORTDUINO - if (res == RADIOLIB_ERR_NONE && settingsMap[rxen] != RADIOLIB_NC && settingsMap[txen] != RADIOLIB_NC) { - lora.setRfSwitchPins(settingsMap[rxen], settingsMap[txen]); + if (res == RADIOLIB_ERR_NONE && settingsMap[rxen_pin] != RADIOLIB_NC && settingsMap[txen_pin] != RADIOLIB_NC) { + lora.setRfSwitchPins(settingsMap[rxen_pin], settingsMap[txen_pin]); } #endif @@ -174,11 +174,11 @@ template void SX128xInterface::setStandby() LOG_ERROR("SX128x standby %s%d", radioLibErr, err); assert(err == RADIOLIB_ERR_NONE); #if ARCH_PORTDUINO - if (settingsMap[rxen] != RADIOLIB_NC) { - digitalWrite(settingsMap[rxen], LOW); + if (settingsMap[rxen_pin] != RADIOLIB_NC) { + digitalWrite(settingsMap[rxen_pin], LOW); } - if (settingsMap[txen] != RADIOLIB_NC) { - digitalWrite(settingsMap[txen], LOW); + if (settingsMap[txen_pin] != RADIOLIB_NC) { + digitalWrite(settingsMap[txen_pin], LOW); } #else #if defined(SX128X_RXEN) && (SX128X_RXEN != RADIOLIB_NC) // we have RXEN/TXEN control - turn off RX and TX power @@ -210,11 +210,11 @@ template void SX128xInterface::addReceiveMetadata(meshtastic_Mes template void SX128xInterface::configHardwareForSend() { #if ARCH_PORTDUINO - if (settingsMap[txen] != RADIOLIB_NC) { - digitalWrite(settingsMap[txen], HIGH); + if (settingsMap[txen_pin] != RADIOLIB_NC) { + digitalWrite(settingsMap[txen_pin], HIGH); } - if (settingsMap[rxen] != RADIOLIB_NC) { - digitalWrite(settingsMap[rxen], LOW); + if (settingsMap[rxen_pin] != RADIOLIB_NC) { + digitalWrite(settingsMap[rxen_pin], LOW); } #else @@ -241,11 +241,11 @@ template void SX128xInterface::startReceive() setStandby(); #if ARCH_PORTDUINO - if (settingsMap[rxen] != RADIOLIB_NC) { - digitalWrite(settingsMap[rxen], HIGH); + if (settingsMap[rxen_pin] != RADIOLIB_NC) { + digitalWrite(settingsMap[rxen_pin], HIGH); } - if (settingsMap[txen] != RADIOLIB_NC) { - digitalWrite(settingsMap[txen], LOW); + if (settingsMap[txen_pin] != RADIOLIB_NC) { + digitalWrite(settingsMap[txen_pin], LOW); } #else @@ -315,4 +315,4 @@ template bool SX128xInterface::sleep() return true; } -#endif \ No newline at end of file +#endif diff --git a/src/platform/portduino/PortduinoGlue.cpp b/src/platform/portduino/PortduinoGlue.cpp index b042510f5..e75112235 100644 --- a/src/platform/portduino/PortduinoGlue.cpp +++ b/src/platform/portduino/PortduinoGlue.cpp @@ -134,13 +134,13 @@ void portduinoSetup() { printf("Set up Meshtastic on Portduino...\n"); int max_GPIO = 0; - const configNames GPIO_lines[] = {cs, - irq, - busy, - reset, - sx126x_ant_sw, - txen, - rxen, + const configNames GPIO_lines[] = {cs_pin, + irq_pin, + busy_pin, + reset_pin, + sx126x_ant_sw_pin, + txen_pin, + rxen_pin, displayDC, displayCS, displayBacklight, @@ -247,7 +247,7 @@ void portduinoSetup() // Rather important to set this, if not running simulated. randomSeed(time(NULL)); - gpioChipName += std::to_string(settingsMap[gpiochip]); + std::string defaultGpioChipName = gpioChipName + std::to_string(settingsMap[default_gpiochip]); for (configNames i : GPIO_lines) { if (settingsMap.count(i) && settingsMap[i] > max_GPIO) @@ -260,62 +260,46 @@ void portduinoSetup() // TODO: Can we do this in the for loop above? // TODO: If one of these fails, we should log and terminate if (settingsMap.count(user) > 0 && settingsMap[user] != RADIOLIB_NC) { - if (initGPIOPin(settingsMap[user], gpioChipName) != ERRNO_OK) { + if (initGPIOPin(settingsMap[user], defaultGpioChipName, settingsMap[user]) != ERRNO_OK) { settingsMap[user] = RADIOLIB_NC; } } if (settingsMap[displayPanel] != no_screen) { if (settingsMap[displayCS] > 0) - initGPIOPin(settingsMap[displayCS], gpioChipName); + initGPIOPin(settingsMap[displayCS], defaultGpioChipName, settingsMap[displayCS]); if (settingsMap[displayDC] > 0) - initGPIOPin(settingsMap[displayDC], gpioChipName); + initGPIOPin(settingsMap[displayDC], defaultGpioChipName, settingsMap[displayDC]); if (settingsMap[displayBacklight] > 0) - initGPIOPin(settingsMap[displayBacklight], gpioChipName); + initGPIOPin(settingsMap[displayBacklight], defaultGpioChipName, settingsMap[displayBacklight]); if (settingsMap[displayReset] > 0) - initGPIOPin(settingsMap[displayReset], gpioChipName); + initGPIOPin(settingsMap[displayReset], defaultGpioChipName, settingsMap[displayReset]); } if (settingsMap[touchscreenModule] != no_touchscreen) { if (settingsMap[touchscreenCS] > 0) - initGPIOPin(settingsMap[touchscreenCS], gpioChipName); + initGPIOPin(settingsMap[touchscreenCS], defaultGpioChipName, settingsMap[touchscreenCS]); if (settingsMap[touchscreenIRQ] > 0) - initGPIOPin(settingsMap[touchscreenIRQ], gpioChipName); + initGPIOPin(settingsMap[touchscreenIRQ], defaultGpioChipName, settingsMap[touchscreenIRQ]); } // Only initialize the radio pins when dealing with real, kernel controlled SPI hardware if (settingsStrings[spidev] != "" && settingsStrings[spidev] != "ch341") { - if (settingsMap.count(cs) > 0 && settingsMap[cs] != RADIOLIB_NC) { - if (initGPIOPin(settingsMap[cs], gpioChipName) != ERRNO_OK) { - settingsMap[cs] = RADIOLIB_NC; - } - } - if (settingsMap.count(irq) > 0 && settingsMap[irq] != RADIOLIB_NC) { - if (initGPIOPin(settingsMap[irq], gpioChipName) != ERRNO_OK) { - settingsMap[irq] = RADIOLIB_NC; - } - } - if (settingsMap.count(busy) > 0 && settingsMap[busy] != RADIOLIB_NC) { - if (initGPIOPin(settingsMap[busy], gpioChipName) != ERRNO_OK) { - settingsMap[busy] = RADIOLIB_NC; - } - } - if (settingsMap.count(reset) > 0 && settingsMap[reset] != RADIOLIB_NC) { - if (initGPIOPin(settingsMap[reset], gpioChipName) != ERRNO_OK) { - settingsMap[reset] = RADIOLIB_NC; - } - } - if (settingsMap.count(sx126x_ant_sw) > 0 && settingsMap[sx126x_ant_sw] != RADIOLIB_NC) { - if (initGPIOPin(settingsMap[sx126x_ant_sw], gpioChipName) != ERRNO_OK) { - settingsMap[sx126x_ant_sw] = RADIOLIB_NC; - } - } - if (settingsMap.count(rxen) > 0 && settingsMap[rxen] != RADIOLIB_NC) { - if (initGPIOPin(settingsMap[rxen], gpioChipName) != ERRNO_OK) { - settingsMap[rxen] = RADIOLIB_NC; - } - } - if (settingsMap.count(txen) > 0 && settingsMap[txen] != RADIOLIB_NC) { - if (initGPIOPin(settingsMap[txen], gpioChipName) != ERRNO_OK) { - settingsMap[txen] = RADIOLIB_NC; + const struct { configNames pin; configNames gpiochip; configNames line; } pinMappings[] = { + { cs_pin, cs_gpiochip, cs_line }, + { irq_pin, irq_gpiochip, irq_line }, + { busy_pin, busy_gpiochip, busy_line }, + { reset_pin, reset_gpiochip, reset_line }, + { rxen_pin, rxen_gpiochip, rxen_line }, + { txen_pin, txen_gpiochip, txen_line }, + { sx126x_ant_sw_pin, sx126x_ant_sw_gpiochip, sx126x_ant_sw_line } + }; + for (auto& pinMap : pinMappings) { + auto setMapIter = settingsMap.find(pinMap.pin); + if (setMapIter != settingsMap.end() && setMapIter->second != RADIOLIB_NC) { + if (initGPIOPin(setMapIter->second, gpioChipName + std::to_string(settingsMap[pinMap.gpiochip]), settingsMap[pinMap.line] ) != ERRNO_OK) { + settingsMap[pinMap.pin] = RADIOLIB_NC; + settingsMap[pinMap.gpiochip] = RADIOLIB_NC; + settingsMap[pinMap.line] = RADIOLIB_NC; + } } } SPI.begin(settingsStrings[spidev].c_str()); @@ -332,13 +316,13 @@ void portduinoSetup() return; } -int initGPIOPin(int pinNum, const std::string gpioChipName) +int initGPIOPin(int pinNum, const std::string gpioChipName, int line) { #ifdef PORTDUINO_LINUX_HARDWARE std::string gpio_name = "GPIO" + std::to_string(pinNum); try { GPIOPin *csPin; - csPin = new LinuxGPIOPin(pinNum, gpioChipName.c_str(), pinNum, gpio_name.c_str()); + csPin = new LinuxGPIOPin(pinNum, gpioChipName.c_str(), line, gpio_name.c_str()); csPin->setSilent(); gpioBind(csPin); return ERRNO_OK; @@ -376,42 +360,65 @@ bool loadConfig(const char *configPath) } } if (yamlConfig["Lora"]) { - settingsMap[use_sx1262] = false; - settingsMap[use_rf95] = false; - settingsMap[use_sx1280] = false; - settingsMap[use_lr1110] = false; - settingsMap[use_lr1120] = false; - settingsMap[use_lr1121] = 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("") == "lr1110") { - settingsMap[use_lr1110] = true; - } else if (yamlConfig["Lora"]["Module"] && yamlConfig["Lora"]["Module"].as("") == "lr1120") { - settingsMap[use_lr1120] = true; - } else if (yamlConfig["Lora"]["Module"] && yamlConfig["Lora"]["Module"].as("") == "lr1121") { - settingsMap[use_lr1121] = true; - } else if (yamlConfig["Lora"]["Module"] && yamlConfig["Lora"]["Module"].as("") == "sx1268") { - settingsMap[use_sx1268] = true; + const struct { configNames cfgName; std::string strName; } loraModules[] = { + { use_rf95, "RF95" }, + { use_sx1262, "sx1262" }, + { use_sx1268, "sx1268" }, + { use_sx1280, "sx1280" }, + { use_lr1110, "lr1110" }, + { use_lr1120, "lr1120" }, + { use_lr1121, "lr1121" }, + { use_llcc68, "LLCC68" } + }; + for (auto& loraModule : loraModules) { + settingsMap[loraModule.cfgName] = false; } + if (yamlConfig["Lora"]["Module"]) { + for (auto& loraModule : loraModules) { + if (yamlConfig["Lora"]["Module"].as("") == loraModule.strName) { + settingsMap[loraModule.cfgName] = true; + break; + } + } + } + settingsMap[dio2_as_rf_switch] = yamlConfig["Lora"]["DIO2_AS_RF_SWITCH"].as(false); settingsMap[dio3_tcxo_voltage] = yamlConfig["Lora"]["DIO3_TCXO_VOLTAGE"].as(0) * 1000; if (settingsMap[dio3_tcxo_voltage] == 0 && yamlConfig["Lora"]["DIO3_TCXO_VOLTAGE"].as(false)) { settingsMap[dio3_tcxo_voltage] = 1800; // default millivolts for "true" } - 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); + + // backwards API compatibility and to globally set gpiochip once + int defaultGpioChip = settingsMap[default_gpiochip] = yamlConfig["Lora"]["gpiochip"].as(0); + + const struct { configNames pin; + configNames gpiochip; + configNames line; + std::string strName; } pinMappings[] = { + { cs_pin, cs_gpiochip, cs_line, "CS" }, + { irq_pin, irq_gpiochip, irq_line, "IRQ" }, + { busy_pin, busy_gpiochip, busy_line, "Busy" }, + { reset_pin, reset_gpiochip, reset_line, "Reset" }, + { txen_pin, txen_gpiochip, txen_line, "TXen" }, + { rxen_pin, rxen_gpiochip, rxen_line, "RXen" }, + { sx126x_ant_sw_pin, sx126x_ant_sw_gpiochip, sx126x_ant_sw_line, "SX126X_ANT_SW" }, + }; + for (auto& pinMap : pinMappings) { + if (yamlConfig["Lora"][pinMap.strName].IsMap() + && (yamlConfig["Lora"][pinMap.strName]["pin"] + || yamlConfig["Lora"][pinMap.strName]["line"] + || yamlConfig["Lora"][pinMap.strName]["gpiochip"])) { + settingsMap[pinMap.pin] = yamlConfig["Lora"][pinMap.strName]["pin"].as(RADIOLIB_NC); + settingsMap[pinMap.line] = yamlConfig["Lora"][pinMap.strName]["line"].as(settingsMap[pinMap.pin]); + settingsMap[pinMap.gpiochip] = yamlConfig["Lora"][pinMap.strName]["gpiochip"].as(defaultGpioChip); + } + else { // backwards API compatibility + settingsMap[pinMap.pin] = yamlConfig["Lora"][pinMap.strName].as(RADIOLIB_NC); + settingsMap[pinMap.line] = settingsMap[pinMap.pin]; + settingsMap[pinMap.gpiochip] = defaultGpioChip; + } + } + settingsMap[spiSpeed] = yamlConfig["Lora"]["spiSpeed"].as(2000000); settingsStrings[lora_usb_serial_num] = yamlConfig["Lora"]["USB_Serialnum"].as(""); settingsMap[lora_usb_pid] = yamlConfig["Lora"]["USB_PID"].as(0x5512); diff --git a/src/platform/portduino/PortduinoGlue.h b/src/platform/portduino/PortduinoGlue.h index 5bc07df6a..d1e91956d 100644 --- a/src/platform/portduino/PortduinoGlue.h +++ b/src/platform/portduino/PortduinoGlue.h @@ -5,27 +5,42 @@ #include "platform/portduino/USBHal.h" enum configNames { - use_sx1262, - cs, - irq, - busy, - reset, - sx126x_ant_sw, - txen, - rxen, + default_gpiochip, + cs_pin, + cs_line, + cs_gpiochip, + irq_pin, + irq_line, + irq_gpiochip, + busy_pin, + busy_line, + busy_gpiochip, + reset_pin, + reset_line, + reset_gpiochip, + txen_pin, + txen_line, + txen_gpiochip, + rxen_pin, + rxen_line, + rxen_gpiochip, + sx126x_ant_sw_pin, + sx126x_ant_sw_line, + sx126x_ant_sw_gpiochip, dio2_as_rf_switch, dio3_tcxo_voltage, use_rf95, + use_sx1262, + use_sx1268, use_sx1280, use_lr1110, use_lr1120, use_lr1121, - use_sx1268, + use_llcc68, lora_usb_serial_num, lora_usb_pid, lora_usb_vid, user, - gpiochip, spidev, spiSpeed, i2cdev, @@ -75,7 +90,7 @@ extern std::map settingsMap; extern std::map settingsStrings; extern std::ofstream traceFile; extern Ch341Hal *ch341Hal; -int initGPIOPin(int pinNum, std::string gpioChipname); +int initGPIOPin(int pinNum, std::string gpioChipname, int line); bool loadConfig(const char *configPath); static bool ends_with(std::string_view str, std::string_view suffix); void getMacAddr(uint8_t *dmac); From 6b1c01ce02e57788873a9f36197e46885b8f11fc Mon Sep 17 00:00:00 2001 From: Jonathan Bennett Date: Sun, 12 Jan 2025 15:10:50 -0600 Subject: [PATCH 18/89] Trunk n stuff (#5833) * Trunk * Allow new gpio syntax with defaults * Exit on pin init failure --- src/main.cpp | 67 +++++++------- src/mesh/SX126xInterface.cpp | 3 +- src/platform/portduino/PortduinoGlue.cpp | 106 ++++++++++------------- 3 files changed, 78 insertions(+), 98 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index 338fca5c1..fc9d24e37 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -826,43 +826,37 @@ void setup() #endif #ifdef ARCH_PORTDUINO - const struct { configNames cfgName; - std::string strName; - } loraModules[] = { - { use_rf95, "RF95" }, - { use_sx1262, "sx1262" }, - { use_sx1268, "sx1268" }, - { use_sx1280, "sx1280" }, - { use_lr1110, "lr1110" }, - { use_lr1120, "lr1120" }, - { use_lr1121, "lr1121" }, - { use_llcc68, "LLCC68" } - }; + const struct { + configNames cfgName; + std::string strName; + } loraModules[] = {{use_rf95, "RF95"}, {use_sx1262, "sx1262"}, {use_sx1268, "sx1268"}, {use_sx1280, "sx1280"}, + {use_lr1110, "lr1110"}, {use_lr1120, "lr1120"}, {use_lr1121, "lr1121"}, {use_llcc68, "LLCC68"}}; // as one can't use a function pointer to the class constructor: - auto loraModuleInterface = [](configNames cfgName, LockingArduinoHal *hal, RADIOLIB_PIN_TYPE cs, RADIOLIB_PIN_TYPE irq, RADIOLIB_PIN_TYPE rst, RADIOLIB_PIN_TYPE busy) { - switch (cfgName) { - case use_rf95: - return (RadioInterface*)new RF95Interface(hal, cs, irq, rst, busy); - case use_sx1262: - return (RadioInterface*)new SX1262Interface(hal, cs, irq, rst, busy); - case use_sx1268: - return (RadioInterface*)new SX1268Interface(hal, cs, irq, rst, busy); - case use_sx1280: - return (RadioInterface*)new SX1280Interface(hal, cs, irq, rst, busy); - case use_lr1110: - return (RadioInterface*)new LR1110Interface(hal, cs, irq, rst, busy); - case use_lr1120: - return (RadioInterface*)new LR1120Interface(hal, cs, irq, rst, busy); - case use_lr1121: - return (RadioInterface*)new LR1121Interface(hal, cs, irq, rst, busy); - case use_llcc68: - return (RadioInterface*)new LLCC68Interface(hal, cs, irq, rst, busy); - default: - assert(0); // shouldn't happen - return (RadioInterface*)nullptr; - } + auto loraModuleInterface = [](configNames cfgName, LockingArduinoHal *hal, RADIOLIB_PIN_TYPE cs, RADIOLIB_PIN_TYPE irq, + RADIOLIB_PIN_TYPE rst, RADIOLIB_PIN_TYPE busy) { + switch (cfgName) { + case use_rf95: + return (RadioInterface *)new RF95Interface(hal, cs, irq, rst, busy); + case use_sx1262: + return (RadioInterface *)new SX1262Interface(hal, cs, irq, rst, busy); + case use_sx1268: + return (RadioInterface *)new SX1268Interface(hal, cs, irq, rst, busy); + case use_sx1280: + return (RadioInterface *)new SX1280Interface(hal, cs, irq, rst, busy); + case use_lr1110: + return (RadioInterface *)new LR1110Interface(hal, cs, irq, rst, busy); + case use_lr1120: + return (RadioInterface *)new LR1120Interface(hal, cs, irq, rst, busy); + case use_lr1121: + return (RadioInterface *)new LR1121Interface(hal, cs, irq, rst, busy); + case use_llcc68: + return (RadioInterface *)new LLCC68Interface(hal, cs, irq, rst, busy); + default: + assert(0); // shouldn't happen + return (RadioInterface *)nullptr; + } }; - for (auto& loraModule : loraModules) { + for (auto &loraModule : loraModules) { if (settingsMap[loraModule.cfgName] && !rIf) { LOG_DEBUG("Activate %s radio on SPI port %s", loraModule.strName.c_str(), settingsStrings[spidev].c_str()); if (settingsStrings[spidev] == "ch341") { @@ -870,7 +864,8 @@ void setup() } else { RadioLibHAL = new LockingArduinoHal(SPI, spiSettings); } - rIf = loraModuleInterface(loraModule.cfgName, (LockingArduinoHal *)RadioLibHAL, settingsMap[cs_pin], settingsMap[irq_pin], settingsMap[reset_pin], settingsMap[busy_pin]); + rIf = loraModuleInterface(loraModule.cfgName, (LockingArduinoHal *)RadioLibHAL, settingsMap[cs_pin], + settingsMap[irq_pin], settingsMap[reset_pin], settingsMap[busy_pin]); if (!rIf->init()) { LOG_WARN("No %s radio", loraModule.strName.c_str()); delete rIf; diff --git a/src/mesh/SX126xInterface.cpp b/src/mesh/SX126xInterface.cpp index b13bb6faf..8a7bc7670 100644 --- a/src/mesh/SX126xInterface.cpp +++ b/src/mesh/SX126xInterface.cpp @@ -121,7 +121,8 @@ template bool SX126xInterface::init() // no effect #if ARCH_PORTDUINO if (res == RADIOLIB_ERR_NONE) { - LOG_DEBUG("Use MCU pin %i as RXEN and pin %i as TXEN to control RF switching", settingsMap[rxen_pin], settingsMap[txen_pin]); + LOG_DEBUG("Use MCU pin %i as RXEN and pin %i as TXEN to control RF switching", settingsMap[rxen_pin], + settingsMap[txen_pin]); lora.setRfSwitchPins(settingsMap[rxen_pin], settingsMap[txen_pin]); } #else diff --git a/src/platform/portduino/PortduinoGlue.cpp b/src/platform/portduino/PortduinoGlue.cpp index e75112235..ce2418e86 100644 --- a/src/platform/portduino/PortduinoGlue.cpp +++ b/src/platform/portduino/PortduinoGlue.cpp @@ -134,21 +134,10 @@ void portduinoSetup() { printf("Set up Meshtastic on Portduino...\n"); int max_GPIO = 0; - const configNames GPIO_lines[] = {cs_pin, - irq_pin, - busy_pin, - reset_pin, - sx126x_ant_sw_pin, - txen_pin, - rxen_pin, - displayDC, - displayCS, - displayBacklight, - displayBacklightPWMChannel, - displayReset, - touchscreenCS, - touchscreenIRQ, - user}; + const configNames GPIO_lines[] = { + cs_pin, irq_pin, busy_pin, reset_pin, sx126x_ant_sw_pin, txen_pin, + rxen_pin, displayDC, displayCS, displayBacklight, displayBacklightPWMChannel, displayReset, + touchscreenCS, touchscreenIRQ, user}; std::string gpioChipName = "gpiochip"; settingsStrings[i2cdev] = ""; @@ -257,7 +246,6 @@ void portduinoSetup() gpioInit(max_GPIO + 1); // Done here so we can inform Portduino how many GPIOs we need. // Need to bind all the configured GPIO pins so they're not simulated - // TODO: Can we do this in the for loop above? // TODO: If one of these fails, we should log and terminate if (settingsMap.count(user) > 0 && settingsMap[user] != RADIOLIB_NC) { if (initGPIOPin(settingsMap[user], defaultGpioChipName, settingsMap[user]) != ERRNO_OK) { @@ -283,22 +271,25 @@ void portduinoSetup() // Only initialize the radio pins when dealing with real, kernel controlled SPI hardware if (settingsStrings[spidev] != "" && settingsStrings[spidev] != "ch341") { - const struct { configNames pin; configNames gpiochip; configNames line; } pinMappings[] = { - { cs_pin, cs_gpiochip, cs_line }, - { irq_pin, irq_gpiochip, irq_line }, - { busy_pin, busy_gpiochip, busy_line }, - { reset_pin, reset_gpiochip, reset_line }, - { rxen_pin, rxen_gpiochip, rxen_line }, - { txen_pin, txen_gpiochip, txen_line }, - { sx126x_ant_sw_pin, sx126x_ant_sw_gpiochip, sx126x_ant_sw_line } - }; - for (auto& pinMap : pinMappings) { + const struct { + configNames pin; + configNames gpiochip; + configNames line; + } pinMappings[] = {{cs_pin, cs_gpiochip, cs_line}, + {irq_pin, irq_gpiochip, irq_line}, + {busy_pin, busy_gpiochip, busy_line}, + {reset_pin, reset_gpiochip, reset_line}, + {rxen_pin, rxen_gpiochip, rxen_line}, + {txen_pin, txen_gpiochip, txen_line}, + {sx126x_ant_sw_pin, sx126x_ant_sw_gpiochip, sx126x_ant_sw_line}}; + for (auto &pinMap : pinMappings) { auto setMapIter = settingsMap.find(pinMap.pin); if (setMapIter != settingsMap.end() && setMapIter->second != RADIOLIB_NC) { - if (initGPIOPin(setMapIter->second, gpioChipName + std::to_string(settingsMap[pinMap.gpiochip]), settingsMap[pinMap.line] ) != ERRNO_OK) { - settingsMap[pinMap.pin] = RADIOLIB_NC; - settingsMap[pinMap.gpiochip] = RADIOLIB_NC; - settingsMap[pinMap.line] = RADIOLIB_NC; + if (initGPIOPin(setMapIter->second, gpioChipName + std::to_string(settingsMap[pinMap.gpiochip]), + settingsMap[pinMap.line]) != ERRNO_OK) { + printf("Error setting pin number %d. It may not exist, or may already be in use.\n", + settingsMap[pinMap.line]); + exit(EXIT_FAILURE); } } } @@ -360,21 +351,16 @@ bool loadConfig(const char *configPath) } } if (yamlConfig["Lora"]) { - const struct { configNames cfgName; std::string strName; } loraModules[] = { - { use_rf95, "RF95" }, - { use_sx1262, "sx1262" }, - { use_sx1268, "sx1268" }, - { use_sx1280, "sx1280" }, - { use_lr1110, "lr1110" }, - { use_lr1120, "lr1120" }, - { use_lr1121, "lr1121" }, - { use_llcc68, "LLCC68" } - }; - for (auto& loraModule : loraModules) { + const struct { + configNames cfgName; + std::string strName; + } loraModules[] = {{use_rf95, "RF95"}, {use_sx1262, "sx1262"}, {use_sx1268, "sx1268"}, {use_sx1280, "sx1280"}, + {use_lr1110, "lr1110"}, {use_lr1120, "lr1120"}, {use_lr1121, "lr1121"}, {use_llcc68, "LLCC68"}}; + for (auto &loraModule : loraModules) { settingsMap[loraModule.cfgName] = false; } if (yamlConfig["Lora"]["Module"]) { - for (auto& loraModule : loraModules) { + for (auto &loraModule : loraModules) { if (yamlConfig["Lora"]["Module"].as("") == loraModule.strName) { settingsMap[loraModule.cfgName] = true; break; @@ -391,28 +377,26 @@ bool loadConfig(const char *configPath) // backwards API compatibility and to globally set gpiochip once int defaultGpioChip = settingsMap[default_gpiochip] = yamlConfig["Lora"]["gpiochip"].as(0); - const struct { configNames pin; - configNames gpiochip; - configNames line; - std::string strName; } pinMappings[] = { - { cs_pin, cs_gpiochip, cs_line, "CS" }, - { irq_pin, irq_gpiochip, irq_line, "IRQ" }, - { busy_pin, busy_gpiochip, busy_line, "Busy" }, - { reset_pin, reset_gpiochip, reset_line, "Reset" }, - { txen_pin, txen_gpiochip, txen_line, "TXen" }, - { rxen_pin, rxen_gpiochip, rxen_line, "RXen" }, - { sx126x_ant_sw_pin, sx126x_ant_sw_gpiochip, sx126x_ant_sw_line, "SX126X_ANT_SW" }, + const struct { + configNames pin; + configNames gpiochip; + configNames line; + std::string strName; + } pinMappings[] = { + {cs_pin, cs_gpiochip, cs_line, "CS"}, + {irq_pin, irq_gpiochip, irq_line, "IRQ"}, + {busy_pin, busy_gpiochip, busy_line, "Busy"}, + {reset_pin, reset_gpiochip, reset_line, "Reset"}, + {txen_pin, txen_gpiochip, txen_line, "TXen"}, + {rxen_pin, rxen_gpiochip, rxen_line, "RXen"}, + {sx126x_ant_sw_pin, sx126x_ant_sw_gpiochip, sx126x_ant_sw_line, "SX126X_ANT_SW"}, }; - for (auto& pinMap : pinMappings) { - if (yamlConfig["Lora"][pinMap.strName].IsMap() - && (yamlConfig["Lora"][pinMap.strName]["pin"] - || yamlConfig["Lora"][pinMap.strName]["line"] - || yamlConfig["Lora"][pinMap.strName]["gpiochip"])) { + for (auto &pinMap : pinMappings) { + if (yamlConfig["Lora"][pinMap.strName].IsMap()) { settingsMap[pinMap.pin] = yamlConfig["Lora"][pinMap.strName]["pin"].as(RADIOLIB_NC); settingsMap[pinMap.line] = yamlConfig["Lora"][pinMap.strName]["line"].as(settingsMap[pinMap.pin]); settingsMap[pinMap.gpiochip] = yamlConfig["Lora"][pinMap.strName]["gpiochip"].as(defaultGpioChip); - } - else { // backwards API compatibility + } else { // backwards API compatibility settingsMap[pinMap.pin] = yamlConfig["Lora"][pinMap.strName].as(RADIOLIB_NC); settingsMap[pinMap.line] = settingsMap[pinMap.pin]; settingsMap[pinMap.gpiochip] = defaultGpioChip; @@ -584,4 +568,4 @@ bool MAC_from_string(std::string mac_str, uint8_t *dmac) } else { return false; } -} +} \ No newline at end of file From e0f97c930648e8adf71af3a279c8ee79da1f2dca Mon Sep 17 00:00:00 2001 From: Austin Date: Sun, 12 Jan 2025 23:24:05 -0500 Subject: [PATCH 19/89] rpkg Fedora packaging (#5735) --- .github/workflows/hook_copr.yml | 73 +++++++++++++++ ...ghtly_debian.yml => nightly_packaging.yml} | 12 ++- .github/workflows/release_channels.yml | 7 ++ bin/rpkg.macros | 12 +++ meshtasticd.spec.rpkg | 91 +++++++++++++++++++ rpkg.conf | 2 + 6 files changed, 195 insertions(+), 2 deletions(-) create mode 100644 .github/workflows/hook_copr.yml rename .github/workflows/{nightly_debian.yml => nightly_packaging.yml} (75%) create mode 100644 bin/rpkg.macros create mode 100644 meshtasticd.spec.rpkg create mode 100644 rpkg.conf diff --git a/.github/workflows/hook_copr.yml b/.github/workflows/hook_copr.yml new file mode 100644 index 000000000..c7b6b8d79 --- /dev/null +++ b/.github/workflows/hook_copr.yml @@ -0,0 +1,73 @@ +name: Trigger COPR build + +on: + workflow_call: + secrets: + COPR_HOOK_DAILY: + COPR_HOOK_ALPHA: + COPR_HOOK_BETA: + inputs: + copr_project: + description: COPR project to target + required: true + type: string + +permissions: read-all + +jobs: + build-copr-hook: + runs-on: ubuntu-24.04 + steps: + - name: Checkout code + uses: actions/checkout@v4 + with: + submodules: recursive + ref: ${{ github.ref }} + repository: ${{ github.repository }} + + - name: Install Python dependencies + run: | + pip install requests + + - name: Trigger COPR build + shell: python + run: | + import requests + + project_name = "${{ inputs.copr_project }}" + if project_name == "daily": + hook_secret = "${{ secrets.COPR_HOOK_DAILY }}" + project_id = 160277 + elif project_name == "alpha": + hook_secret = "${{ secrets.COPR_HOOK_ALPHA }}" + project_id = 160278 + elif project_name == "beta": + hook_secret = "${{ secrets.COPR_HOOK_BETA }}" + project_id = 160279 + else: + raise ValueError(f"Unknown COPR project: {project_name}") + + webhook_url = f"https://copr.fedorainfracloud.org/webhooks/github/{project_id}/{hook_secret}/meshtasticd/" + copr_payload = { + "event": "push", + "payload": { + "ref": "${{ github.ref }}", + "after": "${{ github.sha }}", + "repository": { + "id": "${{ github.repository_id }}", + "full_name": "${{ github.repository }}", + "git_url": "${{ github.repositoryUrl }}", + "owner": { + "name": "${{ github.repository_owner }}" + } + }, + "pusher": { + "name": "${{ github.actor }}" + }, + "sender": { + "login": "github-actions[bot]" + } + } + } + r = requests.post(webhook_url, json=copr_payload) + r.raise_for_status() diff --git a/.github/workflows/nightly_debian.yml b/.github/workflows/nightly_packaging.yml similarity index 75% rename from .github/workflows/nightly_debian.yml rename to .github/workflows/nightly_packaging.yml index aa001854e..e477fe194 100644 --- a/.github/workflows/nightly_debian.yml +++ b/.github/workflows/nightly_packaging.yml @@ -1,4 +1,4 @@ -name: Nightly Debian Packaging +name: Nightly Packaging on: schedule: - cron: 0 9 * * * @@ -8,10 +8,12 @@ on: - master paths: - debian/** - - .github/workflows/nightly_debian.yml + - "*.rpkg" + - .github/workflows/nightly_packaging.yml - .github/workflows/build_debian_src.yml - .github/workflows/package_ppa.yml - .github/workflows/package_obs.yml + - .github/workflows/hook_copr.yml permissions: contents: write @@ -35,3 +37,9 @@ jobs: obs_project: home:meshtastic:daily series: unstable secrets: inherit + + hook-copr: + uses: ./.github/workflows/hook_copr.yml + with: + copr_project: daily + secrets: inherit diff --git a/.github/workflows/release_channels.yml b/.github/workflows/release_channels.yml index 34673f0c7..8891826d2 100644 --- a/.github/workflows/release_channels.yml +++ b/.github/workflows/release_channels.yml @@ -27,3 +27,10 @@ jobs: series: |- ${{ contains(github.event.release.name, 'Beta') && 'beta' || contains(github.event.release.name, 'Alpha') && 'alpha' }} secrets: inherit + + # hook-copr: + # uses: ./.github/workflows/hook_copr.yml + # with: + # copr_project: |- + # ${{ contains(github.event.release.name, 'Beta') && 'beta' || contains(github.event.release.name, 'Alpha') && 'alpha' }} + # secrets: inherit diff --git a/bin/rpkg.macros b/bin/rpkg.macros new file mode 100644 index 000000000..2bbb203de --- /dev/null +++ b/bin/rpkg.macros @@ -0,0 +1,12 @@ +function meshtastic_version { + meshtastic_version=$(python3 bin/buildinfo.py short) + echo -n "$meshtastic_version" +} +function git_commits_num { + total_commits=$(git rev-list --all --count) + echo -n "$total_commits" +} +function git_commit_sha { + commit_sha=$(git rev-parse --short HEAD) + echo -n "$commit_sha" +} \ No newline at end of file diff --git a/meshtasticd.spec.rpkg b/meshtasticd.spec.rpkg new file mode 100644 index 000000000..1819897b0 --- /dev/null +++ b/meshtasticd.spec.rpkg @@ -0,0 +1,91 @@ +# meshtasticd spec file for RPM-based distributions +# +# Build locally with: +# ``` +# sudo dnf install rpkg-util +# rpkg local +# ``` +# +# See: +# - https://docs.pagure.org/rpkg-util/v3/index.html +# - https://docs.fedoraproject.org/en-US/packaging-guidelines/Versioning/ + +Name: meshtasticd +# Version Ex: 2.5.19 +Version: {{{ meshtastic_version }}} +# Release Ex: 9127.daily.gitd7f5f620.fc41 +Release: {{{ git_commits_num }}}%{?copr_projectname:.%{copr_projectname}}.git{{{ git_commit_sha }}}%{?dist} +VCS: {{{ git_dir_vcs }}} +Summary: Meshtastic daemon for communicating with Meshtastic devices + +License: GPL-3.0 +URL: https://github.com/meshtastic/firmware +Source0: {{{ git_dir_pack }}} +Source1: https://github.com/meshtastic/web/releases/download/latest/build.tar + +BuildRequires: systemd-rpm-macros +BuildRequires: python3-devel +BuildRequires: platformio +BuildRequires: python3dist(protobuf) +BuildRequires: python3dist(grpcio[protobuf]) +BuildRequires: python3dist(grpcio-tools) +BuildRequires: git-core +BuildRequires: gcc-c++ +BuildRequires: pkgconfig(yaml-cpp) +BuildRequires: pkgconfig(libgpiod) +BuildRequires: pkgconfig(bluez) +BuildRequires: pkgconfig(libusb-1.0) +BuildRequires: libi2c-devel +# Web components: +BuildRequires: pkgconfig(openssl) +BuildRequires: pkgconfig(liborcania) +BuildRequires: pkgconfig(libyder) +BuildRequires: pkgconfig(libulfius) + +%description +Meshtastic daemon for controlling Meshtastic devices. Meshtastic is an off-grid +text communication platform that uses inexpensive LoRa radios. + +%prep +{{{ git_dir_setup_macro }}} +# Unpack the web files +mkdir -p web +tar -xf %{SOURCE1} -C web +gzip -dr web + +%build +# Use the “native” environment from platformio to build a Linux binary +platformio run -e native + +%install +mkdir -p %{buildroot}%{_sbindir} +install -m 0755 .pio/build/native/program %{buildroot}%{_sbindir}/meshtasticd + +mkdir -p %{buildroot}%{_sysconfdir}/meshtasticd +install -m 0644 bin/config-dist.yaml %{buildroot}%{_sysconfdir}/meshtasticd/config.yaml +mkdir -p %{buildroot}%{_sysconfdir}/meshtasticd/config.d +mkdir -p %{buildroot}%{_sysconfdir}/meshtasticd/available.d +cp -r bin/config.d/* %{buildroot}%{_sysconfdir}/meshtasticd/available.d + +install -D -m 0644 bin/meshtasticd.service %{buildroot}%{_unitdir}/meshtasticd.service + +# Install the web files under /usr/share/meshtasticd/web +mkdir -p %{buildroot}%{_datadir}/meshtasticd/web +cp -r web/* %{buildroot}%{_datadir}/meshtasticd/web + +%files +%license LICENSE +%doc README.md +%{_sbindir}/meshtasticd +%dir %{_sysconfdir}/meshtasticd +%dir %{_sysconfdir}/meshtasticd/config.d +%dir %{_sysconfdir}/meshtasticd/available.d +%config(noreplace) %{_sysconfdir}/meshtasticd/config.yaml +%config %{_sysconfdir}/meshtasticd/available.d/* +%{_unitdir}/meshtasticd.service +%dir %{_datadir}/meshtasticd +%dir %{_datadir}/meshtasticd/web +%{_datadir}/meshtasticd/web/* + +%changelog +%autochangelog \ No newline at end of file diff --git a/rpkg.conf b/rpkg.conf new file mode 100644 index 000000000..f574f9a13 --- /dev/null +++ b/rpkg.conf @@ -0,0 +1,2 @@ +[rpkg] +user_macros = "${git_props:root}/bin/rpkg.macros" From 4dc8d6e4006c485fa7d5fb7b658c8e5d5f23b49d Mon Sep 17 00:00:00 2001 From: Austin Date: Mon, 13 Jan 2025 00:20:22 -0500 Subject: [PATCH 20/89] Fix daily packaging perms (#5836) --- .../workflows/{nightly_packaging.yml => daily_packaging.yml} | 2 +- .github/workflows/hook_copr.yml | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) rename .github/workflows/{nightly_packaging.yml => daily_packaging.yml} (97%) diff --git a/.github/workflows/nightly_packaging.yml b/.github/workflows/daily_packaging.yml similarity index 97% rename from .github/workflows/nightly_packaging.yml rename to .github/workflows/daily_packaging.yml index e477fe194..14daae74d 100644 --- a/.github/workflows/nightly_packaging.yml +++ b/.github/workflows/daily_packaging.yml @@ -1,4 +1,4 @@ -name: Nightly Packaging +name: Daily Packaging on: schedule: - cron: 0 9 * * * diff --git a/.github/workflows/hook_copr.yml b/.github/workflows/hook_copr.yml index c7b6b8d79..94d9d095f 100644 --- a/.github/workflows/hook_copr.yml +++ b/.github/workflows/hook_copr.yml @@ -12,7 +12,9 @@ on: required: true type: string -permissions: read-all +permissions: + contents: write + packages: write jobs: build-copr-hook: From 89a9e0b99d579df9b97f87a35ba33653f496636e Mon Sep 17 00:00:00 2001 From: isseysandei Date: Mon, 13 Jan 2025 09:39:01 +0100 Subject: [PATCH 21/89] Added illuminance sensors to the node's environmental sensor page (#5832) * illuminance sensors added * added white_lux to debug --- src/modules/Telemetry/EnvironmentTelemetry.cpp | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/src/modules/Telemetry/EnvironmentTelemetry.cpp b/src/modules/Telemetry/EnvironmentTelemetry.cpp index e72b03bd4..be5df6f63 100644 --- a/src/modules/Telemetry/EnvironmentTelemetry.cpp +++ b/src/modules/Telemetry/EnvironmentTelemetry.cpp @@ -253,6 +253,16 @@ void EnvironmentTelemetryModule::drawFrame(OLEDDisplay *display, OLEDDisplayUiSt display->drawString(x, y += _fontHeight(FONT_SMALL), "IAQ: " + String(lastMeasurement.variant.environment_metrics.iaq)); } + if (lastMeasurement.variant.environment_metrics.lux != 0) { + display->drawString(x, y += _fontHeight(FONT_SMALL), + "Illuminance: " + String(lastMeasurement.variant.environment_metrics.lux, 2) + "lx"); + } + + if (lastMeasurement.variant.environment_metrics.white_lux != 0) { + display->drawString(x, y += _fontHeight(FONT_SMALL), + "W_Lux: " + String(lastMeasurement.variant.environment_metrics.white_lux, 2) + "lx"); + } + if (lastMeasurement.variant.environment_metrics.distance != 0) display->drawString(x, y += _fontHeight(FONT_SMALL), "Water Level: " + String(lastMeasurement.variant.environment_metrics.distance, 0) + "mm"); @@ -277,8 +287,10 @@ bool EnvironmentTelemetryModule::handleReceivedProtobuf(const meshtastic_MeshPac 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", 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): voltage=%f, IAQ=%d, distance=%f, lux=%f, white_lux=%f", sender, + t->variant.environment_metrics.voltage, t->variant.environment_metrics.iaq, + t->variant.environment_metrics.distance, t->variant.environment_metrics.lux, + t->variant.environment_metrics.white_lux); 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, From 1c0f43c8e2fb4cbbc285eeac319d339ab63c866f Mon Sep 17 00:00:00 2001 From: Ben Meadors Date: Mon, 13 Jan 2025 06:28:18 -0600 Subject: [PATCH 22/89] NRF52 SafeFile should not remove / rename files (#5840) --- src/SafeFile.cpp | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/SafeFile.cpp b/src/SafeFile.cpp index c5d7b335e..825ba3028 100644 --- a/src/SafeFile.cpp +++ b/src/SafeFile.cpp @@ -6,6 +6,11 @@ static File openFile(const char *filename, bool fullAtomic) { concurrency::LockGuard g(spiLock); + LOG_DEBUG("Opening %s, fullAtomic=%d", filename, fullAtomic); +#ifdef ARCH_NRF52 + lfs_assert_failed = false; + return FSCom.open(filename, FILE_O_WRITE); +#endif if (!fullAtomic) FSCom.remove(filename); // Nuke the old file to make space (ignore if it !exists) @@ -14,7 +19,6 @@ static File openFile(const char *filename, bool fullAtomic) // clear any previous LFS errors lfs_assert_failed = false; - return FSCom.open(filenameTmp.c_str(), FILE_O_WRITE); } @@ -57,6 +61,10 @@ bool SafeFile::close() spiLock->lock(); f.close(); spiLock->unlock(); + +#ifdef ARCH_NRF52 + return true; +#endif if (!testReadback()) return false; From 6366633cd402a902b496959df9784cfbbb68c7a2 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 13 Jan 2025 07:05:59 -0600 Subject: [PATCH 23/89] [create-pull-request] automated change (#5841) Co-authored-by: thebentern <9000580+thebentern@users.noreply.github.com> --- protobufs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/protobufs b/protobufs index c55f120a9..76f806e1b 160000 --- a/protobufs +++ b/protobufs @@ -1 +1 @@ -Subproject commit c55f120a9c1ce90c85e4826907a0b9bcb2d5f5a2 +Subproject commit 76f806e1bb1e2a7b157a14fadd095775f63db5e4 From e2dd845051cf40003a8c4102a8d7dad90dd4d3d2 Mon Sep 17 00:00:00 2001 From: Ben Meadors Date: Mon, 13 Jan 2025 08:30:20 -0600 Subject: [PATCH 24/89] Fix devicestate protobuf / filesize allocation (#5835) --- src/mesh/NodeDB.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/mesh/NodeDB.cpp b/src/mesh/NodeDB.cpp index 8e084c99d..a9e5565ef 100644 --- a/src/mesh/NodeDB.cpp +++ b/src/mesh/NodeDB.cpp @@ -958,7 +958,7 @@ void NodeDB::loadFromDisk() #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_FS * sizeof(meshtastic_NodeInfo), + auto state = loadProto(prefFileName, sizeof(meshtastic_DeviceState) + MAX_NUM_NODES_FS * meshtastic_NodeInfoLite_size, sizeof(meshtastic_DeviceState), &meshtastic_DeviceState_msg, &devicestate); // See https://github.com/meshtastic/firmware/issues/4184#issuecomment-2269390786 @@ -1147,8 +1147,9 @@ bool NodeDB::saveDeviceStateToDisk() #endif // Note: if MAX_NUM_NODES=100 and meshtastic_NodeInfoLite_size=166, so will be approximately 17KB // Because so huge we _must_ not use fullAtomic, because the filesystem is probably too small to hold two copies of this - return saveProto(prefFileName, sizeof(devicestate) + numMeshNodes * meshtastic_NodeInfoLite_size, &meshtastic_DeviceState_msg, - &devicestate, false); + size_t deviceStateSize; + pb_get_encoded_size(&deviceStateSize, meshtastic_DeviceState_fields, &devicestate); + return saveProto(prefFileName, deviceStateSize, &meshtastic_DeviceState_msg, &devicestate, false); } bool NodeDB::saveToDiskNoRetry(int saveWhat) From de42d96adf41a36ce4aa509715fb101dc1736376 Mon Sep 17 00:00:00 2001 From: Austin Date: Mon, 13 Jan 2025 11:46:07 -0500 Subject: [PATCH 25/89] Actions: Fix issues with new Release process (#5845) --- .github/workflows/main_matrix.yml | 31 +++++++++++++++++-------------- 1 file changed, 17 insertions(+), 14 deletions(-) diff --git a/.github/workflows/main_matrix.yml b/.github/workflows/main_matrix.yml index b55696301..66a398566 100644 --- a/.github/workflows/main_matrix.yml +++ b/.github/workflows/main_matrix.yml @@ -320,19 +320,23 @@ jobs: run: ls -lR - name: Add deb files to release - uses: softprops/action-gh-release@v2 - with: - tag_name: v${{ steps.version.outputs.long }} - files: | - ./output/meshtasticd_${{ steps.version.outputs.long }}_arm64.deb - ./output/meshtasticd_${{ steps.version.outputs.long }}_armhf.deb - ./output/meshtasticd_${{ steps.version.outputs.long }}_amd64.deb - ./output/meshtasticd-${{ steps.version.outputs.deb }}-src.zip + run: | + gh release upload v${{ steps.version.outputs.long }} ./output/meshtasticd_${{ steps.version.outputs.long }}_arm64.deb + gh release upload v${{ steps.version.outputs.long }} ./output/meshtasticd_${{ steps.version.outputs.long }}_armhf.deb + gh release upload v${{ steps.version.outputs.long }} ./output/meshtasticd_${{ steps.version.outputs.long }}_amd64.deb + gh release upload v${{ steps.version.outputs.long }} ./output/meshtasticd-${{ steps.version.outputs.deb }}-src.zip + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - name: Bump version.properties run: >- bin/bump_version.py + - name: Install debian tools for changelog + run: | + sudo apt-get update -y + sudo apt-get install -y devscripts + - name: Update debian changelog run: >- debian/ci_changelog.sh @@ -397,9 +401,8 @@ jobs: run: ls -lR - name: Add bins and debug elfs to release - uses: softprops/action-gh-release@v2 - with: - tag_name: v${{ steps.version.outputs.long }} - files: | - ./firmware-${{matrix.arch}}-${{ steps.version.outputs.long }}.zip - ./debug-elfs-${{matrix.arch}}-${{ steps.version.outputs.long }}.zip + run: | + gh release upload v${{ steps.version.outputs.long }} ./firmware-${{matrix.arch}}-${{ steps.version.outputs.long }}.zip + gh release upload v${{ steps.version.outputs.long }} ./debug-elfs-${{matrix.arch}}-${{ steps.version.outputs.long }}.zip + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} From d5cd6f87a02e63dd6ab6f5c851107912a2cf5a9f Mon Sep 17 00:00:00 2001 From: Ben Meadors Date: Mon, 13 Jan 2025 12:15:27 -0600 Subject: [PATCH 26/89] Kablammo --- .github/workflows/main_matrix.yml | 5 ----- 1 file changed, 5 deletions(-) diff --git a/.github/workflows/main_matrix.yml b/.github/workflows/main_matrix.yml index 66a398566..0a0ea9954 100644 --- a/.github/workflows/main_matrix.yml +++ b/.github/workflows/main_matrix.yml @@ -332,11 +332,6 @@ jobs: run: >- bin/bump_version.py - - name: Install debian tools for changelog - run: | - sudo apt-get update -y - sudo apt-get install -y devscripts - - name: Update debian changelog run: >- debian/ci_changelog.sh From 038430db2336bdea1ec9093dd539943d6a56a870 Mon Sep 17 00:00:00 2001 From: isseysandei Date: Tue, 14 Jan 2025 02:00:00 +0100 Subject: [PATCH 27/89] Environmental sensors page scrolling (#5847) * env scrolling first iteration * ran trunk --- .../Telemetry/EnvironmentTelemetry.cpp | 77 +++++++++++++------ 1 file changed, 53 insertions(+), 24 deletions(-) diff --git a/src/modules/Telemetry/EnvironmentTelemetry.cpp b/src/modules/Telemetry/EnvironmentTelemetry.cpp index be5df6f63..2fc9bab0b 100644 --- a/src/modules/Telemetry/EnvironmentTelemetry.cpp +++ b/src/modules/Telemetry/EnvironmentTelemetry.cpp @@ -222,7 +222,11 @@ void EnvironmentTelemetryModule::drawFrame(OLEDDisplay *display, OLEDDisplayUiSt } // Display "Env. From: ..." on its own - display->drawString(x, y, "Env. From: " + String(lastSender) + "(" + String(agoSecs) + "s)"); + display->drawString(x, y, "Env. From: " + String(lastSender) + " (" + String(agoSecs) + "s)"); + + // Prepare sensor data strings + String sensorData[10]; + int sensorCount = 0; if (lastMeasurement.variant.environment_metrics.has_temperature || lastMeasurement.variant.environment_metrics.has_relative_humidity) { @@ -232,48 +236,73 @@ void EnvironmentTelemetryModule::drawFrame(OLEDDisplay *display, OLEDDisplayUiSt 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) + "%"); + sensorData[sensorCount++] = + "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), - "Press: " + String(lastMeasurement.variant.environment_metrics.barometric_pressure, 0) + "hPA"); + sensorData[sensorCount++] = + "Press: " + String(lastMeasurement.variant.environment_metrics.barometric_pressure, 0) + "hPA"; } if (lastMeasurement.variant.environment_metrics.voltage != 0) { - display->drawString(x, y += _fontHeight(FONT_SMALL), - "Volt/Cur: " + String(lastMeasurement.variant.environment_metrics.voltage, 0) + "V / " + - String(lastMeasurement.variant.environment_metrics.current, 0) + "mA"); + sensorData[sensorCount++] = "Volt/Cur: " + String(lastMeasurement.variant.environment_metrics.voltage, 0) + "V / " + + String(lastMeasurement.variant.environment_metrics.current, 0) + "mA"; } if (lastMeasurement.variant.environment_metrics.iaq != 0) { - display->drawString(x, y += _fontHeight(FONT_SMALL), "IAQ: " + String(lastMeasurement.variant.environment_metrics.iaq)); + sensorData[sensorCount++] = "IAQ: " + String(lastMeasurement.variant.environment_metrics.iaq); + } + + if (lastMeasurement.variant.environment_metrics.distance != 0) { + sensorData[sensorCount++] = "Water Level: " + String(lastMeasurement.variant.environment_metrics.distance, 0) + "mm"; + } + + if (lastMeasurement.variant.environment_metrics.weight != 0) { + sensorData[sensorCount++] = "Weight: " + String(lastMeasurement.variant.environment_metrics.weight, 0) + "kg"; + } + + if (lastMeasurement.variant.environment_metrics.radiation != 0) { + sensorData[sensorCount++] = "Rad: " + String(lastMeasurement.variant.environment_metrics.radiation, 2) + "µR/h"; } if (lastMeasurement.variant.environment_metrics.lux != 0) { - display->drawString(x, y += _fontHeight(FONT_SMALL), - "Illuminance: " + String(lastMeasurement.variant.environment_metrics.lux, 2) + "lx"); + sensorData[sensorCount++] = "Illuminance: " + String(lastMeasurement.variant.environment_metrics.lux, 2) + "lx"; } if (lastMeasurement.variant.environment_metrics.white_lux != 0) { - display->drawString(x, y += _fontHeight(FONT_SMALL), - "W_Lux: " + String(lastMeasurement.variant.environment_metrics.white_lux, 2) + "lx"); + sensorData[sensorCount++] = "W_Lux: " + String(lastMeasurement.variant.environment_metrics.white_lux, 2) + "lx"; } - if (lastMeasurement.variant.environment_metrics.distance != 0) - display->drawString(x, y += _fontHeight(FONT_SMALL), - "Water Level: " + String(lastMeasurement.variant.environment_metrics.distance, 0) + "mm"); + static int scrollOffset = 0; + static bool scrollingDown = true; + static uint32_t lastScrollTime = millis(); - if (lastMeasurement.variant.environment_metrics.weight != 0) - display->drawString(x, y += _fontHeight(FONT_SMALL), - "Weight: " + String(lastMeasurement.variant.environment_metrics.weight, 0) + "kg"); + // Draw up to 3 sensor data lines + int linesToShow = min(3, sensorCount); + for (int i = 0; i < linesToShow; i++) { + int index = (scrollOffset + i) % sensorCount; + display->drawString(x, y += _fontHeight(FONT_SMALL), sensorData[index]); + } - if (lastMeasurement.variant.environment_metrics.radiation != 0) - display->drawString(x, y += _fontHeight(FONT_SMALL), - "Rad: " + String(lastMeasurement.variant.environment_metrics.radiation, 2) + "µR/h"); + // Only scroll if there are more than 3 sensor data lines + if (sensorCount > 3) { + // Update scroll offset every 5 seconds + if (millis() - lastScrollTime > 5000) { + if (scrollingDown) { + scrollOffset++; + if (scrollOffset + linesToShow >= sensorCount) { + scrollingDown = false; + } + } else { + scrollOffset--; + if (scrollOffset <= 0) { + scrollingDown = true; + } + } + lastScrollTime = millis(); + } + } } bool EnvironmentTelemetryModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp, meshtastic_Telemetry *t) From 729c39fb8621e0b76f4178fee88cc1dfa2711d06 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 13 Jan 2025 19:18:34 -0600 Subject: [PATCH 28/89] [create-pull-request] automated change (#5849) Co-authored-by: thebentern <9000580+thebentern@users.noreply.github.com> --- debian/changelog | 5 +++-- version.properties | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/debian/changelog b/debian/changelog index 035d5f034..8b1799c52 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,5 +1,6 @@ -meshtasticd (2.5.19.0) UNRELEASED; urgency=medium +meshtasticd (2.5.20.0) UNRELEASED; urgency=medium * Initial packaging + * GitHub Actions Automatic version bump - -- Austin Lane Thu, 02 Jan 2025 12:00:00 +0000 \ No newline at end of file + -- Austin Lane Mon, 13 Jan 2025 19:24:14 +0000 diff --git a/version.properties b/version.properties index 800529e39..4312ae59a 100644 --- a/version.properties +++ b/version.properties @@ -1,4 +1,4 @@ [VERSION] major = 2 minor = 5 -build = 19 +build = 20 From dd9ab7f0e1f74c2cdb33d1fe3d044cebab56cad0 Mon Sep 17 00:00:00 2001 From: Austin Date: Tue, 14 Jan 2025 02:17:54 -0500 Subject: [PATCH 29/89] Small Fix: Release_Channels permissions (#5852) --- .github/workflows/hook_copr.yml | 38 ++++++++------------------ .github/workflows/release_channels.yml | 4 ++- 2 files changed, 15 insertions(+), 27 deletions(-) diff --git a/.github/workflows/hook_copr.yml b/.github/workflows/hook_copr.yml index 94d9d095f..c30038d6b 100644 --- a/.github/workflows/hook_copr.yml +++ b/.github/workflows/hook_copr.yml @@ -38,38 +38,24 @@ jobs: project_name = "${{ inputs.copr_project }}" if project_name == "daily": - hook_secret = "${{ secrets.COPR_HOOK_DAILY }}" - project_id = 160277 + hook_secret = "${{ secrets.COPR_HOOK_DAILY }}" + project_id = 160277 elif project_name == "alpha": - hook_secret = "${{ secrets.COPR_HOOK_ALPHA }}" - project_id = 160278 + hook_secret = "${{ secrets.COPR_HOOK_ALPHA }}" + project_id = 160278 elif project_name == "beta": - hook_secret = "${{ secrets.COPR_HOOK_BETA }}" - project_id = 160279 + hook_secret = "${{ secrets.COPR_HOOK_BETA }}" + project_id = 160279 else: - raise ValueError(f"Unknown COPR project: {project_name}") + raise ValueError(f"Unknown COPR project: {project_name}") webhook_url = f"https://copr.fedorainfracloud.org/webhooks/github/{project_id}/{hook_secret}/meshtasticd/" copr_payload = { - "event": "push", - "payload": { - "ref": "${{ github.ref }}", - "after": "${{ github.sha }}", - "repository": { - "id": "${{ github.repository_id }}", - "full_name": "${{ github.repository }}", - "git_url": "${{ github.repositoryUrl }}", - "owner": { - "name": "${{ github.repository_owner }}" - } - }, - "pusher": { - "name": "${{ github.actor }}" - }, - "sender": { - "login": "github-actions[bot]" - } + "ref": "${{ github.ref }}", + "after": "${{ github.sha }}", + "repository": { + "clone_url": "${{ github.server_url }}/${{ github.repository }}.git", } } - r = requests.post(webhook_url, json=copr_payload) + r = requests.post(webhook_url, json=copr_payload, headers={"X-GitHub-Event": "push"}) r.raise_for_status() diff --git a/.github/workflows/release_channels.yml b/.github/workflows/release_channels.yml index 8891826d2..afb7319ed 100644 --- a/.github/workflows/release_channels.yml +++ b/.github/workflows/release_channels.yml @@ -4,7 +4,9 @@ on: release: types: [published, released] -permissions: read-all +permissions: + contents: write + packages: write jobs: package-ppa: From fb2c008c89cbd9930eda10193baf66c0c45fc6b4 Mon Sep 17 00:00:00 2001 From: Ben Meadors Date: Tue, 14 Jan 2025 18:55:02 -0600 Subject: [PATCH 30/89] Update version.properties --- version.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version.properties b/version.properties index 4312ae59a..800529e39 100644 --- a/version.properties +++ b/version.properties @@ -1,4 +1,4 @@ [VERSION] major = 2 minor = 5 -build = 20 +build = 19 From 85de193845bae37e1a19a87372e8c3d187f0ecb2 Mon Sep 17 00:00:00 2001 From: Ben Meadors Date: Wed, 15 Jan 2025 06:46:12 -0600 Subject: [PATCH 31/89] Fix NRF52 default append write mode of files (#5858) * Fix NRF52 default append write mode of files * Inside the lock --- src/SafeFile.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/SafeFile.cpp b/src/SafeFile.cpp index 825ba3028..f874164ae 100644 --- a/src/SafeFile.cpp +++ b/src/SafeFile.cpp @@ -9,7 +9,9 @@ static File openFile(const char *filename, bool fullAtomic) LOG_DEBUG("Opening %s, fullAtomic=%d", filename, fullAtomic); #ifdef ARCH_NRF52 lfs_assert_failed = false; - return FSCom.open(filename, FILE_O_WRITE); + File file = FSCom.open(filename, FILE_O_WRITE); + file.seek(0); + return file; #endif if (!fullAtomic) FSCom.remove(filename); // Nuke the old file to make space (ignore if it !exists) @@ -59,6 +61,9 @@ bool SafeFile::close() return false; spiLock->lock(); +#ifdef ARCH_NRF52 + f.truncate(); +#endif f.close(); spiLock->unlock(); From f9876cfe9c52691b56305270e1246fdc6d72f37f Mon Sep 17 00:00:00 2001 From: todd-herbert Date: Thu, 16 Jan 2025 02:19:51 +1300 Subject: [PATCH 32/89] Wait for disconnection (#5859) --- src/platform/nrf52/NRF52Bluetooth.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/platform/nrf52/NRF52Bluetooth.cpp b/src/platform/nrf52/NRF52Bluetooth.cpp index 541e90ab2..b98620f33 100644 --- a/src/platform/nrf52/NRF52Bluetooth.cpp +++ b/src/platform/nrf52/NRF52Bluetooth.cpp @@ -210,7 +210,10 @@ void NRF52Bluetooth::shutdown() LOG_INFO("NRF52 bluetooth disconnecting handle %d", i); Bluefruit.disconnect(i); } - delay(100); // wait for ondisconnect; + // Wait for disconnection + while (Bluefruit.connected()) + yield(); + LOG_INFO("All bluetooth connections ended"); } Bluefruit.Advertising.stop(); } From 4cd2ba54795f47b732608eab8e834c50ea8cc9c1 Mon Sep 17 00:00:00 2001 From: todd-herbert Date: Thu, 16 Jan 2025 23:23:57 +1300 Subject: [PATCH 33/89] More lines of environmental telemetry on-screen (#5853) --- src/modules/Telemetry/EnvironmentTelemetry.cpp | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/src/modules/Telemetry/EnvironmentTelemetry.cpp b/src/modules/Telemetry/EnvironmentTelemetry.cpp index 2fc9bab0b..fe1d0d2a9 100644 --- a/src/modules/Telemetry/EnvironmentTelemetry.cpp +++ b/src/modules/Telemetry/EnvironmentTelemetry.cpp @@ -278,8 +278,18 @@ void EnvironmentTelemetryModule::drawFrame(OLEDDisplay *display, OLEDDisplayUiSt static bool scrollingDown = true; static uint32_t lastScrollTime = millis(); - // Draw up to 3 sensor data lines - int linesToShow = min(3, sensorCount); + // Determine how many lines we can fit on display + // Calculated once only: display dimensions don't change during runtime. + static int maxLines = 0; + if (!maxLines) { + const int16_t paddingTop = _fontHeight(FONT_SMALL); // Heading text + const int16_t paddingBottom = 8; // Indicator dots + maxLines = (display->getHeight() - paddingTop - paddingBottom) / _fontHeight(FONT_SMALL); + assert(maxLines > 0); + } + + // Draw as many lines of data as we can fit + int linesToShow = min(maxLines, sensorCount); for (int i = 0; i < linesToShow; i++) { int index = (scrollOffset + i) % sensorCount; display->drawString(x, y += _fontHeight(FONT_SMALL), sensorData[index]); From 262f1d25a20ad68036fec402b2e5d7ca0b19adcf Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 16 Jan 2025 06:15:17 -0600 Subject: [PATCH 34/89] [create-pull-request] automated change (#5860) Co-authored-by: thebentern <9000580+thebentern@users.noreply.github.com> --- debian/changelog | 3 ++- version.properties | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/debian/changelog b/debian/changelog index 8b1799c52..a1a359cfb 100644 --- a/debian/changelog +++ b/debian/changelog @@ -2,5 +2,6 @@ meshtasticd (2.5.20.0) UNRELEASED; urgency=medium * Initial packaging * GitHub Actions Automatic version bump + * GitHub Actions Automatic version bump - -- Austin Lane Mon, 13 Jan 2025 19:24:14 +0000 + -- Austin Lane Wed, 15 Jan 2025 14:08:54 +0000 diff --git a/version.properties b/version.properties index 800529e39..4312ae59a 100644 --- a/version.properties +++ b/version.properties @@ -1,4 +1,4 @@ [VERSION] major = 2 minor = 5 -build = 19 +build = 20 From a48df917374019f408608e814451e9029a75d002 Mon Sep 17 00:00:00 2001 From: todd-herbert Date: Fri, 17 Jan 2025 01:38:22 +1300 Subject: [PATCH 35/89] Canned messages: allow GPIO0 with "scan and select" input (#5838) * Allow GPIO0; check for conflict with user button * Guard for no BUTTON_PIN; handle portduino * Portduino settings: attempt two We don't really need to #include radio code here just to check if the pin is RADIOLIB_NC. We're only interested if scanAndSelect pin matches user button pin, but they won't match if user button is RADIOLIB_NC. * Portduino attempt 3: glue --- src/input/ScanAndSelect.cpp | 27 +++++++++++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) diff --git a/src/input/ScanAndSelect.cpp b/src/input/ScanAndSelect.cpp index d8767fab8..1262f99b4 100644 --- a/src/input/ScanAndSelect.cpp +++ b/src/input/ScanAndSelect.cpp @@ -7,6 +7,9 @@ #include "ScanAndSelect.h" #include "modules/CannedMessageModule.h" #include +#ifdef ARCH_PORTDUINO // Only to check for pin conflict with user button +#include "platform/portduino/PortduinoGlue.h" +#endif // Config static const char name[] = "scanAndSelect"; // should match "allow input source" string @@ -30,7 +33,9 @@ bool ScanAndSelectInput::init() if (strcasecmp(moduleConfig.canned_message.allow_input_source, name) != 0) return false; - // Use any available inputbroker pin as the button + // Determine which pin to use for the single scan-and-select button + // User can specify this by setting any of the inputbroker pins + // If all values are zero, we'll assume the user *does* want GPIO0 if (moduleConfig.canned_message.inputbroker_pin_press) pin = moduleConfig.canned_message.inputbroker_pin_press; else if (moduleConfig.canned_message.inputbroker_pin_a) @@ -38,7 +43,25 @@ bool ScanAndSelectInput::init() else if (moduleConfig.canned_message.inputbroker_pin_b) pin = moduleConfig.canned_message.inputbroker_pin_b; else - return false; // Short circuit: no button found + pin = 0; // GPIO 0 then + + // Short circuit: if selected pin conficts with the user button +#if defined(ARCH_PORTDUINO) + int pinUserButton = 0; + if (settingsMap.count(user) != 0) { + pinUserButton = settingsMap[user]; + } +#elif defined(USERPREFS_BUTTON_PIN) + int pinUserButton = config.device.button_gpio ? config.device.button_gpio : USERPREFS_BUTTON_PIN; +#elif defined(BUTTON_PIN) + int pinUserButton = config.device.button_gpio ? config.device.button_gpio : BUTTON_PIN; +#else + int pinUserButton = config.device.button_gpio; +#endif + if (pin == pinUserButton) { + LOG_ERROR("ScanAndSelect conflict with user button"); + return false; + } // Set-up the button pinMode(pin, INPUT_PULLUP); From 7ba593432e33b288db704a181eb8b910ef3afc22 Mon Sep 17 00:00:00 2001 From: Austin Date: Thu, 16 Jan 2025 16:51:18 -0500 Subject: [PATCH 36/89] COPR: Switch from hook to copr_cli (#5864) --- .github/workflows/hook_copr.yml | 41 +++++++++------------------------ 1 file changed, 11 insertions(+), 30 deletions(-) diff --git a/.github/workflows/hook_copr.yml b/.github/workflows/hook_copr.yml index c30038d6b..a72ae5cf5 100644 --- a/.github/workflows/hook_copr.yml +++ b/.github/workflows/hook_copr.yml @@ -3,9 +3,7 @@ name: Trigger COPR build on: workflow_call: secrets: - COPR_HOOK_DAILY: - COPR_HOOK_ALPHA: - COPR_HOOK_BETA: + COPR_API_CONFIG: inputs: copr_project: description: COPR project to target @@ -32,30 +30,13 @@ jobs: pip install requests - name: Trigger COPR build - shell: python - run: | - import requests - - project_name = "${{ inputs.copr_project }}" - if project_name == "daily": - hook_secret = "${{ secrets.COPR_HOOK_DAILY }}" - project_id = 160277 - elif project_name == "alpha": - hook_secret = "${{ secrets.COPR_HOOK_ALPHA }}" - project_id = 160278 - elif project_name == "beta": - hook_secret = "${{ secrets.COPR_HOOK_BETA }}" - project_id = 160279 - else: - raise ValueError(f"Unknown COPR project: {project_name}") - - webhook_url = f"https://copr.fedorainfracloud.org/webhooks/github/{project_id}/{hook_secret}/meshtasticd/" - copr_payload = { - "ref": "${{ github.ref }}", - "after": "${{ github.sha }}", - "repository": { - "clone_url": "${{ github.server_url }}/${{ github.repository }}.git", - } - } - r = requests.post(webhook_url, json=copr_payload, headers={"X-GitHub-Event": "push"}) - r.raise_for_status() + uses: akdev1l/copr-build@main + id: copr_build + env: + COPR_API_TOKEN_CONFIG: ${{ secrets.COPR_API_CONFIG }} + with: + owner: meshtastic + package-name: meshtasticd + project-name: ${{ inputs.copr_project }} + git-remote: "${{ github.server_url }}/${{ github.repository }}.git" + committish: ${{ github.sha }} From 7acd72ede145ef92b829add29beaf7d269ce636d Mon Sep 17 00:00:00 2001 From: Ben Meadors Date: Thu, 16 Jan 2025 16:00:23 -0600 Subject: [PATCH 37/89] Cleanup unique id --- src/mesh/NodeDB.cpp | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/mesh/NodeDB.cpp b/src/mesh/NodeDB.cpp index a9e5565ef..762982287 100644 --- a/src/mesh/NodeDB.cpp +++ b/src/mesh/NodeDB.cpp @@ -197,9 +197,8 @@ 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) +#if defined(CONFIG_IDF_TARGET_ESP32C3) || defined(CONFIG_IDF_TARGET_ESP32S3) 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 @@ -207,7 +206,6 @@ NodeDB::NodeDB() 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"); } @@ -221,12 +219,12 @@ NodeDB::NodeDB() 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) { + // if (myNodeInfo.device_id.size == 16) { // std::string deviceIdHex; // for (size_t i = 0; i < myNodeInfo.device_id.size; ++i) { // char buf[3]; From a085614aaa602ab44cb453609d3a47bb70ed0f24 Mon Sep 17 00:00:00 2001 From: danwelch3 Date: Thu, 16 Jan 2025 16:22:27 -0700 Subject: [PATCH 38/89] Initiate magnetometer based compass calibration from button presses (#5553) * Initiate magenetometer based compass calibration from button presses - only active for BMX160 accelerometers on RAK_4631 - replace automatic calibration on power on with button triggered calibration - set 5 presses to trigger 30s calibration - set 6 presses to trigger 60s calibration (useful if unit is not handheld, ie vehicle mounted) - show calibration time remaining on calibration alert screen * Fix non RAK 4631 builds - exclude changes from non RAK 4631 builds - remove calls to screen when not present * Fix build on RAK4631_eth_gw - exclude all compass heading updates on variant without screen --------- Co-authored-by: Ben Meadors --- src/ButtonThread.cpp | 14 +++++++++++++ src/graphics/Screen.h | 6 ++++++ src/motion/AccelerometerThread.h | 7 +++++++ src/motion/BMX160Sensor.cpp | 35 ++++++++++++++++++++++++++------ src/motion/BMX160Sensor.h | 1 + src/motion/MotionSensor.cpp | 8 ++++++++ src/motion/MotionSensor.h | 6 ++++++ 7 files changed, 71 insertions(+), 6 deletions(-) diff --git a/src/ButtonThread.cpp b/src/ButtonThread.cpp index 3f64b3b3e..5175a2680 100644 --- a/src/ButtonThread.cpp +++ b/src/ButtonThread.cpp @@ -190,6 +190,20 @@ int32_t ButtonThread::runOnce() case 4: digitalWrite(PIN_EINK_EN, digitalRead(PIN_EINK_EN) == LOW); break; +#endif +#if defined(RAK_4631) + // 5 clicks: start accelerometer/magenetometer calibration for 30 seconds + case 5: + if (accelerometerThread) { + accelerometerThread->calibrate(30); + } + break; + // 6 clicks: start accelerometer/magenetometer calibration for 60 seconds + case 6: + if (accelerometerThread) { + accelerometerThread->calibrate(60); + } + break; #endif // No valid multipress action default: diff --git a/src/graphics/Screen.h b/src/graphics/Screen.h index 3cb39e8ec..ce416156f 100644 --- a/src/graphics/Screen.h +++ b/src/graphics/Screen.h @@ -278,6 +278,10 @@ class Screen : public concurrency::OSThread bool hasHeading() { return hasCompass; } long getHeading() { return compassHeading; } + + void setEndCalibration(uint32_t _endCalibrationAt) { endCalibrationAt = _endCalibrationAt; } + uint32_t getEndCalibration() { return endCalibrationAt; } + // functions for display brightness void increaseBrightness(); void decreaseBrightness(); @@ -673,6 +677,8 @@ class Screen : public concurrency::OSThread bool hasCompass = false; float compassHeading; + uint32_t endCalibrationAt; + /// Holds state for debug information DebugInfo debugInfo; diff --git a/src/motion/AccelerometerThread.h b/src/motion/AccelerometerThread.h index 95f09910f..6e517d6b0 100755 --- a/src/motion/AccelerometerThread.h +++ b/src/motion/AccelerometerThread.h @@ -48,6 +48,13 @@ class AccelerometerThread : public concurrency::OSThread setIntervalFromNow(0); }; + void calibrate(uint16_t forSeconds) + { + if (sensor) { + sensor->calibrate(forSeconds); + } + } + protected: int32_t runOnce() override { diff --git a/src/motion/BMX160Sensor.cpp b/src/motion/BMX160Sensor.cpp index 6562a651c..06cea3229 100755 --- a/src/motion/BMX160Sensor.cpp +++ b/src/motion/BMX160Sensor.cpp @@ -25,19 +25,21 @@ bool BMX160Sensor::init() int32_t BMX160Sensor::runOnce() { +#if !defined(MESHTASTIC_EXCLUDE_SCREEN) sBmx160SensorData_t magAccel; sBmx160SensorData_t gAccel; /* Get a new sensor event */ sensor.getAllData(&magAccel, NULL, &gAccel); -#if !defined(MESHTASTIC_EXCLUDE_SCREEN) - // experimental calibrate routine. Limited to between 10 and 30 seconds after boot - if (millis() > 12 * 1000 && millis() < 30 * 1000) { + if (doCalibration) { + if (!showingScreen) { + powerFSM.trigger(EVENT_PRESS); // keep screen alive during calibration showingScreen = true; screen->startAlert((FrameCallback)drawFrameCalibration); } + if (magAccel.x > highestX) highestX = magAccel.x; if (magAccel.x < lowestX) @@ -50,9 +52,17 @@ int32_t BMX160Sensor::runOnce() highestZ = magAccel.z; if (magAccel.z < lowestZ) lowestZ = magAccel.z; - } else if (showingScreen && millis() >= 30 * 1000) { - showingScreen = false; - screen->endAlert(); + + uint32_t now = millis(); + if (now > endCalibrationAt) { + doCalibration = false; + endCalibrationAt = 0; + showingScreen = false; + screen->endAlert(); + } + + // LOG_DEBUG("BMX160 min_x: %.4f, max_X: %.4f, min_Y: %.4f, max_Y: %.4f, min_Z: %.4f, max_Z: %.4f", lowestX, highestX, + // lowestY, highestY, lowestZ, highestZ); } int highestRealX = highestX - (highestX + lowestX) / 2; @@ -93,12 +103,25 @@ int32_t BMX160Sensor::runOnce() heading += 270; break; } + screen->setHeading(heading); #endif return MOTION_SENSOR_CHECK_INTERVAL_MS; } +void BMX160Sensor::calibrate(uint16_t forSeconds) +{ +#if !defined(MESHTASTIC_EXCLUDE_SCREEN) + LOG_DEBUG("BMX160 calibration started for %is", forSeconds); + + doCalibration = true; + uint16_t calibrateFor = forSeconds * 1000; // calibrate for seconds provided + endCalibrationAt = millis() + calibrateFor; + screen->setEndCalibration(endCalibrationAt); +#endif +} + #endif #endif \ No newline at end of file diff --git a/src/motion/BMX160Sensor.h b/src/motion/BMX160Sensor.h index 26f477271..9031b4504 100755 --- a/src/motion/BMX160Sensor.h +++ b/src/motion/BMX160Sensor.h @@ -23,6 +23,7 @@ class BMX160Sensor : public MotionSensor explicit BMX160Sensor(ScanI2C::FoundDevice foundDevice); virtual bool init() override; virtual int32_t runOnce() override; + virtual void calibrate(uint16_t forSeconds) override; }; #else diff --git a/src/motion/MotionSensor.cpp b/src/motion/MotionSensor.cpp index 242e3709f..d87380085 100755 --- a/src/motion/MotionSensor.cpp +++ b/src/motion/MotionSensor.cpp @@ -2,6 +2,8 @@ #if !defined(ARCH_PORTDUINO) && !defined(ARCH_STM32WL) && !MESHTASTIC_EXCLUDE_I2C +char timeRemainingBuffer[12]; + // screen is defined in main.cpp extern graphics::Screen *screen; @@ -37,6 +39,12 @@ void MotionSensor::drawFrameCalibration(OLEDDisplay *display, OLEDDisplayUiState display->setTextAlignment(TEXT_ALIGN_LEFT); display->setFont(FONT_MEDIUM); display->drawString(x, y, "Calibrating\nCompass"); + + uint8_t timeRemaining = (screen->getEndCalibration() - millis()) / 1000; + sprintf(timeRemainingBuffer, "( %02d )", timeRemaining); + display->setFont(FONT_SMALL); + display->drawString(x, y + 40, timeRemainingBuffer); + int16_t compassX = 0, compassY = 0; uint16_t compassDiam = graphics::Screen::getCompassDiam(display->getWidth(), display->getHeight()); diff --git a/src/motion/MotionSensor.h b/src/motion/MotionSensor.h index 78eec54ce..1f4d093bf 100755 --- a/src/motion/MotionSensor.h +++ b/src/motion/MotionSensor.h @@ -40,6 +40,8 @@ class MotionSensor // Refer to /src/concurrency/OSThread.h for more information inline virtual int32_t runOnce() { return MOTION_SENSOR_CHECK_INTERVAL_MS; }; + virtual void calibrate(uint16_t forSeconds){}; + protected: // Turn on the screen when a tap or motion is detected virtual void wakeScreen(); @@ -53,6 +55,10 @@ class MotionSensor #endif ScanI2C::FoundDevice device; + + // Do calibration if true + bool doCalibration = false; + uint32_t endCalibrationAt = 0; }; namespace MotionSensorI2C From 8179e61fdc5ed73a2deec577312cf12918efbe5c Mon Sep 17 00:00:00 2001 From: SignalMedic Date: Thu, 16 Jan 2025 18:26:02 -0500 Subject: [PATCH 39/89] changed GPS buad rate to 9600 (#5786) Co-authored-by: Huston Hedinger <1875033+hdngr@users.noreply.github.com> --- variants/canaryone/variant.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/variants/canaryone/variant.h b/variants/canaryone/variant.h index 140b605ad..836fa74a3 100644 --- a/variants/canaryone/variant.h +++ b/variants/canaryone/variant.h @@ -123,7 +123,7 @@ static const uint8_t A0 = PIN_A0; */ #define HAS_GPS 1 #define GPS_UBLOX -#define GPS_BAUDRATE 38400 +#define GPS_BAUDRATE 9600 // #define PIN_GPS_WAKE (GPIO_PORT1 + 2) // An output to wake GPS, low means allow sleep, high means force wake // Seems to be missing on this new board From f132158c3e83fd95004be5d19b302a6d493c295d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADt=20Hol=C3=A1sek?= Date: Fri, 17 Jan 2025 01:39:39 +0100 Subject: [PATCH 40/89] Fixed localization on bigger screens (#5695) Co-authored-by: Ben Meadors --- src/graphics/ScreenFonts.h | 102 ++++++++++++++++++------------------- 1 file changed, 50 insertions(+), 52 deletions(-) diff --git a/src/graphics/ScreenFonts.h b/src/graphics/ScreenFonts.h index 7881b464f..f6abec0f5 100644 --- a/src/graphics/ScreenFonts.h +++ b/src/graphics/ScreenFonts.h @@ -16,63 +16,61 @@ #include "graphics/fonts/OLEDDisplayFontsCS.h" #endif +#ifdef OLED_PL +#define FONT_SMALL_LOCAL ArialMT_Plain_10_PL +#else +#ifdef OLED_RU +#define FONT_SMALL_LOCAL ArialMT_Plain_10_RU +#else +#ifdef OLED_UA +#define FONT_SMALL_LOCAL ArialMT_Plain_10_UA // Height: 13 +#else +#ifdef OLED_CS +#define FONT_SMALL_LOCAL ArialMT_Plain_10_CS +#else +#define FONT_SMALL_LOCAL ArialMT_Plain_10 // Height: 13 +#endif +#endif +#endif +#endif +#ifdef OLED_PL +#define FONT_MEDIUM_LOCAL ArialMT_Plain_16_PL // Height: 19 +#else +#ifdef OLED_UA +#define FONT_MEDIUM_LOCAL ArialMT_Plain_16_UA // Height: 19 +#else +#ifdef OLED_CS +#define FONT_MEDIUM_LOCAL ArialMT_Plain_16_CS +#else +#define FONT_MEDIUM_LOCAL ArialMT_Plain_16 // Height: 19 +#endif +#endif +#endif +#ifdef OLED_PL +#define FONT_LARGE_LOCAL ArialMT_Plain_24_PL // Height: 28 +#else +#ifdef OLED_UA +#define FONT_LARGE_LOCAL ArialMT_Plain_24_UA // Height: 28 +#else +#ifdef OLED_CS +#define FONT_LARGE_LOCAL ArialMT_Plain_24_CS // Height: 28 +#else +#define FONT_LARGE_LOCAL ArialMT_Plain_24 // Height: 28 +#endif +#endif +#endif + #if (defined(USE_EINK) || defined(ILI9341_DRIVER) || defined(ILI9342_DRIVER) || defined(ST7701_CS) || defined(ST7735_CS) || \ defined(ST7789_CS) || defined(USE_ST7789) || defined(HX8357_CS)) && \ !defined(DISPLAY_FORCE_SMALL_FONTS) // The screen is bigger so use bigger fonts -#ifdef OLED_PL -#define FONT_SMALL ArialMT_Plain_16_PL // Height: 19 -#define FONT_MEDIUM ArialMT_Plain_24_PL // Height: 28 -#define FONT_LARGE ArialMT_Plain_24_PL // Height: 28 +#define FONT_SMALL FONT_MEDIUM_LOCAL // Height: 19 +#define FONT_MEDIUM FONT_LARGE_LOCAL // Height: 28 +#define FONT_LARGE FONT_LARGE_LOCAL // Height: 28 #else -#define FONT_SMALL ArialMT_Plain_16 // Height: 19 -#define FONT_MEDIUM ArialMT_Plain_24 // Height: 28 -#define FONT_LARGE ArialMT_Plain_24 // Height: 28 -#endif -#else -#ifdef OLED_PL -#define FONT_SMALL ArialMT_Plain_10_PL -#else -#ifdef OLED_RU -#define FONT_SMALL ArialMT_Plain_10_RU -#else -#ifdef OLED_UA -#define FONT_SMALL ArialMT_Plain_10_UA // Height: 13 -#else -#ifdef OLED_CS -#define FONT_SMALL ArialMT_Plain_10_CS -#else -#define FONT_SMALL ArialMT_Plain_10 // Height: 13 -#endif -#endif -#endif -#endif -#ifdef OLED_PL -#define FONT_MEDIUM ArialMT_Plain_16_PL // Height: 19 -#else -#ifdef OLED_UA -#define FONT_MEDIUM ArialMT_Plain_16_UA // Height: 19 -#else -#ifdef OLED_CS -#define FONT_MEDIUM ArialMT_Plain_16_CS -#else -#define FONT_MEDIUM ArialMT_Plain_16 // Height: 19 -#endif -#endif -#endif -#ifdef OLED_PL -#define FONT_LARGE ArialMT_Plain_24_PL // Height: 28 -#else -#ifdef OLED_UA -#define FONT_LARGE ArialMT_Plain_24_UA // Height: 28 -#else -#ifdef OLED_CS -#define FONT_LARGE ArialMT_Plain_24_CS // Height: 28 -#else -#define FONT_LARGE ArialMT_Plain_24 // Height: 28 -#endif -#endif -#endif +#define FONT_SMALL FONT_SMALL_LOCAL // Height: 13 +#define FONT_MEDIUM FONT_MEDIUM_LOCAL // Height: 19 +#define FONT_LARGE FONT_LARGE_LOCAL // Height: 28 #endif #define _fontHeight(font) ((font)[1] + 1) // height is position 1 From b0fe5ef8ba13af2ddc774018b9f08217859d7c3d Mon Sep 17 00:00:00 2001 From: Eric Severance Date: Thu, 16 Jan 2025 16:42:21 -0800 Subject: [PATCH 41/89] Initial commit of a fuzzer for Meshtastic (#5790) * Initial commit of a fuzzer for Meshtastic. * Use a max of 5 for the phone queues * Only write files to the temp dir * Limitless queue + fuzzer = lots of ram :) * Use $PIO_ENV for path to program * spelling: s/is/to/ * Use loopCanSleep instead of a lock in Router * realHardware allows full use of a CPU core * Ignore checkov CKV_DOCKER_2 & CKV_DOCKER_3 * Add Atak seed * Fix lint issues in build.sh * Use exception to exit from portduino_main * Separate build & source files into $WORK & $SRC * Use an ephemeral port for the API server * Include CXXFLAGS in the link step * Read all shared libraries * Use a separate work directory for each sanitizer --------- Co-authored-by: Ben Meadors --- .clusterfuzzlite/Dockerfile | 52 +++++ .clusterfuzzlite/README.md | 59 +++++ .clusterfuzzlite/build.sh | 71 ++++++ .../platformio-clusterfuzzlite-post.py | 35 +++ .../platformio-clusterfuzzlite-pre.py | 52 +++++ .clusterfuzzlite/project.yaml | 1 + .clusterfuzzlite/router_fuzzer.cpp | 206 ++++++++++++++++++ .clusterfuzzlite/router_fuzzer.options | 2 + .clusterfuzzlite/router_fuzzer_seed_corpus.py | 168 ++++++++++++++ .dockerignore | 1 + platformio.ini | 2 +- src/FSCommon.cpp | 2 +- src/mesh/PacketHistory.h | 4 + src/mesh/TypedQueue.h | 15 +- src/platform/portduino/PortduinoGlue.cpp | 8 +- 15 files changed, 671 insertions(+), 7 deletions(-) create mode 100644 .clusterfuzzlite/Dockerfile create mode 100644 .clusterfuzzlite/README.md create mode 100644 .clusterfuzzlite/build.sh create mode 100644 .clusterfuzzlite/platformio-clusterfuzzlite-post.py create mode 100644 .clusterfuzzlite/platformio-clusterfuzzlite-pre.py create mode 100644 .clusterfuzzlite/project.yaml create mode 100644 .clusterfuzzlite/router_fuzzer.cpp create mode 100644 .clusterfuzzlite/router_fuzzer.options create mode 100644 .clusterfuzzlite/router_fuzzer_seed_corpus.py create mode 120000 .dockerignore diff --git a/.clusterfuzzlite/Dockerfile b/.clusterfuzzlite/Dockerfile new file mode 100644 index 000000000..a769a976d --- /dev/null +++ b/.clusterfuzzlite/Dockerfile @@ -0,0 +1,52 @@ +# This container is used to build Meshtastic with the libraries required by the fuzzer. +# ClusterFuzzLite starts the container, runs the build.sh script, and then exits. + +# As this is not a long running service, health-checks are not required. ClusterFuzzLite +# also only works if the user remains unchanged from the base image (it expects to run +# as root). +# trunk-ignore-all(trivy/DS026): No healthcheck is needed for this builder container +# trunk-ignore-all(checkov/CKV_DOCKER_2): No healthcheck is needed for this builder container +# trunk-ignore-all(checkov/CKV_DOCKER_3): We must run as root for this container +# trunk-ignore-all(trivy/DS002): We must run as root for this container +# trunk-ignore-all(checkov/CKV_DOCKER_8): We must run as root for this container +# trunk-ignore-all(hadolint/DL3002): We must run as root for this container + +FROM gcr.io/oss-fuzz-base/base-builder:v1 + +ENV PIP_ROOT_USER_ACTION=ignore + +# trunk-ignore(hadolint/DL3008): apt packages are not pinned. +# trunk-ignore(terrascan/AC_DOCKER_0002): apt packages are not pinned. +RUN apt-get update && apt-get install --no-install-recommends -y \ + cmake git zip libgpiod-dev libbluetooth-dev libi2c-dev \ + libunistring-dev libmicrohttpd-dev libgnutls28-dev libgcrypt20-dev \ + libusb-1.0-0-dev libssl-dev pkg-config && \ + apt-get clean && rm -rf /var/lib/apt/lists/* && \ + pip install --no-cache-dir -U \ + platformio==6.1.16 \ + grpcio-tools==1.68.1 \ + meshtastic==2.5.9 + +# Ugly hack to avoid clang detecting a conflict between the math "log" function and the "log" function in framework-portduino/cores/portduino/logging.h +RUN sed -i -e 's/__MATHCALL_VEC (log,, (_Mdouble_ __x));//' /usr/include/x86_64-linux-gnu/bits/mathcalls.h + +# A few dependencies are too old on the base-builder image. More recent versions are built from source. +WORKDIR $SRC +RUN git config --global advice.detachedHead false && \ + git clone --depth 1 --branch 0.8.0 https://github.com/jbeder/yaml-cpp.git && \ + git clone --depth 1 --branch v2.3.3 https://github.com/babelouest/orcania.git && \ + git clone --depth 1 --branch v1.4.20 https://github.com/babelouest/yder.git && \ + git clone --depth 1 --branch v2.7.15 https://github.com/babelouest/ulfius.git + +COPY ./.clusterfuzzlite/build.sh $SRC/ + +WORKDIR $SRC/firmware +COPY . $SRC/firmware/ + +# https://docs.platformio.org/en/latest/envvars.html +ENV PLATFORMIO_CORE_DIR=$SRC/pio/core \ + PLATFORMIO_LIBDEPS_DIR=$SRC/pio/libdeps \ + PLATFORMIO_PACKAGES_DIR=$SRC/pio/packages \ + PLATFORMIO_SETTING_ENABLE_CACHE=No \ + PIO_ENV=buildroot +RUN platformio pkg install --environment $PIO_ENV diff --git a/.clusterfuzzlite/README.md b/.clusterfuzzlite/README.md new file mode 100644 index 000000000..f6e4089a3 --- /dev/null +++ b/.clusterfuzzlite/README.md @@ -0,0 +1,59 @@ +# ClusterFuzzLite for Meshtastic + +This directory contains the fuzzer implementation for Meshtastic using the ClusterFuzzLite framework. +See the [ClusterFuzzLite documentation](https://google.github.io/clusterfuzzlite/) for more details. + +## Running locally + +ClusterFuzzLite uses the OSS-Fuzz toolchain. To build the fuzzer manually, first grab a copy of OSS-Fuzz. + +```shell +git clone https://github.com/google/oss-fuzz.git +cd oss-fuzz +``` + +To build the fuzzer, run: + +```shell +python3 infra/helper.py build_image --external $PATH_TO_MESHTASTIC_FIRMWARE_DIRECTORY +python3 infra/helper.py build_fuzzers --external $PATH_TO_MESHTASTIC_FIRMWARE_DIRECTORY --sanitizer address +``` + +To run the fuzzer, run: + +```shell +python3 infra/helper.py run_fuzzer --external --corpus-dir= $PATH_TO_MESHTASTIC_FIRMWARE_DIRECTORY router_fuzzer +``` + +More background on these commands can be found in the +[ClusterFuzzLite documentation](https://google.github.io/clusterfuzzlite/build-integration/#testing-locally). + +## router_fuzzer.cpp + +This fuzzer submits MeshPacket protos to the `Router::enqueueReceivedMessage` method. It takes the binary +data from the fuzzer and decodes that data to a MeshPacket using nanopb. A few fields in +the MeshPacket are modified by the fuzzer. + +- If the `to` field is 0, it will be replaced with the NodeID of the running node. +- If the `from` field is 0, it will be replaced with the NodeID of the running node. +- If the `id` field is 0, it will be replaced with an incrementing counter value. +- If the `pki_encrypted` field is true, the `public_key` field will be populated with the first admin key. + +The `router_fuzzer_seed_corpus.py` file contains a list of MeshPackets. It is run from inside build.sh and +writes the binary MeshPacket protos to files. These files are use used by the fuzzer as its initial seed data, +helping the fuzzer to start off with a few known inputs. + +### Interpreting a fuzzer crash + +If the fuzzer crashes, it'll write the input bytes used for the test case to a file and notify about the +location of that file. The contents of the file are a binary serialized MeshPacket protobuf. The following +snippet of Python code can be used to parse the file into a human readable form. + +```python +from meshtastic.protobuf import mesh_pb2 + +mesh_pb2.MeshPacket.FromString(open("crash-XXXX-file", "rb").read()) +``` + +Consider adding any such crash results to the `router_fuzzer_seed_corpus.py` file to ensure there a isn't +a future regression for that crash test case. diff --git a/.clusterfuzzlite/build.sh b/.clusterfuzzlite/build.sh new file mode 100644 index 000000000..10a2db0bd --- /dev/null +++ b/.clusterfuzzlite/build.sh @@ -0,0 +1,71 @@ +#!/bin/bash -eu + +# Build Meshtastic and a few needed dependencies using clang++ +# and the OSS-Fuzz required build flags. + +env + +cd "$SRC" +NPROC=$(nproc || echo 1) + +LDFLAGS=-lpthread cmake -S "$SRC/yaml-cpp" -B "$WORK/yaml-cpp/$SANITIZER" \ + -DBUILD_SHARED_LIBS=OFF +cmake --build "$WORK/yaml-cpp/$SANITIZER" -j "$NPROC" +cmake --install "$WORK/yaml-cpp/$SANITIZER" --prefix /usr + +cmake -S "$SRC/orcania" -B "$WORK/orcania/$SANITIZER" \ + -DBUILD_STATIC=ON +cmake --build "$WORK/orcania/$SANITIZER" -j "$NPROC" +cmake --install "$WORK/orcania/$SANITIZER" --prefix /usr + +cmake -S "$SRC/yder" -B "$WORK/yder/$SANITIZER" \ + -DBUILD_STATIC=ON -DWITH_JOURNALD=OFF +cmake --build "$WORK/yder/$SANITIZER" -j "$NPROC" +cmake --install "$WORK/yder/$SANITIZER" --prefix /usr + +cmake -S "$SRC/ulfius" -B "$WORK/ulfius/$SANITIZER" \ + -DBUILD_STATIC=ON -DWITH_JANSSON=OFF -DWITH_CURL=OFF -DWITH_WEBSOCKET=OFF +cmake --build "$WORK/ulfius/$SANITIZER" -j "$NPROC" +cmake --install "$WORK/ulfius/$SANITIZER" --prefix /usr + +cd "$SRC/firmware" + +PLATFORMIO_EXTRA_SCRIPTS=$(echo -e "pre:.clusterfuzzlite/platformio-clusterfuzzlite-pre.py\npost:.clusterfuzzlite/platformio-clusterfuzzlite-post.py") +STATIC_LIBS=$(pkg-config --libs --static libulfius openssl libgpiod yaml-cpp bluez --silence-errors) +export PLATFORMIO_EXTRA_SCRIPTS +export STATIC_LIBS +export PLATFORMIO_WORKSPACE_DIR="$WORK/pio/$SANITIZER" +export TARGET_CC=$CC +export TARGET_CXX=$CXX +export TARGET_LD=$CXX +export TARGET_AR=llvm-ar +export TARGET_AS=llvm-as +export TARGET_OBJCOPY=llvm-objcopy +export TARGET_RANLIB=llvm-ranlib + +mkdir -p "$OUT/lib" + +cp .clusterfuzzlite/*_fuzzer.options "$OUT/" + +for f in .clusterfuzzlite/*_fuzzer.cpp; do + fuzzer=$(basename "$f" .cpp) + cp -f "$f" src/fuzzer.cpp + pio run -vvv --environment "$PIO_ENV" + program="$PLATFORMIO_WORKSPACE_DIR/build/$PIO_ENV/program" + cp "$program" "$OUT/$fuzzer" + + # Copy shared libraries used by the fuzzer. + read -d '' -ra shared_libs < <(ldd "$program" | sed -n 's/[^=]\+=> \([^ ]\+\).*/\1/p') || true + cp -f "${shared_libs[@]}" "$OUT/lib/" + + # Build the initial fuzzer seed corpus. + corpus_name="${fuzzer}_seed_corpus" + corpus_generator="$PWD/.clusterfuzzlite/${corpus_name}.py" + if [[ -f $corpus_generator ]]; then + mkdir "$corpus_name" + pushd "$corpus_name" + python3 "$corpus_generator" + popd + zip -D "$OUT/${corpus_name}.zip" "$corpus_name"/* + fi +done diff --git a/.clusterfuzzlite/platformio-clusterfuzzlite-post.py b/.clusterfuzzlite/platformio-clusterfuzzlite-post.py new file mode 100644 index 000000000..f62078bb3 --- /dev/null +++ b/.clusterfuzzlite/platformio-clusterfuzzlite-post.py @@ -0,0 +1,35 @@ +"""PlatformIO build script (post: runs after other Meshtastic scripts).""" + +import os +import shlex + +from SCons.Script import DefaultEnvironment + +env = DefaultEnvironment() + +# Remove any static libraries from the LIBS environment. Static libraries are +# handled in platformio-clusterfuzzlite-pre.py. +static_libs = set(lib[2:] for lib in shlex.split(os.getenv("STATIC_LIBS"))) +env.Replace( + LIBS=[ + lib for lib in env["LIBS"] if not (isinstance(lib, str) and lib in static_libs) + ], +) + +# FrameworkArduino/portduino/main.cpp contains the "main" function the binary. +# The fuzzing framework also provides a "main" function and needs to be run +# before Meshtastic is started. We rename the "main" function for Meshtastic to +# "portduino_main" here so that it can be called inside the fuzzer. +env.AddPostAction( + "$BUILD_DIR/FrameworkArduino/portduino/main.cpp.o", + env.VerboseAction( + " ".join( + [ + "$OBJCOPY", + "--redefine-sym=main=portduino_main", + "$BUILD_DIR/FrameworkArduino/portduino/main.cpp.o", + ] + ), + "Renaming main symbol to portduino_main", + ), +) diff --git a/.clusterfuzzlite/platformio-clusterfuzzlite-pre.py b/.clusterfuzzlite/platformio-clusterfuzzlite-pre.py new file mode 100644 index 000000000..a70630cf0 --- /dev/null +++ b/.clusterfuzzlite/platformio-clusterfuzzlite-pre.py @@ -0,0 +1,52 @@ +"""PlatformIO build script (pre: runs before other Meshtastic scripts). + +ClusterFuzzLite executes in a different container from the build. During the build, +attempt to link statically to as many dependencies as possible. For dependencies that +do not have static libraries, the shared library files are copied to the output +directory by the build.sh script. +""" + +import glob +import os +import shlex + +from SCons.Script import DefaultEnvironment, Literal + +env = DefaultEnvironment() + +cxxflags = shlex.split(os.getenv("CXXFLAGS")) +sanitizer_flags = shlex.split(os.getenv("SANITIZER_FLAGS")) +lib_fuzzing_engine = shlex.split(os.getenv("LIB_FUZZING_ENGINE")) +statics = glob.glob("/usr/lib/lib*.a") + glob.glob("/usr/lib/*/lib*.a") +no_static = set(("-ldl",)) + + +def replaceStatic(lib): + """Replace -l with the static .a file for the library.""" + if not lib.startswith("-l") or lib in no_static: + return lib + static_name = f"/lib{lib[2:]}.a" + static = [s for s in statics if s.endswith(static_name)] + if len(static) == 1: + return static[0] + return lib + + +# Setup the environment for building with Clang and the OSS-Fuzz required build flags. +env.Append( + CFLAGS=os.getenv("CFLAGS"), + CXXFLAGS=cxxflags, + LIBSOURCE_DIRS=["/usr/lib/x86_64-linux-gnu"], + LINKFLAGS=cxxflags + + sanitizer_flags + + lib_fuzzing_engine + + ["-stdlib=libc++", "-std=c++17"], + _LIBFLAGS=[replaceStatic(s) for s in shlex.split(os.getenv("STATIC_LIBS"))] + + [ + "/usr/lib/x86_64-linux-gnu/libunistring.a", # Needs to be at the end. + # Find the shared libraries in a subdirectory named lib + # within the same directory as the binary. + Literal("-Wl,-rpath,$ORIGIN/lib"), + "-Wl,-z,origin", + ], +) diff --git a/.clusterfuzzlite/project.yaml b/.clusterfuzzlite/project.yaml new file mode 100644 index 000000000..b4788012b --- /dev/null +++ b/.clusterfuzzlite/project.yaml @@ -0,0 +1 @@ +language: c++ diff --git a/.clusterfuzzlite/router_fuzzer.cpp b/.clusterfuzzlite/router_fuzzer.cpp new file mode 100644 index 000000000..bc4d248db --- /dev/null +++ b/.clusterfuzzlite/router_fuzzer.cpp @@ -0,0 +1,206 @@ +// Fuzzer implementation that sends MeshPackets to Router::enqueueReceivedMessage. +#include +#include +#include +#include +#include +#include +#include + +#include "PortduinoGPIO.h" +#include "PortduinoGlue.h" +#include "PowerFSM.h" +#include "mesh/MeshTypes.h" +#include "mesh/NodeDB.h" +#include "mesh/Router.h" +#include "mesh/TypeConversions.h" +#include "mesh/mesh-pb-constants.h" + +namespace +{ +constexpr uint32_t nodeId = 0x12345678; +// Set to true when lateInitVariant finishes. Used to ensure lateInitVariant was called during startup. +bool hasBeenConfigured = false; + +// These are used to block the Arduino loop() function until a fuzzer input is ready. This is +// an optimization that prevents a sleep from happening before the loop is run. The Arduino loop +// function calls loopCanSleep() before sleeping. loopCanSleep is implemented here in the fuzzer +// and blocks until runLoopOnce() is called to signal for the loop to run. +bool fuzzerRunning = false; // Set to true once LLVMFuzzerTestOneInput has started running. +bool loopCanRun = true; // The main Arduino loop() can run when this is true. +bool loopIsWaiting = false; // The main Arduino loop() is waiting to be signaled to run. +bool loopShouldExit = false; // Indicates that the main Arduino thread should exit by throwing ShouldExitException. +std::mutex loopLock; +std::condition_variable loopCV; +std::thread meshtasticThread; + +// This exception is thrown when the portuino main thread should exit. +class ShouldExitException : public std::runtime_error +{ + public: + using std::runtime_error::runtime_error; +}; + +// Start the loop for one test case and wait till the loop has completed. This ensures fuzz +// test cases do not overlap with one another. This helps the fuzzer attribute a crash to the +// single, currently running, test case. +void runLoopOnce() +{ + realHardware = true; // Avoids delay(100) within portduino/main.cpp + std::unique_lock lck(loopLock); + fuzzerRunning = true; + loopCanRun = true; + loopCV.notify_one(); + loopCV.wait(lck, [] { return !loopCanRun && loopIsWaiting; }); +} +} // namespace + +// Called in the main Arduino loop function to determine if the loop can delay/sleep before running again. +// We use this as a way to block the loop from sleeping and to start the loop function immediately when a +// fuzzer input is ready. +bool loopCanSleep() +{ + std::unique_lock lck(loopLock); + loopIsWaiting = true; + loopCV.notify_one(); + loopCV.wait(lck, [] { return loopCanRun || loopShouldExit; }); + loopIsWaiting = false; + if (loopShouldExit) + throw ShouldExitException("exit"); + if (!fuzzerRunning) + return true; // The loop can sleep before the fuzzer starts. + loopCanRun = false; // Only run the loop once before waiting again. + return false; +} + +// Called just prior to starting Meshtastic. Allows for setting config values before startup. +void lateInitVariant() +{ + settingsMap[logoutputlevel] = level_error; + channelFile.channels[0] = meshtastic_Channel{ + .has_settings = true, + .settings = + meshtastic_ChannelSettings{ + .psk = {.size = 1, .bytes = {/*defaultpskIndex=*/1}}, + .name = "LongFast", + .uplink_enabled = true, + .has_module_settings = true, + .module_settings = {.position_precision = 16}, + }, + .role = meshtastic_Channel_Role_PRIMARY, + }; + config.security.admin_key[0] = { + .size = 32, + .bytes = {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}, + }; + config.security.admin_key_count = 1; + config.lora.region = meshtastic_Config_LoRaConfig_RegionCode_US; + moduleConfig.has_mqtt = true; + moduleConfig.mqtt = meshtastic_ModuleConfig_MQTTConfig{ + .enabled = true, + .proxy_to_client_enabled = true, + }; + moduleConfig.has_store_forward = true; + moduleConfig.store_forward = meshtastic_ModuleConfig_StoreForwardConfig{ + .enabled = true, + .history_return_max = 4, + .history_return_window = 600, + .is_server = true, + }; + meshtastic_Position fixedGPS = meshtastic_Position{ + .has_latitude_i = true, + .latitude_i = static_cast(1 * 1e7), + .has_longitude_i = true, + .longitude_i = static_cast(3 * 1e7), + .has_altitude = true, + .altitude = 64, + .location_source = meshtastic_Position_LocSource_LOC_MANUAL, + }; + nodeDB->setLocalPosition(fixedGPS); + config.has_position = true; + config.position.fixed_position = true; + meshtastic_NodeInfoLite *info = nodeDB->getMeshNode(nodeDB->getNodeNum()); + info->has_position = true; + info->position = TypeConversions::ConvertToPositionLite(fixedGPS); + hasBeenConfigured = true; +} + +extern "C" { +int portduino_main(int argc, char **argv); // Renamed "main" function from Meshtastic binary. + +// Start Meshtastic in a thread and wait till it has reached the ON state. +int LLVMFuzzerInitialize(int *argc, char ***argv) +{ + settingsMap[maxtophone] = 5; + + meshtasticThread = std::thread([program = *argv[0]]() { + char nodeIdStr[12]; + strcpy(nodeIdStr, std::to_string(nodeId).c_str()); + int argc = 7; + char *argv[] = {program, "-d", "/tmp/meshtastic", "-h", nodeIdStr, "-p", "0", nullptr}; + try { + portduino_main(argc, argv); + } catch (const ShouldExitException &) { + } + }); + std::atexit([] { + { + const std::lock_guard lck(loopLock); + loopShouldExit = true; + loopCV.notify_one(); + } + meshtasticThread.join(); + }); + + // Wait for startup. + for (int i = 1; i < 20; ++i) { + if (powerFSM.getState() == &stateON) { + assert(hasBeenConfigured); + assert(router); + assert(nodeDB); + return 0; + } + std::this_thread::sleep_for(std::chrono::seconds(1)); + } + return 1; +} + +// This is the main entrypoint for the fuzzer (the fuzz target). The fuzzer will provide an array of bytes to be +// interpreted by this method. To keep things simple, the bytes are interpreted as a binary serialized MeshPacket +// proto. Any crashes discovered by the fuzzer will be written to a file. Unserialize that file to print the MeshPacket +// that caused the failure. +// +// This guide provides best practices for writing a fuzzer target. +// https://github.com/google/fuzzing/blob/master/docs/good-fuzz-target.md +int LLVMFuzzerTestOneInput(const uint8_t *data, size_t length) +{ + meshtastic_MeshPacket p = meshtastic_MeshPacket_init_default; + pb_istream_t stream = pb_istream_from_buffer(data, length); + // Ignore any inputs that fail to decode or have fields set that are not transmitted over LoRa. + if (!pb_decode(&stream, &meshtastic_MeshPacket_msg, &p) || p.rx_time || p.rx_snr || p.priority || p.rx_rssi || p.delayed || + p.public_key.size || p.next_hop || p.relay_node || p.tx_after) + return -1; // Reject: The input will not be added to the corpus. + if (p.which_payload_variant == meshtastic_MeshPacket_decoded_tag) { + meshtastic_Data d; + stream = pb_istream_from_buffer(p.decoded.payload.bytes, p.decoded.payload.size); + if (!pb_decode(&stream, &meshtastic_Data_msg, &d)) + return -1; // Reject: The input will not be added to the corpus. + } + + // Provide default values for a few fields so the fuzzer doesn't need to guess them. + if (p.from == 0) + p.from = nodeDB->getNodeNum(); + if (p.to == 0) + p.to = nodeDB->getNodeNum(); + static uint32_t packetId = 0; + if (p.id == 0) + p.id == ++packetId; + if (p.pki_encrypted && config.security.admin_key_count) + memcpy(&p.public_key, &config.security.admin_key[0], sizeof(p.public_key)); + + router->enqueueReceivedMessage(packetPool.allocCopy(p)); + runLoopOnce(); + return 0; // Accept: The input may be added to the corpus. +} +} \ No newline at end of file diff --git a/.clusterfuzzlite/router_fuzzer.options b/.clusterfuzzlite/router_fuzzer.options new file mode 100644 index 000000000..7cbd646dc --- /dev/null +++ b/.clusterfuzzlite/router_fuzzer.options @@ -0,0 +1,2 @@ +[libfuzzer] +max_len=256 diff --git a/.clusterfuzzlite/router_fuzzer_seed_corpus.py b/.clusterfuzzlite/router_fuzzer_seed_corpus.py new file mode 100644 index 000000000..71736c147 --- /dev/null +++ b/.clusterfuzzlite/router_fuzzer_seed_corpus.py @@ -0,0 +1,168 @@ +"""Generate an initial set of MeshPackets. + +The fuzzer uses these MeshPackets as an initial seed of test candidates. + +It's also good to add any previously discovered crash test cases to this list +to avoid future regressions. + +If left unset, the following values will be automatically set by the fuzzer. + - to: automatically set to the running node's NodeID + - from: automatically set to the running node's NodeID + - id: automatically set to the value of an incrementing counter + +Additionally, if `pki_encrypted` is populated in the packet, the first admin key +will be copied into the `public_key` field. +""" + +import base64 + +from meshtastic import BROADCAST_NUM +from meshtastic.protobuf import ( + admin_pb2, + atak_pb2, + mesh_pb2, + portnums_pb2, + telemetry_pb2, +) + + +def From(node: int = 9): + """Return a dict suitable for **kwargs for populating the 'from' field. + + 'from' is a reserved keyword in Python. It can't be used directly as an + argument to the MeshPacket constructor. Rather **From() can be used as + the final argument to provide the from node as a **kwarg. + + Defaults to 9 if no value is provided. + """ + return {"from": node} + + +packets = ( + ( + "position", + mesh_pb2.MeshPacket( + decoded=mesh_pb2.Data( + portnum=portnums_pb2.PortNum.POSITION_APP, + payload=mesh_pb2.Position( + latitude_i=int(1 * 1e7), + longitude_i=int(2 * 1e7), + altitude=5, + precision_bits=32, + ).SerializeToString(), + ), + to=BROADCAST_NUM, + **From(), + ), + ), + ( + "telemetry", + mesh_pb2.MeshPacket( + decoded=mesh_pb2.Data( + portnum=portnums_pb2.PortNum.TELEMETRY_APP, + payload=telemetry_pb2.Telemetry( + time=1736192207, + device_metrics=telemetry_pb2.DeviceMetrics( + battery_level=101, + channel_utilization=8, + air_util_tx=2, + uptime_seconds=42, + ), + ).SerializeToString(), + ), + to=BROADCAST_NUM, + **From(), + ), + ), + ( + "text", + mesh_pb2.MeshPacket( + decoded=mesh_pb2.Data( + portnum=portnums_pb2.PortNum.TEXT_MESSAGE_APP, + payload=b"Hello world", + ), + to=BROADCAST_NUM, + **From(), + ), + ), + ( + "user", + mesh_pb2.MeshPacket( + decoded=mesh_pb2.Data( + portnum=portnums_pb2.PortNum.NODEINFO_APP, + payload=mesh_pb2.User( + id="!00000009", + long_name="Node 9", + short_name="N9", + macaddr=b"\x00\x00\x00\x00\x00\x09", + hw_model=mesh_pb2.HardwareModel.RAK4631, + public_key=base64.b64decode( + "L0ih/6F41itofdE8mYyHk1SdfOJ/QRM1KQ+pO4vEEjQ=" + ), + ).SerializeToString(), + ), + **From(), + ), + ), + ( + "traceroute", + mesh_pb2.MeshPacket( + decoded=mesh_pb2.Data( + portnum=portnums_pb2.PortNum.TRACEROUTE_APP, + payload=mesh_pb2.RouteDiscovery( + route=[10], + ).SerializeToString(), + ), + **From(), + ), + ), + ( + "routing", + mesh_pb2.MeshPacket( + decoded=mesh_pb2.Data( + portnum=portnums_pb2.PortNum.ROUTING_APP, + payload=mesh_pb2.Routing( + error_reason=mesh_pb2.Routing.NO_RESPONSE, + ).SerializeToString(), + ), + **From(), + ), + ), + ( + "admin", + mesh_pb2.MeshPacket( + decoded=mesh_pb2.Data( + portnum=portnums_pb2.PortNum.ADMIN_APP, + payload=admin_pb2.AdminMessage( + get_owner_request=True, + ).SerializeToString(), + ), + pki_encrypted=True, + **From(), + ), + ), + ( + "atak", + mesh_pb2.MeshPacket( + decoded=mesh_pb2.Data( + portnum=portnums_pb2.PortNum.ATAK_PLUGIN, + payload=atak_pb2.TAKPacket( + is_compressed=True, + # Note, the strings are not valid for a compressed message, but will + # give the fuzzer a starting point. + contact=atak_pb2.Contact( + callsign="callsign", device_callsign="device_callsign" + ), + chat=atak_pb2.GeoChat( + message="message", to="to", to_callsign="to_callsign" + ), + ).SerializeToString(), + ), + **From(), + ), + ), +) + +for name, packet in packets: + with open(f"{name}.MeshPacket", "wb") as f: + f.write(packet.SerializeToString()) diff --git a/.dockerignore b/.dockerignore new file mode 120000 index 000000000..3e4e48b0b --- /dev/null +++ b/.dockerignore @@ -0,0 +1 @@ +.gitignore \ No newline at end of file diff --git a/platformio.ini b/platformio.ini index e6735ec23..217e75631 100644 --- a/platformio.ini +++ b/platformio.ini @@ -20,7 +20,7 @@ extra_scripts = bin/platformio-custom.py build_flags = -Wno-missing-field-initializers -Wno-format - -Isrc -Isrc/mesh -Isrc/mesh/generated -Isrc/gps -Isrc/buzz -Wl,-Map,.pio/build/output.map + -Isrc -Isrc/mesh -Isrc/mesh/generated -Isrc/gps -Isrc/buzz -Wl,-Map,${platformio.build_dir}/output.map -DUSE_THREAD_NAMES -DTINYGPS_OPTION_NO_CUSTOM_FIELDS -DPB_ENABLE_MALLOC=1 diff --git a/src/FSCommon.cpp b/src/FSCommon.cpp index 6d8ff835c..1f2994b29 100644 --- a/src/FSCommon.cpp +++ b/src/FSCommon.cpp @@ -203,7 +203,7 @@ std::vector getFiles(const char *dirname, uint8_t levels) file.close(); } } else { - meshtastic_FileInfo fileInfo = {"", file.size()}; + meshtastic_FileInfo fileInfo = {"", static_cast(file.size())}; #ifdef ARCH_ESP32 strcpy(fileInfo.file_name, file.path()); #else diff --git a/src/mesh/PacketHistory.h b/src/mesh/PacketHistory.h index 89d237a02..0417d0997 100644 --- a/src/mesh/PacketHistory.h +++ b/src/mesh/PacketHistory.h @@ -4,7 +4,11 @@ #include /// We clear our old flood record 10 minutes after we see the last of it +#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION +#define FLOOD_EXPIRE_TIME (5 * 1000L) // Don't allow too many packets to accumulate when fuzzing. +#else #define FLOOD_EXPIRE_TIME (10 * 60 * 1000L) +#endif /** * A record of a recent message broadcast diff --git a/src/mesh/TypedQueue.h b/src/mesh/TypedQueue.h index f7d016f10..47d7200a5 100644 --- a/src/mesh/TypedQueue.h +++ b/src/mesh/TypedQueue.h @@ -74,11 +74,17 @@ template class TypedQueue { std::queue q; concurrency::OSThread *reader = NULL; + int maxElements; public: - explicit TypedQueue(int maxElements) {} + explicit TypedQueue(int _maxElements) : maxElements(_maxElements) {} - int numFree() { return 1; } // Always claim 1 free, because we can grow to any size + int numFree() + { + if (maxElements <= 0) + return 1; // Always claim 1 free, because we can grow to any size + return maxElements - numUsed(); + } bool isEmpty() { return q.empty(); } @@ -86,6 +92,9 @@ template class TypedQueue bool enqueue(T x, TickType_t maxWait = portMAX_DELAY) { + if (numFree() <= 0) + return false; + if (reader) { reader->setInterval(0); concurrency::mainDelay.interrupt(); @@ -112,4 +121,4 @@ template class TypedQueue void setReader(concurrency::OSThread *t) { reader = t; } }; -#endif +#endif \ No newline at end of file diff --git a/src/platform/portduino/PortduinoGlue.cpp b/src/platform/portduino/PortduinoGlue.cpp index ce2418e86..5b1fe9c8c 100644 --- a/src/platform/portduino/PortduinoGlue.cpp +++ b/src/platform/portduino/PortduinoGlue.cpp @@ -21,6 +21,10 @@ #include #include +#ifdef PORTDUINO_LINUX_HARDWARE +#include +#endif + #include "platform/portduino/USBHal.h" std::map settingsMap; @@ -318,8 +322,8 @@ int initGPIOPin(int pinNum, const std::string gpioChipName, int line) gpioBind(csPin); return ERRNO_OK; } catch (...) { - std::exception_ptr p = std::current_exception(); - std::cout << "Warning, cannot claim pin " << gpio_name << (p ? p.__cxa_exception_type()->name() : "null") << std::endl; + const std::type_info *t = abi::__cxa_current_exception_type(); + std::cout << "Warning, cannot claim pin " << gpio_name << (t ? t->name() : "null") << std::endl; return ERRNO_DISABLED; } #else From e466bf247548b989d59d7f61d1fb716b3f6dafe4 Mon Sep 17 00:00:00 2001 From: Patrick Siegl <3261314+psiegl@users.noreply.github.com> Date: Fri, 17 Jan 2025 02:38:58 +0100 Subject: [PATCH 42/89] Slight rework of CH341 HAL (#5848) * Rework of CH341 HAL * Applied trunk fmt * revert serial reading --------- Co-authored-by: Ben Meadors Co-authored-by: Jonathan Bennett --- src/platform/portduino/PortduinoGlue.cpp | 17 ++-- src/platform/portduino/USBHal.h | 108 ++++++++--------------- 2 files changed, 45 insertions(+), 80 deletions(-) diff --git a/src/platform/portduino/PortduinoGlue.cpp b/src/platform/portduino/PortduinoGlue.cpp index 5b1fe9c8c..ab78baa1a 100644 --- a/src/platform/portduino/PortduinoGlue.cpp +++ b/src/platform/portduino/PortduinoGlue.cpp @@ -200,15 +200,12 @@ void portduinoSetup() // if we're using a usermode driver, we need to initialize it here, to get a serial number back for mac address uint8_t dmac[6] = {0}; if (settingsStrings[spidev] == "ch341") { - ch341Hal = new Ch341Hal(0); - if (settingsStrings[lora_usb_serial_num] != "") { - ch341Hal->serial = settingsStrings[lora_usb_serial_num]; - } - ch341Hal->vid = settingsMap[lora_usb_vid]; - ch341Hal->pid = settingsMap[lora_usb_pid]; - ch341Hal->init(); - if (!ch341Hal->isInit()) { - std::cout << "Could not initialize CH341 device!" << std::endl; + try { + ch341Hal = + new Ch341Hal(0, settingsStrings[lora_usb_serial_num], settingsMap[lora_usb_vid], settingsMap[lora_usb_pid]); + } catch (std::exception &e) { + std::cerr << e.what() << std::endl; + std::cerr << "Could not initialize CH341 device!" << std::endl; exit(EXIT_FAILURE); } char serial[9] = {0}; @@ -572,4 +569,4 @@ bool MAC_from_string(std::string mac_str, uint8_t *dmac) } else { return false; } -} \ No newline at end of file +} diff --git a/src/platform/portduino/USBHal.h b/src/platform/portduino/USBHal.h index 064f7ae36..0d6b361f4 100644 --- a/src/platform/portduino/USBHal.h +++ b/src/platform/portduino/USBHal.h @@ -5,6 +5,7 @@ #include "platform/portduino/PortduinoGlue.h" #include #include +#include #include #include @@ -26,30 +27,42 @@ class Ch341Hal : public RadioLibHal { public: // default constructor - initializes the base HAL and any needed private members - explicit Ch341Hal(uint8_t spiChannel, uint32_t spiSpeed = 2000000, uint8_t spiDevice = 0, uint8_t gpioDevice = 0) + explicit Ch341Hal(uint8_t spiChannel, std::string serial = "", uint32_t vid = 0x1A86, uint32_t pid = 0x5512, + uint32_t spiSpeed = 2000000, uint8_t spiDevice = 0, uint8_t gpioDevice = 0) : RadioLibHal(PI_INPUT, PI_OUTPUT, PI_LOW, PI_HIGH, PI_RISING, PI_FALLING) { + if (serial != "") { + strncpy(pinedio.serial_number, serial.c_str(), 8); + pinedio_set_option(&pinedio, PINEDIO_OPTION_SEARCH_SERIAL, 1); + } + // LOG_INFO("USB Serial: %s", pinedio.serial_number); + + // There is no vendor with 0x0 -> so check + if (vid != 0x0) { + pinedio_set_option(&pinedio, PINEDIO_OPTION_VID, vid); + pinedio_set_option(&pinedio, PINEDIO_OPTION_PID, pid); + } + int32_t ret = pinedio_init(&pinedio, NULL); + if (ret != 0) { + std::string s = "Could not open SPI: "; + throw(s + std::to_string(ret)); + } + + pinedio_set_option(&pinedio, PINEDIO_OPTION_AUTO_CS, 0); + pinedio_set_pin_mode(&pinedio, 3, true); + pinedio_set_pin_mode(&pinedio, 5, true); } + ~Ch341Hal() { pinedio_deinit(&pinedio); } + void getSerialString(char *_serial, size_t len) { - if (!pinedio_is_init) { - return; - } + len = len > 8 ? 8 : len; strncpy(_serial, pinedio.serial_number, len); } - void init() override - { - // now the SPI - spiBegin(); - } - - void term() override - { - // stop the SPI - spiEnd(); - } + void init() override {} + void term() override {} // GPIO-related methods (pinMode, digitalWrite etc.) should check // RADIOLIB_NC as an alias for non-connected pins @@ -79,7 +92,7 @@ class Ch341Hal : public RadioLibHal void attachInterrupt(uint32_t interruptNum, void (*interruptCb)(void), uint32_t mode) override { - if ((interruptNum == RADIOLIB_NC)) { + if (interruptNum == RADIOLIB_NC) { return; } // LOG_DEBUG("Attach interrupt to pin %d", interruptNum); @@ -88,23 +101,14 @@ class Ch341Hal : public RadioLibHal void detachInterrupt(uint32_t interruptNum) override { - if ((interruptNum == RADIOLIB_NC)) { + if (interruptNum == RADIOLIB_NC) { return; } // LOG_DEBUG("Detach interrupt from pin %d", interruptNum); - pinedio_deattach_interrupt(&this->pinedio, (pinedio_int_pin)interruptNum); } - void delay(unsigned long ms) override - { - if (ms == 0) { - sched_yield(); - return; - } - - usleep(ms * 1000); - } + void delay(unsigned long ms) override { delayMicroseconds(ms * 1000); } void delayMicroseconds(unsigned long us) override { @@ -133,62 +137,26 @@ class Ch341Hal : public RadioLibHal long pulseIn(uint32_t pin, uint32_t state, unsigned long timeout) override { - fprintf(stderr, "pulseIn for pin %u is not supported!\n", pin); + std::cerr << "pulseIn for pin " << pin << "is not supported!" << std::endl; return 0; } - void spiBegin() - { - if (!pinedio_is_init) { - if (serial != "") { - strncpy(pinedio.serial_number, serial.c_str(), 8); - pinedio_set_option(&pinedio, PINEDIO_OPTION_SEARCH_SERIAL, 1); - } - pinedio_set_option(&pinedio, PINEDIO_OPTION_PID, pid); - pinedio_set_option(&pinedio, PINEDIO_OPTION_VID, vid); - int32_t ret = pinedio_init(&pinedio, NULL); - if (ret != 0) { - fprintf(stderr, "Could not open SPI: %d\n", ret); - } else { - pinedio_is_init = true; - // LOG_INFO("USB Serial: %s", pinedio.serial_number); - pinedio_set_option(&pinedio, PINEDIO_OPTION_AUTO_CS, 0); - pinedio_set_pin_mode(&pinedio, 3, true); - pinedio_set_pin_mode(&pinedio, 5, true); - } - } - } - + void spiBegin() {} void spiBeginTransaction() {} void spiTransfer(uint8_t *out, size_t len, uint8_t *in) { - int32_t result = pinedio_transceive(&this->pinedio, out, in, len); - if (result < 0) { - fprintf(stderr, "Could not perform SPI transfer: %d\n", result); + int32_t ret = pinedio_transceive(&this->pinedio, out, in, len); + if (ret < 0) { + std::cerr << "Could not perform SPI transfer: " << ret << std::endl; } } void spiEndTransaction() {} - - void spiEnd() - { - if (pinedio_is_init) { - pinedio_deinit(&pinedio); - pinedio_is_init = false; - } - } - - bool isInit() { return pinedio_is_init; } - - std::string serial = ""; - uint32_t pid = 0x5512; - uint32_t vid = 0x1A86; + void spiEnd() {} private: - // the HAL can contain any additional private members pinedio_inst pinedio = {0}; - bool pinedio_is_init = false; }; -#endif \ No newline at end of file +#endif From 9566d6ffd47ca858607809951a58afc12285541c Mon Sep 17 00:00:00 2001 From: Austin Date: Thu, 16 Jan 2025 21:21:52 -0500 Subject: [PATCH 43/89] COPR: Switch to forked GitHub Action (#5871) --- .github/workflows/hook_copr.yml | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/.github/workflows/hook_copr.yml b/.github/workflows/hook_copr.yml index a72ae5cf5..fd2c3014b 100644 --- a/.github/workflows/hook_copr.yml +++ b/.github/workflows/hook_copr.yml @@ -25,12 +25,8 @@ jobs: ref: ${{ github.ref }} repository: ${{ github.repository }} - - name: Install Python dependencies - run: | - pip install requests - - name: Trigger COPR build - uses: akdev1l/copr-build@main + uses: vidplace7/copr-build@main id: copr_build env: COPR_API_TOKEN_CONFIG: ${{ secrets.COPR_API_CONFIG }} From 2262d77be4372e655a1fe1b10202fd008fc4977d Mon Sep 17 00:00:00 2001 From: Austin Date: Thu, 16 Jan 2025 23:27:49 -0500 Subject: [PATCH 44/89] Small fix: Reference COPR group correctly (`@`) (#5872) --- .github/workflows/hook_copr.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/hook_copr.yml b/.github/workflows/hook_copr.yml index fd2c3014b..94aaca49c 100644 --- a/.github/workflows/hook_copr.yml +++ b/.github/workflows/hook_copr.yml @@ -31,7 +31,7 @@ jobs: env: COPR_API_TOKEN_CONFIG: ${{ secrets.COPR_API_CONFIG }} with: - owner: meshtastic + owner: "@meshtastic" package-name: meshtasticd project-name: ${{ inputs.copr_project }} git-remote: "${{ github.server_url }}/${{ github.repository }}.git" From c4051c1a7b524051767f4aa3037cf99f570184c0 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sat, 18 Jan 2025 13:32:09 +0100 Subject: [PATCH 45/89] [create-pull-request] automated change (#5877) Co-authored-by: caveman99 <25002+caveman99@users.noreply.github.com> --- protobufs | 2 +- src/mesh/generated/meshtastic/telemetry.pb.h | 28 ++++++++++++++------ 2 files changed, 21 insertions(+), 9 deletions(-) diff --git a/protobufs b/protobufs index 76f806e1b..fde27e4ef 160000 --- a/protobufs +++ b/protobufs @@ -1 +1 @@ -Subproject commit 76f806e1bb1e2a7b157a14fadd095775f63db5e4 +Subproject commit fde27e4ef0fcee967063ba353422ed5f9a1c4790 diff --git a/src/mesh/generated/meshtastic/telemetry.pb.h b/src/mesh/generated/meshtastic/telemetry.pb.h index 85fe4bdc1..bb612d870 100644 --- a/src/mesh/generated/meshtastic/telemetry.pb.h +++ b/src/mesh/generated/meshtastic/telemetry.pb.h @@ -81,7 +81,9 @@ typedef enum _meshtastic_TelemetrySensorType { /* ClimateGuard RadSens, radiation, Geiger-Muller Tube */ meshtastic_TelemetrySensorType_RADSENS = 33, /* High accuracy current and voltage */ - meshtastic_TelemetrySensorType_INA226 = 34 + meshtastic_TelemetrySensorType_INA226 = 34, + /* DFRobot Gravity tipping bucket rain gauge */ + meshtastic_TelemetrySensorType_DFROBOT_RAIN = 35 } meshtastic_TelemetrySensorType; /* Struct definitions */ @@ -162,6 +164,12 @@ typedef struct _meshtastic_EnvironmentMetrics { /* Radiation in µR/h */ bool has_radiation; float radiation; + /* Rainfall in the last hour in mm */ + bool has_rainfall_1h; + float rainfall_1h; + /* Rainfall in the last 24 hours in mm */ + bool has_rainfall_24h; + float rainfall_24h; } meshtastic_EnvironmentMetrics; /* Power Metrics (voltage / current / etc) */ @@ -306,8 +314,8 @@ extern "C" { /* Helper constants for enums */ #define _meshtastic_TelemetrySensorType_MIN meshtastic_TelemetrySensorType_SENSOR_UNSET -#define _meshtastic_TelemetrySensorType_MAX meshtastic_TelemetrySensorType_INA226 -#define _meshtastic_TelemetrySensorType_ARRAYSIZE ((meshtastic_TelemetrySensorType)(meshtastic_TelemetrySensorType_INA226+1)) +#define _meshtastic_TelemetrySensorType_MAX meshtastic_TelemetrySensorType_DFROBOT_RAIN +#define _meshtastic_TelemetrySensorType_ARRAYSIZE ((meshtastic_TelemetrySensorType)(meshtastic_TelemetrySensorType_DFROBOT_RAIN+1)) @@ -320,7 +328,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, 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, 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} @@ -328,7 +336,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, 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, 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} @@ -360,6 +368,8 @@ extern "C" { #define meshtastic_EnvironmentMetrics_wind_gust_tag 16 #define meshtastic_EnvironmentMetrics_wind_lull_tag 17 #define meshtastic_EnvironmentMetrics_radiation_tag 18 +#define meshtastic_EnvironmentMetrics_rainfall_1h_tag 19 +#define meshtastic_EnvironmentMetrics_rainfall_24h_tag 20 #define meshtastic_PowerMetrics_ch1_voltage_tag 1 #define meshtastic_PowerMetrics_ch1_current_tag 2 #define meshtastic_PowerMetrics_ch2_voltage_tag 3 @@ -431,7 +441,9 @@ 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, radiation, 18) +X(a, STATIC, OPTIONAL, FLOAT, radiation, 18) \ +X(a, STATIC, OPTIONAL, FLOAT, rainfall_1h, 19) \ +X(a, STATIC, OPTIONAL, FLOAT, rainfall_24h, 20) #define meshtastic_EnvironmentMetrics_CALLBACK NULL #define meshtastic_EnvironmentMetrics_DEFAULT NULL @@ -530,12 +542,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 91 +#define meshtastic_EnvironmentMetrics_size 103 #define meshtastic_HealthMetrics_size 11 #define meshtastic_LocalStats_size 60 #define meshtastic_Nau7802Config_size 16 #define meshtastic_PowerMetrics_size 30 -#define meshtastic_Telemetry_size 98 +#define meshtastic_Telemetry_size 110 #ifdef __cplusplus } /* extern "C" */ From b353bcc04acf9eeab7d2f700e7ee977a6a0d7582 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20G=C3=B6ttgens?= Date: Sat, 18 Jan 2025 14:10:13 +0100 Subject: [PATCH 46/89] fix detection of lark weather station and add rain sensor (#5874) * fix detection of lark weather station * fix unit tests and add support for Dfrobot rain gauge * fix name display on bootup * fix gauge init logic * trunk fmt --- platformio.ini | 1 + src/configuration.h | 1 + src/detect/ScanI2C.h | 1 + src/detect/ScanI2CTwoWire.cpp | 17 +++++-- src/detect/ScanI2CTwoWire.h | 2 +- src/main.cpp | 1 + .../Telemetry/EnvironmentTelemetry.cpp | 13 ++++++ .../Telemetry/Sensor/DFRobotGravitySensor.cpp | 44 +++++++++++++++++++ .../Telemetry/Sensor/DFRobotGravitySensor.h | 29 ++++++++++++ 9 files changed, 105 insertions(+), 4 deletions(-) create mode 100644 src/modules/Telemetry/Sensor/DFRobotGravitySensor.cpp create mode 100644 src/modules/Telemetry/Sensor/DFRobotGravitySensor.h diff --git a/platformio.ini b/platformio.ini index 217e75631..ea4de4db1 100644 --- a/platformio.ini +++ b/platformio.ini @@ -126,6 +126,7 @@ lib_deps = mprograms/QMC5883LCompass@1.2.3 dfrobot/DFRobot_RTU@1.0.3 https://github.com/meshtastic/DFRobot_LarkWeatherStation#4de3a9cadef0f6a5220a8a906cf9775b02b0040d + https://github.com/DFRobot/DFRobot_RainfallSensor#38fea5e02b40a5430be6dab39a99a6f6347d667e robtillaart/INA226@0.6.0 ; Health Sensor Libraries diff --git a/src/configuration.h b/src/configuration.h index 2c77b55e3..6f5255ec9 100644 --- a/src/configuration.h +++ b/src/configuration.h @@ -145,6 +145,7 @@ along with this program. If not, see . #define OPT3001_ADDR_ALT 0x44 #define MLX90632_ADDR 0x3A #define DFROBOT_LARK_ADDR 0x42 +#define DFROBOT_RAIN_ADDR 0x1d #define NAU7802_ADDR 0x2A #define MAX30102_ADDR 0x57 #define MLX90614_ADDR_DEF 0x5A diff --git a/src/detect/ScanI2C.h b/src/detect/ScanI2C.h index 2561a8e17..faa94c7d3 100644 --- a/src/detect/ScanI2C.h +++ b/src/detect/ScanI2C.h @@ -66,6 +66,7 @@ class ScanI2C CGRADSENS, INA226, NXP_SE050, + DFROBOT_RAIN, } DeviceType; // typedef uint8_t DeviceAddress; diff --git a/src/detect/ScanI2CTwoWire.cpp b/src/detect/ScanI2CTwoWire.cpp index a786f874d..880e5c131 100644 --- a/src/detect/ScanI2CTwoWire.cpp +++ b/src/detect/ScanI2CTwoWire.cpp @@ -84,23 +84,33 @@ ScanI2C::DeviceType ScanI2CTwoWire::probeOLED(ScanI2C::DeviceAddress addr) const return o_probe; } uint16_t ScanI2CTwoWire::getRegisterValue(const ScanI2CTwoWire::RegisterLocation ®isterLocation, - ScanI2CTwoWire::ResponseWidth responseWidth) const + ScanI2CTwoWire::ResponseWidth responseWidth, bool zeropad = false) const { uint16_t value = 0x00; TwoWire *i2cBus = fetchI2CBus(registerLocation.i2cAddress); i2cBus->beginTransmission(registerLocation.i2cAddress.address); i2cBus->write(registerLocation.registerAddress); + if (zeropad) { + // Lark Commands need the argument list length in 2 bytes. + i2cBus->write((int)0); + i2cBus->write((int)0); + } i2cBus->endTransmission(); delay(20); i2cBus->requestFrom(registerLocation.i2cAddress.address, responseWidth); - if (i2cBus->available() == 2) { + if (i2cBus->available() > 1) { // Read MSB, then LSB value = (uint16_t)i2cBus->read() << 8; value |= i2cBus->read(); } else if (i2cBus->available()) { value = i2cBus->read(); } + // Drain excess bytes + for (uint8_t i = 0; i < responseWidth - 1; i++) { + if (i2cBus->available()) + i2cBus->read(); + } return value; } @@ -286,7 +296,7 @@ void ScanI2CTwoWire::scanPort(I2CPort port, uint8_t *address, uint8_t asize) RESPONSE_PAYLOAD 0x01 RESPONSE_PAYLOAD+1 0x00 */ - registerValue = getRegisterValue(ScanI2CTwoWire::RegisterLocation(addr, 0x05), 2); + registerValue = getRegisterValue(ScanI2CTwoWire::RegisterLocation(addr, 0x05), 6, true); LOG_DEBUG("Register MFG_UID 05: 0x%x", registerValue); if (registerValue == 0x5305) { logFoundDevice("DFRobot Lark", (uint8_t)addr.address); @@ -402,6 +412,7 @@ void ScanI2CTwoWire::scanPort(I2CPort port, uint8_t *address, uint8_t asize) 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(MAX1704X_ADDR, MAX17048, "MAX17048", (uint8_t)addr.address); + SCAN_SIMPLE_CASE(DFROBOT_RAIN_ADDR, DFROBOT_RAIN, "DFRobot Rain Gauge", (uint8_t)addr.address); #ifdef HAS_TPS65233 SCAN_SIMPLE_CASE(TPS65233_ADDR, TPS65233, "TPS65233", (uint8_t)addr.address); #endif diff --git a/src/detect/ScanI2CTwoWire.h b/src/detect/ScanI2CTwoWire.h index d0af7cde6..6988091ad 100644 --- a/src/detect/ScanI2CTwoWire.h +++ b/src/detect/ScanI2CTwoWire.h @@ -53,7 +53,7 @@ class ScanI2CTwoWire : public ScanI2C concurrency::Lock lock; - uint16_t getRegisterValue(const RegisterLocation &, ResponseWidth) const; + uint16_t getRegisterValue(const RegisterLocation &, ResponseWidth, bool) const; DeviceType probeOLED(ScanI2C::DeviceAddress) const; diff --git a/src/main.cpp b/src/main.cpp index fc9d24e37..24fc71749 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -610,6 +610,7 @@ void setup() scannerToSensorsMap(i2cScanner, ScanI2C::DeviceType::ICM20948, meshtastic_TelemetrySensorType_ICM20948); scannerToSensorsMap(i2cScanner, ScanI2C::DeviceType::MAX30102, meshtastic_TelemetrySensorType_MAX30102); scannerToSensorsMap(i2cScanner, ScanI2C::DeviceType::CGRADSENS, meshtastic_TelemetrySensorType_RADSENS); + scannerToSensorsMap(i2cScanner, ScanI2C::DeviceType::DFROBOT_RAIN, meshtastic_TelemetrySensorType_DFROBOT_RAIN); i2cScanner.reset(); #endif diff --git a/src/modules/Telemetry/EnvironmentTelemetry.cpp b/src/modules/Telemetry/EnvironmentTelemetry.cpp index fe1d0d2a9..1af6347f2 100644 --- a/src/modules/Telemetry/EnvironmentTelemetry.cpp +++ b/src/modules/Telemetry/EnvironmentTelemetry.cpp @@ -27,6 +27,7 @@ #include "Sensor/BMP280Sensor.h" #include "Sensor/BMP3XXSensor.h" #include "Sensor/CGRadSensSensor.h" +#include "Sensor/DFRobotGravitySensor.h" #include "Sensor/DFRobotLarkSensor.h" #include "Sensor/LPS22HBSensor.h" #include "Sensor/MCP9808Sensor.h" @@ -56,6 +57,7 @@ RCWL9620Sensor rcwl9620Sensor; AHT10Sensor aht10Sensor; MLX90632Sensor mlx90632Sensor; DFRobotLarkSensor dfRobotLarkSensor; +DFRobotGravitySensor dfRobotGravitySensor; NAU7802Sensor nau7802Sensor; BMP3XXSensor bmp3xxSensor; CGRadSensSensor cgRadSens; @@ -115,6 +117,8 @@ int32_t EnvironmentTelemetryModule::runOnce() #elif !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR_EXTERNAL if (dfRobotLarkSensor.hasSensor()) result = dfRobotLarkSensor.runOnce(); + if (dfRobotGravitySensor.hasSensor()) + result = dfRobotGravitySensor.runOnce(); if (bmp085Sensor.hasSensor()) result = bmp085Sensor.runOnce(); if (bmp280Sensor.hasSensor()) @@ -368,6 +372,10 @@ bool EnvironmentTelemetryModule::getEnvironmentTelemetry(meshtastic_Telemetry *m valid = valid && dfRobotLarkSensor.getMetrics(m); hasSensor = true; } + if (dfRobotGravitySensor.hasSensor()) { + valid = valid && dfRobotGravitySensor.getMetrics(m); + hasSensor = true; + } if (sht31Sensor.hasSensor()) { valid = valid && sht31Sensor.getMetrics(m); hasSensor = true; @@ -569,6 +577,11 @@ AdminMessageHandleResult EnvironmentTelemetryModule::handleAdminMessageForModule if (result != AdminMessageHandleResult::NOT_HANDLED) return result; } + if (dfRobotGravitySensor.hasSensor()) { + result = dfRobotGravitySensor.handleAdminMessage(mp, request, response); + if (result != AdminMessageHandleResult::NOT_HANDLED) + return result; + } if (sht31Sensor.hasSensor()) { result = sht31Sensor.handleAdminMessage(mp, request, response); if (result != AdminMessageHandleResult::NOT_HANDLED) diff --git a/src/modules/Telemetry/Sensor/DFRobotGravitySensor.cpp b/src/modules/Telemetry/Sensor/DFRobotGravitySensor.cpp new file mode 100644 index 000000000..c7fa29966 --- /dev/null +++ b/src/modules/Telemetry/Sensor/DFRobotGravitySensor.cpp @@ -0,0 +1,44 @@ +#include "configuration.h" + +#if !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR + +#include "../mesh/generated/meshtastic/telemetry.pb.h" +#include "DFRobotGravitySensor.h" +#include "TelemetrySensor.h" +#include +#include + +DFRobotGravitySensor::DFRobotGravitySensor() : TelemetrySensor(meshtastic_TelemetrySensorType_DFROBOT_RAIN, "DFROBOT_RAIN") {} + +int32_t DFRobotGravitySensor::runOnce() +{ + LOG_INFO("Init sensor: %s", sensorName); + if (!hasSensor()) { + return DEFAULT_SENSOR_MINIMUM_WAIT_TIME_BETWEEN_READS; + } + + gravity = DFRobot_RainfallSensor_I2C(nodeTelemetrySensorsMap[sensorType].second); + status = gravity.begin(); + + return initI2CSensor(); +} + +void DFRobotGravitySensor::setup() +{ + LOG_DEBUG("%s VID: %x, PID: %x, Version: %s", sensorName, gravity.vid, gravity.pid, gravity.getFirmwareVersion().c_str()); +} + +bool DFRobotGravitySensor::getMetrics(meshtastic_Telemetry *measurement) +{ + measurement->variant.environment_metrics.has_rainfall_1h = true; + measurement->variant.environment_metrics.has_rainfall_24h = true; + + measurement->variant.environment_metrics.rainfall_1h = gravity.getRainfall(1); + measurement->variant.environment_metrics.rainfall_24h = gravity.getRainfall(24); + + LOG_INFO("Rain 1h: %f mm", measurement->variant.environment_metrics.rainfall_1h); + LOG_INFO("Rain 24h: %f mm", measurement->variant.environment_metrics.rainfall_24h); + return true; +} + +#endif \ No newline at end of file diff --git a/src/modules/Telemetry/Sensor/DFRobotGravitySensor.h b/src/modules/Telemetry/Sensor/DFRobotGravitySensor.h new file mode 100644 index 000000000..8bd7335b5 --- /dev/null +++ b/src/modules/Telemetry/Sensor/DFRobotGravitySensor.h @@ -0,0 +1,29 @@ +#pragma once + +#ifndef _MT_DFROBOTGRAVITYSENSOR_H +#define _MT_DFROBOTGRAVITYSENSOR_H +#include "configuration.h" + +#if !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR + +#include "../mesh/generated/meshtastic/telemetry.pb.h" +#include "TelemetrySensor.h" +#include +#include + +class DFRobotGravitySensor : public TelemetrySensor +{ + private: + DFRobot_RainfallSensor_I2C gravity = DFRobot_RainfallSensor_I2C(nodeTelemetrySensorsMap[sensorType].second); + + protected: + virtual void setup() override; + + public: + DFRobotGravitySensor(); + virtual int32_t runOnce() override; + virtual bool getMetrics(meshtastic_Telemetry *measurement) override; +}; + +#endif +#endif \ No newline at end of file From 950341d1f980369b58cc3d63dac6b036effc0270 Mon Sep 17 00:00:00 2001 From: Ben Meadors Date: Sat, 18 Jan 2025 08:15:06 -0600 Subject: [PATCH 47/89] Alert app messages should be treated as text (#5878) --- src/mesh/MeshService.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/mesh/MeshService.h b/src/mesh/MeshService.h index 175d8a595..42f701d5c 100644 --- a/src/mesh/MeshService.h +++ b/src/mesh/MeshService.h @@ -64,7 +64,8 @@ class MeshService return true; } return p->decoded.portnum == meshtastic_PortNum_TEXT_MESSAGE_APP || - p->decoded.portnum == meshtastic_PortNum_DETECTION_SENSOR_APP; + p->decoded.portnum == meshtastic_PortNum_DETECTION_SENSOR_APP || + p->decoded.portnum == meshtastic_PortNum_ALERT_APP; } /// Called when some new packets have arrived from one of the radios Observable fromNumChanged; From 973b453d43ba5d9d4239dabff56cab676e578e9e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20G=C3=B6ttgens?= Date: Mon, 20 Jan 2025 09:34:54 +0100 Subject: [PATCH 48/89] Update RAK2560 code (#5844) * * Update RAK9154 sensor to tx remote power telemetry * remove uf2 script, pio run does that inline * move sensor module to correct position * disable LED and Accelerometer code on rak2560 * trunk fmt * mention epaper variant * attention, revert, revert * Enable Environment Telemetry of these values * fix float values --- src/Power.cpp | 10 +- .../Telemetry/EnvironmentTelemetry.cpp | 10 ++ src/modules/Telemetry/PowerTelemetry.cpp | 16 +-- .../Telemetry/Sensor}/RAK9154Sensor.cpp | 48 ++++++-- .../modules/Telemetry/Sensor}/RAK9154Sensor.h | 15 ++- src/motion/BMX160Sensor.cpp | 2 +- src/motion/BMX160Sensor.h | 2 +- src/power.h | 5 +- variants/rak2560/create_uf2.py | 113 ------------------ variants/rak2560/platformio.ini | 2 - 10 files changed, 76 insertions(+), 147 deletions(-) rename {variants/rak2560 => src/modules/Telemetry/Sensor}/RAK9154Sensor.cpp (76%) rename {variants/rak2560 => src/modules/Telemetry/Sensor}/RAK9154Sensor.h (55%) delete mode 100644 variants/rak2560/create_uf2.py diff --git a/src/Power.cpp b/src/Power.cpp index ae0908ec6..8d5fe1c32 100644 --- a/src/Power.cpp +++ b/src/Power.cpp @@ -87,7 +87,7 @@ MAX17048Sensor max17048Sensor; #endif #endif -#if HAS_RAKPROT && !defined(ARCH_PORTDUINO) +#if HAS_TELEMETRY && !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR && HAS_RAKPROT && !defined(ARCH_PORTDUINO) RAK9154Sensor rak9154Sensor; #endif @@ -243,7 +243,8 @@ class AnalogBatteryLevel : public HasBatteryLevel virtual uint16_t getBattVoltage() override { -#if defined(HAS_RAKPROT) && !defined(ARCH_PORTDUINO) && !defined(HAS_PMU) +#if HAS_TELEMETRY && defined(HAS_RAKPROT) && !defined(ARCH_PORTDUINO) && !defined(HAS_PMU) && \ + !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR if (hasRAK()) { return getRAKVoltage(); } @@ -406,7 +407,8 @@ class AnalogBatteryLevel : public HasBatteryLevel /// we can't be smart enough to say 'full'? virtual bool isCharging() override { -#if defined(HAS_RAKPROT) && !defined(ARCH_PORTDUINO) && !defined(HAS_PMU) +#if HAS_TELEMETRY && !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR && defined(HAS_RAKPROT) && !defined(ARCH_PORTDUINO) && \ + !defined(HAS_PMU) if (hasRAK()) { return (rak9154Sensor.isCharging()) ? OptTrue : OptFalse; } @@ -447,7 +449,7 @@ class AnalogBatteryLevel : public HasBatteryLevel float last_read_value = (OCV[NUM_OCV_POINTS - 1] * NUM_CELLS); uint32_t last_read_time_ms = 0; -#if defined(HAS_RAKPROT) +#if HAS_TELEMETRY && !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR && defined(HAS_RAKPROT) uint16_t getRAKVoltage() { return rak9154Sensor.getBusVoltageMv(); } diff --git a/src/modules/Telemetry/EnvironmentTelemetry.cpp b/src/modules/Telemetry/EnvironmentTelemetry.cpp index 1af6347f2..6a5e8376d 100644 --- a/src/modules/Telemetry/EnvironmentTelemetry.cpp +++ b/src/modules/Telemetry/EnvironmentTelemetry.cpp @@ -163,6 +163,12 @@ int32_t EnvironmentTelemetryModule::runOnce() result = max17048Sensor.runOnce(); if (cgRadSens.hasSensor()) result = cgRadSens.runOnce(); + // this only works on the wismesh hub with the solar option. This is not an I2C sensor, so we don't need the + // sensormap here. +#ifdef HAS_RAKPROT + + result = rak9154Sensor.runOnce(); +#endif #endif } return result; @@ -480,6 +486,10 @@ bool EnvironmentTelemetryModule::getEnvironmentTelemetry(meshtastic_Telemetry *m valid = valid && cgRadSens.getMetrics(m); hasSensor = true; } +#ifdef HAS_RAKPROT + valid = valid && rak9154Sensor.getMetrics(m); + hasSensor = true; +#endif #endif return valid && hasSensor; } diff --git a/src/modules/Telemetry/PowerTelemetry.cpp b/src/modules/Telemetry/PowerTelemetry.cpp index 9c794e31e..38a5c6f11 100644 --- a/src/modules/Telemetry/PowerTelemetry.cpp +++ b/src/modules/Telemetry/PowerTelemetry.cpp @@ -100,7 +100,7 @@ void PowerTelemetryModule::drawFrame(OLEDDisplay *display, OLEDDisplayUiState *s { display->setTextAlignment(TEXT_ALIGN_LEFT); display->setFont(FONT_SMALL); - + if (lastMeasurementPacket == nullptr) { // In case of no valid packet, display "Power Telemetry", "No measurement" display->drawString(x, y, "Power Telemetry"); @@ -121,23 +121,23 @@ void PowerTelemetryModule::drawFrame(OLEDDisplay *display, OLEDDisplayUiState *s } // Display "Pow. From: ..." - display->drawString(x, y, "Pow. From: " + String(lastSender) + "(" + String(agoSecs) + "s)"); + display->drawString(x, y, "Pow. From: " + String(lastSender) + "(" + String(agoSecs) + "s)"); // Display current and voltage based on ...power_metrics.has_[channel/voltage/current]... flags if (lastMeasurement.variant.power_metrics.has_ch1_voltage || lastMeasurement.variant.power_metrics.has_ch1_current) { display->drawString(x, y += _fontHeight(FONT_SMALL), - "Ch1: " + String(lastMeasurement.variant.power_metrics.ch1_voltage, 2) + - "V " + String(lastMeasurement.variant.power_metrics.ch1_current, 0) + "mA"); + "Ch1: " + String(lastMeasurement.variant.power_metrics.ch1_voltage, 2) + "V " + + String(lastMeasurement.variant.power_metrics.ch1_current, 0) + "mA"); } if (lastMeasurement.variant.power_metrics.has_ch2_voltage || lastMeasurement.variant.power_metrics.has_ch2_current) { display->drawString(x, y += _fontHeight(FONT_SMALL), - "Ch2: " + String(lastMeasurement.variant.power_metrics.ch2_voltage, 2) + - "V " + String(lastMeasurement.variant.power_metrics.ch2_current, 0) + "mA"); + "Ch2: " + String(lastMeasurement.variant.power_metrics.ch2_voltage, 2) + "V " + + String(lastMeasurement.variant.power_metrics.ch2_current, 0) + "mA"); } if (lastMeasurement.variant.power_metrics.has_ch3_voltage || lastMeasurement.variant.power_metrics.has_ch3_current) { display->drawString(x, y += _fontHeight(FONT_SMALL), - "Ch3: " + String(lastMeasurement.variant.power_metrics.ch3_voltage, 2) + - "V " + String(lastMeasurement.variant.power_metrics.ch3_current, 0) + "mA"); + "Ch3: " + String(lastMeasurement.variant.power_metrics.ch3_voltage, 2) + "V " + + String(lastMeasurement.variant.power_metrics.ch3_current, 0) + "mA"); } } diff --git a/variants/rak2560/RAK9154Sensor.cpp b/src/modules/Telemetry/Sensor/RAK9154Sensor.cpp similarity index 76% rename from variants/rak2560/RAK9154Sensor.cpp rename to src/modules/Telemetry/Sensor/RAK9154Sensor.cpp index 43affe581..ad3925f08 100644 --- a/variants/rak2560/RAK9154Sensor.cpp +++ b/src/modules/Telemetry/Sensor/RAK9154Sensor.cpp @@ -1,9 +1,10 @@ -#ifdef HAS_RAKPROT -#include "../variants/rak2560/RAK9154Sensor.h" -#include "../mesh/generated/meshtastic/telemetry.pb.h" -#include "../modules/Telemetry/Sensor/TelemetrySensor.h" #include "configuration.h" +#if !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR && defined(HAS_RAKPROT) + +#include "../mesh/generated/meshtastic/telemetry.pb.h" +#include "RAK9154Sensor.h" +#include "TelemetrySensor.h" #include "concurrency/Periodic.h" #include @@ -25,6 +26,8 @@ static uint16_t dc_vol = 0; static uint8_t dc_prec = 0; static uint8_t provision = 0; +extern RAK9154Sensor rak9154Sensor; + static void onewire_evt(const uint8_t pid, const uint8_t sid, const SNHUBAPI_EVT_E eid, uint8_t *msg, uint16_t len) { switch (eid) { @@ -78,6 +81,7 @@ static void onewire_evt(const uint8_t pid, const uint8_t sid, const SNHUBAPI_EVT default: break; } + rak9154Sensor.setLastRead(millis()); break; case SNHUBAPI_EVT_REPORT: @@ -106,6 +110,7 @@ static void onewire_evt(const uint8_t pid, const uint8_t sid, const SNHUBAPI_EVT default: break; } + rak9154Sensor.setLastRead(millis()); break; @@ -145,15 +150,18 @@ static int32_t onewireHandle() int32_t RAK9154Sensor::runOnce() { - onewirePeriodic = new Periodic("onewireHandle", onewireHandle); + if (!rak9154Sensor.isInitialized()) { + onewirePeriodic = new Periodic("onewireHandle", onewireHandle); - mySerial.begin(9600); + mySerial.begin(9600); - RakSNHub_Protocl_API.init(onewire_evt); + RakSNHub_Protocl_API.init(onewire_evt); - status = true; - initialized = true; - return 0; + status = true; + initialized = true; + } + + return DEFAULT_SENSOR_MINIMUM_WAIT_TIME_BETWEEN_READS; } void RAK9154Sensor::setup() @@ -163,7 +171,16 @@ void RAK9154Sensor::setup() bool RAK9154Sensor::getMetrics(meshtastic_Telemetry *measurement) { - return true; + if (getBusVoltageMv() > 0) { + measurement->variant.environment_metrics.has_voltage = true; + measurement->variant.environment_metrics.has_current = true; + + measurement->variant.environment_metrics.voltage = (float)getBusVoltageMv() / 1000; + measurement->variant.environment_metrics.current = (float)getCurrentMa() / 1000; + return true; + } else { + return false; + } } uint16_t RAK9154Sensor::getBusVoltageMv() @@ -171,6 +188,11 @@ uint16_t RAK9154Sensor::getBusVoltageMv() return dc_vol; } +int16_t RAK9154Sensor::getCurrentMa() +{ + return dc_cur; +} + int RAK9154Sensor::getBusBatteryPercent() { return (int)dc_prec; @@ -180,4 +202,8 @@ bool RAK9154Sensor::isCharging() { return (dc_cur > 0) ? true : false; } +void RAK9154Sensor::setLastRead(uint32_t lastRead) +{ + this->lastRead = lastRead; +} #endif // HAS_RAKPROT diff --git a/variants/rak2560/RAK9154Sensor.h b/src/modules/Telemetry/Sensor/RAK9154Sensor.h similarity index 55% rename from variants/rak2560/RAK9154Sensor.h rename to src/modules/Telemetry/Sensor/RAK9154Sensor.h index 6c6f304d6..c96139f9c 100644 --- a/variants/rak2560/RAK9154Sensor.h +++ b/src/modules/Telemetry/Sensor/RAK9154Sensor.h @@ -1,23 +1,30 @@ -#ifdef HAS_RAKPROT +#include "configuration.h" + +#if !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR && defined(HAS_RAKPROT) + #ifndef _RAK9154SENSOR_H #define _RAK9154SENSOR_H 1 #include "../mesh/generated/meshtastic/telemetry.pb.h" -#include "../modules/Telemetry/Sensor/TelemetrySensor.h" -#include "../modules/Telemetry/Sensor/VoltageSensor.h" +#include "CurrentSensor.h" +#include "TelemetrySensor.h" +#include "VoltageSensor.h" -class RAK9154Sensor : public TelemetrySensor, VoltageSensor +class RAK9154Sensor : public TelemetrySensor, VoltageSensor, CurrentSensor { private: protected: virtual void setup() override; + uint32_t lastRead = 0; public: RAK9154Sensor(); virtual int32_t runOnce() override; virtual bool getMetrics(meshtastic_Telemetry *measurement) override; virtual uint16_t getBusVoltageMv() override; + virtual int16_t getCurrentMa() override; int getBusBatteryPercent(); bool isCharging(); + void setLastRead(uint32_t lastRead); }; #endif // _RAK9154SENSOR_H #endif // HAS_RAKPROT \ No newline at end of file diff --git a/src/motion/BMX160Sensor.cpp b/src/motion/BMX160Sensor.cpp index 06cea3229..3ddbe46ea 100755 --- a/src/motion/BMX160Sensor.cpp +++ b/src/motion/BMX160Sensor.cpp @@ -4,7 +4,7 @@ BMX160Sensor::BMX160Sensor(ScanI2C::FoundDevice foundDevice) : MotionSensor::MotionSensor(foundDevice) {} -#ifdef RAK_4631 +#if defined(RAK_4631) && !defined(RAK2560) #if !defined(MESHTASTIC_EXCLUDE_SCREEN) // screen is defined in main.cpp diff --git a/src/motion/BMX160Sensor.h b/src/motion/BMX160Sensor.h index 9031b4504..fc5a48aa4 100755 --- a/src/motion/BMX160Sensor.h +++ b/src/motion/BMX160Sensor.h @@ -7,7 +7,7 @@ #if !defined(ARCH_PORTDUINO) && !defined(ARCH_STM32WL) && !MESHTASTIC_EXCLUDE_I2C -#ifdef RAK_4631 +#if defined(RAK_4631) && !defined(RAK2560) #include "Fusion/Fusion.h" #include diff --git a/src/power.h b/src/power.h index ab55fc7e1..176e16ee5 100644 --- a/src/power.h +++ b/src/power.h @@ -1,5 +1,4 @@ #pragma once -#include "../variants/rak2560/RAK9154Sensor.h" #include "PowerStatus.h" #include "concurrency/OSThread.h" #include "configuration.h" @@ -56,8 +55,8 @@ extern INA3221Sensor ina3221Sensor; extern MAX17048Sensor max17048Sensor; #endif -#if HAS_RAKPROT && !defined(ARCH_PORTDUINO) -#include "../variants/rak2560/RAK9154Sensor.h" +#if HAS_TELEMETRY && !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR && HAS_RAKPROT && !defined(ARCH_PORTDUINO) +#include "modules/Telemetry/Sensor/RAK9154Sensor.h" extern RAK9154Sensor rak9154Sensor; #endif diff --git a/variants/rak2560/create_uf2.py b/variants/rak2560/create_uf2.py deleted file mode 100644 index af78f3e09..000000000 --- a/variants/rak2560/create_uf2.py +++ /dev/null @@ -1,113 +0,0 @@ -import struct - -Import("env") # noqa: F821 - - -# Parse input and create UF2 file -def create_uf2(source, target, env): - # source_hex = target[0].get_abspath() - source_hex = target[0].get_string(False) - source_hex = ".\\" + source_hex - print("#########################################################") - print("Create UF2 from " + source_hex) - print("#########################################################") - # print("Source: " + source_hex) - target = source_hex.replace(".hex", "") - target = target + ".uf2" - # print("Target: " + target) - - with open(source_hex, mode="rb") as f: - inpbuf = f.read() - - outbuf = convert_from_hex_to_uf2(inpbuf.decode("utf-8")) - - write_file(target, outbuf) - print("#########################################################") - print(target + " is ready to flash to target device") - print("#########################################################") - - -# Add callback after .hex file was created -env.AddPostAction("$BUILD_DIR/${PROGNAME}.hex", create_uf2) # noqa: F821 - -# UF2 creation taken from uf2conv.py -UF2_MAGIC_START0 = 0x0A324655 # "UF2\n" -UF2_MAGIC_START1 = 0x9E5D5157 # Randomly selected -UF2_MAGIC_END = 0x0AB16F30 # Ditto - -familyid = 0xADA52840 - - -class Block: - def __init__(self, addr): - self.addr = addr - self.bytes = bytearray(256) - - def encode(self, blockno, numblocks): - global familyid - flags = 0x0 - if familyid: - flags |= 0x2000 - hd = struct.pack( - " Date: Mon, 20 Jan 2025 13:20:59 +0200 Subject: [PATCH 49/89] Create BananaPi-BPI-R4-sx1262.yaml (#5897) --- bin/config.d/BananaPi-BPI-R4-sx1262.yaml | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 bin/config.d/BananaPi-BPI-R4-sx1262.yaml diff --git a/bin/config.d/BananaPi-BPI-R4-sx1262.yaml b/bin/config.d/BananaPi-BPI-R4-sx1262.yaml new file mode 100644 index 000000000..825ab2699 --- /dev/null +++ b/bin/config.d/BananaPi-BPI-R4-sx1262.yaml @@ -0,0 +1,9 @@ +Lora: + Module: sx1262 # BananaPi-BPI-R4 SPI via 26p GPIO Header +## CS: 28 + IRQ: 50 + Busy: 62 + Reset: 51 + spidev: spidev1.0 + DIO2_AS_RF_SWITCH: true + DIO3_TCXO_VOLTAGE: true From 0f981153ebbad06675f04bd9b0dd6170c7f08352 Mon Sep 17 00:00:00 2001 From: isseysandei Date: Mon, 20 Jan 2025 17:47:47 +0100 Subject: [PATCH 50/89] No focus on new messages if auto-carousel is off (#5881) * no focus on messages if screen carousel is disabled * trunk + comment * compacted the nested if using ternary operator * trunk --- src/graphics/Screen.cpp | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/src/graphics/Screen.cpp b/src/graphics/Screen.cpp index 198dcc235..b7253ca17 100644 --- a/src/graphics/Screen.cpp +++ b/src/graphics/Screen.cpp @@ -2662,14 +2662,13 @@ int Screen::handleStatusUpdate(const meshtastic::Status *arg) int Screen::handleTextMessage(const meshtastic_MeshPacket *packet) { - if (showingNormalScreen) { - // Outgoing message - if (packet->from == 0) - setFrames(FOCUS_PRESERVE); // Return to same frame (quietly hiding the rx text message frame) + // If auto carousel is disabled -> return 0 and skip new messages handling + if (config.display.auto_screen_carousel_secs == 0) + return 0; - // Incoming message - else - setFrames(FOCUS_TEXTMESSAGE); // Focus on the new message + // Handle focus change based on message type + if (showingNormalScreen) { + setFrames(packet->from == 0 ? FOCUS_PRESERVE : FOCUS_TEXTMESSAGE); } return 0; @@ -2756,4 +2755,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 +#endif // HAS_SCREEN \ No newline at end of file From c4fcbad3723d75a98a28501c3354cae5a424e20b Mon Sep 17 00:00:00 2001 From: Eric Severance Date: Mon, 20 Jan 2025 09:43:35 -0800 Subject: [PATCH 51/89] Reboot before formatting LittleFS (#5900) Co-authored-by: Ben Meadors --- src/FSCommon.cpp | 29 +++++------------ src/FSCommon.h | 5 +-- src/SafeFile.cpp | 6 +--- src/platform/nrf52/main-nrf52.cpp | 52 ++++++++++++++++++++++++++++++- 4 files changed, 61 insertions(+), 31 deletions(-) diff --git a/src/FSCommon.cpp b/src/FSCommon.cpp index 1f2994b29..461c72c26 100644 --- a/src/FSCommon.cpp +++ b/src/FSCommon.cpp @@ -49,24 +49,6 @@ void OSFS::writeNBytes(uint16_t address, unsigned int num, const byte *input) } #endif -bool lfs_assert_failed = - false; // Note: we use this global on all platforms, though it can only be set true on nrf52 (in our modified lfs_util.h) - -extern "C" void lfs_assert(const char *reason) -{ - LOG_ERROR("LFS assert: %s", reason); - lfs_assert_failed = true; - -#ifndef ARCH_PORTDUINO -#ifdef FSCom - // CORRUPTED FILESYSTEM. This causes bootloop so - // might as well try formatting now. - LOG_ERROR("Trying FSCom.format()"); - FSCom.format(); -#endif -#endif -} - /** * @brief Copies a file from one location to another. * @@ -348,10 +330,16 @@ void rmDir(const char *dirname) #endif } +/** + * Some platforms (nrf52) might need to do an extra step before FSBegin(). + */ +__attribute__((weak, noinline)) void preFSBegin() {} + void fsInit() { #ifdef FSCom - spiLock->lock(); + concurrency::LockGuard g(spiLock); + preFSBegin(); if (!FSBegin()) { LOG_ERROR("Filesystem mount failed"); // assert(0); This auto-formats the partition, so no need to fail here. @@ -362,7 +350,6 @@ void fsInit() LOG_DEBUG("Filesystem files:"); #endif listDir("/", 10); - spiLock->unlock(); #endif } @@ -400,4 +387,4 @@ void setupSDCard() 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/FSCommon.h b/src/FSCommon.h index 254245b29..10ce4aeec 100644 --- a/src/FSCommon.h +++ b/src/FSCommon.h @@ -57,7 +57,4 @@ bool renameFile(const char *pathFrom, const char *pathTo); std::vector getFiles(const char *dirname, uint8_t levels); void listDir(const char *dirname, uint8_t levels, bool del = false); void rmDir(const char *dirname); -void setupSDCard(); - -extern bool lfs_assert_failed; // Note: we use this global on all platforms, though it can only be set true on nrf52 (in our - // modified lfs_util.h) +void setupSDCard(); \ No newline at end of file diff --git a/src/SafeFile.cpp b/src/SafeFile.cpp index f874164ae..94232e81d 100644 --- a/src/SafeFile.cpp +++ b/src/SafeFile.cpp @@ -8,7 +8,6 @@ static File openFile(const char *filename, bool fullAtomic) concurrency::LockGuard g(spiLock); LOG_DEBUG("Opening %s, fullAtomic=%d", filename, fullAtomic); #ifdef ARCH_NRF52 - lfs_assert_failed = false; File file = FSCom.open(filename, FILE_O_WRITE); file.seek(0); return file; @@ -20,7 +19,6 @@ static File openFile(const char *filename, bool fullAtomic) filenameTmp += ".tmp"; // clear any previous LFS errors - lfs_assert_failed = false; return FSCom.open(filenameTmp.c_str(), FILE_O_WRITE); } @@ -96,8 +94,6 @@ bool SafeFile::close() bool SafeFile::testReadback() { concurrency::LockGuard g(spiLock); - bool lfs_failed = lfs_assert_failed; - lfs_assert_failed = false; String filenameTmp = filename; filenameTmp += ".tmp"; @@ -119,7 +115,7 @@ bool SafeFile::testReadback() return false; } - return !lfs_failed; + return true; } #endif \ No newline at end of file diff --git a/src/platform/nrf52/main-nrf52.cpp b/src/platform/nrf52/main-nrf52.cpp index 7ca047654..ad4d7a881 100644 --- a/src/platform/nrf52/main-nrf52.cpp +++ b/src/platform/nrf52/main-nrf52.cpp @@ -1,6 +1,7 @@ #include "configuration.h" #include #include +#include #include #include #include @@ -130,6 +131,54 @@ int printf(const char *fmt, ...) return res; } +namespace +{ +constexpr uint8_t NRF52_MAGIC_LFS_IS_CORRUPT = 0xF5; +constexpr uint32_t MULTIPLE_CORRUPTION_DELAY_MILLIS = 20 * 60 * 1000; +static unsigned long millis_until_formatting_again = 0; + +// Report the critical error from loop(), giving a chance for the screen to be initialized first. +inline void reportLittleFSCorruptionOnce() +{ + static bool report_corruption = !!millis_until_formatting_again; + if (report_corruption) { + report_corruption = false; + RECORD_CRITICALERROR(meshtastic_CriticalErrorCode_FLASH_CORRUPTION_UNRECOVERABLE); + } +} +} // namespace + +void preFSBegin() +{ + // The GPREGRET register keeps its value across warm boots. Check that this is a warm boot and, if GPREGRET + // is set to NRF52_MAGIC_LFS_IS_CORRUPT, format LittleFS. + if (!(NRF_POWER->RESETREAS == 0 && NRF_POWER->GPREGRET == NRF52_MAGIC_LFS_IS_CORRUPT)) + return; + NRF_POWER->GPREGRET = 0; + millis_until_formatting_again = millis() + MULTIPLE_CORRUPTION_DELAY_MILLIS; + InternalFS.format(); + LOG_INFO("LittleFS format complete; restoring default settings"); +} + +extern "C" void lfs_assert(const char *reason) +{ + LOG_ERROR("LittleFS corruption detected: %s", reason); + if (millis_until_formatting_again > millis()) { + RECORD_CRITICALERROR(meshtastic_CriticalErrorCode_FLASH_CORRUPTION_UNRECOVERABLE); + const long millis_remain = millis_until_formatting_again - millis(); + LOG_WARN("Pausing %d seconds to avoid wear on flash storage", millis_remain / 1000); + delay(millis_remain); + } + LOG_INFO("Rebooting to format LittleFS"); + delay(500); // Give the serial port a bit of time to output that last message. + // Try setting GPREGRET with the SoftDevice first. If that fails (perhaps because the SD hasn't been initialize yet) then set + // NRF_POWER->GPREGRET directly. + if (!(sd_power_gpregret_clr(0, 0xFF) == NRF_SUCCESS && sd_power_gpregret_set(0, NRF52_MAGIC_LFS_IS_CORRUPT) == NRF_SUCCESS)) { + NRF_POWER->GPREGRET = NRF52_MAGIC_LFS_IS_CORRUPT; + } + NVIC_SystemReset(); +} + void checkSDEvents() { if (useSoftDevice) { @@ -154,6 +203,7 @@ void checkSDEvents() void nrf52Loop() { checkSDEvents(); + reportLittleFSCorruptionOnce(); } #ifdef USE_SEMIHOSTING @@ -309,4 +359,4 @@ void enterDfuMode() #else enterUf2Dfu(); #endif -} +} \ No newline at end of file From f87c37012386cc0eab10a100cf161be5dd5f5613 Mon Sep 17 00:00:00 2001 From: GUVWAF <78759985+GUVWAF@users.noreply.github.com> Date: Tue, 21 Jan 2025 18:11:37 +0100 Subject: [PATCH 52/89] Fix possible memory leak for `ROUTER_LATE` (#5901) --- src/mesh/RadioLibInterface.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/mesh/RadioLibInterface.cpp b/src/mesh/RadioLibInterface.cpp index e31f0b3e2..69809b7a4 100644 --- a/src/mesh/RadioLibInterface.cpp +++ b/src/mesh/RadioLibInterface.cpp @@ -340,8 +340,11 @@ void RadioLibInterface::clampToLateRebroadcastWindow(NodeNum from, PacketId id) meshtastic_MeshPacket *p = txQueue.remove(from, id, true, false); if (p) { p->tx_after = millis() + getTxDelayMsecWeightedWorst(p->rx_snr); - txQueue.enqueue(p); - LOG_DEBUG("Move existing queued packet to the late rebroadcast window %dms from now", p->tx_after - millis()); + if (txQueue.enqueue(p)) { + LOG_DEBUG("Move existing queued packet to the late rebroadcast window %dms from now", p->tx_after - millis()); + } else { + packetPool.release(p); + } } } From 9041af365de649e6c782c6ebedd19a61ea9fb2b6 Mon Sep 17 00:00:00 2001 From: Austin Date: Tue, 21 Jan 2025 17:18:40 -0500 Subject: [PATCH 53/89] Move OpenWRT configs to subdir (#5902) --- bin/config.d/{ => OpenWRT}/BananaPi-BPI-R4-sx1262.yaml | 0 bin/config.d/{ => OpenWRT}/OpenWRT-One-mikroBUS-LR-IOT-CLICK.yaml | 0 bin/config.d/{ => OpenWRT}/OpenWRT_One_mikroBUS_sx1262.yaml | 0 3 files changed, 0 insertions(+), 0 deletions(-) rename bin/config.d/{ => OpenWRT}/BananaPi-BPI-R4-sx1262.yaml (100%) rename bin/config.d/{ => OpenWRT}/OpenWRT-One-mikroBUS-LR-IOT-CLICK.yaml (100%) rename bin/config.d/{ => OpenWRT}/OpenWRT_One_mikroBUS_sx1262.yaml (100%) diff --git a/bin/config.d/BananaPi-BPI-R4-sx1262.yaml b/bin/config.d/OpenWRT/BananaPi-BPI-R4-sx1262.yaml similarity index 100% rename from bin/config.d/BananaPi-BPI-R4-sx1262.yaml rename to bin/config.d/OpenWRT/BananaPi-BPI-R4-sx1262.yaml diff --git a/bin/config.d/OpenWRT-One-mikroBUS-LR-IOT-CLICK.yaml b/bin/config.d/OpenWRT/OpenWRT-One-mikroBUS-LR-IOT-CLICK.yaml similarity index 100% rename from bin/config.d/OpenWRT-One-mikroBUS-LR-IOT-CLICK.yaml rename to bin/config.d/OpenWRT/OpenWRT-One-mikroBUS-LR-IOT-CLICK.yaml diff --git a/bin/config.d/OpenWRT_One_mikroBUS_sx1262.yaml b/bin/config.d/OpenWRT/OpenWRT_One_mikroBUS_sx1262.yaml similarity index 100% rename from bin/config.d/OpenWRT_One_mikroBUS_sx1262.yaml rename to bin/config.d/OpenWRT/OpenWRT_One_mikroBUS_sx1262.yaml From 71591fb06a5d4018bd6ed3ab4c9673c2a03287ac Mon Sep 17 00:00:00 2001 From: Austin Date: Tue, 21 Jan 2025 19:53:32 -0500 Subject: [PATCH 54/89] Build docker images with other linux (#5837) --- .github/workflows/build_docker.yml | 51 -------- .github/workflows/daily_packaging.yml | 6 + .github/workflows/docker_build.yml | 70 +++++++++++ .github/workflows/docker_manifest.yml | 167 +++++++++++++++++++++++++ .github/workflows/main_matrix.yml | 35 +++++- .github/workflows/release_channels.yml | 7 ++ 6 files changed, 281 insertions(+), 55 deletions(-) delete mode 100644 .github/workflows/build_docker.yml create mode 100644 .github/workflows/docker_build.yml create mode 100644 .github/workflows/docker_manifest.yml diff --git a/.github/workflows/build_docker.yml b/.github/workflows/build_docker.yml deleted file mode 100644 index 18787f16a..000000000 --- a/.github/workflows/build_docker.yml +++ /dev/null @@ -1,51 +0,0 @@ -name: Build Docker - -on: workflow_call - -permissions: - contents: write - packages: write - -jobs: - build-native: - runs-on: ubuntu-latest - steps: - - name: Checkout code - uses: actions/checkout@v4 - with: - submodules: recursive - ref: ${{github.event.pull_request.head.ref}} - repository: ${{github.event.pull_request.head.repo.full_name}} - - - name: Get release version string - run: echo "long=$(./bin/buildinfo.py long)" >> $GITHUB_OUTPUT - id: version - - - name: Docker login - if: ${{ github.event_name != 'pull_request_target' && github.event_name != 'pull_request' }} - uses: docker/login-action@v3 - with: - username: meshtastic - password: ${{ secrets.DOCKER_FIRMWARE_TOKEN }} - - - name: Docker setup - if: ${{ github.event_name != 'pull_request_target' && github.event_name != 'pull_request' }} - uses: docker/setup-buildx-action@v3 - - - name: Docker build and push tagged versions - if: ${{ github.event_name == 'workflow_dispatch' }} - uses: docker/build-push-action@v6 - with: - context: . - file: ./Dockerfile - push: true - tags: meshtastic/meshtasticd:${{ steps.version.outputs.long }} - - - name: Docker build and push - if: ${{ github.ref == 'refs/heads/master' && github.event_name != 'pull_request_target' && github.event_name != 'pull_request' }} - uses: docker/build-push-action@v6 - with: - context: . - file: ./Dockerfile - push: true - tags: meshtastic/meshtasticd:latest diff --git a/.github/workflows/daily_packaging.yml b/.github/workflows/daily_packaging.yml index 14daae74d..cb8f866c6 100644 --- a/.github/workflows/daily_packaging.yml +++ b/.github/workflows/daily_packaging.yml @@ -20,6 +20,12 @@ permissions: packages: write jobs: + docker-multiarch: + uses: ./.github/workflows/docker_manifest.yml + with: + release_channel: daily + secrets: inherit + package-ppa: strategy: fail-fast: false diff --git a/.github/workflows/docker_build.yml b/.github/workflows/docker_build.yml new file mode 100644 index 000000000..83c67bb32 --- /dev/null +++ b/.github/workflows/docker_build.yml @@ -0,0 +1,70 @@ +name: Build Docker + +# Build Docker image, push untagged (digest-only) + +on: + workflow_call: + inputs: + distro: + description: Distro to target + required: true + type: string + # choices: [debian, alpine] + platform: + description: Platform to target + required: true + type: string + runs-on: + description: Runner to use + required: true + type: string + push: + description: Push images to registry + required: false + type: boolean + default: false + outputs: + digest: + description: Digest of built image + value: ${{ jobs.docker-build.outputs.digest }} + +permissions: + contents: write + packages: write + +jobs: + docker-build: + outputs: + digest: ${{ steps.docker_variant.outputs.digest }} + runs-on: ${{ inputs.runs-on }} + steps: + - name: Checkout code + uses: actions/checkout@v4 + with: + submodules: recursive + ref: ${{github.event.pull_request.head.ref}} + repository: ${{github.event.pull_request.head.repo.full_name}} + + - name: Docker login + if: ${{ inputs.push }} + uses: docker/login-action@v3 + with: + username: meshtastic + password: ${{ secrets.DOCKER_FIRMWARE_TOKEN }} + + - name: Set up QEMU + uses: docker/setup-qemu-action@v3 + + - name: Docker setup + uses: docker/setup-buildx-action@v3 + + - name: Docker build and push + uses: docker/build-push-action@v6 + id: docker_variant + with: + context: . + file: | + ${{ contains(inputs.distro, 'debian') && './Dockerfile' || contains(inputs.distro, 'alpine') && './alpine.Dockerfile' }} + push: ${{ inputs.push }} + tags: "" # Intentionally empty, push with digest only + platforms: ${{ inputs.platform }} diff --git a/.github/workflows/docker_manifest.yml b/.github/workflows/docker_manifest.yml new file mode 100644 index 000000000..30dcfb067 --- /dev/null +++ b/.github/workflows/docker_manifest.yml @@ -0,0 +1,167 @@ +name: Build Docker Multi-Arch Manifest + +on: + workflow_call: + inputs: + release_channel: + description: Release channel to target + required: true + type: string + +permissions: + contents: write + packages: write + +jobs: + docker-debian-amd64: + uses: ./.github/workflows/docker_build.yml + with: + distro: debian + platform: linux/amd64 + runs-on: ubuntu-24.04 + push: true + + docker-debian-arm64: + uses: ./.github/workflows/docker_build.yml + with: + distro: debian + platform: linux/arm64 + runs-on: ubuntu-24.04-arm + push: true + + docker-debian-armv7: + uses: ./.github/workflows/docker_build.yml + with: + distro: debian + platform: linux/arm/v7 + runs-on: ubuntu-24.04-arm + push: true + + docker-alpine-amd64: + uses: ./.github/workflows/docker_build.yml + with: + distro: alpine + platform: linux/amd64 + runs-on: ubuntu-24.04 + push: true + + docker-alpine-arm64: + uses: ./.github/workflows/docker_build.yml + with: + distro: alpine + platform: linux/arm64 + runs-on: ubuntu-24.04-arm + push: true + + docker-alpine-armv7: + uses: ./.github/workflows/docker_build.yml + with: + distro: alpine + platform: linux/arm/v7 + runs-on: ubuntu-24.04-arm + push: true + + docker-manifest: + needs: + # Debian + - docker-debian-amd64 + - docker-debian-arm64 + - docker-debian-armv7 + # Alpine + - docker-alpine-amd64 + - docker-alpine-arm64 + - docker-alpine-armv7 + runs-on: ubuntu-24.04 + steps: + - name: Checkout code + uses: actions/checkout@v4 + with: + submodules: recursive + ref: ${{github.event.pull_request.head.ref}} + repository: ${{github.event.pull_request.head.repo.full_name}} + + - name: Get release version string + run: | + echo "long=$(./bin/buildinfo.py long)" >> $GITHUB_OUTPUT + echo "short=$(./bin/buildinfo.py short)" >> $GITHUB_OUTPUT + id: version + + - name: Enumerate tags + shell: python + run: | + import os + + short = "${{ steps.version.outputs.short }}" + long = "${{ steps.version.outputs.long }}" + release_channel = "${{ inputs.release_channel }}" + tags = { + "beta": { + "debian": [ + f"{short}", f"{long}", f"{short}-beta", f"{long}-beta", "beta", "latest", + f"{short}-debian", f"{long}-debian", f"{short}-beta-debian", f"{long}-beta-debian", "beta-debian" + ], + "alpine": [ + f"{short}-alpine", f"{long}-alpine", f"{short}-beta-alpine", f"{long}-beta-alpine", "beta-alpine" + ] + }, + "alpha": { + "debian": [ + f"{short}-alpha", f"{long}-alpha", "alpha", + f"{short}-alpha-debian", f"{long}-alpha-debian", "alpha-debian" + ], + "alpine": [ + f"{short}-alpha-alpine", f"{long}-alpha-alpine", "alpha-alpine" + ] + }, + "daily": { + "debian": ["daily", "daily-debian"], + "alpine": ["daily-alpine"] + } + } + + with open(os.environ['GITHUB_OUTPUT'], 'a') as fh: + fh.write(f"debian={','.join(tags[release_channel]['debian'])}\n") + fh.write(f"alpine={','.join(tags[release_channel]['alpine'])}\n") + id: tags + + - name: Docker login + uses: docker/login-action@v3 + with: + username: meshtastic + password: ${{ secrets.DOCKER_FIRMWARE_TOKEN }} + + - name: Docker meta (Debian) + id: meta_debian + uses: docker/metadata-action@v5 + with: + images: meshtastic/meshtasticd + tags: ${{ steps.tags.outputs.debian }} + + - name: Create Docker manifest (Debian) + id: manifest_debian + uses: int128/docker-manifest-create-action@v2 + with: + tags: ${{ steps.meta_debian.outputs.tags }} + push: true + sources: | + meshtastic/meshtasticd@${{ needs.docker-debian-amd64.outputs.digest }} + meshtastic/meshtasticd@${{ needs.docker-debian-arm64.outputs.digest }} + meshtastic/meshtasticd@${{ needs.docker-debian-armv7.outputs.digest }} + + - name: Docker meta (Alpine) + id: meta_alpine + uses: docker/metadata-action@v5 + with: + images: meshtastic/meshtasticd + tags: ${{ steps.tags.outputs.alpine }} + + - name: Create Docker manifest (Alpine) + id: manifest_alpine + uses: int128/docker-manifest-create-action@v2 + with: + tags: ${{ steps.meta_alpine.outputs.tags }} + push: true + sources: | + meshtastic/meshtasticd@${{ needs.docker-alpine-amd64.outputs.digest }} + meshtastic/meshtasticd@${{ needs.docker-alpine-arm64.outputs.digest }} + meshtastic/meshtasticd@${{ needs.docker-alpine-armv7.outputs.digest }} diff --git a/.github/workflows/main_matrix.yml b/.github/workflows/main_matrix.yml index 0a0ea9954..a9678f4fc 100644 --- a/.github/workflows/main_matrix.yml +++ b/.github/workflows/main_matrix.yml @@ -147,10 +147,37 @@ jobs: test-native: uses: ./.github/workflows/test_native.yml - build-docker: - if: ${{ github.event_name == 'workflow_dispatch' }} - uses: ./.github/workflows/build_docker.yml - secrets: inherit + docker-debian-amd64: + uses: ./.github/workflows/docker_build.yml + with: + distro: debian + platform: linux/amd64 + runs-on: ubuntu-24.04 + push: false + + docker-alpine-amd64: + uses: ./.github/workflows/docker_build.yml + with: + distro: debian + platform: linux/amd64 + runs-on: ubuntu-24.04 + push: false + + docker-debian-arm64: + uses: ./.github/workflows/docker_build.yml + with: + distro: debian + platform: linux/arm64 + runs-on: ubuntu-24.04-arm + push: false + + docker-debian-armv7: + uses: ./.github/workflows/docker_build.yml + with: + distro: debian + platform: linux/arm/v7 + runs-on: ubuntu-24.04-arm + push: false after-checks: runs-on: ubuntu-latest diff --git a/.github/workflows/release_channels.yml b/.github/workflows/release_channels.yml index afb7319ed..b59a0316c 100644 --- a/.github/workflows/release_channels.yml +++ b/.github/workflows/release_channels.yml @@ -9,6 +9,13 @@ permissions: packages: write jobs: + build-docker: + uses: ./.github/workflows/docker_manifest.yml + with: + release_channel: |- + ${{ contains(github.event.release.name, 'Beta') && 'beta' || contains(github.event.release.name, 'Alpha') && 'alpha' }} + secrets: inherit + package-ppa: strategy: fail-fast: false From 0fdbf70452158d292d71bdd8b02499d8bd4b2c2a Mon Sep 17 00:00:00 2001 From: Austin Date: Tue, 21 Jan 2025 22:26:10 -0500 Subject: [PATCH 55/89] Small fix: Correctly pass secrets in Docker builds (#5905) --- .github/workflows/docker_build.yml | 3 +++ .github/workflows/docker_manifest.yml | 9 +++++++++ 2 files changed, 12 insertions(+) diff --git a/.github/workflows/docker_build.yml b/.github/workflows/docker_build.yml index 83c67bb32..43072b777 100644 --- a/.github/workflows/docker_build.yml +++ b/.github/workflows/docker_build.yml @@ -4,6 +4,9 @@ name: Build Docker on: workflow_call: + secrets: + DOCKER_FIRMWARE_TOKEN: + required: false # Only required for push inputs: distro: description: Distro to target diff --git a/.github/workflows/docker_manifest.yml b/.github/workflows/docker_manifest.yml index 30dcfb067..9183dfd6c 100644 --- a/.github/workflows/docker_manifest.yml +++ b/.github/workflows/docker_manifest.yml @@ -2,6 +2,9 @@ name: Build Docker Multi-Arch Manifest on: workflow_call: + secrets: + DOCKER_FIRMWARE_TOKEN: + required: true inputs: release_channel: description: Release channel to target @@ -20,6 +23,7 @@ jobs: platform: linux/amd64 runs-on: ubuntu-24.04 push: true + secrets: inherit docker-debian-arm64: uses: ./.github/workflows/docker_build.yml @@ -28,6 +32,7 @@ jobs: platform: linux/arm64 runs-on: ubuntu-24.04-arm push: true + secrets: inherit docker-debian-armv7: uses: ./.github/workflows/docker_build.yml @@ -36,6 +41,7 @@ jobs: platform: linux/arm/v7 runs-on: ubuntu-24.04-arm push: true + secrets: inherit docker-alpine-amd64: uses: ./.github/workflows/docker_build.yml @@ -44,6 +50,7 @@ jobs: platform: linux/amd64 runs-on: ubuntu-24.04 push: true + secrets: inherit docker-alpine-arm64: uses: ./.github/workflows/docker_build.yml @@ -52,6 +59,7 @@ jobs: platform: linux/arm64 runs-on: ubuntu-24.04-arm push: true + secrets: inherit docker-alpine-armv7: uses: ./.github/workflows/docker_build.yml @@ -60,6 +68,7 @@ jobs: platform: linux/arm/v7 runs-on: ubuntu-24.04-arm push: true + secrets: inherit docker-manifest: needs: From fdc87d492c0bc164a50f02da2f1ab2806013704e Mon Sep 17 00:00:00 2001 From: Eric Severance Date: Wed, 22 Jan 2025 00:45:34 -0800 Subject: [PATCH 56/89] Add quotes around ${platformio.build_dir} (#5906) Fixes #5898 (hopefully) --- platformio.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platformio.ini b/platformio.ini index ea4de4db1..1c51e53b4 100644 --- a/platformio.ini +++ b/platformio.ini @@ -20,7 +20,7 @@ extra_scripts = bin/platformio-custom.py build_flags = -Wno-missing-field-initializers -Wno-format - -Isrc -Isrc/mesh -Isrc/mesh/generated -Isrc/gps -Isrc/buzz -Wl,-Map,${platformio.build_dir}/output.map + -Isrc -Isrc/mesh -Isrc/mesh/generated -Isrc/gps -Isrc/buzz -Wl,-Map,"${platformio.build_dir}"/output.map -DUSE_THREAD_NAMES -DTINYGPS_OPTION_NO_CUSTOM_FIELDS -DPB_ENABLE_MALLOC=1 From 7fb22cf678d0e40d7e44e3e9ecd8cb10f734e8c9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20G=C3=B6ttgens?= Date: Wed, 22 Jan 2025 14:11:58 +0100 Subject: [PATCH 57/89] ignore platformio core files when building in place --- .gitignore | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.gitignore b/.gitignore index 803aee139..b63f431d1 100644 --- a/.gitignore +++ b/.gitignore @@ -12,6 +12,9 @@ web.tar *.code-workspace .idea +.platformio +.local +.cache .DS_Store Thumbs.db From 01892cbd1eaa824628315f3c79078763f3e8cc91 Mon Sep 17 00:00:00 2001 From: Austin Date: Wed, 22 Jan 2025 09:55:57 -0500 Subject: [PATCH 58/89] Docker: tag intermediate containers (#5910) --- .github/workflows/docker_build.yml | 21 ++++++++++++++++++++- .github/workflows/docker_manifest.yml | 1 + 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/.github/workflows/docker_build.yml b/.github/workflows/docker_build.yml index 43072b777..eec0785c0 100644 --- a/.github/workflows/docker_build.yml +++ b/.github/workflows/docker_build.yml @@ -48,6 +48,11 @@ jobs: ref: ${{github.event.pull_request.head.ref}} repository: ${{github.event.pull_request.head.repo.full_name}} + - name: Get release version string + run: | + echo "long=$(./bin/buildinfo.py long)" >> $GITHUB_OUTPUT + id: version + - name: Docker login if: ${{ inputs.push }} uses: docker/login-action@v3 @@ -61,6 +66,20 @@ jobs: - name: Docker setup uses: docker/setup-buildx-action@v3 + - name: Sanitize platform string + id: sanitize_platform + # Replace slashes with underscores + run: echo "cleaned_platform=${{ inputs.platform }}" | sed 's/\//_/g' >> $GITHUB_OUTPUT + + - name: Docker tag + id: meta + uses: docker/metadata-action@v5 + with: + images: meshtastic/meshtasticd + tags: | + GHA-${{ steps.version.outputs.long }}-${{ inputs.distro }}-${{ steps.sanitize_platform.outputs.cleaned_platform }} + flavor: latest=false + - name: Docker build and push uses: docker/build-push-action@v6 id: docker_variant @@ -69,5 +88,5 @@ jobs: file: | ${{ contains(inputs.distro, 'debian') && './Dockerfile' || contains(inputs.distro, 'alpine') && './alpine.Dockerfile' }} push: ${{ inputs.push }} - tags: "" # Intentionally empty, push with digest only + tags: ${{ steps.meta.outputs.tags }} # Tag is only meant to be consumed by the "manifest" job platforms: ${{ inputs.platform }} diff --git a/.github/workflows/docker_manifest.yml b/.github/workflows/docker_manifest.yml index 9183dfd6c..28dbf8c21 100644 --- a/.github/workflows/docker_manifest.yml +++ b/.github/workflows/docker_manifest.yml @@ -145,6 +145,7 @@ jobs: with: images: meshtastic/meshtasticd tags: ${{ steps.tags.outputs.debian }} + flavor: latest=false - name: Create Docker manifest (Debian) id: manifest_debian From 8e8b22edb0ae0aa56f92bbf49967b667a9acc747 Mon Sep 17 00:00:00 2001 From: Austin Date: Wed, 22 Jan 2025 12:09:29 -0500 Subject: [PATCH 59/89] Debian: Switch OBS repo to `network:Meshtastic` (#5912) --- .github/workflows/daily_packaging.yml | 2 +- .github/workflows/release_channels.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/daily_packaging.yml b/.github/workflows/daily_packaging.yml index cb8f866c6..11fe2043a 100644 --- a/.github/workflows/daily_packaging.yml +++ b/.github/workflows/daily_packaging.yml @@ -40,7 +40,7 @@ jobs: package-obs: uses: ./.github/workflows/package_obs.yml with: - obs_project: home:meshtastic:daily + obs_project: network:Meshtastic:daily series: unstable secrets: inherit diff --git a/.github/workflows/release_channels.yml b/.github/workflows/release_channels.yml index b59a0316c..a3a105d6d 100644 --- a/.github/workflows/release_channels.yml +++ b/.github/workflows/release_channels.yml @@ -32,7 +32,7 @@ jobs: uses: ./.github/workflows/package_obs.yml with: obs_project: |- - home:meshtastic:${{ contains(github.event.release.name, 'Beta') && 'beta' || contains(github.event.release.name, 'Alpha') && 'alpha' }} + network:Meshtastic:${{ contains(github.event.release.name, 'Beta') && 'beta' || contains(github.event.release.name, 'Alpha') && 'alpha' }} series: |- ${{ contains(github.event.release.name, 'Beta') && 'beta' || contains(github.event.release.name, 'Alpha') && 'alpha' }} secrets: inherit From 3b40fe9805041a69381442bce1b2273bdd91b99d Mon Sep 17 00:00:00 2001 From: Austin Date: Thu, 23 Jan 2025 17:03:03 -0500 Subject: [PATCH 60/89] Docker: Switch tags to newline-seperated (#5919) --- .github/workflows/docker_manifest.yml | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/.github/workflows/docker_manifest.yml b/.github/workflows/docker_manifest.yml index 28dbf8c21..d1d1a5634 100644 --- a/.github/workflows/docker_manifest.yml +++ b/.github/workflows/docker_manifest.yml @@ -128,9 +128,14 @@ jobs: } } - with open(os.environ['GITHUB_OUTPUT'], 'a') as fh: - fh.write(f"debian={','.join(tags[release_channel]['debian'])}\n") - fh.write(f"alpine={','.join(tags[release_channel]['alpine'])}\n") + with open(os.environ["GITHUB_OUTPUT"], "a") as fh: + fh.write("debian< Date: Thu, 23 Jan 2025 19:12:20 -0600 Subject: [PATCH 61/89] NRF52 - Remove file totally before opening write (#5916) * Remove prefs first * Remove file first * Remove truncate * No longer needed * Missed a param * That wasn't supposed to be there * Remove vestigal lfs assert * Durr --- src/SafeFile.cpp | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/src/SafeFile.cpp b/src/SafeFile.cpp index 94232e81d..c942aa0ee 100644 --- a/src/SafeFile.cpp +++ b/src/SafeFile.cpp @@ -8,9 +8,8 @@ static File openFile(const char *filename, bool fullAtomic) concurrency::LockGuard g(spiLock); LOG_DEBUG("Opening %s, fullAtomic=%d", filename, fullAtomic); #ifdef ARCH_NRF52 - File file = FSCom.open(filename, FILE_O_WRITE); - file.seek(0); - return file; + FSCom.remove(filename); + return FSCom.open(filename, FILE_O_WRITE); #endif if (!fullAtomic) FSCom.remove(filename); // Nuke the old file to make space (ignore if it !exists) @@ -59,9 +58,6 @@ bool SafeFile::close() return false; spiLock->lock(); -#ifdef ARCH_NRF52 - f.truncate(); -#endif f.close(); spiLock->unlock(); From d1f7739bbea229049ac92d32022888fbd8b8e382 Mon Sep 17 00:00:00 2001 From: Ben Meadors Date: Thu, 23 Jan 2025 19:56:59 -0600 Subject: [PATCH 62/89] Peg NRF52 arduino to meshtastic fork with LFE bluetooth fix (#5924) --- arch/nrf52/nrf52.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/nrf52/nrf52.ini b/arch/nrf52/nrf52.ini index 57b276978..b68977c78 100644 --- a/arch/nrf52/nrf52.ini +++ b/arch/nrf52/nrf52.ini @@ -4,7 +4,7 @@ platform = platformio/nordicnrf52@^10.7.0 extends = arduino_base platform_packages = ; our custom Git version until they merge our PR - framework-arduinoadafruitnrf52 @ https://github.com/geeksville/Adafruit_nRF52_Arduino.git + framework-arduinoadafruitnrf52 @ https://github.com/meshtastic/Adafruit_nRF52_Arduino.git#e13f5820002a4fb2a5e6754b42ace185277e5adf toolchain-gccarmnoneeabi@~1.90301.0 build_type = debug From 3298df953a75d70471ca77abe78dff8cca2eb6bc Mon Sep 17 00:00:00 2001 From: Tom Fifield Date: Sat, 25 Jan 2025 00:30:18 +1100 Subject: [PATCH 63/89] Fixed the issue that the wifi configuration saved to RAM did not take effect. (#5925) Co-authored-by: virgil --- src/mesh/wifi/WiFiAPClient.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mesh/wifi/WiFiAPClient.cpp b/src/mesh/wifi/WiFiAPClient.cpp index dcfcdc047..41de89794 100644 --- a/src/mesh/wifi/WiFiAPClient.cpp +++ b/src/mesh/wifi/WiFiAPClient.cpp @@ -225,7 +225,7 @@ bool initWifi() #if !MESHTASTIC_EXCLUDE_WEBSERVER createSSLCert(); // For WebServer #endif - esp_wifi_set_storage(WIFI_STORAGE_RAM); // Disable flash storage for WiFi credentials + WiFi.persistent(false); // Disable flash storage for WiFi credentials #endif if (!*wifiPsw) // Treat empty password as no password wifiPsw = NULL; From 4c97351187c80f38d680f2ef3fde18e427d29d50 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 24 Jan 2025 18:52:17 -0600 Subject: [PATCH 64/89] [create-pull-request] automated change (#5926) Co-authored-by: thebentern <9000580+thebentern@users.noreply.github.com> --- protobufs | 2 +- src/mesh/generated/meshtastic/mesh.pb.h | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/protobufs b/protobufs index fde27e4ef..7f13df0e5 160000 --- a/protobufs +++ b/protobufs @@ -1 +1 @@ -Subproject commit fde27e4ef0fcee967063ba353422ed5f9a1c4790 +Subproject commit 7f13df0e5f7cbb07f0e6f3a57c0d86ad448738db diff --git a/src/mesh/generated/meshtastic/mesh.pb.h b/src/mesh/generated/meshtastic/mesh.pb.h index 5cd23c8e3..3353a020f 100644 --- a/src/mesh/generated/meshtastic/mesh.pb.h +++ b/src/mesh/generated/meshtastic/mesh.pb.h @@ -223,6 +223,9 @@ typedef enum _meshtastic_HardwareModel { /* Mesh-Tab, esp32 based https://github.com/valzzu/Mesh-Tab */ meshtastic_HardwareModel_MESH_TAB = 86, + /* MeshLink board developed by LoraItalia. NRF52840, eByte E22900M22S (Will also come with other frequencies), 25w MPPT solar charger (5v,12v,18v selectable), support for gps, buzzer, oled or e-ink display, 10 gpios, hardware watchdog + https://www.loraitalia.it */ + meshtastic_HardwareModel_MESHLINK = 87, /* ------------------------------------------------------------------------------------------------------------------------------------------ Reserved ID For developing private Ports. These will show up in live traffic sparsely, so we can use a high number. Keep it within 8 bits. ------------------------------------------------------------------------------------------------------------------------------------------ */ From fd56995764c3ea003a989e85965e197617854e3c Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sat, 25 Jan 2025 07:53:24 -0600 Subject: [PATCH 65/89] [create-pull-request] automated change (#5928) Co-authored-by: thebentern <9000580+thebentern@users.noreply.github.com> --- debian/changelog | 5 +++-- version.properties | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/debian/changelog b/debian/changelog index a1a359cfb..1b371296b 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,7 +1,8 @@ -meshtasticd (2.5.20.0) UNRELEASED; urgency=medium +meshtasticd (2.5.21.0) UNRELEASED; urgency=medium * Initial packaging * GitHub Actions Automatic version bump * GitHub Actions Automatic version bump + * GitHub Actions Automatic version bump - -- Austin Lane Wed, 15 Jan 2025 14:08:54 +0000 + -- Austin Lane Sat, 25 Jan 2025 01:39:16 +0000 diff --git a/version.properties b/version.properties index 4312ae59a..efc42428c 100644 --- a/version.properties +++ b/version.properties @@ -1,4 +1,4 @@ [VERSION] major = 2 minor = 5 -build = 20 +build = 21 From a14346bc4f862f8f713e9d2e21704b14ae5c99b1 Mon Sep 17 00:00:00 2001 From: GUVWAF <78759985+GUVWAF@users.noreply.github.com> Date: Sat, 25 Jan 2025 16:24:24 +0100 Subject: [PATCH 66/89] Rate limit position replies to three minutes (#5932) --- src/modules/PositionModule.cpp | 20 ++++++++++++++++---- src/modules/PositionModule.h | 2 ++ 2 files changed, 18 insertions(+), 4 deletions(-) diff --git a/src/modules/PositionModule.cpp b/src/modules/PositionModule.cpp index 6285d7aa5..95a47f0a1 100644 --- a/src/modules/PositionModule.cpp +++ b/src/modules/PositionModule.cpp @@ -160,7 +160,8 @@ bool PositionModule::hasGPS() #endif } -meshtastic_MeshPacket *PositionModule::allocReply() +// Allocate a packet with our position data if we have one +meshtastic_MeshPacket *PositionModule::allocPositionPacket() { if (precision == 0) { LOG_DEBUG("Skip location send because precision is set to 0!"); @@ -262,7 +263,8 @@ meshtastic_MeshPacket *PositionModule::allocReply() p.has_ground_speed = true; } - LOG_INFO("Position reply: time=%i lat=%i lon=%i", p.time, p.latitude_i, p.longitude_i); + LOG_INFO("Position packet: time=%i lat=%i lon=%i", p.time, p.latitude_i, p.longitude_i); + lastSentToMesh = millis(); // 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) @@ -271,6 +273,16 @@ meshtastic_MeshPacket *PositionModule::allocReply() return allocDataProtobuf(p); } +meshtastic_MeshPacket *PositionModule::allocReply() +{ + if (lastSentToMesh && Throttle::isWithinTimespanMs(lastSentToMesh, 3 * 60 * 1000)) { + LOG_DEBUG("Skip Position reply since we sent it <3min ago"); + ignoreRequest = true; // Mark it as ignored for MeshModule + return nullptr; + } + return allocPositionPacket(); +} + meshtastic_MeshPacket *PositionModule::allocAtakPli() { LOG_INFO("Send TAK PLI packet"); @@ -333,9 +345,9 @@ void PositionModule::sendOurPosition(NodeNum dest, bool wantReplies, uint8_t cha precision = 0; } - meshtastic_MeshPacket *p = allocReply(); + meshtastic_MeshPacket *p = allocPositionPacket(); if (p == nullptr) { - LOG_DEBUG("allocReply returned a nullptr"); + LOG_DEBUG("allocPositionPacket returned a nullptr"); return; } diff --git a/src/modules/PositionModule.h b/src/modules/PositionModule.h index 1e4aa5d29..dc732a3db 100644 --- a/src/modules/PositionModule.h +++ b/src/modules/PositionModule.h @@ -55,6 +55,7 @@ class PositionModule : public ProtobufModule, private concu virtual int32_t runOnce() override; private: + meshtastic_MeshPacket *allocPositionPacket(); struct SmartPosition getDistanceTraveledSinceLastSend(meshtastic_PositionLite currentPosition); meshtastic_MeshPacket *allocAtakPli(); void trySetRtc(meshtastic_Position p, bool isLocal, bool forceUpdate = false); @@ -62,6 +63,7 @@ class PositionModule : public ProtobufModule, private concu void sendLostAndFoundText(); bool hasQualityTimesource(); bool hasGPS(); + uint32_t lastSentToMesh = 0; // Last time we sent our position to the mesh const uint32_t minimumTimeThreshold = Default::getConfiguredOrDefaultMs(config.position.broadcast_smart_minimum_interval_secs, 30); From 7649e70585ab1fa9a67924c7e5ab4a85b4bf702a Mon Sep 17 00:00:00 2001 From: Ben Meadors Date: Sat, 25 Jan 2025 12:01:25 -0600 Subject: [PATCH 67/89] Revert "No focus on new messages if auto-carousel is off (#5881)" (#5936) This reverts commit 0f981153ebbad06675f04bd9b0dd6170c7f08352. --- src/graphics/Screen.cpp | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/src/graphics/Screen.cpp b/src/graphics/Screen.cpp index b7253ca17..198dcc235 100644 --- a/src/graphics/Screen.cpp +++ b/src/graphics/Screen.cpp @@ -2662,13 +2662,14 @@ int Screen::handleStatusUpdate(const meshtastic::Status *arg) int Screen::handleTextMessage(const meshtastic_MeshPacket *packet) { - // If auto carousel is disabled -> return 0 and skip new messages handling - if (config.display.auto_screen_carousel_secs == 0) - return 0; - - // Handle focus change based on message type if (showingNormalScreen) { - setFrames(packet->from == 0 ? FOCUS_PRESERVE : FOCUS_TEXTMESSAGE); + // Outgoing message + if (packet->from == 0) + setFrames(FOCUS_PRESERVE); // Return to same frame (quietly hiding the rx text message frame) + + // Incoming message + else + setFrames(FOCUS_TEXTMESSAGE); // Focus on the new message } return 0; @@ -2755,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 From 10d553087c4d8158b961a5b9ab8b6a1ff413ca38 Mon Sep 17 00:00:00 2001 From: Aleksey Vasilenko Date: Sun, 26 Jan 2025 10:54:26 +0200 Subject: [PATCH 68/89] Add missing build_unflags (#5941) Fixes 'undefined reference to app_main' build error for my_esp32s3_diy_eink and my_esp32s3_diy_oled variants. --- variants/my_esp32s3_diy_eink/platformio.ini | 4 +++- variants/my_esp32s3_diy_oled/platformio.ini | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/variants/my_esp32s3_diy_eink/platformio.ini b/variants/my_esp32s3_diy_eink/platformio.ini index e81f2c1ab..b2404566f 100644 --- a/variants/my_esp32s3_diy_eink/platformio.ini +++ b/variants/my_esp32s3_diy_eink/platformio.ini @@ -14,7 +14,9 @@ lib_deps = ${esp32_base.lib_deps} zinggjm/GxEPD2@^1.5.1 adafruit/Adafruit NeoPixel @ ^1.12.0 -build_unflags = -DARDUINO_USB_MODE=1 +build_unflags = + ${esp32s3_base.build_unflags} + -DARDUINO_USB_MODE=1 build_flags = ;${esp32_base.build_flags} -D MY_ESP32S3_DIY -I variants/my_esp32s3_diy_eink ${esp32_base.build_flags} -D PRIVATE_HW -I variants/my_esp32s3_diy_eink diff --git a/variants/my_esp32s3_diy_oled/platformio.ini b/variants/my_esp32s3_diy_oled/platformio.ini index 2d7a5cd91..0fbbaa899 100644 --- a/variants/my_esp32s3_diy_oled/platformio.ini +++ b/variants/my_esp32s3_diy_oled/platformio.ini @@ -13,7 +13,9 @@ platform_packages = lib_deps = ${esp32_base.lib_deps} adafruit/Adafruit NeoPixel @ ^1.12.0 -build_unflags = -DARDUINO_USB_MODE=1 +build_unflags = + ${esp32s3_base.build_unflags} + -DARDUINO_USB_MODE=1 build_flags = ;${esp32_base.build_flags} -D MY_ESP32S3_DIY -I variants/my_esp32s3_diy_oled ${esp32_base.build_flags} -D PRIVATE_HW -I variants/my_esp32s3_diy_oled From 4747e73f37e8f6d14aaa3288841fa0f7d8f1c177 Mon Sep 17 00:00:00 2001 From: GUVWAF <78759985+GUVWAF@users.noreply.github.com> Date: Sun, 26 Jan 2025 20:59:59 +0100 Subject: [PATCH 69/89] Space out periodic broadcasts of modules automatically (#5931) * Space out periodic broadcasts of modules automatically * Add warning for function usage --------- Co-authored-by: Ben Meadors --- src/mesh/MeshModule.cpp | 10 ++++++++++ src/mesh/MeshModule.h | 9 +++++++++ src/modules/DetectionSensorModule.cpp | 4 ++-- src/modules/NodeInfoModule.cpp | 7 ++++--- src/modules/PositionModule.cpp | 5 +++-- src/modules/Telemetry/AirQualityTelemetry.cpp | 4 ++-- src/modules/Telemetry/DeviceTelemetry.h | 4 ++-- src/modules/Telemetry/EnvironmentTelemetry.cpp | 6 +++--- src/modules/Telemetry/HealthTelemetry.cpp | 2 +- src/modules/Telemetry/PowerTelemetry.cpp | 2 +- 10 files changed, 37 insertions(+), 16 deletions(-) diff --git a/src/mesh/MeshModule.cpp b/src/mesh/MeshModule.cpp index 2f2863fa5..62d3c82bc 100644 --- a/src/mesh/MeshModule.cpp +++ b/src/mesh/MeshModule.cpp @@ -10,6 +10,7 @@ std::vector *MeshModule::modules; const meshtastic_MeshPacket *MeshModule::currentRequest; +uint8_t MeshModule::numPeriodicModules = 0; /** * If any of the current chain of modules has already sent a reply, it will be here. This is useful to allow @@ -35,6 +36,15 @@ MeshModule::~MeshModule() modules->erase(it); } +// ⚠️ **Only call once** to set the initial delay before a module starts broadcasting periodically +int32_t MeshModule::setStartDelay() +{ + int32_t startDelay = MESHMODULE_MIN_BROADCAST_DELAY_MS + numPeriodicModules * MESHMODULE_BROADCAST_SPACING_MS; + numPeriodicModules++; + + return startDelay; +} + meshtastic_MeshPacket *MeshModule::allocAckNak(meshtastic_Routing_Error err, NodeNum to, PacketId idFrom, ChannelIndex chIndex, uint8_t hopLimit) { diff --git a/src/mesh/MeshModule.h b/src/mesh/MeshModule.h index a88f1e6ff..f08b8f49c 100644 --- a/src/mesh/MeshModule.h +++ b/src/mesh/MeshModule.h @@ -9,6 +9,9 @@ #include #endif +#define MESHMODULE_MIN_BROADCAST_DELAY_MS 30 * 1000 // Min. delay after boot before sending first broadcast by any module +#define MESHMODULE_BROADCAST_SPACING_MS 15 * 1000 // Initial spacing between broadcasts of different modules + /** handleReceived return enumeration * * Use ProcessMessage::CONTINUE to allows other modules to process a message. @@ -119,6 +122,12 @@ class MeshModule */ static const meshtastic_MeshPacket *currentRequest; + // We keep track of the number of modules that send a periodic broadcast to schedule them spaced out over time + static uint8_t numPeriodicModules; + + // Set the start delay for module that broadcasts periodically + int32_t setStartDelay(); + /** * If your handler wants to send a response, simply set currentReply and it will be sent at the end of response handling. */ diff --git a/src/modules/DetectionSensorModule.cpp b/src/modules/DetectionSensorModule.cpp index c479867fc..ca682b772 100644 --- a/src/modules/DetectionSensorModule.cpp +++ b/src/modules/DetectionSensorModule.cpp @@ -81,7 +81,7 @@ int32_t DetectionSensorModule::runOnce() } LOG_INFO("Detection Sensor Module: init"); - return DELAYED_INTERVAL; + return setStartDelay(); } // LOG_DEBUG("Detection Sensor Module: Current pin state: %i", digitalRead(moduleConfig.detection_sensor.monitor_pin)); @@ -161,4 +161,4 @@ bool DetectionSensorModule::hasDetectionEvent() bool currentState = digitalRead(moduleConfig.detection_sensor.monitor_pin); // 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/NodeInfoModule.cpp b/src/modules/NodeInfoModule.cpp index b55d47d5b..ce4a6bd06 100644 --- a/src/modules/NodeInfoModule.cpp +++ b/src/modules/NodeInfoModule.cpp @@ -97,8 +97,9 @@ NodeInfoModule::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 * - 1000); // Send our initial owner announcement 30 seconds after we start (to give network time to setup) + + setIntervalFromNow(setStartDelay()); // Send our initial owner announcement 30 seconds + // after we start (to give network time to setup) } int32_t NodeInfoModule::runOnce() @@ -112,4 +113,4 @@ int32_t NodeInfoModule::runOnce() 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 95a47f0a1..e0f5b513f 100644 --- a/src/modules/PositionModule.cpp +++ b/src/modules/PositionModule.cpp @@ -28,8 +28,9 @@ PositionModule::PositionModule() nodeStatusObserver.observe(&nodeStatus->onNewStatus); if (config.device.role != meshtastic_Config_DeviceConfig_Role_TRACKER && - config.device.role != meshtastic_Config_DeviceConfig_Role_TAK_TRACKER) - setIntervalFromNow(60 * 1000); + config.device.role != meshtastic_Config_DeviceConfig_Role_TAK_TRACKER) { + setIntervalFromNow(setStartDelay()); + } // Power saving trackers should clear their position on startup to avoid waking up and sending a stale position if ((config.device.role == meshtastic_Config_DeviceConfig_Role_TRACKER || diff --git a/src/modules/Telemetry/AirQualityTelemetry.cpp b/src/modules/Telemetry/AirQualityTelemetry.cpp index 6a8077f03..392bd6148 100644 --- a/src/modules/Telemetry/AirQualityTelemetry.cpp +++ b/src/modules/Telemetry/AirQualityTelemetry.cpp @@ -50,12 +50,12 @@ int32_t AirQualityTelemetryModule::runOnce() nodeTelemetrySensorsMap[meshtastic_TelemetrySensorType_PMSA003I].first = found.address.address; nodeTelemetrySensorsMap[meshtastic_TelemetrySensorType_PMSA003I].second = i2cScanner->fetchI2CBus(found.address); - return 1000; + return setStartDelay(); } #endif return disable(); } - return 1000; + return setStartDelay(); } return disable(); } else { diff --git a/src/modules/Telemetry/DeviceTelemetry.h b/src/modules/Telemetry/DeviceTelemetry.h index 19b7d5b01..a1d55a596 100644 --- a/src/modules/Telemetry/DeviceTelemetry.h +++ b/src/modules/Telemetry/DeviceTelemetry.h @@ -18,7 +18,7 @@ class DeviceTelemetryModule : private concurrency::OSThread, public ProtobufModu uptimeWrapCount = 0; uptimeLastMs = millis(); nodeStatusObserver.observe(&nodeStatus->onNewStatus); - setIntervalFromNow(45 * 1000); // Wait until NodeInfo is sent + setIntervalFromNow(setStartDelay()); // Wait until NodeInfo is sent } virtual bool wantUIFrame() { return false; } @@ -62,4 +62,4 @@ class DeviceTelemetryModule : private concurrency::OSThread, public ProtobufModu uint32_t uptimeWrapCount; uint32_t uptimeLastMs; -}; +}; \ No newline at end of file diff --git a/src/modules/Telemetry/EnvironmentTelemetry.cpp b/src/modules/Telemetry/EnvironmentTelemetry.cpp index 6a5e8376d..3fa3e848a 100644 --- a/src/modules/Telemetry/EnvironmentTelemetry.cpp +++ b/src/modules/Telemetry/EnvironmentTelemetry.cpp @@ -107,8 +107,6 @@ int32_t EnvironmentTelemetryModule::runOnce() if (moduleConfig.telemetry.environment_measurement_enabled) { 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 SENSECAP_INDICATOR result = indicatorSensor.runOnce(); #endif @@ -171,7 +169,9 @@ int32_t EnvironmentTelemetryModule::runOnce() #endif #endif } - return result; + // 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 + return result == UINT32_MAX ? disable() : setStartDelay(); } else { // if we somehow got to a second run of this module with measurement disabled, then just wait forever if (!moduleConfig.telemetry.environment_measurement_enabled) { diff --git a/src/modules/Telemetry/HealthTelemetry.cpp b/src/modules/Telemetry/HealthTelemetry.cpp index 1b9b49813..a2a18ba03 100644 --- a/src/modules/Telemetry/HealthTelemetry.cpp +++ b/src/modules/Telemetry/HealthTelemetry.cpp @@ -62,7 +62,7 @@ int32_t HealthTelemetryModule::runOnce() if (max30102Sensor.hasSensor()) result = max30102Sensor.runOnce(); } - return result; + return result == UINT32_MAX ? disable() : setStartDelay(); } else { // if we somehow got to a second run of this module with measurement disabled, then just wait forever if (!moduleConfig.telemetry.health_measurement_enabled) { diff --git a/src/modules/Telemetry/PowerTelemetry.cpp b/src/modules/Telemetry/PowerTelemetry.cpp index 38a5c6f11..04bcbe200 100644 --- a/src/modules/Telemetry/PowerTelemetry.cpp +++ b/src/modules/Telemetry/PowerTelemetry.cpp @@ -65,7 +65,7 @@ int32_t PowerTelemetryModule::runOnce() if (max17048Sensor.hasSensor() && !max17048Sensor.isInitialized()) result = max17048Sensor.runOnce(); } - return result; + return result == UINT32_MAX ? disable() : setStartDelay(); #else return disable(); #endif From 2d42e1b2bcd85ca80ec1c3ed5ba9600a21a75182 Mon Sep 17 00:00:00 2001 From: Manuel <71137295+mverch67@users.noreply.github.com> Date: Mon, 27 Jan 2025 21:00:12 +0100 Subject: [PATCH 70/89] fix: TCXO_OPTIONAL featuring SenseCAP Indicator (V1/V2) (#5948) * fix TCXO_OPTIONAL * fix LOG_WARN * fix lora.begin() returns -707 * trunk fmt --- src/main.cpp | 38 +++++++++---------- src/mesh/SX126xInterface.cpp | 15 ++------ src/mesh/SX126xInterface.h | 3 ++ .../diy/nrf52_promicro_diy_tcxo/variant.h | 3 +- variants/seeed-sensecap-indicator/variant.h | 3 ++ 5 files changed, 28 insertions(+), 34 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index 24fc71749..f4599e0e3 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -115,10 +115,6 @@ AccelerometerThread *accelerometerThread = nullptr; AudioThread *audioThread = nullptr; #endif -#if defined(TCXO_OPTIONAL) -float tcxoVoltage = SX126X_DIO3_TCXO_VOLTAGE; // if TCXO is optional, put this here so it can be changed further down. -#endif - using namespace concurrency; volatile static const char slipstreamTZString[] = USERPREFS_TZ_STRING; @@ -928,13 +924,16 @@ void setup() #if defined(USE_SX1262) && !defined(ARCH_PORTDUINO) && !defined(TCXO_OPTIONAL) && RADIOLIB_EXCLUDE_SX126X != 1 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()) { + auto *sxIf = new SX1262Interface(RadioLibHAL, SX126X_CS, SX126X_DIO1, SX126X_RESET, SX126X_BUSY); +#ifdef SX126X_DIO3_TCXO_VOLTAGE + sxIf->setTCXOVoltage(SX126X_DIO3_TCXO_VOLTAGE); +#endif + if (!sxIf->init()) { LOG_WARN("No SX1262 radio"); - delete rIf; - rIf = NULL; + delete sxIf; } else { LOG_INFO("SX1262 init success"); + rIf = sxIf; radioType = SX1262_RADIO; } } @@ -942,29 +941,28 @@ void setup() #if defined(USE_SX1262) && !defined(ARCH_PORTDUINO) && defined(TCXO_OPTIONAL) if ((!rIf) && (config.lora.region != meshtastic_Config_LoRaConfig_RegionCode_LORA_24)) { - // Try using the specified TCXO voltage - rIf = new SX1262Interface(RadioLibHAL, SX126X_CS, SX126X_DIO1, SX126X_RESET, SX126X_BUSY); - if (!rIf->init()) { - 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 + // try using the specified TCXO voltage + auto *sxIf = new SX1262Interface(RadioLibHAL, SX126X_CS, SX126X_DIO1, SX126X_RESET, SX126X_BUSY); + sxIf->setTCXOVoltage(SX126X_DIO3_TCXO_VOLTAGE); + if (!sxIf->init()) { + LOG_WARN("No SX1262 radio with TCXO, Vref %fV", SX126X_DIO3_TCXO_VOLTAGE); + delete sxIf; } else { - LOG_WARN("SX1262 init success, TCXO, Vref %f V", tcxoVoltage); + LOG_INFO("SX1262 init success, TCXO, Vref %fV", SX126X_DIO3_TCXO_VOLTAGE); + rIf = sxIf; radioType = SX1262_RADIO; } } if ((!rIf) && (config.lora.region != meshtastic_Config_LoRaConfig_RegionCode_LORA_24)) { - // If specified TCXO voltage fails, attempt to use DIO3 as a reference instea + // If specified TCXO voltage fails, attempt to use DIO3 as a reference instead rIf = new SX1262Interface(RadioLibHAL, SX126X_CS, SX126X_DIO1, SX126X_RESET, SX126X_BUSY); if (!rIf->init()) { - LOG_WARN("No SX1262 radio with XTAL, Vref %f V", tcxoVoltage); + LOG_WARN("No SX1262 radio with XTAL, Vref 0.0V"); 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 init success, XTAL, Vref %f V", tcxoVoltage); + LOG_INFO("SX1262 init success, XTAL, Vref 0.0V"); radioType = SX1262_RADIO; } } diff --git a/src/mesh/SX126xInterface.cpp b/src/mesh/SX126xInterface.cpp index 8a7bc7670..5710de7ea 100644 --- a/src/mesh/SX126xInterface.cpp +++ b/src/mesh/SX126xInterface.cpp @@ -50,22 +50,13 @@ template bool SX126xInterface::init() #endif #if ARCH_PORTDUINO - float tcxoVoltage = (float)settingsMap[dio3_tcxo_voltage] / 1000; + tcxoVoltage = (float)settingsMap[dio3_tcxo_voltage] / 1000; if (settingsMap[sx126x_ant_sw_pin] != RADIOLIB_NC) { digitalWrite(settingsMap[sx126x_ant_sw_pin], HIGH); pinMode(settingsMap[sx126x_ant_sw_pin], OUTPUT); } -// FIXME: correct logic to default to not using TCXO if no voltage is specified for SX126X_DIO3_TCXO_VOLTAGE -#elif !defined(SX126X_DIO3_TCXO_VOLTAGE) - float tcxoVoltage = - 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/SX126x/SX126x.h#L471C26-L471C104 - // (DIO3 is free to be used as an IRQ) -#elif !defined(TCXO_OPTIONAL) - float tcxoVoltage = SX126X_DIO3_TCXO_VOLTAGE; - // (DIO3 is not free to be used as an IRQ) #endif - if (tcxoVoltage == 0) + if (tcxoVoltage == 0.0) 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", tcxoVoltage); @@ -83,7 +74,7 @@ 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", res); - if (res == RADIOLIB_ERR_CHIP_NOT_FOUND) + if (res == RADIOLIB_ERR_CHIP_NOT_FOUND || res == RADIOLIB_ERR_SPI_CMD_FAILED) return false; LOG_INFO("Frequency set to %f", getFreq()); diff --git a/src/mesh/SX126xInterface.h b/src/mesh/SX126xInterface.h index 45b39a68a..47b07c284 100644 --- a/src/mesh/SX126xInterface.h +++ b/src/mesh/SX126xInterface.h @@ -28,8 +28,11 @@ template class SX126xInterface : public RadioLibInterface bool isIRQPending() override { return lora.getIrqFlags() != 0; } + void setTCXOVoltage(float voltage) { tcxoVoltage = voltage; } + protected: float currentLimit = 140; // Higher OCP limit for SX126x PA + float tcxoVoltage = 0.0; /** * Specific module instance diff --git a/variants/diy/nrf52_promicro_diy_tcxo/variant.h b/variants/diy/nrf52_promicro_diy_tcxo/variant.h index 6ffb86cff..5e939c023 100644 --- a/variants/diy/nrf52_promicro_diy_tcxo/variant.h +++ b/variants/diy/nrf52_promicro_diy_tcxo/variant.h @@ -183,8 +183,7 @@ settings. */ #define SX126X_DIO3_TCXO_VOLTAGE 1.8 -#define TCXO_OPTIONAL // make it so that the firmware can try both TCXO and XTAL -extern float tcxoVoltage; // make this available everywhere +#define TCXO_OPTIONAL // make it so that the firmware can try both TCXO and XTAL #ifdef __cplusplus } diff --git a/variants/seeed-sensecap-indicator/variant.h b/variants/seeed-sensecap-indicator/variant.h index 29d547be3..c5fc685cd 100644 --- a/variants/seeed-sensecap-indicator/variant.h +++ b/variants/seeed-sensecap-indicator/variant.h @@ -70,5 +70,8 @@ #define SX126X_RESET LORA_RESET #define SX126X_DIO2_AS_RF_SWITCH +#define TCXO_OPTIONAL // handle Indicator V1 and V2 +#define SX126X_DIO3_TCXO_VOLTAGE 1.8 + #define USE_VIRTUAL_KEYBOARD 1 #define DISPLAY_CLOCK_FRAME 1 From 30a31a3a13caec27fa1d09e119e97fdb2881b8a6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20G=C3=B6ttgens?= Date: Tue, 28 Jan 2025 15:38:22 +0100 Subject: [PATCH 71/89] Oem logo (#5939) * reinstate oemlogo, add to userPrefs.jsonc * disable from default build --- src/graphics/Screen.cpp | 76 ++++++++++++++++++++++++++++++++++++++--- userPrefs.jsonc | 10 ++++-- 2 files changed, 78 insertions(+), 8 deletions(-) diff --git a/src/graphics/Screen.cpp b/src/graphics/Screen.cpp index 198dcc235..c9004432f 100644 --- a/src/graphics/Screen.cpp +++ b/src/graphics/Screen.cpp @@ -123,7 +123,7 @@ static bool heartbeat = false; #define getStringCenteredX(s) ((SCREEN_WIDTH - display->getStringWidth(s)) / 2) -/// Check if the display can render a string (detect special chars; emoji) +// Check if the display can render a string (detect special chars; emoji) static bool haveGlyphs(const char *str) { #if defined(OLED_PL) || defined(OLED_UA) || defined(OLED_RU) || defined(OLED_CS) @@ -162,11 +162,7 @@ static void drawIconScreen(const char *upperMsg, OLEDDisplay *display, OLEDDispl display->setFont(FONT_MEDIUM); display->setTextAlignment(TEXT_ALIGN_LEFT); -#ifdef USERPREFS_SPLASH_TITLE - const char *title = USERPREFS_SPLASH_TITLE; -#else const char *title = "meshtastic.org"; -#endif display->drawString(x + getStringCenteredX(title), y + SCREEN_HEIGHT - FONT_HEIGHT_MEDIUM, title); display->setFont(FONT_SMALL); @@ -185,6 +181,56 @@ 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 } +#ifdef USERPREFS_OEM_TEXT + +static void drawOEMIconScreen(const char *upperMsg, OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y) +{ + static const uint8_t xbm[] = USERPREFS_OEM_IMAGE_DATA; + display->drawXbm(x + (SCREEN_WIDTH - USERPREFS_OEM_IMAGE_WIDTH) / 2, + y + (SCREEN_HEIGHT - FONT_HEIGHT_MEDIUM - USERPREFS_OEM_IMAGE_HEIGHT) / 2 + 2, USERPREFS_OEM_IMAGE_WIDTH, + USERPREFS_OEM_IMAGE_HEIGHT, xbm); + + switch (USERPREFS_OEM_FONT_SIZE) { + 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 = USERPREFS_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); +} + +#endif + void Screen::drawFrameText(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y, const char *message) { uint16_t x_offset = display->width() / 2; @@ -1658,6 +1704,10 @@ void Screen::setup() // Set the utf8 conversion function dispdev->setFontTableLookupFunction(customFontTableLookup); +#ifdef USERPREFS_OEM_TEXT + logo_timeout *= 2; // Double the time if we have a custom logo +#endif + // Add frames. EINK_ADD_FRAMEFLAG(dispdev, DEMAND_FAST); alertFrames[0] = [this](OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y) -> void { @@ -1803,6 +1853,22 @@ int32_t Screen::runOnce() showingBootScreen = false; } +#ifdef USERPREFS_OEM_TEXT + static bool showingOEMBootScreen = true; + if (showingOEMBootScreen && (millis() > ((logo_timeout / 2) + serialSinceMsec))) { + LOG_INFO("Switch to OEM screen..."); + // 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; + } +#endif + #ifndef DISABLE_WELCOME_UNSET if (showingNormalScreen && config.lora.region == meshtastic_Config_LoRaConfig_RegionCode_UNSET) { setWelcomeFrames(); diff --git a/userPrefs.jsonc b/userPrefs.jsonc index 055f59273..de610464d 100644 --- a/userPrefs.jsonc +++ b/userPrefs.jsonc @@ -29,9 +29,13 @@ // "USERPREFS_FIXED_GPS_LON": "2.294508368", // "USERPREFS_LORACONFIG_CHANNEL_NUM": "31", // "USERPREFS_LORACONFIG_MODEM_PRESET": "meshtastic_Config_LoRaConfig_ModemPreset_SHORT_FAST", - // "USERPREFS_SPLASH_TITLE": "DEFCONtastic", "USERPREFS_TZ_STRING": "tzplaceholder " // "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 }", // "USERPREFS_USE_ADMIN_KEY_1": "{}", - // "USERPREFS_USE_ADMIN_KEY_2": "{}" -} \ No newline at end of file + // "USERPREFS_USE_ADMIN_KEY_2": "{}", + // "USERPREFS_OEM_TEXT": "Caterham Car Club", + // "USERPREFS_OEM_FONT_SIZE": "0", + // "USERPREFS_OEM_IMAGE_WIDTH": "50", + // "USERPREFS_OEM_IMAGE_HEIGHT": "28", + // "USERPREFS_OEM_IMAGE_DATA": "{ 0x00, 0x00, 0xF0, 0x3F, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x00, 0xC0, 0x07, 0x80, 0x0F, 0x00, 0x00, 0x00, 0xF0, 0x00, 0x00, 0x3C, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x18, 0xFF, 0xFF, 0x61, 0x00, 0x00, 0x00, 0x0C, 0xFF, 0xFF, 0xC7, 0x00, 0x00, 0x00, 0x0C, 0xFF, 0xFF, 0xC7, 0x00, 0x00, 0x00, 0x18, 0xFF, 0xFF, 0x67, 0x00, 0x00, 0x00, 0x18, 0x1F, 0xF0, 0x67, 0x00, 0x00, 0x00, 0x30, 0x1F, 0xF8, 0x33, 0x00, 0x00, 0x00, 0x30, 0x00, 0xFC, 0x31, 0x00, 0x00, 0x00, 0x60, 0x00, 0xFE, 0x18, 0x00, 0x00, 0x00, 0x60, 0x00, 0x7E, 0x18, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x3F, 0x0C, 0x00, 0x00, 0x00, 0xC0, 0x80, 0x1F, 0x0C, 0x00, 0x00, 0x00, 0x80, 0x81, 0x1F, 0x06, 0x00, 0x00, 0x00, 0x80, 0xC1, 0x0F, 0x06, 0x00, 0x00, 0x00, 0x00, 0xC3, 0x0F, 0x03, 0x00, 0x00, 0x00, 0x00, 0xC3, 0x0F, 0x03, 0x00, 0x00, 0x00, 0x00, 0xE6, 0x8F, 0x01, 0x00, 0x00, 0x00, 0x00, 0xEE, 0xC7, 0x01, 0x00, 0x00, 0x00, 0x00, 0x0C, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1C, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x07, 0x00, 0x00, 0x00}" +} From 6a12760c3d07805b5af614820af8732751be6037 Mon Sep 17 00:00:00 2001 From: Tom Fifield Date: Wed, 29 Jan 2025 09:57:52 +0800 Subject: [PATCH 72/89] Fix off-by-one error with log writes (#5959) As reported by @jstockdale, when writing coloured logs we were writing the full string, including a null terminator. This caused issues for programs consuming our logs. The fix as identified is not to write the null. Fixes https://github.com/meshtastic/firmware/issues/5945 --- src/RedirectablePrint.cpp | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/src/RedirectablePrint.cpp b/src/RedirectablePrint.cpp index 57f53019d..07f873864 100644 --- a/src/RedirectablePrint.cpp +++ b/src/RedirectablePrint.cpp @@ -79,17 +79,17 @@ size_t RedirectablePrint::vprintf(const char *logLevel, const char *format, va_l } if (color && logLevel != nullptr) { if (strcmp(logLevel, MESHTASTIC_LOG_LEVEL_DEBUG) == 0) - Print::write("\u001b[34m", 6); + Print::write("\u001b[34m", 5); if (strcmp(logLevel, MESHTASTIC_LOG_LEVEL_INFO) == 0) - Print::write("\u001b[32m", 6); + Print::write("\u001b[32m", 5); if (strcmp(logLevel, MESHTASTIC_LOG_LEVEL_WARN) == 0) - Print::write("\u001b[33m", 6); + Print::write("\u001b[33m", 5); if (strcmp(logLevel, MESHTASTIC_LOG_LEVEL_ERROR) == 0) - Print::write("\u001b[31m", 6); + Print::write("\u001b[31m", 5); } len = Print::write(printBuf, len); if (color && logLevel != nullptr) { - Print::write("\u001b[0m", 5); + Print::write("\u001b[0m", 4); } return len; } @@ -107,15 +107,15 @@ void RedirectablePrint::log_to_serial(const char *logLevel, const char *format, // include the header if (color) { if (strcmp(logLevel, MESHTASTIC_LOG_LEVEL_DEBUG) == 0) - Print::write("\u001b[34m", 6); + Print::write("\u001b[34m", 5); if (strcmp(logLevel, MESHTASTIC_LOG_LEVEL_INFO) == 0) - Print::write("\u001b[32m", 6); + Print::write("\u001b[32m", 5); if (strcmp(logLevel, MESHTASTIC_LOG_LEVEL_WARN) == 0) - Print::write("\u001b[33m", 6); + Print::write("\u001b[33m", 5); if (strcmp(logLevel, MESHTASTIC_LOG_LEVEL_ERROR) == 0) - Print::write("\u001b[31m", 6); + Print::write("\u001b[31m", 5); if (strcmp(logLevel, MESHTASTIC_LOG_LEVEL_TRACE) == 0) - Print::write("\u001b[35m", 6); + Print::write("\u001b[35m", 5); } uint32_t rtc_sec = getValidTime(RTCQuality::RTCQualityDevice, true); // display local time on logfile @@ -393,4 +393,4 @@ std::string RedirectablePrint::mt_sprintf(const std::string fmt_str, ...) break; } return std::string(formatted.get()); -} +} \ No newline at end of file From 78da8f6fc43591d9fbd5822fb2a885d9e8a93258 Mon Sep 17 00:00:00 2001 From: Austin Date: Wed, 29 Jan 2025 06:51:26 -0500 Subject: [PATCH 73/89] Portduino: Allow limiting TX Power from yaml (#5954) --- bin/config-dist.yaml | 4 +++- bin/config.d/lora-MeshAdv-900M30S.yaml | 3 +++ src/mesh/LR11x0Interface.cpp | 6 ++++++ src/mesh/RF95Interface.cpp | 5 ++++- src/mesh/SX126xInterface.cpp | 5 ++++- src/mesh/SX128xInterface.cpp | 5 ++++- src/platform/portduino/PortduinoGlue.cpp | 8 +++++++- src/platform/portduino/PortduinoGlue.h | 7 ++++++- 8 files changed, 37 insertions(+), 6 deletions(-) diff --git a/bin/config-dist.yaml b/bin/config-dist.yaml index c8f181308..1bf52fda2 100644 --- a/bin/config-dist.yaml +++ b/bin/config-dist.yaml @@ -78,6 +78,8 @@ Lora: # TXen: x # TX and RX enable pins # RXen: x +# SX126X_MAX_POWER: 8 # Limit the output power to 8 dBm, useful for amped nodes + # spiSpeed: 2000000 ### Set default/fallback gpio chip to use in /dev/. Defaults to 0. @@ -188,4 +190,4 @@ General: MaxMessageQueue: 100 ConfigDirectory: /etc/meshtasticd/config.d/ # MACAddress: AA:BB:CC:DD:EE:FF -# MACAddressSource: eth0 +# MACAddressSource: eth0 \ No newline at end of file diff --git a/bin/config.d/lora-MeshAdv-900M30S.yaml b/bin/config.d/lora-MeshAdv-900M30S.yaml index 07dada620..113901d5e 100644 --- a/bin/config.d/lora-MeshAdv-900M30S.yaml +++ b/bin/config.d/lora-MeshAdv-900M30S.yaml @@ -7,3 +7,6 @@ Lora: TXen: 13 RXen: 12 DIO3_TCXO_VOLTAGE: true + # Only for E22-900M33S: + # Limit the output power to 8 dBm + # SX126X_MAX_POWER: 8 \ No newline at end of file diff --git a/src/mesh/LR11x0Interface.cpp b/src/mesh/LR11x0Interface.cpp index ce4f912ba..5a9a53d2d 100644 --- a/src/mesh/LR11x0Interface.cpp +++ b/src/mesh/LR11x0Interface.cpp @@ -20,12 +20,18 @@ static const Module::RfSwitchMode_t rfswitch_table[] = { // Particular boards might define a different max power based on what their hardware can do, default to max power output if not // specified (may be dangerous if using external PA and LR11x0 power config forgotten) +#if ARCH_PORTDUINO +#define LR1110_MAX_POWER settingsMap[lr1110_max_power] +#endif #ifndef LR1110_MAX_POWER #define LR1110_MAX_POWER 22 #endif // the 2.4G part maxes at 13dBm +#if ARCH_PORTDUINO +#define LR1120_MAX_POWER settingsMap[lr1120_max_power] +#endif #ifndef LR1120_MAX_POWER #define LR1120_MAX_POWER 13 #endif diff --git a/src/mesh/RF95Interface.cpp b/src/mesh/RF95Interface.cpp index d4d9ad23c..1dfc72708 100644 --- a/src/mesh/RF95Interface.cpp +++ b/src/mesh/RF95Interface.cpp @@ -9,6 +9,9 @@ #include "PortduinoGlue.h" #endif +#if ARCH_PORTDUINO +#define RF95_MAX_POWER settingsMap[rf95_max_power] +#endif #ifndef RF95_MAX_POWER #define RF95_MAX_POWER 20 #endif @@ -337,4 +340,4 @@ bool RF95Interface::sleep() return true; } -#endif +#endif \ No newline at end of file diff --git a/src/mesh/SX126xInterface.cpp b/src/mesh/SX126xInterface.cpp index 5710de7ea..7c950bc8e 100644 --- a/src/mesh/SX126xInterface.cpp +++ b/src/mesh/SX126xInterface.cpp @@ -11,6 +11,9 @@ // Particular boards might define a different max power based on what their hardware can do, default to max power output if not // specified (may be dangerous if using external PA and SX126x power config forgotten) +#if ARCH_PORTDUINO +#define SX126X_MAX_POWER settingsMap[sx126x_max_power] +#endif #ifndef SX126X_MAX_POWER #define SX126X_MAX_POWER 22 #endif @@ -333,4 +336,4 @@ template bool SX126xInterface::sleep() return true; } -#endif +#endif \ No newline at end of file diff --git a/src/mesh/SX128xInterface.cpp b/src/mesh/SX128xInterface.cpp index ee3408456..1032934b8 100644 --- a/src/mesh/SX128xInterface.cpp +++ b/src/mesh/SX128xInterface.cpp @@ -10,6 +10,9 @@ #endif // Particular boards might define a different max power based on what their hardware can do +#if ARCH_PORTDUINO +#define SX128X_MAX_POWER settingsMap[sx128x_max_power] +#endif #ifndef SX128X_MAX_POWER #define SX128X_MAX_POWER 13 #endif @@ -315,4 +318,4 @@ template bool SX128xInterface::sleep() return true; } -#endif +#endif \ No newline at end of file diff --git a/src/platform/portduino/PortduinoGlue.cpp b/src/platform/portduino/PortduinoGlue.cpp index ab78baa1a..d7ff4fc65 100644 --- a/src/platform/portduino/PortduinoGlue.cpp +++ b/src/platform/portduino/PortduinoGlue.cpp @@ -369,6 +369,12 @@ bool loadConfig(const char *configPath) } } + settingsMap[sx126x_max_power] = yamlConfig["Lora"]["SX126X_MAX_POWER"].as(22); + settingsMap[sx128x_max_power] = yamlConfig["Lora"]["SX128X_MAX_POWER"].as(13); + settingsMap[lr1110_max_power] = yamlConfig["Lora"]["LR1110_MAX_POWER"].as(22); + settingsMap[lr1120_max_power] = yamlConfig["Lora"]["LR1120_MAX_POWER"].as(13); + settingsMap[rf95_max_power] = yamlConfig["Lora"]["RF95_MAX_POWER"].as(20); + settingsMap[dio2_as_rf_switch] = yamlConfig["Lora"]["DIO2_AS_RF_SWITCH"].as(false); settingsMap[dio3_tcxo_voltage] = yamlConfig["Lora"]["DIO3_TCXO_VOLTAGE"].as(0) * 1000; if (settingsMap[dio3_tcxo_voltage] == 0 && yamlConfig["Lora"]["DIO3_TCXO_VOLTAGE"].as(false)) { @@ -569,4 +575,4 @@ bool MAC_from_string(std::string mac_str, uint8_t *dmac) } else { return false; } -} +} \ No newline at end of file diff --git a/src/platform/portduino/PortduinoGlue.h b/src/platform/portduino/PortduinoGlue.h index d1e91956d..c6b5f8b41 100644 --- a/src/platform/portduino/PortduinoGlue.h +++ b/src/platform/portduino/PortduinoGlue.h @@ -27,6 +27,11 @@ enum configNames { sx126x_ant_sw_pin, sx126x_ant_sw_line, sx126x_ant_sw_gpiochip, + sx126x_max_power, + sx128x_max_power, + lr1110_max_power, + lr1120_max_power, + rf95_max_power, dio2_as_rf_switch, dio3_tcxo_voltage, use_rf95, @@ -94,4 +99,4 @@ int initGPIOPin(int pinNum, std::string gpioChipname, int line); bool loadConfig(const char *configPath); static bool ends_with(std::string_view str, std::string_view suffix); void getMacAddr(uint8_t *dmac); -bool MAC_from_string(std::string mac_str, uint8_t *dmac); +bool MAC_from_string(std::string mac_str, uint8_t *dmac); \ No newline at end of file From cd8592ef4accb3e758e8bae80744eacc93df7015 Mon Sep 17 00:00:00 2001 From: Jason P Date: Wed, 29 Jan 2025 06:14:43 -0600 Subject: [PATCH 74/89] Fixes #5766 Updated MQTT privateCidrRanges to add Tailscale (#5957) --- src/mqtt/MQTT.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/mqtt/MQTT.cpp b/src/mqtt/MQTT.cpp index f642af231..f808a66ef 100644 --- a/src/mqtt/MQTT.cpp +++ b/src/mqtt/MQTT.cpp @@ -217,6 +217,7 @@ bool isPrivateIpAddress(const IPAddress &ip) {.network = 169u << 24 | 254 << 16, .mask = 0xffff0000}, // 169.254.0.0/16 {.network = 10u << 24, .mask = 0xff000000}, // 10.0.0.0/8 {.network = 127u << 24 | 1, .mask = 0xffffffff}, // 127.0.0.1/32 + {.network = 100u << 24 | 64 << 16, .mask = 0xffc00000}, // 100.64.0.0/10 }; const uint32_t addr = ntohl(ip); for (const auto &cidrRange : privateCidrRanges) { From b5cad2b65e934efac1bbf1d92a6616c12e680a39 Mon Sep 17 00:00:00 2001 From: Ben Meadors Date: Wed, 29 Jan 2025 20:52:24 -0600 Subject: [PATCH 75/89] Fix negative decimal value detection in userPrefs (#5963) --- bin/platformio-custom.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bin/platformio-custom.py b/bin/platformio-custom.py index acfeae10c..09e8e6d83 100644 --- a/bin/platformio-custom.py +++ b/bin/platformio-custom.py @@ -102,7 +102,7 @@ pref_flags = [] for pref in userPrefs: if userPrefs[pref].startswith("{"): pref_flags.append("-D" + pref + "=" + userPrefs[pref]) - elif userPrefs[pref].replace(".", "").isdigit(): + elif userPrefs[pref].lstrip("-").replace(".", "").isdigit(): pref_flags.append("-D" + pref + "=" + userPrefs[pref]) elif userPrefs[pref] == "true" or userPrefs[pref] == "false": pref_flags.append("-D" + pref + "=" + userPrefs[pref]) From 4c0e0b84712e08371d84400187e42996bb5bc254 Mon Sep 17 00:00:00 2001 From: Austin Date: Sat, 1 Feb 2025 03:58:58 -0500 Subject: [PATCH 76/89] Portduino: Set Web SSL Cert / Key paths from yaml (#5961) --- Dockerfile | 3 ++- alpine.Dockerfile | 3 ++- bin/config-dist.yaml | 2 ++ debian/meshtasticd.dirs | 3 ++- meshtasticd.spec.rpkg | 3 +++ src/mesh/raspihttp/PiWebServer.cpp | 18 +++++++++++------- src/platform/portduino/PortduinoGlue.cpp | 7 ++++++- src/platform/portduino/PortduinoGlue.h | 2 ++ 8 files changed, 30 insertions(+), 11 deletions(-) diff --git a/Dockerfile b/Dockerfile index f3b294a5b..f9a3b9962 100644 --- a/Dockerfile +++ b/Dockerfile @@ -38,7 +38,8 @@ USER root RUN apt-get update && apt-get --no-install-recommends -y install libc-bin libc6 libgpiod2 libyaml-cpp0.7 libi2c0 libulfius2.7 libusb-1.0-0-dev liborcania2.3 libssl3 && \ apt-get clean && rm -rf /var/lib/apt/lists/* \ && mkdir -p /var/lib/meshtasticd \ - && mkdir -p /etc/meshtasticd/config.d + && mkdir -p /etc/meshtasticd/config.d \ + && mkdir -p /etc/meshtasticd/ssl # Fetch compiled binary from the builder COPY --from=builder /tmp/firmware/release/meshtasticd /usr/sbin/ diff --git a/alpine.Dockerfile b/alpine.Dockerfile index 115602b3b..8b48eeca3 100644 --- a/alpine.Dockerfile +++ b/alpine.Dockerfile @@ -29,7 +29,8 @@ USER root RUN apk add libstdc++ libgpiod yaml-cpp libusb i2c-tools \ && mkdir -p /var/lib/meshtasticd \ - && mkdir -p /etc/meshtasticd/config.d + && mkdir -p /etc/meshtasticd/config.d \ + && mkdir -p /etc/meshtasticd/ssl COPY --from=builder /tmp/firmware/release/meshtasticd /usr/sbin/ WORKDIR /var/lib/meshtasticd diff --git a/bin/config-dist.yaml b/bin/config-dist.yaml index 1bf52fda2..da4c192c7 100644 --- a/bin/config-dist.yaml +++ b/bin/config-dist.yaml @@ -184,6 +184,8 @@ Logging: Webserver: # Port: 443 # Port for Webserver & Webservices # RootPath: /usr/share/meshtasticd/web # Root Dir of WebServer +# SSLKey: /etc/meshtasticd/ssl/private_key.pem # Path to SSL Key, generated if not present +# SSLCert: /etc/meshtasticd/ssl/certificate.pem # Path to SSL Certificate, generated if not present General: MaxNodes: 200 diff --git a/debian/meshtasticd.dirs b/debian/meshtasticd.dirs index 5f57ff7be..45a1ca3db 100644 --- a/debian/meshtasticd.dirs +++ b/debian/meshtasticd.dirs @@ -1,4 +1,5 @@ etc/meshtasticd etc/meshtasticd/config.d etc/meshtasticd/available.d -usr/share/meshtasticd/web \ No newline at end of file +usr/share/meshtasticd/web +etc/meshtasticd/ssl \ No newline at end of file diff --git a/meshtasticd.spec.rpkg b/meshtasticd.spec.rpkg index 1819897b0..720e94408 100644 --- a/meshtasticd.spec.rpkg +++ b/meshtasticd.spec.rpkg @@ -72,6 +72,8 @@ install -D -m 0644 bin/meshtasticd.service %{buildroot}%{_unitdir}/meshtasticd.s # Install the web files under /usr/share/meshtasticd/web mkdir -p %{buildroot}%{_datadir}/meshtasticd/web cp -r web/* %{buildroot}%{_datadir}/meshtasticd/web +# Install default SSL storage directory (for web) +mkdir -p %{buildroot}%{_sysconfdir}/meshtasticd/ssl %files %license LICENSE @@ -86,6 +88,7 @@ cp -r web/* %{buildroot}%{_datadir}/meshtasticd/web %dir %{_datadir}/meshtasticd %dir %{_datadir}/meshtasticd/web %{_datadir}/meshtasticd/web/* +%dir %{_sysconfdir}/meshtasticd/ssl %changelog %autochangelog \ No newline at end of file diff --git a/src/mesh/raspihttp/PiWebServer.cpp b/src/mesh/raspihttp/PiWebServer.cpp index 9d2625410..4fae0bc3d 100644 --- a/src/mesh/raspihttp/PiWebServer.cpp +++ b/src/mesh/raspihttp/PiWebServer.cpp @@ -65,6 +65,9 @@ mail: marchammermann@googlemail.com #define DEFAULT_REALM "default_realm" #define PREFIX "" +#define KEY_PATH settingsStrings[websslkeypath].c_str() +#define CERT_PATH settingsStrings[websslcertpath].c_str() + struct _file_config configWeb; // We need to specify some content-type mapping, so the resources get delivered with the @@ -384,13 +387,13 @@ char *read_file_into_string(const char *filename) int PiWebServerThread::CheckSSLandLoad() { // read certificate - cert_pem = read_file_into_string("certificate.pem"); + cert_pem = read_file_into_string(CERT_PATH); if (cert_pem == NULL) { 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"); + key_pem = read_file_into_string(KEY_PATH); if (key_pem == NULL) { LOG_ERROR("ERROR file private_key can't be loaded or is missing"); return 2; @@ -415,8 +418,8 @@ int PiWebServerThread::CreateSSLCertificate() return 2; } - // Ope file to write private key file - FILE *pkey_file = fopen("private_key.pem", "wb"); + // Open file to write private key file + FILE *pkey_file = fopen(KEY_PATH, "wb"); if (!pkey_file) { LOG_ERROR("Error opening private key file"); return 3; @@ -426,18 +429,19 @@ int PiWebServerThread::CreateSSLCertificate() fclose(pkey_file); // open Certificate file - FILE *x509_file = fopen("certificate.pem", "wb"); + FILE *x509_file = fopen(CERT_PATH, "wb"); if (!x509_file) { LOG_ERROR("Error opening cert"); return 4; } - // write cirtificate + // write certificate PEM_write_X509(x509_file, x509); fclose(x509_file); EVP_PKEY_free(pkey); + LOG_INFO("Create SSL Key %s successful", KEY_PATH); X509_free(x509); - LOG_INFO("Create SSL Cert -certificate.pem- succesfull "); + LOG_INFO("Create SSL Cert %s successful", CERT_PATH); return 0; } diff --git a/src/platform/portduino/PortduinoGlue.cpp b/src/platform/portduino/PortduinoGlue.cpp index d7ff4fc65..9da65c92c 100644 --- a/src/platform/portduino/PortduinoGlue.cpp +++ b/src/platform/portduino/PortduinoGlue.cpp @@ -524,7 +524,12 @@ bool loadConfig(const char *configPath) if (yamlConfig["Webserver"]) { settingsMap[webserverport] = (yamlConfig["Webserver"]["Port"]).as(-1); - settingsStrings[webserverrootpath] = (yamlConfig["Webserver"]["RootPath"]).as(""); + settingsStrings[webserverrootpath] = + (yamlConfig["Webserver"]["RootPath"]).as("/usr/share/meshtasticd/web"); + settingsStrings[websslkeypath] = + (yamlConfig["Webserver"]["SSLKey"]).as("/etc/meshtasticd/ssl/private_key.pem"); + settingsStrings[websslcertpath] = + (yamlConfig["Webserver"]["SSLCert"]).as("/etc/meshtasticd/ssl/certificate.pem"); } if (yamlConfig["General"]) { diff --git a/src/platform/portduino/PortduinoGlue.h b/src/platform/portduino/PortduinoGlue.h index c6b5f8b41..a52ca88f8 100644 --- a/src/platform/portduino/PortduinoGlue.h +++ b/src/platform/portduino/PortduinoGlue.h @@ -81,6 +81,8 @@ enum configNames { webserver, webserverport, webserverrootpath, + websslkeypath, + websslcertpath, maxtophone, maxnodes, ascii_logs, From d9534cfc9d487f3c4e3b0a92f9b7244e599813db Mon Sep 17 00:00:00 2001 From: Chloe Bethel Date: Mon, 3 Feb 2025 03:31:54 +0000 Subject: [PATCH 77/89] Remove unused usages of #include to save Flash (#5978) Saves ~100KB on wio-e5, which was previously at 99% Flash usage --- src/mesh/NodeDB.cpp | 1 - src/serialization/JSONValue.cpp | 1 - 2 files changed, 2 deletions(-) diff --git a/src/mesh/NodeDB.cpp b/src/mesh/NodeDB.cpp index 762982287..4a01e0d41 100644 --- a/src/mesh/NodeDB.cpp +++ b/src/mesh/NodeDB.cpp @@ -23,7 +23,6 @@ #include "modules/NeighborInfoModule.h" #include #include -#include #include #include #include diff --git a/src/serialization/JSONValue.cpp b/src/serialization/JSONValue.cpp index b2e9575bf..64dc10abe 100644 --- a/src/serialization/JSONValue.cpp +++ b/src/serialization/JSONValue.cpp @@ -22,7 +22,6 @@ * THE SOFTWARE. */ -#include #include #include #include From b370717dcd50d3961d6c94565df6a86919688af7 Mon Sep 17 00:00:00 2001 From: Woutvstk <119763111+Woutvstk@users.noreply.github.com> Date: Mon, 3 Feb 2025 06:43:32 +0100 Subject: [PATCH 78/89] Add bearing to other node on device screen in text (#5968) * Merge branch 'store-and-forward' of https://github.com/Woutvstk/meshtastic_firmware into store-and-forward * also show bearing to a waypoint in text on screen --- src/graphics/Screen.cpp | 33 +++++++++++++++++------------- src/modules/WaypointModule.cpp | 37 +++++++++++++++++++--------------- 2 files changed, 40 insertions(+), 30 deletions(-) diff --git a/src/graphics/Screen.cpp b/src/graphics/Screen.cpp index c9004432f..4ee49e3c0 100644 --- a/src/graphics/Screen.cpp +++ b/src/graphics/Screen.cpp @@ -1446,9 +1446,9 @@ static void drawNodeInfo(OLEDDisplay *display, OLEDDisplayUiState *state, int16_ static char distStr[20]; if (config.display.units == meshtastic_Config_DisplayConfig_DisplayUnits_IMPERIAL) { - strncpy(distStr, "? mi", sizeof(distStr)); // might not have location data + strncpy(distStr, "? mi ?°", sizeof(distStr)); // might not have location data } else { - strncpy(distStr, "? km", sizeof(distStr)); + strncpy(distStr, "? km ?°", sizeof(distStr)); } meshtastic_NodeInfoLite *ourNode = nodeDB->getMeshNode(nodeDB->getNodeNum()); const char *fields[] = {username, lastStr, signalStr, distStr, NULL}; @@ -1481,18 +1481,6 @@ static void drawNodeInfo(OLEDDisplay *display, OLEDDisplayUiState *state, int16_ float d = GeoCoord::latLongToMeter(DegD(p.latitude_i), DegD(p.longitude_i), DegD(op.latitude_i), DegD(op.longitude_i)); - if (config.display.units == meshtastic_Config_DisplayConfig_DisplayUnits_IMPERIAL) { - if (d < (2 * MILES_TO_FEET)) - snprintf(distStr, sizeof(distStr), "%.0f ft", d * METERS_TO_FEET); - else - snprintf(distStr, sizeof(distStr), "%.1f mi", d * METERS_TO_FEET / MILES_TO_FEET); - } else { - if (d < 2000) - snprintf(distStr, sizeof(distStr), "%.0f m", d); - else - snprintf(distStr, sizeof(distStr), "%.1f km", d / 1000); - } - float bearingToOther = GeoCoord::bearing(DegD(op.latitude_i), DegD(op.longitude_i), DegD(p.latitude_i), DegD(p.longitude_i)); // If the top of the compass is a static north then bearingToOther can be drawn on the compass directly @@ -1500,6 +1488,23 @@ static void drawNodeInfo(OLEDDisplay *display, OLEDDisplayUiState *state, int16_ if (!config.display.compass_north_top) bearingToOther -= myHeading; screen->drawNodeHeading(display, compassX, compassY, compassDiam, bearingToOther); + + float bearingToOtherDegrees = (bearingToOther < 0) ? bearingToOther + 2*PI : bearingToOther; + bearingToOtherDegrees = bearingToOtherDegrees * 180 / PI; + + if (config.display.units == meshtastic_Config_DisplayConfig_DisplayUnits_IMPERIAL) { + if (d < (2 * MILES_TO_FEET)) + snprintf(distStr, sizeof(distStr), "%.0fft %.0f°", d * METERS_TO_FEET, bearingToOtherDegrees); + else + snprintf(distStr, sizeof(distStr), "%.1fmi %.0f°", d * METERS_TO_FEET / MILES_TO_FEET, bearingToOtherDegrees); + } else { + if (d < 2000) + snprintf(distStr, sizeof(distStr), "%.0fm %.0f°", d, bearingToOtherDegrees); + else + snprintf(distStr, sizeof(distStr), "%.1fkm %.0f°", d / 1000, bearingToOtherDegrees); + } + + } } if (!hasNodeHeading) { diff --git a/src/modules/WaypointModule.cpp b/src/modules/WaypointModule.cpp index b8b738309..08b48b682 100644 --- a/src/modules/WaypointModule.cpp +++ b/src/modules/WaypointModule.cpp @@ -135,20 +135,6 @@ void WaypointModule::drawFrame(OLEDDisplay *display, OLEDDisplayUiState *state, myHeading = screen->estimatedHeading(DegD(op.latitude_i), DegD(op.longitude_i)); screen->drawCompassNorth(display, compassX, compassY, myHeading); - // Distance to Waypoint - float d = GeoCoord::latLongToMeter(DegD(wp.latitude_i), DegD(wp.longitude_i), DegD(op.latitude_i), DegD(op.longitude_i)); - if (config.display.units == meshtastic_Config_DisplayConfig_DisplayUnits_IMPERIAL) { - if (d < (2 * MILES_TO_FEET)) - snprintf(distStr, sizeof(distStr), "%.0f ft", d * METERS_TO_FEET); - else - snprintf(distStr, sizeof(distStr), "%.1f mi", d * METERS_TO_FEET / MILES_TO_FEET); - } else { - if (d < 2000) - snprintf(distStr, sizeof(distStr), "%.0f m", d); - else - snprintf(distStr, sizeof(distStr), "%.1f km", d / 1000); - } - // Compass bearing to waypoint float bearingToOther = GeoCoord::bearing(DegD(op.latitude_i), DegD(op.longitude_i), DegD(wp.latitude_i), DegD(wp.longitude_i)); @@ -157,6 +143,25 @@ void WaypointModule::drawFrame(OLEDDisplay *display, OLEDDisplayUiState *state, if (!config.display.compass_north_top) bearingToOther -= myHeading; screen->drawNodeHeading(display, compassX, compassY, compassDiam, bearingToOther); + + float bearingToOtherDegrees = (bearingToOther < 0) ? bearingToOther + 2*PI : bearingToOther; + bearingToOtherDegrees = bearingToOtherDegrees * 180 / PI; + + // Distance to Waypoint + float d = GeoCoord::latLongToMeter(DegD(wp.latitude_i), DegD(wp.longitude_i), DegD(op.latitude_i), DegD(op.longitude_i)); + if (config.display.units == meshtastic_Config_DisplayConfig_DisplayUnits_IMPERIAL) { + if (d < (2 * MILES_TO_FEET)) + snprintf(distStr, sizeof(distStr), "%.0fft %.0f°", d * METERS_TO_FEET, bearingToOtherDegrees); + else + snprintf(distStr, sizeof(distStr), "%.1fmi %.0f°", d * METERS_TO_FEET / MILES_TO_FEET, bearingToOtherDegrees); + } else { + if (d < 2000) + snprintf(distStr, sizeof(distStr), "%.0fm %.0f°", d, bearingToOtherDegrees); + else + snprintf(distStr, sizeof(distStr), "%.1fkm %.0f°", d / 1000, bearingToOtherDegrees); + } + + } // If our node doesn't have position @@ -166,9 +171,9 @@ void WaypointModule::drawFrame(OLEDDisplay *display, OLEDDisplayUiState *state, // ? in the distance field if (config.display.units == meshtastic_Config_DisplayConfig_DisplayUnits_IMPERIAL) - strncpy(distStr, "? mi", sizeof(distStr)); + strncpy(distStr, "? mi ?°", sizeof(distStr)); else - strncpy(distStr, "? km", sizeof(distStr)); + strncpy(distStr, "? km ?°", sizeof(distStr)); } // Draw compass circle From d7409342786f8c60b10e012b03810454ebb292c8 Mon Sep 17 00:00:00 2001 From: GUVWAF <78759985+GUVWAF@users.noreply.github.com> Date: Mon, 3 Feb 2025 12:24:47 +0100 Subject: [PATCH 79/89] Don't rate-limit position requests for Lost and Found role (#5981) --- src/modules/PositionModule.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/modules/PositionModule.cpp b/src/modules/PositionModule.cpp index e0f5b513f..acbc3143d 100644 --- a/src/modules/PositionModule.cpp +++ b/src/modules/PositionModule.cpp @@ -276,7 +276,8 @@ meshtastic_MeshPacket *PositionModule::allocPositionPacket() meshtastic_MeshPacket *PositionModule::allocReply() { - if (lastSentToMesh && Throttle::isWithinTimespanMs(lastSentToMesh, 3 * 60 * 1000)) { + if (config.device.role != meshtastic_Config_DeviceConfig_Role_LOST_AND_FOUND && lastSentToMesh && + Throttle::isWithinTimespanMs(lastSentToMesh, 3 * 60 * 1000)) { LOG_DEBUG("Skip Position reply since we sent it <3min ago"); ignoreRequest = true; // Mark it as ignored for MeshModule return nullptr; From 3a34f8beaff09f494e3996f059737d10d3af75f0 Mon Sep 17 00:00:00 2001 From: Tom <116762865+Nestpebble@users.noreply.github.com> Date: Mon, 3 Feb 2025 12:16:35 +0000 Subject: [PATCH 80/89] E80 promicro update (#5967) * add readme and update rfswitch * Updated readme to include all data from Ebyte * Added results from switch testing & notes thereon * fixed picture * Whoops! Forgot to uncomment some settings from test. * Update readme.md * Delete variants/diy/nrf52_promicro_diy_tcxo/E80_RSSI_per_case.png * Add webp image to appease trunk * Update readme.md * Trunky trunk trunk * Clang and the trunk is done --- .../E80_RSSI_per_case.webp | Bin 0 -> 10512 bytes .../diy/nrf52_promicro_diy_tcxo/readme.md | 107 ++++++++++++++++++ .../diy/nrf52_promicro_diy_tcxo/rfswitch.h | 11 +- .../diy/nrf52_promicro_diy_tcxo/variant.h | 2 +- 4 files changed, 115 insertions(+), 5 deletions(-) create mode 100644 variants/diy/nrf52_promicro_diy_tcxo/E80_RSSI_per_case.webp create mode 100644 variants/diy/nrf52_promicro_diy_tcxo/readme.md diff --git a/variants/diy/nrf52_promicro_diy_tcxo/E80_RSSI_per_case.webp b/variants/diy/nrf52_promicro_diy_tcxo/E80_RSSI_per_case.webp new file mode 100644 index 0000000000000000000000000000000000000000..825c3cbc04ac6bc3f08cb824750e8fa171bd06b3 GIT binary patch literal 10512 zcmbVw<8vhrtoCWQcDuE0Z*AK=wY_z>wr$(Cjje6l*48}l@4j>Yf}6=RnM^(;Gs!%e zL`7Oc0)_?v&=eO@(oo_8!T|sP?Ei2J4j2Rj2+Ju+ng8d3Q4y09e+#0EGi`;3uff1= zNJZWNz@!HHCh{a6{lK;5-cdhiLH~F4V`6No{qbLZVsmN$K4ee zMQIT!$^1L>92%^d-KV${oENxmfu|+!rrqbu$E~4!SG?-p&m&EXjlM!Z3Xtf)`3gwq zGx1w9fxoC*@8Q=&-zRh}(L-)N{(*PDA1X-t;_0mHTr=kM4ODI**Y}d0u}cUt_$+)i z*t0(YwSRAc3?FBQ>S`Z)-_pjI#J`6;3qG5!!!887ywtv$K4+h9w`Q+<(?4}UcuQET zf~S7X9;{bE3w<+w8z6=&_=~ogzIBj4h~$ImEaq!8e>dOH7i50}_PP4L`dO#ZxBDvo zwfn7EulUGxllRm&_|fRc4vPA8I}vQ{oAaCTy8@}-;e76XP<=Dq_f+<|_oVp!27#hT z!3+%jGKVjz`6IYM*Tzj=D1spNs1+qIG)K_Juqjc|`i6}(l{lu9(!+hji{X_uEv8FY z)EXt1wJF2M-u_yPiul6-SYeLnlR1j%)vM0LVe1sN3OfGvlDjRGaOS>$Nyt8%?ay6mmXFz!A$kIH9BOgHN<@0N>}+ zP*6{SaZH#&ygD`)D{JpmCj!k*Ks+@Rx<+(B7=D}Z@!koMS6Xg5ruGXKroCC`QYdxGy|Aqqp6Zf;l|cR#`$J@ObC7*Z&$x2dFEfueHo~{kn&g41I?qP!l?hLugUnl z2Jeqj)yiOB%u+E7TD&ehoH*C5Nz8BCSLTZecHBFNwah(87ORR1Le)U$;sK8>KN)JG z4eGw7u}LJc!jq#^ZSp-6eOS@Z#PO_E%OpZh=upxQNkz8(CS3Mo>QssBza0= zBLb=Y0`GtslTjC88spoQ#Uy6*CzfmmHGcP|*fB6QvPg%Q;pfhKp(?-10wu*%mT3Zf zRDJ0w9vGiI<8sI@dL2|dXTAC^;&Vt6!5xkzZ=DtRK;;Z>4ZLjQhiBJUNWr5nPeFUg zf1(~8X3ThGTCoT(WV$vyqwqCKc**S3Su>o=&7w4aIqEOH2e2o<@^If5m@!?e! z^)El&H|qvN1InD4R6N6o#Y~hSjDHWf7#6wyg47#L0Cw?_O4A`r{CM%Az3mAnF8BdUr7{B(Wf!sjtR-_NYhZb)C4a-O9KC6< z0%dT?Py>PLS4-E`U*HW3NPSfxK}H4c)F9ym6*1CRU81$3Z&1pSyxUt9}>F4zA1mg$@ccuZODVqcD0}?*nA*PpxpZaG zM1-)qY^_R34YhwRqHwsdfA(!IBc2H%<6oC%_ep9|a4I3yVVV1fhXiBTrcsOjY6ocM z@ky7QZw{QhnWj~hIOY$)(8xG1bp!;sWsAS%J2S~D2wr#{YHF%cM~M4<+hB4m*UFs{ zJt4wJMW_yJhU!bx6?0*1ac?~}M6@S8m2bl1F=!zL6ASEIe#E>K_FZx7`&N<6QEH|Y z3nM^A2EF*NGKRBFqK-v>%t1CC=wt0Oljb?*ThW9mQJ|igq5yuH+L;a5l}LFO9%J*< zXKhX8KjZ8Wj3T6@1Qq;&+qxxiMR-Uqr5EBs2iiqSgwQu#Nb*5KQu0NZw#Irgmg8my@1s=N3Sb%1hwZq zaDrqWJ(}u<{4I9=wTqMxD~Sq`^ylO&X#Ghi4eqqa#=@E(z>-n--ksX!L>H=5-hL11 zCfk{lm<+JwAzrB!dV@oiHEMPt_AF2AVWtdh0Hz1jdg4Pv>X-nG4eM;yL8QXnkm9#lvVT|Feqz>#bN0 zqBO_`BWo=H^j59k7_^oY_q{^L+3*AO0PHb?u6P&o006!teE@<{%wT205d{za066H% zhA}pLaviRHJ}@FaMTIye*e;fR_%kn9%2Ra-3#YmT_{bigXX|^_``-`elTFp4=0>e0 zQBqW$Y;juz_X}2wqgKct2lZjmc&Kih^B$?b;*VFD+9888IlY?=*m{JONq@=-a?D%g z^qGvUP&|Vmrw`STRx&#z?k#1$uDvM`UesMo-sp$U$j3&cmuekp44^|q6)fz9cx+cc zW;@nLRZo_om9Z{sQ|*SLO17s*WxEU&L`;Y!hW&o~1NbqR-Xf%M0?WF0Uza8N^%sKwm(EHuWDFA!6{B6G(U*#1cvlC_}QoooOQOHvm*c z_B5g(m;3FCsc+gX=xYql?WZi%Cc{t8Kp$gkRj4TV6T#aeSQk+}o{3G%m>`Q&Ht^SQ zAiSVcIB~`!Do0RZ-vqlq7I>JKfVb5Iz8Q+L5M}|!mc?rHjtCGPDpo>`Y%!yY{jYX! z1=P*xpwRVNy9n$&Tccou(hF;>g_5GG+|qsoH@$s9G{uM?`fE8uima)aL$RVwroJHG zcEn_vBo*N3=J1x39a$$Q`ySE^hvGJjo@^sC|4lbNk}=^4^`_qP>MpdWX=}6DL|mW> zerG0hNekkk#w9|Dx~`I~f9REXnf}8z_isQEmxeYsBeT4OYeezERK<==4oN%5W4Y@}#YQ+-W#FNDr#svDRw$L-*ogv1xtNxcb z*x0YAQ6s*DLm4}XZA9>iT~y{5_U@fM=UX|dB)Ygify|JwZ+V>wZHzyz3_^}vsXD)T zEzY^O8LIa#`S*HOr-O>j$$1@T2rfxTjl^rbJ+EbGha4C9I9%0ACt7_&<2f9OIt%BE zd6wxz0ZNWw^0*&m{o{Z6);^Po{;ZSp_FR8qpSWvkCCwKSq?}a}y?gplU^_y0SDe1< z!!HF%D%#NPn~T3U@|#JlYc9?LlAwy9PVwH>>6fm9k*zFT<*wvvPOzBJ$=`FQ zvg7&vA`-UsFbJ&vySUaDwp)DEZBNa}ikh148p4zbd<(!bSx&7R%r`j{^`4;zs!H8g zu`>`cL^bK*w#5-kxk20T4~bz(H!Cv>yCx0|@&z_!{^<@HN6Uu9{Hry8CyL{d-a;-`0fSn|1_!5VD7COfkdrT z(BEgQY(xEeX~UZMIuQ$~kxe#z-ms7<50!Q1iJ3#fn?P;XExA#IE6{qTimt2sOPh&MR^gzmpm_#YcTPxpC^6h z8GXOcjjVrm;pVRoidlLX~fU|%DE-|{ghjtcuc!W~AU@Ck(lF9=~c5|#uvzC;CS4#5Xo8!RC@ubk%va+BJ zEWi2)PTokM#9N`;eM^kKegKfJI#Mwb8QN+tjfxUZprL?&18E<%?4>e8!|Y^8pdEW6 zU_1#tpSK!bYxoU>c+B94cqEntuF)FFAS|_(#U#kZL=6sM{7gg5hv7Sc_E#YRp2xD0 zKQg;c@^hD%)yo2d&=yHs|Ye zoki`SeK8z&zbV<^X0odR7QphBtug15QV5{K#q-viNG>!4>c)^QPiR`w92K60?+`JC zCwJD4f;+&Amnf(97O?gkP8p9MUs%_r%qj?2H8pNCH9MZ%I~BagT#lPDXw%VMth%!e z5t_3oY4e^*-jmkrvTUz(2<0uF-xM##z1ihUEdm38t2 zoAB?9jd-~YA{4F6u8vpl@N{X|aDJ1m{ zZ2Y|6iejQM6^XggZni9E1=>*@W9Nw-ZtNwgbAThtfIlqvwYV`k4;xE(38kl$BZhdI z%a7MOzzxRC6s|idRx0eG>q^3*RWr~CeC#94|D=2T zH8Z%*^`7R8TyRfmYZC4CAq0Yn9619eN705wruT&y0Lar-qack0yBv&W7yHSv;kmP#Vn>Y(Y9CaF_TtijPAV;${2u_v$ zypPl{WOmh2MfQmrYWb-yA~z{ql}oHzGG;>OL@jYAm!3ywllncHg=`HqDCV-xQb;-F zNU5R17X|dAPzz5s8@-)o)Hy>;Jbdw(Q?~<@@wS7y)>K3MAI(kB zKCAWtf41?Ef-|Qj9Qr=I@T#z7M7ExMM@C)kQQMod`PAIuA3{8HuJ1s77wy_&=ie8%Y>8Ha53 zt4Hep5$M2wqjNObbC0tx1(Gb~L3JM+R{d*}Jx5u8Vse^8ziqpXK_te{G%xNa{ob8j%;S*phz|T`B0ZB|2w- zX{x9ud^@eF88^UFWZCPd*iNC5xi~*|%fT1K(68V*NXQk*1r@{chcnUZtT`1L(cg}Q1)lZ+vLo6LaQ^N{75f_R??}k1-+ePX5)TIQ1!*Mi3?ep! zy6x8N{A44<+$U;{f)h{-Hh|kLG^m^wy>pzi=-E$=e1@75OQQk)jq|~`?*xkslZ6!& z;T`-$rpar_7#XoDLgnOKV>3gblV^5*QRXSA&C9=Foe#^AF_gq!;ZEmU(n^Wbe-YUa6=Z87lPyS#21BzO7oTW}=`#|Gs zwGk2cxe7#Ao@rbHN&`#v?6Z1TN3Yscg;(w%zf1l@l#(oHS9<{f8SV2Ls z)*H2&gB?@W|Bwwn5^KF7VSO-jpl*Rv4DdwZ{`hB!?l^ZyveCK}nxz(!VzF`6#waDn=37WSQ1 z8k)>J`&{queTTU98A;BK_CDmz22*B)>t#*jAsbW;{{?NG^sbBrYc!*%pUDqqfHLW;Lp>^AmGq@^Mlqcv~VttW7S8w`oPSR(HHCnEKSJ`qeXl4;ahe~H0*M_D6DJM3~G zE=fQ1+-kRagL~T^A)_W@X?OV6QJkS2o~86$z~I;oRV00%WCNI06ZmMuZxSuZ!&<&} z$O|nGf5`2+Vf)0y+8~ri{+?YH4||zq6kI$3?A-C;+TI4)7#8KXQ?ah&{8}%Lz}y_g z2?^5+6M}FEf6ikUOxVR8clUwoczS|8uM>>F%wz0!4RxiH&9W>V(=U)mZ;y=oRK$2~ zF-6_jqPc?d0`g`&$J4Nz6AZh&s(I5dLa_={t;kc-kZ!pBtxhcMup95Ahp#(6Sht=; zejGeU9LV#6Z+bbv}3 z-D9ynIugZULY#_JGLRtls-UG}|C%jV^y<_f(GaeEA&0Gi>gg`=dV$$iy%=GVY@?(X zEIMz^c>4`%JjsMS1e(Mw(4L93u{G^5Ez-bwuFE4^6d& zx24U#0{CV(8dL8~7 zfd)bbG+`>rUSN2PS|oG)kXVPwH2*;-NFr3xyZ$*EeUcBVeQFx_i8}z@?K-!Qt^~~eF|%EZ&rZl z=T8MfDL)T&mFhDes+@NTL)j<=Tkd=)lGJPV5^6(i0YP2T`hc;?VewT@=zwgR(M-{v zC(Aa-eOeb$-ZDeQ?uE;Mwz}4mfsn_+gAZYmoQT>XU$JjWmAyf`$Al_-SPHPQf+D)5 zIX;?C=NTRnji)?@MB&-f7vJlmZ?mTOYWiQtNmTyqzh5K}xjy=un!zWYc|t?VBlqv( ziCjan*xK6UF>Z$bN6AW zZZIigi6}#er$^5-M`vvIdiMnea8><{ur`YZwe(y+PSyt!CQH$2`XPXF!+8sVm9V#z z+qzt{9Sv$;dA}-rvuD5#8Et?;-v;e$S&&SNImYJ$+aS9-gu+*W;#JcV8Mo@tk|3dV zb$z;xm3sl+gd+PHLG7^LUDvrYCGg#0 z`;6^`CV_e~y!H6)L(+}5eY1PzND59uB)YHfseggYvB{Y=#HT2I=PK*UmY3m+yOLV> zS*Fyxk;2KWtRi~yB#Q|<{QIAz=^=I430J4r5Ao3Z`Lo?1;&hJ*Xqyp+5}}zAHB0EX z;MSv)^e>ih0(1tWBw4)BO>7>S1F)Yn?}X_6$8BCv5_F|?-o*Z$$+R!U* zVh;Yic$&XjYa6fWx&R9nl+xz@3_7aASY6dUQ26bwf1jszQNUQ;6Z=spk}2mRS05yM z7k|_9DUqA9{AA+EvhAhCbdyIz8Bg@2pkO)0-_eto@%^Sn;2!_i>GjY1)&}%|SH`_6 zwu!B_l%I9NDk>GF!)?AY?mthp8EE?GZk*eLAtE#Z5l9j)W5775aG^O1;Y9aY*#?jv z+8jnFlVA|GDi2LY3FSgcS0<%G)#4>(Pb)i%V{uh8`URO*Z&q2!4vmgfi+t_#D)@Q6 z4Jh;ik33-i9)^&DdY1Z~F_@FtUB2E250y6-sO&1Ofxn*zBR zshKEQ!*#J*l4~3<|-o>1WhS^$OPB8TZCR-e10AjKqSTDKjWIA-Cj&5qd`^(#W z%~6_*^n49wfqMN`c;!k4|!BVQb9f>(h3|I{F z-VvTa6I-inNa<+C(iGCKNECQwDj-^B$-5__}o z>?pBuE0T2M9ev(kN?qG^Bz9#Hh@pFVP#$to$x%mBDwXSrRMn4)@1|1=6peFM^O;zx zY@BY|ICaZfW|4$JRDwnI$TZMTsQ5z4V)D^rThUCB=+EkS@3gf=U z7bj^wY2(keE#4cI4JQ(BV3{tb6-Y_xwgsVXE+*4X7<)77s z;m!OC;#&TVG|J)R(p1NAvjMzU z5m_1}d?~{GE&ow%p4S6Yz9h^~7JnC5MvRhDqtLa{hoT_?li=j+$%GzL_Klbe^q6$0 z;3wpO?lZcn_EA*kb1&ZgP}RS6m}|RNj?@mSwiKmkBpi>J@CfUAip=3P-}r6J&25Zy z9Gd7C7-PT>IOLQ4Gy6=&?Q9sWo|NC;rIcytya*K?WBD!QtM+RSE30{uCH8~x&m^yJ z;w@Ff=@%lSV64Y01U;FRt#;WMIL$G!_DZQtcFw-b&tj;BnxqvfzbbMAKhtjLd)gMb zqN#q`qbtQ=w?LM9YHzSm2HkdGwVpdhDLAV+BADf7DOi0`6$=a)W6Csy|6o^(_4x&f zwnLd%4p@3QR`^$hDMzqKIwBijoyH##4Js_bOjT z$m+=&#AolZ+DJLU&$y~8#Un<^mwy26;0wm^aQiNj5AZMtHsLo0gK7|;JbT`#rbiK% z+e5JIOgthyJ221J>MI#t#6k+h%Li~ku-Qt7RbHC}861L_mgOz3ieHKZ2PSd2?uYfn zsn^RMSe-eF+ktH;@|%)lOntaYLs3EN9?(<7&=drNp}6-!WSqA;KZ`-XLBY^U4F-L) zll5y9EyrMmXRR)moK0Gg9pJTsx>X_G2YM!nQI>r?h_{Z`Hc?TbxZoQ6^I5mIBYR$Z zA;VKGW7hp`TughFzL+~G16M8Ga+vml9v(6uVLl5q3C|`jk^VP}O!(^jolQ-slBY6X zb)ttJ#1fCCD+SYrM~5jzr#JV`8F83srAldw4q*m^07M=XXPxv)v9?QinPj!fM2-?# zj?IC$V19a-^vsN>HADvou}ta_OCzgv2jUDDW#GJ;d*DU-Q2KA!xvK|gOw%5yD{dAh zQm~1D3VMNgR?+Rt^8rKYpUb(2_reTw;S&V`KIcm#y73_=QjFf!lwqa)U^GStJvUcbE`BPSOGN0_?hy{ zRZsI?pX-dwl?pA>BBgi)y!PMMCUzMjslgUzNl1BGdPDqLn zv#EP4G5W7M@E8K8i*kSVyMM6~o zg-N$AKe9*xcN0yHdi?Ps#HFvjMcju!2JxX`!OO8(8qC>Eq;Vn`z$1bux44r-Mc|yw zgGB*MxAT7UIac0yQ2z|0N3!6J6=6#AOshQ<%fu`2F}w zBJw=|3Hf&Dx21*})@oT%iMR7~c9emoWXkHq!htoIa{@jR8a} ztt&mQYPWwG>}-et`KCCy6FDN-!qWI z#9z(~TuMA!#z;`zn4)8~L~6fYw|CgF&D2W;h?((zL|VTyPRlVT+J5>VItJ)OZ$tF2 zAC>bmk^A-3?c?V^rV@`>dU~i13vnl?To{dcD!$VL>2lk&?EG!>Ua~YaK3*xMP-I3k z{(1J*(NR26qNAR@=Uqj#QkB4{15PTrBKM(kMS_PLzv^h9ZC2>9t$@aZ&uz*b>kFpV zfK|w)_5p&cA?z0!=V=j!+NW@yrpbtFVq$J{G%GEtl91-RV`5USP;}ZFf%=v6MV#cf6f~h(7OhcZNZfKF_d#X#DV$Z0e z2Z+O8$mLlQ$FJ&wyN7;IO7W7qR*EXxE%~z&jN!Tw*uTfN|&M63-A9mR!S3fhGG!z|Pf9qYjl?w-3#{ z{tVM#-R2Vt7k1Hh*JCYr9VHx|`bCsfK)nC9v*PRN8BuNk78m`!n8g?oQz8fTDqt9H z<9ZLhxIHkkH`x1<4{)B>^!8YpZ7w`3buzg17JBC6uN%Hh5WSkDoC+G*9i`ybO6Fw9 z9Cfsb;WJh@ZK3u&aeuvHk_#P=W~J!HZ?tkE{vG){ja26s-+L;+UOMlyrol_5v*q|J zN89zzYSdNH`Wf#8*pm2QZm?w-!=m1lv^P3u-Qh_v=*i(b92fRb*1^XXlQxqQca#lh z6L(}kDlGN+Sh?(j{~B5*ZqHr0!&fn#Vg(q;M0#sOdVN8I&u=>95Kw-xyy0bhuD{FFr+I{GxW#@3a-9cS9QYE2O+kJI5u_`USK9T_Q9`4xU=f37c5;wNg3cIu{7 zY)MjfRurT|`dEEiDnS3^0RqP>b))XkSUI@&pV#_=OKPr_jQ||0vjt5Qzx@vvf(^s} t$Pa9hoC85TET5}#-W>pdanbnq*bV@Q1EnR9{)@`X18Dy + + The table of known modules is at the bottom of the variant.h, and reproduced here for convenience. + +| 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 | +| Ebyte | E80-900M2213S | Yes | Int | LR1121 radio | + + + +## LR1121 modules - E80 is the default + +The E80 from CDEbyte is the most obtainable module at present, and has been selected as the default option. + +Naturally, CDEbyte have chosen to ignore the generic Semtech impelementation of the RF switching logic and have supplied confusing and contradictory documentation, which is explained below. + +tl;dr: The E80 is chosen as the default. **If you wish to use another module, the table in `rfswitch.h` must be adjusted accordingly.** + +### E80 switching - the saga + +The CDEbyte implementation of the LR1121 is contained in their E80 module. As stated above, CDEbyte have chosen to ignore the generic Semtech implementation of the RF switching logic and have their own table, which is located at the bottom of the page [here](https://www.cdebyte.com/products/E80-900M2213S/2#Pin), and reflected on page 6 of their user manual, and reproduced below: + +| DIO5/RFSW0 | DIO6/RFSW1 | RF status | +| ---------- | ---------- | ----------------------------- | +| 0 | 0 | RX | +| 0 | 1 | TX (Sub-1GHz low power mode) | +| 1 | 0 | TX (Sub-1GHz high power mode) | +| 1 | 1 | TX(2.4GHz) | + +However, looking at the sample code they provide on page 9, the values would be: + +| DIO5/RFSW0 | DIO6/RFSW1 | RF status | +| ---------- | ---------- | ----------------------------- | +| 0 | 1 | RX | +| 1 | 1 | TX (Sub-1GHz low power mode) | +| 1 | 0 | TX (Sub-1GHz high power mode) | +| 0 | 0 | TX(2.4GHz) | + +The Semtech default, the values are (taken from [here](https://github.com/Lora-net/SWSD006/blob/v2.6.1/lib/app_subGHz_config_lr11xx.c#L145-L154)): + +
+ +```cpp + .rfswitch = { + .enable = LR11XX_SYSTEM_RFSW0_HIGH | LR11XX_SYSTEM_RFSW1_HIGH | LR11XX_SYSTEM_RFSW2_HIGH, + .standby = 0, + .rx = LR11XX_SYSTEM_RFSW0_HIGH, + .tx = LR11XX_SYSTEM_RFSW0_HIGH | LR11XX_SYSTEM_RFSW1_HIGH, + .tx_hp = LR11XX_SYSTEM_RFSW1_HIGH, + .tx_hf = 0, + .gnss = LR11XX_SYSTEM_RFSW2_HIGH, + .wifi = 0, + }, +``` + +
+ +| DIO5/RFSW0 | DIO6/RFSW1 | RF status | +| ---------- | ---------- | ----------------------------- | +| 1 | 0 | RX | +| 1 | 1 | TX (Sub-1GHz low power mode) | +| 0 | 1 | TX (Sub-1GHz high power mode) | +| 0 | 0 | TX(2.4GHz) | + +It is evident from the tables above that there is no real consistency to those provided by Ebyte. + +#### An experiment + +Tests were conducted in each of the three configurations between a known-good SX1262 and an E80, passing packets in both directions and recording the reported RSSI. The E80 was set at 22db and 14db to activate the high and low power settings respectively. The results are shown in the chart below. + +![Chart showing RSSI readings in each configuration and setting](./E80_RSSI_per_case.webp) + +## Conclusion + +The RF switching is based on the code example given. Logically, this shows the DIO5 and DIO6 are swapped compared to the reference design. + +If future DIYers wish to use an alternative module, the table in `rfswitch.h` must be adjusted accordingly. diff --git a/variants/diy/nrf52_promicro_diy_tcxo/rfswitch.h b/variants/diy/nrf52_promicro_diy_tcxo/rfswitch.h index 2258c3135..71508c037 100644 --- a/variants/diy/nrf52_promicro_diy_tcxo/rfswitch.h +++ b/variants/diy/nrf52_promicro_diy_tcxo/rfswitch.h @@ -1,17 +1,20 @@ #include "RadioLib.h" +// This is rewritten to match the requirements of the E80-900M2213S +// The E80 does not conform to the reference Semtech switches(!) and therefore needs a custom matrix. +// See footnote #3 in "https://www.cdebyte.com/products/E80-900M2213S/2#Pin" // RF Switch Matrix SubG RFO_HP_LF / RFO_LP_LF / RFI_[NP]_LF0 // DIO5 -> RFSW0_V1 // DIO6 -> RFSW1_V2 -// DIO7 -> ANT_CTRL_ON + ESP_IO9/LR_GPS_ANT_DC_EN -> RFI_GPS (Bias-T GPS) (LR11x0 only) +// DIO7 -> not connected on E80 module - note that GNSS and Wifi scanning are not possible. static const uint32_t rfswitch_dio_pins[] = {RADIOLIB_LR11X0_DIO5, RADIOLIB_LR11X0_DIO6, RADIOLIB_LR11X0_DIO7, RADIOLIB_NC, RADIOLIB_NC}; static const Module::RfSwitchMode_t rfswitch_table[] = { // mode DIO5 DIO6 DIO7 - {LR11x0::MODE_STBY, {LOW, LOW, LOW}}, {LR11x0::MODE_RX, {HIGH, LOW, LOW}}, - {LR11x0::MODE_TX, {LOW, HIGH, LOW}}, {LR11x0::MODE_TX_HP, {LOW, HIGH, LOW}}, + {LR11x0::MODE_STBY, {LOW, LOW, LOW}}, {LR11x0::MODE_RX, {LOW, HIGH, LOW}}, + {LR11x0::MODE_TX, {HIGH, HIGH, LOW}}, {LR11x0::MODE_TX_HP, {HIGH, LOW, LOW}}, {LR11x0::MODE_TX_HF, {LOW, LOW, LOW}}, {LR11x0::MODE_GNSS, {LOW, LOW, HIGH}}, {LR11x0::MODE_WIFI, {LOW, LOW, LOW}}, END_OF_MODE_TABLE, -}; \ No newline at end of file +}; diff --git a/variants/diy/nrf52_promicro_diy_tcxo/variant.h b/variants/diy/nrf52_promicro_diy_tcxo/variant.h index 5e939c023..b74b100a3 100644 --- a/variants/diy/nrf52_promicro_diy_tcxo/variant.h +++ b/variants/diy/nrf52_promicro_diy_tcxo/variant.h @@ -193,4 +193,4 @@ settings. * Arduino objects - C++ only *----------------------------------------------------------------------------*/ -#endif \ No newline at end of file +#endif From 8cacdb65d6fe9ce943824e54cc741b2785ec3014 Mon Sep 17 00:00:00 2001 From: Tom Fifield Date: Mon, 3 Feb 2025 22:39:42 +0800 Subject: [PATCH 81/89] Fix INA226 Sensor Voltage Readings (#5972) They were off by a factor of 1000 due to the difference between Volts and MilliVolts, as reported by @morcant . Fixes https://github.com/meshtastic/firmware/issues/5969 --- src/modules/Telemetry/Sensor/INA226Sensor.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/modules/Telemetry/Sensor/INA226Sensor.cpp b/src/modules/Telemetry/Sensor/INA226Sensor.cpp index 1ee7cd92e..8b1cded60 100644 --- a/src/modules/Telemetry/Sensor/INA226Sensor.cpp +++ b/src/modules/Telemetry/Sensor/INA226Sensor.cpp @@ -40,14 +40,14 @@ bool INA226Sensor::getMetrics(meshtastic_Telemetry *measurement) measurement->variant.environment_metrics.has_current = true; // mV conversion to V - measurement->variant.environment_metrics.voltage = ina226.getBusVoltage() / 1000; + measurement->variant.environment_metrics.voltage = ina226.getBusVoltage(); measurement->variant.environment_metrics.current = ina226.getCurrent_mA(); return true; } uint16_t INA226Sensor::getBusVoltageMv() { - return lround(ina226.getBusVoltage()); + return lround(ina226.getBusVoltage() * 1000); } int16_t INA226Sensor::getCurrentMa() From 5c17afb2ac6b40f6cf4a20b8c86f1f906df47273 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20G=C3=B6ttgens?= Date: Mon, 3 Feb 2025 16:36:05 +0100 Subject: [PATCH 82/89] Clean up some legacy macro definitions (#5983) --- src/graphics/EInkDynamicDisplay.cpp | 4 ++-- variants/heltec_vision_master_e213/platformio.ini | 4 ---- variants/heltec_vision_master_e290/platformio.ini | 6 +----- variants/heltec_wireless_paper/platformio.ini | 4 ---- variants/heltec_wireless_paper_v1/platformio.ini | 4 ---- variants/t-echo/platformio.ini | 3 --- variants/tlora_t3s3_epaper/platformio.ini | 5 ----- 7 files changed, 3 insertions(+), 27 deletions(-) diff --git a/src/graphics/EInkDynamicDisplay.cpp b/src/graphics/EInkDynamicDisplay.cpp index 6664646b9..47012ca47 100644 --- a/src/graphics/EInkDynamicDisplay.cpp +++ b/src/graphics/EInkDynamicDisplay.cpp @@ -238,7 +238,7 @@ void EInkDynamicDisplay::checkRateLimiting() // Skip update: too soon for BACKGROUND if (frameFlags == BACKGROUND) { - if (Throttle::isWithinTimespanMs(previousRunMs, EINK_LIMIT_RATE_BACKGROUND_SEC * 1000)) { + if (Throttle::isWithinTimespanMs(previousRunMs, 30000)) { refresh = SKIPPED; reason = EXCEEDED_RATELIMIT_FULL; return; @@ -251,7 +251,7 @@ void EInkDynamicDisplay::checkRateLimiting() // Skip update: too soon for RESPONSIVE if (frameFlags & RESPONSIVE) { - if (Throttle::isWithinTimespanMs(previousRunMs, EINK_LIMIT_RATE_RESPONSIVE_SEC * 1000)) { + if (Throttle::isWithinTimespanMs(previousRunMs, 1000)) { refresh = SKIPPED; reason = EXCEEDED_RATELIMIT_FAST; LOG_DEBUG("refresh=SKIPPED, reason=EXCEEDED_RATELIMIT_FAST, frameFlags=0x%x", frameFlags); diff --git a/variants/heltec_vision_master_e213/platformio.ini b/variants/heltec_vision_master_e213/platformio.ini index 709ae321f..cc6f283b5 100644 --- a/variants/heltec_vision_master_e213/platformio.ini +++ b/variants/heltec_vision_master_e213/platformio.ini @@ -10,12 +10,8 @@ build_flags = -DEINK_HEIGHT=122 -DUSE_EINK_DYNAMICDISPLAY ; Enable Dynamic EInk -DEINK_LIMIT_FASTREFRESH=10 ; How many consecutive fast-refreshes are permitted - -DEINK_LIMIT_RATE_BACKGROUND_SEC=30 ; Minimum interval between BACKGROUND updates - -DEINK_LIMIT_RATE_RESPONSIVE_SEC=1 ; Minimum interval between RESPONSIVE updates -; -D EINK_LIMIT_GHOSTING_PX=2000 ; (Optional) How much image ghosting is tolerated -DEINK_BACKGROUND_USES_FAST ; (Optional) Use FAST refresh for both BACKGROUND and RESPONSIVE, until a limit is reached. -DEINK_HASQUIRK_GHOSTING ; Display model is identified as "prone to ghosting" - -DEINK_HASQUIRK_WEAKFASTREFRESH ; Pixels set with fast-refresh are easy to clear, disrupted by sunlight lib_deps = ${esp32s3_base.lib_deps} https://github.com/meshtastic/GxEPD2#b202ebfec6a4821e098cf7a625ba0f6f2400292d diff --git a/variants/heltec_vision_master_e290/platformio.ini b/variants/heltec_vision_master_e290/platformio.ini index e1ba100ae..06804e4f2 100644 --- a/variants/heltec_vision_master_e290/platformio.ini +++ b/variants/heltec_vision_master_e290/platformio.ini @@ -11,12 +11,8 @@ build_flags = -D EINK_HEIGHT=128 -D USE_EINK_DYNAMICDISPLAY ; Enable Dynamic EInk -D EINK_LIMIT_FASTREFRESH=10 ; How many consecutive fast-refreshes are permitted - -D EINK_LIMIT_RATE_BACKGROUND_SEC=30 ; Minimum interval between BACKGROUND updates - -D EINK_LIMIT_RATE_RESPONSIVE_SEC=1 ; Minimum interval between RESPONSIVE updates -D EINK_HASQUIRK_GHOSTING ; Display model is identified as "prone to ghosting" - -D EINK_HASQUIRK_WEAKFASTREFRESH ; Pixels set with fast-refresh are easy to clear, disrupted by sunlight -; -D EINK_LIMIT_GHOSTING_PX=2000 ; How much image ghosting is tolerated -; -D EINK_BACKGROUND_USES_FAST ; (If enabled) don't redraw RESPONSIVE frames at next BACKGROUND update + lib_deps = ${esp32s3_base.lib_deps} diff --git a/variants/heltec_wireless_paper/platformio.ini b/variants/heltec_wireless_paper/platformio.ini index afbbd8be9..a7045b182 100644 --- a/variants/heltec_wireless_paper/platformio.ini +++ b/variants/heltec_wireless_paper/platformio.ini @@ -10,12 +10,8 @@ build_flags = -D EINK_HEIGHT=122 -D USE_EINK_DYNAMICDISPLAY ; Enable Dynamic EInk -D EINK_LIMIT_FASTREFRESH=10 ; How many consecutive fast-refreshes are permitted - -D EINK_LIMIT_RATE_BACKGROUND_SEC=30 ; Minimum interval between BACKGROUND updates - -D EINK_LIMIT_RATE_RESPONSIVE_SEC=1 ; Minimum interval between RESPONSIVE updates -; -D EINK_LIMIT_GHOSTING_PX=2000 ; (Optional) How much image ghosting is tolerated -D EINK_BACKGROUND_USES_FAST ; (Optional) Use FAST refresh for both BACKGROUND and RESPONSIVE, until a limit is reached. -D EINK_HASQUIRK_GHOSTING ; Display model is identified as "prone to ghosting" - -D EINK_HASQUIRK_WEAKFASTREFRESH ; Pixels set with fast-refresh are easy to clear, disrupted by sunlight lib_deps = ${esp32s3_base.lib_deps} https://github.com/meshtastic/GxEPD2#b202ebfec6a4821e098cf7a625ba0f6f2400292d diff --git a/variants/heltec_wireless_paper_v1/platformio.ini b/variants/heltec_wireless_paper_v1/platformio.ini index c94bcacca..2ce7559f9 100644 --- a/variants/heltec_wireless_paper_v1/platformio.ini +++ b/variants/heltec_wireless_paper_v1/platformio.ini @@ -11,11 +11,7 @@ build_flags = -D EINK_HEIGHT=122 -D USE_EINK_DYNAMICDISPLAY ; Enable Dynamic EInk -D EINK_LIMIT_FASTREFRESH=5 ; How many consecutive fast-refreshes are permitted - -D EINK_LIMIT_RATE_BACKGROUND_SEC=30 ; Minimum interval between BACKGROUND updates - -D EINK_LIMIT_RATE_RESPONSIVE_SEC=1 ; Minimum interval between RESPONSIVE updates -D EINK_LIMIT_GHOSTING_PX=2000 ; (Optional) How much image ghosting is tolerated - ;-D EINK_BACKGROUND_USES_FAST ; (Optional) Use FAST refresh for both BACKGROUND and RESPONSIVE, until a limit is reached. - -D EINK_HASQUIRK_VICIOUSFASTREFRESH ; Identify that pixels drawn by fast-refresh are harder to clear lib_deps = ${esp32s3_base.lib_deps} https://github.com/meshtastic/GxEPD2#55f618961db45a23eff0233546430f1e5a80f63a diff --git a/variants/t-echo/platformio.ini b/variants/t-echo/platformio.ini index 5b295c96a..ce58c0b88 100644 --- a/variants/t-echo/platformio.ini +++ b/variants/t-echo/platformio.ini @@ -14,9 +14,6 @@ build_flags = ${nrf52840_base.build_flags} -Ivariants/t-echo -DEINK_HEIGHT=200 -DUSE_EINK_DYNAMICDISPLAY ; Enable Dynamic EInk -DEINK_LIMIT_FASTREFRESH=20 ; How many consecutive fast-refreshes are permitted - -DEINK_LIMIT_RATE_BACKGROUND_SEC=30 ; Minimum interval between BACKGROUND updates - -DEINK_LIMIT_RATE_RESPONSIVE_SEC=1 ; Minimum interval between RESPONSIVE updates -; -DEINK_LIMIT_GHOSTING_PX=2000 ; (Optional) How much image ghosting is tolerated -DEINK_BACKGROUND_USES_FAST ; (Optional) Use FAST refresh for both BACKGROUND and RESPONSIVE, until a limit is reached. build_src_filter = ${nrf52_base.build_src_filter} +<../variants/t-echo> diff --git a/variants/tlora_t3s3_epaper/platformio.ini b/variants/tlora_t3s3_epaper/platformio.ini index ceb4fbaf5..3f3b3fe50 100644 --- a/variants/tlora_t3s3_epaper/platformio.ini +++ b/variants/tlora_t3s3_epaper/platformio.ini @@ -12,11 +12,6 @@ build_flags = -DEINK_HEIGHT=122 -DUSE_EINK_DYNAMICDISPLAY ; Enable Dynamic EInk -DEINK_LIMIT_FASTREFRESH=10 ; How many consecutive fast-refreshes are permitted - -DEINK_LIMIT_RATE_BACKGROUND_SEC=30 ; Minimum interval between BACKGROUND updates - -DEINK_LIMIT_RATE_RESPONSIVE_SEC=1 ; Minimum interval between RESPONSIVE updates - -DEINK_HASQUIRK_VICIOUSFASTREFRESH ; Identify that pixels drawn by fast-refresh are harder to clear - ;-DEINK_LIMIT_GHOSTING_PX=2000 ; (Optional) How much image ghosting is tolerated - ;-DEINK_BACKGROUND_USES_FAST ; (Optional) Use FAST refresh for both BACKGROUND and RESPONSIVE, until a limit is reached. lib_deps = ${esp32s3_base.lib_deps} From a3a295488c5f6092494f139fab93ac4926981949 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20G=C3=B6ttgens?= Date: Mon, 3 Feb 2025 16:48:10 +0100 Subject: [PATCH 83/89] add firmware build script for use with docker --- bin/build-firmware.sh | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 bin/build-firmware.sh diff --git a/bin/build-firmware.sh b/bin/build-firmware.sh new file mode 100644 index 000000000..c53f1b660 --- /dev/null +++ b/bin/build-firmware.sh @@ -0,0 +1,18 @@ +#!/usr/bin/env bash + +sed -i 's/#-DBUILD_EPOCH=$UNIX_TIME/-DBUILD_EPOCH=$UNIX_TIME/' platformio.ini + +export PIP_BREAK_SYSTEM_PACKAGES=1 + +if (echo $2 | grep -q "esp32"); then + bin/build-esp32.sh $1 +elif (echo $2 | grep -q "nrf52"); then + bin/build-nrf52.sh $1 +elif (echo $2 | grep -q "stm32"); then + bin/build-stm32.sh $1 +elif (echo $2 | grep -q "rpi2040"); then + bin/build-rpi2040.sh $1 +else + echo "Unknown target $2" + exit 1 +fi \ No newline at end of file From 1b457bcfbb36a4032f853c06512f2ac7936fad3a Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 3 Feb 2025 20:47:51 -0600 Subject: [PATCH 84/89] [create-pull-request] automated change (#5985) Co-authored-by: thebentern <9000580+thebentern@users.noreply.github.com> --- protobufs | 2 +- src/mesh/generated/meshtastic/config.pb.h | 13 +++++++++---- src/mesh/generated/meshtastic/localonly.pb.h | 2 +- 3 files changed, 11 insertions(+), 6 deletions(-) diff --git a/protobufs b/protobufs index 7f13df0e5..b80785b16 160000 --- a/protobufs +++ b/protobufs @@ -1 +1 @@ -Subproject commit 7f13df0e5f7cbb07f0e6f3a57c0d86ad448738db +Subproject commit b80785b16bc0d243b97917998706e7bf209cd9d0 diff --git a/src/mesh/generated/meshtastic/config.pb.h b/src/mesh/generated/meshtastic/config.pb.h index 14aed9dfe..4747ddb5a 100644 --- a/src/mesh/generated/meshtastic/config.pb.h +++ b/src/mesh/generated/meshtastic/config.pb.h @@ -468,6 +468,9 @@ typedef struct _meshtastic_Config_DisplayConfig { bool wake_on_tap_or_motion; /* Indicates how to rotate or invert the compass output to accurate display on the display. */ meshtastic_Config_DisplayConfig_CompassOrientation compass_orientation; + /* If false (default), the device will display the time in 24-hour format on screen. + If true, the device will display the time in 12-hour format on screen. */ + bool use_12h_clock; } meshtastic_Config_DisplayConfig; /* Lora Config */ @@ -690,7 +693,7 @@ extern "C" { #define meshtastic_Config_PowerConfig_init_default {0, 0, 0, 0, 0, 0, 0, 0, 0} #define meshtastic_Config_NetworkConfig_init_default {0, "", "", "", 0, _meshtastic_Config_NetworkConfig_AddressMode_MIN, false, meshtastic_Config_NetworkConfig_IpV4Config_init_default, "", 0} #define meshtastic_Config_NetworkConfig_IpV4Config_init_default {0, 0, 0, 0} -#define meshtastic_Config_DisplayConfig_init_default {0, _meshtastic_Config_DisplayConfig_GpsCoordinateFormat_MIN, 0, 0, 0, _meshtastic_Config_DisplayConfig_DisplayUnits_MIN, _meshtastic_Config_DisplayConfig_OledType_MIN, _meshtastic_Config_DisplayConfig_DisplayMode_MIN, 0, 0, _meshtastic_Config_DisplayConfig_CompassOrientation_MIN} +#define meshtastic_Config_DisplayConfig_init_default {0, _meshtastic_Config_DisplayConfig_GpsCoordinateFormat_MIN, 0, 0, 0, _meshtastic_Config_DisplayConfig_DisplayUnits_MIN, _meshtastic_Config_DisplayConfig_OledType_MIN, _meshtastic_Config_DisplayConfig_DisplayMode_MIN, 0, 0, _meshtastic_Config_DisplayConfig_CompassOrientation_MIN, 0} #define meshtastic_Config_LoRaConfig_init_default {0, _meshtastic_Config_LoRaConfig_ModemPreset_MIN, 0, 0, 0, 0, _meshtastic_Config_LoRaConfig_RegionCode_MIN, 0, 0, 0, 0, 0, 0, 0, 0, 0, {0, 0, 0}, 0, 0} #define meshtastic_Config_BluetoothConfig_init_default {0, _meshtastic_Config_BluetoothConfig_PairingMode_MIN, 0} #define meshtastic_Config_SecurityConfig_init_default {{0, {0}}, {0, {0}}, 0, {{0, {0}}, {0, {0}}, {0, {0}}}, 0, 0, 0, 0} @@ -701,7 +704,7 @@ extern "C" { #define meshtastic_Config_PowerConfig_init_zero {0, 0, 0, 0, 0, 0, 0, 0, 0} #define meshtastic_Config_NetworkConfig_init_zero {0, "", "", "", 0, _meshtastic_Config_NetworkConfig_AddressMode_MIN, false, meshtastic_Config_NetworkConfig_IpV4Config_init_zero, "", 0} #define meshtastic_Config_NetworkConfig_IpV4Config_init_zero {0, 0, 0, 0} -#define meshtastic_Config_DisplayConfig_init_zero {0, _meshtastic_Config_DisplayConfig_GpsCoordinateFormat_MIN, 0, 0, 0, _meshtastic_Config_DisplayConfig_DisplayUnits_MIN, _meshtastic_Config_DisplayConfig_OledType_MIN, _meshtastic_Config_DisplayConfig_DisplayMode_MIN, 0, 0, _meshtastic_Config_DisplayConfig_CompassOrientation_MIN} +#define meshtastic_Config_DisplayConfig_init_zero {0, _meshtastic_Config_DisplayConfig_GpsCoordinateFormat_MIN, 0, 0, 0, _meshtastic_Config_DisplayConfig_DisplayUnits_MIN, _meshtastic_Config_DisplayConfig_OledType_MIN, _meshtastic_Config_DisplayConfig_DisplayMode_MIN, 0, 0, _meshtastic_Config_DisplayConfig_CompassOrientation_MIN, 0} #define meshtastic_Config_LoRaConfig_init_zero {0, _meshtastic_Config_LoRaConfig_ModemPreset_MIN, 0, 0, 0, 0, _meshtastic_Config_LoRaConfig_RegionCode_MIN, 0, 0, 0, 0, 0, 0, 0, 0, 0, {0, 0, 0}, 0, 0} #define meshtastic_Config_BluetoothConfig_init_zero {0, _meshtastic_Config_BluetoothConfig_PairingMode_MIN, 0} #define meshtastic_Config_SecurityConfig_init_zero {{0, {0}}, {0, {0}}, 0, {{0, {0}}, {0, {0}}, {0, {0}}}, 0, 0, 0, 0} @@ -765,6 +768,7 @@ extern "C" { #define meshtastic_Config_DisplayConfig_heading_bold_tag 9 #define meshtastic_Config_DisplayConfig_wake_on_tap_or_motion_tag 10 #define meshtastic_Config_DisplayConfig_compass_orientation_tag 11 +#define meshtastic_Config_DisplayConfig_use_12h_clock_tag 12 #define meshtastic_Config_LoRaConfig_use_preset_tag 1 #define meshtastic_Config_LoRaConfig_modem_preset_tag 2 #define meshtastic_Config_LoRaConfig_bandwidth_tag 3 @@ -907,7 +911,8 @@ X(a, STATIC, SINGULAR, UENUM, oled, 7) \ X(a, STATIC, SINGULAR, UENUM, displaymode, 8) \ X(a, STATIC, SINGULAR, BOOL, heading_bold, 9) \ X(a, STATIC, SINGULAR, BOOL, wake_on_tap_or_motion, 10) \ -X(a, STATIC, SINGULAR, UENUM, compass_orientation, 11) +X(a, STATIC, SINGULAR, UENUM, compass_orientation, 11) \ +X(a, STATIC, SINGULAR, BOOL, use_12h_clock, 12) #define meshtastic_Config_DisplayConfig_CALLBACK NULL #define meshtastic_Config_DisplayConfig_DEFAULT NULL @@ -985,7 +990,7 @@ extern const pb_msgdesc_t meshtastic_Config_SessionkeyConfig_msg; #define MESHTASTIC_MESHTASTIC_CONFIG_PB_H_MAX_SIZE meshtastic_Config_size #define meshtastic_Config_BluetoothConfig_size 10 #define meshtastic_Config_DeviceConfig_size 98 -#define meshtastic_Config_DisplayConfig_size 30 +#define meshtastic_Config_DisplayConfig_size 32 #define meshtastic_Config_LoRaConfig_size 85 #define meshtastic_Config_NetworkConfig_IpV4Config_size 20 #define meshtastic_Config_NetworkConfig_size 202 diff --git a/src/mesh/generated/meshtastic/localonly.pb.h b/src/mesh/generated/meshtastic/localonly.pb.h index dc0f507c9..7a6712bf0 100644 --- a/src/mesh/generated/meshtastic/localonly.pb.h +++ b/src/mesh/generated/meshtastic/localonly.pb.h @@ -187,7 +187,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 741 +#define meshtastic_LocalConfig_size 743 #define meshtastic_LocalModuleConfig_size 699 #ifdef __cplusplus From 447533aae5b82398e8dcd648745a4ff1a3c0a8ac Mon Sep 17 00:00:00 2001 From: Austin Date: Tue, 4 Feb 2025 07:38:54 -0500 Subject: [PATCH 85/89] meshtasticd-debian: Remove existing deb builds (#5792) Replaced with OpenSUSE Build Service https://build.opensuse.org/project/show/network:Meshtastic --- .github/workflows/build_native.yml | 38 -------- .github/workflows/build_raspbian.yml | 52 ----------- .github/workflows/build_raspbian_armv7l.yml | 52 ----------- .github/workflows/main_matrix.yml | 32 +------ .github/workflows/package_amd64.yml | 90 ------------------- .github/workflows/package_raspbian.yml | 90 ------------------- .github/workflows/package_raspbian_armv7l.yml | 90 ------------------- .github/workflows/release_channels.yml | 12 +-- 8 files changed, 9 insertions(+), 447 deletions(-) delete mode 100644 .github/workflows/build_native.yml delete mode 100644 .github/workflows/build_raspbian.yml delete mode 100644 .github/workflows/build_raspbian_armv7l.yml delete mode 100644 .github/workflows/package_amd64.yml delete mode 100644 .github/workflows/package_raspbian.yml delete mode 100644 .github/workflows/package_raspbian_armv7l.yml diff --git a/.github/workflows/build_native.yml b/.github/workflows/build_native.yml deleted file mode 100644 index cca839328..000000000 --- a/.github/workflows/build_native.yml +++ /dev/null @@ -1,38 +0,0 @@ -name: Build Native - -on: workflow_call - -permissions: - contents: write - packages: write - -jobs: - build-native: - runs-on: ubuntu-latest - steps: - - name: Checkout code - uses: actions/checkout@v4 - with: - submodules: recursive - ref: ${{github.event.pull_request.head.ref}} - repository: ${{github.event.pull_request.head.repo.full_name}} - - - name: Setup native build - id: base - uses: ./.github/actions/setup-native - - - name: Build Native - run: bin/build-native.sh - - - name: Get release version string - run: echo "long=$(./bin/buildinfo.py long)" >> $GITHUB_OUTPUT - id: version - - - name: Store binaries as an artifact - uses: actions/upload-artifact@v4 - with: - name: firmware-native-${{ steps.version.outputs.long }}.zip - overwrite: true - path: | - release/meshtasticd_linux_x86_64 - bin/config-dist.yaml diff --git a/.github/workflows/build_raspbian.yml b/.github/workflows/build_raspbian.yml deleted file mode 100644 index 646c6c9f3..000000000 --- a/.github/workflows/build_raspbian.yml +++ /dev/null @@ -1,52 +0,0 @@ -name: Build Raspbian - -on: workflow_call - -permissions: - contents: write - packages: write - -jobs: - build-raspbian: - runs-on: [self-hosted, linux, ARM64] - steps: - - name: Install libbluetooth - shell: bash - run: | - 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 libusb-1.0-0-dev libi2c-dev - - - name: Checkout code - uses: actions/checkout@v4 - with: - submodules: recursive - ref: ${{github.event.pull_request.head.ref}} - repository: ${{github.event.pull_request.head.repo.full_name}} - - - name: Upgrade python tools - shell: bash - run: | - python -m pip install --upgrade pip - pip install -U platformio adafruit-nrfutil - pip install -U meshtastic --pre - - - name: Upgrade platformio - shell: bash - run: | - pio upgrade - - - name: Build Raspbian - run: bin/build-native.sh - - - name: Get release version string - run: echo "long=$(./bin/buildinfo.py long)" >> $GITHUB_OUTPUT - id: version - - - name: Store binaries as an artifact - uses: actions/upload-artifact@v4 - with: - name: firmware-raspbian-${{ steps.version.outputs.long }}.zip - overwrite: true - path: | - release/meshtasticd_linux_aarch64 - bin/config-dist.yaml diff --git a/.github/workflows/build_raspbian_armv7l.yml b/.github/workflows/build_raspbian_armv7l.yml deleted file mode 100644 index 21b1aea79..000000000 --- a/.github/workflows/build_raspbian_armv7l.yml +++ /dev/null @@ -1,52 +0,0 @@ -name: Build Raspbian Arm - -on: workflow_call - -permissions: - contents: write - packages: write - -jobs: - build-raspbian-armv7l: - runs-on: [self-hosted, linux, ARM] - steps: - - name: Install libbluetooth - shell: bash - run: | - 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 libusb-1.0-0-dev libi2c-dev - - - name: Checkout code - uses: actions/checkout@v4 - with: - submodules: recursive - ref: ${{github.event.pull_request.head.ref}} - repository: ${{github.event.pull_request.head.repo.full_name}} - - - name: Upgrade python tools - shell: bash - run: | - python -m pip install --upgrade pip - pip install -U platformio adafruit-nrfutil - pip install -U meshtastic --pre - - - name: Upgrade platformio - shell: bash - run: | - pio upgrade - - - name: Build Raspbian - run: bin/build-native.sh - - - name: Get release version string - run: echo "long=$(./bin/buildinfo.py long)" >> $GITHUB_OUTPUT - id: version - - - name: Store binaries as an artifact - uses: actions/upload-artifact@v4 - with: - name: firmware-raspbian-armv7l-${{ steps.version.outputs.long }}.zip - overwrite: true - path: | - release/meshtasticd_linux_armv7l - bin/config-dist.yaml diff --git a/.github/workflows/main_matrix.yml b/.github/workflows/main_matrix.yml index a9678f4fc..b13866435 100644 --- a/.github/workflows/main_matrix.yml +++ b/.github/workflows/main_matrix.yml @@ -128,15 +128,6 @@ jobs: with: board: ${{ matrix.board }} - package-raspbian: - uses: ./.github/workflows/package_raspbian.yml - - package-raspbian-armv7l: - uses: ./.github/workflows/package_raspbian_armv7l.yml - - package-native: - uses: ./.github/workflows/package_amd64.yml - build-debian-src: uses: ./.github/workflows/build_debian_src.yml with: @@ -158,7 +149,7 @@ jobs: docker-alpine-amd64: uses: ./.github/workflows/docker_build.yml with: - distro: debian + distro: alpine platform: linux/amd64 runs-on: ubuntu-24.04 push: false @@ -288,14 +279,7 @@ jobs: if: ${{ github.event_name == 'workflow_dispatch' }} outputs: upload_url: ${{ steps.create_release.outputs.upload_url }} - needs: - [ - gather-artifacts, - package-raspbian, - package-raspbian-armv7l, - package-native, - build-debian-src, - ] + needs: [gather-artifacts, build-debian-src] steps: - name: Checkout uses: actions/checkout@v4 @@ -324,13 +308,6 @@ jobs: body: | Autogenerated by github action, developer should edit as required before publishing... - - name: Download deb files - uses: actions/download-artifact@v4 - with: - pattern: meshtasticd_${{ steps.version.outputs.long }}_*.deb - merge-multiple: true - path: ./output - - name: Download source deb uses: actions/download-artifact@v4 with: @@ -346,11 +323,8 @@ jobs: - name: Display structure of downloaded files run: ls -lR - - name: Add deb files to release + - name: Add source deb to release run: | - gh release upload v${{ steps.version.outputs.long }} ./output/meshtasticd_${{ steps.version.outputs.long }}_arm64.deb - gh release upload v${{ steps.version.outputs.long }} ./output/meshtasticd_${{ steps.version.outputs.long }}_armhf.deb - gh release upload v${{ steps.version.outputs.long }} ./output/meshtasticd_${{ steps.version.outputs.long }}_amd64.deb gh release upload v${{ steps.version.outputs.long }} ./output/meshtasticd-${{ steps.version.outputs.deb }}-src.zip env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/package_amd64.yml b/.github/workflows/package_amd64.yml deleted file mode 100644 index d9f041736..000000000 --- a/.github/workflows/package_amd64.yml +++ /dev/null @@ -1,90 +0,0 @@ -name: Package Native - -on: - workflow_call: - workflow_dispatch: - -permissions: - contents: write - packages: write - -jobs: - build-native: - uses: ./.github/workflows/build_native.yml - - package-native: - runs-on: ubuntu-22.04 - needs: build-native - steps: - - name: Checkout code - uses: actions/checkout@v4 - with: - submodules: recursive - ref: ${{github.event.pull_request.head.ref}} - repository: ${{github.event.pull_request.head.repo.full_name}} - - - name: Pull web ui - uses: dsaltares/fetch-gh-release-asset@master - with: - repo: meshtastic/web - file: build.tar - target: build.tar - token: ${{ secrets.GITHUB_TOKEN }} - - - name: Get release version string - run: echo "long=$(./bin/buildinfo.py long)" >> $GITHUB_OUTPUT - id: version - - - name: Download artifacts - uses: actions/download-artifact@v4 - with: - name: firmware-native-${{ steps.version.outputs.long }}.zip - merge-multiple: true - - - name: Display structure of downloaded files - run: ls -R - - - name: build .debpkg - run: | - mkdir -p .debpkg/DEBIAN - mkdir -p .debpkg/usr/share/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/meshtasticd/web - shopt -s dotglob nullglob - if [ -d .debpkg/usr/share/meshtasticd/web/build ]; then mv .debpkg/usr/share/meshtasticd/web/build/* .debpkg/usr/share/meshtasticd/web/; fi - if [ -d .debpkg/usr/share/meshtasticd/web/build ]; then rmdir .debpkg/usr/share/meshtasticd/web/build; fi - if [ -d .debpkg/usr/share/meshtasticd/web/.DS_Store ]; then rm -f .debpkg/usr/share/meshtasticd/web/.DS_Store; fi - gunzip .debpkg/usr/share/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/ -r - 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 - chmod +x .debpkg/DEBIAN/conffiles - # Transition /usr/share/doc/meshtasticd to /usr/share/meshtasticd - echo "rm -rf /usr/share/doc/meshtasticd" > .debpkg/DEBIAN/preinst - chmod +x .debpkg/DEBIAN/preinst - echo "ln -sf /usr/share/meshtasticd /usr/share/doc/meshtasticd" > .debpkg/DEBIAN/postinst - chmod +x .debpkg/DEBIAN/postinst - - - uses: jiro4989/build-deb-action@v3 - with: - package: meshtasticd - package_root: .debpkg - maintainer: Jonathan Bennett - version: ${{ steps.version.outputs.long }} # refs/tags/v*.*.* - arch: amd64 - depends: libyaml-cpp0.7, openssl, libulfius2.7, libi2c0 - desc: Native Linux Meshtastic binary. - - - uses: actions/upload-artifact@v4 - with: - name: meshtasticd_${{ steps.version.outputs.long }}_amd64.deb - overwrite: true - path: | - ./*.deb diff --git a/.github/workflows/package_raspbian.yml b/.github/workflows/package_raspbian.yml deleted file mode 100644 index 62613f85f..000000000 --- a/.github/workflows/package_raspbian.yml +++ /dev/null @@ -1,90 +0,0 @@ -name: Package Raspbian - -on: - workflow_call: - workflow_dispatch: - -permissions: - contents: write - packages: write - -jobs: - build-raspbian: - uses: ./.github/workflows/build_raspbian.yml - - package-raspbian: - runs-on: ubuntu-22.04 - needs: build-raspbian - steps: - - name: Checkout code - uses: actions/checkout@v4 - with: - submodules: recursive - ref: ${{github.event.pull_request.head.ref}} - repository: ${{github.event.pull_request.head.repo.full_name}} - - - name: Pull web ui - uses: dsaltares/fetch-gh-release-asset@master - with: - repo: meshtastic/web - file: build.tar - target: build.tar - token: ${{ secrets.GITHUB_TOKEN }} - - - name: Get release version string - run: echo "long=$(./bin/buildinfo.py long)" >> $GITHUB_OUTPUT - id: version - - - name: Download artifacts - uses: actions/download-artifact@v4 - with: - name: firmware-raspbian-${{ steps.version.outputs.long }}.zip - merge-multiple: true - - - name: Display structure of downloaded files - run: ls -R - - - name: build .debpkg - run: | - mkdir -p .debpkg/DEBIAN - mkdir -p .debpkg/usr/share/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/meshtasticd/web - shopt -s dotglob nullglob - if [ -d .debpkg/usr/share/meshtasticd/web/build ]; then mv .debpkg/usr/share/meshtasticd/web/build/* .debpkg/usr/share/meshtasticd/web/; fi - if [ -d .debpkg/usr/share/meshtasticd/web/build ]; then rmdir .debpkg/usr/share/meshtasticd/web/build; fi - if [ -d .debpkg/usr/share/meshtasticd/web/.DS_Store ]; then rm -f .debpkg/usr/share/meshtasticd/web/.DS_Store; fi - gunzip .debpkg/usr/share/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/ -r - 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 - chmod +x .debpkg/DEBIAN/conffiles - # Transition /usr/share/doc/meshtasticd to /usr/share/meshtasticd - echo "rm -rf /usr/share/doc/meshtasticd" > .debpkg/DEBIAN/preinst - chmod +x .debpkg/DEBIAN/preinst - echo "ln -sf /usr/share/meshtasticd /usr/share/doc/meshtasticd" > .debpkg/DEBIAN/postinst - chmod +x .debpkg/DEBIAN/postinst - - - uses: jiro4989/build-deb-action@v3 - with: - package: meshtasticd - package_root: .debpkg - maintainer: Jonathan Bennett - version: ${{ steps.version.outputs.long }} # refs/tags/v*.*.* - arch: arm64 - depends: libyaml-cpp0.7, openssl, libulfius2.7, libi2c0 - desc: Native Linux Meshtastic binary. - - - uses: actions/upload-artifact@v4 - with: - name: meshtasticd_${{ steps.version.outputs.long }}_arm64.deb - overwrite: true - path: | - ./*.deb diff --git a/.github/workflows/package_raspbian_armv7l.yml b/.github/workflows/package_raspbian_armv7l.yml deleted file mode 100644 index 8a9df1710..000000000 --- a/.github/workflows/package_raspbian_armv7l.yml +++ /dev/null @@ -1,90 +0,0 @@ -name: Package Raspbian - -on: - workflow_call: - workflow_dispatch: - -permissions: - contents: write - packages: write - -jobs: - build-raspbian_armv7l: - uses: ./.github/workflows/build_raspbian_armv7l.yml - - package-raspbian_armv7l: - runs-on: ubuntu-22.04 - needs: build-raspbian_armv7l - steps: - - name: Checkout code - uses: actions/checkout@v4 - with: - submodules: recursive - ref: ${{github.event.pull_request.head.ref}} - repository: ${{github.event.pull_request.head.repo.full_name}} - - - name: Pull web ui - uses: dsaltares/fetch-gh-release-asset@master - with: - repo: meshtastic/web - file: build.tar - target: build.tar - token: ${{ secrets.GITHUB_TOKEN }} - - - name: Get release version string - run: echo "long=$(./bin/buildinfo.py long)" >> $GITHUB_OUTPUT - id: version - - - name: Download artifacts - uses: actions/download-artifact@v4 - with: - name: firmware-raspbian-armv7l-${{ steps.version.outputs.long }}.zip - merge-multiple: true - - - name: Display structure of downloaded files - run: ls -R - - - name: build .debpkg - run: | - mkdir -p .debpkg/DEBIAN - mkdir -p .debpkg/usr/share/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/meshtasticd/web - shopt -s dotglob nullglob - if [ -d .debpkg/usr/share/meshtasticd/web/build ]; then mv .debpkg/usr/share/meshtasticd/web/build/* .debpkg/usr/share/meshtasticd/web/; fi - if [ -d .debpkg/usr/share/meshtasticd/web/build ]; then rmdir .debpkg/usr/share/meshtasticd/web/build; fi - if [ -d .debpkg/usr/share/meshtasticd/web/.DS_Store ]; then rm -f .debpkg/usr/share/meshtasticd/web/.DS_Store; fi - gunzip .debpkg/usr/share/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/ -r - 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 - chmod +x .debpkg/DEBIAN/conffiles - # Transition /usr/share/doc/meshtasticd to /usr/share/meshtasticd - echo "rm -rf /usr/share/doc/meshtasticd" > .debpkg/DEBIAN/preinst - chmod +x .debpkg/DEBIAN/preinst - echo "ln -sf /usr/share/meshtasticd /usr/share/doc/meshtasticd" > .debpkg/DEBIAN/postinst - chmod +x .debpkg/DEBIAN/postinst - - - uses: jiro4989/build-deb-action@v3 - with: - package: meshtasticd - package_root: .debpkg - maintainer: Jonathan Bennett - version: ${{ steps.version.outputs.long }} # refs/tags/v*.*.* - arch: armhf - depends: libyaml-cpp0.7, openssl, libulfius2.7, libi2c0 - desc: Native Linux Meshtastic binary. - - - uses: actions/upload-artifact@v4 - with: - name: meshtasticd_${{ steps.version.outputs.long }}_armhf.deb - overwrite: true - path: | - ./*.deb diff --git a/.github/workflows/release_channels.yml b/.github/workflows/release_channels.yml index a3a105d6d..9cdabde9e 100644 --- a/.github/workflows/release_channels.yml +++ b/.github/workflows/release_channels.yml @@ -37,9 +37,9 @@ jobs: ${{ contains(github.event.release.name, 'Beta') && 'beta' || contains(github.event.release.name, 'Alpha') && 'alpha' }} secrets: inherit - # hook-copr: - # uses: ./.github/workflows/hook_copr.yml - # with: - # copr_project: |- - # ${{ contains(github.event.release.name, 'Beta') && 'beta' || contains(github.event.release.name, 'Alpha') && 'alpha' }} - # secrets: inherit + hook-copr: + uses: ./.github/workflows/hook_copr.yml + with: + copr_project: |- + ${{ contains(github.event.release.name, 'Beta') && 'beta' || contains(github.event.release.name, 'Alpha') && 'alpha' }} + secrets: inherit From 1c8eb7ece3895f608a2da84b6e8e47050e639991 Mon Sep 17 00:00:00 2001 From: Austin Date: Wed, 5 Feb 2025 16:19:22 -0500 Subject: [PATCH 86/89] meshtasticd: Fix web download location (#5993) --- debian/ci_pack_sdeb.sh | 2 +- meshtasticd.spec.rpkg | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/debian/ci_pack_sdeb.sh b/debian/ci_pack_sdeb.sh index 1f311af93..a8b2252ae 100755 --- a/debian/ci_pack_sdeb.sh +++ b/debian/ci_pack_sdeb.sh @@ -11,7 +11,7 @@ platformio pkg install -e native -t platformio/tool-scons@4.40502.0 tar -cf pio.tar pio/ rm -rf pio # Download the latest meshtastic/web release build.tar to `web.tar` -curl -L https://github.com/meshtastic/web/releases/download/latest/build.tar -o web.tar +curl -L https://github.com/meshtastic/web/releases/latest/download/build.tar -o web.tar package=$(dpkg-parsechangelog --show-field Source) diff --git a/meshtasticd.spec.rpkg b/meshtasticd.spec.rpkg index 720e94408..0a0f03557 100644 --- a/meshtasticd.spec.rpkg +++ b/meshtasticd.spec.rpkg @@ -21,7 +21,7 @@ Summary: Meshtastic daemon for communicating with Meshtastic devices License: GPL-3.0 URL: https://github.com/meshtastic/firmware Source0: {{{ git_dir_pack }}} -Source1: https://github.com/meshtastic/web/releases/download/latest/build.tar +Source1: https://github.com/meshtastic/web/releases/latest/download/build.tar BuildRequires: systemd-rpm-macros BuildRequires: python3-devel From 64def246eeeee4f9139e4c576da92a9faced8853 Mon Sep 17 00:00:00 2001 From: Tom <116762865+NomDeTom@users.noreply.github.com> Date: Thu, 6 Feb 2025 03:36:04 +0000 Subject: [PATCH 87/89] Corrected some misinformation (#5995) Change the module text too soon , before it had chance to reach a final conclusion. Co-authored-by: Tom <116762865+Nestpebble@users.noreply.github.com> --- .../diy/nrf52_promicro_diy_tcxo/readme.md | 2 +- .../diy/nrf52_promicro_diy_tcxo/variant.h | 40 +++++++++---------- 2 files changed, 21 insertions(+), 21 deletions(-) diff --git a/variants/diy/nrf52_promicro_diy_tcxo/readme.md b/variants/diy/nrf52_promicro_diy_tcxo/readme.md index 4da6566ec..585ac36de 100644 --- a/variants/diy/nrf52_promicro_diy_tcxo/readme.md +++ b/variants/diy/nrf52_promicro_diy_tcxo/readme.md @@ -31,7 +31,7 @@ Also worth noting that the Seeed WIO SX1262 in particular only has RXEN exposed | NiceRF | Lora1262 | yes | Int | | | Waveshare | Core1262-HF | yes | Ext | | | Waveshare | LoRa Node Module | yes | Int | | -| Seeed | Wio-SX1262 | yes | Int | Sooooo cute! | +| Seeed | Wio-SX1262 | yes | Ext | Cute! DIO2/TXEN are not exposed | | AI-Thinker | RA-02 | No | Int | SX1278 **433mhz band only** | | RF Solutions | RFM95 | No | Int | Untested | | Ebyte | E80-900M2213S | Yes | Int | LR1121 radio | diff --git a/variants/diy/nrf52_promicro_diy_tcxo/variant.h b/variants/diy/nrf52_promicro_diy_tcxo/variant.h index b74b100a3..de49018f4 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     | RF95 | +| 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   |   |         |             | | +| 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 @@ -175,7 +175,7 @@ settings. | NiceRF | Lora1262 | yes | Int | | | Waveshare | Core1262-HF | yes | Ext | | | Waveshare | LoRa Node Module | yes | Int | | -| Seeed | Wio-SX1262 | yes | Int | Sooooo cute! | +| Seeed | Wio-SX1262 | yes | Ext | Cute! DIO2/TXEN are not exposed | | AI-Thinker | RA-02 | No | Int | SX1278 **433mhz band only** | | RF Solutions | RFM95 | No | Int | Untested | | Ebyte | E80-900M2213S | Yes | Int | LR1121 radio | From 9db51a72a4459357af28d112baaebe91c0e0c39f Mon Sep 17 00:00:00 2001 From: Manuel <71137295+mverch67@users.noreply.github.com> Date: Thu, 6 Feb 2025 21:11:17 +0100 Subject: [PATCH 88/89] Fix T-Deck/T-Watch no BT (#5998) fixes #5997 --- src/mesh/NodeDB.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/mesh/NodeDB.cpp b/src/mesh/NodeDB.cpp index 4a01e0d41..9caa03928 100644 --- a/src/mesh/NodeDB.cpp +++ b/src/mesh/NodeDB.cpp @@ -407,7 +407,7 @@ bool NodeDB::resetRadioConfig(bool factory_reset) rebootAtMsec = millis() + (5 * 1000); } -#if (defined(T_DECK) || defined(T_WATCH_S3) || defined(UNPHONE) || defined(PICOMPUTER_S3)) && defined(HAS_TFT) +#if (defined(T_DECK) || defined(T_WATCH_S3) || defined(UNPHONE) || defined(PICOMPUTER_S3)) && HAS_TFT // as long as PhoneAPI shares BT and TFT app switch BT off config.bluetooth.enabled = false; if (moduleConfig.external_notification.nag_timeout == 60) @@ -1528,4 +1528,4 @@ void recordCriticalError(meshtastic_CriticalErrorCode code, uint32_t address, co LOG_ERROR("A critical failure occurred, portduino is exiting"); exit(2); #endif -} \ No newline at end of file +} From cb0519dd9ce9b19945a791f67ac6b81bf88567b6 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 6 Feb 2025 14:11:32 -0600 Subject: [PATCH 89/89] [create-pull-request] automated change (#5989) Co-authored-by: thebentern <9000580+thebentern@users.noreply.github.com> --- debian/changelog | 5 +++-- version.properties | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/debian/changelog b/debian/changelog index 1b371296b..3ec57b805 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,8 +1,9 @@ -meshtasticd (2.5.21.0) UNRELEASED; urgency=medium +meshtasticd (2.5.22.0) UNRELEASED; urgency=medium * Initial packaging * GitHub Actions Automatic version bump * GitHub Actions Automatic version bump * GitHub Actions Automatic version bump + * GitHub Actions Automatic version bump - -- Austin Lane Sat, 25 Jan 2025 01:39:16 +0000 + -- Austin Lane Wed, 05 Feb 2025 01:10:33 +0000 diff --git a/version.properties b/version.properties index efc42428c..2e207e21e 100644 --- a/version.properties +++ b/version.properties @@ -1,4 +1,4 @@ [VERSION] major = 2 minor = 5 -build = 21 +build = 22