Compare commits

..

2 Commits

Author SHA1 Message Date
Manuel
0451e61ec2 add to native after tests 2025-11-02 14:00:29 +01:00
Manuel
b4b2d36cfd fix strlcpy error in Ubuntu 20.04 2025-11-02 13:37:42 +01:00
222 changed files with 11260 additions and 38242 deletions

View File

@@ -1,7 +1,7 @@
# trunk-ignore-all(terrascan/AC_DOCKER_0002): Known terrascan issue
# trunk-ignore-all(hadolint/DL3008): Do not pin apt package versions
# trunk-ignore-all(hadolint/DL3013): Do not pin pip package versions
FROM mcr.microsoft.com/devcontainers/cpp:2-debian-13
FROM mcr.microsoft.com/devcontainers/cpp:2-debian-12
USER root

View File

@@ -8,7 +8,7 @@
"features": {
"ghcr.io/devcontainers/features/python:1": {
"installTools": true,
"version": "3.14"
"version": "3.13"
}
},
"customizations": {

View File

@@ -5,7 +5,7 @@ runs:
using: composite
steps:
- name: Checkout code
uses: actions/checkout@v6
uses: actions/checkout@v5
with:
submodules: recursive
ref: ${{github.event.pull_request.head.ref}}

View File

@@ -24,7 +24,7 @@ jobs:
runs-on: ubuntu-24.04
steps:
- name: Checkout code
uses: actions/checkout@v6
uses: actions/checkout@v5
with:
submodules: recursive
path: meshtasticd

View File

@@ -22,7 +22,7 @@ jobs:
outputs:
artifact-id: ${{ steps.upload.outputs.artifact-id }}
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v5
with:
submodules: recursive
ref: ${{github.event.pull_request.head.ref}}

176
.github/workflows/build_one_arch.yml vendored Normal file
View File

@@ -0,0 +1,176 @@
name: Build One Arch
on:
workflow_dispatch:
inputs:
# trunk-ignore(checkov/CKV_GHA_7)
arch:
type: choice
options:
- esp32
- esp32s3
- esp32c3
- esp32c6
- nrf52840
- rp2040
- rp2350
- stm32
- native
permissions: read-all
env:
INPUT_ARCH: ${{ github.event.inputs.arch }}
jobs:
setup:
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v5
- uses: actions/setup-python@v6
with:
python-version: 3.x
cache: pip
- run: pip install -U platformio
- name: Generate matrix
id: jsonStep
run: |
TARGETS=$(./bin/generate_ci_matrix.py $INPUT_ARCH --level extra)
echo "Name: $GITHUB_REF_NAME Base: $GITHUB_BASE_REF Ref: $GITHUB_REF"
echo "selected_arch=$TARGETS" >> $GITHUB_OUTPUT
outputs:
selected_arch: ${{ steps.jsonStep.outputs.selected_arch }}
version:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
- name: Get release version string
run: |
echo "long=$(./bin/buildinfo.py long)" >> $GITHUB_OUTPUT
echo "deb=$(./bin/buildinfo.py deb)" >> $GITHUB_OUTPUT
id: version
env:
BUILD_LOCATION: local
outputs:
long: ${{ steps.version.outputs.long }}
deb: ${{ steps.version.outputs.deb }}
build:
if: ${{ github.event_name != 'workflow_dispatch' }}
needs: [setup, version]
strategy:
fail-fast: false
matrix:
build: ${{ fromJson(needs.setup.outputs.selected_arch) }}
uses: ./.github/workflows/build_firmware.yml
with:
version: ${{ needs.version.outputs.long }}
pio_env: ${{ matrix.build.board }}
platform: ${{ matrix.build.arch }}
build-debian-src:
if: ${{ github.repository == 'meshtastic/firmware' && github.event_name != 'workflow_dispatch' || inputs.arch == 'native' }}
uses: ./.github/workflows/build_debian_src.yml
with:
series: UNRELEASED
build_location: local
secrets: inherit
package-pio-deps-native-tft:
if: ${{ inputs.arch == 'native' }}
uses: ./.github/workflows/package_pio_deps.yml
with:
pio_env: native-tft
secrets: inherit
test-native:
if: ${{ !contains(github.ref_name, 'event/') && github.event_name != 'workflow_dispatch' || !contains(github.ref_name, 'event/') && inputs.arch == 'native' }}
uses: ./.github/workflows/test_native.yml
gather-artifacts:
permissions:
contents: write
pull-requests: write
strategy:
fail-fast: false
matrix:
arch:
- esp32
- esp32s3
- esp32c3
- esp32c6
- nrf52840
- rp2040
- rp2350
- stm32
runs-on: ubuntu-latest
needs: [version, build]
steps:
- name: Checkout code
uses: actions/checkout@v5
with:
ref: ${{github.event.pull_request.head.ref}}
repository: ${{github.event.pull_request.head.repo.full_name}}
- uses: actions/download-artifact@v6
with:
path: ./
pattern: firmware-${{inputs.arch}}-*
merge-multiple: true
- name: Display structure of downloaded files
run: ls -R
- name: Move files up
run: mv -b -t ./ ./bin/device-*.sh ./bin/device-*.bat
- name: Repackage in single firmware zip
uses: actions/upload-artifact@v5
with:
name: firmware-${{inputs.arch}}-${{ needs.version.outputs.long }}
overwrite: true
path: |
./firmware-*.bin
./firmware-*.uf2
./firmware-*.hex
./firmware-*-ota.zip
./device-*.sh
./device-*.bat
./littlefs-*.bin
./bleota*bin
./Meshtastic_nRF52_factory_erase*.uf2
retention-days: 30
- uses: actions/download-artifact@v6
with:
name: firmware-${{inputs.arch}}-${{ needs.version.outputs.long }}
merge-multiple: true
path: ./output
# For diagnostics
- name: Show artifacts
run: ls -lR
- name: Device scripts permissions
run: |
chmod +x ./output/device-install.sh
chmod +x ./output/device-update.sh
- name: Zip firmware
run: zip -j -9 -r ./firmware-${{inputs.arch}}-${{ needs.version.outputs.long }}.zip ./output
- name: Repackage in single elfs zip
uses: actions/upload-artifact@v5
with:
name: debug-elfs-${{inputs.arch}}-${{ needs.version.outputs.long }}.zip
overwrite: true
path: ./*.elf
retention-days: 30
- uses: scruplelesswizard/comment-artifact@main
if: ${{ github.event_name == 'pull_request' }}
with:
name: firmware-${{inputs.arch}}-${{ needs.version.outputs.long }}
description: "Download firmware-${{inputs.arch}}-${{ needs.version.outputs.long }}.zip. This artifact will be available for 90 days from creation"
github-token: ${{ secrets.GITHUB_TOKEN }}

View File

@@ -15,6 +15,7 @@ on:
- rp2040
- rp2350
- stm32
- native
target:
type: string
required: false
@@ -41,9 +42,10 @@ jobs:
- rp2040
- rp2350
- stm32
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v5
- uses: actions/setup-python@v6
with:
python-version: 3.x
@@ -58,13 +60,13 @@ jobs:
echo "Arch: ${{matrix.arch}}" >> $GITHUB_STEP_SUMMARY
echo "Ref: $GITHUB_REF" >> $GITHUB_STEP_SUMMARY
echo "Targets:" >> $GITHUB_STEP_SUMMARY
echo $TARGETS | jq -r 'sort_by(.board) |.[] | "- " + .board' >> $GITHUB_STEP_SUMMARY
echo $TARGETS >> $GITHUB_STEP_SUMMARY
version:
if: ${{ inputs.target != '' }}
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v5
- name: Get release version string
run: |
echo "long=$(./bin/buildinfo.py long)" >> $GITHUB_OUTPUT
@@ -85,6 +87,25 @@ jobs:
pio_env: ${{ inputs.target }}
platform: ${{ inputs.arch }}
build-debian-src:
if: ${{ github.repository == 'meshtastic/firmware' && inputs.arch == 'native' }}
uses: ./.github/workflows/build_debian_src.yml
with:
series: UNRELEASED
build_location: local
secrets: inherit
package-pio-deps-native-tft:
if: ${{ inputs.arch == 'native' }}
uses: ./.github/workflows/package_pio_deps.yml
with:
pio_env: native-tft
secrets: inherit
test-native:
if: ${{ !contains(github.ref_name, 'event/') && github.event_name != 'workflow_dispatch' || !contains(github.ref_name, 'event/') && inputs.arch == 'native' && inputs.target != '' }}
uses: ./.github/workflows/test_native.yml
gather-artifacts:
permissions:
contents: write
@@ -93,7 +114,7 @@ jobs:
needs: [version, build]
steps:
- name: Checkout code
uses: actions/checkout@v6
uses: actions/checkout@v5
with:
ref: ${{github.event.pull_request.head.ref}}
repository: ${{github.event.pull_request.head.repo.full_name}}

View File

@@ -47,7 +47,7 @@ jobs:
runs-on: ${{ inputs.runs-on }}
steps:
- name: Checkout code
uses: actions/checkout@v6
uses: actions/checkout@v5
with:
submodules: recursive
ref: ${{github.event.pull_request.head.ref}}

View File

@@ -83,7 +83,7 @@ jobs:
runs-on: ubuntu-24.04
steps:
- name: Checkout code
uses: actions/checkout@v6
uses: actions/checkout@v5
with:
submodules: recursive
ref: ${{github.event.pull_request.head.ref}}

View File

@@ -19,7 +19,7 @@ jobs:
runs-on: ubuntu-24.04
steps:
- name: Checkout code
uses: actions/checkout@v6
uses: actions/checkout@v5
with:
submodules: recursive
ref: ${{ github.ref }}

View File

@@ -35,7 +35,7 @@ jobs:
- check
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v5
- uses: actions/setup-python@v6
with:
python-version: 3.x
@@ -59,7 +59,7 @@ jobs:
version:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v5
- name: Get release version string
run: |
echo "long=$(./bin/buildinfo.py long)" >> $GITHUB_OUTPUT
@@ -81,7 +81,7 @@ jobs:
runs-on: ubuntu-latest
if: ${{ github.event_name != 'workflow_dispatch' && github.repository == 'meshtastic/firmware' }}
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v5
- name: Build base
id: base
uses: ./.github/actions/setup-base
@@ -163,7 +163,7 @@ jobs:
needs: [version, build]
steps:
- name: Checkout code
uses: actions/checkout@v6
uses: actions/checkout@v5
with:
ref: ${{github.event.pull_request.head.ref}}
repository: ${{github.event.pull_request.head.repo.full_name}}
@@ -242,7 +242,7 @@ jobs:
- package-pio-deps-native-tft
steps:
- name: Checkout
uses: actions/checkout@v6
uses: actions/checkout@v5
- name: Setup Python
uses: actions/setup-python@v6
@@ -311,7 +311,7 @@ jobs:
needs: [release-artifacts, version]
steps:
- name: Checkout
uses: actions/checkout@v6
uses: actions/checkout@v5
- name: Setup Python
uses: actions/setup-python@v6
@@ -366,7 +366,7 @@ jobs:
esp32,esp32s3,esp32c3,esp32c6,nrf52840,rp2040,rp2350,stm32
steps:
- name: Checkout
uses: actions/checkout@v6
uses: actions/checkout@v5
- name: Setup Python
uses: actions/setup-python@v6

View File

@@ -17,7 +17,7 @@ jobs:
- check
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v5
- uses: actions/setup-python@v6
with:
python-version: 3.x
@@ -40,7 +40,7 @@ jobs:
version:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v5
- name: Get release version string
run: |
echo "long=$(./bin/buildinfo.py long)" >> $GITHUB_OUTPUT
@@ -62,7 +62,7 @@ jobs:
runs-on: ubuntu-latest
if: ${{ github.event_name != 'workflow_dispatch' }}
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v5
- name: Build base
id: base
uses: ./.github/actions/setup-base
@@ -142,7 +142,7 @@ jobs:
needs: [version, build]
steps:
- name: Checkout code
uses: actions/checkout@v6
uses: actions/checkout@v5
with:
ref: ${{github.event.pull_request.head.ref}}
repository: ${{github.event.pull_request.head.repo.full_name}}
@@ -221,7 +221,7 @@ jobs:
- package-pio-deps-native-tft
steps:
- name: Checkout
uses: actions/checkout@v6
uses: actions/checkout@v5
- name: Setup Python
uses: actions/setup-python@v6
@@ -290,7 +290,7 @@ jobs:
needs: [release-artifacts, version]
steps:
- name: Checkout
uses: actions/checkout@v6
uses: actions/checkout@v5
- name: Setup Python
uses: actions/setup-python@v6
@@ -345,7 +345,7 @@ jobs:
esp32,esp32s3,esp32c3,esp32c6,nrf52840,rp2040,rp2350,stm32
steps:
- name: Checkout
uses: actions/checkout@v6
uses: actions/checkout@v5
- name: Setup Python
uses: actions/setup-python@v6

View File

@@ -14,7 +14,7 @@ jobs:
steps:
- name: Checkout
uses: actions/checkout@v6
uses: actions/checkout@v5
- name: Trunk Check
uses: trunk-io/trunk-action@v1
@@ -31,7 +31,7 @@ jobs:
pull-requests: write # For trunk to create PRs
steps:
- name: Checkout
uses: actions/checkout@v6
uses: actions/checkout@v5
- name: Trunk Upgrade
uses: trunk-io/trunk-action/upgrade@v1

View File

@@ -34,7 +34,7 @@ jobs:
needs: build-debian-src
steps:
- name: Checkout code
uses: actions/checkout@v6
uses: actions/checkout@v5
with:
submodules: recursive
path: meshtasticd

View File

@@ -24,7 +24,7 @@ jobs:
runs-on: ubuntu-24.04
steps:
- name: Checkout code
uses: actions/checkout@v6
uses: actions/checkout@v5
with:
submodules: recursive
ref: ${{github.event.pull_request.head.ref}}

View File

@@ -32,7 +32,7 @@ jobs:
needs: build-debian-src
steps:
- name: Checkout code
uses: actions/checkout@v6
uses: actions/checkout@v5
with:
submodules: recursive
path: meshtasticd

View File

@@ -40,7 +40,7 @@ jobs:
checks: write
pull-requests: write
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v5
with:
submodules: recursive

View File

@@ -60,10 +60,7 @@ jobs:
shell: bash
steps:
- name: Checkout
uses: actions/checkout@v6
with:
# Always use master branch for version bumps
ref: master
uses: actions/checkout@v5
- name: Setup Python
uses: actions/setup-python@v6

View File

@@ -21,7 +21,7 @@ jobs:
steps:
# step 1
- name: clone application source code
uses: actions/checkout@v6
uses: actions/checkout@v5
# step 2
- name: full scan

View File

@@ -13,7 +13,7 @@ jobs:
steps:
# step 1
- name: clone application source code
uses: actions/checkout@v6
uses: actions/checkout@v5
with:
fetch-depth: 0

View File

@@ -14,7 +14,7 @@ jobs:
name: Native Simulator Tests
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v5
with:
ref: ${{github.event.pull_request.head.ref}}
repository: ${{github.event.pull_request.head.repo.full_name}}
@@ -70,7 +70,7 @@ jobs:
name: Native PlatformIO Tests
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v5
with:
ref: ${{github.event.pull_request.head.ref}}
repository: ${{github.event.pull_request.head.repo.full_name}}
@@ -127,7 +127,7 @@ jobs:
- platformio-tests
if: always()
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v5
with:
ref: ${{github.event.pull_request.head.ref}}
repository: ${{github.event.pull_request.head.repo.full_name}}
@@ -143,7 +143,7 @@ jobs:
merge-multiple: true
- name: Test Report
uses: dorny/test-reporter@v2.3.0
uses: dorny/test-reporter@v2.1.1
with:
name: PlatformIO Tests
path: testreport.xml

View File

@@ -20,7 +20,7 @@ jobs:
runs-on: test-runner
steps:
- name: Checkout code
uses: actions/checkout@v6
uses: actions/checkout@v5
# - uses: actions/setup-python@v5
# with:

View File

@@ -18,7 +18,7 @@ jobs:
steps:
- name: Checkout
uses: actions/checkout@v6
uses: actions/checkout@v5
- name: Trunk Check
uses: trunk-io/trunk-action@v1

View File

@@ -16,7 +16,7 @@ jobs:
steps:
- name: Checkout
uses: actions/checkout@v6
uses: actions/checkout@v5
- name: Trunk Check
uses: trunk-io/trunk-action@v1

View File

@@ -15,7 +15,7 @@ jobs:
pull-requests: write
steps:
- name: Checkout repository
uses: actions/checkout@v6
uses: actions/checkout@v5
with:
ref: ${{github.event.pull_request.head.ref}}
repository: ${{github.event.pull_request.head.repo.full_name}}

View File

@@ -11,7 +11,7 @@ jobs:
pull-requests: write
steps:
- name: Checkout code
uses: actions/checkout@v6
uses: actions/checkout@v5
with:
submodules: true

View File

@@ -4,31 +4,31 @@ cli:
plugins:
sources:
- id: trunk
ref: v1.7.4
ref: v1.7.3
uri: https://github.com/trunk-io/plugins
lint:
enabled:
- checkov@3.2.495
- renovate@42.27.1
- prettier@3.7.3
- trufflehog@3.91.1
- checkov@3.2.486
- renovate@41.157.0
- prettier@3.6.2
- trufflehog@3.90.11
- yamllint@1.37.1
- bandit@1.9.2
- bandit@1.8.6
- trivy@0.67.2
- taplo@0.10.0
- ruff@0.14.7
- ruff@0.14.1
- isort@7.0.0
- markdownlint@0.46.0
- markdownlint@0.45.0
- oxipng@9.1.5
- svgo@4.0.0
- actionlint@1.7.9
- actionlint@1.7.8
- flake8@7.3.0
- hadolint@2.14.0
- shfmt@3.6.0
- shellcheck@0.11.0
- black@25.11.0
- black@25.9.0
- git-diff-check
- gitleaks@8.30.0
- gitleaks@8.28.0
- clang-format@16.0.3
ignore:
- linters: [ALL]

View File

@@ -37,3 +37,4 @@ Join our community and help improve Meshtastic! 🚀
## Stats
![Alt](https://repobeats.axiom.co/api/embed/8025e56c482ec63541593cc5bd322c19d5c0bdcf.svg "Repobeats analytics image")

View File

@@ -8,8 +8,7 @@ ARG PIO_ENV=native
ENV PIP_ROOT_USER_ACTION=ignore
RUN apk --no-cache add \
bash g++ libstdc++-dev linux-headers zip git ca-certificates libbsd-dev \
libgpiod-dev yaml-cpp-dev bluez-dev \
bash g++ libstdc++-dev linux-headers zip git ca-certificates libgpiod-dev yaml-cpp-dev bluez-dev \
libusb-dev i2c-tools-dev libuv-dev openssl-dev pkgconf argp-standalone \
libx11-dev libinput-dev libxkbcommon-dev \
&& rm -rf /var/cache/apk/* \
@@ -41,8 +40,8 @@ LABEL org.opencontainers.image.title="Meshtastic" \
USER root
RUN apk --no-cache add \
shadow libstdc++ libbsd libgpiod yaml-cpp libusb \
i2c-tools libuv libx11 libinput libxkbcommon \
shadow libstdc++ libgpiod yaml-cpp libusb i2c-tools libuv \
libx11 libinput libxkbcommon \
&& rm -rf /var/cache/apk/* \
&& mkdir -p /var/lib/meshtasticd \
&& mkdir -p /etc/meshtasticd/config.d \

View File

@@ -57,7 +57,7 @@ lib_deps =
# renovate: datasource=git-refs depName=libpax packageName=https://github.com/dbinfrago/libpax gitBranch=master
https://github.com/dbinfrago/libpax/archive/3cdc0371c375676a97967547f4065607d4c53fd1.zip
# renovate: datasource=github-tags depName=XPowersLib packageName=lewisxhe/XPowersLib
https://github.com/lewisxhe/XPowersLib/archive/v0.3.2.zip
https://github.com/lewisxhe/XPowersLib/archive/v0.3.1.zip
# renovate: datasource=git-refs depName=meshtastic-ESP32_Codec2 packageName=https://github.com/meshtastic/ESP32_Codec2 gitBranch=master
https://github.com/meshtastic/ESP32_Codec2/archive/633326c78ac251c059ab3a8c430fcdf25b41672f.zip
# renovate: datasource=custom.pio depName=rweather/Crypto packageName=rweather/library/Crypto

View File

@@ -28,7 +28,7 @@ lib_deps =
${environmental_extra.lib_deps}
${radiolib_base.lib_deps}
# renovate: datasource=custom.pio depName=XPowersLib packageName=lewisxhe/library/XPowersLib
lewisxhe/XPowersLib@0.3.2
lewisxhe/XPowersLib@0.3.1
# renovate: datasource=git-refs depName=meshtastic-ESP32_Codec2 packageName=https://github.com/meshtastic/ESP32_Codec2 gitBranch=master
https://github.com/meshtastic/ESP32_Codec2/archive/633326c78ac251c059ab3a8c430fcdf25b41672f.zip
# renovate: datasource=custom.pio depName=rweather/Crypto packageName=rweather/library/Crypto

View File

@@ -7,13 +7,13 @@ extends = arduino_base
platform_packages =
; our custom Git version until they merge our PR
# TODO renovate
platformio/framework-arduinoadafruitnrf52 @ https://github.com/meshtastic/Adafruit_nRF52_Arduino#c770c8a16a351b55b86e347a3d9d7b74ad0bbf39
platformio/framework-arduinoadafruitnrf52 @ https://github.com/meshtastic/Adafruit_nRF52_Arduino#e13f5820002a4fb2a5e6754b42ace185277e5adf
; Don't renovate toolchain-gccarmnoneeabi
platformio/toolchain-gccarmnoneeabi@~1.90301.0
build_type = release
build_type = debug
build_flags =
-include variants/nrf52840/cpp_overrides/lfs_util.h
-include arch/nrf52/cpp_overrides/lfs_util.h
${arduino_base.build_flags}
-DSERIAL_BUFFER_SIZE=1024
-Wno-unused-variable
@@ -21,17 +21,6 @@ build_flags =
-DLFS_NO_ASSERT ; Disable LFS assertions , see https://github.com/meshtastic/firmware/pull/3818
-DMESHTASTIC_EXCLUDE_AUDIO=1
-DMESHTASTIC_EXCLUDE_PAXCOUNTER=1
-Os
build_unflags =
-Ofast
-Og
-ggdb3
-ggdb2
-g3
-g2
-g
-g1
-g0
build_src_filter =
${arduino_base.build_src_filter} -<platform/esp32/> -<platform/stm32wl> -<nimble/> -<mesh/wifi/> -<mesh/api/> -<mesh/http/> -<modules/esp32> -<platform/rp2xx0> -<mesh/eth/> -<mesh/raspihttp> -<serialization/>

View File

@@ -8,7 +8,7 @@ lib_deps =
${environmental_base.lib_deps}
${environmental_extra.lib_deps}
# renovate: datasource=git-refs depName=Kongduino-Adafruit_nRFCrypto packageName=https://github.com/Kongduino/Adafruit_nRFCrypto gitBranch=master
https://github.com/Kongduino/Adafruit_nRFCrypto/archive/8cde7189b5ead9dcd49f72601b43b969c0bbc06e.zip
https://github.com/Kongduino/Adafruit_nRFCrypto/archive/5f838d2709461a2c981f642917aa50254a25c14c.zip
; Common NRF52 debugging settings follow. See the Meshtastic developer docs for how to connect SWD debugging probes to your board.

View File

@@ -2,7 +2,7 @@
[portduino_base]
platform =
# renovate: datasource=git-refs depName=platform-native packageName=https://github.com/meshtastic/platform-native gitBranch=develop
https://github.com/meshtastic/platform-native/archive/f566d364204416cdbf298e349213f7d551f793d9.zip
https://github.com/meshtastic/platform-native/archive/d3f6e339534233c7217818867368767590ce549e.zip
framework = arduino
build_src_filter =

View File

@@ -2,7 +2,7 @@
extends = arduino_base
platform =
# renovate: datasource=custom.pio depName=platformio/ststm32 packageName=platformio/platform/ststm32
platformio/ststm32@19.4.0
platformio/ststm32@19.3.0
platform_packages =
# TODO renovate
platformio/framework-arduinoststm32@https://github.com/stm32duino/Arduino_Core_STM32/archive/2.10.1.zip
@@ -37,9 +37,6 @@ build_flags =
-DRADIOLIB_EXCLUDE_LR11X0=1
-DHAL_DAC_MODULE_ONLY
-DHAL_RNG_MODULE_ENABLED
-Wl,--wrap=__assert_func
-Wl,--wrap=strerror
-Wl,--wrap=_tzset_unlocked_r
build_src_filter =
${arduino_base.build_src_filter} -<platform/esp32/> -<nimble/> -<mesh/api/> -<mesh/wifi/> -<mesh/http/> -<modules/esp32> -<mesh/eth/> -<input> -<buzz> -<modules/RemoteHardwareModule.cpp> -<platform/nrf52> -<platform/portduino> -<platform/rp2xx0> -<mesh/raspihttp>

View File

@@ -1,165 +0,0 @@
#!/usr/bin/env python3
"""Summarise linker map output to highlight heavy object files and libraries.
Usage:
python bin/analyze_map.py --map .pio/build/rak4631/output.map --top 20
The script parses GNU ld map files and aggregates section sizes per object file
and per archive/library, then prints sortable tables that make it easy to spot
modules worth trimming or hiding behind feature flags.
"""
from __future__ import annotations
import argparse
import collections
import os
import re
import sys
from typing import DefaultDict, Dict, Tuple
SECTION_LINE_RE = re.compile(r"^\s+(?P<section>\S+)\s+0x[0-9A-Fa-f]+\s+0x(?P<size>[0-9A-Fa-f]+)\s+(?P<object>.+)$")
ARCHIVE_MEMBER_RE = re.compile(r"^(?P<archive>.+)\((?P<object>[^)]+)\)$")
def human_size(num_bytes: int) -> str:
"""Return a friendly size string with one decimal place."""
if num_bytes < 1024:
return f"{num_bytes:,} B"
num = float(num_bytes)
for unit in ("KB", "MB", "GB"):
num /= 1024.0
if num < 1024.0:
return f"{num:.1f} {unit}"
return f"{num:.1f} TB"
def shorten_path(path: str, root: str) -> str:
"""Prefer repository-relative paths for readability."""
path = path.strip()
if not path:
return path
# Normalise Windows archives (backslashes) to POSIX style for consistency.
path = path.replace("\\", "/")
# Attempt to strip the root when an absolute path lives inside the repo.
if os.path.isabs(path):
try:
rel = os.path.relpath(path, root)
if not rel.startswith(".."):
return rel
except ValueError:
# relpath can fail on mixed drives on Windows; fall back to basename.
pass
return path
def describe_object(raw_object: str, root: str) -> Tuple[str, str]:
"""Return a human friendly object label and the library it belongs to."""
raw_object = raw_object.strip()
lib_label = "[app]"
match = ARCHIVE_MEMBER_RE.match(raw_object)
if match:
archive = shorten_path(match.group("archive"), root)
obj = match.group("object")
lib_label = os.path.basename(archive) or archive
label = f"{archive}:{obj}"
else:
label = shorten_path(raw_object, root)
# If the object lives under libs, hint at the containing directory.
parent = os.path.basename(os.path.dirname(label))
if parent:
lib_label = parent
return label, lib_label
def parse_map(map_path: str, repo_root: str) -> Tuple[Dict[str, int], Dict[str, int], Dict[str, Dict[str, int]]]:
per_object: DefaultDict[str, int] = collections.defaultdict(int)
per_library: DefaultDict[str, int] = collections.defaultdict(int)
per_object_sections: DefaultDict[str, DefaultDict[str, int]] = collections.defaultdict(lambda: collections.defaultdict(int))
try:
with open(map_path, "r", encoding="utf-8", errors="ignore") as handle:
for line in handle:
match = SECTION_LINE_RE.match(line)
if not match:
continue
section = match.group("section")
if section.startswith("*") or section in {"LOAD", "ORIGIN"}:
continue
size = int(match.group("size"), 16)
if size == 0:
continue
obj_token = match.group("object").strip()
if not obj_token or obj_token.startswith("*") or "load address" in obj_token:
continue
label, lib_label = describe_object(obj_token, repo_root)
per_object[label] += size
per_library[lib_label] += size
per_object_sections[label][section] += size
except FileNotFoundError:
raise SystemExit(f"error: map file '{map_path}' not found. Run a build first.")
return per_object, per_library, per_object_sections
def format_section_breakdown(section_sizes: Dict[str, int], total: int, limit: int = 3) -> str:
items = sorted(section_sizes.items(), key=lambda kv: kv[1], reverse=True)
parts = []
for section, size in items[:limit]:
pct = (size / total) * 100 if total else 0
parts.append(f"{section} {pct:.1f}%")
if len(items) > limit:
remainder = total - sum(size for _, size in items[:limit])
pct = (remainder / total) * 100 if total else 0
parts.append(f"other {pct:.1f}%")
return ", ".join(parts)
def print_report(map_path: str, top_n: int, per_object: Dict[str, int], per_library: Dict[str, int], per_object_sections: Dict[str, Dict[str, int]]):
total_bytes = sum(per_object.values())
if total_bytes == 0:
print("No section data found in map file.")
return
print(f"Map file: {map_path}")
print(f"Accounted size: {human_size(total_bytes)} across {len(per_object)} object files\n")
sorted_objects = sorted(per_object.items(), key=lambda kv: kv[1], reverse=True)
print(f"Top {min(top_n, len(sorted_objects))} object files by linked size:")
for idx, (obj, size) in enumerate(sorted_objects[:top_n], 1):
pct = (size / total_bytes) * 100
breakdown = format_section_breakdown(per_object_sections[obj], size)
print(f"{idx:2}. {human_size(size):>9} ({size:,} B, {pct:5.2f}% of linked size)")
print(f" {obj}")
if breakdown:
print(f" sections: {breakdown}")
print()
sorted_libs = sorted(per_library.items(), key=lambda kv: kv[1], reverse=True)
print(f"Top {min(top_n, len(sorted_libs))} libraries or source roots:")
for idx, (lib, size) in enumerate(sorted_libs[:top_n], 1):
pct = (size / total_bytes) * 100
print(f"{idx:2}. {human_size(size):>9} ({size:,} B, {pct:5.2f}% of linked size) {lib}")
def main() -> None:
parser = argparse.ArgumentParser(description="Highlight heavy object files from a GNU ld map file.")
parser.add_argument("--map", default=".pio/build/rak4631/output.map", help="Path to the map file (default: %(default)s)")
parser.add_argument("--top", type=int, default=20, help="Number of entries to display per table (default: %(default)s)")
args = parser.parse_args()
map_path = os.path.abspath(args.map)
repo_root = os.path.abspath(os.getcwd())
per_object, per_library, per_object_sections = parse_map(map_path, repo_root)
print_report(os.path.relpath(map_path, repo_root), args.top, per_object, per_library, per_object_sections)
if __name__ == "__main__":
main()

View File

@@ -87,18 +87,6 @@
</screenshots>
<releases>
<release version="2.7.17" date="2025-11-28">
<url type="details">https://github.com/meshtastic/firmware/releases?q=tag%3Av2.7.17</url>
</release>
<release version="2.7.16" date="2025-11-19">
<url type="details">https://github.com/meshtastic/firmware/releases?q=tag%3Av2.7.16</url>
</release>
<release version="2.7.15" date="2025-11-13">
<url type="details">https://github.com/meshtastic/firmware/releases?q=tag%3Av2.7.15</url>
</release>
<release version="2.7.14" date="2025-11-03">
<url type="details">https://github.com/meshtastic/firmware/releases?q=tag%3Av2.7.14</url>
</release>
<release version="2.7.13" date="2025-10-11">
<url type="details">https://github.com/meshtastic/firmware/releases?q=tag%3Av2.7.13</url>
</release>

View File

@@ -1,53 +0,0 @@
{
"build": {
"arduino": {
"ldscript": "nrf52840_s140_v6.ld"
},
"core": "nRF5",
"cpu": "cortex-m4",
"extra_flags": "-DNRF52840_XXAA",
"f_cpu": "64000000L",
"hwids": [
["0x239A", "0x4405"],
["0x239A", "0x0029"],
["0x239A", "0x002A"]
],
"usb_product": "elecrow_eink",
"mcu": "nrf52840",
"variant": "ELECROW-ThinkNode-M3",
"variants_dir": "variants",
"bsp": {
"name": "adafruit"
},
"softdevice": {
"sd_flags": "-DS140",
"sd_name": "s140",
"sd_version": "6.1.1",
"sd_fwid": "0x00B6"
},
"bootloader": {
"settings_addr": "0xFF000"
}
},
"connectivity": ["bluetooth"],
"debug": {
"jlink_device": "nRF52840_xxAA",
"onboard_tools": ["jlink"],
"svd_path": "nrf52840.svd",
"openocd_target": "nrf52840-mdk-rs"
},
"frameworks": ["arduino"],
"name": "elecrow nrf",
"upload": {
"maximum_ram_size": 248832,
"maximum_size": 815104,
"speed": 115200,
"protocol": "nrfutil",
"protocols": ["jlink", "nrfjprog", "nrfutil", "stlink"],
"use_1200bps_touch": true,
"require_upload_port": true,
"wait_for_upload_port": true
},
"url": "",
"vendor": "ELECROW"
}

View File

@@ -1,53 +0,0 @@
{
"build": {
"arduino": {
"ldscript": "nrf52840_s140_v6.ld"
},
"core": "nRF5",
"cpu": "cortex-m4",
"extra_flags": "-DARDUINO_NRF52840_ELECROW_M6 -DNRF52840_XXAA",
"f_cpu": "64000000L",
"hwids": [
["0x239A", "0x4405"],
["0x239A", "0x0029"],
["0x239A", "0x002A"]
],
"usb_product": "elecrow_thinknode_m6",
"mcu": "nrf52840",
"variant": "ELECROW-ThinkNode-M6",
"variants_dir": "variants",
"bsp": {
"name": "adafruit"
},
"softdevice": {
"sd_flags": "-DS140",
"sd_name": "s140",
"sd_version": "6.1.1",
"sd_fwid": "0x00B6"
},
"bootloader": {
"settings_addr": "0xFF000"
}
},
"connectivity": ["bluetooth"],
"debug": {
"jlink_device": "nRF52840_xxAA",
"onboard_tools": ["jlink"],
"svd_path": "nrf52840.svd",
"openocd_target": "nrf52840-mdk-rs"
},
"frameworks": ["arduino"],
"name": "ELECROW ThinkNode M6",
"upload": {
"maximum_ram_size": 248832,
"maximum_size": 815104,
"speed": 115200,
"protocol": "nrfutil",
"protocols": ["jlink", "nrfjprog", "nrfutil", "stlink"],
"use_1200bps_touch": true,
"require_upload_port": true,
"wait_for_upload_port": true
},
"url": "https://www.elecrow.com/thinknode-m6-outdoor-solar-power-for-lora-powered-by-nrf52840-supports-gps.html",
"vendor": "ELECROW"
}

View File

@@ -1,41 +0,0 @@
{
"build": {
"arduino": {
"ldscript": "esp32s3_out.ld",
"memory_type": "qio_opi"
},
"core": "esp32",
"extra_flags": [
"-DBOARD_HAS_PSRAM",
"-DARDUINO_USB_CDC_ON_BOOT=1",
"-DARDUINO_USB_MODE=0",
"-DARDUINO_RUNNING_CORE=1",
"-DARDUINO_EVENT_RUNNING_CORE=1"
],
"f_cpu": "240000000L",
"f_flash": "80000000L",
"flash_mode": "qio",
"hwids": [["0x303A", "0x1001"]],
"mcu": "esp32s3",
"variant": "hackaday-communicator"
},
"connectivity": ["wifi", "bluetooth", "lora"],
"debug": {
"default_tool": "esp-builtin",
"onboard_tools": ["esp-builtin"],
"openocd_target": "esp32s3.cfg"
},
"frameworks": ["arduino", "espidf"],
"name": "hackaday-communicator (16 MB FLASH, 8 MB PSRAM)",
"upload": {
"flash_size": "16MB",
"maximum_ram_size": 327680,
"maximum_size": 16777216,
"use_1200bps_touch": true,
"wait_for_upload_port": true,
"require_upload_port": true,
"speed": 1500000
},
"url": "hackaday.com",
"vendor": "hackaday"
}

View File

@@ -9,7 +9,7 @@
"extra_flags": [
"-DBOARD_HAS_PSRAM",
"-DARDUINO_USB_CDC_ON_BOOT=1",
"-DARDUINO_USB_MODE=1",
"-DARDUINO_USB_MODE=0",
"-DARDUINO_RUNNING_CORE=1",
"-DARDUINO_EVENT_RUNNING_CORE=1"
],

View File

@@ -1,56 +0,0 @@
{
"build": {
"arduino": {
"ldscript": "nrf52840_s140_v6.ld"
},
"core": "nRF5",
"cpu": "cortex-m4",
"extra_flags": "-DARDUINO_NRF52840_MUZI_BASE -DNRF52840_XXAA",
"f_cpu": "64000000L",
"hwids": [["0x239A", "0xcafe"]],
"mcu": "nrf52840",
"variant": "muzi-base",
"variants_dir": "variants",
"bsp": {
"name": "adafruit"
},
"softdevice": {
"sd_flags": "-DS140",
"sd_name": "s140",
"sd_version": "6.1.1",
"sd_fwid": "0x00B6"
},
"bootloader": {
"settings_addr": "0xFF000"
}
},
"connectivity": ["bluetooth"],
"debug": {
"jlink_device": "nRF52840_xxAA",
"onboard_tools": ["jlink"],
"svd_path": "nrf52840.svd",
"openocd_target": "nrf52840-mdk-rs"
},
"frameworks": ["arduino"],
"name": "Muzi Base",
"url": "https://muzi.works/",
"vendor": "MuziWorks",
"upload": {
"maximum_ram_size": 248832,
"maximum_size": 815104,
"speed": 115200,
"protocol": "nrfutil",
"protocols": [
"jlink",
"nrfjprog",
"nrfutil",
"blackmagic",
"cmsis-dap",
"mbed",
"stlink"
],
"use_1200bps_touch": true,
"require_upload_port": true,
"wait_for_upload_port": true
}
}

25
debian/changelog vendored
View File

@@ -1,28 +1,3 @@
meshtasticd (2.7.17.0) unstable; urgency=medium
* Version 2.7.17
-- GitHub Actions <github-actions[bot]@users.noreply.github.com> Fri, 28 Nov 2025 15:11:34 +0000
meshtasticd (2.7.16.0) unstable; urgency=medium
* Version 2.7.16
-- GitHub Actions <github-actions[bot]@users.noreply.github.com> Wed, 19 Nov 2025 16:12:32 +0000
meshtasticd (2.7.15.0) unstable; urgency=medium
* Version 2.7.15
-- GitHub Actions <github-actions[bot]@users.noreply.github.com> Thu, 13 Nov 2025 12:31:57 +0000
meshtasticd (2.7.14.0) unstable; urgency=medium
* Version 2.7.14
-- GitHub Actions <github-actions[bot]@users.noreply.github.com> Mon, 03 Nov 2025 16:11:31 +0000
meshtasticd (2.7.13.0) unstable; urgency=medium
* Version 2.7.13

1
debian/control vendored
View File

@@ -3,7 +3,6 @@ Section: misc
Priority: optional
Maintainer: Austin Lane <vidplace7@gmail.com>
Build-Depends: debhelper-compat (= 13),
libc6-dev (>= 2.38) | libbsd-dev,
lsb-release,
tar,
gzip,

View File

@@ -49,13 +49,6 @@ BuildRequires: pkgconfig(x11)
BuildRequires: pkgconfig(libinput)
BuildRequires: pkgconfig(xkbcommon-x11)
# libbsd is needed on older Fedora/RHEL to provide 'strlcpy'
%if 0%{?fedora} >= 39 || 0%{?rhel} >= 10
BuildRequires: glibc-devel >= 2.38
%else
BuildRequires: pkgconfig(libbsd-overlay)
%endif
Requires: systemd-udev
%description

View File

@@ -5,7 +5,7 @@
default_envs = tbeam
extra_configs =
variants/*/*.ini
arch/*/*.ini
variants/*/*/platformio.ini
variants/*/diy/*/platformio.ini
src/graphics/niche/InkHUD/PlatformioConfig.ini
@@ -62,7 +62,7 @@ monitor_speed = 115200
monitor_filters = direct
lib_deps =
# renovate: datasource=git-refs depName=meshtastic-esp8266-oled-ssd1306 packageName=https://github.com/meshtastic/esp8266-oled-ssd1306 gitBranch=master
https://github.com/meshtastic/esp8266-oled-ssd1306/archive/2887bf4a19f64d92c984dcc8fd5ca7429e425e4a.zip
https://github.com/meshtastic/esp8266-oled-ssd1306/archive/0cbc26b1f8f61957af0475f486b362eafe7cc4e2.zip
# renovate: datasource=git-refs depName=meshtastic-OneButton packageName=https://github.com/meshtastic/OneButton gitBranch=master
https://github.com/meshtastic/OneButton/archive/fa352d668c53f290cfa480a5f79ad422cd828c70.zip
# renovate: datasource=git-refs depName=meshtastic-arduino-fsm packageName=https://github.com/meshtastic/arduino-fsm gitBranch=master
@@ -90,7 +90,7 @@ framework = arduino
lib_deps =
${env.lib_deps}
# renovate: datasource=custom.pio depName=NonBlockingRTTTL packageName=end2endzone/library/NonBlockingRTTTL
end2endzone/NonBlockingRTTTL@1.4.0
end2endzone/NonBlockingRTTTL@1.3.0
build_flags = ${env.build_flags} -Os
build_src_filter = ${env.build_src_filter} -<platform/portduino/> -<graphics/niche/>
@@ -115,13 +115,12 @@ lib_deps =
[radiolib_base]
lib_deps =
# renovate: datasource=custom.pio depName=RadioLib packageName=jgromes/library/RadioLib
# jgromes/RadioLib@7.4.0
https://github.com/jgromes/RadioLib/archive/536c7267362e2c1345be7054ba45e503252975ff.zip
jgromes/RadioLib@7.3.0
[device-ui_base]
lib_deps =
# renovate: datasource=git-refs depName=meshtastic/device-ui packageName=https://github.com/meshtastic/device-ui gitBranch=master
https://github.com/meshtastic/device-ui/archive/3bf332240416c5cb8c919fac2a0ec7260eb3be75.zip
https://github.com/meshtastic/device-ui/archive/19b7855e9a1d9deff37391659ca7194e4ef57c43.zip
; Common libs for environmental measurements in telemetry module
[environmental_base]
@@ -169,7 +168,7 @@ lib_deps =
# renovate: datasource=git-refs depName=DFRobot_RainfallSensor packageName=https://github.com/DFRobot/DFRobot_RainfallSensor gitBranch=master
https://github.com/DFRobot/DFRobot_RainfallSensor/archive/38fea5e02b40a5430be6dab39a99a6f6347d667e.zip
# renovate: datasource=custom.pio depName=INA226 packageName=robtillaart/library/INA226
robtillaart/INA226@0.6.5
robtillaart/INA226@0.6.4
# renovate: datasource=custom.pio depName=SparkFun MAX3010x packageName=sparkfun/library/SparkFun MAX3010x Pulse and Proximity Sensor Library
sparkfun/SparkFun MAX3010x Pulse and Proximity Sensor Library@1.1.2
# renovate: datasource=custom.pio depName=SparkFun 9DoF IMU Breakout ICM 20948 packageName=sparkfun/library/SparkFun 9DoF IMU Breakout - ICM 20948 - Arduino Library
@@ -177,13 +176,11 @@ lib_deps =
# renovate: datasource=custom.pio depName=Adafruit LTR390 Library packageName=adafruit/library/Adafruit LTR390 Library
adafruit/Adafruit LTR390 Library@1.1.2
# renovate: datasource=custom.pio depName=Adafruit PCT2075 packageName=adafruit/library/Adafruit PCT2075
adafruit/Adafruit PCT2075@1.0.6
adafruit/Adafruit PCT2075@1.0.5
# renovate: datasource=custom.pio depName=DFRobot_BMM150 packageName=dfrobot/library/DFRobot_BMM150
dfrobot/DFRobot_BMM150@1.0.0
# renovate: datasource=custom.pio depName=Adafruit_TSL2561 packageName=adafruit/library/Adafruit TSL2561
adafruit/Adafruit TSL2561@1.1.2
# renovate: datasource=custom.pio depName=BH1750_WE packageName=wollewald/BH1750_WE@^1.1.10
wollewald/BH1750_WE@^1.1.10
; (not included in native / portduino)
[environmental_extra]
@@ -213,6 +210,6 @@ lib_deps =
# renovate: datasource=git-refs depName=meshtastic-DFRobot_LarkWeatherStation packageName=https://github.com/meshtastic/DFRobot_LarkWeatherStation gitBranch=master
https://github.com/meshtastic/DFRobot_LarkWeatherStation/archive/4de3a9cadef0f6a5220a8a906cf9775b02b0040d.zip
# renovate: datasource=custom.pio depName=Sensirion Core packageName=sensirion/library/Sensirion Core
sensirion/Sensirion Core@0.7.2
sensirion/Sensirion Core@0.7.1
# renovate: datasource=custom.pio depName=Sensirion I2C SCD4x packageName=sensirion/library/Sensirion I2C SCD4x
sensirion/Sensirion I2C SCD4x@1.1.0

View File

@@ -50,11 +50,8 @@ class AudioThread : public concurrency::OSThread
delete i2sRtttl;
i2sRtttl = nullptr;
}
if (rtttlFile != nullptr) {
delete rtttlFile;
rtttlFile = nullptr;
}
delete rtttlFile;
rtttlFile = nullptr;
setCPUFast(false);
#ifdef T_LORA_PAGER
@@ -102,9 +99,9 @@ class AudioThread : public concurrency::OSThread
};
AudioGeneratorRTTTL *i2sRtttl = nullptr;
AudioOutputI2S *audioOut = nullptr;
AudioOutputI2S *audioOut;
AudioFileSourcePROGMEM *rtttlFile = nullptr;
AudioFileSourcePROGMEM *rtttlFile;
};
#endif

View File

@@ -14,16 +14,16 @@ class NodeStatus : public Status
CallbackObserver<NodeStatus, const NodeStatus *> statusObserver =
CallbackObserver<NodeStatus, const NodeStatus *>(this, &NodeStatus::updateStatus);
uint16_t numOnline = 0;
uint16_t numTotal = 0;
uint8_t numOnline = 0;
uint8_t numTotal = 0;
uint16_t lastNumTotal = 0;
uint8_t lastNumTotal = 0;
public:
bool forceUpdate = false;
NodeStatus() { statusType = STATUS_TYPE_NODE; }
NodeStatus(uint16_t numOnline, uint16_t numTotal, bool forceUpdate = false) : Status()
NodeStatus(uint8_t numOnline, uint8_t numTotal, bool forceUpdate = false) : Status()
{
this->forceUpdate = forceUpdate;
this->numOnline = numOnline;
@@ -34,11 +34,11 @@ class NodeStatus : public Status
void observe(Observable<const NodeStatus *> *source) { statusObserver.observe(source); }
uint16_t getNumOnline() const { return numOnline; }
uint8_t getNumOnline() const { return numOnline; }
uint16_t getNumTotal() const { return numTotal; }
uint8_t getNumTotal() const { return numTotal; }
uint16_t getLastNumTotal() const { return lastNumTotal; }
uint8_t getLastNumTotal() const { return lastNumTotal; }
bool matches(const NodeStatus *newStatus) const
{
@@ -56,7 +56,7 @@ class NodeStatus : public Status
numTotal = newStatus->getNumTotal();
}
if (isDirty || newStatus->forceUpdate) {
LOG_DEBUG("Node status update: %u online, %u total", numOnline, numTotal);
LOG_DEBUG("Node status update: %d online, %d total", numOnline, numTotal);
onNewStatus.notifyObservers(this);
}
return 0;

View File

@@ -194,7 +194,7 @@ static HasBatteryLevel *batteryLevel; // Default to NULL for no battery level se
#ifdef BATTERY_PIN
void battery_adcEnable()
static void adcEnable()
{
#ifdef ADC_CTRL // enable adc voltage divider when we need to read
#ifdef ADC_USE_PULLUP
@@ -214,7 +214,7 @@ void battery_adcEnable()
#endif
}
static void battery_adcDisable()
static void adcDisable()
{
#ifdef ADC_CTRL // disable adc voltage divider when we need to read
#ifdef ADC_USE_PULLUP
@@ -278,11 +278,6 @@ class AnalogBatteryLevel : public HasBatteryLevel
break;
}
}
#if defined(BATTERY_CHARGING_INV)
// bit of trickery to show 99% up until the charge finishes
if (!digitalRead(BATTERY_CHARGING_INV) && battery_SOC > 99)
battery_SOC = 99;
#endif
return clamp((int)(battery_SOC), 0, 100);
}
@@ -325,7 +320,7 @@ class AnalogBatteryLevel : public HasBatteryLevel
uint32_t raw = 0;
float scaled = 0;
battery_adcEnable();
adcEnable();
#ifdef ARCH_ESP32 // ADC block for espressif platforms
raw = espAdcRead();
scaled = esp_adc_cal_raw_to_voltage(raw, adc_characs);
@@ -337,7 +332,7 @@ class AnalogBatteryLevel : public HasBatteryLevel
raw = raw / BATTERY_SENSE_SAMPLES;
scaled = operativeAdcMultiplier * ((1000 * AREF_VOLTAGE) / pow(2, BATTERY_SENSE_RESOLUTION_BITS)) * raw;
#endif
battery_adcDisable();
adcDisable();
if (!initial_read_done) {
// Flush the smoothing filter with an ADC reading, if the reading is plausibly correct
@@ -460,8 +455,6 @@ class AnalogBatteryLevel : public HasBatteryLevel
}
// if it's not HIGH - check the battery
#endif
#elif defined(MUZI_BASE)
return NRF_POWER->USBREGSTATUS & POWER_USBREGSTATUS_VBUSDETECT_Msk;
#endif
return getBattVoltage() > chargingVolt;
}
@@ -477,8 +470,6 @@ class AnalogBatteryLevel : public HasBatteryLevel
#endif
#ifdef EXT_CHRG_DETECT
return digitalRead(EXT_CHRG_DETECT) == ext_chrg_detect_value;
#elif defined(BATTERY_CHARGING_INV)
return !digitalRead(BATTERY_CHARGING_INV);
#else
#if HAS_TELEMETRY && !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR && !defined(DISABLE_INA_CHARGING_DETECTION)
if (hasINA()) {
@@ -707,18 +698,11 @@ bool Power::setup()
[]() {
power->setIntervalFromNow(0);
runASAP = true;
BaseType_t higherWake = 0;
},
CHANGE);
#endif
#ifdef BATTERY_CHARGING_INV
attachInterrupt(
BATTERY_CHARGING_INV,
[]() {
power->setIntervalFromNow(0);
runASAP = true;
},
CHANGE);
#endif
enabled = found;
low_voltage_counter = 0;
@@ -775,8 +759,6 @@ void Power::shutdown()
if (screen) {
#ifdef T_DECK_PRO
screen->showSimpleBanner("Device is powered off.\nConnect USB to start!", 0); // T-Deck Pro has no power button
#elif defined(USE_EINK)
screen->showSimpleBanner("Shutting Down...", 2250); // dismiss after 3 seconds to avoid the banner on the sleep screen
#else
screen->showSimpleBanner("Shutting Down...", 0); // stays on screen
#endif
@@ -924,8 +906,13 @@ void Power::readPowerStatus()
low_voltage_counter++;
LOG_DEBUG("Low voltage counter: %d/10", low_voltage_counter);
if (low_voltage_counter > 10) {
#ifdef ARCH_NRF52
// We can't trigger deep sleep on NRF52, it's freezing the board
LOG_DEBUG("Low voltage detected, but not trigger deep sleep");
#else
LOG_INFO("Low voltage detected, trigger deep sleep");
powerFSM.trigger(EVENT_LOW_BATTERY);
#endif
}
} else {
low_voltage_counter = 0;
@@ -1453,7 +1440,7 @@ class LipoCharger : public HasBatteryLevel
/**
* return true if there is an external power source detected
*/
virtual bool isVbusIn() override { return PPM->isVbusIn(); }
virtual bool isVbusIn() override { return PPM->getVbusVoltage() > 0; }
/**
* return true if the battery is currently charging
@@ -1565,4 +1552,4 @@ bool Power::meshSolarInit()
{
return false;
}
#endif
#endif

View File

@@ -57,21 +57,21 @@ static bool isPowered()
static void sdsEnter()
{
LOG_POWERFSM("State: SDS");
LOG_DEBUG("State: SDS");
// FIXME - make sure GPS and LORA radio are off first - because we want close to zero current draw
doDeepSleep(Default::getConfiguredOrDefaultMs(config.power.sds_secs), false, false);
}
static void lowBattSDSEnter()
{
LOG_POWERFSM("State: Lower batt SDS");
LOG_DEBUG("State: Lower batt SDS");
doDeepSleep(Default::getConfiguredOrDefaultMs(config.power.sds_secs), false, true);
}
extern Power *power;
static void shutdownEnter()
{
LOG_POWERFSM("State: SHUTDOWN");
LOG_DEBUG("State: SHUTDOWN");
shutdownAtMsec = millis();
}
@@ -81,7 +81,7 @@ static uint32_t secsSlept;
static void lsEnter()
{
LOG_POWERFSM("lsEnter begin, ls_secs=%u", config.power.ls_secs);
LOG_INFO("lsEnter begin, ls_secs=%u", config.power.ls_secs);
if (screen)
screen->setOn(false);
secsSlept = 0; // How long have we been sleeping this time
@@ -155,12 +155,12 @@ static void lsIdle()
static void lsExit()
{
LOG_POWERFSM("State: lsExit");
LOG_INFO("Exit state: LS");
}
static void nbEnter()
{
LOG_POWERFSM("State: nbEnter");
LOG_DEBUG("State: NB");
if (screen)
screen->setOn(false);
#ifdef ARCH_ESP32
@@ -173,7 +173,6 @@ static void nbEnter()
static void darkEnter()
{
LOG_POWERFSM("State: darkEnter");
setBluetoothEnable(true);
if (screen)
screen->setOn(false);
@@ -181,7 +180,7 @@ static void darkEnter()
static void serialEnter()
{
LOG_POWERFSM("State: serialEnter");
LOG_DEBUG("State: SERIAL");
setBluetoothEnable(false);
if (screen) {
screen->setOn(true);
@@ -190,14 +189,13 @@ static void serialEnter()
static void serialExit()
{
LOG_POWERFSM("State: serialExit");
// Turn bluetooth back on when we leave serial stream API
setBluetoothEnable(true);
}
static void powerEnter()
{
LOG_POWERFSM("State: powerEnter");
// LOG_DEBUG("State: POWER");
if (!isPowered()) {
// If we got here, we are in the wrong state - we should be in powered, let that state handle things
LOG_INFO("Loss of power in Powered");
@@ -212,7 +210,6 @@ static void powerEnter()
static void powerIdle()
{
// LOG_POWERFSM("State: powerIdle"); // very chatty
if (!isPowered()) {
// If we got here, we are in the wrong state
LOG_INFO("Loss of power in Powered");
@@ -222,13 +219,14 @@ static void powerIdle()
static void powerExit()
{
LOG_POWERFSM("State: powerExit");
if (screen)
screen->setOn(true);
setBluetoothEnable(true);
}
static void onEnter()
{
LOG_POWERFSM("State: onEnter");
LOG_DEBUG("State: ON");
if (screen)
screen->setOn(true);
setBluetoothEnable(true);
@@ -236,7 +234,6 @@ static void onEnter()
static void onIdle()
{
LOG_POWERFSM("State: onIdle");
if (isPowered()) {
// If we got here, we are in the wrong state - we should be in powered, let that state handle things
powerFSM.trigger(EVENT_POWER_CONNECTED);
@@ -245,7 +242,7 @@ static void onIdle()
static void bootEnter()
{
LOG_POWERFSM("State: bootEnter");
LOG_DEBUG("State: BOOT");
}
State stateSHUTDOWN(shutdownEnter, NULL, NULL, "SHUTDOWN");
@@ -322,6 +319,11 @@ void PowerFSM_setup()
// if any packet destined for phone arrives, turn on bluetooth at least
powerFSM.add_transition(&stateNB, &stateDARK, EVENT_PACKET_FOR_PHONE, NULL, "Packet for phone");
// Removed 2.7: we don't show the nodes individually for every node on the screen anymore
// powerFSM.add_transition(&stateNB, &stateON, EVENT_NODEDB_UPDATED, NULL, "NodeDB update");
// powerFSM.add_transition(&stateDARK, &stateON, EVENT_NODEDB_UPDATED, NULL, "NodeDB update");
// powerFSM.add_transition(&stateON, &stateON, EVENT_NODEDB_UPDATED, NULL, "NodeDB update");
// Show the received text message
powerFSM.add_transition(&stateLS, &stateON, EVENT_RECEIVED_MSG, NULL, "Received text");
powerFSM.add_transition(&stateNB, &stateON, EVENT_RECEIVED_MSG, NULL, "Received text");
@@ -370,7 +372,7 @@ void PowerFSM_setup()
// Don't add power saving transitions if we are a power saving tracker or sensor or have Wifi enabled. Sleep will be initiated
// through the modules
#if HAS_WIFI && !defined(MESHTASTIC_EXCLUDE_WIFI)
#if HAS_WIFI || !defined(MESHTASTIC_EXCLUDE_WIFI)
bool isTrackerOrSensor = config.device.role == meshtastic_Config_DeviceConfig_Role_TRACKER ||
config.device.role == meshtastic_Config_DeviceConfig_Role_TAK_TRACKER ||
config.device.role == meshtastic_Config_DeviceConfig_Role_SENSOR;

View File

@@ -2,12 +2,6 @@
#include "configuration.h"
#ifdef PowerFSMDebug
#define LOG_POWERFSM(...) LOG_DEBUG(__VA_ARGS__)
#else
#define LOG_POWERFSM(...)
#endif
// See sw-design.md for documentation
#define EVENT_PRESS 1

View File

@@ -50,7 +50,6 @@ void consolePrintf(const char *format, ...)
SerialConsole::SerialConsole() : StreamAPI(&Port), RedirectablePrint(&Port), concurrency::OSThread("SerialConsole")
{
api_type = TYPE_SERIAL;
assert(!console);
console = this;
canWrite = false; // We don't send packets to our port until it has talked to us first

View File

@@ -36,29 +36,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
/* Offer chance for variant-specific defines */
#include "variant.h"
// -----------------------------------------------------------------------------
// Display feature overrides
// -----------------------------------------------------------------------------
// Allow build environments to opt-in explicitly to the E-Ink UI stack while
// keeping headless targets slim by default. Existing variants that already
// define USE_EINK continue to work without additional flags.
#ifndef MESHTASTIC_USE_EINK_UI
#ifdef USE_EINK
#define MESHTASTIC_USE_EINK_UI 1
#else
#define MESHTASTIC_USE_EINK_UI 0
#endif
#endif
#if MESHTASTIC_USE_EINK_UI
#ifndef USE_EINK
#define USE_EINK
#endif
#else
#undef USE_EINK
#endif
// -----------------------------------------------------------------------------
// Version
// -----------------------------------------------------------------------------
@@ -251,7 +228,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#define ICM20948_ADDR_ALT 0x68
#define BHI260AP_ADDR 0x28
#define BMM150_ADDR 0x13
#define DA217_ADDR 0x26
// -----------------------------------------------------------------------------
// LED
@@ -273,9 +249,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
// Touchscreen
// -----------------------------------------------------------------------------
#define FT6336U_ADDR 0x48
#define CST328_ADDR 0x1A // same address as CST226SE
#define CHSC6X_ADDR 0x2E
#define CST226SE_ADDR_ALT 0x5A
#define CST328_ADDR 0x1A
// -----------------------------------------------------------------------------
// RAK12035VB Soil Monitor (using RAK12023 up to 3 RAK12035 monitors can be connected)
@@ -394,9 +368,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#ifndef HAS_BLUETOOTH
#define HAS_BLUETOOTH 0
#endif
#ifndef USE_TFTDISPLAY
#define USE_TFTDISPLAY 0
#endif
#ifndef HW_VENDOR
#error HW_VENDOR must be defined
@@ -423,13 +394,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#define HAS_RGB_LED
#endif
#ifndef LED_STATE_OFF
#define LED_STATE_OFF 0
#endif
#ifndef LED_STATE_ON
#define LED_STATE_ON 1
#endif
// default mapping of pins
#if defined(PIN_BUTTON2) && !defined(CANCEL_BUTTON_PIN)
#define ALT_BUTTON_PIN PIN_BUTTON2

View File

@@ -82,11 +82,7 @@ class ScanI2C
BHI260AP,
BMM150,
TSL2561,
DRV2605,
BH1750,
DA217,
CHSC6X,
CST226SE
DRV2605
} DeviceType;
// typedef uint8_t DeviceAddress;

View File

@@ -106,7 +106,6 @@ uint16_t ScanI2CTwoWire::getRegisterValue(const ScanI2CTwoWire::RegisterLocation
if (i2cBus->available())
i2cBus->read();
}
LOG_DEBUG("Register value: 0x%x", value);
return value;
}
@@ -378,13 +377,14 @@ void ScanI2CTwoWire::scanPort(I2CPort port, uint8_t *address, uint8_t asize)
}
case SHT31_4x_ADDR: // same as OPT3001_ADDR_ALT
case SHT31_4x_ADDR_ALT: // same as OPT3001_ADDR
registerValue = getRegisterValue(ScanI2CTwoWire::RegisterLocation(addr, 0x7E), 2);
if (registerValue == 0x5449) {
type = OPT3001;
logFoundDevice("OPT3001", (uint8_t)addr.address);
} else if (getRegisterValue(ScanI2CTwoWire::RegisterLocation(addr, 0x89), 2) != 0) { // unique SHT4x serial number
registerValue = getRegisterValue(ScanI2CTwoWire::RegisterLocation(addr, 0x89), 2);
if (registerValue == 0x11a2 || registerValue == 0x11da || registerValue == 0x11f3 || registerValue == 0xe9c ||
registerValue == 0xc8d) {
type = SHT4X;
logFoundDevice("SHT4X", (uint8_t)addr.address);
} else if (getRegisterValue(ScanI2CTwoWire::RegisterLocation(addr, 0x7E), 2) == 0x5449) {
type = OPT3001;
logFoundDevice("OPT3001", (uint8_t)addr.address);
} else {
type = SHT31;
logFoundDevice("SHT31", (uint8_t)addr.address);
@@ -465,23 +465,8 @@ void ScanI2CTwoWire::scanPort(I2CPort port, uint8_t *address, uint8_t asize)
break;
SCAN_SIMPLE_CASE(LSM6DS3_ADDR, LSM6DS3, "LSM6DS3", (uint8_t)addr.address);
SCAN_SIMPLE_CASE(TCA9555_ADDR, TCA9555, "TCA9555", (uint8_t)addr.address);
SCAN_SIMPLE_CASE(VEML7700_ADDR, VEML7700, "VEML7700", (uint8_t)addr.address);
case TCA9555_ADDR:
registerValue = getRegisterValue(ScanI2CTwoWire::RegisterLocation(addr, 0x01), 1);
if (registerValue == 0x13) {
registerValue = getRegisterValue(ScanI2CTwoWire::RegisterLocation(addr, 0x00), 1);
if (registerValue == 0x81) {
type = DA217;
logFoundDevice("DA217", (uint8_t)addr.address);
} else {
type = TCA9555;
logFoundDevice("TCA9555", (uint8_t)addr.address);
}
} else {
type = TCA9555;
logFoundDevice("TCA9555", (uint8_t)addr.address);
}
break;
case TSL25911_ADDR:
registerValue = getRegisterValue(ScanI2CTwoWire::RegisterLocation(addr, 0x12), 1);
if (registerValue == 0x50) {
@@ -499,38 +484,8 @@ void ScanI2CTwoWire::scanPort(I2CPort port, uint8_t *address, uint8_t asize)
SCAN_SIMPLE_CASE(DFROBOT_RAIN_ADDR, DFROBOT_RAIN, "DFRobot Rain Gauge", (uint8_t)addr.address);
SCAN_SIMPLE_CASE(LTR390UV_ADDR, LTR390UV, "LTR390UV", (uint8_t)addr.address);
SCAN_SIMPLE_CASE(PCT2075_ADDR, PCT2075, "PCT2075", (uint8_t)addr.address);
case CST328_ADDR:
// Do we have the CST328 or the CST226SE
registerValue = getRegisterValue(ScanI2CTwoWire::RegisterLocation(addr, 0xAB), 1);
if (registerValue == 0xA9) {
type = CST226SE;
logFoundDevice("CST226SE", (uint8_t)addr.address);
} else {
type = CST328;
logFoundDevice("CST328", (uint8_t)addr.address);
}
break;
SCAN_SIMPLE_CASE(CHSC6X_ADDR, CHSC6X, "CHSC6X", (uint8_t)addr.address);
case LTR553ALS_ADDR:
registerValue = getRegisterValue(ScanI2CTwoWire::RegisterLocation(addr, 0x86), 1); // Part ID register
if (registerValue == 0x92) { // LTR553ALS Part ID
type = LTR553ALS;
logFoundDevice("LTR553ALS", (uint8_t)addr.address);
} else {
// Test BH1750 - send power on command
i2cBus->beginTransmission(addr.address);
i2cBus->write(0x01); // Power On command
uint8_t bh1750_error = i2cBus->endTransmission();
if (bh1750_error == 0) {
type = BH1750;
logFoundDevice("BH1750", (uint8_t)addr.address);
} else {
LOG_INFO("Device found at address 0x%x was not able to be enumerated", (uint8_t)addr.address);
}
}
break;
SCAN_SIMPLE_CASE(CST328_ADDR, CST328, "CST328", (uint8_t)addr.address);
SCAN_SIMPLE_CASE(LTR553ALS_ADDR, LTR553ALS, "LTR553ALS", (uint8_t)addr.address);
SCAN_SIMPLE_CASE(BHI260AP_ADDR, BHI260AP, "BHI260AP", (uint8_t)addr.address);
SCAN_SIMPLE_CASE(SCD4X_ADDR, SCD4X, "SCD4X", (uint8_t)addr.address);
SCAN_SIMPLE_CASE(BMM150_ADDR, BMM150, "BMM150", (uint8_t)addr.address);
@@ -539,12 +494,8 @@ void ScanI2CTwoWire::scanPort(I2CPort port, uint8_t *address, uint8_t asize)
#endif
case MLX90614_ADDR_DEF:
// Do we have the MLX90614 or the MPR121KB or the CST226SE
registerValue = getRegisterValue(ScanI2CTwoWire::RegisterLocation(addr, 0x06), 1);
if (registerValue == 0xAB) {
type = CST226SE;
logFoundDevice("CST226SE", (uint8_t)addr.address);
} else if (getRegisterValue(ScanI2CTwoWire::RegisterLocation(addr, 0x0e), 1) == 0x5a) {
registerValue = getRegisterValue(ScanI2CTwoWire::RegisterLocation(addr, 0x0e), 1);
if (registerValue == 0x5a) {
type = MLX90614;
logFoundDevice("MLX90614", (uint8_t)addr.address);
} else {
@@ -562,11 +513,6 @@ void ScanI2CTwoWire::scanPort(I2CPort port, uint8_t *address, uint8_t asize)
case ICM20948_ADDR: // same as BMX160_ADDR
case ICM20948_ADDR_ALT: // same as MPU6050_ADDR
registerValue = getRegisterValue(ScanI2CTwoWire::RegisterLocation(addr, 0x00), 1);
#ifdef HAS_ICM20948
type = ICM20948;
logFoundDevice("ICM20948", (uint8_t)addr.address);
break;
#endif
if (registerValue == 0xEA) {
type = ICM20948;
logFoundDevice("ICM20948", (uint8_t)addr.address);

View File

@@ -38,16 +38,14 @@ template <typename T, std::size_t N> std::size_t array_count(const T (&)[N])
return N;
}
#ifndef GPS_SERIAL_PORT
#define GPS_SERIAL_PORT Serial1
#endif
#if defined(ARCH_NRF52)
Uart *GPS::_serial_gps = &GPS_SERIAL_PORT;
#elif defined(ARCH_ESP32) || defined(ARCH_PORTDUINO) || defined(ARCH_STM32WL)
#if defined(NRF52840_XXAA) || defined(NRF52833_XXAA) || defined(ARCH_ESP32) || defined(ARCH_PORTDUINO) || defined(ARCH_STM32WL)
#if defined(GPS_SERIAL_PORT)
HardwareSerial *GPS::_serial_gps = &GPS_SERIAL_PORT;
#else
HardwareSerial *GPS::_serial_gps = &Serial1;
#endif
#elif defined(ARCH_RP2040)
SerialUART *GPS::_serial_gps = &GPS_SERIAL_PORT;
SerialUART *GPS::_serial_gps = &Serial1;
#else
HardwareSerial *GPS::_serial_gps = nullptr;
#endif
@@ -242,9 +240,6 @@ GPS_RESPONSE GPS::getACK(const char *message, uint32_t waitMillis)
buffer[bytesRead] = b;
bytesRead++;
if ((bytesRead == 767) || (b == '\r')) {
#ifdef GPS_DEBUG
LOG_DEBUG(debugmsg.c_str());
#endif
if (strnstr((char *)buffer, message, bytesRead) != nullptr) {
#ifdef GPS_DEBUG
LOG_DEBUG("Found: %s", message); // Log the found message
@@ -252,6 +247,9 @@ GPS_RESPONSE GPS::getACK(const char *message, uint32_t waitMillis)
return GNSS_RESPONSE_OK;
} else {
bytesRead = 0;
#ifdef GPS_DEBUG
LOG_DEBUG(debugmsg.c_str());
#endif
}
}
}
@@ -1277,24 +1275,6 @@ GnssModel_t GPS::probe(int serialSpeed)
memset(&ublox_info, 0, sizeof(ublox_info));
delay(100);
#if defined(PIN_GPS_RESET) && PIN_GPS_RESET != -1
digitalWrite(PIN_GPS_RESET, GPS_RESET_MODE); // assert for 10ms
delay(10);
digitalWrite(PIN_GPS_RESET, !GPS_RESET_MODE);
// attempt to detect the chip based on boot messages
std::vector<ChipInfo> passive_detect = {
{"AG3335", "$PAIR021,AG3335", GNSS_MODEL_AG3335},
{"AG3352", "$PAIR021,AG3352", GNSS_MODEL_AG3352},
{"RYS3520", "$PAIR021,REYAX_RYS3520_V2", GNSS_MODEL_AG3352},
{"UC6580", "UC6580", GNSS_MODEL_UC6580},
// as L76K is sort of a last ditch effort, we won't attempt to detect it by startup messages for now.
/*{"L76K", "SW=URANUS", GNSS_MODEL_MTK}*/};
GnssModel_t detectedDriver = getProbeResponse(500, passive_detect, serialSpeed);
if (detectedDriver != GNSS_MODEL_UNKNOWN) {
return detectedDriver;
}
#endif
// Close all NMEA sentences, valid for L76K, ATGM336H (and likely other AT6558 devices)
_serial_gps->write("$PCAS03,0,0,0,0,0,0,0,0,0,0,,,0,0*02\r\n");
delay(20);
@@ -1493,12 +1473,12 @@ GnssModel_t GPS::getProbeResponse(unsigned long timeout, const std::vector<ChipI
}
if (c == ',' || (responseLen >= 2 && response[responseLen - 2] == '\r' && response[responseLen - 1] == '\n')) {
#ifdef GPS_DEBUG
LOG_DEBUG(response);
#endif
// check if we can see our chips
for (const auto &chipInfo : responseMap) {
if (strstr(response, chipInfo.detectionString.c_str()) != nullptr) {
#ifdef GPS_DEBUG
LOG_DEBUG(response);
#endif
LOG_INFO("%s detected", chipInfo.chipName.c_str());
delete[] response; // Cleanup before return
return chipInfo.driver;
@@ -1506,9 +1486,6 @@ GnssModel_t GPS::getProbeResponse(unsigned long timeout, const std::vector<ChipI
}
}
if (responseLen >= 2 && response[responseLen - 2] == '\r' && response[responseLen - 1] == '\n') {
#ifdef GPS_DEBUG
LOG_DEBUG(response);
#endif
// Reset the response buffer for the next potential message
responseLen = 0;
response[0] = '\0';
@@ -1527,7 +1504,10 @@ GPS *GPS::createGps()
int8_t _rx_gpio = config.position.rx_gpio;
int8_t _tx_gpio = config.position.tx_gpio;
int8_t _en_gpio = config.position.gps_en_gpio;
#if HAS_GPS && !defined(ARCH_ESP32)
_rx_gpio = 1; // We only specify GPS serial ports on ESP32. Otherwise, these are just flags.
_tx_gpio = 1;
#endif
#if defined(GPS_RX_PIN)
if (!_rx_gpio)
_rx_gpio = GPS_RX_PIN;
@@ -1592,6 +1572,8 @@ GPS *GPS::createGps()
#ifdef PIN_GPS_RESET
pinMode(PIN_GPS_RESET, OUTPUT);
digitalWrite(PIN_GPS_RESET, GPS_RESET_MODE); // assert for 10ms
delay(10);
digitalWrite(PIN_GPS_RESET, !GPS_RESET_MODE);
#endif
@@ -1601,28 +1583,16 @@ GPS *GPS::createGps()
_serial_gps->setRxBufferSize(SERIAL_BUFFER_SIZE); // the default is 256
#endif
LOG_DEBUG("Use GPIO%d for GPS RX", new_gps->rx_gpio);
LOG_DEBUG("Use GPIO%d for GPS TX", new_gps->tx_gpio);
// ESP32 has a special set of parameters vs other arduino ports
#if defined(ARCH_ESP32)
LOG_DEBUG("Use GPIO%d for GPS RX", new_gps->rx_gpio);
LOG_DEBUG("Use GPIO%d for GPS TX", new_gps->tx_gpio);
_serial_gps->begin(GPS_BAUDRATE, SERIAL_8N1, new_gps->rx_gpio, new_gps->tx_gpio);
#elif defined(ARCH_RP2040)
_serial_gps->setPinout(new_gps->tx_gpio, new_gps->rx_gpio);
_serial_gps->setFIFOSize(256);
_serial_gps->begin(GPS_BAUDRATE);
#elif defined(ARCH_NRF52)
_serial_gps->setPins(new_gps->rx_gpio, new_gps->tx_gpio);
_serial_gps->begin(GPS_BAUDRATE);
#elif defined(ARCH_STM32WL)
_serial_gps->setTx(new_gps->tx_gpio);
_serial_gps->setRx(new_gps->rx_gpio);
_serial_gps->begin(GPS_BAUDRATE);
#elif defined(ARCH_PORTDUINO)
// Portduino can't set the GPS pins directly.
_serial_gps->begin(GPS_BAUDRATE);
#else
#error Unsupported architecture!
_serial_gps->begin(GPS_BAUDRATE);
#endif
}
return new_gps;

View File

@@ -194,8 +194,6 @@ class GPS : private concurrency::OSThread
/** If !NULL we will use this serial port to construct our GPS */
#if defined(ARCH_RP2040)
static SerialUART *_serial_gps;
#elif defined(ARCH_NRF52)
static Uart *_serial_gps;
#else
static HardwareSerial *_serial_gps;
#endif

View File

@@ -112,11 +112,7 @@ RTCSetResult readFromRTC()
#elif defined(RX8130CE_RTC)
if (rtc_found.address == RX8130CE_RTC) {
uint32_t now = millis();
#ifdef MUZI_BASE
ArtronShop_RX8130CE rtc(&Wire1);
#else
ArtronShop_RX8130CE rtc(&Wire);
#endif
tm t;
if (rtc.getTime(&t)) {
tv.tv_sec = gm_mktime(&t);
@@ -249,11 +245,7 @@ RTCSetResult perhapsSetRTC(RTCQuality q, const struct timeval *tv, bool forceUpd
}
#elif defined(RX8130CE_RTC)
if (rtc_found.address == RX8130CE_RTC) {
#ifdef MUZI_BASE
ArtronShop_RX8130CE rtc(&Wire1);
#else
ArtronShop_RX8130CE rtc(&Wire);
#endif
tm *t = gmtime(&tv->tv_sec);
if (rtc.setTime(*t)) {
LOG_DEBUG("RX8130CE setDateTime %02d-%02d-%02d %02d:%02d:%02d (%ld)", t->tm_year + 1900, t->tm_mon + 1,
@@ -318,7 +310,7 @@ RTCSetResult perhapsSetRTC(RTCQuality q, struct tm &t)
#ifdef BUILD_EPOCH
if (tv.tv_sec < BUILD_EPOCH) {
if (Throttle::isWithinTimespanMs(lastTimeValidationWarning, TIME_VALIDATION_WARNING_INTERVAL_MS) == false) {
LOG_WARN("Ignore time (%lu) before build epoch (%lu)!", printableEpoch, BUILD_EPOCH);
LOG_WARN("Ignore time (%ld) before build epoch (%ld)!", printableEpoch, BUILD_EPOCH);
lastTimeValidationWarning = millis();
}
return RTCSetResultInvalidTime;
@@ -327,7 +319,7 @@ RTCSetResult perhapsSetRTC(RTCQuality q, struct tm &t)
// Calculate max allowed time safely to avoid overflow in logging
uint64_t maxAllowedTime = (uint64_t)BUILD_EPOCH + FORTY_YEARS;
uint32_t maxAllowedPrintable = (maxAllowedTime > UINT32_MAX) ? UINT32_MAX : (uint32_t)maxAllowedTime;
LOG_WARN("Ignore time (%lu) too far in the future (build epoch: %lu, max allowed: %lu)!", printableEpoch,
LOG_WARN("Ignore time (%ld) too far in the future (build epoch: %ld, max allowed: %ld)!", printableEpoch,
(uint32_t)BUILD_EPOCH, maxAllowedPrintable);
lastTimeValidationWarning = millis();
}

View File

@@ -69,11 +69,7 @@ using graphics::Emote;
using graphics::emotes;
using graphics::numEmotes;
#if USE_TFTDISPLAY
extern uint16_t TFT_MESH;
#else
uint16_t TFT_MESH = COLOR565(0x67, 0xEA, 0x94);
#endif
#if HAS_WIFI && !defined(ARCH_PORTDUINO)
#include "mesh/wifi/WiFiAPClient.h"
@@ -328,7 +324,7 @@ static int8_t prevFrame = -1;
// Combined dynamic node list frame cycling through LastHeard, HopSignal, and Distance modes
// Uses a single frame and changes data every few seconds (E-Ink variant is separate)
#if defined(ESP_PLATFORM) && (defined(USE_ST7789) || defined(USE_ST7796))
#if defined(ESP_PLATFORM) && defined(USE_ST7789)
SPIClass SPI1(HSPI);
#endif
@@ -360,13 +356,7 @@ Screen::Screen(ScanI2C::DeviceAddress address, meshtastic_Config_DisplayConfig_O
#else
dispdev = new ST7789Spi(&SPI1, ST7789_RESET, ST7789_RS, ST7789_NSS, GEOMETRY_RAWMODE, TFT_WIDTH, TFT_HEIGHT);
#endif
#elif defined(USE_ST7796)
#ifdef ESP_PLATFORM
dispdev = new ST7796Spi(&SPI1, ST7796_RESET, ST7796_RS, ST7796_NSS, GEOMETRY_RAWMODE, TFT_WIDTH, TFT_HEIGHT, ST7796_SDA,
ST7796_MISO, ST7796_SCK, TFT_SPI_FREQUENCY);
#else
dispdev = new ST7796Spi(&SPI1, ST7796_RESET, ST7796_RS, ST7796_NSS, GEOMETRY_RAWMODE, TFT_WIDTH, TFT_HEIGHT);
#endif
static_cast<ST7789Spi *>(dispdev)->setRGB(TFT_MESH);
#elif defined(USE_SSD1306)
dispdev = new SSD1306Wire(address.address, -1, -1, geometry,
(address.port == ScanI2C::I2CPort::WIRE1) ? HW_I2C::I2C_TWO : HW_I2C::I2C_ONE);
@@ -379,7 +369,7 @@ Screen::Screen(ScanI2C::DeviceAddress address, meshtastic_Config_DisplayConfig_O
LOG_INFO("SSD1306 init success");
}
#elif defined(ST7735_CS) || defined(ILI9341_DRIVER) || defined(ILI9342_DRIVER) || defined(ST7701_CS) || defined(ST7789_CS) || \
defined(RAK14014) || defined(HX8357_CS) || defined(ILI9488_CS) || defined(ST7796_CS) || defined(HACKADAY_COMMUNICATOR)
defined(RAK14014) || defined(HX8357_CS) || defined(ILI9488_CS) || defined(ST7796_CS)
dispdev = new TFTDisplay(address.address, -1, -1, geometry,
(address.port == ScanI2C::I2CPort::WIRE1) ? HW_I2C::I2C_TWO : HW_I2C::I2C_ONE);
#elif defined(USE_EINK) && !defined(USE_EINK_DYNAMICDISPLAY)
@@ -409,12 +399,6 @@ Screen::Screen(ScanI2C::DeviceAddress address, meshtastic_Config_DisplayConfig_O
isAUTOOled = true;
#endif
#if defined(USE_ST7789)
static_cast<ST7789Spi *>(dispdev)->setRGB(TFT_MESH);
#elif defined(USE_ST7796)
static_cast<ST7796Spi *>(dispdev)->setRGB(TFT_MESH);
#endif
ui = new OLEDDisplayUi(dispdev);
cmdQueue.setReader(this);
}
@@ -451,14 +435,6 @@ void Screen::handleSetOn(bool on, FrameCallback einkScreensaver)
PMU->enablePowerOutput(XPOWERS_ALDO2);
#endif
#if defined(MUZI_BASE)
dispdev->init();
dispdev->setBrightness(brightness);
dispdev->flipScreenVertically();
dispdev->resetDisplay();
digitalWrite(SCREEN_12V_ENABLE, HIGH);
delay(100);
#endif
#if !ARCH_PORTDUINO
dispdev->displayOn();
#endif
@@ -490,15 +466,6 @@ void Screen::handleSetOn(bool on, FrameCallback einkScreensaver)
pinMode(VTFT_LEDA, OUTPUT);
digitalWrite(VTFT_LEDA, TFT_BACKLIGHT_ON);
#endif
#endif
#ifdef USE_ST7796
ui->init();
#ifdef ESP_PLATFORM
analogWrite(VTFT_LEDA, BRIGHTNESS_DEFAULT);
#else
pinMode(VTFT_LEDA, OUTPUT);
digitalWrite(VTFT_LEDA, TFT_BACKLIGHT_ON);
#endif
#endif
enabled = true;
setInterval(0); // Draw ASAP
@@ -517,10 +484,6 @@ void Screen::handleSetOn(bool on, FrameCallback einkScreensaver)
#endif
dispdev->displayOff();
#ifdef SCREEN_12V_ENABLE
digitalWrite(SCREEN_12V_ENABLE, LOW);
#endif
#ifdef USE_ST7789
SPI1.end();
#if defined(ARCH_ESP32)
@@ -537,21 +500,6 @@ void Screen::handleSetOn(bool on, FrameCallback einkScreensaver)
nrf_gpio_cfg_default(ST7789_NSS);
#endif
#endif
#ifdef USE_ST7796
SPI1.end();
#if defined(ARCH_ESP32)
pinMode(VTFT_LEDA, OUTPUT);
digitalWrite(VTFT_LEDA, LOW);
pinMode(ST7796_RESET, ANALOG);
pinMode(ST7796_RS, ANALOG);
pinMode(ST7796_NSS, ANALOG);
#else
nrf_gpio_cfg_default(VTFT_LEDA);
nrf_gpio_cfg_default(ST7796_RESET);
nrf_gpio_cfg_default(ST7796_RS);
nrf_gpio_cfg_default(ST7796_NSS);
#endif
#endif
#ifdef T_WATCH_S3
PMU->disablePowerOutput(XPOWERS_ALDO2);
@@ -586,7 +534,7 @@ void Screen::setup()
static_cast<AutoOLEDWire *>(dispdev)->setDetected(model);
#endif
#if defined(USE_SH1107_128_64) || defined(USE_SH1107)
#ifdef USE_SH1107_128_64
static_cast<SH1106Wire *>(dispdev)->setSubtype(7);
#endif
@@ -594,13 +542,6 @@ void Screen::setup()
// Apply custom RGB color (e.g. Heltec T114/T190)
static_cast<ST7789Spi *>(dispdev)->setRGB(TFT_MESH);
#endif
#if defined(MUZI_BASE)
dispdev->delayPoweron = true;
#endif
#if defined(USE_ST7796) && defined(TFT_MESH)
// Custom text color, if defined in variant.h
static_cast<ST7796Spi *>(dispdev)->setRGB(TFT_MESH);
#endif
// === Initialize display and UI system ===
ui->init();
@@ -660,12 +601,10 @@ void Screen::setup()
#else
if (!config.display.flip_screen) {
#if defined(ST7701_CS) || defined(ST7735_CS) || defined(ILI9341_DRIVER) || defined(ILI9342_DRIVER) || defined(ST7789_CS) || \
defined(RAK14014) || defined(HX8357_CS) || defined(ILI9488_CS) || defined(ST7796_CS) || defined(HACKADAY_COMMUNICATOR)
defined(RAK14014) || defined(HX8357_CS) || defined(ILI9488_CS) || defined(ST7796_CS)
static_cast<TFTDisplay *>(dispdev)->flipScreenVertically();
#elif defined(USE_ST7789)
static_cast<ST7789Spi *>(dispdev)->flipScreenVertically();
#elif defined(USE_ST7796)
static_cast<ST7796Spi *>(dispdev)->mirrorScreen();
#elif !defined(M5STACK_UNITC6L)
dispdev->flipScreenVertically();
#endif
@@ -698,7 +637,7 @@ void Screen::setup()
touchScreenImpl1->init();
}
}
#elif HAS_TOUCHSCREEN && !defined(USE_EINK) && !HAS_CST226SE
#elif HAS_TOUCHSCREEN && !defined(USE_EINK)
touchScreenImpl1 =
new TouchScreenImpl1(dispdev->getWidth(), dispdev->getHeight(), static_cast<TFTDisplay *>(dispdev)->getTouch);
touchScreenImpl1->init();
@@ -1610,7 +1549,6 @@ int Screen::handleUIFrameEvent(const UIFrameEvent *event)
int Screen::handleInputEvent(const InputEvent *event)
{
LOG_INPUT("Screen Input event %u! kb %u", event->inputEvent, event->kbchar);
if (!screenOn)
return 0;

View File

@@ -83,8 +83,6 @@ class Screen
#include <ST7789Spi.h>
#elif defined(USE_SPISSD1306)
#include <SSD1306Spi.h>
#elif defined(USE_ST7796)
#include <ST7796Spi.h>
#else
// the SH1106/SSD1306 variant is auto-detected
#include <AutoOLEDWire.h>
@@ -251,8 +249,6 @@ class Screen : public concurrency::OSThread
bool isOverlayBannerShowing();
bool isScreenOn() { return screenOn; }
// Stores the last 4 of our hardware ID, to make finding the device for pairing easier
// FIXME: Needs refactoring and getMacAddr needs to be moved to a utility class
char ourId[5];

View File

@@ -73,8 +73,7 @@
#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(ILI9488_CS) || defined(ST7796_CS) || \
defined(HACKADAY_COMMUNICATOR) || defined(USE_ST7796)) && \
defined(ST7789_CS) || defined(USE_ST7789) || defined(HX8357_CS) || defined(ILI9488_CS) || defined(ST7796_CS)) && \
!defined(DISPLAY_FORCE_SMALL_FONTS)
// The screen is bigger so use bigger fonts
#define FONT_SMALL FONT_MEDIUM_LOCAL // Height: 19

View File

@@ -1,8 +1,6 @@
#include "configuration.h"
#if HAS_SCREEN
#include "MeshService.h"
#include "RTC.h"
#include "draw/NodeListRenderer.h"
#include "graphics/ScreenFonts.h"
#include "graphics/SharedUIDisplay.h"
#include "graphics/draw/UIRenderer.h"
@@ -17,12 +15,6 @@ namespace graphics
void determineResolution(int16_t screenheight, int16_t screenwidth)
{
#ifdef FORCE_LOW_RES
isHighResolution = false;
return;
#endif
if (screenwidth > 128) {
isHighResolution = true;
}
@@ -30,6 +22,11 @@ void determineResolution(int16_t screenheight, int16_t screenwidth)
if (screenwidth > 128 && screenheight <= 64) {
isHighResolution = false;
}
// Special case for Heltec Wireless Tracker v1.1
if (screenwidth == 160 && screenheight == 80) {
isHighResolution = false;
}
}
// === Shared External State ===
@@ -401,43 +398,6 @@ const int *getTextPositions(OLEDDisplay *display)
return textPositions;
}
// *************************
// * Common Footer Drawing *
// *************************
void drawCommonFooter(OLEDDisplay *display, int16_t x, int16_t y)
{
bool drawConnectionState = false;
if (service->api_state == service->STATE_BLE || service->api_state == service->STATE_WIFI ||
service->api_state == service->STATE_SERIAL || service->api_state == service->STATE_PACKET ||
service->api_state == service->STATE_HTTP || service->api_state == service->STATE_ETH) {
drawConnectionState = true;
}
if (drawConnectionState) {
if (isHighResolution) {
const int scale = 2;
const int bytesPerRow = (connection_icon_width + 7) / 8;
int iconX = 0;
int iconY = SCREEN_HEIGHT - (connection_icon_height * 2);
for (int yy = 0; yy < connection_icon_height; ++yy) {
const uint8_t *rowPtr = connection_icon + yy * bytesPerRow;
for (int xx = 0; xx < connection_icon_width; ++xx) {
const uint8_t byteVal = pgm_read_byte(rowPtr + (xx >> 3));
const uint8_t bitMask = 1U << (xx & 7); // XBM is LSB-first
if (byteVal & bitMask) {
display->fillRect(iconX + xx * scale, iconY + yy * scale, scale, scale);
}
}
}
} else {
display->drawXbm(0, SCREEN_HEIGHT - connection_icon_height, connection_icon_width, connection_icon_height,
connection_icon);
}
}
}
bool isAllowedPunctuation(char c)
{
const std::string allowed = ".,!?;:-_()[]{}'\"@#$/\\&+=%~^ ";

View File

@@ -52,9 +52,6 @@ void drawRoundedHighlight(OLEDDisplay *display, int16_t x, int16_t y, int16_t w,
void drawCommonHeader(OLEDDisplay *display, int16_t x, int16_t y, const char *titleStr = "", bool force_no_invert = false,
bool show_date = false);
// Shared battery/time/mail header
void drawCommonFooter(OLEDDisplay *display, int16_t x, int16_t y);
const int *getTextPositions(OLEDDisplay *display);
bool isAllowedPunctuation(char c);

View File

@@ -1,6 +1,5 @@
#include "configuration.h"
#include "main.h"
#if USE_TFTDISPLAY
#if ARCH_PORTDUINO
#include "platform/portduino/PortduinoGlue.h"
@@ -124,11 +123,6 @@ static void rak14014_tpIntHandle(void)
_rak14014_touch_int = true;
}
#elif defined(HACKADAY_COMMUNICATOR)
#include <Arduino_GFX_Library.h>
Arduino_DataBus *bus = nullptr;
Arduino_GFX *tft = nullptr;
#elif defined(ST72xx_DE)
#include <LovyanGFX.hpp>
#include <TCA9534.h>
@@ -428,57 +422,7 @@ static LGFX *tft = nullptr;
#elif defined(ST7789_CS)
#include <LovyanGFX.hpp> // Graphics and font library for ST7735 driver chip
#ifdef HELTEC_V4_TFT
#include "chsc6x.h"
#include "lgfx/v1/Touch.hpp"
namespace lgfx
{
inline namespace v1
{
class TOUCH_CHSC6X : public ITouch
{
public:
TOUCH_CHSC6X(void)
{
_cfg.i2c_addr = TOUCH_SLAVE_ADDRESS;
_cfg.x_min = 0;
_cfg.x_max = 240;
_cfg.y_min = 0;
_cfg.y_max = 320;
};
bool init(void) override
{
if (chsc6xTouch == nullptr) {
chsc6xTouch = new chsc6x(&Wire1, TOUCH_SDA_PIN, TOUCH_SCL_PIN, TOUCH_INT_PIN, TOUCH_RST_PIN);
}
chsc6xTouch->chsc6x_init();
return true;
};
uint_fast8_t getTouchRaw(touch_point_t *tp, uint_fast8_t count) override
{
uint16_t raw_x, raw_y;
if (chsc6xTouch->chsc6x_read_touch_info(&raw_x, &raw_y) == 0) {
tp[0].x = 320 - 1 - raw_y;
tp[0].y = 240 - 1 - raw_x;
tp[0].size = 1;
tp[0].id = 1;
return 1;
}
tp[0].size = 0;
return 0;
};
void wakeup(void) override{};
void sleep(void) override{};
private:
chsc6x *chsc6xTouch = nullptr;
};
} // namespace v1
} // namespace lgfx
#endif
class LGFX : public lgfx::LGFX_Device
{
lgfx::Panel_ST7789 _panel_instance;
@@ -487,8 +431,6 @@ class LGFX : public lgfx::LGFX_Device
#if HAS_TOUCHSCREEN
#if defined(T_WATCH_S3) || defined(ELECROW)
lgfx::Touch_FT5x06 _touch_instance;
#elif defined(HELTEC_V4_TFT)
lgfx::TOUCH_CHSC6X _touch_instance;
#else
lgfx::Touch_GT911 _touch_instance;
#endif
@@ -522,9 +464,9 @@ class LGFX : public lgfx::LGFX_Device
{ // Set the display panel control.
auto cfg = _panel_instance.config(); // Gets a structure for display panel settings.
cfg.pin_cs = ST7789_CS; // Pin number where CS is connected (-1 = disable)
cfg.pin_rst = ST7789_RESET; // Pin number where RST is connected (-1 = disable)
cfg.pin_busy = ST7789_BUSY; // Pin number where BUSY is connected (-1 = disable)
cfg.pin_cs = ST7789_CS; // Pin number where CS is connected (-1 = disable)
cfg.pin_rst = -1; // Pin number where RST is connected (-1 = disable)
cfg.pin_busy = -1; // Pin number where BUSY is connected (-1 = disable)
// The following setting values are general initial values for each panel, so please comment out any
// unknown items and try them.
@@ -1139,6 +1081,9 @@ static LGFX *tft = nullptr;
#endif
#if defined(ST7701_CS) || defined(ST7735_CS) || defined(ST7789_CS) || defined(ST7796_CS) || defined(ILI9341_DRIVER) || \
defined(ILI9342_DRIVER) || defined(RAK14014) || defined(HX8357_CS) || defined(ILI9488_CS) || defined(ST72xx_DE) || \
(ARCH_PORTDUINO && HAS_SCREEN != 0)
#include "SPILock.h"
#include "TFTDisplay.h"
#include <SPI.h>
@@ -1274,15 +1219,12 @@ void TFTDisplay::display(bool fromBlank)
x_LastPixelUpdate = x;
}
}
#if defined(HACKADAY_COMMUNICATOR)
tft->draw16bitBeRGBBitmap(x_FirstPixelUpdate, y, &linePixelBuffer[x_FirstPixelUpdate],
(x_LastPixelUpdate - x_FirstPixelUpdate + 1), 1);
#else
// Step 4: Send the changed pixels on this line to the screen as a single block transfer.
// This function accepts pixel data MSB first so it can dump the memory straight out the SPI port.
tft->pushRect(x_FirstPixelUpdate, y, (x_LastPixelUpdate - x_FirstPixelUpdate + 1), 1,
&linePixelBuffer[x_FirstPixelUpdate]);
#endif
somethingChanged = true;
}
y++;
@@ -1346,8 +1288,6 @@ void TFTDisplay::sendCommand(uint8_t com)
display(true);
if (portduino_config.displayBacklight.pin > 0)
digitalWrite(portduino_config.displayBacklight.pin, TFT_BACKLIGHT_ON);
#elif defined(HACKADAY_COMMUNICATOR)
tft->displayOn();
#elif !defined(RAK14014) && !defined(M5STACK) && !defined(UNPHONE)
tft->wakeup();
tft->powerSaveOff();
@@ -1360,8 +1300,7 @@ void TFTDisplay::sendCommand(uint8_t com)
unphone.backlight(true); // using unPhone library
#endif
#ifdef RAK14014
#elif !defined(M5STACK) && !defined(ST7789_CS) && \
!defined(HACKADAY_COMMUNICATOR) // T-Deck gets brightness set in Screen.cpp in the handleSetOn function
#elif !defined(M5STACK) && !defined(ST7789_CS) // T-Deck gets brightness set in Screen.cpp in the handleSetOn function
tft->setBrightness(172);
#endif
break;
@@ -1373,8 +1312,6 @@ void TFTDisplay::sendCommand(uint8_t com)
tft->clear();
if (portduino_config.displayBacklight.pin > 0)
digitalWrite(portduino_config.displayBacklight.pin, !TFT_BACKLIGHT_ON);
#elif defined(HACKADAY_COMMUNICATOR)
tft->displayOff();
#elif !defined(RAK14014) && !defined(M5STACK) && !defined(UNPHONE)
tft->sleep();
tft->powerSaveOn();
@@ -1387,7 +1324,7 @@ void TFTDisplay::sendCommand(uint8_t com)
unphone.backlight(false); // using unPhone library
#endif
#ifdef RAK14014
#elif !defined(M5STACK) && !defined(HACKADAY_COMMUNICATOR)
#elif !defined(M5STACK)
tft->setBrightness(0);
#endif
break;
@@ -1403,7 +1340,7 @@ void TFTDisplay::setDisplayBrightness(uint8_t _brightness)
{
#ifdef RAK14014
// todo
#elif !defined(HACKADAY_COMMUNICATOR)
#else
tft->setBrightness(_brightness);
LOG_DEBUG("Brightness is set to value: %i ", _brightness);
#endif
@@ -1421,7 +1358,7 @@ bool TFTDisplay::hasTouch(void)
{
#ifdef RAK14014
return true;
#elif !defined(M5STACK) && !defined(HACKADAY_COMMUNICATOR)
#elif !defined(M5STACK)
return tft->touch() != nullptr;
#else
return false;
@@ -1440,7 +1377,7 @@ bool TFTDisplay::getTouch(int16_t *x, int16_t *y)
} else {
return false;
}
#elif !defined(M5STACK) && !defined(HACKADAY_COMMUNICATOR)
#elif !defined(M5STACK)
return tft->getTouch(x, y);
#else
return false;
@@ -1459,12 +1396,6 @@ bool TFTDisplay::connect()
LOG_INFO("Do TFT init");
#ifdef RAK14014
tft = new TFT_eSPI;
#elif defined(HACKADAY_COMMUNICATOR)
bus = new Arduino_ESP32SPI(TFT_DC, TFT_CS, 38 /* SCK */, 21 /* MOSI */, GFX_NOT_DEFINED /* MISO */, HSPI /* spi_num */);
tft = new Arduino_NV3007(bus, 40, 0 /* rotation */, false /* IPS */, 142 /* width */, 428 /* height */, 12 /* col offset 1 */,
0 /* row offset 1 */, 14 /* col offset 2 */, 0 /* row offset 2 */, nv3007_279_init_operations,
sizeof(nv3007_279_init_operations));
#else
tft = new LGFX;
#endif
@@ -1475,15 +1406,8 @@ bool TFTDisplay::connect()
#ifdef UNPHONE
unphone.backlight(true); // using unPhone library
#endif
#ifdef HACKADAY_COMMUNICATOR
bool beginStatus = tft->begin();
if (beginStatus)
LOG_DEBUG("TFT Success!");
else
LOG_ERROR("TFT Fail!");
#else
tft->init();
#endif
#if defined(M5STACK)
tft->setRotation(0);
@@ -1516,4 +1440,4 @@ bool TFTDisplay::connect()
return true;
}
#endif // USE_TFTDISPLAY
#endif

View File

@@ -101,23 +101,3 @@ void getTimeAgoStr(uint32_t agoSecs, char *timeStr, uint8_t maxLength)
else
snprintf(timeStr, maxLength, "unknown age");
}
void getUptimeStr(uint32_t uptimeMillis, const char *prefix, char *uptimeStr, uint8_t maxLength, bool includeSecs)
{
uint32_t days = uptimeMillis / 86400000;
uint32_t hours = (uptimeMillis % 86400000) / 3600000;
uint32_t mins = (uptimeMillis % 3600000) / 60000;
uint32_t secs = (uptimeMillis % 60000) / 1000;
if (days) {
snprintf(uptimeStr, maxLength, "%s: %ud %uh", prefix, days, hours);
} else if (hours) {
snprintf(uptimeStr, maxLength, "%s: %uh %um", prefix, hours, mins);
} else if (!includeSecs) {
snprintf(uptimeStr, maxLength, "%s: %um", prefix, mins);
} else if (mins) {
snprintf(uptimeStr, maxLength, "%s: %um %us", prefix, mins, secs);
} else {
snprintf(uptimeStr, maxLength, "%s: %us", prefix, secs);
}
}

View File

@@ -24,10 +24,3 @@ bool deltaToTimestamp(uint32_t secondsAgo, uint8_t *hours, uint8_t *minutes, int
* @param maxLength Maximum length of the resulting string buffer
*/
void getTimeAgoStr(uint32_t agoSecs, char *timeStr, uint8_t maxLength);
/**
* Get a compact human-readable string that only shows the largest non-zero time components.
* For example, 0 days 1 hour 2 minutes will display as "1h 2m" but 1 day 2 hours 3 minutes
* will display as "1d 2h".
*/
void getUptimeStr(uint32_t uptimeMillis, const char *prefix, char *uptimeStr, uint8_t maxLength, bool includeSecs = false);

View File

@@ -506,9 +506,6 @@ void VirtualKeyboard::drawKey(OLEDDisplay *display, const VirtualKey &key, bool
centeredTextY -= 1;
}
}
#ifdef MUZI_BASE // Correct issue with character vertical position on MUZI_BASE
centeredTextY -= 2;
#endif
display->drawString(textX, centeredTextY, keyText.c_str());
}

View File

@@ -194,12 +194,17 @@ void drawDigitalClockFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int1
graphics::drawCommonHeader(display, x, y, titleStr, true, true);
int line = 0;
#ifdef T_WATCH_S3
if (nimbleBluetooth && nimbleBluetooth->isConnected()) {
graphics::ClockRenderer::drawBluetoothConnectedIcon(display, display->getWidth() - 18, display->getHeight() - 14);
}
#endif
uint32_t rtc_sec = getValidTime(RTCQuality::RTCQualityDevice, true); // Display local timezone
char timeString[16];
int hour = 0;
int minute = 0;
int second = 0;
if (rtc_sec > 0) {
long hms = rtc_sec % SEC_PER_DAY;
hms = (hms + SEC_PER_DAY) % SEC_PER_DAY;
@@ -210,11 +215,11 @@ void drawDigitalClockFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int1
}
bool isPM = hour >= 12;
// hour = hour > 12 ? hour - 12 : hour;
if (config.display.use_12h_clock) {
hour %= 12;
if (hour == 0) {
if (hour == 0)
hour = 12;
}
snprintf(timeString, sizeof(timeString), "%d:%02d", hour, minute);
} else {
snprintf(timeString, sizeof(timeString), "%02d:%02d", hour, minute);
@@ -224,56 +229,24 @@ void drawDigitalClockFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int1
char secondString[8];
snprintf(secondString, sizeof(secondString), "%02d", second);
static bool scaleInitialized = false;
static float scale = 0.75f;
static float segmentWidth = SEGMENT_WIDTH * 0.75f;
static float segmentHeight = SEGMENT_HEIGHT * 0.75f;
if (!scaleInitialized) {
float screenwidth_target_ratio = 0.80f; // Target 80% of display width (adjustable)
float max_scale = 3.5f; // Safety limit to avoid runaway scaling
float step = 0.05f; // Step increment per iteration
float target_width = display->getWidth() * screenwidth_target_ratio;
float target_height =
display->getHeight() -
(isHighResolution
? 46
: 33); // Be careful adjusting this number, we have to account for header and the text under the time
float calculated_width_size = 0.0f;
float calculated_height_size = 0.0f;
while (true) {
segmentWidth = SEGMENT_WIDTH * scale;
segmentHeight = SEGMENT_HEIGHT * scale;
calculated_width_size = segmentHeight + ((segmentWidth + (segmentHeight * 2) + 4) * 4);
calculated_height_size = segmentHeight + ((segmentHeight + (segmentHeight * 2) + 4) * 2);
if (calculated_width_size >= target_width || calculated_height_size >= target_height || scale >= max_scale) {
break;
}
scale += step;
}
// If we overshot width, back off one step and recompute segment sizes
if (calculated_width_size > target_width || calculated_height_size > target_height) {
scale -= step;
segmentWidth = SEGMENT_WIDTH * scale;
segmentHeight = SEGMENT_HEIGHT * scale;
}
scaleInitialized = true;
#ifdef T_WATCH_S3
float scale = 1.5;
#elif defined(CHATTER_2)
float scale = 1.1;
#else
float scale = 0.75;
if (isHighResolution) {
scale = 1.5;
}
#endif
size_t len = strlen(timeString);
uint16_t segmentWidth = SEGMENT_WIDTH * scale;
uint16_t segmentHeight = SEGMENT_HEIGHT * scale;
// calculate hours:minutes string width
uint16_t timeStringWidth = len * 5; // base spacing between characters
uint16_t timeStringWidth = strlen(timeString) * 5;
for (size_t i = 0; i < len; i++) {
for (uint8_t i = 0; i < strlen(timeString); i++) {
char character = timeString[i];
if (character == ':') {
@@ -284,21 +257,19 @@ void drawDigitalClockFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int1
}
uint16_t hourMinuteTextX = (display->getWidth() / 2) - (timeStringWidth / 2);
uint16_t startingHourMinuteTextX = hourMinuteTextX;
uint16_t hourMinuteTextY = (display->getHeight() / 2) - (((segmentWidth * 2) + (segmentHeight * 3) + 8) / 2) + 2;
uint16_t hourMinuteTextY = (display->getHeight() / 2) - (((segmentWidth * 2) + (segmentHeight * 3) + 8) / 2);
// iterate over characters in hours:minutes string and draw segmented characters
for (size_t i = 0; i < len; i++) {
for (uint8_t i = 0; i < strlen(timeString); i++) {
char character = timeString[i];
if (character == ':') {
drawSegmentedDisplayColon(display, hourMinuteTextX, hourMinuteTextY, scale);
hourMinuteTextX += segmentHeight + 6;
if (scale >= 2.0f) {
hourMinuteTextX += (uint16_t)(4.5f * scale);
}
} else {
drawSegmentedDisplayCharacter(display, hourMinuteTextX, hourMinuteTextY, character - '0', scale);
@@ -308,27 +279,34 @@ void drawDigitalClockFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int1
hourMinuteTextX += 5;
}
// draw seconds string + AM/PM
// draw seconds string
display->setFont(FONT_SMALL);
int xOffset = (isHighResolution) ? 0 : -1;
if (hour >= 10) {
xOffset += (isHighResolution) ? 32 : 18;
}
int yOffset = (isHighResolution) ? 3 : 1;
#ifdef SENSECAP_INDICATOR
yOffset -= 3;
#endif
#ifdef T_DECK
yOffset -= 5;
#endif
if (config.display.use_12h_clock) {
display->drawString(startingHourMinuteTextX + xOffset, (display->getHeight() - hourMinuteTextY) - 1, isPM ? "pm" : "am");
display->drawString(startingHourMinuteTextX + xOffset, (display->getHeight() - hourMinuteTextY) - yOffset - 2,
isPM ? "pm" : "am");
}
#ifndef USE_EINK
xOffset = (isHighResolution) ? 18 : 10;
if (scale >= 2.0f) {
xOffset -= (int)(4.5f * scale);
}
display->drawString(startingHourMinuteTextX + timeStringWidth - xOffset, (display->getHeight() - hourMinuteTextY) - 1,
display->drawString(startingHourMinuteTextX + timeStringWidth - xOffset, (display->getHeight() - hourMinuteTextY) - yOffset,
secondString);
#endif
}
graphics::drawCommonFooter(display, x, y);
void drawBluetoothConnectedIcon(OLEDDisplay *display, int16_t x, int16_t y)
{
display->drawFastImage(x, y, 18, 14, bluetoothConnectedIcon);
}
// Draw an analog clock
@@ -341,6 +319,11 @@ void drawAnalogClockFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16
graphics::drawCommonHeader(display, x, y, titleStr, true, true);
int line = 0;
#ifdef T_WATCH_S3
if (nimbleBluetooth && nimbleBluetooth->isConnected()) {
drawBluetoothConnectedIcon(display, display->getWidth() - 18, display->getHeight() - 14);
}
#endif
// clock face center coordinates
int16_t centerX = display->getWidth() / 2;
int16_t centerY = display->getHeight() / 2;
@@ -533,7 +516,6 @@ void drawAnalogClockFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16
display->drawLine(centerX, centerY, secondX, secondY);
#endif
}
graphics::drawCommonFooter(display, x, y);
}
} // namespace ClockRenderer

View File

@@ -24,6 +24,7 @@ void drawVerticalSegment(OLEDDisplay *display, int x, int y, int width, int heig
// UI elements for clock displays
// void drawWatchFaceToggleButton(OLEDDisplay *display, int16_t x, int16_t y, bool digitalMode = true, float scale = 1);
void drawBluetoothConnectedIcon(OLEDDisplay *display, int16_t x, int16_t y);
} // namespace ClockRenderer

View File

@@ -3,7 +3,6 @@
#include "../Screen.h"
#include "DebugRenderer.h"
#include "FSCommon.h"
#include "MeshService.h"
#include "NodeDB.h"
#include "Throttle.h"
#include "UIRenderer.h"
@@ -11,7 +10,6 @@
#include "gps/RTC.h"
#include "graphics/ScreenFonts.h"
#include "graphics/SharedUIDisplay.h"
#include "graphics/TimeFormatters.h"
#include "graphics/images.h"
#include "main.h"
#include "mesh/Channels.h"
@@ -97,7 +95,7 @@ void drawFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16
(storeForwardModule->heartbeatInterval * 1200))) { // no heartbeat, overlap a bit
#if (defined(USE_EINK) || defined(ILI9341_DRIVER) || defined(ILI9342_DRIVER) || defined(ST7701_CS) || defined(ST7735_CS) || \
defined(ST7789_CS) || defined(USE_ST7789) || defined(ILI9488_CS) || defined(HX8357_CS) || defined(ST7796_CS) || \
defined(HACKADAY_COMMUNICATOR) || defined(USE_ST7796) || ARCH_PORTDUINO) && \
ARCH_PORTDUINO) && \
!defined(DISPLAY_FORCE_SMALL_FONTS)
display->drawFastImage(x + SCREEN_WIDTH - 14 - display->getStringWidth(screen->ourId), y + 3 + FONT_HEIGHT_SMALL, 12,
8, imgQuestionL1);
@@ -109,8 +107,7 @@ void drawFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16
#endif
} else {
#if (defined(USE_EINK) || defined(ILI9341_DRIVER) || defined(ILI9342_DRIVER) || defined(ST7701_CS) || defined(ST7735_CS) || \
defined(ST7789_CS) || defined(USE_ST7789) || defined(ILI9488_CS) || defined(HX8357_CS) || defined(ST7796_CS) || \
defined(HACKADAY_COMMUNICATOR) || defined(USE_ST7796)) && \
defined(ST7789_CS) || defined(USE_ST7789) || defined(ILI9488_CS) || defined(HX8357_CS) || defined(ST7796_CS)) && \
!defined(DISPLAY_FORCE_SMALL_FONTS)
display->drawFastImage(x + SCREEN_WIDTH - 18 - display->getStringWidth(screen->ourId), y + 3 + FONT_HEIGHT_SMALL, 16,
8, imgSFL1);
@@ -126,7 +123,7 @@ void drawFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16
// TODO: Raspberry Pi supports more than just the one screen size
#if (defined(USE_EINK) || defined(ILI9341_DRIVER) || defined(ILI9342_DRIVER) || defined(ST7701_CS) || defined(ST7735_CS) || \
defined(ST7789_CS) || defined(USE_ST7789) || defined(ILI9488_CS) || defined(HX8357_CS) || defined(ST7796_CS) || \
defined(HACKADAY_COMMUNICATOR) || defined(USE_ST7796) || ARCH_PORTDUINO) && \
ARCH_PORTDUINO) && \
!defined(DISPLAY_FORCE_SMALL_FONTS)
display->drawFastImage(x + SCREEN_WIDTH - 14 - display->getStringWidth(screen->ourId), y + 3 + FONT_HEIGHT_SMALL, 12, 8,
imgInfoL1);
@@ -226,8 +223,6 @@ void drawFrameWiFi(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, i
display->drawString(x, getTextPositions(display)[line++], "URL: http://meshtastic.local");
graphics::drawCommonFooter(display, x, y);
/* Display a heartbeat pixel that blinks every time the frame is redrawn */
#ifdef SHOW_REDRAWS
if (heartbeat)
@@ -508,7 +503,6 @@ void drawLoRaFocused(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x,
display->drawString(starting_position + chUtil_x + chutil_bar_width + extraoffset, getTextPositions(display)[line++],
chUtilPercentage);
#endif
graphics::drawCommonFooter(display, x, y);
}
// ****************************
@@ -648,48 +642,27 @@ void drawSystemScreen(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x
int textWidth = display->getStringWidth(appversionstr);
int nameX = (SCREEN_WIDTH - textWidth) / 2;
display->drawString(nameX, getTextPositions(display)[line++], appversionstr);
if (SCREEN_HEIGHT > 64 || (SCREEN_HEIGHT <= 64 && line <= 5)) { // Only show uptime if the screen can show it
display->drawString(nameX, getTextPositions(display)[line], appversionstr);
#if !defined(M5STACK_UNITC6L)
if (SCREEN_HEIGHT > 64 || (SCREEN_HEIGHT <= 64 && line < 4)) { // Only show uptime if the screen can show it
line += 1;
char uptimeStr[32] = "";
getUptimeStr(millis(), "Up", uptimeStr, sizeof(uptimeStr));
uint32_t uptime = millis() / 1000;
uint32_t days = uptime / 86400;
uint32_t hours = (uptime % 86400) / 3600;
uint32_t mins = (uptime % 3600) / 60;
// Show as "Up: 2d 3h", "Up: 5h 14m", or "Up: 37m"
if (days)
snprintf(uptimeStr, sizeof(uptimeStr), " Up: %ud %uh", days, hours);
else if (hours)
snprintf(uptimeStr, sizeof(uptimeStr), " Up: %uh %um", hours, mins);
else
snprintf(uptimeStr, sizeof(uptimeStr), " Uptime: %um", mins);
textWidth = display->getStringWidth(uptimeStr);
nameX = (SCREEN_WIDTH - textWidth) / 2;
display->drawString(nameX, getTextPositions(display)[line++], uptimeStr);
display->drawString(nameX, getTextPositions(display)[line], uptimeStr);
}
if (SCREEN_HEIGHT > 64 || (SCREEN_HEIGHT <= 64 && line <= 5)) { // Only show API state if the screen can show it
char api_state[32] = "";
const char *clientWord = nullptr;
// Determine if narrow or wide screen
if (isHighResolution) {
clientWord = "Client";
} else {
clientWord = "App";
}
snprintf(api_state, sizeof(api_state), "No %ss Connected", clientWord);
if (service->api_state == service->STATE_BLE) {
snprintf(api_state, sizeof(api_state), "%s Connected (BLE)", clientWord);
} else if (service->api_state == service->STATE_WIFI) {
snprintf(api_state, sizeof(api_state), "%s Connected (WiFi)", clientWord);
} else if (service->api_state == service->STATE_SERIAL) {
snprintf(api_state, sizeof(api_state), "%s Connected (Serial)", clientWord);
} else if (service->api_state == service->STATE_PACKET) {
snprintf(api_state, sizeof(api_state), "%s Connected (Internal)", clientWord);
} else if (service->api_state == service->STATE_HTTP) {
snprintf(api_state, sizeof(api_state), "%s Connected (HTTP)", clientWord);
} else if (service->api_state == service->STATE_ETH) {
snprintf(api_state, sizeof(api_state), "%s Connected (Ethernet)", clientWord);
}
if (api_state[0] != '\0') {
display->drawString((SCREEN_WIDTH - display->getStringWidth(api_state)) / 2, getTextPositions(display)[line++],
api_state);
}
}
graphics::drawCommonFooter(display, x, y);
#endif
}
// ****************************
@@ -721,4 +694,4 @@ void drawChirpy(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int1
} // namespace DebugRenderer
} // namespace graphics
#endif
#endif

View File

@@ -119,7 +119,6 @@ void menuHandler::LoraRegionPicker(uint32_t duration)
auto changes = SEGMENT_CONFIG;
// This is needed as we wait til picking the LoRa region to generate keys for the first time.
#if !(MESHTASTIC_EXCLUDE_PKI_KEYGEN || MESHTASTIC_EXCLUDE_PKI)
if (!owner.is_licensed) {
bool keygenSuccess = false;
if (config.security.private_key.size == 32) {
@@ -140,7 +139,6 @@ void menuHandler::LoraRegionPicker(uint32_t duration)
memcpy(owner.public_key.bytes, config.security.public_key.bytes, 32);
}
}
#endif
config.lora.tx_enabled = true;
initRegion();
if (myRegion->dutyCycle < 100) {
@@ -576,26 +574,27 @@ void menuHandler::textMessageBaseMenu()
void menuHandler::systemBaseMenu()
{
enum optionsNumbers { Back, Notifications, ScreenOptions, Bluetooth, WiFiToggle, PowerMenu, Test, enumEnd };
enum optionsNumbers { Back, Notifications, ScreenOptions, Bluetooth, PowerMenu, FrameToggles, Test, enumEnd };
static const char *optionsArray[enumEnd] = {"Back"};
static int optionsEnumArray[enumEnd] = {Back};
int options = 1;
optionsArray[options] = "Notifications";
optionsEnumArray[options++] = Notifications;
optionsArray[options] = "Display Options";
#if defined(ST7789_CS) || defined(ST7796_CS) || defined(USE_OLED) || defined(USE_SSD1306) || defined(USE_SH1106) || \
defined(USE_SH1107) || defined(HELTEC_MESH_NODE_T114) || defined(HELTEC_VISION_MASTER_T190) || HAS_TFT
optionsArray[options] = "Screen Options";
optionsEnumArray[options++] = ScreenOptions;
#endif
optionsArray[options] = "Frame Visiblity Toggle";
optionsEnumArray[options++] = FrameToggles;
#if defined(M5STACK_UNITC6L)
optionsArray[options] = "Bluetooth";
#else
optionsArray[options] = "Bluetooth Toggle";
#endif
optionsEnumArray[options++] = Bluetooth;
#if HAS_WIFI && !defined(ARCH_PORTDUINO)
optionsArray[options] = "WiFi Toggle";
optionsEnumArray[options++] = WiFiToggle;
#endif
#if defined(M5STACK_UNITC6L)
optionsArray[options] = "Power";
#else
@@ -627,17 +626,15 @@ void menuHandler::systemBaseMenu()
} else if (selected == PowerMenu) {
menuHandler::menuQueue = menuHandler::power_menu;
screen->runNow();
} else if (selected == FrameToggles) {
menuHandler::menuQueue = menuHandler::FrameToggles;
screen->runNow();
} else if (selected == Test) {
menuHandler::menuQueue = menuHandler::test_menu;
screen->runNow();
} else if (selected == Bluetooth) {
menuQueue = bluetooth_toggle_menu;
screen->runNow();
#if HAS_WIFI && !defined(ARCH_PORTDUINO)
} else if (selected == WiFiToggle) {
menuQueue = wifi_toggle_menu;
screen->runNow();
#endif
} else if (selected == Back && !test_enabled) {
test_count++;
if (test_count > 4) {
@@ -793,24 +790,17 @@ void menuHandler::nodeNameLengthMenu()
void menuHandler::resetNodeDBMenu()
{
static const char *optionsArray[] = {"Back", "Reset All", "Preserve Favorites"};
static const char *optionsArray[] = {"Back", "Confirm"};
BannerOverlayOptions bannerOptions;
bannerOptions.message = "Confirm Reset NodeDB";
bannerOptions.optionsArrayPtr = optionsArray;
bannerOptions.optionsCount = 3;
bannerOptions.optionsCount = 2;
bannerOptions.bannerCallback = [](int selected) -> void {
if (selected == 1 || selected == 2) {
disableBluetooth();
screen->setFrames(Screen::FOCUS_DEFAULT);
}
if (selected == 1) {
disableBluetooth();
LOG_INFO("Initiate node-db reset");
nodeDB->resetNodes();
rebootAtMsec = (millis() + DEFAULT_REBOOT_SECONDS * 1000);
} else if (selected == 2) {
LOG_INFO("Initiate node-db reset but keeping favorites");
nodeDB->resetNodes(1);
rebootAtMsec = (millis() + DEFAULT_REBOOT_SECONDS * 1000);
}
};
screen->showOverlayBanner(bannerOptions);
@@ -946,9 +936,7 @@ void menuHandler::BluetoothToggleMenu()
bannerOptions.optionsArrayPtr = optionsArray;
bannerOptions.optionsCount = 3;
bannerOptions.bannerCallback = [](int selected) -> void {
if (selected == 0)
return;
else if (selected != (config.bluetooth.enabled ? 1 : 2)) {
if (selected == 1 || selected == 2) {
InputEvent event = {.inputEvent = (input_broker_event)170, .kbchar = 170, .touchX = 0, .touchY = 0};
inputBroker->injectInputEvent(&event);
}
@@ -1047,8 +1035,7 @@ void menuHandler::TFTColorPickerMenu(OLEDDisplay *display)
bannerOptions.optionsArrayPtr = optionsArray;
bannerOptions.optionsCount = 10;
bannerOptions.bannerCallback = [display](int selected) -> void {
#if defined(HELTEC_MESH_NODE_T114) || defined(HELTEC_VISION_MASTER_T190) || defined(T_DECK) || defined(T_LORA_PAGER) || \
HAS_TFT || defined(HACKADAY_COMMUNICATOR)
#if defined(HELTEC_MESH_NODE_T114) || defined(HELTEC_VISION_MASTER_T190) || defined(T_DECK) || defined(T_LORA_PAGER) || HAS_TFT
uint8_t TFT_MESH_r = 0;
uint8_t TFT_MESH_g = 0;
uint8_t TFT_MESH_b = 0;
@@ -1288,28 +1275,19 @@ void menuHandler::wifiBaseMenu()
void menuHandler::wifiToggleMenu()
{
enum optionsNumbers { Back, Wifi_disable, Wifi_enable };
enum optionsNumbers { Back, Wifi_toggle };
static const char *optionsArray[] = {"Back", "WiFi Disabled", "WiFi Enabled"};
static const char *optionsArray[] = {"Back", "Disable"};
BannerOverlayOptions bannerOptions;
bannerOptions.message = "WiFi Actions";
bannerOptions.message = "Disable Wifi and\nEnable Bluetooth?";
bannerOptions.optionsArrayPtr = optionsArray;
bannerOptions.optionsCount = 3;
if (config.network.wifi_enabled == true)
bannerOptions.InitialSelected = 2;
else
bannerOptions.InitialSelected = 1;
bannerOptions.optionsCount = 2;
bannerOptions.bannerCallback = [](int selected) -> void {
if (selected == Wifi_disable) {
if (selected == Wifi_toggle) {
config.network.wifi_enabled = false;
config.bluetooth.enabled = true;
service->reloadConfig(SEGMENT_CONFIG);
rebootAtMsec = (millis() + DEFAULT_REBOOT_SECONDS * 1000);
} else if (selected == Wifi_enable) {
config.network.wifi_enabled = true;
config.bluetooth.enabled = false;
service->reloadConfig(SEGMENT_CONFIG);
rebootAtMsec = (millis() + DEFAULT_REBOOT_SECONDS * 1000);
}
};
screen->showOverlayBanner(bannerOptions);
@@ -1352,12 +1330,12 @@ void menuHandler::screenOptionsMenu()
hasSupportBrightness = false;
#endif
enum optionsNumbers { Back, NodeNameLength, Brightness, ScreenColor, FrameToggles, DisplayUnits };
enum optionsNumbers { Back, NodeNameLength, Brightness, ScreenColor };
static const char *optionsArray[5] = {"Back"};
static int optionsEnumArray[5] = {Back};
int options = 1;
#if defined(T_DECK) || defined(T_LORA_PAGER) || defined(HACKADAY_COMMUNICATOR)
#if defined(T_DECK) || defined(T_LORA_PAGER)
optionsArray[options] = "Show Long/Short Name";
optionsEnumArray[options++] = NodeNameLength;
#endif
@@ -1369,20 +1347,13 @@ void menuHandler::screenOptionsMenu()
}
// Only show screen color for TFT displays
#if defined(HELTEC_MESH_NODE_T114) || defined(HELTEC_VISION_MASTER_T190) || defined(T_DECK) || defined(T_LORA_PAGER) || \
HAS_TFT || defined(HACKADAY_COMMUNICATOR)
#if defined(HELTEC_MESH_NODE_T114) || defined(HELTEC_VISION_MASTER_T190) || defined(T_DECK) || defined(T_LORA_PAGER) || HAS_TFT
optionsArray[options] = "Screen Color";
optionsEnumArray[options++] = ScreenColor;
#endif
optionsArray[options] = "Frame Visibility Toggle";
optionsEnumArray[options++] = FrameToggles;
optionsArray[options] = "Display Units";
optionsEnumArray[options++] = DisplayUnits;
BannerOverlayOptions bannerOptions;
bannerOptions.message = "Display Options";
bannerOptions.message = "Screen Options";
bannerOptions.optionsArrayPtr = optionsArray;
bannerOptions.optionsCount = options;
bannerOptions.optionsEnumPtr = optionsEnumArray;
@@ -1396,12 +1367,6 @@ void menuHandler::screenOptionsMenu()
} else if (selected == NodeNameLength) {
menuHandler::menuQueue = menuHandler::node_name_length_menu;
screen->runNow();
} else if (selected == FrameToggles) {
menuHandler::menuQueue = menuHandler::FrameToggles;
screen->runNow();
} else if (selected == DisplayUnits) {
menuHandler::menuQueue = menuHandler::DisplayUnits;
screen->runNow();
} else {
menuQueue = system_base_menu;
screen->runNow();
@@ -1613,34 +1578,6 @@ void menuHandler::FrameToggles_menu()
screen->showOverlayBanner(bannerOptions);
}
void menuHandler::DisplayUnits_menu()
{
enum optionsNumbers { Back, MetricUnits, ImperialUnits };
static const char *optionsArray[] = {"Back", "Metric", "Imperial"};
BannerOverlayOptions bannerOptions;
bannerOptions.message = " Select display units";
bannerOptions.optionsArrayPtr = optionsArray;
bannerOptions.optionsCount = 3;
if (config.display.units == meshtastic_Config_DisplayConfig_DisplayUnits_IMPERIAL)
bannerOptions.InitialSelected = 2;
else
bannerOptions.InitialSelected = 1;
bannerOptions.bannerCallback = [](int selected) -> void {
if (selected == MetricUnits) {
config.display.units = meshtastic_Config_DisplayConfig_DisplayUnits_METRIC;
service->reloadConfig(SEGMENT_CONFIG);
} else if (selected == ImperialUnits) {
config.display.units = meshtastic_Config_DisplayConfig_DisplayUnits_IMPERIAL;
service->reloadConfig(SEGMENT_CONFIG);
} else {
menuHandler::menuQueue = menuHandler::screen_options_menu;
screen->runNow();
}
};
screen->showOverlayBanner(bannerOptions);
}
void menuHandler::handleMenuSwitch(OLEDDisplay *display)
{
if (menuQueue != menu_none)
@@ -1755,9 +1692,6 @@ void menuHandler::handleMenuSwitch(OLEDDisplay *display)
case FrameToggles:
FrameToggles_menu();
break;
case DisplayUnits:
DisplayUnits_menu();
break;
case throttle_message:
screen->showSimpleBanner("Too Many Attempts\nTry again in 60 seconds.", 5000);
break;
@@ -1772,4 +1706,4 @@ void menuHandler::saveUIConfig()
} // namespace graphics
#endif
#endif

View File

@@ -44,8 +44,7 @@ class menuHandler
trace_route_menu,
throttle_message,
node_name_length_menu,
FrameToggles,
DisplayUnits
FrameToggles
};
static screenMenus menuQueue;
@@ -89,7 +88,6 @@ class menuHandler
static void powerMenu();
static void nodeNameLengthMenu();
static void FrameToggles_menu();
static void DisplayUnits_menu();
static void textMessageMenu();
private:

View File

@@ -213,7 +213,6 @@ void drawTextMessageFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16
#else
display->drawString(center_text, getTextPositions(display)[2], messageString);
#endif
graphics::drawCommonFooter(display, x, y);
return;
}
@@ -424,7 +423,6 @@ void drawTextMessageFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16
// Draw header at the end to sort out overlapping elements
graphics::drawCommonHeader(display, x, y, titleStr);
#endif
graphics::drawCommonFooter(display, x, y);
}
std::vector<std::string> generateLines(OLEDDisplay *display, const char *headerStr, const char *messageBuf, int textWidth)

View File

@@ -425,12 +425,6 @@ void drawNodeListScreen(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t
{
const int COMMON_HEADER_HEIGHT = FONT_HEIGHT_SMALL - 1;
const int rowYOffset = FONT_HEIGHT_SMALL - 3;
bool locationScreen = false;
if (strcmp(title, "Bearings") == 0)
locationScreen = true;
else if (strcmp(title, "Distance") == 0)
locationScreen = true;
#if defined(M5STACK_UNITC6L)
int columnWidth = display->getWidth();
#else
@@ -446,7 +440,7 @@ void drawNodeListScreen(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t
int totalEntries = nodeDB->getNumMeshNodes();
int totalRowsAvailable = (display->getHeight() - y) / rowYOffset;
int numskipped = 0;
int visibleNodeRows = totalRowsAvailable;
#if defined(M5STACK_UNITC6L)
int totalColumns = 1;
@@ -466,10 +460,6 @@ void drawNodeListScreen(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t
int rowCount = 0;
for (int i = startIndex; i < endIndex; ++i) {
if (locationScreen && !nodeDB->getMeshNodeByIndex(i)->has_position) {
numskipped++;
continue;
}
int xPos = x + (col * columnWidth);
int yPos = y + yOffset;
renderer(display, nodeDB->getMeshNodeByIndex(i), xPos, yPos, columnWidth);
@@ -492,9 +482,6 @@ void drawNodeListScreen(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t
}
}
// This should correct the scrollbar
totalEntries -= numskipped;
#if !defined(M5STACK_UNITC6L)
// Draw column separator
if (shownCount > 0) {
@@ -505,7 +492,6 @@ void drawNodeListScreen(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t
#endif
const int scrollStartY = y + 3;
drawScrollbar(display, visibleNodeRows, totalEntries, scrollIndex, 2, scrollStartY);
graphics::drawCommonFooter(display, x, y);
}
// =============================

View File

@@ -11,7 +11,6 @@
#include "graphics/Screen.h"
#include "graphics/ScreenFonts.h"
#include "graphics/SharedUIDisplay.h"
#include "graphics/TimeFormatters.h"
#include "graphics/images.h"
#include "main.h"
#include "target_specific.h"
@@ -257,8 +256,7 @@ void UIRenderer::drawNodes(OLEDDisplay *display, int16_t x, int16_t y, const mes
}
#if (defined(USE_EINK) || defined(ILI9341_DRIVER) || defined(ILI9342_DRIVER) || defined(ST7701_CS) || defined(ST7735_CS) || \
defined(ST7789_CS) || defined(USE_ST7789) || defined(ILI9488_CS) || defined(HX8357_CS) || defined(ST7796_CS) || \
defined(HACKADAY_COMMUNICATOR) || defined(USE_ST7796)) && \
defined(ST7789_CS) || defined(USE_ST7789) || defined(ILI9488_CS) || defined(HX8357_CS) || defined(ST7796_CS)) && \
!defined(DISPLAY_FORCE_SMALL_FONTS)
if (isHighResolution) {
@@ -385,7 +383,17 @@ void UIRenderer::drawNodeInfo(OLEDDisplay *display, const OLEDDisplayUiState *st
// === 4. Uptime (only show if metric is present) ===
char uptimeStr[32] = "";
if (node->has_device_metrics && node->device_metrics.has_uptime_seconds) {
getUptimeStr(node->device_metrics.uptime_seconds * 1000, " Up", uptimeStr, sizeof(uptimeStr));
uint32_t uptime = node->device_metrics.uptime_seconds;
uint32_t days = uptime / 86400;
uint32_t hours = (uptime % 86400) / 3600;
uint32_t mins = (uptime % 3600) / 60;
// Show as "Up: 2d 3h", "Up: 5h 14m", or "Up: 37m"
if (days)
snprintf(uptimeStr, sizeof(uptimeStr), " Uptime: %ud %uh", days, hours);
else if (hours)
snprintf(uptimeStr, sizeof(uptimeStr), " Uptime: %uh %um", hours, mins);
else
snprintf(uptimeStr, sizeof(uptimeStr), " Uptime: %um", mins);
}
if (uptimeStr[0] && line < 5) {
display->drawString(x, getTextPositions(display)[line++], uptimeStr);
@@ -544,7 +552,6 @@ void UIRenderer::drawNodeInfo(OLEDDisplay *display, const OLEDDisplayUiState *st
// else show nothing
}
#endif
graphics::drawCommonFooter(display, x, y);
}
// ****************************
@@ -584,8 +591,18 @@ void UIRenderer::drawDeviceFocused(OLEDDisplay *display, OLEDDisplayUiState *sta
drawNodes(display, x + 1, getTextPositions(display)[line] + 2, nodeStatus, -1, false, "online");
#endif
char uptimeStr[32] = "";
uint32_t uptime = millis() / 1000;
uint32_t days = uptime / 86400;
uint32_t hours = (uptime % 86400) / 3600;
uint32_t mins = (uptime % 3600) / 60;
// Show as "Up: 2d 3h", "Up: 5h 14m", or "Up: 37m"
#if !defined(M5STACK_UNITC6L)
getUptimeStr(millis(), "Up", uptimeStr, sizeof(uptimeStr));
if (days)
snprintf(uptimeStr, sizeof(uptimeStr), "Up: %ud %uh", days, hours);
else if (hours)
snprintf(uptimeStr, sizeof(uptimeStr), "Up: %uh %um", hours, mins);
else
snprintf(uptimeStr, sizeof(uptimeStr), "Up: %um", mins);
#endif
display->drawString(SCREEN_WIDTH - display->getStringWidth(uptimeStr), getTextPositions(display)[line++], uptimeStr);
@@ -754,7 +771,6 @@ void UIRenderer::drawDeviceFocused(OLEDDisplay *display, OLEDDisplayUiState *sta
display->drawString(nameX, getTextPositions(display)[line++], shortnameble);
}
#endif
graphics::drawCommonFooter(display, x, y);
}
// Start Functions to write date/time to the screen
@@ -1030,17 +1046,36 @@ void UIRenderer::drawCompassAndLocationScreen(OLEDDisplay *display, OLEDDisplayU
if (strcmp(displayLine, "GPS off") != 0 && strcmp(displayLine, "No GPS") != 0) {
// === Second Row: Last GPS Fix ===
if (gpsStatus->getLastFixMillis() > 0) {
uint32_t delta = millis() - gpsStatus->getLastFixMillis();
char uptimeStr[32];
uint32_t delta = (millis() - gpsStatus->getLastFixMillis()) / 1000; // seconds since last fix
uint32_t days = delta / 86400;
uint32_t hours = (delta % 86400) / 3600;
uint32_t mins = (delta % 3600) / 60;
uint32_t secs = delta % 60;
char buf[32];
#if defined(USE_EINK)
// E-Ink: skip seconds, show only days/hours/mins
getUptimeStr(delta, "Last", uptimeStr, sizeof(uptimeStr), false);
if (days > 0) {
snprintf(buf, sizeof(buf), "Last: %ud %uh", days, hours);
} else if (hours > 0) {
snprintf(buf, sizeof(buf), "Last: %uh %um", hours, mins);
} else {
snprintf(buf, sizeof(buf), "Last: %um", mins);
}
#else
// Non E-Ink: include seconds where useful
getUptimeStr(delta, "Last", uptimeStr, sizeof(uptimeStr), true);
if (days > 0) {
snprintf(buf, sizeof(buf), "Last: %ud %uh", days, hours);
} else if (hours > 0) {
snprintf(buf, sizeof(buf), "Last: %uh %um", hours, mins);
} else if (mins > 0) {
snprintf(buf, sizeof(buf), "Last: %um %us", mins, secs);
} else {
snprintf(buf, sizeof(buf), "Last: %us", secs);
}
#endif
display->drawString(0, getTextPositions(display)[line++], uptimeStr);
display->drawString(0, getTextPositions(display)[line++], buf);
} else {
display->drawString(0, getTextPositions(display)[line++], "Last: ?");
}
@@ -1148,7 +1183,6 @@ void UIRenderer::drawCompassAndLocationScreen(OLEDDisplay *display, OLEDDisplayU
}
#endif
#endif // HAS_GPS
graphics::drawCommonFooter(display, x, y);
}
#ifdef USERPREFS_OEM_TEXT
@@ -1233,13 +1267,7 @@ void UIRenderer::drawNavigationBar(OLEDDisplay *display, OLEDDisplayUiState *sta
if (totalIcons == 0)
return;
const int navPadding = isHighResolution ? 24 : 12; // padding per side
int usableWidth = SCREEN_WIDTH - (navPadding * 2);
if (usableWidth < iconSize)
usableWidth = iconSize;
const size_t iconsPerPage = usableWidth / (iconSize + spacing);
const size_t iconsPerPage = (SCREEN_WIDTH + spacing) / (iconSize + spacing);
const size_t currentPage = currentFrame / iconsPerPage;
const size_t pageStart = currentPage * iconsPerPage;
const size_t pageEnd = min(pageStart + iconsPerPage, totalIcons);
@@ -1310,47 +1338,6 @@ void UIRenderer::drawNavigationBar(OLEDDisplay *display, OLEDDisplayUiState *sta
display->setColor(WHITE);
}
}
// Compact arrow drawer
auto drawArrow = [&](bool rightSide) {
display->setColor(WHITE);
const int offset = isHighResolution ? 3 : 1;
const int halfH = rectHeight / 2;
const int top = (y - 2) + (rectHeight - halfH) / 2;
const int bottom = top + halfH - 1;
const int midY = top + (halfH / 2);
const int maxW = 4;
// Determine left X coordinate
int baseX = rightSide ? (rectX + rectWidth + offset) : // right arrow
(rectX - offset - 1); // left arrow
for (int yy = top; yy <= bottom; yy++) {
int dist = abs(yy - midY);
int lineW = maxW - (dist * maxW / (halfH / 2));
if (lineW < 1)
lineW = 1;
if (rightSide) {
display->drawHorizontalLine(baseX, yy, lineW);
} else {
display->drawHorizontalLine(baseX - lineW + 1, yy, lineW);
}
}
};
// Right arrow
if (pageEnd < totalIcons) {
drawArrow(true);
}
// Left arrow
if (pageStart > 0) {
drawArrow(false);
}
// Knock the corners off the square
display->setColor(BLACK);
display->drawRect(rectX, y - 2, 1, 1);
@@ -1385,4 +1372,4 @@ std::string UIRenderer::drawTimeDelta(uint32_t days, uint32_t hours, uint32_t mi
} // namespace graphics
#endif // HAS_SCREEN
#endif // HAS_SCREEN

View File

@@ -18,8 +18,6 @@ const Emote emotes[] = {
{"\U0001F642", Slightly_Smiling, Slightly_Smiling_width, Slightly_Smiling_height}, // 🙂 Slightly Smiling Face
{"\U0001F609", Winking_Face, Winking_Face_width, Winking_Face_height}, // 😉 Winking Face
{"\U0001F601", Grinning_Smiling_Eyes, Grinning_Smiling_Eyes_width, Grinning_Smiling_Eyes_height}, // 😁 Grinning Smiling Eyes
{"\U0001F60D", Heart_eyes, Heart_eyes_width, Heart_eyes_height}, // 😍 Heart Eyes
{"\U0001F970", heart_smile, heart_smile_width, heart_smile_height}, // 🥰 Smiling Face with Hearts
// --- Question/Alert ---
{"\u2753", question, question_width, question_height}, // ❓ Question Mark
@@ -32,15 +30,11 @@ const Emote emotes[] = {
{"\U0001F605", haha, haha_width, haha_height}, // 😅 Smiling with Sweat
{"\U0001F604", Grinning_SmilingEyes2, Grinning_SmilingEyes2_width,
Grinning_SmilingEyes2_height}, // 😄 Grinning Face with Smiling Eyes
{"\U0001F62D", Loudly_Crying_Face, Loudly_Crying_Face_width, Loudly_Crying_Face_height}, // 😭 Loudly Crying Face
// --- Gestures and People ---
{"\U0001F44B", wave_icon, wave_icon_width, wave_icon_height}, // 👋 Waving Hand
{"\u270C\uFE0F", peace_sign, peace_sign_width, peace_sign_height}, // ✌️ Victory Hand
{"\U0001F596", vulcan_salute, vulcan_salute_width, vulcan_salute_height}, // 🖖 Vulcan Salute
{"\U0001F64F", Praying, Praying_width, Praying_height}, // 🙏 Praying Hands
{"\U0001F920", cowboy, cowboy_width, cowboy_height}, // 🤠 Cowboy Hat Face
{"\U0001F3A7", deadmau5, deadmau5_width, deadmau5_height}, // 🎧 Headphones
{"\U0001F44B", wave_icon, wave_icon_width, wave_icon_height}, // 👋 Waving Hand
{"\U0001F920", cowboy, cowboy_width, cowboy_height}, // 🤠 Cowboy Hat Face
{"\U0001F3A7", deadmau5, deadmau5_width, deadmau5_height}, // 🎧 Headphones
// --- Weather ---
{"\u2600", sun, sun_width, sun_height}, // ☀ Sun (without variation selector)
@@ -51,12 +45,8 @@ const Emote emotes[] = {
// --- Misc Faces ---
{"\U0001F608", devil, devil_width, devil_height}, // 😈 Smiling Face with Horns
{"\U0001F921", clown, clown_width, clown_height}, // 🤡 Clown Face
{"\U0001F916", robo, robo_width, robo_height}, // 🤖 Robot Face
// --- Hearts (Multiple Unicode Aliases) ---
{"\u2665", heart, heart_width, heart_height}, // ♥ Black Heart Suit
{"\u2665\uFE0F", heart, heart_width, heart_height}, // ♥️ Black Heart Suit (emoji presentation)
{"\u2764\uFE0F", heart, heart_width, heart_height}, // ❤️ Red Heart
{"\U0001F9E1", heart, heart_width, heart_height}, // 🧡 Orange Heart
{"\U00002763", heart, heart_width, heart_height}, // ❣ Heart Exclamation
@@ -67,166 +57,223 @@ const Emote emotes[] = {
{"\U0001F498", heart, heart_width, heart_height}, // 💘 Heart with Arrow
// --- Objects ---
{"\U0001F4A9", poo, poo_width, poo_height}, // 💩 Pile of Poo
{"\U0001F514", bell_icon, bell_icon_width, bell_icon_height}, // 🔔 Bell
{"\U0001F36A", cookie, cookie_width, cookie_height}, // 🍪 Cookie
{"\U0001F525", Fire, Fire_width, Fire_height}, // 🔥 Fire
{"\u2728", Sparkles, Sparkles_width, Sparkles_height}, // ✨ Sparkles
{"\U0001F573\uFE0F", hole, hole_width, hole_height}, // 🕳️ Hole
{"\U0001F3B3", bowling, bowling_width, bowling_height} // 🎳 Bowling
{"\U0001F4A9", poo, poo_width, poo_height}, // 💩 Pile of Poo
{"\U0001F514", bell_icon, bell_icon_width, bell_icon_height} // 🔔 Bell
#endif
};
const int numEmotes = sizeof(emotes) / sizeof(emotes[0]);
#ifndef EXCLUDE_EMOJI
const unsigned char thumbup[] PROGMEM = {0x00, 0x03, 0x80, 0x04, 0x80, 0x04, 0x40, 0x04, 0x20, 0x02, 0x18,
0x02, 0x06, 0x3F, 0x06, 0x40, 0x06, 0x70, 0x06, 0x40, 0x06, 0x70,
0x06, 0x40, 0x06, 0x30, 0x08, 0x20, 0xF0, 0x1F, 0x00, 0x00};
const unsigned char thumbup[] PROGMEM = {
0x00, 0x1C, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x80, 0x09, 0x00, 0x00,
0xC0, 0x08, 0x00, 0x00, 0x40, 0x08, 0x00, 0x00, 0x20, 0x04, 0x00, 0x00, 0x10, 0x04, 0x00, 0x00, 0x18, 0x02, 0x00, 0x00,
0x0C, 0xCE, 0x7F, 0x00, 0x04, 0x20, 0x80, 0x00, 0x02, 0x20, 0x80, 0x00, 0x02, 0x60, 0xC0, 0x00, 0x01, 0xF8, 0xFF, 0x01,
0x01, 0x08, 0x00, 0x01, 0x01, 0x08, 0x00, 0x01, 0x01, 0xF8, 0xFF, 0x00, 0x01, 0x10, 0x80, 0x00, 0x01, 0x18, 0x80, 0x00,
0x02, 0x30, 0xC0, 0x00, 0x06, 0xE0, 0x3F, 0x00, 0x0C, 0x20, 0x30, 0x00, 0x38, 0x20, 0x10, 0x00, 0xE0, 0xCF, 0x1F, 0x00,
};
const unsigned char thumbdown[] PROGMEM = {0xF0, 0x1F, 0x08, 0x20, 0x06, 0x30, 0x06, 0x40, 0x06, 0x70, 0x06,
0x40, 0x06, 0x70, 0x06, 0x40, 0x06, 0x3F, 0x18, 0x02, 0x20, 0x02,
0x40, 0x04, 0x80, 0x04, 0x80, 0x04, 0x00, 0x03, 0x00, 0x00};
const unsigned char thumbdown[] PROGMEM = {
0xE0, 0xCF, 0x1F, 0x00, 0x38, 0x20, 0x10, 0x00, 0x0C, 0x20, 0x30, 0x00, 0x06, 0xE0, 0x3F, 0x00, 0x02, 0x30, 0xC0, 0x00,
0x01, 0x18, 0x80, 0x00, 0x01, 0x10, 0x80, 0x00, 0x01, 0xF8, 0xFF, 0x00, 0x01, 0x08, 0x00, 0x01, 0x01, 0x08, 0x00, 0x01,
0x01, 0xF8, 0xFF, 0x01, 0x02, 0x60, 0xC0, 0x00, 0x02, 0x20, 0x80, 0x00, 0x04, 0x20, 0x80, 0x00, 0x0C, 0xCE, 0x7F, 0x00,
0x18, 0x02, 0x00, 0x00, 0x10, 0x04, 0x00, 0x00, 0x20, 0x04, 0x00, 0x00, 0x40, 0x08, 0x00, 0x00, 0xC0, 0x08, 0x00, 0x00,
0x80, 0x09, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, 0x1C, 0x00, 0x00,
};
const unsigned char Smiling_Eyes[] PROGMEM = {0x00, 0x00, 0xE0, 0x07, 0x18, 0x18, 0x04, 0x20, 0x24, 0x24, 0x52,
0x4A, 0x02, 0x40, 0x02, 0x40, 0x22, 0x44, 0x22, 0x44, 0xC2, 0x43,
0x04, 0x20, 0x04, 0x20, 0x18, 0x18, 0xE0, 0x07, 0x00, 0x00};
const unsigned char Smiling_Eyes[] PROGMEM = {
0x00, 0xf8, 0x03, 0xc0, 0x00, 0xfe, 0x0f, 0xc0, 0x80, 0xff, 0x3f, 0xc0, 0xc0, 0xff, 0xff, 0xc0, 0xe0, 0xff, 0xff, 0xc1,
0xf0, 0xff, 0xff, 0xc3, 0xf8, 0xff, 0xff, 0xc7, 0xf8, 0xff, 0xff, 0xcf, 0xfc, 0xff, 0xff, 0xcf, 0xfc, 0xff, 0xff, 0xcf,
0x7e, 0xf8, 0xc3, 0xdf, 0x3e, 0xf0, 0x81, 0xdf, 0xbf, 0xf7, 0xbd, 0xff, 0xff, 0xff, 0xff, 0xff, 0x9f, 0xff, 0x3f, 0xff,
0x6f, 0xff, 0xdf, 0xfe, 0x6f, 0xff, 0xdf, 0xfe, 0x9f, 0xff, 0x3f, 0xff, 0xfe, 0xff, 0xff, 0xdf, 0x7e, 0xff, 0xdf, 0xdf,
0x7c, 0xff, 0xdf, 0xcf, 0xfc, 0xfe, 0xef, 0xcf, 0xf8, 0xf9, 0xf7, 0xc7, 0xf8, 0x03, 0xf8, 0xc7, 0xf0, 0xff, 0xff, 0xc3,
0xe0, 0xff, 0xff, 0xc1, 0xc0, 0xff, 0xff, 0xc0, 0x80, 0xff, 0x3f, 0xc0, 0x00, 0xfe, 0x0f, 0xc0, 0x00, 0xf8, 0x07, 0xc0};
const unsigned char Grinning[] PROGMEM = {0x00, 0x00, 0xE0, 0x07, 0x18, 0x18, 0x04, 0x20, 0x44, 0x22, 0x42,
0x42, 0x02, 0x40, 0x02, 0x40, 0xF2, 0x4F, 0x12, 0x48, 0x22, 0x44,
0xC4, 0x23, 0x04, 0x20, 0x18, 0x18, 0xE0, 0x07, 0x00, 0x00};
const unsigned char Grinning[] PROGMEM = {
0x00, 0xf8, 0x03, 0xc0, 0x00, 0xfe, 0x0f, 0xc0, 0x80, 0xff, 0x3f, 0xc0, 0xc0, 0xff, 0xff, 0xc0, 0xe0, 0xff, 0xff, 0xc1,
0xf0, 0xff, 0xff, 0xc3, 0xf8, 0xff, 0xff, 0xc7, 0xf8, 0xff, 0xff, 0xcf, 0xfc, 0xf9, 0xf3, 0xcf, 0xfc, 0xf0, 0xe1, 0xcf,
0xfe, 0xf0, 0xe1, 0xdf, 0xfe, 0xf0, 0xe1, 0xdf, 0xff, 0xf0, 0xe1, 0xff, 0xff, 0xf9, 0xf3, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x3f, 0x00, 0x80, 0xff, 0xbe, 0xff, 0xbf, 0xdf, 0x7e, 0x00, 0xc0, 0xdf,
0x7c, 0x00, 0xc0, 0xcf, 0xfc, 0x00, 0xe0, 0xcf, 0xf8, 0x01, 0xf0, 0xc7, 0xf8, 0x03, 0xf8, 0xc7, 0xf0, 0xff, 0xff, 0xc3,
0xe0, 0xff, 0xff, 0xc1, 0xc0, 0xff, 0xff, 0xc0, 0x80, 0xff, 0x3f, 0xc0, 0x00, 0xfe, 0x0f, 0xc0, 0x00, 0xf8, 0x03, 0xc0};
const unsigned char Slightly_Smiling[] PROGMEM = {0x00, 0x00, 0xE0, 0x07, 0x18, 0x18, 0x04, 0x20, 0x44, 0x22, 0x42,
0x42, 0x02, 0x40, 0x02, 0x40, 0x12, 0x48, 0x12, 0x48, 0x22, 0x44,
0xC4, 0x23, 0x04, 0x20, 0x18, 0x18, 0xE0, 0x07, 0x00, 0x00};
const unsigned char Slightly_Smiling[] PROGMEM = {
0x00, 0xf8, 0x03, 0xc0, 0x00, 0xfe, 0x0f, 0xc0, 0x80, 0xff, 0x3f, 0xc0, 0xc0, 0xff, 0xff, 0xc0, 0xe0, 0xff, 0xff, 0xc1,
0xf0, 0xff, 0xff, 0xc3, 0xf8, 0xff, 0xff, 0xc7, 0xf8, 0xff, 0xff, 0xcf, 0xfc, 0xf9, 0xf3, 0xcf, 0xfc, 0xf0, 0xe1, 0xcf,
0xfe, 0xf0, 0xe1, 0xdf, 0xfe, 0xf0, 0xe1, 0xdf, 0xff, 0xf0, 0xe1, 0xff, 0xff, 0xf9, 0xf3, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xdf, 0x7e, 0xff, 0xdf, 0xdf,
0x7c, 0xff, 0xdf, 0xcf, 0xfc, 0xfe, 0xef, 0xcf, 0xf8, 0xf9, 0xf7, 0xc7, 0xf8, 0x03, 0xf8, 0xc7, 0xf0, 0xff, 0xff, 0xc3,
0xe0, 0xff, 0xff, 0xc1, 0xc0, 0xff, 0xff, 0xc0, 0x80, 0xff, 0x3f, 0xc0, 0x00, 0xfe, 0x0f, 0xc0, 0x00, 0xf8, 0x03, 0xc0};
const unsigned char Winking_Face[] PROGMEM = {0x00, 0x00, 0xE0, 0x07, 0x18, 0x18, 0x04, 0x20, 0x44, 0x20, 0x42,
0x46, 0x02, 0x40, 0x02, 0x40, 0x12, 0x48, 0x12, 0x48, 0x22, 0x44,
0xC4, 0x23, 0x04, 0x20, 0x18, 0x18, 0xE0, 0x07, 0x00, 0x00};
const unsigned char Winking_Face[] PROGMEM = {
0x00, 0xf8, 0x03, 0xc0, 0x00, 0xfe, 0x0f, 0xc0, 0x80, 0xff, 0x3f, 0xc0, 0xc0, 0xff, 0xff, 0xc0, 0xe0, 0xff, 0xff, 0xc1,
0xf0, 0xf0, 0xff, 0xc3, 0x78, 0xef, 0xc3, 0xc7, 0xb8, 0xdf, 0xbd, 0xcf, 0xfc, 0xf9, 0x7f, 0xcf, 0xfc, 0xf0, 0xff, 0xcf,
0xfe, 0xf0, 0xc3, 0xdf, 0xfe, 0xf0, 0x81, 0xdf, 0xff, 0xf0, 0xbf, 0xff, 0xff, 0xf9, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xdf, 0x7e, 0xff, 0xdf, 0xdf,
0x7c, 0xff, 0xdf, 0xcf, 0xfc, 0xfe, 0xef, 0xcf, 0xf8, 0xf9, 0xf7, 0xc7, 0xf8, 0x03, 0xf8, 0xc7, 0xf0, 0xff, 0xff, 0xc3,
0xe0, 0xff, 0xff, 0xc1, 0xc0, 0xff, 0xff, 0xc0, 0x80, 0xff, 0x3f, 0xc0, 0x00, 0xfe, 0x0f, 0xc0, 0x00, 0xf8, 0x07, 0xc0};
const unsigned char Grinning_Smiling_Eyes[] PROGMEM = {0x00, 0x00, 0xE0, 0x07, 0x18, 0x18, 0x04, 0x20, 0x24, 0x24, 0x52,
0x4A, 0x02, 0x40, 0xFA, 0x5F, 0x0A, 0x50, 0x0A, 0x50, 0x12, 0x48,
0x24, 0x24, 0xC4, 0x23, 0x18, 0x18, 0xE0, 0x07, 0x00, 0x00};
const unsigned char Grinning_Smiling_Eyes[] PROGMEM = {
0x00, 0xf8, 0x03, 0xc0, 0x00, 0xfe, 0x0f, 0xc0, 0x80, 0xff, 0x3f, 0xc0, 0xc0, 0xff, 0xff, 0xc0, 0xe0, 0xff, 0xff, 0xc1,
0xf0, 0xff, 0xff, 0xc3, 0xf8, 0xff, 0xff, 0xc7, 0xf8, 0xff, 0xff, 0xcf, 0xfc, 0xf8, 0xe3, 0xcf, 0x7c, 0xf7, 0xdd, 0xcf,
0xbe, 0xef, 0xbe, 0xdf, 0xbe, 0xef, 0xbe, 0xdf, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x1f, 0x00, 0x00, 0xff, 0x5e, 0x55, 0x55, 0xdf, 0x5e, 0x55, 0x55, 0xdf,
0x3c, 0x00, 0x80, 0xcf, 0x7c, 0x55, 0xd5, 0xcf, 0xf8, 0x54, 0xe5, 0xc7, 0xf8, 0x03, 0xf8, 0xc7, 0xf0, 0xff, 0xff, 0xc3,
0xe0, 0xff, 0xff, 0xc1, 0xc0, 0xff, 0xff, 0xc0, 0x80, 0xff, 0x3f, 0xc0, 0x00, 0xfe, 0x0f, 0xc0, 0x00, 0xf8, 0x03, 0xc0};
const unsigned char heart_smile[] PROGMEM = {0x00, 0x00, 0x6C, 0x07, 0x7C, 0x18, 0x7C, 0x20, 0x38, 0x24, 0x52,
0x0A, 0x02, 0xD8, 0x02, 0xF8, 0x22, 0xFC, 0x20, 0x74, 0xDB, 0x23,
0x1F, 0x00, 0x1F, 0x20, 0x0E, 0x18, 0xE4, 0x07, 0x00, 0x00};
const unsigned char question[] PROGMEM = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x3C, 0x00, 0x00, 0x80, 0xFF, 0x01, 0x00, 0xC0, 0xFF, 0x07, 0x00, 0xE0, 0xFF, 0x07, 0x00,
0xE0, 0xC3, 0x0F, 0x00, 0xF0, 0x81, 0x0F, 0x00, 0xF0, 0x01, 0x0F, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x80, 0x0F, 0x00,
0x00, 0xC0, 0x0F, 0x00, 0x00, 0xE0, 0x07, 0x00, 0x00, 0xF0, 0x03, 0x00, 0x00, 0xF8, 0x01, 0x00, 0x00, 0x7C, 0x00, 0x00,
0x00, 0x3C, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x3E, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
};
const unsigned char Heart_eyes[] PROGMEM = {0x00, 0x00, 0xE0, 0x07, 0x18, 0x18, 0x04, 0x20, 0x54, 0x2A, 0xFA,
0x5F, 0x72, 0x4E, 0x22, 0x44, 0x02, 0x40, 0x12, 0x48, 0x12, 0x48,
0x24, 0x24, 0xC4, 0x23, 0x18, 0x18, 0xE0, 0x07, 0x00, 0x00};
const unsigned char bang[] PROGMEM = {
0xFF, 0x0F, 0xFC, 0x3F, 0xFF, 0x0F, 0xFC, 0x3F, 0xFF, 0x0F, 0xFC, 0x3F, 0xFF, 0x07, 0xF8, 0x3F, 0xFF, 0x07, 0xF8, 0x3F,
0xFE, 0x07, 0xF8, 0x1F, 0xFE, 0x07, 0xF8, 0x1F, 0xFE, 0x07, 0xF8, 0x1F, 0xFE, 0x07, 0xF8, 0x1F, 0xFE, 0x07, 0xF8, 0x1F,
0xFE, 0x03, 0xF0, 0x1F, 0xFE, 0x03, 0xF0, 0x1F, 0xFC, 0x03, 0xF0, 0x0F, 0xFC, 0x03, 0xF0, 0x0F, 0xFC, 0x03, 0xF0, 0x0F,
0xFC, 0x03, 0xF0, 0x0F, 0xFC, 0x03, 0xF0, 0x0F, 0xFC, 0x03, 0xF0, 0x0F, 0xFC, 0x01, 0xE0, 0x0F, 0xFC, 0x01, 0xE0, 0x0F,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0x00, 0xC0, 0x03, 0xFC, 0x03, 0xF0, 0x0F, 0xFE, 0x03, 0xF0, 0x1F,
0xFE, 0x07, 0xF8, 0x1F, 0xFE, 0x07, 0xF8, 0x1F, 0xFE, 0x07, 0xF8, 0x1F, 0xFC, 0x03, 0xF0, 0x0F, 0xF8, 0x01, 0xE0, 0x07,
};
const unsigned char question[] PROGMEM = {0xE0, 0x07, 0x10, 0x08, 0x08, 0x10, 0x88, 0x11, 0x48, 0x12, 0x48,
0x12, 0x48, 0x12, 0x30, 0x11, 0x80, 0x08, 0x40, 0x04, 0x40, 0x02,
0xC0, 0x03, 0x00, 0x00, 0xC0, 0x03, 0x40, 0x02, 0x80, 0x01};
const unsigned char haha[] PROGMEM = {
0x00, 0xf8, 0x03, 0xc0, 0x00, 0xfe, 0x0f, 0xc0, 0x80, 0xff, 0x3f, 0xc0, 0xc0, 0xff, 0x7f, 0xc0, 0xe0, 0xf9, 0xf3, 0xc0,
0xf0, 0xfe, 0xef, 0xc1, 0x38, 0xff, 0x9f, 0xc3, 0xd8, 0xff, 0x7f, 0xc3, 0xfc, 0xf8, 0xe3, 0xc7, 0x7c, 0xf7, 0xdd, 0xcf,
0xbe, 0xef, 0xbe, 0xcf, 0xfe, 0xff, 0xff, 0xcf, 0xef, 0xff, 0xff, 0xde, 0xe7, 0xff, 0xff, 0xdc, 0xeb, 0xff, 0xff, 0xda,
0xed, 0xff, 0xff, 0xd6, 0xee, 0xff, 0xff, 0xce, 0x36, 0x00, 0x80, 0xcd, 0xb8, 0xff, 0xbf, 0xc3, 0x7e, 0x00, 0xc0, 0xdf,
0x7c, 0x00, 0xc0, 0xcf, 0xfc, 0x00, 0xe0, 0xcf, 0xf8, 0x01, 0xf0, 0xc7, 0xf8, 0x03, 0xf8, 0xc7, 0xf0, 0xff, 0xff, 0xc3,
0xe0, 0xff, 0xff, 0xc1, 0xc0, 0xff, 0xff, 0xc0, 0x80, 0xff, 0x3f, 0xc0, 0x00, 0xfe, 0x0f, 0xc0, 0x00, 0xf8, 0x03, 0xc0};
const unsigned char bang[] PROGMEM = {0x30, 0x0C, 0x48, 0x12, 0x48, 0x12, 0x48, 0x12, 0x48, 0x12, 0x48,
0x12, 0x48, 0x12, 0x48, 0x12, 0x48, 0x12, 0x48, 0x12, 0x30, 0x0C,
0x00, 0x00, 0x30, 0x0C, 0x48, 0x12, 0x30, 0x0C, 0x00, 0x00};
const unsigned char ROFL[] PROGMEM = {
0x00, 0x00, 0x00, 0xc0, 0x00, 0xfc, 0x07, 0xc0, 0x00, 0xff, 0x1f, 0xc0, 0x80, 0xff, 0x7f, 0xc0, 0xc0, 0xff, 0xff, 0xc0,
0xe0, 0x9f, 0xff, 0xc1, 0xf0, 0x9f, 0xff, 0xc0, 0xf8, 0x9f, 0x7f, 0xcb, 0xf8, 0x9f, 0xbf, 0xcb, 0xfc, 0x9f, 0xdf, 0xdb,
0xfc, 0x1f, 0x08, 0xdc, 0xfe, 0x1f, 0xf8, 0xfe, 0xfe, 0xff, 0xff, 0xfe, 0x1e, 0xf0, 0x7f, 0xfe, 0x1e, 0xf0, 0xbf, 0xfe,
0xfe, 0xf3, 0xdf, 0xfe, 0xfe, 0xf3, 0x6f, 0xfe, 0xfe, 0xf3, 0x37, 0xfe, 0xfe, 0xeb, 0x1b, 0xfe, 0xfc, 0xef, 0x0d, 0xde,
0xfc, 0xe7, 0x06, 0xcf, 0xf8, 0x6b, 0x83, 0xcf, 0xf8, 0x0d, 0xc0, 0xc7, 0xf0, 0xed, 0xff, 0xc7, 0xe0, 0xee, 0xff, 0xc3,
0xc0, 0xee, 0xff, 0xc1, 0x80, 0xee, 0xff, 0xc0, 0x00, 0xe6, 0x3f, 0xc0, 0x00, 0xf0, 0x0f, 0xc0, 0x00, 0x00, 0x00, 0xc0};
const unsigned char haha[] PROGMEM = {0x00, 0x00, 0xE0, 0x07, 0x18, 0x18, 0x04, 0x20, 0x24, 0x24, 0x52,
0x4A, 0x0A, 0x50, 0x0E, 0x70, 0xF2, 0x4F, 0x12, 0x48, 0x32, 0x44,
0xC4, 0x23, 0x04, 0x20, 0x18, 0x18, 0xE0, 0x07, 0x00, 0x00};
const unsigned char Smiling_Closed_Eyes[] PROGMEM = {
0x00, 0xf8, 0x03, 0xc0, 0x00, 0xfe, 0x0f, 0xc0, 0x80, 0xff, 0x3f, 0xc0, 0xc0, 0xff, 0xff, 0xc0, 0xe0, 0xff, 0xff, 0xc1,
0xf0, 0xff, 0xff, 0xc3, 0xf8, 0xff, 0xff, 0xc7, 0xf8, 0xff, 0xff, 0xcf, 0x7c, 0xfe, 0xcf, 0xcf, 0xfc, 0xfc, 0xe7, 0xcf,
0xfe, 0xf9, 0xf3, 0xdf, 0xfe, 0xf3, 0xf9, 0xdf, 0xff, 0xf9, 0xf3, 0xff, 0xff, 0xfc, 0xe7, 0xff, 0x7f, 0xfe, 0xcf, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x3f, 0x00, 0x80, 0xff, 0xbe, 0xff, 0xbf, 0xdf, 0x7e, 0x00, 0xc0, 0xdf,
0x7c, 0x00, 0xc0, 0xcf, 0xfc, 0x00, 0xe0, 0xcf, 0xf8, 0x01, 0xf0, 0xc7, 0xf8, 0x03, 0xf8, 0xc7, 0xf0, 0xff, 0xff, 0xc3,
0xe0, 0xff, 0xff, 0xc1, 0xc0, 0xff, 0xff, 0xc0, 0x80, 0xff, 0x3f, 0xc0, 0x00, 0xfe, 0x0f, 0xc0, 0x00, 0xf8, 0x03, 0xc0};
const unsigned char ROFL[] PROGMEM = {0x00, 0x00, 0xE0, 0x07, 0x18, 0x18, 0x84, 0x21, 0x84, 0x20, 0x02,
0x4C, 0x02, 0x4A, 0x1A, 0x49, 0x8A, 0x48, 0x42, 0x48, 0x22, 0x44,
0xE4, 0x23, 0x04, 0x20, 0x18, 0x18, 0xE0, 0x07, 0x00, 0x00};
const unsigned char Grinning_SmilingEyes2[] PROGMEM = {
0x00, 0xf8, 0x03, 0xc0, 0x00, 0xfe, 0x0f, 0xc0, 0x80, 0xff, 0x3f, 0xc0, 0xc0, 0xff, 0x7f, 0xc0, 0xe0, 0xff, 0xff, 0xc0,
0xf0, 0xff, 0xff, 0xc1, 0xf8, 0xff, 0xff, 0xc3, 0xf8, 0xff, 0xff, 0xc3, 0xfc, 0xf8, 0xe3, 0xc7, 0x7c, 0xf7, 0xdd, 0xc7,
0xbe, 0xef, 0xbe, 0xcf, 0xfe, 0xff, 0xff, 0xcf, 0xff, 0xff, 0xff, 0xdf, 0xff, 0xff, 0xff, 0xdf, 0xff, 0xff, 0xff, 0xdf,
0xff, 0xff, 0xff, 0xdf, 0xff, 0xff, 0xff, 0xdf, 0x3f, 0x00, 0x80, 0xdf, 0xbe, 0xff, 0xbf, 0xcf, 0x7e, 0x00, 0xc0, 0xcf,
0x7c, 0x00, 0xc0, 0xc7, 0xfc, 0x00, 0xe0, 0xc7, 0xf8, 0x01, 0xf0, 0xc3, 0xf8, 0x03, 0xf8, 0xc3, 0xf0, 0xff, 0xff, 0xc1,
0xe0, 0xff, 0xff, 0xc0, 0xc0, 0xff, 0x7f, 0xc0, 0x80, 0xff, 0x3f, 0xc0, 0x00, 0xfe, 0x0f, 0xc0, 0x00, 0xf8, 0x03, 0xc0};
const unsigned char Smiling_Closed_Eyes[] PROGMEM = {0x00, 0x00, 0xE0, 0x07, 0x18, 0x18, 0x04, 0x20, 0x24, 0x24, 0x42,
0x42, 0x22, 0x44, 0x02, 0x40, 0xF2, 0x4F, 0x12, 0x48, 0x22, 0x44,
0xC4, 0x23, 0x04, 0x20, 0x18, 0x18, 0xE0, 0x07, 0x00, 0x00};
const unsigned char wave_icon[] PROGMEM = {
0x00, 0x00, 0x00, 0xc0, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x00, 0x7f, 0xc0, 0x00, 0x00, 0xc0, 0xc1, 0x00, 0x00, 0x00, 0xc7,
0x00, 0x00, 0x1e, 0xcc, 0x00, 0x00, 0x30, 0xc8, 0x00, 0x00, 0x60, 0xd8, 0x00, 0x08, 0xc0, 0xd0, 0x00, 0x1a, 0x81, 0xd1,
0x00, 0x36, 0x03, 0xd3, 0x80, 0x6d, 0x06, 0xd2, 0x00, 0xdb, 0x0c, 0xc2, 0x80, 0xb6, 0x1d, 0xc0, 0x80, 0x6d, 0x1f, 0xc0,
0x00, 0xdb, 0x3f, 0xc0, 0x00, 0xf6, 0x7f, 0xc0, 0x00, 0xfc, 0x7f, 0xc0, 0x08, 0xf8, 0x7f, 0xc0, 0x48, 0xf0, 0x7f, 0xc0,
0x48, 0xe0, 0x7f, 0xc0, 0xc8, 0xc0, 0x3f, 0xc0, 0x98, 0x81, 0x1f, 0xc0, 0x10, 0x03, 0x00, 0xc0, 0x30, 0x0e, 0x00, 0xc0,
0x20, 0x38, 0x00, 0xc0, 0xe0, 0x00, 0x00, 0xc0, 0x80, 0x07, 0x00, 0xc0, 0x00, 0x1e, 0x00, 0xc0, 0x00, 0x00, 0x00, 0xc0};
const unsigned char Grinning_SmilingEyes2[] PROGMEM = {0x00, 0x00, 0xE0, 0x07, 0x18, 0x18, 0x04, 0x20, 0x24, 0x24, 0x52,
0x4A, 0x02, 0x40, 0x02, 0x40, 0xF2, 0x4F, 0x12, 0x48, 0x22, 0x44,
0xC4, 0x23, 0x04, 0x20, 0x18, 0x18, 0xE0, 0x07, 0x00, 0x00};
const unsigned char cowboy[] PROGMEM = {
0x00, 0x0c, 0x0c, 0xc0, 0x00, 0x02, 0x10, 0xc0, 0x00, 0x01, 0x20, 0xc0, 0xbc, 0x00, 0x40, 0xcf, 0xc2, 0x01, 0xe0, 0xd0,
0x01, 0x01, 0x20, 0xe0, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x00, 0x00, 0xc0,
0xc1, 0x3f, 0xff, 0xe0, 0xe1, 0xff, 0xff, 0xe1, 0xf2, 0xf3, 0xf3, 0xd3, 0xf4, 0xf1, 0xe3, 0xcb, 0xfc, 0xf1, 0xe3, 0xc7,
0xf8, 0xf1, 0xe3, 0xc7, 0xf8, 0xf1, 0xe3, 0xc7, 0xf8, 0xfb, 0xf7, 0xc7, 0xf8, 0xff, 0xff, 0xc7, 0xf8, 0xff, 0xff, 0xc7,
0x70, 0xf8, 0x8f, 0xc3, 0x70, 0x03, 0xb0, 0xc3, 0x70, 0xfe, 0xbf, 0xc3, 0x60, 0x00, 0x80, 0xc1, 0xc0, 0x00, 0xc0, 0xc0,
0x80, 0x01, 0x60, 0xc0, 0x00, 0x07, 0x38, 0xc0, 0x00, 0xfe, 0x1f, 0xc0, 0x00, 0xf0, 0x03, 0xc0, 0x00, 0x00, 0x00, 0xc0};
const unsigned char Loudly_Crying_Face[] PROGMEM = {0x00, 0x00, 0xE0, 0x07, 0x18, 0x18, 0x04, 0x20, 0x34, 0x2C, 0x4A,
0x52, 0x12, 0x48, 0x12, 0x48, 0x92, 0x49, 0x52, 0x4A, 0x52, 0x4A,
0x54, 0x2A, 0x94, 0x29, 0x18, 0x18, 0xF0, 0x0F, 0x00, 0x00};
const unsigned char deadmau5[] PROGMEM = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF8, 0x07, 0x00,
0x00, 0xFC, 0x03, 0x00, 0x00, 0xFF, 0x3F, 0x00, 0x80, 0xFF, 0x0F, 0x00, 0xC0, 0xFF, 0xFF, 0x00, 0xE0, 0xFF, 0x3F, 0x00,
0xE0, 0xFF, 0xFF, 0x01, 0xF0, 0xFF, 0x7F, 0x00, 0xF0, 0xFF, 0xFF, 0x03, 0xF8, 0xFF, 0xFF, 0x00, 0xF0, 0xFF, 0xFF, 0x07,
0xFC, 0xFF, 0xFF, 0x00, 0xF0, 0xFF, 0xFF, 0x0F, 0xFC, 0xFF, 0xFF, 0x00, 0xF0, 0xFF, 0xFF, 0x0F, 0xFE, 0xFF, 0xFF, 0x00,
0xF0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0xF0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0xE0, 0xFF, 0x3F, 0xFC,
0x0F, 0xFF, 0x7F, 0x00, 0xC0, 0xFF, 0x1F, 0xF8, 0x0F, 0xFC, 0x3F, 0x00, 0x80, 0xFF, 0x0F, 0xF8, 0x1F, 0xFC, 0x1F, 0x00,
0x00, 0xFF, 0x0F, 0xFC, 0x3F, 0xFC, 0x0F, 0x00, 0x00, 0xF8, 0x1F, 0xFF, 0xFF, 0xFE, 0x01, 0x00, 0x00, 0x00, 0xFC, 0xFF,
0xFF, 0x07, 0x00, 0x00, 0x00, 0x00, 0xFC, 0xFF, 0xFF, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00,
0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0xE0, 0x00, 0xC0, 0x00, 0x00, 0x00,
0x00, 0x00, 0x80, 0x07, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFC, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
};
const unsigned char wave_icon[] PROGMEM = {0x00, 0x00, 0xC0, 0x18, 0x30, 0x21, 0x48, 0x5A, 0x94, 0x64, 0x24,
0x25, 0x4A, 0x24, 0x12, 0x44, 0x22, 0x44, 0x04, 0x40, 0x08, 0x40,
0x12, 0x40, 0x22, 0x20, 0xC4, 0x10, 0x18, 0x0F, 0x00, 0x00};
const unsigned char sun[] PROGMEM = {
0x00, 0xC0, 0x00, 0x00, 0x00, 0xE0, 0x01, 0x00, 0x00, 0xE0, 0x01, 0x00, 0x00, 0xE0, 0x01, 0x00, 0x30, 0xC0, 0x00, 0x03,
0x70, 0x00, 0x80, 0x03, 0xF0, 0x00, 0xC0, 0x03, 0xF0, 0xF8, 0xC7, 0x03, 0xE0, 0xFC, 0xCF, 0x01, 0x00, 0xFE, 0x1F, 0x00,
0x00, 0xFF, 0x3F, 0x00, 0x80, 0xFF, 0x7F, 0x00, 0x80, 0xFF, 0x7F, 0x00, 0x8E, 0xFF, 0x7F, 0x1C, 0x9F, 0xFF, 0x7F, 0x3E,
0x9F, 0xFF, 0x7F, 0x3E, 0x8E, 0xFF, 0x7F, 0x1C, 0x80, 0xFF, 0x7F, 0x00, 0x80, 0xFF, 0x7F, 0x00, 0x00, 0xFF, 0x3F, 0x00,
0x00, 0xFE, 0x1F, 0x00, 0x00, 0xFC, 0x0F, 0x00, 0xC0, 0xF9, 0xE7, 0x00, 0xE0, 0x01, 0xE0, 0x01, 0xF0, 0x01, 0xE0, 0x03,
0xF0, 0xC0, 0xC0, 0x03, 0x00, 0xE0, 0x01, 0x00, 0x00, 0xE0, 0x01, 0x00, 0x00, 0xE0, 0x01, 0x00, 0x00, 0xC0, 0x00, 0x00,
};
const unsigned char cowboy[] PROGMEM = {0x70, 0x0E, 0x8F, 0xF1, 0x11, 0x88, 0x21, 0x84, 0xC2, 0x43, 0x1E,
0x78, 0xE2, 0x47, 0x42, 0x42, 0x12, 0x48, 0x12, 0x48, 0x22, 0x44,
0xC4, 0x23, 0x04, 0x20, 0x18, 0x18, 0xE0, 0x07, 0x00, 0x00};
const unsigned char rain[] PROGMEM = {
0xC0, 0x0F, 0xC0, 0x00, 0x40, 0x00, 0x80, 0x00, 0x20, 0x00, 0x80, 0x00, 0x20, 0x00, 0x80, 0x03, 0x38, 0x00,
0x00, 0x0E, 0x0C, 0x00, 0x00, 0x18, 0x02, 0x00, 0x00, 0x10, 0x03, 0x00, 0x00, 0x30, 0x01, 0x00, 0x00, 0x20,
0x01, 0x00, 0x00, 0x20, 0x01, 0x00, 0x00, 0x20, 0x01, 0x00, 0x00, 0x20, 0x03, 0x00, 0x00, 0x30, 0x02, 0x00,
0x00, 0x10, 0x06, 0x00, 0x00, 0x08, 0xFC, 0xFF, 0xFF, 0x07, 0xF0, 0xFF, 0xFF, 0x01, 0x80, 0x00, 0x01, 0x00,
0xC0, 0xC0, 0x81, 0x03, 0xA0, 0x60, 0xC1, 0x03, 0x90, 0x20, 0x41, 0x01, 0xF0, 0xE0, 0xC0, 0x01, 0x60, 0x4C,
0x98, 0x00, 0x00, 0x0E, 0x1C, 0x00, 0x00, 0x0B, 0x12, 0x00, 0x00, 0x09, 0x1A, 0x00, 0x00, 0x06, 0x0E, 0x00,
};
const unsigned char deadmau5[] PROGMEM = {0x00, 0x00, 0xE0, 0x07, 0x18, 0x18, 0xE4, 0x27, 0x12, 0x48, 0x0A,
0x50, 0x0E, 0x70, 0x11, 0x88, 0x19, 0x98, 0x19, 0x98, 0x19, 0x98,
0x19, 0x98, 0x19, 0x98, 0x11, 0x88, 0x0E, 0x70, 0x00, 0x00};
const unsigned char cloud[] PROGMEM = {
0x00, 0x80, 0x07, 0x00, 0x00, 0xE0, 0x1F, 0x00, 0x00, 0x70, 0x30, 0x00, 0x00, 0x10, 0x60, 0x00, 0x80, 0x1F, 0x40, 0x00,
0xC0, 0x0F, 0xC0, 0x00, 0xC0, 0x00, 0x80, 0x00, 0x60, 0x00, 0x80, 0x00, 0x20, 0x00, 0x80, 0x00, 0x20, 0x00, 0x80, 0x01,
0x20, 0x00, 0x00, 0x07, 0x38, 0x00, 0x00, 0x0C, 0x0C, 0x00, 0x00, 0x08, 0x06, 0x00, 0x00, 0x18, 0x02, 0x00, 0x00, 0x10,
0x02, 0x00, 0x00, 0x30, 0x03, 0x00, 0x00, 0x20, 0x01, 0x00, 0x00, 0x20, 0x01, 0x00, 0x00, 0x20, 0x01, 0x00, 0x00, 0x20,
0x01, 0x00, 0x00, 0x20, 0x01, 0x00, 0x00, 0x20, 0x01, 0x00, 0x00, 0x20, 0x01, 0x00, 0x00, 0x30, 0x03, 0x00, 0x00, 0x10,
0x02, 0x00, 0x00, 0x10, 0x06, 0x00, 0x00, 0x18, 0x0C, 0x00, 0x00, 0x0C, 0xFC, 0xFF, 0xFF, 0x07, 0xF0, 0xFF, 0xFF, 0x03,
};
const unsigned char sun[] PROGMEM = {0x00, 0x00, 0x80, 0x01, 0xEC, 0x37, 0xFC, 0x3F, 0xF8, 0x1F, 0xFC,
0x3F, 0xFE, 0x7F, 0xFC, 0x3F, 0xFC, 0x3F, 0xFE, 0x7F, 0xFC, 0x3F,
0xF8, 0x1F, 0xFC, 0x3F, 0xEC, 0x37, 0x80, 0x01, 0x00, 0x00};
const unsigned char fog[] PROGMEM = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x78, 0x00, 0x3C, 0x00, 0xFE, 0x01, 0xFF, 0x00, 0x87, 0xC7, 0xC3, 0x01, 0x03, 0xFE, 0x80, 0x01,
0x00, 0x38, 0x00, 0x00, 0xFC, 0x00, 0x7E, 0x00, 0xFF, 0x83, 0xFF, 0x01, 0x03, 0xFF, 0x81, 0x01, 0x00, 0x7C, 0x00, 0x00,
0xF8, 0x00, 0x3E, 0x00, 0xFE, 0x01, 0xFF, 0x00, 0x87, 0xC7, 0xC3, 0x01, 0x03, 0xFE, 0x80, 0x01, 0x00, 0x38, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
};
const unsigned char rain[] PROGMEM = {0x00, 0x00, 0x00, 0x00, 0x00, 0x0E, 0x38, 0x1F, 0xFC, 0x3F, 0xFE,
0x7F, 0xFE, 0x7F, 0xFE, 0x7F, 0xFC, 0x3F, 0x00, 0x00, 0x48, 0x12,
0x48, 0x12, 0x24, 0x09, 0x24, 0x09, 0x00, 0x00, 0x00, 0x00};
const unsigned char devil[] PROGMEM = {
0x00, 0x00, 0x00, 0xc0, 0x00, 0x00, 0x00, 0xc0, 0x01, 0x00, 0x00, 0xe0, 0x03, 0x00, 0x00, 0xf0, 0x0f, 0xfc, 0x0f, 0xfc,
0x3f, 0xff, 0x3f, 0xff, 0xfe, 0xff, 0xff, 0xdf, 0xfe, 0xff, 0xff, 0xdf, 0xfe, 0xff, 0xff, 0xdf, 0xfc, 0xff, 0xff, 0xcf,
0xfc, 0xff, 0xff, 0xcf, 0xf8, 0xff, 0xff, 0xc7, 0xf0, 0xff, 0xff, 0xc3, 0xf0, 0xff, 0xff, 0xc3, 0xf0, 0xf1, 0xe3, 0xc3,
0xf0, 0xe7, 0xf9, 0xc3, 0xf0, 0xe7, 0xf9, 0xc3, 0xf0, 0xe3, 0xf1, 0xc3, 0xf0, 0xe3, 0xf1, 0xc3, 0xf0, 0xe7, 0xf9, 0xc3,
0xf0, 0xff, 0xff, 0xc3, 0xe0, 0xfd, 0xef, 0xc1, 0xe0, 0xf3, 0xf3, 0xc1, 0xc0, 0x07, 0xf8, 0xc0, 0x80, 0x1f, 0x7e, 0xc0,
0x00, 0xff, 0x3f, 0xc0, 0x00, 0xfe, 0x0f, 0xc0, 0x00, 0xf8, 0x03, 0xc0, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x00, 0x00, 0xc0};
const unsigned char cloud[] PROGMEM = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0E, 0x38, 0x1F, 0xFC,
0x3F, 0xFE, 0x7F, 0xFE, 0x7F, 0xFE, 0x7F, 0xFC, 0x3F, 0xF8, 0x1F,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
const unsigned char heart[] PROGMEM = {
0x00, 0x00, 0x00, 0x00, 0xC0, 0x03, 0xF0, 0x00, 0xF8, 0x0F, 0xFC, 0x07, 0xFC, 0x1F, 0x06, 0x0E, 0xFE, 0x3F, 0x03, 0x18,
0xFE, 0xFF, 0x7F, 0x10, 0xFF, 0xFF, 0xFF, 0x31, 0xFF, 0xFF, 0xFF, 0x33, 0xFF, 0xFF, 0xFF, 0x37, 0xFF, 0xFF, 0xFF, 0x37,
0xFF, 0xFF, 0xFF, 0x3F, 0xFF, 0xFF, 0xFF, 0x3F, 0xFF, 0xFF, 0xFF, 0x3F, 0xFE, 0xFF, 0xFF, 0x1F, 0xFE, 0xFF, 0xFF, 0x1F,
0xFC, 0xFF, 0xFF, 0x0F, 0xFC, 0xFF, 0xFF, 0x0F, 0xF8, 0xFF, 0xFF, 0x07, 0xF0, 0xFF, 0xFF, 0x03, 0xF0, 0xFF, 0xFF, 0x03,
0xE0, 0xFF, 0xFF, 0x01, 0xC0, 0xFF, 0xFF, 0x00, 0x80, 0xFF, 0x7F, 0x00, 0x00, 0xFF, 0x3F, 0x00, 0x00, 0xFE, 0x1F, 0x00,
0x00, 0xFC, 0x0F, 0x00, 0x00, 0xF8, 0x07, 0x00, 0x00, 0xF0, 0x03, 0x00, 0x00, 0xE0, 0x01, 0x00, 0x00, 0x80, 0x00, 0x00,
};
const unsigned char fog[] PROGMEM = {0x00, 0x00, 0x00, 0x00, 0x88, 0x88, 0x54, 0x55, 0x22, 0x22, 0x00,
0x00, 0x44, 0x44, 0xAA, 0x2A, 0x11, 0x11, 0x00, 0x00, 0x88, 0x88,
0x54, 0x55, 0x22, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
const unsigned char poo[] PROGMEM = {
0x00, 0x1c, 0x00, 0xc0, 0x00, 0x7c, 0x00, 0xc0, 0x00, 0xfc, 0x00, 0xc0, 0x00, 0x7c, 0x03, 0xc0, 0x00, 0xbe, 0x03, 0xc0,
0x00, 0xdf, 0x0f, 0xc0, 0x80, 0xcf, 0x0f, 0xc0, 0xc0, 0xf1, 0x0f, 0xc0, 0x60, 0xfc, 0x0f, 0xc0, 0x30, 0xff, 0x07, 0xc0,
0x90, 0xff, 0x3b, 0xc0, 0xc0, 0xff, 0x7d, 0xc0, 0xf8, 0xff, 0xfc, 0xc0, 0xf8, 0x3f, 0xf0, 0xc0, 0x78, 0x88, 0xc0, 0xc0,
0x20, 0xe3, 0x18, 0xc0, 0x98, 0xe7, 0xbc, 0xc1, 0x9c, 0x64, 0xa4, 0xc3, 0x9e, 0x64, 0xa4, 0xc7, 0xbe, 0xe4, 0xa4, 0xc7,
0xbc, 0x27, 0xbc, 0xc7, 0x38, 0x03, 0xd9, 0xc3, 0x00, 0xf0, 0x63, 0xc0, 0xf8, 0xfc, 0x3f, 0xcf, 0xfc, 0xff, 0x87, 0xdf,
0xfe, 0xff, 0xe0, 0xdf, 0xfc, 0x1f, 0xfe, 0xdf, 0xf8, 0x07, 0xf8, 0xcf, 0xf0, 0x03, 0xe0, 0xc7, 0x00, 0x00, 0x00, 0xc0};
const unsigned char devil[] PROGMEM = {0x06, 0x60, 0xCA, 0x53, 0x32, 0x4C, 0x22, 0x44, 0x44, 0x22, 0x3A,
0x5C, 0x32, 0x4C, 0x52, 0x4A, 0x72, 0x4E, 0x02, 0x40, 0x22, 0x44,
0xC4, 0x23, 0x04, 0x20, 0x18, 0x18, 0xE0, 0x07, 0x00, 0x00};
const unsigned char heart[] PROGMEM = {0x00, 0x00, 0x00, 0x00, 0x3C, 0x3C, 0x7E, 0x7E, 0xFE, 0x7F, 0xFE,
0x7F, 0xFE, 0x7F, 0xFE, 0x7F, 0xFC, 0x3F, 0xF8, 0x1F, 0xF8, 0x1F,
0xF0, 0x0F, 0xE0, 0x07, 0xC0, 0x03, 0x80, 0x01, 0x00, 0x00};
const unsigned char poo[] PROGMEM = {0x00, 0x00, 0x80, 0x01, 0x40, 0x02, 0x20, 0x04, 0x10, 0x04, 0xF0,
0x08, 0x10, 0x10, 0x48, 0x12, 0x08, 0x18, 0xE8, 0x21, 0x1C, 0x40,
0x42, 0x42, 0x82, 0x41, 0x02, 0x30, 0xFC, 0x0F, 0x00, 0x00};
const unsigned char bell_icon[] PROGMEM = {0x00, 0x00, 0x80, 0x01, 0x80, 0x01, 0xE0, 0x07, 0xF0, 0x0F, 0xF0,
0x0F, 0xF8, 0x1F, 0xF8, 0x1F, 0xF8, 0x1F, 0xF8, 0x1F, 0xFC, 0x3F,
0xFC, 0x3F, 0xFE, 0x7F, 0xFE, 0x7F, 0x80, 0x01, 0x00, 0x00};
const unsigned char cookie[] PROGMEM = {0x00, 0x00, 0xE0, 0x07, 0x18, 0x18, 0x04, 0x20, 0x34, 0x22, 0x32,
0x40, 0x02, 0x58, 0x82, 0x5B, 0x92, 0x43, 0x82, 0x43, 0x02, 0x40,
0x64, 0x28, 0x64, 0x20, 0x18, 0x18, 0xE0, 0x07, 0x00, 0x00};
const unsigned char Fire[] PROGMEM = {0x30, 0x00, 0xF0, 0x00, 0xF8, 0x03, 0xF8, 0x07, 0xFC, 0x1F, 0xFC,
0x1F, 0xFE, 0x3E, 0x7E, 0x3E, 0x3E, 0x7C, 0x1E, 0x78, 0x1E, 0x70,
0x1C, 0x70, 0x1C, 0x70, 0x38, 0x38, 0x30, 0x38, 0x60, 0x0C};
const unsigned char peace_sign[] PROGMEM = {0xC0, 0x30, 0x40, 0x29, 0x40, 0x25, 0x40, 0x15, 0x40, 0x12, 0x38,
0x0A, 0x54, 0x68, 0x54, 0x58, 0x54, 0x44, 0x3C, 0x22, 0x04, 0x22,
0x04, 0x12, 0x08, 0x10, 0x10, 0x08, 0xE0, 0x07, 0x00, 0x00};
const unsigned char Praying[] PROGMEM = {0x00, 0x00, 0x40, 0x02, 0xA0, 0x05, 0x90, 0x09, 0x90, 0x09, 0x90,
0x09, 0x98, 0x19, 0x94, 0x29, 0xA4, 0x25, 0xA4, 0x25, 0x84, 0x21,
0x84, 0x21, 0x86, 0x61, 0x4E, 0x72, 0x7F, 0x7E, 0x3F, 0xFC};
const unsigned char Sparkles[] PROGMEM = {0x00, 0x00, 0x10, 0x00, 0x38, 0x04, 0x10, 0x04, 0x00, 0x0E, 0x00,
0x1F, 0x80, 0x3F, 0xE0, 0xFF, 0x80, 0x3F, 0x10, 0x1F, 0x10, 0x0E,
0x38, 0x04, 0xFE, 0x04, 0x38, 0x00, 0x10, 0x00, 0x10, 0x00};
const unsigned char clown[] PROGMEM = {0x00, 0x00, 0xEE, 0x77, 0x1A, 0x58, 0x06, 0x60, 0x24, 0x24, 0x72,
0x4E, 0x22, 0x44, 0x82, 0x41, 0x82, 0x41, 0x1A, 0x58, 0xF2, 0x4F,
0x14, 0x28, 0xE4, 0x27, 0x18, 0x18, 0xE0, 0x07, 0x00, 0x00};
const unsigned char robo[] PROGMEM = {0x80, 0x01, 0xC0, 0x03, 0x80, 0x01, 0xFC, 0x3F, 0x04, 0x20, 0x74,
0x2E, 0x52, 0x4A, 0x72, 0x4E, 0x02, 0x40, 0x02, 0x40, 0xA2, 0x4A,
0x52, 0x45, 0x04, 0x20, 0x04, 0x20, 0xFC, 0x3F, 0x00, 0x00};
const unsigned char hole[] PROGMEM = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0x0F, 0x3C, 0x3C,
0x06, 0x60, 0x0C, 0x30, 0xF0, 0x0F, 0x00, 0x00, 0x00, 0x00};
const unsigned char bowling[] PROGMEM = {0x00, 0x38, 0x00, 0x44, 0x00, 0x44, 0x00, 0x44, 0x00, 0x28, 0x00,
0x38, 0x00, 0x28, 0x78, 0x44, 0x84, 0x82, 0x22, 0x83, 0x52, 0x83,
0x02, 0x83, 0x02, 0x45, 0x84, 0x44, 0x78, 0x38, 0x00, 0x00};
const unsigned char vulcan_salute[] PROGMEM = {0x08, 0x02, 0x16, 0x0D, 0x15, 0x15, 0x15, 0x15, 0xA9, 0x12, 0x4A,
0x0A, 0x02, 0x38, 0x04, 0x48, 0x04, 0x44, 0x04, 0x22, 0x04, 0x22,
0x04, 0x12, 0x08, 0x10, 0x10, 0x08, 0xE0, 0x07, 0x00, 0x00};
const unsigned char bell_icon[] PROGMEM = {
0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b11110000,
0b00000011, 0b00000000, 0b00000000, 0b11111100, 0b00001111, 0b00000000, 0b00000000, 0b00001111, 0b00111100, 0b00000000,
0b00000000, 0b00000011, 0b00110000, 0b00000000, 0b10000000, 0b00000001, 0b01100000, 0b00000000, 0b11000000, 0b00000000,
0b11000000, 0b00000000, 0b11000000, 0b00000000, 0b11000000, 0b00000000, 0b11000000, 0b00000000, 0b11000000, 0b00000000,
0b11000000, 0b00000000, 0b11000000, 0b00000000, 0b11000000, 0b00000000, 0b11000000, 0b00000000, 0b11000000, 0b00000000,
0b11000000, 0b00000000, 0b11000000, 0b00000000, 0b11000000, 0b00000000, 0b11000000, 0b00000000, 0b11000000, 0b00000000,
0b11000000, 0b00000000, 0b11000000, 0b00000000, 0b01000000, 0b00000000, 0b10000000, 0b00000000, 0b01100000, 0b00000000,
0b10000000, 0b00000001, 0b01110000, 0b00000000, 0b10000000, 0b00000011, 0b00110000, 0b00000000, 0b00000000, 0b00000011,
0b00011000, 0b00000000, 0b00000000, 0b00000110, 0b11110000, 0b11111111, 0b11111111, 0b00000011, 0b00000000, 0b00001100,
0b00001100, 0b00000000, 0b00000000, 0b00011000, 0b00000110, 0b00000000, 0b00000000, 0b11111000, 0b00000111, 0b00000000,
0b00000000, 0b11100000, 0b00000001, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000,
0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000};
#endif
} // namespace graphics

View File

@@ -17,150 +17,98 @@ extern const int numEmotes;
#ifndef EXCLUDE_EMOJI
// === Emote Bitmaps ===
#define thumbs_height 16
#define thumbs_width 16
#define thumbs_height 25
#define thumbs_width 25
extern const unsigned char thumbup[] PROGMEM;
extern const unsigned char thumbdown[] PROGMEM;
#define Smiling_Eyes_height 16
#define Smiling_Eyes_width 16
#define Smiling_Eyes_height 30
#define Smiling_Eyes_width 30
extern const unsigned char Smiling_Eyes[] PROGMEM;
#define Grinning_height 16
#define Grinning_width 16
#define Grinning_height 30
#define Grinning_width 30
extern const unsigned char Grinning[] PROGMEM;
#define Slightly_Smiling_height 16
#define Slightly_Smiling_width 16
#define Slightly_Smiling_height 30
#define Slightly_Smiling_width 30
extern const unsigned char Slightly_Smiling[] PROGMEM;
#define Winking_Face_height 16
#define Winking_Face_width 16
#define Winking_Face_height 30
#define Winking_Face_width 30
extern const unsigned char Winking_Face[] PROGMEM;
#define Grinning_Smiling_Eyes_height 16
#define Grinning_Smiling_Eyes_width 16
#define Grinning_Smiling_Eyes_height 30
#define Grinning_Smiling_Eyes_width 30
extern const unsigned char Grinning_Smiling_Eyes[] PROGMEM;
#define heart_smile_height 16
#define heart_smile_width 16
extern const unsigned char heart_smile[] PROGMEM;
#define Heart_eyes_height 16
#define Heart_eyes_width 16
extern const unsigned char Heart_eyes[] PROGMEM;
#define question_height 16
#define question_width 16
#define question_height 25
#define question_width 25
extern const unsigned char question[] PROGMEM;
#define bang_height 16
#define bang_width 16
#define bang_height 30
#define bang_width 30
extern const unsigned char bang[] PROGMEM;
#define haha_height 16
#define haha_width 16
#define haha_height 30
#define haha_width 30
extern const unsigned char haha[] PROGMEM;
#define ROFL_height 16
#define ROFL_width 16
#define ROFL_height 30
#define ROFL_width 30
extern const unsigned char ROFL[] PROGMEM;
#define Smiling_Closed_Eyes_height 16
#define Smiling_Closed_Eyes_width 16
#define Smiling_Closed_Eyes_height 30
#define Smiling_Closed_Eyes_width 30
extern const unsigned char Smiling_Closed_Eyes[] PROGMEM;
#define Grinning_SmilingEyes2_height 16
#define Grinning_SmilingEyes2_width 16
#define Grinning_SmilingEyes2_height 30
#define Grinning_SmilingEyes2_width 30
extern const unsigned char Grinning_SmilingEyes2[] PROGMEM;
#define Loudly_Crying_Face_height 16
#define Loudly_Crying_Face_width 16
extern const unsigned char Loudly_Crying_Face[] PROGMEM;
#define wave_icon_height 16
#define wave_icon_width 16
#define wave_icon_height 30
#define wave_icon_width 30
extern const unsigned char wave_icon[] PROGMEM;
#define cowboy_height 16
#define cowboy_width 16
#define cowboy_height 30
#define cowboy_width 30
extern const unsigned char cowboy[] PROGMEM;
#define deadmau5_height 16
#define deadmau5_width 16
#define deadmau5_height 30
#define deadmau5_width 60
extern const unsigned char deadmau5[] PROGMEM;
#define sun_height 16
#define sun_width 16
#define sun_height 30
#define sun_width 30
extern const unsigned char sun[] PROGMEM;
#define rain_height 16
#define rain_width 16
#define rain_height 30
#define rain_width 30
extern const unsigned char rain[] PROGMEM;
#define cloud_height 16
#define cloud_width 16
#define cloud_height 30
#define cloud_width 30
extern const unsigned char cloud[] PROGMEM;
#define fog_height 16
#define fog_width 16
#define fog_height 25
#define fog_width 25
extern const unsigned char fog[] PROGMEM;
#define devil_height 16
#define devil_width 16
#define devil_height 30
#define devil_width 30
extern const unsigned char devil[] PROGMEM;
#define heart_height 16
#define heart_width 16
#define heart_height 30
#define heart_width 30
extern const unsigned char heart[] PROGMEM;
#define poo_height 16
#define poo_width 16
#define poo_height 30
#define poo_width 30
extern const unsigned char poo[] PROGMEM;
#define bell_icon_width 16
#define bell_icon_height 16
#define bell_icon_width 30
#define bell_icon_height 30
extern const unsigned char bell_icon[] PROGMEM;
#define cookie_width 16
#define cookie_height 16
extern const unsigned char cookie[] PROGMEM;
#define Fire_width 16
#define Fire_height 16
extern const unsigned char Fire[] PROGMEM;
#define peace_sign_width 16
#define peace_sign_height 16
extern const unsigned char peace_sign[] PROGMEM;
#define Praying_width 16
#define Praying_height 16
extern const unsigned char Praying[] PROGMEM;
#define Sparkles_width 16
#define Sparkles_height 16
extern const unsigned char Sparkles[] PROGMEM;
#define clown_width 16
#define clown_height 16
extern const unsigned char clown[] PROGMEM;
#define robo_width 16
#define robo_height 16
extern const unsigned char robo[] PROGMEM;
#define hole_width 16
#define hole_height 16
extern const unsigned char hole[] PROGMEM;
#define bowling_width 16
#define bowling_height 16
extern const unsigned char bowling[] PROGMEM;
#define vulcan_salute_width 16
#define vulcan_salute_height 16
extern const unsigned char vulcan_salute[] PROGMEM;
#endif // EXCLUDE_EMOJI
} // namespace graphics
} // namespace graphics

View File

@@ -28,7 +28,7 @@ const uint8_t bluetoothConnectedIcon[36] PROGMEM = {0xfe, 0x01, 0xff, 0x03, 0x03
#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(ILI9488_CS) || defined(ST7796_CS) || \
defined(USE_ST7796) || defined(HACKADAY_COMMUNICATOR) || ARCH_PORTDUINO) && \
ARCH_PORTDUINO) && \
!defined(DISPLAY_FORCE_SMALL_FONTS)
const uint8_t imgQuestionL1[] PROGMEM = {0xff, 0x01, 0x01, 0x32, 0x7b, 0x49, 0x49, 0x6f, 0x26, 0x01, 0x01, 0xff};
const uint8_t imgQuestionL2[] PROGMEM = {0x0f, 0x08, 0x08, 0x08, 0x06, 0x0f, 0x0f, 0x06, 0x08, 0x08, 0x08, 0x0f};
@@ -360,10 +360,6 @@ const uint8_t chirpy_hirez[] = {
#define chirpy_small_image_height 8
const uint8_t chirpy_small[] = {0x7f, 0x41, 0x55, 0x55, 0x55, 0x55, 0x41, 0x7f};
#define connection_icon_width 7
#define connection_icon_height 5
const uint8_t connection_icon[] = {0x36, 0x41, 0x5D, 0x41, 0x36};
#ifdef M5STACK_UNITC6L
#include "img/icon_small.xbm"
#else

View File

@@ -124,7 +124,7 @@ uint32_t InkHUD::AppletFont::toUtf32(std::string utf8)
utf32 |= (utf8.at(3) & 0b00111111);
break;
default:
return 0;
assert(false);
}
return utf32;

View File

@@ -287,7 +287,7 @@ void InkHUD::MapApplet::getMapCenter(float *lat, float *lng)
float easternmost = lngCenter;
float westernmost = lngCenter;
for (size_t i = 0; i < nodeDB->getNumMeshNodes(); i++) {
for (uint8_t i = 0; i < nodeDB->getNumMeshNodes(); i++) {
meshtastic_NodeInfoLite *node = nodeDB->getMeshNodeByIndex(i);
// Skip if no position
@@ -474,8 +474,8 @@ void InkHUD::MapApplet::drawLabeledMarker(meshtastic_NodeInfoLite *node)
// Need at least two, to draw a sensible map
bool InkHUD::MapApplet::enoughMarkers()
{
size_t count = 0;
for (size_t i = 0; i < nodeDB->getNumMeshNodes(); i++) {
uint8_t count = 0;
for (uint8_t i = 0; i < nodeDB->getNumMeshNodes(); i++) {
meshtastic_NodeInfoLite *node = nodeDB->getMeshNodeByIndex(i);
// Count nodes

View File

@@ -5,6 +5,7 @@ A pattern / collection of resources for creating custom UIs, to target small gro
For an example, see the `heltec-vision-master-e290-inkhud` platformio env.
- platformio.ini
- suppress default Meshtastic components (Screen, ButtonThread, etc)
- define `MESHTASTIC_INCLUDE_NICHE_GRAPHICS`
- (possibly) Edit `build_src_filter` to include our new nicheGraphics.h file

View File

@@ -1,217 +0,0 @@
#if defined(HACKADAY_COMMUNICATOR)
#include "HackadayCommunicatorKeyboard.h"
#include "main.h"
#define _TCA8418_COLS 10
#define _TCA8418_ROWS 8
#define _TCA8418_NUM_KEYS 80
#define _TCA8418_MULTI_TAP_THRESHOLD 1500
using Key = TCA8418KeyboardBase::TCA8418Key;
constexpr uint8_t modifierRightShiftKey = 30;
constexpr uint8_t modifierRightShift = 0b0001;
constexpr uint8_t modifierLeftShiftKey = 76; // keynum -1
constexpr uint8_t modifierLeftShift = 0b0001;
// constexpr uint8_t modifierSymKey = 42;
// constexpr uint8_t modifierSym = 0b0010;
// Num chars per key, Modulus for rotating through characters
static uint8_t HackadayCommunicatorTapMod[_TCA8418_NUM_KEYS] = {
0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2,
0, 0, 0, 1, 1, 0, 1, 1, 1, 0, 0, 0, 1, 2, 2, 2, 2, 2, 2, 0, 0, 0, 1, 2, 2, 2, 1, 2, 2, 0, 0, 0, 2, 1, 2, 2, 0, 1, 1, 0,
};
static unsigned char HackadayCommunicatorTapMap[_TCA8418_NUM_KEYS][2] = {{},
{},
{'+'},
{'9'},
{'8'},
{'7'},
{'2'},
{'3'},
{'4'},
{'5'},
{Key::ESC},
{'q', 'Q'},
{'w', 'W'},
{'e', 'E'},
{'r', 'R'},
{'t', 'T'},
{'y', 'Y'},
{'u', 'U'},
{'i', 'I'},
{'o', 'O'},
{Key::TAB},
{'a', 'A'},
{'s', 'S'},
{'d', 'D'},
{'f', 'F'},
{'g', 'G'},
{'h', 'H'},
{'j', 'J'},
{'k', 'K'},
{'l', 'L'},
{},
{'z', 'Z'},
{'x', 'X'},
{'c', 'C'},
{'v', 'V'},
{'b', 'B'},
{'n', 'N'},
{'m', 'M'},
{',', '<'},
{'.', '>'},
{},
{},
{},
{'\\'},
{' '},
{},
{Key::RIGHT},
{Key::DOWN},
{Key::LEFT},
{},
{},
{},
{'-'},
{'6', '^'},
{'5', '%'},
{'4', '$'},
{'[', '{'},
{']', '}'},
{'p', 'P'},
{},
{},
{},
{'*'},
{'3', '#'},
{'2', '@'},
{'1', '!'},
{Key::SELECT},
{'\'', '"'},
{';', ':'},
{},
{},
{},
{'/', '?'},
{'='},
{'.', '>'},
{'0', ')'},
{},
{Key::UP},
{Key::BSP},
{}};
HackadayCommunicatorKeyboard::HackadayCommunicatorKeyboard()
: TCA8418KeyboardBase(_TCA8418_ROWS, _TCA8418_COLS), modifierFlag(0), last_modifier_time(0), last_key(-1), next_key(-1),
last_tap(0L), char_idx(0), tap_interval(0)
{
reset();
}
void HackadayCommunicatorKeyboard::reset(void)
{
TCA8418KeyboardBase::reset();
enableInterrupts();
}
// handle multi-key presses (shift and alt)
void HackadayCommunicatorKeyboard::trigger()
{
uint8_t count = keyCount();
if (count == 0)
return;
for (uint8_t i = 0; i < count; ++i) {
uint8_t k = readRegister(TCA8418_REG_KEY_EVENT_A + i);
uint8_t key = k & 0x7F;
if (k & 0x80) {
pressed(key);
} else {
released();
state = Idle;
}
}
}
void HackadayCommunicatorKeyboard::pressed(uint8_t key)
{
if (state == Init || state == Busy) {
return;
}
if (modifierFlag && (millis() - last_modifier_time > _TCA8418_MULTI_TAP_THRESHOLD)) {
modifierFlag = 0;
}
uint8_t next_key = 0;
int row = (key - 1) / 10;
int col = (key - 1) % 10;
if (row >= _TCA8418_ROWS || col >= _TCA8418_COLS) {
return; // Invalid key
}
next_key = row * _TCA8418_COLS + col;
state = Held;
uint32_t now = millis();
tap_interval = now - last_tap;
updateModifierFlag(next_key);
if (isModifierKey(next_key)) {
last_modifier_time = now;
}
if (tap_interval < 0) {
last_tap = 0;
state = Busy;
return;
}
if (next_key != last_key || tap_interval > _TCA8418_MULTI_TAP_THRESHOLD) {
char_idx = 0;
} else {
char_idx += 1;
}
last_key = next_key;
last_tap = now;
}
void HackadayCommunicatorKeyboard::released()
{
if (state != Held) {
return;
}
if (last_key < 0 || last_key >= _TCA8418_NUM_KEYS) {
last_key = -1;
state = Idle;
return;
}
uint32_t now = millis();
last_tap = now;
if (HackadayCommunicatorTapMod[last_key])
queueEvent(HackadayCommunicatorTapMap[last_key][modifierFlag % HackadayCommunicatorTapMod[last_key]]);
if (isModifierKey(last_key) == false)
modifierFlag = 0;
}
void HackadayCommunicatorKeyboard::updateModifierFlag(uint8_t key)
{
if (key == modifierRightShiftKey) {
modifierFlag ^= modifierRightShift;
} else if (key == modifierLeftShiftKey) {
modifierFlag ^= modifierLeftShift;
}
}
bool HackadayCommunicatorKeyboard::isModifierKey(uint8_t key)
{
return (key == modifierRightShiftKey || key == modifierLeftShiftKey);
}
#endif

View File

@@ -1,26 +0,0 @@
#include "TCA8418KeyboardBase.h"
class HackadayCommunicatorKeyboard : public TCA8418KeyboardBase
{
public:
HackadayCommunicatorKeyboard();
void reset(void);
void trigger(void) override;
virtual ~HackadayCommunicatorKeyboard() {}
protected:
void pressed(uint8_t key) override;
void released(void) override;
void updateModifierFlag(uint8_t key);
bool isModifierKey(uint8_t key);
private:
uint8_t modifierFlag; // Flag to indicate if a modifier key is pressed
uint32_t last_modifier_time; // Timestamp of the last modifier key press
int8_t last_key;
int8_t next_key;
uint32_t last_tap;
uint8_t char_idx;
int32_t tap_interval;
};

View File

@@ -52,7 +52,7 @@ int InputBroker::handleInputEvent(const InputEvent *event)
powerFSM.trigger(EVENT_INPUT); // todo: not every input should wake, like long hold release
if (event && event->inputEvent != INPUT_BROKER_NONE && externalNotificationModule &&
moduleConfig.external_notification.enabled && externalNotificationModule->nagging()) {
moduleConfig.external_notification.enabled) {
externalNotificationModule->stopNow();
}

View File

@@ -3,12 +3,6 @@
#include "Observer.h"
#include "freertosinc.h"
#ifdef InputBrokerDebug
#define LOG_INPUT(...) LOG_DEBUG(__VA_ARGS__)
#else
#define LOG_INPUT(...)
#endif
enum input_broker_event {
INPUT_BROKER_NONE = 0,
INPUT_BROKER_SELECT = 10,

View File

@@ -2,8 +2,6 @@
#include "configuration.h"
#include <Throttle.h>
SerialKeyboard *globalSerialKeyboard = nullptr;
#ifdef INPUTBROKER_SERIAL_TYPE
#define CANNED_MESSAGE_MODULE_ENABLE 1 // in case it's not set in the variant file
@@ -27,8 +25,6 @@ unsigned char KeyMap[3][4][10] = {{{'.', 'a', 'd', 'g', 'j', 'm', 'p', 't', 'w',
SerialKeyboard::SerialKeyboard(const char *name) : concurrency::OSThread(name)
{
this->_originName = name;
globalSerialKeyboard = this;
}
void SerialKeyboard::erase()
@@ -89,21 +85,9 @@ int32_t SerialKeyboard::runOnce()
e.source = this->_originName;
// SELECT OR SEND OR CANCEL EVENT
if (!(shiftRegister2 & (1 << 3))) {
if (shift > 0) {
e.inputEvent = INPUT_BROKER_ANYKEY; // REQUIRED
e.kbchar = 0x09; // TAB
shift = 0; // reset shift after TAB
} else {
e.inputEvent = INPUT_BROKER_LEFT;
}
e.inputEvent = INPUT_BROKER_UP;
} else if (!(shiftRegister2 & (1 << 2))) {
if (shift > 0) {
e.inputEvent = INPUT_BROKER_ANYKEY; // REQUIRED
e.kbchar = 0x09; // TAB
shift = 0; // reset shift after TAB
} else {
e.inputEvent = INPUT_BROKER_RIGHT;
}
e.inputEvent = INPUT_BROKER_RIGHT;
e.kbchar = 0;
} else if (!(shiftRegister2 & (1 << 1))) {
e.inputEvent = INPUT_BROKER_SELECT;

View File

@@ -8,8 +8,6 @@ class SerialKeyboard : public Observable<const InputEvent *>, public concurrency
public:
explicit SerialKeyboard(const char *name);
uint8_t getShift() const { return shift; }
protected:
virtual int32_t runOnce() override;
void erase();
@@ -24,6 +22,4 @@ class SerialKeyboard : public Observable<const InputEvent *>, public concurrency
int lastKeyPressed = 13;
int quickPress = 0;
unsigned long lastPressTime = 0;
};
extern SerialKeyboard *globalSerialKeyboard;
};

View File

@@ -57,7 +57,7 @@ static unsigned char TDeckProTapMap[_TCA8418_NUM_KEYS][5] = {
{0x00, 0x00, 0x00},
{0x00, 0x00, 0x00},
{0x20, 0x00, 0x00},
{0x00, 0x00, '0'},
{0x00, 0x00, 0x00},
{0x00, 0x00, 0x00} // R_Shift, sym, space, mic, L_Shift
};

View File

@@ -7,8 +7,6 @@
#include "TDeckProKeyboard.h"
#elif defined(T_LORA_PAGER)
#include "TLoraPagerKeyboard.h"
#elif defined(HACKADAY_COMMUNICATOR)
#include "HackadayCommunicatorKeyboard.h"
#else
#include "TCA8418Keyboard.h"
#endif
@@ -22,8 +20,6 @@ KbI2cBase::KbI2cBase(const char *name)
TCAKeyboard(*(new TDeckProKeyboard()))
#elif defined(T_LORA_PAGER)
TCAKeyboard(*(new TLoraPagerKeyboard()))
#elif defined(HACKADAY_COMMUNICATOR)
TCAKeyboard(*(new HackadayCommunicatorKeyboard()))
#else
TCAKeyboard(*(new TCA8418Keyboard()))
#endif
@@ -332,7 +328,7 @@ int32_t KbI2cBase::runOnce()
break;
}
if (e.inputEvent != INPUT_BROKER_NONE) {
// LOG_DEBUG("TCA8418 Notifying: %i Char: %c", e.inputEvent, e.kbchar);
LOG_DEBUG("TCA8418 Notifying: %i Char: %c", e.inputEvent, e.kbchar);
this->notifyObservers(&e);
}
TCAKeyboard.trigger();

View File

@@ -394,10 +394,7 @@ void setup()
io.pinMode(EXPANDS_GPIO_EN, OUTPUT);
io.digitalWrite(EXPANDS_GPIO_EN, HIGH);
io.pinMode(EXPANDS_SD_PULLEN, INPUT);
#elif defined(HACKADAY_COMMUNICATOR)
pinMode(KB_INT, INPUT);
#endif
concurrency::hasBeenSetup = true;
#if ARCH_PORTDUINO
SPISettings spiSettings(portduino_config.spiSpeed, MSBFIRST, SPI_MODE0);
@@ -480,10 +477,6 @@ void setup()
#ifdef RESET_OLED
pinMode(RESET_OLED, OUTPUT);
digitalWrite(RESET_OLED, 1);
delay(2);
digitalWrite(RESET_OLED, 0);
delay(10);
digitalWrite(RESET_OLED, 1);
#endif
#ifdef SENSOR_POWER_CTRL_PIN
@@ -881,7 +874,7 @@ void setup()
#if defined(ST7701_CS) || defined(ST7735_CS) || defined(USE_EINK) || defined(ILI9341_DRIVER) || defined(ILI9342_DRIVER) || \
defined(ST7789_CS) || defined(HX8357_CS) || defined(USE_ST7789) || defined(ILI9488_CS) || defined(ST7796_CS) || \
defined(USE_SPISSD1306) || defined(USE_ST7796) || defined(HACKADAY_COMMUNICATOR)
defined(USE_SPISSD1306)
screen = new graphics::Screen(screen_found, screen_model, screen_geometry);
#elif defined(ARCH_PORTDUINO)
if ((screen_found.port != ScanI2C::I2CPort::NO_I2C || portduino_config.displayPanel) &&
@@ -966,7 +959,6 @@ void setup()
i2cScanner.reset();
#endif
#if !defined(MESHTASTIC_EXCLUDE_PKI)
// warn the user about a low entropy key
if (nodeDB->keyIsLowEntropy && !nodeDB->hasWarned) {
LOG_WARN(LOW_ENTROPY_WARNING);
@@ -977,7 +969,6 @@ void setup()
service->sendClientNotification(cn);
nodeDB->hasWarned = true;
}
#endif
// buttons are now inputBroker, so have to come after setupModules
#if HAS_BUTTON
@@ -1158,7 +1149,7 @@ void setup()
// the current region name)
#if defined(ST7701_CS) || defined(ST7735_CS) || defined(USE_EINK) || defined(ILI9341_DRIVER) || defined(ILI9342_DRIVER) || \
defined(ST7789_CS) || defined(HX8357_CS) || defined(USE_ST7789) || defined(ILI9488_CS) || defined(ST7796_CS) || \
defined(USE_ST7796) || defined(USE_SPISSD1306) || defined(HACKADAY_COMMUNICATOR)
defined(USE_SPISSD1306)
if (screen)
screen->setup();
#elif defined(ARCH_PORTDUINO)
@@ -1410,7 +1401,7 @@ void setup()
#endif
// check if the radio chip matches the selected region
if ((config.lora.region == meshtastic_Config_LoRaConfig_RegionCode_LORA_24) && rIf && (!rIf->wideLora())) {
if ((config.lora.region == meshtastic_Config_LoRaConfig_RegionCode_LORA_24) && (!rIf->wideLora())) {
LOG_WARN("LoRa chip does not support 2.4GHz. Revert to unset");
config.lora.region = meshtastic_Config_LoRaConfig_RegionCode_UNSET;
nodeDB->saveToDisk(SEGMENT_CONFIG);

View File

@@ -92,10 +92,10 @@ bool CryptoEngine::encryptCurve25519(uint32_t toNode, uint32_t fromNode, meshtas
LOG_DEBUG("Node %d or their public_key not found", toNode);
return false;
}
if (!setDHPublicKey(remotePublic.bytes)) {
if (!crypto->setDHPublicKey(remotePublic.bytes)) {
return false;
}
hash(shared_key, 32);
crypto->hash(shared_key, 32);
initNonce(fromNode, packetNum, extraNonceTmp);
// Calculate the shared secret with the destination node and encrypt
@@ -134,10 +134,10 @@ bool CryptoEngine::decryptCurve25519(uint32_t fromNode, meshtastic_UserLite_publ
}
// Calculate the shared secret with the sending node and decrypt
if (!setDHPublicKey(remotePublic.bytes)) {
if (!crypto->setDHPublicKey(remotePublic.bytes)) {
return false;
}
hash(shared_key, 32);
crypto->hash(shared_key, 32);
initNonce(fromNode, packetNum, extraNonce);
printBytes("Attempt decrypt with nonce: ", nonce, 13);

View File

@@ -29,7 +29,6 @@
#else
#define default_ringtone_nag_secs 15
#endif
#define default_network_ipv6_enabled false
#define default_mqtt_address "mqtt.meshtastic.org"
#define default_mqtt_username "meshdev"
@@ -47,17 +46,21 @@ class Default
static uint32_t getConfiguredOrDefaultMs(uint32_t configuredInterval);
static uint32_t getConfiguredOrDefaultMs(uint32_t configuredInterval, uint32_t defaultInterval);
static uint32_t getConfiguredOrDefault(uint32_t configured, uint32_t defaultValue);
// Note: numOnlineNodes uses uint32_t to match the public API and allow flexibility,
// even though internal node counts use uint16_t (max 65535 nodes)
static uint32_t getConfiguredOrDefaultMsScaled(uint32_t configured, uint32_t defaultValue, uint32_t numOnlineNodes);
static uint8_t getConfiguredOrDefaultHopLimit(uint8_t configured);
static uint32_t getConfiguredOrMinimumValue(uint32_t configured, uint32_t minValue);
private:
// Note: Kept as uint32_t to match the public API parameter type
static float congestionScalingCoefficient(uint32_t numOnlineNodes)
static float congestionScalingCoefficient(int numOnlineNodes)
{
if (numOnlineNodes <= 40) {
// Increase frequency of broadcasts for small networks regardless of preset
if (numOnlineNodes <= 10) {
return 0.6;
} else if (numOnlineNodes <= 20) {
return 0.7;
} else if (numOnlineNodes <= 30) {
return 0.8;
} else if (numOnlineNodes <= 40) {
return 1.0;
} else {
float throttlingFactor = 0.075;

Some files were not shown because too many files have changed in this diff Show More