Compare commits

..
Author SHA1 Message Date
GUVWAF 24458a73d6 Add missing hops in traceroute as "unkown" (#4056)
E.g. in case a node couldn't decrypt the packet
2024-06-09 16:03:53 -05:00
Michael Gjelsø a2fb3d23a1 Radio Master 900 Bandit Nano Power output interpolation (#4057)
* DAC and DB values based on dBm using interpolation

* Moved getDACandDB funtion

Moved getDACandDB funtion up so it won't conflict with RF95_MAX_POWER

* Added DAC output to LOG_INFO

Added DAC output to LOG_INFO

* Make Trunk Happy
2024-06-09 16:03:39 -05:00
GUVWAF 237944aaf0 Avoid assert on receiving undecryptable packet (#4059)
* Send NAK on primary if original packet couldn't be decoded

* Add checks for `isDecoded` when accessing `decoded`

* Channel index should be of original packet, not of newly allocated NAK
2024-06-09 16:02:52 -05:00
Thomas Göttgens 1d98e48bab Update main_matrix.yml 2024-06-09 16:33:50 +02:00
Ben Meadors 01a214aa59 Add continues on failing docker steps 2024-06-09 08:32:31 -05:00
Ben Meadors 4da3f202e5 Roll-back to v2 2024-06-09 07:55:45 -05:00
Ben Meadors 2f99a8dbb8 Try latest version 2024-06-09 07:40:57 -05:00
Ben Meadors f59cbc8ffb Add firmware repo level secret 2024-06-09 07:19:25 -05:00
Thomas Göttgens 095887de40 do the docker dance 2024-06-09 13:00:06 +02:00
Thomas Göttgens 46b8e2a850 fix binary location 2024-06-09 12:56:44 +02:00
Thomas Göttgens 27ad3da0ac reinstate after checks and hope the coffee kicks in 2024-06-09 12:37:23 +02:00
Thomas Göttgens 2335352fbe fix native build 2024-06-09 12:30:47 +02:00
Ben Meadors 2fa55b7b6f Remove bandit from extra 2024-06-08 09:44:13 -05:00
todd-herbert da5bca31ed Triple-press not disabling GPS (#4041)
* Replace (bool) isAwake with an enum, to track standby states

* Tidy-up, extra logging

* Rename enum values

* Reorder GPSPowerState enum
Possibly more intuitive when reading logs

* Avoid lego comments
https://github.com/meshtastic/firmware/pull/4041/files/de22c57298535f8b94154bffbef64a44af09648c#r1627334779
2024-06-07 09:41:46 -05:00
Thomas Göttgens 8a4e91e848 Merge pull request #4048 from Jorropo/improve-eu-868-docs
fix dead link in EU_868 documentation
2024-06-07 16:35:11 +02:00
Thomas Göttgens d91fdc5ea7 Merge pull request #4053 from meshtastic/xiao-ble
Compile without toolchain patching
2024-06-07 16:34:41 +02:00
Thomas Göttgens 355c610824 Compile without toolchain patching 2024-06-07 15:36:48 +02:00
Thomas Göttgens a52db85ebe fix base setup 2024-06-07 15:36:31 +02:00
todd-herbert 338244de32 Wake screen on first press (#4052) 2024-06-07 07:28:29 -05:00
Thomas Göttgens 90d45f24fd Merge pull request #4049 from Talie5in/minimized_build_fixes
Minimized build fixes
2024-06-06 20:13:32 +02:00
Talie5in d09da96780 Fix indentation oopsie 2024-06-06 23:57:44 +09:30
Talie5in a5c96a29d5 Fix missing IFNDEF and IFDEF in main-esp32.cpp when EXCLUDE_WIFI is defined.
Moved IFDEF HAS_NETWORK to beginning of MQTT:runOnce (to catch when EXCLUDE_WIFI is defined)
2024-06-06 22:52:11 +09:30
Talie5in 1f9f885aca If EXCUDE_MQTT Defined, skip reconnecting MQTT in WiFiAPClient and dont check status of is_mqtt_connected 2024-06-06 22:31:10 +09:30
Talie5in 646b252786 Include PositionModule if EXCLUDE_GPS defined (requied by AdminModule) 2024-06-06 22:19:40 +09:30
Jorropo d82d9f5ef1 fix dead link in EU_868 documentation
See https://discord.com/channels/867578229534359593/871553168369148024/1248026118276255745.
2024-06-05 23:32:59 +02:00
Thomas Göttgens 96b286cd48 release x86_64 deb 2024-06-05 17:50:58 +02:00
Thomas Göttgens 7874ebc568 Update package_amd64.yml 2024-06-05 17:24:55 +02:00
Thomas Göttgens fb3c141231 update package index 2024-06-05 17:04:22 +02:00
Thomas Göttgens f1906c38f1 sudo make me a sandwich 2024-06-05 16:37:12 +02:00
Thomas Göttgens 14b7c5b6ef Create build_native.yml 2024-06-05 16:34:30 +02:00
Thomas Göttgens d8775d94e3 try harder dude 2024-06-05 16:29:40 +02:00
Thomas Göttgens 2cc5598f89 Try building a deb for native 2024-06-05 16:27:46 +02:00
Thomas Göttgens fbc8f6c03b package x86_64 meshtasticd 2024-06-05 15:08:23 +02:00
jcyrio d0ca616c19 typo: 'our our' to 'of our' (#4037) 2024-06-04 16:02:43 -05:00
Thomas Göttgens c37316e723 use correct hardware tag for tracker and sdk (#4036) 2024-06-04 08:44:48 -05:00
github-actions[bot] 67b67a481f [create-pull-request] automated change (#4035) 2024-06-04 08:34:38 -05:00
Thomas Göttgens 181f03cb95 tryfix random values (#4034) 2024-06-04 08:14:27 -05:00
Ben Meadors 9632e4c405 Add missing excludes to environmental sensors (#4033)
* DF Robot Lark weather station support

* Missed it

* I am a man of const char sorrow...

* Strang

* Use our fork

* Add excludes
2024-06-04 07:04:25 -05:00
Ben Meadors a218c6fb4d DFRobot Lark weather station support (#4032)
* DF Robot Lark weather station support

* Missed it

* I am a man of const char sorrow...

* Strang

* Use our fork
2024-06-03 21:50:28 -05:00
Thomas GöttgensandBen Meadors b43c7c0f23 LR1110 support (#3013)
* DOES NOT WORK

* trunk

* DOES NOT WORK

* trunk

* DOES NOT WORK

* trunk

* WIP: LR11x0 non functional interface code. Please don't expect a working firmware out of this! I don't know what i am doing! :-)

* trunk fmt

* use canon toolchain

* update and fix radiolib dependency

* Switch Radiolib back to GIT checkout

* enable tcxo and fix startReceive

* progress

* Correct midjudgement on scope of build defines.

* - enable peripheral power rail during startup init
- fix portduino builds

* add tracker pinout variant

* update to radiolib 6.6.0 API (aka: godmode is not for mere mortals)

* tracker is not so 'extra' any more

---------

Co-authored-by: Ben Meadors <benmmeadors@gmail.com>
2024-06-03 16:04:40 -05:00
github-actions[bot]andthebentern 7cbfe7aa54 [create-pull-request] automated change (#4029)
Co-authored-by: thebentern <9000580+thebentern@users.noreply.github.com>
2024-06-03 15:59:39 -05:00
Thomas Göttgens 86f2000382 Merge pull request #4025 from meshtastic/bme-680
tryfix bme some more
2024-06-03 17:25:27 +02:00
Thomas Göttgens 79333c85a3 tryfix bme some more 2024-06-03 16:34:19 +02:00
Thomas Göttgens 4a93e4d85e Merge pull request #4023 from meshtastic/radiolib
update Radiolib to 6.6.0
2024-06-03 14:25:03 +02:00
Thomas Göttgens b551c8b592 update Radiolib to 6.6.0 2024-06-03 14:00:58 +02:00
Ben Meadors 06c635eca0 MLX90632 IR temperature sensor support (#4019) 2024-06-02 09:38:28 -05:00
Ben Meadors 97a5abbc82 TI OPT3001 light sensor support (#4015)
* TI OPT3001 light sensor support

* Added register interrogation to deconflict with SHT sensors on same address
2024-06-02 07:39:08 -05:00
Manuel 2723ae6e9b fix crash during reset nodedb (#4017) 2024-06-02 07:38:20 -05:00
Ben Meadors 6ce2fdc1c8 Add TSL2591 sensor support (#4014) 2024-06-01 20:21:39 -05:00
github-actions[bot]andthebentern 9a855c0b6f [create-pull-request] automated change (#4011)
Co-authored-by: thebentern <9000580+thebentern@users.noreply.github.com>
2024-06-01 07:46:01 -05:00
Thomas Göttgens 2740a56944 tryfix: init change for BME680 (#3965) 2024-05-31 19:46:42 -05:00
fzelliniandBen Meadors ffff2a03fc dragino trackerd (#4002)
* added AHTx0 sensor

* AHT10 definition in protobuf

* AHT10 definition in protobuf

* protobufs

* Management of AHT20+BMP280 module

* missing newline in log

* missing newline in log

* dragino trackerd support

* dragino trackerd support

* revert back .gitmodules

* reverted gitignore

* merged telemetry.pb.h

* merged telemetry.pb.h

* removed extra script, now bin version works

* reverted

---------

Co-authored-by: Ben Meadors <benmmeadors@gmail.com>
2024-05-31 13:05:40 -05:00
Ben Meadors 4fa2427b8c Trunk variants 2024-05-31 11:18:06 -05:00
fzelliniandBen Meadors eddda3ca43 added AHTx0 sensor (#3977)
* added AHTx0 sensor

* AHT10 definition in protobuf

* AHT10 definition in protobuf

* protobufs

* Management of AHT20+BMP280 module

* missing newline in log

* missing newline in log

* reverted

* reverted .gitignore

---------

Co-authored-by: Ben Meadors <benmmeadors@gmail.com>
2024-05-31 11:17:53 -05:00
Ben Meadors 17142f8778 Add support for RadioMaster Bandit Nano (#4005)
* Add support for RadioMaster Bandit Nano

* Add fan to init and sleep
2024-05-31 10:56:04 -05:00
jonagnewandBen Meadors 953aa4d091 Tracker v1.1 - fix pin 3 description in variant.h (#3990)
Schematic shows the following:
Pin 3 is Vext on v1.1 - HIGH enables LDO for Vext rail which goes to:
 GPS UC6580: GPS V_DET(8), VDD_IO(7), DCDC_IN(21), pulls up RESETN(17), D_SEL(33) and BOOT_MODE(34) through 10kR 
GPS LNA SW7125DE: VCC(4), pulls up SHDN(5) through 10kR
OLED: VDD, LEDA (through diode)

Co-authored-by: Ben Meadors <benmmeadors@gmail.com>
2024-05-31 10:55:20 -05:00
Aaron.Lee b9edc7563b Update the Heltec board battery level read accuracy. (#3955)
* Update variant.h

Update the Heltec board battery voltage read parameter.

* Update variant.h

Update the Heltec board battery voltage read parameter.

* Update variant.h

Update the Heltec board battery voltage read parameter.

* Update variant.h

Update the Heltec board battery voltage read parameter.
2024-05-31 10:55:05 -05:00
github-actions[bot]andthebentern c88278724c [create-pull-request] automated change (#4003)
Co-authored-by: thebentern <9000580+thebentern@users.noreply.github.com>
2024-05-31 07:15:41 -05:00
Jonathan Bennett 54bccb898e Run tzset() and localtime() in getTZOffset() to ensure proper timezone offset (#3999)
* Run tzset() and localtime() in getTZOffset() to ensure proper timezone offset

* Try #2 to fix timezone/DST
2024-05-31 07:15:16 -05:00
Jonathan Bennett 8d90c496d0 Don't send potentially bogus timestamps with fixed location (#4001) 2024-05-31 07:14:33 -05:00
Ben Meadors 10e3040494 Add support for to_callsign on GeoChats for ATAK (#3996) 2024-05-30 18:49:08 -05:00
MikeandBen Meadors f138eaa970 Fix original esp32 boot init panic (#3985)
Co-authored-by: Ben Meadors <benmmeadors@gmail.com>
2024-05-30 10:59:10 -05:00
github-actions[bot]andthebentern cd8a7e44a8 [create-pull-request] automated change (#3992)
Co-authored-by: thebentern <9000580+thebentern@users.noreply.github.com>
2024-05-30 09:08:32 -05:00
Ben Meadors 0b48663cbc Don't alloc NodeInfo replies when channel utilization is > 40% (#3991)
* Don't alloc NodeInfo replies when channel utilization is > 40%

* Commit

* Logs
2024-05-30 08:49:01 -05:00
Ben Meadors af9d825266 Send own node-info earlier and move others to the end of want-config flow (#3949)
* Send own node-info earlier and move others to the end of want-config flow

* Special nonce skips other nodeinfos

* Missed it
2024-05-28 19:25:19 -05:00
Neil Hao 038413f46f User experience improvement - app battery icon (#3979)
* 'app_battery_icon'

* Undo VS automatic modifications to this file

* 'app_battery_icon_2'
2024-05-28 06:30:15 -05:00
andrew-moroz 77cf5c6200 Fix time updates from client device and potentially incorrect UI frame receiving 'toggle watch face' button tap (#3974) 2024-05-26 07:04:31 -05:00
Mike aa33ad1d58 Fix memory leak when there's no display (#3972) 2024-05-26 06:42:44 -05:00
Jonathan Bennett 34553c9714 Bump portduino to pick up improvements to reboot() (#3975) 2024-05-26 06:42:23 -05:00
capslockapocalypseandBen Meadors 2233507667 Added "Hops away" on display (#3934)
* Added "Hops away" on display

Added in logic that displays "hops away" instead of signal strength when the node is more than 0 hops away.

* Added comment

* Update extensions.json to same as master

* attempt 2 at reverting extensions JSON

* Attempt 3 at getting extensions right

* Take 4. should be reverting to before my edits

---------

Co-authored-by: Ben Meadors <benmmeadors@gmail.com>
2024-05-24 08:08:21 -05:00
Thomas Göttgens 9a38a4b024 Merge pull request #3966 from andrew-moroz/t-watch-fix
t-watch-fix: Fully insulate T-Watch free text updates from other hardware platforms
2024-05-24 08:57:37 +02:00
Andrew Moroz 1d288414a5 t-watch-fix: Fully insulate T-Watch free text updates from other hardware platforms 2024-05-24 00:02:03 -04:00
andrew-morozandBen Meadors 2f9dc813d3 t-watch-updates: Add canned message free text via touch keyboard and watch face frames to T-Watch S3 (#3941)
Co-authored-by: Ben Meadors <benmmeadors@gmail.com>
2024-05-23 07:21:27 -05:00
github-actions[bot]andthebentern 1a253dccc3 [create-pull-request] automated change (#3964)
Co-authored-by: thebentern <9000580+thebentern@users.noreply.github.com>
2024-05-23 07:20:13 -05:00
Ben Meadors 7d873eb06b Add exclude emoji macro 2024-05-22 20:40:26 -05:00
868meshbotandBen Meadors 1631462db1 Oled screen emojis (#3940)
* Update images.h

Add some fun emojis

* Update Screen.cpp

Update Screen.cpp to display single emojis on the OLED of devices, if a single known emoji is detected

* Update images.h

add ? ! fog emojis

* Update Screen.cpp

add logic for new emojis

* Update Screen.cpp

correct formatting

* Update images.h

correct formatting

* Update Screen.cpp

change formatting via trunk application

* Update images.h

change formatting based on trunk application

---------

Co-authored-by: Ben Meadors <benmmeadors@gmail.com>
2024-05-22 19:28:30 -05:00
Jonathan Bennett 7bcb8f1fee Portduino: Catch the keyboard power button and initiate poweroff (#3953) 2024-05-22 07:54:06 -05:00
MikeandBen Meadors 0c9da9aec7 Update platformio/espressif32 to the latest 6.7.0 (#3899)
* Bump platfomio/espressif32 version to latest 6.7.0

* Fix deprecated constants

* Remove pin defs already defined by the framework

* ESP_EXT1_WAKEUP_ALL_LOW is deprecated for any target except esp32

* Enable LTO and use newlib nano flavor

* Make trunk happy

* Respect build_unflags of base env

* Recover float printfing

* Disable BLE_SM_PAIR_AUTHREQ_SC

* Distribute BLE_SM_PAIR_KEY_DIST_ID too

---------

Co-authored-by: Ben Meadors <benmmeadors@gmail.com>
2024-05-21 21:02:09 -05:00
todd-herbert a12b9922ed Screen changes from time deltas to timestamps after 15 minutes (#3935)
* E-Ink displays sometimes use timestamp instead of delta

* Allow users to disable E-Ink screensaver

* Clarify variable's purpose

* Operator order oopsie

* Picky print problem

* Implement for all display, not just E-Ink

* Align "unknown age" behavior with existing code

* One more use of timestamp, if screen is wide enough
2024-05-21 16:00:04 -05:00
todd-herbert cdf86f4166 Change NRF_APM USB detection code (#3939) 2024-05-21 15:58:05 -05:00
github-actions[bot]andjp-bennett 040b851615 [create-pull-request] automated change (#3950)
Co-authored-by: jp-bennett <5630967+jp-bennett@users.noreply.github.com>
2024-05-21 14:40:31 -05:00
154 changed files with 12755 additions and 621 deletions
+3 -15
View File
@@ -11,23 +11,11 @@ runs:
ref: ${{github.event.pull_request.head.ref}}
repository: ${{github.event.pull_request.head.repo.full_name}}
- name: Install cppcheck
- name: Install dependencies
shell: bash
run: |
sudo apt-get install -y cppcheck
- name: Install libbluetooth
shell: bash
run: |
sudo apt-get install -y libbluetooth-dev
- name: Install libgpiod
shell: bash
run: |
sudo apt-get install -y libgpiod-dev
- name: Install libyaml-cpp
shell: bash
run: |
sudo apt-get install -y libyaml-cpp-dev
sudo apt-get -y update
sudo apt-get install -y cppcheck libbluetooth-dev libgpiod-dev libyaml-cpp-dev
- name: Setup Python
uses: actions/setup-python@v5
+85
View File
@@ -0,0 +1,85 @@
name: Build Native
on: workflow_call
permissions:
contents: write
packages: write
jobs:
build-native:
runs-on: ubuntu-latest
steps:
- name: Install libbluetooth
shell: bash
run: |
sudo apt-get update
sudo apt-get install -y libbluetooth-dev libgpiod-dev libyaml-cpp-dev openssl libssl-dev libulfius-dev liborcania-dev
- name: Checkout code
uses: actions/checkout@v4
with:
submodules: recursive
ref: ${{github.event.pull_request.head.ref}}
repository: ${{github.event.pull_request.head.repo.full_name}}
- name: Upgrade python tools
shell: bash
run: |
python -m pip install --upgrade pip
pip install -U platformio adafruit-nrfutil
pip install -U meshtastic --pre
- name: Upgrade platformio
shell: bash
run: |
pio upgrade
- name: Build Native
run: bin/build-native.sh
- name: Get release version string
run: echo "version=$(./bin/buildinfo.py long)" >> $GITHUB_OUTPUT
id: version
- name: Store binaries as an artifact
uses: actions/upload-artifact@v4
with:
name: firmware-native-${{ steps.version.outputs.version }}.zip
overwrite: true
path: |
release/meshtasticd_linux_x86_64
bin/config-dist.yaml
- name: Docker login
if: ${{ github.event_name != 'pull_request_target' && github.event_name != 'pull_request' }}
uses: docker/login-action@v3
continue-on-error: true # FIXME: Failing docker login auth
with:
username: meshtastic
password: ${{ secrets.DOCKER_FIRMWARE_TOKEN }}
- name: Docker setup
if: ${{ github.event_name != 'pull_request_target' && github.event_name != 'pull_request' }}
continue-on-error: true # FIXME: Failing docker login auth
uses: docker/setup-buildx-action@v3
- name: Docker build and push tagged versions
if: ${{ github.event_name == 'workflow_dispatch' }}
continue-on-error: true # FIXME: Failing docker login auth
uses: docker/build-push-action@v5
with:
context: .
file: ./Dockerfile
push: true
tags: meshtastic/device-simulator:${{ steps.version.outputs.version }}
- name: Docker build and push
if: ${{ github.ref == 'refs/heads/master' && github.event_name != 'pull_request_target' && github.event_name != 'pull_request' }}
continue-on-error: true # FIXME: Failing docker login auth
uses: docker/build-push-action@v5
with:
context: .
file: ./Dockerfile
push: true
tags: meshtastic/device-simulator:latest
+14 -63
View File
@@ -109,67 +109,8 @@ jobs:
package-raspbian-armv7l:
uses: ./.github/workflows/package_raspbian_armv7l.yml
build-native:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Build base
id: base
uses: ./.github/actions/setup-base
# We now run integration test before other build steps (to quickly see runtime failures)
#- name: Build for native
# run: platformio run -e native
#- name: Integration test
# run: |
#.pio/build/native/program
#& sleep 20 # 5 seconds was not enough
#echo "Simulator started, launching python test..."
#python3 -c 'from meshtastic.test import testSimulator; testSimulator()'
- name: Build Native
run: bin/build-native.sh
- name: Get release version string
run: echo "version=$(./bin/buildinfo.py long)" >> $GITHUB_OUTPUT
id: version
- name: Store binaries as an artifact
uses: actions/upload-artifact@v4
with:
name: firmware-native-${{ steps.version.outputs.version }}.zip
overwrite: true
path: |
release/device-*.sh
release/device-*.bat
- name: Docker login
if: ${{ github.event_name != 'pull_request_target' && github.event_name != 'pull_request' }}
uses: docker/login-action@v3
with:
username: meshtastic
password: ${{ secrets.DOCKER_TOKEN }}
- name: Docker setup
if: ${{ github.event_name != 'pull_request_target' && github.event_name != 'pull_request' }}
uses: docker/setup-buildx-action@v3
- name: Docker build and push tagged versions
if: ${{ github.event_name == 'workflow_dispatch' }}
uses: docker/build-push-action@v5
with:
context: .
file: ./Dockerfile
push: true
tags: meshtastic/device-simulator:${{ steps.version.outputs.version }}
- name: Docker build and push
if: ${{ github.ref == 'refs/heads/master' && github.event_name != 'pull_request_target' && github.event_name != 'pull_request' }}
uses: docker/build-push-action@v5
with:
context: .
file: ./Dockerfile
push: true
tags: meshtastic/device-simulator:latest
package-native:
uses: ./.github/workflows/package_amd64.yml
after-checks:
runs-on: ubuntu-latest
@@ -192,10 +133,10 @@ jobs:
build-esp32-s3,
build-esp32-c3,
build-nrf52,
build-native,
build-rpi2040,
package-raspbian,
package-raspbian-armv7l,
package-native
]
steps:
- name: Checkout code
@@ -217,7 +158,7 @@ jobs:
id: version
- name: Move files up
run: mv -b -t ./ ./release/meshtasticd_linux_aarch64 ./release/meshtasticd_linux_armv7l ./bin/config-dist.yaml
run: mv -b -t ./ ./release/meshtasticd_linux_* ./bin/config-dist.yaml ./bin/device-*.sh ./bin/device-*.bat
- name: Repackage in single firmware zip
uses: actions/upload-artifact@v4
@@ -381,6 +322,16 @@ jobs:
asset_name: meshtasticd_${{ steps.version.outputs.version }}_armhf.deb
asset_content_type: application/vnd.debian.binary-package
- name: Add raspbian amd64 .deb
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ github.token }}
with:
upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_path: ./output/meshtasticd_${{ steps.version.outputs.version }}_amd64.deb
asset_name: meshtasticd_${{ steps.version.outputs.version }}_amd64.deb
asset_content_type: application/vnd.debian.binary-package
- name: Bump version.properties
run: >-
bin/bump_version.py
+78
View File
@@ -0,0 +1,78 @@
name: Package Native
on:
workflow_call:
workflow_dispatch:
permissions:
contents: write
packages: write
jobs:
build-native:
uses: ./.github/workflows/build_native.yml
package-native:
runs-on: ubuntu-latest
needs: build-native
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
submodules: recursive
ref: ${{github.event.pull_request.head.ref}}
repository: ${{github.event.pull_request.head.repo.full_name}}
- name: Pull web ui
uses: dsaltares/fetch-gh-release-asset@master
with:
repo: meshtastic/web
file: build.tar
target: build.tar
token: ${{ secrets.GITHUB_TOKEN }}
- name: Get release version string
run: echo "version=$(./bin/buildinfo.py long)" >> $GITHUB_OUTPUT
id: version
- name: Download artifacts
uses: actions/download-artifact@v4
with:
name: firmware-native-${{ steps.version.outputs.version }}.zip
merge-multiple: true
- name: Display structure of downloaded files
run: ls -R
- name: build .debpkg
run: |
mkdir -p .debpkg/DEBIAN
mkdir -p .debpkg/usr/share/doc/meshtasticd/web
mkdir -p .debpkg/usr/sbin
mkdir -p .debpkg/etc/meshtasticd
mkdir -p .debpkg/usr/lib/systemd/system/
tar -xf build.tar -C .debpkg/usr/share/doc/meshtasticd/web
gunzip .debpkg/usr/share/doc/meshtasticd/web/*.gz
cp release/meshtasticd_linux_x86_64 .debpkg/usr/sbin/meshtasticd
cp bin/config-dist.yaml .debpkg/etc/meshtasticd/config.yaml
chmod +x .debpkg/usr/sbin/meshtasticd
cp bin/meshtasticd.service .debpkg/usr/lib/systemd/system/meshtasticd.service
echo "/etc/meshtasticd/config.yaml" > .debpkg/DEBIAN/conffiles
chmod +x .debpkg/DEBIAN/conffiles
- uses: jiro4989/build-deb-action@v3
with:
package: meshtasticd
package_root: .debpkg
maintainer: Jonathan Bennett
version: ${{ steps.version.outputs.version }} # refs/tags/v*.*.*
arch: amd64
depends: libyaml-cpp0.7, openssl, libulfius2.7
desc: Native Linux Meshtastic binary.
- uses: actions/upload-artifact@v4
with:
name: meshtasticd_${{ steps.version.outputs.version }}_amd64.deb
overwrite: true
path: |
./*.deb
+1 -1
View File
@@ -6,4 +6,4 @@
"platformio.platformio-ide",
"trunk.io"
],
}
}
+4 -1
View File
@@ -1,7 +1,8 @@
; Common settings for ESP targes, mixin with extends = esp32_base
[esp32_base]
extends = arduino_base
platform = platformio/espressif32@6.3.2 # This is a temporary fix to the S3-based devices bluetooth issues until we can determine what within ESP-IDF changed and can develop a suitable patch.
custom_esp32_kind = esp32
platform = platformio/espressif32@6.7.0
build_src_filter =
${arduino_base.build_src_filter} -<platform/nrf52/> -<platform/stm32wl> -<platform/rp2040> -<mesh/eth/> -<mesh/raspihttp>
@@ -15,8 +16,10 @@ board_build.filesystem = littlefs
# Remove -DMYNEWT_VAL_BLE_HS_LOG_LVL=LOG_LEVEL_CRITICAL for low level BLE logging.
# See library directory for BLE logging possible values: .pio/libdeps/tbeam/NimBLE-Arduino/src/log_common/log_common.h
# This overrides the BLE logging default of LOG_LEVEL_INFO (1) from: .pio/libdeps/tbeam/NimBLE-Arduino/src/esp_nimble_cfg.h
build_unflags = -fno-lto
build_flags =
${arduino_base.build_flags}
-flto
-Wall
-Wextra
-Isrc/platform/esp32
+1
View File
@@ -1,5 +1,6 @@
[esp32c3_base]
extends = esp32_base
custom_esp32_kind = esp32c3
monitor_speed = 115200
monitor_filters = esp32_c3_exception_decoder
+1
View File
@@ -1,5 +1,6 @@
[esp32s2_base]
extends = esp32_base
custom_esp32_kind = esp32s2
build_src_filter =
${esp32_base.build_src_filter} - <libpax/> -<nimble/> -<mesh/raspihttp>
+1
View File
@@ -1,5 +1,6 @@
[esp32s3_base]
extends = esp32_base
custom_esp32_kind = esp32s3
monitor_speed = 115200
+1 -1
View File
@@ -1,6 +1,6 @@
; The Portduino based sim environment on top of any host OS, all hardware will be simulated
[portduino_base]
platform = https://github.com/meshtastic/platform-native.git#f5ec3c031b0fcd89c0523de9e43eef3a92d59292
platform = https://github.com/meshtastic/platform-native.git#ad8112adf82ce1f5b917092cf32be07a077801a0
framework = arduino
build_src_filter =
+17
View File
@@ -1,3 +1,5 @@
# trunk-ignore-all(ruff/F821)
# trunk-ignore-all(flake8/F821): For SConstruct imports
import sys
from os.path import join
@@ -61,6 +63,21 @@ if platform.name == "espressif32":
env.AddPostAction("$BUILD_DIR/${PROGNAME}.bin", esp32_create_combined_bin)
esp32_kind = env.GetProjectOption("custom_esp32_kind")
if esp32_kind == "esp32":
# Free up some IRAM by removing auxiliary SPI flash chip drivers.
# Wrapped stub symbols are defined in src/platform/esp32/iram-quirk.c.
env.Append(
LINKFLAGS=[
"-Wl,--wrap=esp_flash_chip_gd",
"-Wl,--wrap=esp_flash_chip_issi",
"-Wl,--wrap=esp_flash_chip_winbond",
]
)
else:
# For newer ESP32 targets, using newlib nano works better.
env.Append(LINKFLAGS=["--specs=nano.specs", "-u", "_printf_float"])
Import("projenv")
prefsLoc = projenv["PROJECT_DIR"] + "/version.properties"
+58
View File
@@ -0,0 +1,58 @@
{
"build": {
"arduino": {
"ldscript": "nrf52840_s140_v6.ld"
},
"core": "nRF5",
"cpu": "cortex-m4",
"extra_flags": "-DARDUINO_WIO_WM1110 -DNRF52840_XXAA",
"f_cpu": "64000000L",
"hwids": [
["0x239A", "0x8029"],
["0x239A", "0x0029"],
["0x239A", "0x002A"],
["0x239A", "0x802A"]
],
"usb_product": "WIO-BOOT",
"mcu": "nrf52840",
"variant": "Seeed_WIO_WM1110",
"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",
"svd_path": "nrf52840.svd"
},
"frameworks": ["arduino"],
"name": "Seeed WIO WM1110",
"upload": {
"maximum_ram_size": 248832,
"maximum_size": 815104,
"speed": 115200,
"protocol": "nrfutil",
"protocols": [
"jlink",
"nrfjprog",
"nrfutil",
"stlink",
"cmsis-dap",
"blackmagic"
],
"use_1200bps_touch": true,
"require_upload_port": true,
"wait_for_upload_port": true
},
"url": "https://www.seeedstudio.com/Wio-WM1110-Dev-Kit-p-5677.html",
"vendor": "Seeed Studio"
}
+58
View File
@@ -0,0 +1,58 @@
{
"build": {
"arduino": {
"ldscript": "nrf52840_s140_v6.ld"
},
"core": "nRF5",
"cpu": "cortex-m4",
"extra_flags": "-DARDUINO_WIO_WM1110 -DNRF52840_XXAA",
"f_cpu": "64000000L",
"hwids": [
["0x239A", "0x8029"],
["0x239A", "0x0029"],
["0x239A", "0x002A"],
["0x239A", "0x802A"]
],
"usb_product": "WIO-BOOT",
"mcu": "nrf52840",
"variant": "Seeed_WIO_WM1110",
"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",
"svd_path": "nrf52840.svd"
},
"frameworks": ["arduino"],
"name": "Seeed WIO WM1110",
"upload": {
"maximum_ram_size": 248832,
"maximum_size": 815104,
"speed": 115200,
"protocol": "nrfutil",
"protocols": [
"jlink",
"nrfjprog",
"nrfutil",
"stlink",
"cmsis-dap",
"blackmagic"
],
"use_1200bps_touch": true,
"require_upload_port": true,
"wait_for_upload_port": true
},
"url": "https://www.seeedstudio.com/Wio-WM1110-Dev-Kit-p-5677.html",
"vendor": "Seeed Studio"
}
+8 -2
View File
@@ -74,7 +74,7 @@ build_flags = -Wno-missing-field-initializers
monitor_speed = 115200
lib_deps =
jgromes/RadioLib@~6.5.0
jgromes/RadioLib@~6.6.0
https://github.com/meshtastic/esp8266-oled-ssd1306.git#ee628ee6c9588d4c56c9e3da35f0fc9448ad54a8 ; ESP8266_SSD1306
mathertel/OneButton@^2.5.0 ; OneButton library for non-blocking button debounce
https://github.com/meshtastic/arduino-fsm.git#7db3702bf0cfe97b783d6c72595e3f38e0b19159
@@ -130,8 +130,14 @@ lib_deps =
adafruit/Adafruit PM25 AQI Sensor@^1.0.6
adafruit/Adafruit MPU6050@^2.2.4
adafruit/Adafruit LIS3DH@^1.2.4
adafruit/Adafruit AHTX0@^2.0.5
lewisxhe/SensorLib@^0.2.0
adafruit/Adafruit LSM6DS@^4.7.2
mprograms/QMC5883LCompass@^1.2.0
adafruit/Adafruit VEML7700 Library@^2.1.6
adafruit/Adafruit SHT4x Library@^1.0.4
adafruit/Adafruit SHT4x Library@^1.0.4
adafruit/Adafruit TSL2591 Library@^1.4.5
ClosedCube OPT3001@^1.1.2
emotibit/EmotiBit MLX90632@^1.0.8
dfrobot/DFRobot_RTU@^1.0.3
https://github.com/meshtastic/DFRobot_LarkWeatherStation#0e884fc86b7a0b602c7ff3d26b893b997f15c6ac
+2 -2
View File
@@ -232,10 +232,10 @@ void ButtonThread::attachButtonInterrupts()
attachInterrupt(
config.device.button_gpio ? config.device.button_gpio : BUTTON_PIN,
[]() {
BaseType_t higherWake = 0;
mainDelay.interruptFromISR(&higherWake);
ButtonThread::userButton.tick();
runASAP = true;
BaseType_t higherWake = 0;
mainDelay.interruptFromISR(&higherWake);
},
CHANGE);
#endif
+17 -7
View File
@@ -50,7 +50,7 @@ RTC_NOINIT_ATTR uint64_t RTC_reg_b;
esp_adc_cal_characteristics_t *adc_characs = (esp_adc_cal_characteristics_t *)calloc(1, sizeof(esp_adc_cal_characteristics_t));
#ifndef ADC_ATTENUATION
static const adc_atten_t atten = ADC_ATTEN_DB_11;
static const adc_atten_t atten = ADC_ATTEN_DB_12;
#else
static const adc_atten_t atten = ADC_ATTENUATION;
#endif
@@ -555,14 +555,24 @@ void Power::readPowerStatus()
#ifdef NRF_APM // Section of code detects USB power on the RAK4631 and updates the power states. Takes 20 seconds or so to detect
// changes.
static nrfx_power_usb_state_t prev_nrf_usb_state = (nrfx_power_usb_state_t)-1; // -1 so that state detected at boot
nrfx_power_usb_state_t nrf_usb_state = nrfx_power_usbstatus_get();
if (nrf_usb_state == NRFX_POWER_USB_STATE_DISCONNECTED) {
powerFSM.trigger(EVENT_POWER_DISCONNECTED);
NRF_USB = OptFalse;
} else {
powerFSM.trigger(EVENT_POWER_CONNECTED);
NRF_USB = OptTrue;
// If state changed
if (nrf_usb_state != prev_nrf_usb_state) {
// If changed to DISCONNECTED
if (nrf_usb_state == NRFX_POWER_USB_STATE_DISCONNECTED) {
powerFSM.trigger(EVENT_POWER_DISCONNECTED);
NRF_USB = OptFalse;
}
// If changed to CONNECTED / READY
else {
powerFSM.trigger(EVENT_POWER_CONNECTED);
NRF_USB = OptTrue;
}
// Cache the current state
prev_nrf_usb_state = nrf_usb_state;
}
#endif
// Notify any status instances that are observing us
+12 -6
View File
@@ -348,12 +348,18 @@ void PowerFSM_setup()
powerFSM.add_transition(&stateDARK, &stateDARK, EVENT_CONTACT_FROM_PHONE, NULL, "Contact from phone");
powerFSM.add_timed_transition(&stateON, &stateDARK,
Default::getConfiguredOrDefaultMs(config.display.screen_on_secs, default_screen_on_secs), NULL,
"Screen-on timeout");
powerFSM.add_timed_transition(&statePOWER, &stateDARK,
Default::getConfiguredOrDefaultMs(config.display.screen_on_secs, default_screen_on_secs), NULL,
"Screen-on timeout");
#ifdef USE_EINK
// Allow E-Ink devices to suppress the screensaver, if screen timeout set to 0
if (config.display.screen_on_secs > 0)
#endif
{
powerFSM.add_timed_transition(&stateON, &stateDARK,
Default::getConfiguredOrDefaultMs(config.display.screen_on_secs, default_screen_on_secs),
NULL, "Screen-on timeout");
powerFSM.add_timed_transition(&statePOWER, &stateDARK,
Default::getConfiguredOrDefaultMs(config.display.screen_on_secs, default_screen_on_secs),
NULL, "Screen-on timeout");
}
// We never enter light-sleep or NB states on NRF52 (because the CPU uses so little power normally)
#ifdef ARCH_ESP32
+10 -1
View File
@@ -59,9 +59,18 @@ class PowerStatus : public Status
int getBatteryVoltageMv() const { return batteryVoltageMv; }
/**
* Note: 0% battery means 'unknown/this board doesn't have a battery installed'
* Note: for boards with battery pin or PMU, 0% battery means 'unknown/this board doesn't have a battery installed'
*/
#if defined(HAS_PMU) || defined(BATTERY_PIN)
uint8_t getBatteryChargePercent() const { return getHasBattery() ? batteryChargePercent : 0; }
#endif
/**
* Note: for boards without battery pin and PMU, 101% battery means 'the board is using external power'
*/
#if !defined(HAS_PMU) && !defined(BATTERY_PIN)
uint8_t getBatteryChargePercent() const { return getHasBattery() ? batteryChargePercent : 101; }
#endif
bool matches(const PowerStatus *newStatus) const
{
+6
View File
@@ -128,8 +128,14 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#define LPS22HB_ADDR_ALT 0x5D
#define SHT31_4x_ADDR 0x44
#define PMSA0031_ADDR 0x12
#define AHT10_ADDR 0x38
#define RCWL9620_ADDR 0x57
#define VEML7700_ADDR 0x10
#define TSL25911_ADDR 0x29
#define OPT3001_ADDR 0x45
#define OPT3001_ADDR_ALT 0x44
#define MLX90632_ADDR 0x3A
#define DFROBOT_LARK_ADDR 0x42
// -----------------------------------------------------------------------------
// ACCELEROMETER
+12 -1
View File
@@ -1,5 +1,16 @@
#pragma once
enum LoRaRadioType { NO_RADIO, STM32WLx_RADIO, SIM_RADIO, RF95_RADIO, SX1262_RADIO, SX1268_RADIO, LLCC68_RADIO, SX1280_RADIO };
enum LoRaRadioType {
NO_RADIO,
STM32WLx_RADIO,
SIM_RADIO,
RF95_RADIO,
SX1262_RADIO,
SX1268_RADIO,
LLCC68_RADIO,
SX1280_RADIO,
LR1110_RADIO,
LR1120_RADIO
};
extern LoRaRadioType radioType;
+5
View File
@@ -45,6 +45,11 @@ class ScanI2C
VEML7700,
RCWL9620,
NCP5623,
TSL2591,
OPT3001,
MLX90632,
AHT10,
DFROBOT_LARK,
} DeviceType;
// typedef uint8_t DeviceAddress;
+16 -4
View File
@@ -256,7 +256,12 @@ void ScanI2CTwoWire::scanPort(I2CPort port)
type = BMP_280;
}
break;
#ifndef HAS_NCP5623
case AHT10_ADDR:
LOG_INFO("AHT10 sensor found at address 0x%x\n", (uint8_t)addr.address);
type = AHT10;
break;
#endif
case INA_ADDR:
case INA_ADDR_ALTERNATE:
case INA_ADDR_WAVESHARE_UPS:
@@ -276,8 +281,9 @@ void ScanI2CTwoWire::scanPort(I2CPort port)
if (registerValue == 0x5449) {
LOG_INFO("INA3221 sensor found at address 0x%x\n", (uint8_t)addr.address);
type = INA3221;
} else { // Unknown device
LOG_INFO("No INA3221 found at address 0x%x\n", (uint8_t)addr.address);
} else {
LOG_INFO("DFRobot Lark weather station found at address 0x%x\n", (uint8_t)addr.address);
type = DFROBOT_LARK;
}
break;
case MCP9808_ADDR:
@@ -297,6 +303,9 @@ void ScanI2CTwoWire::scanPort(I2CPort port)
if (registerValue == 0x11a2) {
type = SHT4X;
LOG_INFO("SHT4X sensor found\n");
} else if (getRegisterValue(ScanI2CTwoWire::RegisterLocation(addr, 0x7E), 2) == 0x5449) {
type = OPT3001;
LOG_INFO("OPT3001 light sensor found\n");
} else {
type = SHT31;
LOG_INFO("SHT31 sensor found\n");
@@ -337,6 +346,9 @@ void ScanI2CTwoWire::scanPort(I2CPort port)
SCAN_SIMPLE_CASE(LSM6DS3_ADDR, LSM6DS3, "LSM6DS3 accelerometer found at address 0x%x\n", (uint8_t)addr.address);
SCAN_SIMPLE_CASE(TCA9555_ADDR, TCA9555, "TCA9555 I2C expander found\n");
SCAN_SIMPLE_CASE(VEML7700_ADDR, VEML7700, "VEML7700 light sensor found\n");
SCAN_SIMPLE_CASE(TSL25911_ADDR, TSL2591, "TSL2591 light sensor found\n");
SCAN_SIMPLE_CASE(OPT3001_ADDR, OPT3001, "OPT3001 light sensor found\n");
SCAN_SIMPLE_CASE(MLX90632_ADDR, MLX90632, "MLX90632 IR temp sensor found\n");
default:
LOG_INFO("Device found at address 0x%x was not able to be enumerated\n", addr.address);
@@ -369,4 +381,4 @@ TwoWire *ScanI2CTwoWire::fetchI2CBus(ScanI2C::DeviceAddress address) const
size_t ScanI2CTwoWire::countDevices() const
{
return foundDevices.size();
}
}
+51 -23
View File
@@ -21,6 +21,13 @@
#define GPS_RESET_MODE HIGH
#endif
// How many minutes of sleep make it worthwhile to power-off the GPS
// Shorter than this, and GPS will only enter standby
// Affected by lock-time, and config.position.gps_update_interval
#ifndef GPS_STANDBY_THRESHOLD_MINUTES
#define GPS_STANDBY_THRESHOLD_MINUTES 15
#endif
#if defined(NRF52840_XXAA) || defined(NRF52833_XXAA) || defined(ARCH_ESP32) || defined(ARCH_PORTDUINO)
HardwareSerial *GPS::_serial_gps = &Serial1;
#else
@@ -767,7 +774,16 @@ GPS::~GPS()
void GPS::setGPSPower(bool on, bool standbyOnly, uint32_t sleepTime)
{
LOG_INFO("Setting GPS power=%d\n", on);
// Record the current powerState
if (on)
powerState = GPS_AWAKE;
else if (!on && standbyOnly)
powerState = GPS_STANDBY;
else
powerState = GPS_OFF;
LOG_DEBUG("GPS::powerState=%d\n", powerState);
if (on) {
clearBuffer(); // drop any old data waiting in the buffer before re-enabling
if (en_gpio)
@@ -861,17 +877,21 @@ void GPS::setConnected()
*
* calls sleep/wake
*/
void GPS::setAwake(bool on)
void GPS::setAwake(bool wantAwake)
{
if (isAwake != on) {
LOG_DEBUG("WANT GPS=%d\n", on);
isAwake = on;
if (!enabled) { // short circuit if the user has disabled GPS
setGPSPower(false, false, 0);
return;
}
if (on) {
// If user has disabled GPS, make sure it is off, not just in standby
if (!wantAwake && !enabled && powerState != GPS_OFF) {
setGPSPower(false, false, 0);
return;
}
// If GPS power state needs to change
if ((wantAwake && powerState != GPS_AWAKE) || (!wantAwake && powerState == GPS_AWAKE)) {
LOG_DEBUG("WANT GPS=%d\n", wantAwake);
// Calculate how long it takes to get a GPS lock
if (wantAwake) {
lastWakeStartMsec = millis();
} else {
lastSleepStartMsec = millis();
@@ -883,23 +903,31 @@ void GPS::setAwake(bool on)
GPSCycles++;
LOG_DEBUG("GPS Lock took %d, average %d\n", (lastSleepStartMsec - lastWakeStartMsec) / 1000, averageLockTime / 1000);
}
if ((int32_t)getSleepTime() - averageLockTime >
15 * 60 * 1000) { // 15 minutes is probably long enough to make a complete poweroff worth it.
setGPSPower(on, false, getSleepTime() - averageLockTime);
// If long interval between updates: power off between updates
if ((int32_t)getSleepTime() - averageLockTime > GPS_STANDBY_THRESHOLD_MINUTES * MS_IN_MINUTE) {
setGPSPower(wantAwake, false, getSleepTime() - averageLockTime);
return;
} else if ((int32_t)getSleepTime() - averageLockTime > 10000) { // 10 seconds is enough for standby
}
// If waking frequently: standby only. Would use more power trying to reacquire lock each time
else if ((int32_t)getSleepTime() - averageLockTime > 10000) { // 10 seconds is enough for standby
#ifdef GPS_UC6580
setGPSPower(on, false, getSleepTime() - averageLockTime);
setGPSPower(wantAwake, false, getSleepTime() - averageLockTime);
#else
setGPSPower(on, true, getSleepTime() - averageLockTime);
setGPSPower(wantAwake, true, getSleepTime() - averageLockTime);
#endif
return;
}
// Gradually recover from an abnormally long "time to get lock"
if (averageLockTime > 20000) {
averageLockTime -= 1000; // eventually want to sleep again.
}
if (on)
setGPSPower(true, true, 0); // make sure we don't have a fallthrough where GPS is stuck off
// Make sure we don't have a fallthrough where GPS is stuck off
if (wantAwake)
setGPSPower(true, true, 0);
}
}
@@ -1005,14 +1033,14 @@ int32_t GPS::runOnce()
uint32_t timeAsleep = now - lastSleepStartMsec;
auto sleepTime = getSleepTime();
if (!isAwake && (sleepTime != UINT32_MAX) &&
if (powerState != GPS_AWAKE && (sleepTime != UINT32_MAX) &&
((timeAsleep > sleepTime) || (isInPowersave && timeAsleep > (sleepTime - averageLockTime)))) {
// We now want to be awake - so wake up the GPS
setAwake(true);
}
// While we are awake
if (isAwake) {
if (powerState == GPS_AWAKE) {
// LOG_DEBUG("looking for location\n");
// If we've already set time from the GPS, no need to ask the GPS
bool gotTime = (getRTCQuality() >= RTCQualityGPS);
@@ -1058,7 +1086,7 @@ int32_t GPS::runOnce()
// 9600bps is approx 1 byte per msec, so considering our buffer size we never need to wake more often than 200ms
// if not awake we can run super infrquently (once every 5 secs?) to see if we need to wake.
return isAwake ? GPS_THREAD_INTERVAL : 5000;
return (powerState == GPS_AWAKE) ? GPS_THREAD_INTERVAL : 5000;
}
// clear the GPS rx buffer as quickly as possible
@@ -1589,9 +1617,9 @@ bool GPS::whileIdle()
{
unsigned int charsInBuf = 0;
bool isValid = false;
if (!isAwake) {
if (powerState != GPS_AWAKE) {
clearBuffer();
return isAwake;
return (powerState == GPS_AWAKE);
}
#ifdef SERIAL_BUFFER_SIZE
if (_serial_gps->available() >= SERIAL_BUFFER_SIZE - 1) {
+8 -2
View File
@@ -38,6 +38,12 @@ typedef enum {
GNSS_RESPONSE_OK,
} GPS_RESPONSE;
enum GPSPowerState : uint8_t {
GPS_OFF = 0,
GPS_AWAKE = 1,
GPS_STANDBY = 2,
};
// Generate a string representation of DOP
const char *getDOPString(uint32_t dop);
@@ -78,8 +84,6 @@ class GPS : private concurrency::OSThread
*/
bool hasValidLocation = false; // default to false, until we complete our first read
bool isAwake = false; // true if we want a location right now
bool isInPowersave = false;
bool shouldPublish = false; // If we've changed GPS state, this will force a publish the next loop()
@@ -89,6 +93,8 @@ class GPS : private concurrency::OSThread
bool GPSInitFinished = false; // Init thread finished?
bool GPSInitStarted = false; // Init thread finished?
GPSPowerState powerState = GPS_OFF; // GPS_AWAKE if we want a location right now
uint8_t numSatellites = 0;
CallbackObserver<GPS, void *> notifyDeepSleepObserver = CallbackObserver<GPS, void *>(this, &GPS::prepareDeepSleep);
+88
View File
@@ -486,3 +486,91 @@ std::shared_ptr<GeoCoord> GeoCoord::pointAtDistance(double bearing, double range
return std::make_shared<GeoCoord>(double(lat), double(lon), this->getAltitude());
}
/**
* Convert bearing to degrees
* @param bearing
* The bearing in string format
* @return Bearing in degrees
*/
uint GeoCoord::bearingToDegrees(const char *bearing)
{
if (strcmp(bearing, "N") == 0)
return 0;
else if (strcmp(bearing, "NNE") == 0)
return 22;
else if (strcmp(bearing, "NE") == 0)
return 45;
else if (strcmp(bearing, "ENE") == 0)
return 67;
else if (strcmp(bearing, "E") == 0)
return 90;
else if (strcmp(bearing, "ESE") == 0)
return 112;
else if (strcmp(bearing, "SE") == 0)
return 135;
else if (strcmp(bearing, "SSE") == 0)
return 157;
else if (strcmp(bearing, "S") == 0)
return 180;
else if (strcmp(bearing, "SSW") == 0)
return 202;
else if (strcmp(bearing, "SW") == 0)
return 225;
else if (strcmp(bearing, "WSW") == 0)
return 247;
else if (strcmp(bearing, "W") == 0)
return 270;
else if (strcmp(bearing, "WNW") == 0)
return 292;
else if (strcmp(bearing, "NW") == 0)
return 315;
else if (strcmp(bearing, "NNW") == 0)
return 337;
else
return 0;
}
/**
* Convert bearing to string
* @param degrees
* The bearing in degrees
* @return Bearing in string format
*/
const char *GeoCoord::degreesToBearing(uint degrees)
{
if (degrees >= 348 || degrees < 11)
return "N";
else if (degrees >= 11 && degrees < 34)
return "NNE";
else if (degrees >= 34 && degrees < 56)
return "NE";
else if (degrees >= 56 && degrees < 79)
return "ENE";
else if (degrees >= 79 && degrees < 101)
return "E";
else if (degrees >= 101 && degrees < 124)
return "ESE";
else if (degrees >= 124 && degrees < 146)
return "SE";
else if (degrees >= 146 && degrees < 169)
return "SSE";
else if (degrees >= 169 && degrees < 191)
return "S";
else if (degrees >= 191 && degrees < 214)
return "SSW";
else if (degrees >= 214 && degrees < 236)
return "SW";
else if (degrees >= 236 && degrees < 259)
return "WSW";
else if (degrees >= 259 && degrees < 281)
return "W";
else if (degrees >= 281 && degrees < 304)
return "WNW";
else if (degrees >= 304 && degrees < 326)
return "NW";
else if (degrees >= 326 && degrees < 348)
return "NNW";
else
return "N";
}
+2
View File
@@ -117,6 +117,8 @@ class GeoCoord
static float bearing(double lat1, double lon1, double lat2, double lon2);
static float rangeRadiansToMeters(double range_radians);
static float rangeMetersToRadians(double range_meters);
static uint bearingToDegrees(const char *bearing);
static const char *degreesToBearing(uint degrees);
// Point to point conversions
int32_t distanceTo(const GeoCoord &pointB);
+8 -5
View File
@@ -96,13 +96,17 @@ void readFromRTC()
*
* If we haven't yet set our RTC this boot, set it from a GPS derived time
*/
bool perhapsSetRTC(RTCQuality q, const struct timeval *tv)
bool perhapsSetRTC(RTCQuality q, const struct timeval *tv, bool forceUpdate)
{
static uint32_t lastSetMsec = 0;
uint32_t now = millis();
bool shouldSet;
if (q > currentQuality) {
if (forceUpdate) {
shouldSet = true;
LOG_DEBUG("Overriding current RTC quality (%s) with incoming time of RTC quality of %s\n", RtcName(currentQuality),
RtcName(q));
} else if (q > currentQuality) {
shouldSet = true;
LOG_DEBUG("Upgrading time to quality %s\n", RtcName(q));
} else if (q >= RTCQualityNTP && (now - lastSetMsec) > (12 * 60 * 60 * 1000UL)) {
@@ -218,9 +222,8 @@ bool perhapsSetRTC(RTCQuality q, struct tm &t)
*/
int32_t getTZOffset()
{
time_t now;
time_t now = getTime(false);
struct tm *gmt;
now = time(NULL);
gmt = gmtime(&now);
gmt->tm_isdst = -1;
return (int32_t)difftime(now, mktime(gmt));
@@ -261,4 +264,4 @@ time_t gm_mktime(struct tm *tm)
setenv("TZ", "UTC0", 1);
}
return res;
}
}
+1 -1
View File
@@ -25,7 +25,7 @@ enum RTCQuality {
RTCQuality getRTCQuality();
/// If we haven't yet set our RTC this boot, set it from a GPS derived time
bool perhapsSetRTC(RTCQuality q, const struct timeval *tv);
bool perhapsSetRTC(RTCQuality q, const struct timeval *tv, bool forceUpdate = false);
bool perhapsSetRTC(RTCQuality q, struct tm &t);
/// Return a string name for the quality
+4
View File
@@ -0,0 +1,4 @@
struct PointStruct {
int x;
int y;
};
+675 -40
View File
@@ -419,6 +419,536 @@ static bool shouldDrawMessage(const meshtastic_MeshPacket *packet)
return packet->from != 0 && !moduleConfig.store_forward.enabled;
}
// Draw power bars or a charging indicator on an image of a battery, determined by battery charge voltage or percentage.
static void drawBattery(OLEDDisplay *display, int16_t x, int16_t y, uint8_t *imgBuffer, const PowerStatus *powerStatus)
{
static const uint8_t powerBar[3] = {0x81, 0xBD, 0xBD};
static const uint8_t lightning[8] = {0xA1, 0xA1, 0xA5, 0xAD, 0xB5, 0xA5, 0x85, 0x85};
// Clear the bar area on the battery image
for (int i = 1; i < 14; i++) {
imgBuffer[i] = 0x81;
}
// If charging, draw a charging indicator
if (powerStatus->getIsCharging()) {
memcpy(imgBuffer + 3, lightning, 8);
// If not charging, Draw power bars
} else {
for (int i = 0; i < 4; i++) {
if (powerStatus->getBatteryChargePercent() >= 25 * i)
memcpy(imgBuffer + 1 + (i * 3), powerBar, 3);
}
}
display->drawFastImage(x, y, 16, 8, imgBuffer);
}
#ifdef T_WATCH_S3
void Screen::drawWatchFaceToggleButton(OLEDDisplay *display, int16_t x, int16_t y, bool digitalMode, float scale)
{
uint16_t segmentWidth = SEGMENT_WIDTH * scale;
uint16_t segmentHeight = SEGMENT_HEIGHT * scale;
if (digitalMode) {
uint16_t radius = (segmentWidth + (segmentHeight * 2) + 4) / 2;
uint16_t centerX = (x + segmentHeight + 2) + (radius / 2);
uint16_t centerY = (y + segmentHeight + 2) + (radius / 2);
display->drawCircle(centerX, centerY, radius);
display->drawCircle(centerX, centerY, radius + 1);
display->drawLine(centerX, centerY, centerX, centerY - radius + 3);
display->drawLine(centerX, centerY, centerX + radius - 3, centerY);
} else {
uint16_t segmentOneX = x + segmentHeight + 2;
uint16_t segmentOneY = y;
uint16_t segmentTwoX = segmentOneX + segmentWidth + 2;
uint16_t segmentTwoY = segmentOneY + segmentHeight + 2;
uint16_t segmentThreeX = segmentOneX;
uint16_t segmentThreeY = segmentTwoY + segmentWidth + 2;
uint16_t segmentFourX = x;
uint16_t segmentFourY = y + segmentHeight + 2;
drawHorizontalSegment(display, segmentOneX, segmentOneY, segmentWidth, segmentHeight);
drawVerticalSegment(display, segmentTwoX, segmentTwoY, segmentWidth, segmentHeight);
drawHorizontalSegment(display, segmentThreeX, segmentThreeY, segmentWidth, segmentHeight);
drawVerticalSegment(display, segmentFourX, segmentFourY, segmentWidth, segmentHeight);
}
}
// Draw a digital clock
void Screen::drawDigitalClockFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y)
{
display->setTextAlignment(TEXT_ALIGN_LEFT);
drawBattery(display, x, y + 7, imgBattery, powerStatus);
if (powerStatus->getHasBattery()) {
String batteryPercent = String(powerStatus->getBatteryChargePercent()) + "%";
display->setFont(FONT_SMALL);
display->drawString(x + 20, y + 2, batteryPercent);
}
if (nimbleBluetooth->isConnected()) {
drawBluetoothConnectedIcon(display, display->getWidth() - 18, y + 2);
}
drawWatchFaceToggleButton(display, display->getWidth() - 36, display->getHeight() - 36, screen->digitalWatchFace, 1);
display->setColor(OLEDDISPLAY_COLOR::WHITE);
uint32_t rtc_sec = getValidTime(RTCQuality::RTCQualityDevice, true); // Display local timezone
if (rtc_sec > 0) {
long hms = rtc_sec % SEC_PER_DAY;
hms = (hms + SEC_PER_DAY) % SEC_PER_DAY;
int hour = hms / SEC_PER_HOUR;
int minute = (hms % SEC_PER_HOUR) / SEC_PER_MIN;
int second = (hms % SEC_PER_HOUR) % SEC_PER_MIN; // or hms % SEC_PER_MIN
hour = hour > 12 ? hour - 12 : hour;
if (hour == 0) {
hour = 12;
}
// hours string
String hourString = String(hour);
// minutes string
String minuteString = minute < 10 ? "0" + String(minute) : String(minute);
String timeString = hourString + ":" + minuteString;
// seconds string
String secondString = second < 10 ? "0" + String(second) : String(second);
float scale = 1.5;
uint16_t segmentWidth = SEGMENT_WIDTH * scale;
uint16_t segmentHeight = SEGMENT_HEIGHT * scale;
// calculate hours:minutes string width
uint16_t timeStringWidth = timeString.length() * 5;
for (uint8_t i = 0; i < timeString.length(); i++) {
String character = String(timeString[i]);
if (character == ":") {
timeStringWidth += segmentHeight;
} else {
timeStringWidth += segmentWidth + (segmentHeight * 2) + 4;
}
}
// calculate seconds string width
uint16_t secondStringWidth = (secondString.length() * 12) + 4;
// sum these to get total string width
uint16_t totalWidth = timeStringWidth + secondStringWidth;
uint16_t hourMinuteTextX = (display->getWidth() / 2) - (totalWidth / 2);
uint16_t startingHourMinuteTextX = hourMinuteTextX;
uint16_t hourMinuteTextY = (display->getHeight() / 2) - (((segmentWidth * 2) + (segmentHeight * 3) + 8) / 2);
// iterate over characters in hours:minutes string and draw segmented characters
for (uint8_t i = 0; i < timeString.length(); i++) {
String character = String(timeString[i]);
if (character == ":") {
drawSegmentedDisplayColon(display, hourMinuteTextX, hourMinuteTextY, scale);
hourMinuteTextX += segmentHeight + 6;
} else {
drawSegmentedDisplayCharacter(display, hourMinuteTextX, hourMinuteTextY, character.toInt(), scale);
hourMinuteTextX += segmentWidth + (segmentHeight * 2) + 4;
}
hourMinuteTextX += 5;
}
// draw seconds string
display->setFont(FONT_MEDIUM);
display->drawString(startingHourMinuteTextX + timeStringWidth + 4,
(display->getHeight() - hourMinuteTextY) - FONT_HEIGHT_MEDIUM + 6, secondString);
}
}
void Screen::drawSegmentedDisplayColon(OLEDDisplay *display, int x, int y, float scale)
{
uint16_t segmentWidth = SEGMENT_WIDTH * scale;
uint16_t segmentHeight = SEGMENT_HEIGHT * scale;
uint16_t cellHeight = (segmentWidth * 2) + (segmentHeight * 3) + 8;
uint16_t topAndBottomX = x + (4 * scale);
uint16_t quarterCellHeight = cellHeight / 4;
uint16_t topY = y + quarterCellHeight;
uint16_t bottomY = y + (quarterCellHeight * 3);
display->fillRect(topAndBottomX, topY, segmentHeight, segmentHeight);
display->fillRect(topAndBottomX, bottomY, segmentHeight, segmentHeight);
}
void Screen::drawSegmentedDisplayCharacter(OLEDDisplay *display, int x, int y, uint8_t number, float scale)
{
// the numbers 0-9, each expressed as an array of seven boolean (0|1) values encoding the on/off state of
// segment {innerIndex + 1}
// e.g., to display the numeral '0', segments 1-6 are on, and segment 7 is off.
uint8_t numbers[10][7] = {
{1, 1, 1, 1, 1, 1, 0}, // 0 Display segment key
{0, 1, 1, 0, 0, 0, 0}, // 1 1
{1, 1, 0, 1, 1, 0, 1}, // 2 ___
{1, 1, 1, 1, 0, 0, 1}, // 3 6 | | 2
{0, 1, 1, 0, 0, 1, 1}, // 4 |_7̲_|
{1, 0, 1, 1, 0, 1, 1}, // 5 5 | | 3
{1, 0, 1, 1, 1, 1, 1}, // 6 |___|
{1, 1, 1, 0, 0, 1, 0}, // 7
{1, 1, 1, 1, 1, 1, 1}, // 8 4
{1, 1, 1, 1, 0, 1, 1}, // 9
};
// the width and height of each segment's central rectangle:
// _____________________
// ⋰| (only this part, |⋱
// ⋰ | not including | ⋱
// ⋱ | the triangles | ⋰
// ⋱| on the ends) |⋰
// ‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾
uint16_t segmentWidth = SEGMENT_WIDTH * scale;
uint16_t segmentHeight = SEGMENT_HEIGHT * scale;
// segment x and y coordinates
uint16_t segmentOneX = x + segmentHeight + 2;
uint16_t segmentOneY = y;
uint16_t segmentTwoX = segmentOneX + segmentWidth + 2;
uint16_t segmentTwoY = segmentOneY + segmentHeight + 2;
uint16_t segmentThreeX = segmentTwoX;
uint16_t segmentThreeY = segmentTwoY + segmentWidth + 2 + segmentHeight + 2;
uint16_t segmentFourX = segmentOneX;
uint16_t segmentFourY = segmentThreeY + segmentWidth + 2;
uint16_t segmentFiveX = x;
uint16_t segmentFiveY = segmentThreeY;
uint16_t segmentSixX = x;
uint16_t segmentSixY = segmentTwoY;
uint16_t segmentSevenX = segmentOneX;
uint16_t segmentSevenY = segmentTwoY + segmentWidth + 2;
if (numbers[number][0]) {
drawHorizontalSegment(display, segmentOneX, segmentOneY, segmentWidth, segmentHeight);
}
if (numbers[number][1]) {
drawVerticalSegment(display, segmentTwoX, segmentTwoY, segmentWidth, segmentHeight);
}
if (numbers[number][2]) {
drawVerticalSegment(display, segmentThreeX, segmentThreeY, segmentWidth, segmentHeight);
}
if (numbers[number][3]) {
drawHorizontalSegment(display, segmentFourX, segmentFourY, segmentWidth, segmentHeight);
}
if (numbers[number][4]) {
drawVerticalSegment(display, segmentFiveX, segmentFiveY, segmentWidth, segmentHeight);
}
if (numbers[number][5]) {
drawVerticalSegment(display, segmentSixX, segmentSixY, segmentWidth, segmentHeight);
}
if (numbers[number][6]) {
drawHorizontalSegment(display, segmentSevenX, segmentSevenY, segmentWidth, segmentHeight);
}
}
void Screen::drawHorizontalSegment(OLEDDisplay *display, int x, int y, int width, int height)
{
int halfHeight = height / 2;
// draw central rectangle
display->fillRect(x, y, width, height);
// draw end triangles
display->fillTriangle(x, y, x, y + height - 1, x - halfHeight, y + halfHeight);
display->fillTriangle(x + width, y, x + width + halfHeight, y + halfHeight, x + width, y + height - 1);
}
void Screen::drawVerticalSegment(OLEDDisplay *display, int x, int y, int width, int height)
{
int halfHeight = height / 2;
// draw central rectangle
display->fillRect(x, y, height, width);
// draw end triangles
display->fillTriangle(x + halfHeight, y - halfHeight, x + height - 1, y, x, y);
display->fillTriangle(x, y + width, x + height - 1, y + width, x + halfHeight, y + width + halfHeight);
}
void Screen::drawBluetoothConnectedIcon(OLEDDisplay *display, int16_t x, int16_t y)
{
display->drawFastImage(x, y, 18, 14, bluetoothConnectedIcon);
}
// Draw an analog clock
void Screen::drawAnalogClockFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y)
{
display->setTextAlignment(TEXT_ALIGN_LEFT);
drawBattery(display, x, y + 7, imgBattery, powerStatus);
if (powerStatus->getHasBattery()) {
String batteryPercent = String(powerStatus->getBatteryChargePercent()) + "%";
display->setFont(FONT_SMALL);
display->drawString(x + 20, y + 2, batteryPercent);
}
if (nimbleBluetooth->isConnected()) {
drawBluetoothConnectedIcon(display, display->getWidth() - 18, y + 2);
}
drawWatchFaceToggleButton(display, display->getWidth() - 36, display->getHeight() - 36, screen->digitalWatchFace, 1);
// clock face center coordinates
int16_t centerX = display->getWidth() / 2;
int16_t centerY = display->getHeight() / 2;
// clock face radius
int16_t radius = (display->getWidth() / 2) * 0.8;
// noon (0 deg) coordinates (outermost circle)
int16_t noonX = centerX;
int16_t noonY = centerY - radius;
// second hand radius and y coordinate (outermost circle)
int16_t secondHandNoonY = noonY + 1;
// tick mark outer y coordinate; (first nested circle)
int16_t tickMarkOuterNoonY = secondHandNoonY;
// seconds tick mark inner y coordinate; (second nested circle)
double secondsTickMarkInnerNoonY = (double)noonY + 8;
// hours tick mark inner y coordinate; (third nested circle)
double hoursTickMarkInnerNoonY = (double)noonY + 16;
// minute hand y coordinate
int16_t minuteHandNoonY = secondsTickMarkInnerNoonY + 4;
// hour string y coordinate
int16_t hourStringNoonY = minuteHandNoonY + 18;
// hour hand radius and y coordinate
int16_t hourHandRadius = radius * 0.55;
int16_t hourHandNoonY = centerY - hourHandRadius;
display->setColor(OLEDDISPLAY_COLOR::WHITE);
display->drawCircle(centerX, centerY, radius);
uint32_t rtc_sec = getValidTime(RTCQuality::RTCQualityDevice, true); // Display local timezone
if (rtc_sec > 0) {
long hms = rtc_sec % SEC_PER_DAY;
hms = (hms + SEC_PER_DAY) % SEC_PER_DAY;
// Tear apart hms into h:m:s
int hour = hms / SEC_PER_HOUR;
int minute = (hms % SEC_PER_HOUR) / SEC_PER_MIN;
int second = (hms % SEC_PER_HOUR) % SEC_PER_MIN; // or hms % SEC_PER_MIN
hour = hour > 12 ? hour - 12 : hour;
int16_t degreesPerHour = 30;
int16_t degreesPerMinuteOrSecond = 6;
double hourBaseAngle = hour * degreesPerHour;
double hourAngleOffset = ((double)minute / 60) * degreesPerHour;
double hourAngle = radians(hourBaseAngle + hourAngleOffset);
double minuteBaseAngle = minute * degreesPerMinuteOrSecond;
double minuteAngleOffset = ((double)second / 60) * degreesPerMinuteOrSecond;
double minuteAngle = radians(minuteBaseAngle + minuteAngleOffset);
double secondAngle = radians(second * degreesPerMinuteOrSecond);
double hourX = sin(-hourAngle) * (hourHandNoonY - centerY) + noonX;
double hourY = cos(-hourAngle) * (hourHandNoonY - centerY) + centerY;
double minuteX = sin(-minuteAngle) * (minuteHandNoonY - centerY) + noonX;
double minuteY = cos(-minuteAngle) * (minuteHandNoonY - centerY) + centerY;
double secondX = sin(-secondAngle) * (secondHandNoonY - centerY) + noonX;
double secondY = cos(-secondAngle) * (secondHandNoonY - centerY) + centerY;
display->setFont(FONT_MEDIUM);
// draw minute and hour tick marks and hour numbers
for (uint16_t angle = 0; angle < 360; angle += 6) {
double angleInRadians = radians(angle);
double sineAngleInRadians = sin(-angleInRadians);
double cosineAngleInRadians = cos(-angleInRadians);
double endX = sineAngleInRadians * (tickMarkOuterNoonY - centerY) + noonX;
double endY = cosineAngleInRadians * (tickMarkOuterNoonY - centerY) + centerY;
if (angle % degreesPerHour == 0) {
double startX = sineAngleInRadians * (hoursTickMarkInnerNoonY - centerY) + noonX;
double startY = cosineAngleInRadians * (hoursTickMarkInnerNoonY - centerY) + centerY;
// draw hour tick mark
display->drawLine(startX, startY, endX, endY);
static char buffer[2];
uint8_t hourInt = (angle / 30);
if (hourInt == 0) {
hourInt = 12;
}
// hour number x offset needs to be adjusted for some cases
int8_t hourStringXOffset;
int8_t hourStringYOffset = 13;
switch (hourInt) {
case 3:
hourStringXOffset = 5;
break;
case 9:
hourStringXOffset = 7;
break;
case 10:
case 11:
hourStringXOffset = 8;
break;
case 12:
hourStringXOffset = 13;
break;
default:
hourStringXOffset = 6;
break;
}
double hourStringX = (sineAngleInRadians * (hourStringNoonY - centerY) + noonX) - hourStringXOffset;
double hourStringY = (cosineAngleInRadians * (hourStringNoonY - centerY) + centerY) - hourStringYOffset;
// draw hour number
display->drawStringf(hourStringX, hourStringY, buffer, "%d", hourInt);
}
if (angle % degreesPerMinuteOrSecond == 0) {
double startX = sineAngleInRadians * (secondsTickMarkInnerNoonY - centerY) + noonX;
double startY = cosineAngleInRadians * (secondsTickMarkInnerNoonY - centerY) + centerY;
// draw minute tick mark
display->drawLine(startX, startY, endX, endY);
}
}
// draw hour hand
display->drawLine(centerX, centerY, hourX, hourY);
// draw minute hand
display->drawLine(centerX, centerY, minuteX, minuteY);
// draw second hand
display->drawLine(centerX, centerY, secondX, secondY);
}
}
#endif
// Get an absolute time from "seconds ago" info. Returns false if no valid timestamp possible
bool deltaToTimestamp(uint32_t secondsAgo, uint8_t *hours, uint8_t *minutes, int32_t *daysAgo)
{
// Cache the result - avoid frequent recalculation
static uint8_t hoursCached = 0, minutesCached = 0;
static uint32_t daysAgoCached = 0;
static uint32_t secondsAgoCached = 0;
static bool validCached = false;
// Abort: if timezone not set
if (strlen(config.device.tzdef) == 0) {
validCached = false;
return validCached;
}
// Abort: if invalid pointers passed
if (hours == nullptr || minutes == nullptr || daysAgo == nullptr) {
validCached = false;
return validCached;
}
// Abort: if time seems invalid.. (> 6 months ago, probably seen before RTC set)
if (secondsAgo > SEC_PER_DAY * 30UL * 6) {
validCached = false;
return validCached;
}
// If repeated request, don't bother recalculating
if (secondsAgo - secondsAgoCached < 60 && secondsAgoCached != 0) {
if (validCached) {
*hours = hoursCached;
*minutes = minutesCached;
*daysAgo = daysAgoCached;
}
return validCached;
}
// Get local time
uint32_t secondsRTC = getValidTime(RTCQuality::RTCQualityDevice, true); // Get local time
// Abort: if RTC not set
if (!secondsRTC) {
validCached = false;
return validCached;
}
// Get absolute time when last seen
uint32_t secondsSeenAt = secondsRTC - secondsAgo;
// Calculate daysAgo
*daysAgo = (secondsRTC / SEC_PER_DAY) - (secondsSeenAt / SEC_PER_DAY); // How many "midnights" have passed
// Get seconds since midnight
uint32_t hms = (secondsRTC - secondsAgo) % SEC_PER_DAY;
hms = (hms + SEC_PER_DAY) % SEC_PER_DAY;
// Tear apart hms into hours and minutes
*hours = hms / SEC_PER_HOUR;
*minutes = (hms % SEC_PER_HOUR) / SEC_PER_MIN;
// Cache the result
daysAgoCached = *daysAgo;
hoursCached = *hours;
minutesCached = *minutes;
secondsAgoCached = secondsAgo;
validCached = true;
return validCached;
}
/// Draw the last text message we received
static void drawTextMessageFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y)
{
@@ -440,22 +970,98 @@ static void drawTextMessageFrame(OLEDDisplay *display, OLEDDisplayUiState *state
display->setColor(BLACK);
}
// For time delta
uint32_t seconds = sinceReceived(&mp);
uint32_t minutes = seconds / 60;
uint32_t hours = minutes / 60;
uint32_t days = hours / 24;
if (config.display.heading_bold) {
display->drawStringf(1 + x, 0 + y, tempBuf, "%s ago from %s",
screen->drawTimeDelta(days, hours, minutes, seconds).c_str(),
(node && node->has_user) ? node->user.short_name : "???");
// For timestamp
uint8_t timestampHours, timestampMinutes;
int32_t daysAgo;
bool useTimestamp = deltaToTimestamp(seconds, &timestampHours, &timestampMinutes, &daysAgo);
// If bold, draw twice, shifting right by one pixel
for (uint8_t xOff = 0; xOff <= (config.display.heading_bold ? 1 : 0); xOff++) {
// Show a timestamp if received today, but longer than 15 minutes ago
if (useTimestamp && minutes >= 15 && daysAgo == 0) {
display->drawStringf(xOff + x, 0 + y, tempBuf, "At %02hu:%02hu from %s", timestampHours, timestampMinutes,
(node && node->has_user) ? node->user.short_name : "???");
}
// Timestamp yesterday (if display is wide enough)
else if (useTimestamp && daysAgo == 1 && display->width() >= 200) {
display->drawStringf(xOff + x, 0 + y, tempBuf, "Yesterday %02hu:%02hu from %s", timestampHours, timestampMinutes,
(node && node->has_user) ? node->user.short_name : "???");
}
// Otherwise, show a time delta
else {
display->drawStringf(xOff + x, 0 + y, tempBuf, "%s ago from %s",
screen->drawTimeDelta(days, hours, minutes, seconds).c_str(),
(node && node->has_user) ? node->user.short_name : "???");
}
}
display->drawStringf(0 + x, 0 + y, tempBuf, "%s ago from %s", screen->drawTimeDelta(days, hours, minutes, seconds).c_str(),
(node && node->has_user) ? node->user.short_name : "???");
display->setColor(WHITE);
#ifndef EXCLUDE_EMOJI
if (strcmp(reinterpret_cast<const char *>(mp.decoded.payload.bytes), u8"\U0001F44D") == 0) {
display->drawXbm(x + (SCREEN_WIDTH - thumbs_width) / 2,
y + (SCREEN_HEIGHT - FONT_HEIGHT_MEDIUM - thumbs_height) / 2 + 2 + 5, thumbs_width, thumbs_height,
thumbup);
} else if (strcmp(reinterpret_cast<const char *>(mp.decoded.payload.bytes), u8"\U0001F44E") == 0) {
display->drawXbm(x + (SCREEN_WIDTH - thumbs_width) / 2,
y + (SCREEN_HEIGHT - FONT_HEIGHT_MEDIUM - thumbs_height) / 2 + 2 + 5, thumbs_width, thumbs_height,
thumbdown);
} else if (strcmp(reinterpret_cast<const char *>(mp.decoded.payload.bytes), u8"❓") == 0) {
display->drawXbm(x + (SCREEN_WIDTH - question_width) / 2,
y + (SCREEN_HEIGHT - FONT_HEIGHT_MEDIUM - question_height) / 2 + 2 + 5, question_width, question_height,
question);
} else if (strcmp(reinterpret_cast<const char *>(mp.decoded.payload.bytes), u8"‼️") == 0) {
display->drawXbm(x + (SCREEN_WIDTH - bang_width) / 2, y + (SCREEN_HEIGHT - FONT_HEIGHT_MEDIUM - bang_height) / 2 + 2 + 5,
bang_width, bang_height, bang);
} else if (strcmp(reinterpret_cast<const char *>(mp.decoded.payload.bytes), u8"\U0001F4A9") == 0) {
display->drawXbm(x + (SCREEN_WIDTH - poo_width) / 2, y + (SCREEN_HEIGHT - FONT_HEIGHT_MEDIUM - poo_height) / 2 + 2 + 5,
poo_width, poo_height, poo);
} else if (strcmp(reinterpret_cast<const char *>(mp.decoded.payload.bytes), "\xf0\x9f\xa4\xa3") == 0) {
display->drawXbm(x + (SCREEN_WIDTH - haha_width) / 2, y + (SCREEN_HEIGHT - FONT_HEIGHT_MEDIUM - haha_height) / 2 + 2 + 5,
haha_width, haha_height, haha);
} else if (strcmp(reinterpret_cast<const char *>(mp.decoded.payload.bytes), u8"\U0001F44B") == 0) {
display->drawXbm(x + (SCREEN_WIDTH - wave_icon_width) / 2,
y + (SCREEN_HEIGHT - FONT_HEIGHT_MEDIUM - wave_icon_height) / 2 + 2 + 5, wave_icon_width,
wave_icon_height, wave_icon);
} else if (strcmp(reinterpret_cast<const char *>(mp.decoded.payload.bytes), u8"\U0001F920") == 0) {
display->drawXbm(x + (SCREEN_WIDTH - cowboy_width) / 2,
y + (SCREEN_HEIGHT - FONT_HEIGHT_MEDIUM - cowboy_height) / 2 + 2 + 5, cowboy_width, cowboy_height,
cowboy);
} else if (strcmp(reinterpret_cast<const char *>(mp.decoded.payload.bytes), u8"\U0001F42D") == 0) {
display->drawXbm(x + (SCREEN_WIDTH - deadmau5_width) / 2,
y + (SCREEN_HEIGHT - FONT_HEIGHT_MEDIUM - deadmau5_height) / 2 + 2 + 5, deadmau5_width, deadmau5_height,
deadmau5);
} else if (strcmp(reinterpret_cast<const char *>(mp.decoded.payload.bytes), u8"\xE2\x98\x80\xEF\xB8\x8F") == 0) {
display->drawXbm(x + (SCREEN_WIDTH - sun_width) / 2, y + (SCREEN_HEIGHT - FONT_HEIGHT_MEDIUM - sun_height) / 2 + 2 + 5,
sun_width, sun_height, sun);
} else if (strcmp(reinterpret_cast<const char *>(mp.decoded.payload.bytes), u8"\u2614") == 0) {
display->drawXbm(x + (SCREEN_WIDTH - rain_width) / 2, y + (SCREEN_HEIGHT - FONT_HEIGHT_MEDIUM - rain_height) / 2 + 2 + 10,
rain_width, rain_height, rain);
} else if (strcmp(reinterpret_cast<const char *>(mp.decoded.payload.bytes), u8"☁️") == 0) {
display->drawXbm(x + (SCREEN_WIDTH - cloud_width) / 2,
y + (SCREEN_HEIGHT - FONT_HEIGHT_MEDIUM - cloud_height) / 2 + 2 + 5, cloud_width, cloud_height, cloud);
} else if (strcmp(reinterpret_cast<const char *>(mp.decoded.payload.bytes), u8"🌫️") == 0) {
display->drawXbm(x + (SCREEN_WIDTH - fog_width) / 2, y + (SCREEN_HEIGHT - FONT_HEIGHT_MEDIUM - fog_height) / 2 + 2 + 5,
fog_width, fog_height, fog);
} else if (strcmp(reinterpret_cast<const char *>(mp.decoded.payload.bytes), u8"\xf0\x9f\x98\x88") == 0) {
display->drawXbm(x + (SCREEN_WIDTH - devil_width) / 2,
y + (SCREEN_HEIGHT - FONT_HEIGHT_MEDIUM - devil_height) / 2 + 2 + 5, devil_width, devil_height, devil);
} else if (strcmp(reinterpret_cast<const char *>(mp.decoded.payload.bytes), u8"♥️") == 0) {
display->drawXbm(x + (SCREEN_WIDTH - heart_width) / 2,
y + (SCREEN_HEIGHT - FONT_HEIGHT_MEDIUM - heart_height) / 2 + 2 + 5, heart_width, heart_height, heart);
} else {
snprintf(tempBuf, sizeof(tempBuf), "%s", mp.decoded.payload.bytes);
display->drawStringMaxWidth(0 + x, 0 + y + FONT_HEIGHT_SMALL, x + display->getWidth(), tempBuf);
}
#else
snprintf(tempBuf, sizeof(tempBuf), "%s", mp.decoded.payload.bytes);
display->drawStringMaxWidth(0 + x, 0 + y + FONT_HEIGHT_SMALL, x + display->getWidth(), tempBuf);
#endif
}
/// Draw the last waypoint we received
@@ -518,28 +1124,6 @@ static void drawColumns(OLEDDisplay *display, int16_t x, int16_t y, const char *
}
}
// Draw power bars or a charging indicator on an image of a battery, determined by battery charge voltage or percentage.
static void drawBattery(OLEDDisplay *display, int16_t x, int16_t y, uint8_t *imgBuffer, const PowerStatus *powerStatus)
{
static const uint8_t powerBar[3] = {0x81, 0xBD, 0xBD};
static const uint8_t lightning[8] = {0xA1, 0xA1, 0xA5, 0xAD, 0xB5, 0xA5, 0x85, 0x85};
// Clear the bar area on the battery image
for (int i = 1; i < 14; i++) {
imgBuffer[i] = 0x81;
}
// If charging, draw a charging indicator
if (powerStatus->getIsCharging()) {
memcpy(imgBuffer + 3, lightning, 8);
// If not charging, Draw power bars
} else {
for (int i = 0; i < 4; i++) {
if (powerStatus->getBatteryChargePercent() >= 25 * i)
memcpy(imgBuffer + 1 + (i * 3), powerBar, 3);
}
}
display->drawFastImage(x, y, 16, 8, imgBuffer);
}
// Draw nodes status
static void drawNodes(OLEDDisplay *display, int16_t x, int16_t y, const NodeStatus *nodeStatus)
{
@@ -875,23 +1459,42 @@ static void drawNodeInfo(OLEDDisplay *display, OLEDDisplayUiState *state, int16_
const char *username = node->has_user ? node->user.long_name : "Unknown Name";
static char signalStr[20];
snprintf(signalStr, sizeof(signalStr), "Signal: %d%%", clamp((int)((node->snr + 10) * 5), 0, 100));
// section here to choose whether to display hops away rather than signal strength if more than 0 hops away.
if (node->hops_away > 0) {
snprintf(signalStr, sizeof(signalStr), "Hops Away: %d", node->hops_away);
} else {
snprintf(signalStr, sizeof(signalStr), "Signal: %d%%", clamp((int)((node->snr + 10) * 5), 0, 100));
}
uint32_t agoSecs = sinceLastSeen(node);
static char lastStr[20];
// Use an absolute timestamp in some cases.
// Particularly useful with E-Ink displays. Static UI, fewer refreshes.
uint8_t timestampHours, timestampMinutes;
int32_t daysAgo;
bool useTimestamp = deltaToTimestamp(agoSecs, &timestampHours, &timestampMinutes, &daysAgo);
if (agoSecs < 120) // last 2 mins?
snprintf(lastStr, sizeof(lastStr), "%u seconds ago", agoSecs);
// -- if suitable for timestamp --
else if (useTimestamp && agoSecs < 15 * SECONDS_IN_MINUTE) // Last 15 minutes
snprintf(lastStr, sizeof(lastStr), "%u minutes ago", agoSecs / SECONDS_IN_MINUTE);
else if (useTimestamp && daysAgo == 0) // Today
snprintf(lastStr, sizeof(lastStr), "Last seen: %02u:%02u", (unsigned int)timestampHours, (unsigned int)timestampMinutes);
else if (useTimestamp && daysAgo == 1) // Yesterday
snprintf(lastStr, sizeof(lastStr), "Seen yesterday");
else if (useTimestamp && daysAgo > 1) // Last six months (capped by deltaToTimestamp method)
snprintf(lastStr, sizeof(lastStr), "%li days ago", (long)daysAgo);
// -- if using time delta instead --
else if (agoSecs < 120 * 60) // last 2 hrs
snprintf(lastStr, sizeof(lastStr), "%u minutes ago", agoSecs / 60);
else {
// Only show hours ago if it's been less than 6 months. Otherwise, we may have bad
// data.
if ((agoSecs / 60 / 60) < (hours_in_month * 6)) {
snprintf(lastStr, sizeof(lastStr), "%u hours ago", agoSecs / 60 / 60);
} else {
snprintf(lastStr, sizeof(lastStr), "unknown age");
}
}
// Only show hours ago if it's been less than 6 months. Otherwise, we may have bad data.
else if ((agoSecs / 60 / 60) < (hours_in_month * 6))
snprintf(lastStr, sizeof(lastStr), "%u hours ago", agoSecs / 60 / 60);
else
snprintf(lastStr, sizeof(lastStr), "unknown age");
static char distStr[20];
if (config.display.units == meshtastic_Config_DisplayConfig_DisplayUnits_IMPERIAL) {
@@ -900,7 +1503,7 @@ static void drawNodeInfo(OLEDDisplay *display, OLEDDisplayUiState *state, int16_
strncpy(distStr, "? km", sizeof(distStr));
}
meshtastic_NodeInfoLite *ourNode = nodeDB->getMeshNode(nodeDB->getNodeNum());
const char *fields[] = {username, distStr, signalStr, lastStr, NULL};
const char *fields[] = {username, lastStr, signalStr, distStr, NULL};
int16_t compassX = 0, compassY = 0;
// coordinates for the center of the compass/circle
@@ -1218,6 +1821,10 @@ int32_t Screen::runOnce()
return RUN_SAME;
}
if (displayHeight == 0) {
displayHeight = dispdev->getHeight();
}
// Show boot screen for first logo_timeout seconds, then switch to normal operation.
// serialSinceMsec adjusts for additional serial wait time during nRF52 bootup
static bool showingBootScreen = true;
@@ -1448,6 +2055,15 @@ void Screen::setFrames()
LOG_DEBUG("showing standard frames\n");
showingNormalScreen = true;
#ifdef USE_EINK
// If user has disabled the screensaver, warn them after boot
static bool warnedScreensaverDisabled = false;
if (config.display.screen_on_secs == 0 && !warnedScreensaverDisabled) {
screen->print("Screensaver disabled\n");
warnedScreensaverDisabled = true;
}
#endif
moduleFrames = MeshModule::GetMeshModulesWithUIFrames();
LOG_DEBUG("Showing %d module frames\n", moduleFrames.size());
#ifdef DEBUG_PORT
@@ -1455,7 +2071,7 @@ void Screen::setFrames()
LOG_DEBUG("Total frame count: %d\n", totalFrameCount);
#endif
// We don't show the node info our our node (if we have it yet - we should)
// We don't show the node info of our node (if we have it yet - we should)
size_t numMeshNodes = nodeDB->getNumMeshNodes();
if (numMeshNodes > 0)
numMeshNodes--;
@@ -1478,6 +2094,10 @@ void Screen::setFrames()
if (error_code)
normalFrames[numframes++] = drawCriticalFaultFrame;
#ifdef T_WATCH_S3
normalFrames[numframes++] = screen->digitalWatchFace ? &Screen::drawDigitalClockFrame : &Screen::drawAnalogClockFrame;
#endif
// If we have a text message - show it next, unless it's a phone message and we aren't using any special modules
if (devicestate.has_rx_text_message && shouldDrawMessage(&devicestate.rx_text_message)) {
normalFrames[numframes++] = drawTextMessageFrame;
@@ -2058,6 +2678,21 @@ int Screen::handleUIFrameEvent(const UIFrameEvent *event)
int Screen::handleInputEvent(const InputEvent *event)
{
#ifdef T_WATCH_S3
// For the T-Watch, intercept touches to the 'toggle digital/analog watch face' button
uint8_t watchFaceFrame = error_code ? 1 : 0;
if (this->ui->getUiState()->currentFrame == watchFaceFrame && event->touchX >= 204 && event->touchX <= 240 &&
event->touchY >= 204 && event->touchY <= 240) {
screen->digitalWatchFace = !screen->digitalWatchFace;
setFrames();
return 0;
}
#endif
if (showingNormalScreen && moduleFrames.size() == 0) {
// LOG_DEBUG("Screen::handleInputEvent from %s\n", event->source);
if (event->inputEvent == static_cast<char>(meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_LEFT)) {
+27 -1
View File
@@ -48,6 +48,7 @@ class Screen
#include "EInkDisplay2.h"
#include "EInkDynamicDisplay.h"
#include "PointStruct.h"
#include "TFTDisplay.h"
#include "TypedQueue.h"
#include "commands.h"
@@ -77,6 +78,10 @@ class Screen
#define EINK_BLACK OLEDDISPLAY_COLOR::WHITE
#define EINK_WHITE OLEDDISPLAY_COLOR::BLACK
// Base segment dimensions for T-Watch segmented display
#define SEGMENT_WIDTH 16
#define SEGMENT_HEIGHT 4
namespace graphics
{
@@ -355,7 +360,7 @@ class Screen : public concurrency::OSThread
bool enqueueCmd(const ScreenCmd &cmd)
{
if (!useDisplay)
return true; // claim success if our display is not in use
return false; // not enqueued if our display is not in use
else {
bool success = cmdQueue.enqueue(cmd, 0);
enabled = true; // handle ASAP (we are the registered reader for cmdQueue, but might have been disabled)
@@ -389,6 +394,27 @@ class Screen : public concurrency::OSThread
static void drawDebugInfoWiFiTrampoline(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y);
#ifdef T_WATCH_S3
static void drawAnalogClockFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y);
static void drawDigitalClockFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y);
static void drawSegmentedDisplayCharacter(OLEDDisplay *display, int x, int y, uint8_t number, float scale = 1);
static void drawHorizontalSegment(OLEDDisplay *display, int x, int y, int width, int height);
static void drawVerticalSegment(OLEDDisplay *display, int x, int y, int width, int height);
static void drawSegmentedDisplayColon(OLEDDisplay *display, int x, int y, float scale = 1);
static void drawWatchFaceToggleButton(OLEDDisplay *display, int16_t x, int16_t y, bool digitalMode = true, float scale = 1);
static void drawBluetoothConnectedIcon(OLEDDisplay *display, int16_t x, int16_t y);
// Whether we are showing the digital watch face or the analog one
bool digitalWatchFace = true;
#endif
/// Queue of commands to execute in doTask.
TypedQueue<ScreenCmd> cmdQueue;
/// Whether we are using a display
+173
View File
@@ -14,6 +14,12 @@ const uint8_t imgUser[] PROGMEM = {0x3C, 0x42, 0x99, 0xA5, 0xA5, 0x99, 0x42, 0x3
const uint8_t imgPositionEmpty[] PROGMEM = {0x20, 0x30, 0x28, 0x24, 0x42, 0xFF};
const uint8_t imgPositionSolid[] PROGMEM = {0x20, 0x30, 0x38, 0x3C, 0x7E, 0xFF};
#ifdef T_WATCH_S3
const uint8_t bluetoothConnectedIcon[36] PROGMEM = {0xfe, 0x01, 0xff, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x00, 0xe3, 0x1f,
0xf3, 0x3f, 0x33, 0x30, 0x33, 0x33, 0x33, 0x33, 0x03, 0x33, 0xff, 0x33,
0xfe, 0x31, 0x00, 0x30, 0x30, 0x30, 0x30, 0x30, 0xf0, 0x3f, 0xe0, 0x1f};
#endif
#if (defined(USE_EINK) || defined(ILI9341_DRIVER) || defined(ST7735_CS) || defined(ST7789_CS) || defined(HX8357_CS) || \
ARCH_PORTDUINO) && \
!defined(DISPLAY_FORCE_SMALL_FONTS)
@@ -31,4 +37,171 @@ const uint8_t imgQuestion[] PROGMEM = {0xbf, 0x41, 0xc0, 0x8b, 0xdb, 0x70, 0xa1,
const uint8_t imgSF[] PROGMEM = {0xd2, 0xb7, 0xad, 0xbb, 0x92, 0x01, 0xfd, 0xfd, 0x15, 0x85, 0xf5};
#endif
#ifndef EXCLUDE_EMOJI
#define thumbs_height 25
#define thumbs_width 25
static 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,
};
static 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,
};
#define question_height 25
#define question_width 25
static 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,
};
#define bang_height 30
#define bang_width 30
static 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,
};
#define haha_height 30
#define haha_width 30
static unsigned char haha[] PROGMEM = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0x01, 0x00,
0x00, 0xFC, 0x0F, 0x00, 0x00, 0x1F, 0x3E, 0x00, 0x80, 0x03, 0x70, 0x00, 0xC0, 0x01, 0xE0, 0x00, 0xC0, 0x00, 0xC2, 0x00,
0x60, 0x00, 0x03, 0x00, 0x60, 0x00, 0xC1, 0x1F, 0x60, 0x80, 0x8F, 0x31, 0x30, 0x0E, 0x80, 0x31, 0x30, 0x10, 0x30, 0x1F,
0x30, 0x08, 0x58, 0x00, 0x30, 0x04, 0x6C, 0x03, 0x60, 0x00, 0xF3, 0x01, 0x60, 0xC0, 0xFC, 0x01, 0x80, 0x38, 0xBF, 0x01,
0xE0, 0xC5, 0xDF, 0x00, 0xB0, 0xF9, 0xEF, 0x00, 0x30, 0xF1, 0x73, 0x00, 0xB0, 0x1D, 0x3E, 0x00, 0xF0, 0xFD, 0x0F, 0x00,
0xE0, 0xE0, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
};
#define wave_icon_height 30
#define wave_icon_width 30
static unsigned char wave_icon[] PROGMEM = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0xC0, 0x00,
0x00, 0x0C, 0x9C, 0x01, 0x80, 0x17, 0x20, 0x01, 0x80, 0x26, 0x46, 0x02, 0x80, 0x44, 0x88, 0x02, 0xC0, 0x89, 0x8A, 0x02,
0x40, 0x93, 0x8B, 0x02, 0x40, 0x26, 0x13, 0x00, 0x80, 0x44, 0x16, 0x00, 0xC0, 0x89, 0x24, 0x00, 0x40, 0x93, 0x60, 0x00,
0x40, 0x26, 0x40, 0x00, 0x80, 0x0C, 0x80, 0x00, 0x00, 0x09, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x40, 0x06, 0x80, 0x00,
0x50, 0x0C, 0x80, 0x00, 0x50, 0x08, 0x40, 0x00, 0x90, 0x10, 0x20, 0x00, 0xB0, 0x21, 0x10, 0x00, 0x20, 0x47, 0x18, 0x00,
0x40, 0x80, 0x0F, 0x00, 0x80, 0x01, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
};
#define cowboy_height 30
#define cowboy_width 30
static unsigned char cowboy[] PROGMEM = {
0x00, 0xF0, 0x03, 0x00, 0x00, 0xFC, 0x0F, 0x00, 0x00, 0xFE, 0x1F, 0x00, 0x00, 0xFF, 0x3F, 0x00, 0x3C, 0xFE, 0x1F, 0x0F,
0xFE, 0xFE, 0xDF, 0x1F, 0xFF, 0xFF, 0xFF, 0x3F, 0xFF, 0xFF, 0xFF, 0x3F, 0xFF, 0xFF, 0xFF, 0x3F, 0xFF, 0xFF, 0xFF, 0x3F,
0x3E, 0xC0, 0x00, 0x1F, 0x1E, 0x00, 0x00, 0x1E, 0x0C, 0x0C, 0x0C, 0x0C, 0x08, 0x0E, 0x1C, 0x04, 0x00, 0x0E, 0x1C, 0x00,
0x04, 0x0E, 0x1C, 0x08, 0x04, 0x0E, 0x1C, 0x08, 0x04, 0x04, 0x08, 0x08, 0x04, 0x00, 0x00, 0x08, 0x04, 0x00, 0x00, 0x08,
0x8C, 0x07, 0x70, 0x0C, 0x88, 0xFC, 0x4F, 0x04, 0x88, 0x01, 0x40, 0x04, 0x90, 0xFF, 0x7F, 0x02, 0x30, 0x03, 0x30, 0x03,
0x60, 0x0E, 0x9C, 0x01, 0xC0, 0xF8, 0xC7, 0x00, 0x80, 0x01, 0x60, 0x00, 0x00, 0x0E, 0x1C, 0x00, 0x00, 0xF8, 0x07, 0x00,
};
#define deadmau5_height 30
#define deadmau5_width 60
static 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,
};
#define sun_width 30
#define sun_height 30
static 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,
};
#define rain_width 30
#define rain_height 30
static 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,
};
#define cloud_height 30
#define cloud_width 30
static 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,
};
#define fog_height 25
#define fog_width 25
static 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,
};
#define devil_height 30
#define devil_width 30
static unsigned char devil[] PROGMEM = {
0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x10, 0x03, 0xC0, 0x01, 0x38, 0x07, 0x7C, 0x0F, 0x38, 0x1F, 0x03, 0x30, 0x1E,
0xFE, 0x01, 0xE0, 0x1F, 0x7E, 0x00, 0x80, 0x1F, 0x3C, 0x00, 0x00, 0x0F, 0x1C, 0x00, 0x00, 0x0E, 0x18, 0x00, 0x00, 0x06,
0x08, 0x00, 0x00, 0x04, 0x0C, 0x00, 0x00, 0x0C, 0x0C, 0x00, 0x00, 0x0C, 0x0C, 0x00, 0x00, 0x0C, 0x0C, 0x0E, 0x1C, 0x0C,
0x0C, 0x18, 0x06, 0x0C, 0x0C, 0x1C, 0x06, 0x0C, 0x0C, 0x1C, 0x0E, 0x0C, 0x0C, 0x1C, 0x0E, 0x0C, 0x0C, 0x0C, 0x06, 0x0C,
0x08, 0x00, 0x00, 0x06, 0x18, 0x02, 0x10, 0x06, 0x10, 0x0C, 0x0C, 0x03, 0x30, 0xF8, 0x07, 0x03, 0x60, 0xE0, 0x80, 0x01,
0xC0, 0x00, 0xC0, 0x00, 0x80, 0x01, 0x70, 0x00, 0x00, 0x06, 0x1C, 0x00, 0x00, 0xF0, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00,
};
#define heart_height 30
#define heart_width 30
static 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,
};
#define poo_width 30
#define poo_height 30
static unsigned char poo[] PROGMEM = {
0x00, 0x1C, 0x00, 0x00, 0x00, 0x78, 0x00, 0x00, 0x00, 0xEC, 0x01, 0x00, 0x00, 0x8C, 0x07, 0x00, 0x00, 0x0C, 0x06, 0x00,
0x00, 0x24, 0x0C, 0x00, 0x00, 0x34, 0x08, 0x00, 0x00, 0x1F, 0x08, 0x00, 0xC0, 0x0F, 0x08, 0x00, 0xC0, 0x00, 0x3C, 0x00,
0x60, 0x00, 0x7C, 0x00, 0x60, 0x00, 0xC6, 0x00, 0x20, 0x00, 0xCB, 0x00, 0xA0, 0xC7, 0xFF, 0x00, 0xE0, 0x7F, 0xF7, 0x00,
0xF0, 0x18, 0xE3, 0x03, 0x78, 0x18, 0x41, 0x03, 0x6C, 0x9B, 0x5D, 0x06, 0x64, 0x9B, 0x5D, 0x04, 0x44, 0x1A, 0x41, 0x04,
0x4C, 0xD8, 0x63, 0x06, 0xF8, 0xFC, 0x36, 0x06, 0xFE, 0x0F, 0x9C, 0x1F, 0x07, 0x03, 0xC0, 0x30, 0x03, 0x00, 0x78, 0x20,
0x01, 0x00, 0x1F, 0x20, 0x03, 0xE0, 0x03, 0x20, 0x07, 0x7E, 0x04, 0x30, 0xFE, 0x0F, 0xFC, 0x1F, 0xF0, 0x00, 0xF0, 0x0F,
};
#endif
#include "img/icon.xbm"
+2
View File
@@ -8,6 +8,8 @@ typedef struct _InputEvent {
const char *source;
char inputEvent;
char kbchar;
uint16_t touchX;
uint16_t touchY;
} InputEvent;
class InputBroker : public Observable<const InputEvent *>
{
+3
View File
@@ -155,6 +155,9 @@ int32_t LinuxInput::runOnce()
case KEY_ENTER: // Enter
e.inputEvent = meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_SELECT;
break;
case KEY_POWER:
system("poweroff");
break;
default: // all other keys
if (keymap[code]) {
e.inputEvent = ANYKEY;
+4
View File
@@ -49,6 +49,10 @@ void TouchScreenImpl1::onEvent(const TouchEvent &event)
{
InputEvent e;
e.source = event.source;
e.touchX = event.x;
e.touchY = event.y;
switch (event.touchEvent) {
case TOUCH_ACTION_LEFT: {
e.inputEvent = static_cast<char>(meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_RIGHT);
+33
View File
@@ -65,6 +65,8 @@ NRF52Bluetooth *nrf52Bluetooth;
#endif
#include "LLCC68Interface.h"
#include "LR1110Interface.h"
#include "LR1120Interface.h"
#include "RF95Interface.h"
#include "SX1262Interface.h"
#include "SX1268Interface.h"
@@ -533,7 +535,12 @@ void setup()
SCANNER_TO_SENSORS_MAP(ScanI2C::DeviceType::PMSA0031, meshtastic_TelemetrySensorType_PMSA003I)
SCANNER_TO_SENSORS_MAP(ScanI2C::DeviceType::RCWL9620, meshtastic_TelemetrySensorType_RCWL9620)
SCANNER_TO_SENSORS_MAP(ScanI2C::DeviceType::VEML7700, meshtastic_TelemetrySensorType_VEML7700)
SCANNER_TO_SENSORS_MAP(ScanI2C::DeviceType::TSL2591, meshtastic_TelemetrySensorType_TSL25911FN)
SCANNER_TO_SENSORS_MAP(ScanI2C::DeviceType::OPT3001, meshtastic_TelemetrySensorType_OPT3001)
SCANNER_TO_SENSORS_MAP(ScanI2C::DeviceType::MLX90632, meshtastic_TelemetrySensorType_MLX90632)
SCANNER_TO_SENSORS_MAP(ScanI2C::DeviceType::SHT4X, meshtastic_TelemetrySensorType_SHT4X)
SCANNER_TO_SENSORS_MAP(ScanI2C::DeviceType::AHT10, meshtastic_TelemetrySensorType_AHT10)
SCANNER_TO_SENSORS_MAP(ScanI2C::DeviceType::DFROBOT_LARK, meshtastic_TelemetrySensorType_DFROBOT_LARK)
i2cScanner.reset();
@@ -878,6 +885,32 @@ void setup()
}
#endif
#if defined(USE_LR1110)
if (!rIf) {
rIf = new LR1110Interface(RadioLibHAL, LR1110_SPI_NSS_PIN, LR1110_IRQ_PIN, LR1110_NRESER_PIN, LR1110_BUSY_PIN);
if (!rIf->init()) {
LOG_WARN("Failed to find LR1110 radio\n");
delete rIf;
rIf = NULL;
} else {
LOG_INFO("LR1110 Radio init succeeded, using LR1110 radio\n");
}
}
#endif
#if defined(USE_LR1120)
if (!rIf) {
rIf = new LR1120Interface(RadioLibHAL, LR1120_SPI_NSS_PIN, LR1120_IRQ_PIN, LR1120_NRESER_PIN, LR1120_BUSY_PIN);
if (!rIf->init()) {
LOG_WARN("Failed to find LR1120 radio\n");
delete rIf;
rIf = NULL;
} else {
LOG_INFO("LR1120 Radio init succeeded, using LR1120 radio\n");
}
}
#endif
#if defined(USE_SX1280)
if (!rIf) {
rIf = new SX1280Interface(RadioLibHAL, SX128X_CS, SX128X_DIO1, SX128X_RESET, SX128X_BUSY);
+4
View File
@@ -1,3 +1,5 @@
#include "LR11x0Interface.cpp"
#include "LR11x0Interface.h"
#include "SX126xInterface.cpp"
#include "SX126xInterface.h"
#include "SX128xInterface.cpp"
@@ -10,6 +12,8 @@ template class SX126xInterface<SX1262>;
template class SX126xInterface<SX1268>;
template class SX126xInterface<LLCC68>;
template class SX128xInterface<SX1280>;
template class LR11x0Interface<LR1110>;
template class LR11x0Interface<LR1120>;
#ifdef ARCH_STM32WL
template class SX126xInterface<STM32WLx>;
#endif
+9
View File
@@ -0,0 +1,9 @@
#include "LR1110Interface.h"
#include "configuration.h"
#include "error.h"
LR1110Interface::LR1110Interface(LockingArduinoHal *hal, RADIOLIB_PIN_TYPE cs, RADIOLIB_PIN_TYPE irq, RADIOLIB_PIN_TYPE rst,
RADIOLIB_PIN_TYPE busy)
: LR11x0Interface(hal, cs, irq, rst, busy)
{
}
+13
View File
@@ -0,0 +1,13 @@
#pragma once
#include "LR11x0Interface.h"
/**
* Our adapter for LR1110 radios
*/
class LR1110Interface : public LR11x0Interface<LR1110>
{
public:
LR1110Interface(LockingArduinoHal *hal, RADIOLIB_PIN_TYPE cs, RADIOLIB_PIN_TYPE irq, RADIOLIB_PIN_TYPE rst,
RADIOLIB_PIN_TYPE busy);
};
+9
View File
@@ -0,0 +1,9 @@
#include "LR1120Interface.h"
#include "configuration.h"
#include "error.h"
LR1120Interface::LR1120Interface(LockingArduinoHal *hal, RADIOLIB_PIN_TYPE cs, RADIOLIB_PIN_TYPE irq, RADIOLIB_PIN_TYPE rst,
RADIOLIB_PIN_TYPE busy)
: LR11x0Interface(hal, cs, irq, rst, busy)
{
}
+13
View File
@@ -0,0 +1,13 @@
#pragma once
#include "LR11x0Interface.h"
/**
* Our adapter for LR1120 wideband radios
*/
class LR1120Interface : public LR11x0Interface<LR1120>
{
public:
LR1120Interface(LockingArduinoHal *hal, RADIOLIB_PIN_TYPE cs, RADIOLIB_PIN_TYPE irq, RADIOLIB_PIN_TYPE rst,
RADIOLIB_PIN_TYPE busy);
};
+299
View File
@@ -0,0 +1,299 @@
#include "LR11x0Interface.h"
#include "configuration.h"
#include "error.h"
#include "mesh/NodeDB.h"
#ifdef ARCH_PORTDUINO
#include "PortduinoGlue.h"
#endif
// Particular boards might define a different max power based on what their hardware can do, default to max power output if not
// specified (may be dangerous if using external PA and LR11x0 power config forgotten)
#ifndef LR11X0_MAX_POWER
#define LR11X0_MAX_POWER 22
#endif
template <typename T>
LR11x0Interface<T>::LR11x0Interface(LockingArduinoHal *hal, RADIOLIB_PIN_TYPE cs, RADIOLIB_PIN_TYPE irq, RADIOLIB_PIN_TYPE rst,
RADIOLIB_PIN_TYPE busy)
: RadioLibInterface(hal, cs, irq, rst, busy, &lora), lora(&module)
{
LOG_WARN("LR11x0Interface(cs=%d, irq=%d, rst=%d, busy=%d)\n", cs, irq, rst, busy);
}
/// Initialise the Driver transport hardware and software.
/// Make sure the Driver is properly configured before calling init().
/// \return true if initialisation succeeded.
template <typename T> bool LR11x0Interface<T>::init()
{
#ifdef LR11X0_POWER_EN
pinMode(LR11X0_POWER_EN, OUTPUT);
digitalWrite(LR11X0_POWER_EN, HIGH);
#endif
// FIXME: correct logic to default to not using TCXO if no voltage is specified for LR11x0_DIO3_TCXO_VOLTAGE
#if !defined(LR11X0_DIO3_TCXO_VOLTAGE)
float tcxoVoltage =
0; // "TCXO reference voltage to be set on DIO3. Defaults to 1.6 V, set to 0 to skip." per
// https://github.com/jgromes/RadioLib/blob/690a050ebb46e6097c5d00c371e961c1caa3b52e/src/modules/LR11x0/LR11x0.h#L471C26-L471C104
// (DIO3 is free to be used as an IRQ)
LOG_DEBUG("LR11X0_DIO3_TCXO_VOLTAGE not defined, not using DIO3 as TCXO reference voltage\n");
#else
float tcxoVoltage = LR11X0_DIO3_TCXO_VOLTAGE;
LOG_DEBUG("LR11X0_DIO3_TCXO_VOLTAGE defined, using DIO3 as TCXO reference voltage at %f V\n", LR11X0_DIO3_TCXO_VOLTAGE);
// (DIO3 is not free to be used as an IRQ)
#endif
RadioLibInterface::init();
if (power > LR11X0_MAX_POWER) // Clamp power to maximum defined level
power = LR11X0_MAX_POWER;
limitPower();
// set RF switch configuration for Wio WM1110
// Wio WM1110 uses DIO5 and DIO6 for RF switching
// NOTE: other boards may be different. If you are
// using a different board, you may need to wrap
// this in a conditional.
static const uint32_t rfswitch_dio_pins[] = {RADIOLIB_LR11X0_DIO5, RADIOLIB_LR11X0_DIO6, RADIOLIB_NC, RADIOLIB_NC,
RADIOLIB_NC};
static const Module::RfSwitchMode_t rfswitch_table[] = {
// mode DIO5 DIO6
{LR11x0::MODE_STBY, {LOW, LOW}}, {LR11x0::MODE_RX, {HIGH, LOW}},
{LR11x0::MODE_TX, {HIGH, HIGH}}, {LR11x0::MODE_TX_HP, {LOW, HIGH}},
{LR11x0::MODE_TX_HF, {LOW, LOW}}, {LR11x0::MODE_GNSS, {LOW, LOW}},
{LR11x0::MODE_WIFI, {LOW, LOW}}, END_OF_MODE_TABLE,
};
// We need to do this before begin() call
#ifdef LR11X0_DIO_AS_RF_SWITCH
LOG_DEBUG("Setting DIO RF switch\n");
bool dioAsRfSwitch = true;
#elif defined(ARCH_PORTDUINO)
bool dioAsRfSwitch = false;
if (settingsMap[dio2_as_rf_switch]) {
LOG_DEBUG("Setting DIO RF switch\n");
dioAsRfSwitch = true;
}
#else
bool dioAsRfSwitch = false;
#endif
if (dioAsRfSwitch)
lora.setRfSwitchTable(rfswitch_dio_pins, rfswitch_table);
int res = lora.begin(getFreq(), bw, sf, cr, syncWord, power, preambleLength, tcxoVoltage);
// \todo Display actual typename of the adapter, not just `LR11x0`
LOG_INFO("LR11x0 init result %d\n", res);
if (res == RADIOLIB_ERR_CHIP_NOT_FOUND)
return false;
LOG_INFO("Frequency set to %f\n", getFreq());
LOG_INFO("Bandwidth set to %f\n", bw);
LOG_INFO("Power output set to %d\n", power);
if (res == RADIOLIB_ERR_NONE)
res = lora.setCRC(2);
// FIXME: May want to set depending on a definition, currently all LR1110 variant files use the DC-DC regulator option
if (res == RADIOLIB_ERR_NONE)
res = lora.setRegulatorDCDC();
if (res == RADIOLIB_ERR_NONE) {
if (config.lora.sx126x_rx_boosted_gain) { // the name is unfortunate but historically accurate
res = lora.setRxBoostedGainMode(true);
LOG_INFO("Set RX gain to boosted mode; result: %d\n", res);
} else {
res = lora.setRxBoostedGainMode(false);
LOG_INFO("Set RX gain to power saving mode (boosted mode off); result: %d\n", res);
}
}
if (res == RADIOLIB_ERR_NONE)
startReceive(); // start receiving
return res == RADIOLIB_ERR_NONE;
}
template <typename T> bool LR11x0Interface<T>::reconfigure()
{
RadioLibInterface::reconfigure();
// set mode to standby
setStandby();
// configure publicly accessible settings
int err = lora.setSpreadingFactor(sf);
if (err != RADIOLIB_ERR_NONE)
RECORD_CRITICALERROR(meshtastic_CriticalErrorCode_INVALID_RADIO_SETTING);
err = lora.setBandwidth(bw);
if (err != RADIOLIB_ERR_NONE)
RECORD_CRITICALERROR(meshtastic_CriticalErrorCode_INVALID_RADIO_SETTING);
err = lora.setCodingRate(cr);
if (err != RADIOLIB_ERR_NONE)
RECORD_CRITICALERROR(meshtastic_CriticalErrorCode_INVALID_RADIO_SETTING);
// Hmm - seems to lower SNR when the signal levels are high. Leaving off for now...
// TODO: Confirm gain registers are okay now
// err = lora.setRxGain(true);
// assert(err == RADIOLIB_ERR_NONE);
err = lora.setSyncWord(syncWord);
assert(err == RADIOLIB_ERR_NONE);
err = lora.setPreambleLength(preambleLength);
assert(err == RADIOLIB_ERR_NONE);
err = lora.setFrequency(getFreq());
if (err != RADIOLIB_ERR_NONE)
RECORD_CRITICALERROR(meshtastic_CriticalErrorCode_INVALID_RADIO_SETTING);
if (power > LR11X0_MAX_POWER) // This chip has lower power limits than some
power = LR11X0_MAX_POWER;
err = lora.setOutputPower(power);
assert(err == RADIOLIB_ERR_NONE);
startReceive(); // restart receiving
return RADIOLIB_ERR_NONE;
}
template <typename T> void INTERRUPT_ATTR LR11x0Interface<T>::disableInterrupt()
{
lora.clearIrqAction();
}
template <typename T> void LR11x0Interface<T>::setStandby()
{
checkNotification(); // handle any pending interrupts before we force standby
int err = lora.standby();
if (err != RADIOLIB_ERR_NONE) {
LOG_DEBUG("LR11x0 standby failed with error %d\n", err);
}
assert(err == RADIOLIB_ERR_NONE);
isReceiving = false; // If we were receiving, not any more
activeReceiveStart = 0;
disableInterrupt();
completeSending(); // If we were sending, not anymore
}
/**
* Add SNR data to received messages
*/
template <typename T> void LR11x0Interface<T>::addReceiveMetadata(meshtastic_MeshPacket *mp)
{
// LOG_DEBUG("PacketStatus %x\n", lora.getPacketStatus());
mp->rx_snr = lora.getSNR();
mp->rx_rssi = lround(lora.getRSSI());
}
/** We override to turn on transmitter power as needed.
*/
template <typename T> void LR11x0Interface<T>::configHardwareForSend()
{
RadioLibInterface::configHardwareForSend();
}
// For power draw measurements, helpful to force radio to stay sleeping
// #define SLEEP_ONLY
template <typename T> void LR11x0Interface<T>::startReceive()
{
#ifdef SLEEP_ONLY
sleep();
#else
setStandby();
lora.setPreambleLength(preambleLength); // Solve RX ack fail after direct message sent. Not sure why this is needed.
// We use a 16 bit preamble so this should save some power by letting radio sit in standby mostly.
// Furthermore, we need the PREAMBLE_DETECTED and HEADER_VALID IRQ flag to detect whether we are actively receiving
int err = lora.startReceive(
RADIOLIB_LR11X0_RX_TIMEOUT_INF, RADIOLIB_LR11X0_IRQ_RX_DONE,
0); // only RX_DONE IRQ is needed, we'll check for PREAMBLE_DETECTED and HEADER_VALID in isActivelyReceiving
assert(err == RADIOLIB_ERR_NONE);
isReceiving = true;
// Must be done AFTER, starting transmit, because startTransmit clears (possibly stale) interrupt pending register bits
enableInterrupt(isrRxLevel0);
#endif
}
/** Is the channel currently active? */
template <typename T> bool LR11x0Interface<T>::isChannelActive()
{
// check if we can detect a LoRa preamble on the current channel
int16_t result;
setStandby();
result = lora.scanChannel();
if (result == RADIOLIB_LORA_DETECTED)
return true;
assert(result != RADIOLIB_ERR_WRONG_MODEM);
return false;
}
/** Could we send right now (i.e. either not actively receiving or transmitting)? */
template <typename T> bool LR11x0Interface<T>::isActivelyReceiving()
{
// The IRQ status will be cleared when we start our read operation. Check if we've started a header, but haven't yet
// received and handled the interrupt for reading the packet/handling errors.
uint16_t irq = lora.getIrqStatus();
bool detected = (irq & (RADIOLIB_LR11X0_IRQ_SYNC_WORD_HEADER_VALID | RADIOLIB_LR11X0_IRQ_PREAMBLE_DETECTED));
// Handle false detections
if (detected) {
uint32_t now = millis();
if (!activeReceiveStart) {
activeReceiveStart = now;
} else if ((now - activeReceiveStart > 2 * preambleTimeMsec) && !(irq & RADIOLIB_LR11X0_IRQ_SYNC_WORD_HEADER_VALID)) {
// The HEADER_VALID flag should be set by now if it was really a packet, so ignore PREAMBLE_DETECTED flag
activeReceiveStart = 0;
LOG_DEBUG("Ignore false preamble detection.\n");
return false;
} else if (now - activeReceiveStart > maxPacketTimeMsec) {
// We should have gotten an RX_DONE IRQ by now if it was really a packet, so ignore HEADER_VALID flag
activeReceiveStart = 0;
LOG_DEBUG("Ignore false header detection.\n");
return false;
}
}
// if (detected) LOG_DEBUG("rx detected\n");
return detected;
}
template <typename T> bool LR11x0Interface<T>::sleep()
{
// Not keeping config is busted - next time nrf52 board boots lora sending fails tcxo related? - see datasheet
// \todo Display actual typename of the adapter, not just `LR11x0`
LOG_DEBUG("LR11x0 entering sleep mode (FIXME, don't keep config)\n");
setStandby(); // Stop any pending operations
// turn off TCXO if it was powered
// FIXME - this isn't correct
// lora.setTCXO(0);
// put chipset into sleep mode (we've already disabled interrupts by now)
bool keepConfig = true;
lora.sleep(keepConfig, 0); // Note: we do not keep the config, full reinit will be needed
#ifdef LR11X0_POWER_EN
digitalWrite(LR11X0_POWER_EN, LOW);
#endif
return true;
}
+71
View File
@@ -0,0 +1,71 @@
#pragma once
#include "RadioLibInterface.h"
/**
* \brief Adapter for LR11x0 radio family. Implements common logic for child classes.
* \tparam T RadioLib module type for LR11x0: SX1262, SX1268.
*/
template <class T> class LR11x0Interface : public RadioLibInterface
{
public:
LR11x0Interface(LockingArduinoHal *hal, RADIOLIB_PIN_TYPE cs, RADIOLIB_PIN_TYPE irq, RADIOLIB_PIN_TYPE rst,
RADIOLIB_PIN_TYPE busy);
/// Initialise the Driver transport hardware and software.
/// Make sure the Driver is properly configured before calling init().
/// \return true if initialisation succeeded.
virtual bool init() override;
/// Apply any radio provisioning changes
/// Make sure the Driver is properly configured before calling init().
/// \return true if initialisation succeeded.
virtual bool reconfigure() override;
/// Prepare hardware for sleep. Call this _only_ for deep sleep, not needed for light sleep.
virtual bool sleep() override;
bool isIRQPending() override { return lora.getIrqStatus() != 0; }
protected:
/**
* Specific module instance
*/
T lora;
/**
* Glue functions called from ISR land
*/
virtual void disableInterrupt() override;
/**
* Enable a particular ISR callback glue function
*/
virtual void enableInterrupt(void (*callback)()) { lora.setIrqAction(callback); }
/** can we detect a LoRa preamble on the current channel? */
virtual bool isChannelActive() override;
/** are we actively receiving a packet (only called during receiving state) */
virtual bool isActivelyReceiving() override;
/**
* Start waiting to receive a message
*/
virtual void startReceive() override;
/**
* We override to turn on transmitter power as needed.
*/
virtual void configHardwareForSend() override;
/**
* Add SNR data to received messages
*/
virtual void addReceiveMetadata(meshtastic_MeshPacket *mp) override;
virtual void setStandby() override;
private:
uint32_t activeReceiveStart = 0;
};
+10 -6
View File
@@ -62,7 +62,10 @@ meshtastic_MeshPacket *MeshModule::allocAckNak(meshtastic_Routing_Error err, Nod
meshtastic_MeshPacket *MeshModule::allocErrorResponse(meshtastic_Routing_Error err, const meshtastic_MeshPacket *p)
{
auto r = allocAckNak(err, getFrom(p), p->id, p->channel);
// If the original packet couldn't be decoded, use the primary channel
uint8_t channelIndex =
p->which_payload_variant == meshtastic_MeshPacket_decoded_tag ? p->channel : channels.getPrimaryIndex();
auto r = allocAckNak(err, getFrom(p), p->id, channelIndex);
setReplyTo(r, *p);
@@ -114,13 +117,13 @@ void MeshModule::callModules(meshtastic_MeshPacket &mp, RxSource src)
/// Also: if a packet comes in on the local PC interface, we don't check for bound channels, because it is TRUSTED and
/// it needs to to be able to fetch the initial admin packets without yet knowing any channels.
bool rxChannelOk = !pi.boundChannel || (mp.from == 0) || (strcasecmp(ch->settings.name, pi.boundChannel) == 0);
bool rxChannelOk = !pi.boundChannel || (mp.from == 0) || (ch && strcasecmp(ch->settings.name, pi.boundChannel) == 0);
if (!rxChannelOk) {
// no one should have already replied!
assert(!currentReply);
if (mp.decoded.want_response) {
if (isDecoded && mp.decoded.want_response) {
printPacket("packet on wrong channel, returning error", &mp);
currentReply = pi.allocErrorResponse(meshtastic_Routing_Error_NOT_AUTHORIZED, &mp);
} else
@@ -138,7 +141,8 @@ void MeshModule::callModules(meshtastic_MeshPacket &mp, RxSource src)
// because currently when the phone sends things, it sends things using the local node ID as the from address. A
// better solution (FIXME) would be to let phones have their own distinct addresses and we 'route' to them like
// any other node.
if (mp.decoded.want_response && toUs && (getFrom(&mp) != ourNodeNum || mp.to == ourNodeNum) && !currentReply) {
if (isDecoded && mp.decoded.want_response && toUs && (getFrom(&mp) != ourNodeNum || mp.to == ourNodeNum) &&
!currentReply) {
pi.sendResponse(mp);
ignoreRequest = ignoreRequest || pi.ignoreRequest; // If at least one module asks it, we may ignore a request
LOG_INFO("Asked module '%s' to send a response\n", pi.name);
@@ -163,7 +167,7 @@ void MeshModule::callModules(meshtastic_MeshPacket &mp, RxSource src)
pi.currentRequest = NULL;
}
if (mp.decoded.want_response && toUs) {
if (isDecoded && mp.decoded.want_response && toUs) {
if (currentReply) {
printPacket("Sending response", currentReply);
service.sendToMesh(currentReply);
@@ -183,7 +187,7 @@ void MeshModule::callModules(meshtastic_MeshPacket &mp, RxSource src)
}
}
if (!moduleFound) {
if (!moduleFound && isDecoded) {
LOG_DEBUG("No modules interested in portnum=%d, src=%s\n", mp.decoded.portnum,
(src == RX_SRC_LOCAL) ? "LOCAL" : "REMOTE");
}
+4 -1
View File
@@ -292,6 +292,9 @@ void NodeDB::installDefaultConfig()
meshtastic_Config_PositionConfig_PositionFlags_SPEED | meshtastic_Config_PositionConfig_PositionFlags_HEADING |
meshtastic_Config_PositionConfig_PositionFlags_DOP | meshtastic_Config_PositionConfig_PositionFlags_SATINVIEW);
#ifdef RADIOMASTER_900_BANDIT_NANO
config.display.flip_screen = true;
#endif
#ifdef T_WATCH_S3
config.display.screen_on_secs = 30;
config.display.wake_on_tap_or_motion = true;
@@ -441,9 +444,9 @@ void NodeDB::installDefaultChannels()
void NodeDB::resetNodes()
{
clearLocalPosition();
numMeshNodes = 1;
std::fill(devicestate.node_db_lite.begin() + 1, devicestate.node_db_lite.end(), meshtastic_NodeInfoLite());
clearLocalPosition();
saveDeviceStateToDisk();
if (neighborInfoModule && moduleConfig.neighbor_info.enabled)
neighborInfoModule->resetNeighbors();
+49 -32
View File
@@ -140,16 +140,18 @@ bool PhoneAPI::handleToRadio(const uint8_t *buf, size_t bufLength)
*
* We assume buf is at least FromRadio_size bytes long.
*
* Our sending states progress in the following sequence (the client app ASSUMES THIS SEQUENCE, DO NOT CHANGE IT):
* STATE_SEND_MY_INFO, // send our my info record
* STATE_SEND_CHANNELS
* STATE_SEND_NODEINFO, // states progress in this order as the device sends to the client
STATE_SEND_CONFIG,
STATE_SEND_MODULE_CONFIG,
STATE_SEND_METADATA,
STATE_SEND_COMPLETE_ID,
STATE_SEND_PACKETS // send packets or debug strings
* Our sending states progress in the following sequence (the client apps ASSUME THIS SEQUENCE, DO NOT CHANGE IT):
STATE_SEND_MY_INFO, // send our my info record
STATE_SEND_OWN_NODEINFO,
STATE_SEND_METADATA,
STATE_SEND_CHANNELS
STATE_SEND_CONFIG,
STATE_SEND_MODULE_CONFIG,
STATE_SEND_OTHER_NODEINFOS, // states progress in this order as the device sends to the client
STATE_SEND_COMPLETE_ID,
STATE_SEND_PACKETS // send packets or debug strings
*/
size_t PhoneAPI::getFromRadio(uint8_t *buf)
{
if (!available()) {
@@ -171,37 +173,32 @@ size_t PhoneAPI::getFromRadio(uint8_t *buf)
// app not to send locations on our behalf.
fromRadioScratch.which_payload_variant = meshtastic_FromRadio_my_info_tag;
fromRadioScratch.my_info = myNodeInfo;
state = STATE_SEND_METADATA;
state = STATE_SEND_OWN_NODEINFO;
service.refreshLocalMeshNode(); // Update my NodeInfo because the client will be asking for it soon.
break;
case STATE_SEND_OWN_NODEINFO: {
LOG_INFO("getFromRadio=STATE_SEND_OWN_NODEINFO\n");
auto us = nodeDB->readNextMeshNode(readIndex);
if (us) {
nodeInfoForPhone = TypeConversions::ConvertToNodeInfo(us);
fromRadioScratch.which_payload_variant = meshtastic_FromRadio_node_info_tag;
fromRadioScratch.node_info = nodeInfoForPhone;
// Should allow us to resume sending NodeInfo in STATE_SEND_OTHER_NODEINFOS
nodeInfoForPhone.num = 0;
}
state = STATE_SEND_METADATA;
break;
}
case STATE_SEND_METADATA:
LOG_INFO("getFromRadio=STATE_SEND_METADATA\n");
fromRadioScratch.which_payload_variant = meshtastic_FromRadio_metadata_tag;
fromRadioScratch.metadata = getDeviceMetadata();
state = STATE_SEND_NODEINFO;
state = STATE_SEND_CHANNELS;
break;
case STATE_SEND_NODEINFO: {
LOG_INFO("getFromRadio=STATE_SEND_NODEINFO\n");
if (nodeInfoForPhone.num != 0) {
LOG_INFO("nodeinfo: num=0x%x, lastseen=%u, id=%s, name=%s\n", nodeInfoForPhone.num, nodeInfoForPhone.last_heard,
nodeInfoForPhone.user.id, nodeInfoForPhone.user.long_name);
fromRadioScratch.which_payload_variant = meshtastic_FromRadio_node_info_tag;
fromRadioScratch.node_info = nodeInfoForPhone;
// Stay in current state until done sending nodeinfos
nodeInfoForPhone.num = 0; // We just consumed a nodeinfo, will need a new one next time
} else {
LOG_INFO("Done sending nodeinfos\n");
state = STATE_SEND_CHANNELS;
// Go ahead and send that ID right now
return getFromRadio(buf);
}
break;
}
case STATE_SEND_CHANNELS:
LOG_INFO("getFromRadio=STATE_SEND_CHANNELS\n");
fromRadioScratch.which_payload_variant = meshtastic_FromRadio_channel_tag;
@@ -325,11 +322,30 @@ size_t PhoneAPI::getFromRadio(uint8_t *buf)
config_state++;
// Advance when we have sent all of our ModuleConfig objects
if (config_state > (_meshtastic_AdminMessage_ModuleConfigType_MAX + 1)) {
state = STATE_SEND_COMPLETE_ID;
// Clients sending special nonce don't want to see other nodeinfos
state = config_nonce == SPECIAL_NONCE ? STATE_SEND_COMPLETE_ID : STATE_SEND_OTHER_NODEINFOS;
config_state = 0;
}
break;
case STATE_SEND_OTHER_NODEINFOS: {
LOG_INFO("getFromRadio=STATE_SEND_OTHER_NODEINFOS\n");
if (nodeInfoForPhone.num != 0) {
LOG_INFO("nodeinfo: num=0x%x, lastseen=%u, id=%s, name=%s\n", nodeInfoForPhone.num, nodeInfoForPhone.last_heard,
nodeInfoForPhone.user.id, nodeInfoForPhone.user.long_name);
fromRadioScratch.which_payload_variant = meshtastic_FromRadio_node_info_tag;
fromRadioScratch.node_info = nodeInfoForPhone;
// Stay in current state until done sending nodeinfos
nodeInfoForPhone.num = 0; // We just consumed a nodeinfo, will need a new one next time
} else {
LOG_INFO("Done sending nodeinfos\n");
state = STATE_SEND_COMPLETE_ID;
// Go ahead and send that ID right now
return getFromRadio(buf);
}
break;
}
case STATE_SEND_COMPLETE_ID:
LOG_INFO("getFromRadio=STATE_SEND_COMPLETE_ID\n");
fromRadioScratch.which_payload_variant = meshtastic_FromRadio_config_complete_id_tag;
@@ -422,10 +438,11 @@ bool PhoneAPI::available()
case STATE_SEND_CONFIG:
case STATE_SEND_MODULECONFIG:
case STATE_SEND_METADATA:
case STATE_SEND_OWN_NODEINFO:
case STATE_SEND_COMPLETE_ID:
return true;
case STATE_SEND_NODEINFO:
case STATE_SEND_OTHER_NODEINFOS:
if (nodeInfoForPhone.num == 0) {
auto nextNode = nodeDB->readNextMeshNode(readIndex);
if (nextNode) {
+8 -6
View File
@@ -6,6 +6,7 @@
// Make sure that we never let our packets grow too large for one BLE packet
#define MAX_TO_FROM_RADIO_SIZE 512
#define SPECIAL_NONCE 69420
/**
* Provides our protobuf based API which phone/PC clients can use to talk to our device
@@ -20,13 +21,14 @@ class PhoneAPI
: public Observer<uint32_t> // FIXME, we shouldn't be inheriting from Observer, instead use CallbackObserver as a member
{
enum State {
STATE_SEND_NOTHING, // Initial state, don't send anything until the client starts asking for config
STATE_SEND_MY_INFO, // send our my info record
STATE_SEND_NODEINFO, // states progress in this order as the device sends to to the client
STATE_SEND_CHANNELS, // Send all channels
STATE_SEND_CONFIG, // Replacement for the old Radioconfig
STATE_SEND_MODULECONFIG, // Send Module specific config
STATE_SEND_NOTHING, // Initial state, don't send anything until the client starts asking for config
STATE_SEND_MY_INFO, // send our my info record
STATE_SEND_OWN_NODEINFO,
STATE_SEND_METADATA,
STATE_SEND_CHANNELS, // Send all channels
STATE_SEND_CONFIG, // Replacement for the old Radioconfig
STATE_SEND_MODULECONFIG, // Send Module specific config
STATE_SEND_OTHER_NODEINFOS, // states progress in this order as the device sends to to the client
STATE_SEND_COMPLETE_ID,
STATE_SEND_PACKETS // send packets or debug strings
};
+87 -6
View File
@@ -8,12 +8,57 @@
#include "PortduinoGlue.h"
#endif
#define MAX_POWER 20
#ifndef RF95_MAX_POWER
#define RF95_MAX_POWER 20
#endif
// if we use 20 we are limited to 1% duty cycle or hw might overheat. For continuous operation set a limit of 17
// In theory up to 27 dBm is possible, but the modules installed in most radios can cope with a max of 20. So BIG WARNING
// if you set power to something higher than 17 or 20 you might fry your board.
#define POWER_DEFAULT 17 // How much power to use if the user hasn't set a power level
#ifdef RADIOMASTER_900_BANDIT_NANO
// Structure to hold DAC and DB values
typedef struct {
uint8_t dac;
uint8_t db;
} DACDB;
// Interpolation function
DACDB interpolate(uint8_t dbm, uint8_t dbm1, uint8_t dbm2, DACDB val1, DACDB val2) {
DACDB result;
double fraction = (double)(dbm - dbm1) / (dbm2 - dbm1);
result.dac = (uint8_t)(val1.dac + fraction * (val2.dac - val1.dac));
result.db = (uint8_t)(val1.db + fraction * (val2.db - val1.db));
return result;
}
// Function to find the correct DAC and DB values based on dBm using interpolation
DACDB getDACandDB(uint8_t dbm) {
// Predefined values
static const struct {
uint8_t dbm;
DACDB values;
} dbmToDACDB[] = {
{20, {168, 2}}, // 100mW
{24, {148, 6}}, // 250mW
{27, {128, 9}}, // 500mW
{30, {90, 12}} // 1000mW
};
const int numValues = sizeof(dbmToDACDB) / sizeof(dbmToDACDB[0]);
// Find the interval dbm falls within and interpolate
for (int i = 0; i < numValues - 1; i++) {
if (dbm >= dbmToDACDB[i].dbm && dbm <= dbmToDACDB[i + 1].dbm) {
return interpolate(dbm, dbmToDACDB[i].dbm, dbmToDACDB[i + 1].dbm, dbmToDACDB[i].values, dbmToDACDB[i + 1].values);
}
}
// Return a default value if no match is found and default to 100mW
DACDB defaultValue = {168, 2};
return defaultValue;
}
#endif
RF95Interface::RF95Interface(LockingArduinoHal *hal, RADIOLIB_PIN_TYPE cs, RADIOLIB_PIN_TYPE irq, RADIOLIB_PIN_TYPE rst,
RADIOLIB_PIN_TYPE busy)
@@ -49,9 +94,16 @@ bool RF95Interface::init()
{
RadioLibInterface::init();
if (power > MAX_POWER) // This chip has lower power limits than some
power = MAX_POWER;
#ifdef RADIOMASTER_900_BANDIT_NANO
// DAC and DB values based on dBm using interpolation
DACDB dacDbValues = getDACandDB(power);
int8_t powerDAC = dacDbValues.dac;
power = dacDbValues.db;
#endif
if (power > RF95_MAX_POWER) // This chip has lower power limits than some
power = RF95_MAX_POWER;
limitPower();
iface = lora = new RadioLibRF95(&module);
@@ -61,6 +113,19 @@ bool RF95Interface::init()
digitalWrite(RF95_TCXO, 1);
#endif
// enable PA
#ifdef RF95_PA_EN
#if defined(RF95_PA_DAC_EN)
#ifdef RADIOMASTER_900_BANDIT_NANO
// Use calculated DAC value
dacWrite(RF95_PA_EN, powerDAC);
#else
// Use Value set in /*/variant.h
dacWrite(RF95_PA_EN, RF95_PA_LEVEL);
#endif
#endif
#endif
/*
#define RF95_TXEN (22) // If defined, this pin should be set high prior to transmit (controls an external analog switch)
#define RF95_RXEN (23) // If defined, this pin should be set high prior to receive (controls an external analog switch)
@@ -71,6 +136,11 @@ bool RF95Interface::init()
digitalWrite(RF95_TXEN, 0);
#endif
#ifdef RF95_FAN_EN
pinMode(RF95_FAN_EN, OUTPUT);
digitalWrite(RF95_FAN_EN, 1);
#endif
#ifdef RF95_RXEN
pinMode(RF95_RXEN, OUTPUT);
digitalWrite(RF95_RXEN, 1);
@@ -92,6 +162,9 @@ bool RF95Interface::init()
LOG_INFO("Frequency set to %f\n", getFreq());
LOG_INFO("Bandwidth set to %f\n", bw);
LOG_INFO("Power output set to %d\n", power);
#ifdef RADIOMASTER_900_BANDIT_NANO
LOG_INFO("DAC output set to %d\n", powerDAC);
#endif
if (res == RADIOLIB_ERR_NONE)
res = lora->setCRC(RADIOLIB_SX126X_LORA_CRC_ON);
@@ -146,10 +219,14 @@ bool RF95Interface::reconfigure()
if (err != RADIOLIB_ERR_NONE)
RECORD_CRITICALERROR(meshtastic_CriticalErrorCode_INVALID_RADIO_SETTING);
if (power > MAX_POWER) // This chip has lower power limits than some
power = MAX_POWER;
if (power > RF95_MAX_POWER) // This chip has lower power limits than some
power = RF95_MAX_POWER;
#ifdef USE_RF95_RFO
err = lora->setOutputPower(power, true);
#else
err = lora->setOutputPower(power);
#endif
if (err != RADIOLIB_ERR_NONE)
RECORD_CRITICALERROR(meshtastic_CriticalErrorCode_INVALID_RADIO_SETTING);
@@ -235,5 +312,9 @@ bool RF95Interface::sleep()
setStandby(); // First cancel any active receiving/sending
lora->sleep();
#ifdef RF95_FAN_EN
digitalWrite(RF95_FAN_EN, 0);
#endif
return true;
}
}
+10 -10
View File
@@ -31,18 +31,18 @@ const RegionInfo regions[] = {
RDEF(EU_433, 433.0f, 434.0f, 10, 0, 12, true, false, false),
/*
https://www.thethingsnetwork.org/docs/lorawan/duty-cycle/
https://www.thethingsnetwork.org/docs/lorawan/regional-parameters/
https://www.legislation.gov.uk/uksi/1999/930/schedule/6/part/III/made/data.xht?view=snippet&wrap=true
https://www.thethingsnetwork.org/docs/lorawan/duty-cycle/
https://www.thethingsnetwork.org/docs/lorawan/regional-parameters/
https://www.legislation.gov.uk/uksi/1999/930/schedule/6/part/III/made/data.xht?view=snippet&wrap=true
audio_permitted = false per regulation
audio_permitted = false per regulation
Special Note:
The link above describes LoRaWAN's band plan, stating a power limit of 16 dBm. This is their own suggested specification,
we do not need to follow it. The European Union regulations clearly state that the power limit for this frequency range is
500 mW, or 27 dBm. It also states that we can use interference avoidance and spectrum access techniques to avoid a duty
cycle. (Please refer to section 4.21 in the following document)
https://ec.europa.eu/growth/tools-databases/tris/index.cfm/ro/search/?trisaction=search.detail&year=2021&num=528&dLang=EN
Special Note:
The link above describes LoRaWAN's band plan, stating a power limit of 16 dBm. This is their own suggested specification,
we do not need to follow it. The European Union regulations clearly state that the power limit for this frequency range is
500 mW, or 27 dBm. It also states that we can use interference avoidance and spectrum access techniques (such as LBT +
AFA) to avoid a duty cycle. (Please refer to line P page 22 of this document.)
https://www.etsi.org/deliver/etsi_en/300200_300299/30022002/03.01.01_60/en_30022002v030101p.pdf
*/
RDEF(EU_868, 869.4f, 869.65f, 10, 0, 27, false, false, false),
+4
View File
@@ -42,7 +42,11 @@ int16_t RadioLibRF95::begin(float freq, float bw, uint8_t sf, uint8_t cr, uint8_
state = setCodingRate(cr);
RADIOLIB_ASSERT(state);
#ifdef USE_RF95_RFO
state = setOutputPower(power, true);
#else
state = setOutputPower(power);
#endif
RADIOLIB_ASSERT(state);
state = setGain(gain);
+2 -2
View File
@@ -75,7 +75,7 @@ typedef struct _meshtastic_HamParameters {
Ensure your radio is capable of operating of the selected frequency before setting this. */
float frequency;
/* Optional short name of user */
char short_name[6];
char short_name[5];
} meshtastic_HamParameters;
/* Response envelope for node_remote_hardware_pins */
@@ -342,7 +342,7 @@ extern const pb_msgdesc_t meshtastic_NodeRemoteHardwarePinsResponse_msg;
/* Maximum encoded size of messages (where known) */
#define MESHTASTIC_MESHTASTIC_ADMIN_PB_H_MAX_SIZE meshtastic_AdminMessage_size
#define meshtastic_AdminMessage_size 500
#define meshtastic_HamParameters_size 32
#define meshtastic_HamParameters_size 31
#define meshtastic_NodeRemoteHardwarePinsResponse_size 496
#ifdef __cplusplus
+10 -5
View File
@@ -73,6 +73,9 @@ typedef struct _meshtastic_GeoChat {
/* Uid recipient of the message */
bool has_to;
char to[120];
/* Callsign of the recipient for the message */
bool has_to_callsign;
char to_callsign[120];
} meshtastic_GeoChat;
/* ATAK Group
@@ -164,13 +167,13 @@ extern "C" {
/* Initializer values for message structs */
#define meshtastic_TAKPacket_init_default {0, false, meshtastic_Contact_init_default, false, meshtastic_Group_init_default, false, meshtastic_Status_init_default, 0, {meshtastic_PLI_init_default}}
#define meshtastic_GeoChat_init_default {"", false, ""}
#define meshtastic_GeoChat_init_default {"", false, "", false, ""}
#define meshtastic_Group_init_default {_meshtastic_MemberRole_MIN, _meshtastic_Team_MIN}
#define meshtastic_Status_init_default {0}
#define meshtastic_Contact_init_default {"", ""}
#define meshtastic_PLI_init_default {0, 0, 0, 0, 0}
#define meshtastic_TAKPacket_init_zero {0, false, meshtastic_Contact_init_zero, false, meshtastic_Group_init_zero, false, meshtastic_Status_init_zero, 0, {meshtastic_PLI_init_zero}}
#define meshtastic_GeoChat_init_zero {"", false, ""}
#define meshtastic_GeoChat_init_zero {"", false, "", false, ""}
#define meshtastic_Group_init_zero {_meshtastic_MemberRole_MIN, _meshtastic_Team_MIN}
#define meshtastic_Status_init_zero {0}
#define meshtastic_Contact_init_zero {"", ""}
@@ -179,6 +182,7 @@ extern "C" {
/* Field tags (for use in manual encoding/decoding) */
#define meshtastic_GeoChat_message_tag 1
#define meshtastic_GeoChat_to_tag 2
#define meshtastic_GeoChat_to_callsign_tag 3
#define meshtastic_Group_role_tag 1
#define meshtastic_Group_team_tag 2
#define meshtastic_Status_battery_tag 1
@@ -214,7 +218,8 @@ X(a, STATIC, ONEOF, MESSAGE, (payload_variant,chat,payload_variant.chat),
#define meshtastic_GeoChat_FIELDLIST(X, a) \
X(a, STATIC, SINGULAR, STRING, message, 1) \
X(a, STATIC, OPTIONAL, STRING, to, 2)
X(a, STATIC, OPTIONAL, STRING, to, 2) \
X(a, STATIC, OPTIONAL, STRING, to_callsign, 3)
#define meshtastic_GeoChat_CALLBACK NULL
#define meshtastic_GeoChat_DEFAULT NULL
@@ -262,11 +267,11 @@ extern const pb_msgdesc_t meshtastic_PLI_msg;
/* Maximum encoded size of messages (where known) */
#define MESHTASTIC_MESHTASTIC_ATAK_PB_H_MAX_SIZE meshtastic_TAKPacket_size
#define meshtastic_Contact_size 242
#define meshtastic_GeoChat_size 323
#define meshtastic_GeoChat_size 444
#define meshtastic_Group_size 4
#define meshtastic_PLI_size 31
#define meshtastic_Status_size 3
#define meshtastic_TAKPacket_size 584
#define meshtastic_TAKPacket_size 705
#ifdef __cplusplus
} /* extern "C" */
@@ -66,6 +66,15 @@ PB_BIND(meshtastic_Heartbeat, meshtastic_Heartbeat, AUTO)
PB_BIND(meshtastic_NodeRemoteHardwarePin, meshtastic_NodeRemoteHardwarePin, AUTO)
PB_BIND(meshtastic_ChunkedPayload, meshtastic_ChunkedPayload, AUTO)
PB_BIND(meshtastic_resend_chunks, meshtastic_resend_chunks, AUTO)
PB_BIND(meshtastic_ChunkedPayloadResponse, meshtastic_ChunkedPayloadResponse, AUTO)
+86
View File
@@ -65,6 +65,8 @@ typedef enum _meshtastic_HardwareModel {
meshtastic_HardwareModel_LORA_TYPE = 19,
/* wiphone https://www.wiphone.io/ */
meshtastic_HardwareModel_WIPHONE = 20,
/* WIO Tracker WM1110 family from Seeed Studio. Includes wio-1110-tracker and wio-1110-sdk */
meshtastic_HardwareModel_WIO_WM1110 = 21,
/* B&Q Consulting Station Edition G1: https://uniteng.com/wiki/doku.php?id=meshtastic:station */
meshtastic_HardwareModel_STATION_G1 = 25,
/* RAK11310 (RP2040 + SX1262) */
@@ -156,6 +158,9 @@ typedef enum _meshtastic_HardwareModel {
/* NRF52_PROMICRO_DIY
Promicro NRF52840 with SX1262/LLCC68, SSD1306 OLED and NEO6M GPS */
meshtastic_HardwareModel_NRF52_PROMICRO_DIY = 63,
/* RadioMaster 900 Bandit Nano, https://www.radiomasterrc.com/products/bandit-nano-expresslrs-rf-module
ESP32-D0WDQ6 With SX1276/SKY66122, SSD1306 OLED and No GPS */
meshtastic_HardwareModel_RADIOMASTER_900_BANDIT_NANO = 64,
/* ------------------------------------------------------------------------------------------------------------------------------------------
Reserved ID For developing private Ports. These will show up in live traffic sparsely, so we can use a high number. Keep it within 8 bits.
------------------------------------------------------------------------------------------------------------------------------------------ */
@@ -850,6 +855,38 @@ typedef struct _meshtastic_NodeRemoteHardwarePin {
meshtastic_RemoteHardwarePin pin;
} meshtastic_NodeRemoteHardwarePin;
typedef PB_BYTES_ARRAY_T(228) meshtastic_ChunkedPayload_payload_chunk_t;
typedef struct _meshtastic_ChunkedPayload {
/* The ID of the entire payload */
uint32_t payload_id;
/* The total number of chunks in the payload */
uint16_t chunk_count;
/* The current chunk index in the total */
uint16_t chunk_index;
/* The binary data of the current chunk */
meshtastic_ChunkedPayload_payload_chunk_t payload_chunk;
} meshtastic_ChunkedPayload;
/* Wrapper message for broken repeated oneof support */
typedef struct _meshtastic_resend_chunks {
pb_callback_t chunks;
} meshtastic_resend_chunks;
/* Responses to a ChunkedPayload request */
typedef struct _meshtastic_ChunkedPayloadResponse {
/* The ID of the entire payload */
uint32_t payload_id;
pb_size_t which_payload_variant;
union {
/* Request to transfer chunked payload */
bool request_transfer;
/* Accept the transfer chunked payload */
bool accept_transfer;
/* Request missing indexes in the chunked payload */
meshtastic_resend_chunks resend_chunks;
} payload_variant;
} meshtastic_ChunkedPayloadResponse;
#ifdef __cplusplus
extern "C" {
@@ -925,6 +962,9 @@ extern "C" {
/* Initializer values for message structs */
#define meshtastic_Position_init_default {0, 0, 0, 0, _meshtastic_Position_LocSource_MIN, _meshtastic_Position_AltSource_MIN, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
#define meshtastic_User_init_default {"", "", "", {0}, _meshtastic_HardwareModel_MIN, 0, _meshtastic_Config_DeviceConfig_Role_MIN}
@@ -946,6 +986,9 @@ extern "C" {
#define meshtastic_DeviceMetadata_init_default {"", 0, 0, 0, 0, 0, _meshtastic_Config_DeviceConfig_Role_MIN, 0, _meshtastic_HardwareModel_MIN, 0}
#define meshtastic_Heartbeat_init_default {0}
#define meshtastic_NodeRemoteHardwarePin_init_default {0, false, meshtastic_RemoteHardwarePin_init_default}
#define meshtastic_ChunkedPayload_init_default {0, 0, 0, {0, {0}}}
#define meshtastic_resend_chunks_init_default {{{NULL}, NULL}}
#define meshtastic_ChunkedPayloadResponse_init_default {0, 0, {0}}
#define meshtastic_Position_init_zero {0, 0, 0, 0, _meshtastic_Position_LocSource_MIN, _meshtastic_Position_AltSource_MIN, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
#define meshtastic_User_init_zero {"", "", "", {0}, _meshtastic_HardwareModel_MIN, 0, _meshtastic_Config_DeviceConfig_Role_MIN}
#define meshtastic_RouteDiscovery_init_zero {0, {0, 0, 0, 0, 0, 0, 0, 0}}
@@ -966,6 +1009,9 @@ extern "C" {
#define meshtastic_DeviceMetadata_init_zero {"", 0, 0, 0, 0, 0, _meshtastic_Config_DeviceConfig_Role_MIN, 0, _meshtastic_HardwareModel_MIN, 0}
#define meshtastic_Heartbeat_init_zero {0}
#define meshtastic_NodeRemoteHardwarePin_init_zero {0, false, meshtastic_RemoteHardwarePin_init_zero}
#define meshtastic_ChunkedPayload_init_zero {0, 0, 0, {0, {0}}}
#define meshtastic_resend_chunks_init_zero {{{NULL}, NULL}}
#define meshtastic_ChunkedPayloadResponse_init_zero {0, 0, {0}}
/* Field tags (for use in manual encoding/decoding) */
#define meshtastic_Position_latitude_i_tag 1
@@ -1100,6 +1146,15 @@ extern "C" {
#define meshtastic_ToRadio_heartbeat_tag 7
#define meshtastic_NodeRemoteHardwarePin_node_num_tag 1
#define meshtastic_NodeRemoteHardwarePin_pin_tag 2
#define meshtastic_ChunkedPayload_payload_id_tag 1
#define meshtastic_ChunkedPayload_chunk_count_tag 2
#define meshtastic_ChunkedPayload_chunk_index_tag 3
#define meshtastic_ChunkedPayload_payload_chunk_tag 4
#define meshtastic_resend_chunks_chunks_tag 1
#define meshtastic_ChunkedPayloadResponse_payload_id_tag 1
#define meshtastic_ChunkedPayloadResponse_request_transfer_tag 2
#define meshtastic_ChunkedPayloadResponse_accept_transfer_tag 3
#define meshtastic_ChunkedPayloadResponse_resend_chunks_tag 4
/* Struct field encoding specification for nanopb */
#define meshtastic_Position_FIELDLIST(X, a) \
@@ -1338,6 +1393,28 @@ X(a, STATIC, OPTIONAL, MESSAGE, pin, 2)
#define meshtastic_NodeRemoteHardwarePin_DEFAULT NULL
#define meshtastic_NodeRemoteHardwarePin_pin_MSGTYPE meshtastic_RemoteHardwarePin
#define meshtastic_ChunkedPayload_FIELDLIST(X, a) \
X(a, STATIC, SINGULAR, UINT32, payload_id, 1) \
X(a, STATIC, SINGULAR, UINT32, chunk_count, 2) \
X(a, STATIC, SINGULAR, UINT32, chunk_index, 3) \
X(a, STATIC, SINGULAR, BYTES, payload_chunk, 4)
#define meshtastic_ChunkedPayload_CALLBACK NULL
#define meshtastic_ChunkedPayload_DEFAULT NULL
#define meshtastic_resend_chunks_FIELDLIST(X, a) \
X(a, CALLBACK, REPEATED, UINT32, chunks, 1)
#define meshtastic_resend_chunks_CALLBACK pb_default_field_callback
#define meshtastic_resend_chunks_DEFAULT NULL
#define meshtastic_ChunkedPayloadResponse_FIELDLIST(X, a) \
X(a, STATIC, SINGULAR, UINT32, payload_id, 1) \
X(a, STATIC, ONEOF, BOOL, (payload_variant,request_transfer,payload_variant.request_transfer), 2) \
X(a, STATIC, ONEOF, BOOL, (payload_variant,accept_transfer,payload_variant.accept_transfer), 3) \
X(a, STATIC, ONEOF, MESSAGE, (payload_variant,resend_chunks,payload_variant.resend_chunks), 4)
#define meshtastic_ChunkedPayloadResponse_CALLBACK NULL
#define meshtastic_ChunkedPayloadResponse_DEFAULT NULL
#define meshtastic_ChunkedPayloadResponse_payload_variant_resend_chunks_MSGTYPE meshtastic_resend_chunks
extern const pb_msgdesc_t meshtastic_Position_msg;
extern const pb_msgdesc_t meshtastic_User_msg;
extern const pb_msgdesc_t meshtastic_RouteDiscovery_msg;
@@ -1358,6 +1435,9 @@ extern const pb_msgdesc_t meshtastic_Neighbor_msg;
extern const pb_msgdesc_t meshtastic_DeviceMetadata_msg;
extern const pb_msgdesc_t meshtastic_Heartbeat_msg;
extern const pb_msgdesc_t meshtastic_NodeRemoteHardwarePin_msg;
extern const pb_msgdesc_t meshtastic_ChunkedPayload_msg;
extern const pb_msgdesc_t meshtastic_resend_chunks_msg;
extern const pb_msgdesc_t meshtastic_ChunkedPayloadResponse_msg;
/* Defines for backwards compatibility with code written before nanopb-0.4.0 */
#define meshtastic_Position_fields &meshtastic_Position_msg
@@ -1380,9 +1460,15 @@ extern const pb_msgdesc_t meshtastic_NodeRemoteHardwarePin_msg;
#define meshtastic_DeviceMetadata_fields &meshtastic_DeviceMetadata_msg
#define meshtastic_Heartbeat_fields &meshtastic_Heartbeat_msg
#define meshtastic_NodeRemoteHardwarePin_fields &meshtastic_NodeRemoteHardwarePin_msg
#define meshtastic_ChunkedPayload_fields &meshtastic_ChunkedPayload_msg
#define meshtastic_resend_chunks_fields &meshtastic_resend_chunks_msg
#define meshtastic_ChunkedPayloadResponse_fields &meshtastic_ChunkedPayloadResponse_msg
/* Maximum encoded size of messages (where known) */
/* meshtastic_resend_chunks_size depends on runtime parameters */
/* meshtastic_ChunkedPayloadResponse_size depends on runtime parameters */
#define MESHTASTIC_MESHTASTIC_MESH_PB_H_MAX_SIZE meshtastic_FromRadio_size
#define meshtastic_ChunkedPayload_size 245
#define meshtastic_Compressed_size 243
#define meshtastic_Data_size 270
#define meshtastic_DeviceMetadata_size 46
+28 -7
View File
@@ -57,7 +57,11 @@ typedef enum _meshtastic_TelemetrySensorType {
/* Lite On LTR-390UV-01 UV Light Sensor */
meshtastic_TelemetrySensorType_LTR390UV = 21,
/* AMS TSL25911FN RGB Light Sensor */
meshtastic_TelemetrySensorType_TSL25911FN = 22
meshtastic_TelemetrySensorType_TSL25911FN = 22,
/* AHT10 Integrated temperature and humidity sensor */
meshtastic_TelemetrySensorType_AHT10 = 23,
/* DFRobot Lark Weather station (temperature, humidity, pressure, wind speed and direction) */
meshtastic_TelemetrySensorType_DFROBOT_LARK = 24
} meshtastic_TelemetrySensorType;
/* Struct definitions */
@@ -98,6 +102,15 @@ typedef struct _meshtastic_EnvironmentMetrics {
float lux;
/* VEML7700 high accuracy white light(irradiance) not calibrated digital 16-bit resolution sensor. */
float white_lux;
/* Infrared lux */
float ir_lux;
/* Ultraviolet lux */
float uv_lux;
/* Wind direction in degrees
0 degrees = North, 90 = East, etc... */
uint16_t wind_direction;
/* Wind speed in m/s */
float wind_speed;
} meshtastic_EnvironmentMetrics;
/* Power Metrics (voltage / current / etc) */
@@ -168,8 +181,8 @@ extern "C" {
/* Helper constants for enums */
#define _meshtastic_TelemetrySensorType_MIN meshtastic_TelemetrySensorType_SENSOR_UNSET
#define _meshtastic_TelemetrySensorType_MAX meshtastic_TelemetrySensorType_TSL25911FN
#define _meshtastic_TelemetrySensorType_ARRAYSIZE ((meshtastic_TelemetrySensorType)(meshtastic_TelemetrySensorType_TSL25911FN+1))
#define _meshtastic_TelemetrySensorType_MAX meshtastic_TelemetrySensorType_DFROBOT_LARK
#define _meshtastic_TelemetrySensorType_ARRAYSIZE ((meshtastic_TelemetrySensorType)(meshtastic_TelemetrySensorType_DFROBOT_LARK+1))
@@ -179,12 +192,12 @@ extern "C" {
/* Initializer values for message structs */
#define meshtastic_DeviceMetrics_init_default {0, 0, 0, 0, 0}
#define meshtastic_EnvironmentMetrics_init_default {0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
#define meshtastic_EnvironmentMetrics_init_default {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
#define meshtastic_PowerMetrics_init_default {0, 0, 0, 0, 0, 0}
#define meshtastic_AirQualityMetrics_init_default {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
#define meshtastic_Telemetry_init_default {0, 0, {meshtastic_DeviceMetrics_init_default}}
#define meshtastic_DeviceMetrics_init_zero {0, 0, 0, 0, 0}
#define meshtastic_EnvironmentMetrics_init_zero {0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
#define meshtastic_EnvironmentMetrics_init_zero {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
#define meshtastic_PowerMetrics_init_zero {0, 0, 0, 0, 0, 0}
#define meshtastic_AirQualityMetrics_init_zero {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
#define meshtastic_Telemetry_init_zero {0, 0, {meshtastic_DeviceMetrics_init_zero}}
@@ -205,6 +218,10 @@ extern "C" {
#define meshtastic_EnvironmentMetrics_distance_tag 8
#define meshtastic_EnvironmentMetrics_lux_tag 9
#define meshtastic_EnvironmentMetrics_white_lux_tag 10
#define meshtastic_EnvironmentMetrics_ir_lux_tag 11
#define meshtastic_EnvironmentMetrics_uv_lux_tag 12
#define meshtastic_EnvironmentMetrics_wind_direction_tag 13
#define meshtastic_EnvironmentMetrics_wind_speed_tag 14
#define meshtastic_PowerMetrics_ch1_voltage_tag 1
#define meshtastic_PowerMetrics_ch1_current_tag 2
#define meshtastic_PowerMetrics_ch2_voltage_tag 3
@@ -249,7 +266,11 @@ X(a, STATIC, SINGULAR, FLOAT, current, 6) \
X(a, STATIC, SINGULAR, UINT32, iaq, 7) \
X(a, STATIC, SINGULAR, FLOAT, distance, 8) \
X(a, STATIC, SINGULAR, FLOAT, lux, 9) \
X(a, STATIC, SINGULAR, FLOAT, white_lux, 10)
X(a, STATIC, SINGULAR, FLOAT, white_lux, 10) \
X(a, STATIC, SINGULAR, FLOAT, ir_lux, 11) \
X(a, STATIC, SINGULAR, FLOAT, uv_lux, 12) \
X(a, STATIC, SINGULAR, UINT32, wind_direction, 13) \
X(a, STATIC, SINGULAR, FLOAT, wind_speed, 14)
#define meshtastic_EnvironmentMetrics_CALLBACK NULL
#define meshtastic_EnvironmentMetrics_DEFAULT NULL
@@ -309,7 +330,7 @@ extern const pb_msgdesc_t meshtastic_Telemetry_msg;
#define MESHTASTIC_MESHTASTIC_TELEMETRY_PB_H_MAX_SIZE meshtastic_Telemetry_size
#define meshtastic_AirQualityMetrics_size 72
#define meshtastic_DeviceMetrics_size 27
#define meshtastic_EnvironmentMetrics_size 49
#define meshtastic_EnvironmentMetrics_size 68
#define meshtastic_PowerMetrics_size 30
#define meshtastic_Telemetry_size 79
+2
View File
@@ -108,8 +108,10 @@ static void onNetworkConnected()
}
// FIXME this is kinda yucky, instead we should just have an observable for 'wifireconnected'
#ifndef MESHTASTIC_EXCLUDE_MQTT
if (mqtt)
mqtt->reconnect();
#endif
}
static int32_t reconnectWiFi()
+7
View File
@@ -26,6 +26,11 @@
#if !MESHTASTIC_EXCLUDE_GPS
#include "GPS.h"
#endif
#if MESHTASTIC_EXCLUDE_GPS
#include "modules/PositionModule.h"
#endif
#if !defined(ARCH_PORTDUINO) && !defined(ARCH_STM32WL) && !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR
#include "AccelerometerThread.h"
#endif
@@ -751,7 +756,9 @@ void AdminModule::handleGetDeviceConnectionStatus(const meshtastic_MeshPacket &r
if (conn.wifi.status.is_connected) {
conn.wifi.rssi = WiFi.RSSI();
conn.wifi.status.ip_address = WiFi.localIP();
#ifndef MESHTASTIC_EXCLUDE_MQTT
conn.wifi.status.is_mqtt_connected = mqtt && mqtt->isConnectedDirectly();
#endif
conn.wifi.status.is_syslog_connected = false; // FIXME wire this up
}
#endif
+16
View File
@@ -87,6 +87,14 @@ void AtakPluginModule::alterReceivedProtobuf(meshtastic_MeshPacket &mp, meshtast
compressed.payload_variant.chat.to);
LOG_DEBUG("Compressed chat to: %d bytes\n", length);
}
if (t->payload_variant.chat.has_to_callsign) {
compressed.payload_variant.chat.has_to_callsign = true;
length =
unishox2_compress_simple(t->payload_variant.chat.to_callsign, strlen(t->payload_variant.chat.to_callsign),
compressed.payload_variant.chat.to_callsign);
LOG_DEBUG("Compressed chat to_callsign: %d bytes\n", length);
}
}
mp.decoded.payload.size = pb_encode_to_bytes(mp.decoded.payload.bytes, sizeof(mp.decoded.payload.bytes),
meshtastic_TAKPacket_fields, &compressed);
@@ -124,6 +132,14 @@ void AtakPluginModule::alterReceivedProtobuf(meshtastic_MeshPacket &mp, meshtast
uncompressed.payload_variant.chat.to);
LOG_DEBUG("Decompressed chat to: %d bytes\n", length);
}
if (t->payload_variant.chat.has_to_callsign) {
uncompressed.payload_variant.chat.has_to_callsign = true;
length =
unishox2_decompress_simple(t->payload_variant.chat.to_callsign, strlen(t->payload_variant.chat.to_callsign),
uncompressed.payload_variant.chat.to_callsign);
LOG_DEBUG("Decompressed chat to_callsign: %d bytes\n", length);
}
}
decompressedCopy->decoded.payload.size =
pb_encode_to_bytes(decompressedCopy->decoded.payload.bytes, sizeof(decompressedCopy->decoded.payload),
+346 -15
View File
@@ -47,6 +47,12 @@ CannedMessageModule::CannedMessageModule()
disable();
} else {
LOG_INFO("CannedMessageModule is enabled\n");
// T-Watch interface currently has no way to select destination type, so default to 'node'
#ifdef T_WATCH_S3
this->destSelect = CANNED_MESSAGE_DESTINATION_TYPE_NODE;
#endif
this->inputObserver.observe(inputBroker);
}
} else {
@@ -67,8 +73,16 @@ int CannedMessageModule::splitConfiguredMessages()
int messageIndex = 0;
int i = 0;
String messages = cannedMessageModuleConfig.messages;
#ifdef T_WATCH_S3
String separator = messages.length() ? "|" : "";
messages = "[---- Free Text ----]" + separator + messages;
#endif
// collect all the message parts
strncpy(this->messageStore, cannedMessageModuleConfig.messages, sizeof(this->messageStore));
strncpy(this->messageStore, messages.c_str(), sizeof(this->messageStore));
// The first message points to the beginning of the store.
this->messages[messageIndex++] = this->messageStore;
@@ -78,7 +92,6 @@ int CannedMessageModule::splitConfiguredMessages()
if (this->messageStore[i] == '|') {
// Message ending found, replace it with string-end character.
this->messageStore[i] = '\0';
LOG_DEBUG("CannedMessage %d is: '%s'\n", messageIndex - 1, this->messages[messageIndex - 1]);
// hit our max messages, bail
if (messageIndex >= CANNED_MESSAGE_MODULE_MESSAGE_MAX_COUNT) {
@@ -119,20 +132,30 @@ int CannedMessageModule::handleInputEvent(const InputEvent *event)
bool validEvent = false;
if (event->inputEvent == static_cast<char>(meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_UP)) {
if (this->messagesCount > 0) {
// LOG_DEBUG("Canned message event UP\n");
this->runState = CANNED_MESSAGE_RUN_STATE_ACTION_UP;
validEvent = true;
}
}
if (event->inputEvent == static_cast<char>(meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_DOWN)) {
if (this->messagesCount > 0) {
// LOG_DEBUG("Canned message event DOWN\n");
this->runState = CANNED_MESSAGE_RUN_STATE_ACTION_DOWN;
validEvent = true;
}
}
if (event->inputEvent == static_cast<char>(meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_SELECT)) {
LOG_DEBUG("Canned message event Select\n");
#ifdef T_WATCH_S3
if (this->currentMessageIndex == 0) {
this->runState = CANNED_MESSAGE_RUN_STATE_FREETEXT;
UIFrameEvent e = {false, true};
e.frameChanged = true;
this->notifyObservers(&e);
return 0;
}
#endif
// when inactive, call the onebutton shortpress instead. Activate Module only on up/down
if ((this->runState == CANNED_MESSAGE_RUN_STATE_INACTIVE) || (this->runState == CANNED_MESSAGE_RUN_STATE_DISABLED)) {
powerFSM.trigger(EVENT_PRESS);
@@ -143,38 +166,47 @@ int CannedMessageModule::handleInputEvent(const InputEvent *event)
}
}
if (event->inputEvent == static_cast<char>(meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_CANCEL)) {
LOG_DEBUG("Canned message event Cancel\n");
UIFrameEvent e = {false, true};
e.frameChanged = true;
this->currentMessageIndex = -1;
#ifndef T_WATCH_S3
this->freetext = ""; // clear freetext
this->cursor = 0;
this->destSelect = CANNED_MESSAGE_DESTINATION_TYPE_NONE;
#endif
this->runState = CANNED_MESSAGE_RUN_STATE_INACTIVE;
this->notifyObservers(&e);
}
if ((event->inputEvent == static_cast<char>(meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_BACK)) ||
(event->inputEvent == static_cast<char>(meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_LEFT)) ||
(event->inputEvent == static_cast<char>(meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_RIGHT))) {
// LOG_DEBUG("Canned message event (%x)\n", event->kbchar);
#ifdef T_WATCH_S3
if (event->inputEvent == static_cast<char>(meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_LEFT)) {
this->payload = 0xb4;
} else if (event->inputEvent == static_cast<char>(meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_RIGHT)) {
this->payload = 0xb7;
}
#else
// tweak for left/right events generated via trackball/touch with empty kbchar
if (!event->kbchar) {
if (event->inputEvent == static_cast<char>(meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_LEFT)) {
this->payload = 0xb4;
// this->destSelect = CANNED_MESSAGE_DESTINATION_TYPE_NODE;
} else if (event->inputEvent == static_cast<char>(meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_RIGHT)) {
this->payload = 0xb7;
// this->destSelect = CANNED_MESSAGE_DESTINATION_TYPE_NODE;
}
} else {
// pass the pressed key
this->payload = event->kbchar;
}
#endif
this->lastTouchMillis = millis();
validEvent = true;
}
if (event->inputEvent == static_cast<char>(ANYKEY)) {
LOG_DEBUG("Canned message event any key pressed\n");
// when inactive, this will switch to the freetext mode
if ((this->runState == CANNED_MESSAGE_RUN_STATE_INACTIVE) || (this->runState == CANNED_MESSAGE_RUN_STATE_ACTIVE) ||
(this->runState == CANNED_MESSAGE_RUN_STATE_DISABLED)) {
@@ -250,8 +282,68 @@ int CannedMessageModule::handleInputEvent(const InputEvent *event)
screen->removeFunctionSymbal("Fn"); // remove modifier (function) symbal
}
}
#ifdef T_WATCH_S3
if (this->runState == CANNED_MESSAGE_RUN_STATE_FREETEXT) {
String keyTapped = keyForCoordinates(event->touchX, event->touchY);
if (keyTapped == "⇧") {
this->highlight = -1;
this->payload = 0x00;
validEvent = true;
this->shift = !this->shift;
} else if (keyTapped == "⌫") {
this->highlight = keyTapped[0];
this->payload = 0x08;
validEvent = true;
this->shift = false;
} else if (keyTapped == "123" || keyTapped == "ABC") {
this->highlight = -1;
this->payload = 0x00;
this->charSet = this->charSet == 0 ? 1 : 0;
validEvent = true;
} else if (keyTapped == " ") {
this->highlight = keyTapped[0];
this->payload = keyTapped[0];
validEvent = true;
this->shift = false;
} else if (keyTapped == "↵") {
this->highlight = 0x00;
this->runState = CANNED_MESSAGE_RUN_STATE_ACTION_SELECT;
this->payload = CANNED_MESSAGE_RUN_STATE_FREETEXT;
this->currentMessageIndex = event->kbchar - 1;
validEvent = true;
this->shift = false;
} else if (keyTapped != "") {
this->highlight = keyTapped[0];
this->payload = this->shift ? keyTapped[0] : std::tolower(keyTapped[0]);
validEvent = true;
this->shift = false;
}
}
#endif
if (event->inputEvent == static_cast<char>(MATRIXKEY)) {
LOG_DEBUG("Canned message event Matrix key pressed\n");
// this will send the text immediately on matrix press
this->runState = CANNED_MESSAGE_RUN_STATE_ACTION_SELECT;
this->payload = MATRIXKEY;
@@ -311,17 +403,24 @@ int32_t CannedMessageModule::runOnce()
this->currentMessageIndex = -1;
this->freetext = ""; // clear freetext
this->cursor = 0;
#ifndef T_WATCH_S3
this->destSelect = CANNED_MESSAGE_DESTINATION_TYPE_NONE;
#endif
this->notifyObservers(&e);
} else if (((this->runState == CANNED_MESSAGE_RUN_STATE_ACTIVE) || (this->runState == CANNED_MESSAGE_RUN_STATE_FREETEXT)) &&
((millis() - this->lastTouchMillis) > INACTIVATE_AFTER_MS)) {
// Reset module
LOG_DEBUG("Reset due to lack of activity.\n");
e.frameChanged = true;
this->currentMessageIndex = -1;
this->freetext = ""; // clear freetext
this->cursor = 0;
#ifndef T_WATCH_S3
this->destSelect = CANNED_MESSAGE_DESTINATION_TYPE_NONE;
#endif
this->runState = CANNED_MESSAGE_RUN_STATE_INACTIVE;
this->notifyObservers(&e);
} else if (this->runState == CANNED_MESSAGE_RUN_STATE_ACTION_SELECT) {
@@ -330,7 +429,6 @@ int32_t CannedMessageModule::runOnce()
sendText(this->dest, indexChannels[this->channel], this->freetext.c_str(), true);
this->runState = CANNED_MESSAGE_RUN_STATE_SENDING_ACTIVE;
} else {
LOG_DEBUG("Reset message is empty.\n");
this->runState = CANNED_MESSAGE_RUN_STATE_INACTIVE;
}
} else {
@@ -339,11 +437,15 @@ int32_t CannedMessageModule::runOnce()
powerFSM.trigger(EVENT_PRESS);
return INT32_MAX;
} else {
#ifdef T_WATCH_S3
sendText(this->dest, indexChannels[this->channel], this->messages[this->currentMessageIndex], true);
#else
sendText(NODENUM_BROADCAST, channels.getPrimaryIndex(), this->messages[this->currentMessageIndex], true);
#endif
}
this->runState = CANNED_MESSAGE_RUN_STATE_SENDING_ACTIVE;
} else {
LOG_DEBUG("Reset message is empty.\n");
// LOG_DEBUG("Reset message is empty.\n");
this->runState = CANNED_MESSAGE_RUN_STATE_INACTIVE;
}
}
@@ -351,7 +453,11 @@ int32_t CannedMessageModule::runOnce()
this->currentMessageIndex = -1;
this->freetext = ""; // clear freetext
this->cursor = 0;
#ifndef T_WATCH_S3
this->destSelect = CANNED_MESSAGE_DESTINATION_TYPE_NONE;
#endif
this->notifyObservers(&e);
return 2000;
} else if ((this->runState != CANNED_MESSAGE_RUN_STATE_FREETEXT) && (this->currentMessageIndex == -1)) {
@@ -364,7 +470,11 @@ int32_t CannedMessageModule::runOnce()
this->currentMessageIndex = getPrevIndex();
this->freetext = ""; // clear freetext
this->cursor = 0;
#ifndef T_WATCH_S3
this->destSelect = CANNED_MESSAGE_DESTINATION_TYPE_NONE;
#endif
this->runState = CANNED_MESSAGE_RUN_STATE_ACTIVE;
LOG_DEBUG("MOVE UP (%d):%s\n", this->currentMessageIndex, this->getCurrentMessage());
}
@@ -373,7 +483,11 @@ int32_t CannedMessageModule::runOnce()
this->currentMessageIndex = this->getNextIndex();
this->freetext = ""; // clear freetext
this->cursor = 0;
#ifndef T_WATCH_S3
this->destSelect = CANNED_MESSAGE_DESTINATION_TYPE_NONE;
#endif
this->runState = CANNED_MESSAGE_RUN_STATE_ACTIVE;
LOG_DEBUG("MOVE DOWN (%d):%s\n", this->currentMessageIndex, this->getCurrentMessage());
}
@@ -457,7 +571,7 @@ int32_t CannedMessageModule::runOnce()
switch (this->payload) { // code below all trigger the freetext window (where you type to send a message) or reset the
// display back to the default window
case 0x08: // backspace
if (this->freetext.length() > 0) {
if (this->freetext.length() > 0 && this->highlight == 0x00) {
if (this->cursor == this->freetext.length()) {
this->freetext = this->freetext.substring(0, this->freetext.length() - 1);
} else {
@@ -495,13 +609,19 @@ int32_t CannedMessageModule::runOnce()
runState = CANNED_MESSAGE_RUN_STATE_INACTIVE;
break;
default:
if (this->highlight != 0x00) {
break;
}
if (this->cursor == this->freetext.length()) {
this->freetext += this->payload;
} else {
this->freetext =
this->freetext.substring(0, this->cursor) + this->payload + this->freetext.substring(this->cursor);
}
this->cursor += 1;
uint16_t maxChars = meshtastic_Constants_DATA_PAYLOAD_LEN - (moduleConfig.canned_message.send_bell ? 1 : 0);
if (this->freetext.length() > maxChars) {
this->cursor = maxChars;
@@ -594,6 +714,201 @@ void CannedMessageModule::showTemporaryMessage(const String &message)
setIntervalFromNow(2000);
}
#ifdef T_WATCH_S3
String CannedMessageModule::keyForCoordinates(uint x, uint y)
{
int outerSize = *(&this->keyboard[this->charSet] + 1) - this->keyboard[this->charSet];
for (int8_t outerIndex = 0; outerIndex < outerSize; outerIndex++) {
int innerSize = *(&this->keyboard[this->charSet][outerIndex] + 1) - this->keyboard[this->charSet][outerIndex];
for (int8_t innerIndex = 0; innerIndex < innerSize; innerIndex++) {
Letter letter = this->keyboard[this->charSet][outerIndex][innerIndex];
if (x > letter.rectX && x < (letter.rectX + letter.rectWidth) && y > letter.rectY &&
y < (letter.rectY + letter.rectHeight)) {
return letter.character;
}
}
}
return "";
}
void CannedMessageModule::drawKeyboard(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y)
{
int outerSize = *(&this->keyboard[this->charSet] + 1) - this->keyboard[this->charSet];
int xOffset = 0;
int yOffset = 56;
display->setTextAlignment(TEXT_ALIGN_LEFT);
display->setFont(FONT_SMALL);
display->setColor(OLEDDISPLAY_COLOR::WHITE);
display->drawStringMaxWidth(0, 0, display->getWidth(),
cannedMessageModule->drawWithCursor(cannedMessageModule->freetext, cannedMessageModule->cursor));
display->setFont(FONT_MEDIUM);
int cellHeight = round((display->height() - 64) / outerSize);
int yCorrection = 8;
for (int8_t outerIndex = 0; outerIndex < outerSize; outerIndex++) {
yOffset += outerIndex > 0 ? cellHeight : 0;
int innerSizeBound = *(&this->keyboard[this->charSet][outerIndex] + 1) - this->keyboard[this->charSet][outerIndex];
int innerSize = 0;
for (int8_t innerIndex = 0; innerIndex < innerSizeBound; innerIndex++) {
if (this->keyboard[this->charSet][outerIndex][innerIndex].character != "") {
innerSize++;
}
}
int cellWidth = display->width() / innerSize;
for (int8_t innerIndex = 0; innerIndex < innerSize; innerIndex++) {
xOffset += innerIndex > 0 ? cellWidth : 0;
Letter letter = this->keyboard[this->charSet][outerIndex][innerIndex];
Letter updatedLetter = {letter.character, letter.width, xOffset, yOffset, cellWidth, cellHeight};
this->keyboard[this->charSet][outerIndex][innerIndex] = updatedLetter;
float characterOffset = ((cellWidth / 2) - (letter.width / 2));
if (letter.character == "⇧") {
if (this->shift) {
display->fillRect(xOffset, yOffset, cellWidth, cellHeight);
display->setColor(OLEDDISPLAY_COLOR::BLACK);
drawShiftIcon(display, xOffset + characterOffset, yOffset + yCorrection + 5, 1.2);
display->setColor(OLEDDISPLAY_COLOR::WHITE);
} else {
display->drawRect(xOffset, yOffset, cellWidth, cellHeight);
drawShiftIcon(display, xOffset + characterOffset, yOffset + yCorrection + 5, 1.2);
}
} else if (letter.character == "⌫") {
if (this->highlight == letter.character[0]) {
display->fillRect(xOffset, yOffset, cellWidth, cellHeight);
display->setColor(OLEDDISPLAY_COLOR::BLACK);
drawBackspaceIcon(display, xOffset + characterOffset, yOffset + yCorrection + 5, 1.2);
display->setColor(OLEDDISPLAY_COLOR::WHITE);
setIntervalFromNow(0);
} else {
display->drawRect(xOffset, yOffset, cellWidth, cellHeight);
drawBackspaceIcon(display, xOffset + characterOffset, yOffset + yCorrection + 5, 1.2);
}
} else if (letter.character == "↵") {
display->drawRect(xOffset, yOffset, cellWidth, cellHeight);
drawEnterIcon(display, xOffset + characterOffset, yOffset + yCorrection + 5, 1.7);
} else {
if (this->highlight == letter.character[0]) {
display->fillRect(xOffset, yOffset, cellWidth, cellHeight);
display->setColor(OLEDDISPLAY_COLOR::BLACK);
display->drawString(xOffset + characterOffset, yOffset + yCorrection,
letter.character == " " ? "space" : letter.character);
display->setColor(OLEDDISPLAY_COLOR::WHITE);
setIntervalFromNow(0);
} else {
display->drawRect(xOffset, yOffset, cellWidth, cellHeight);
display->drawString(xOffset + characterOffset, yOffset + yCorrection,
letter.character == " " ? "space" : letter.character);
}
}
}
xOffset = 0;
}
this->highlight = 0x00;
}
void CannedMessageModule::drawShiftIcon(OLEDDisplay *display, int x, int y, float scale)
{
PointStruct shiftIcon[10] = {{8, 0}, {15, 7}, {15, 8}, {12, 8}, {12, 12}, {4, 12}, {4, 8}, {1, 8}, {1, 7}, {8, 0}};
int size = 10;
for (int i = 0; i < size - 1; i++) {
int x0 = x + (shiftIcon[i].x * scale);
int y0 = y + (shiftIcon[i].y * scale);
int x1 = x + (shiftIcon[i + 1].x * scale);
int y1 = y + (shiftIcon[i + 1].y * scale);
display->drawLine(x0, y0, x1, y1);
}
}
void CannedMessageModule::drawBackspaceIcon(OLEDDisplay *display, int x, int y, float scale)
{
PointStruct backspaceIcon[6] = {{0, 7}, {5, 2}, {15, 2}, {15, 12}, {5, 12}, {0, 7}};
int size = 6;
for (int i = 0; i < size - 1; i++) {
int x0 = x + (backspaceIcon[i].x * scale);
int y0 = y + (backspaceIcon[i].y * scale);
int x1 = x + (backspaceIcon[i + 1].x * scale);
int y1 = y + (backspaceIcon[i + 1].y * scale);
display->drawLine(x0, y0, x1, y1);
}
PointStruct backspaceIconX[4] = {{7, 4}, {13, 10}, {7, 10}, {13, 4}};
size = 4;
for (int i = 0; i < size - 1; i++) {
int x0 = x + (backspaceIconX[i].x * scale);
int y0 = y + (backspaceIconX[i].y * scale);
int x1 = x + (backspaceIconX[i + 1].x * scale);
int y1 = y + (backspaceIconX[i + 1].y * scale);
display->drawLine(x0, y0, x1, y1);
}
}
void CannedMessageModule::drawEnterIcon(OLEDDisplay *display, int x, int y, float scale)
{
PointStruct enterIcon[6] = {{0, 7}, {4, 3}, {4, 11}, {0, 7}, {15, 7}, {15, 0}};
int size = 6;
for (int i = 0; i < size - 1; i++) {
int x0 = x + (enterIcon[i].x * scale);
int y0 = y + (enterIcon[i].y * scale);
int x1 = x + (enterIcon[i + 1].x * scale);
int y1 = y + (enterIcon[i + 1].y * scale);
display->drawLine(x0, y0, x1, y1);
}
}
#endif
void CannedMessageModule::drawFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y)
{
char buffer[50];
@@ -614,6 +929,16 @@ void CannedMessageModule::drawFrame(OLEDDisplay *display, OLEDDisplayUiState *st
}
display->drawStringf(display->getWidth() / 2 + x, 0 + y + 12, buffer, displayString,
cannedMessageModule->getNodeName(this->incoming));
display->setFont(FONT_SMALL);
String snrString = "Last Rx SNR: %f";
String rssiString = "Last Rx RSSI: %d";
if (this->ack) {
display->drawStringf(display->getWidth() / 2 + x, y + 100, buffer, snrString, this->lastRxSnr);
display->drawStringf(display->getWidth() / 2 + x, y + 130, buffer, rssiString, this->lastRxRssi);
}
} else if (cannedMessageModule->runState == CANNED_MESSAGE_RUN_STATE_SENDING_ACTIVE) {
display->setTextAlignment(TEXT_ALIGN_CENTER);
display->setFont(FONT_MEDIUM);
@@ -623,6 +948,11 @@ void CannedMessageModule::drawFrame(OLEDDisplay *display, OLEDDisplayUiState *st
display->setFont(FONT_SMALL);
display->drawString(10 + x, 0 + y + FONT_HEIGHT_SMALL, "Canned Message\nModule disabled.");
} else if (cannedMessageModule->runState == CANNED_MESSAGE_RUN_STATE_FREETEXT) {
#ifdef T_WATCH_S3
drawKeyboard(display, state, 0, 0);
#else
display->setTextAlignment(TEXT_ALIGN_LEFT);
display->setFont(FONT_SMALL);
if (this->destSelect != CANNED_MESSAGE_DESTINATION_TYPE_NONE) {
@@ -663,6 +993,7 @@ void CannedMessageModule::drawFrame(OLEDDisplay *display, OLEDDisplayUiState *st
display->drawStringMaxWidth(
0 + x, 0 + y + FONT_HEIGHT_SMALL, x + display->getWidth(),
cannedMessageModule->drawWithCursor(cannedMessageModule->freetext, cannedMessageModule->cursor));
#endif
} else {
if (this->messagesCount > 0) {
display->setTextAlignment(TEXT_ALIGN_LEFT);
+103
View File
@@ -22,6 +22,17 @@ enum cannedMessageDestinationType {
CANNED_MESSAGE_DESTINATION_TYPE_CHANNEL
};
enum CannedMessageModuleIconType { shift, backspace, space, enter };
struct Letter {
String character;
float width;
int rectX;
int rectY;
int rectWidth;
int rectHeight;
};
#define CANNED_MESSAGE_MODULE_MESSAGE_MAX_COUNT 50
/**
* Sum of CannedMessageModuleConfig part sizes.
@@ -61,6 +72,14 @@ class CannedMessageModule : public SinglePortModule, public Observable<const UIF
*/
virtual bool wantPacket(const meshtastic_MeshPacket *p) override
{
if (p->rx_rssi != 0) {
this->lastRxRssi = p->rx_rssi;
}
if (p->rx_snr > 0) {
this->lastRxSnr = p->rx_snr;
}
switch (p->decoded.portnum) {
case meshtastic_PortNum_TEXT_MESSAGE_APP:
case meshtastic_PortNum_ROUTING_APP:
@@ -79,6 +98,18 @@ class CannedMessageModule : public SinglePortModule, public Observable<const UIF
int getNextIndex();
int getPrevIndex();
#ifdef T_WATCH_S3
void drawKeyboard(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y);
String keyForCoordinates(uint x, uint y);
bool shift = false;
int charSet = 0;
void drawShiftIcon(OLEDDisplay *display, int x, int y, float scale = 1);
void drawBackspaceIcon(OLEDDisplay *display, int x, int y, float scale = 1);
void drawEnterIcon(OLEDDisplay *display, int x, int y, float scale = 1);
#endif
char highlight = 0x00;
int handleInputEvent(const InputEvent *event);
virtual bool wantUIFrame() override { return this->shouldDraw(); }
virtual Observable<const UIFrameEvent *> *getUIFrameObservable() override { return this; }
@@ -110,12 +141,84 @@ class CannedMessageModule : public SinglePortModule, public Observable<const UIF
ChannelIndex indexChannels[MAX_NUM_CHANNELS] = {0};
NodeNum incoming = NODENUM_BROADCAST;
bool ack = false; // True means ACK, false means NAK (error_reason != NONE)
float lastRxSnr = 0;
int32_t lastRxRssi = 0;
char messageStore[CANNED_MESSAGE_MODULE_MESSAGES_SIZE + 1];
char *messages[CANNED_MESSAGE_MODULE_MESSAGE_MAX_COUNT];
int messagesCount = 0;
unsigned long lastTouchMillis = 0;
String temporaryMessage;
#ifdef T_WATCH_S3
Letter keyboard[2][4][10] = {{{{"Q", 20, 0, 0, 0, 0},
{"W", 22, 0, 0, 0, 0},
{"E", 17, 0, 0, 0, 0},
{"R", 16.5, 0, 0, 0, 0},
{"T", 14, 0, 0, 0, 0},
{"Y", 15, 0, 0, 0, 0},
{"U", 16.5, 0, 0, 0, 0},
{"I", 5, 0, 0, 0, 0},
{"O", 19.5, 0, 0, 0, 0},
{"P", 15.5, 0, 0, 0, 0}},
{{"A", 14, 0, 0, 0, 0},
{"S", 15, 0, 0, 0, 0},
{"D", 16.5, 0, 0, 0, 0},
{"F", 15, 0, 0, 0, 0},
{"G", 17, 0, 0, 0, 0},
{"H", 15.5, 0, 0, 0, 0},
{"J", 12, 0, 0, 0, 0},
{"K", 15.5, 0, 0, 0, 0},
{"L", 14, 0, 0, 0, 0},
{"", 0, 0, 0, 0, 0}},
{{"⇧", 20, 0, 0, 0, 0},
{"Z", 14, 0, 0, 0, 0},
{"X", 14.5, 0, 0, 0, 0},
{"C", 15.5, 0, 0, 0, 0},
{"V", 13.5, 0, 0, 0, 0},
{"B", 15, 0, 0, 0, 0},
{"N", 15, 0, 0, 0, 0},
{"M", 17, 0, 0, 0, 0},
{"⌫", 20, 0, 0, 0, 0},
{"", 0, 0, 0, 0, 0}},
{{"123", 42, 0, 0, 0, 0},
{" ", 64, 0, 0, 0, 0},
{"↵", 36, 0, 0, 0, 0},
{"", 0, 0, 0, 0, 0},
{"", 0, 0, 0, 0, 0},
{"", 0, 0, 0, 0, 0},
{"", 0, 0, 0, 0, 0},
{"", 0, 0, 0, 0, 0},
{"", 0, 0, 0, 0, 0},
{"", 0, 0, 0, 0, 0}}},
{{{"1", 12, 0, 0, 0, 0},
{"2", 13.5, 0, 0, 0, 0},
{"3", 12.5, 0, 0, 0, 0},
{"4", 14, 0, 0, 0, 0},
{"5", 14, 0, 0, 0, 0},
{"6", 14, 0, 0, 0, 0},
{"7", 13.5, 0, 0, 0, 0},
{"8", 14, 0, 0, 0, 0},
{"9", 14, 0, 0, 0, 0},
{"0", 14, 0, 0, 0, 0}},
{{"-", 8, 0, 0, 0, 0},
{"/", 8, 0, 0, 0, 0},
{":", 4.5, 0, 0, 0, 0},
{";", 4.5, 0, 0, 0, 0},
{"(", 7, 0, 0, 0, 0},
{")", 6.5, 0, 0, 0, 0},
{"$", 12.5, 0, 0, 0, 0},
{"&", 15, 0, 0, 0, 0},
{"@", 21.5, 0, 0, 0, 0},
{"\"", 8, 0, 0, 0, 0}},
{{".", 8, 0, 0, 0, 0},
{",", 8, 0, 0, 0, 0},
{"?", 10, 0, 0, 0, 0},
{"!", 10, 0, 0, 0, 0},
{"'", 10, 0, 0, 0, 0},
{"⌫", 20, 0, 0, 0, 0}},
{{"ABC", 50, 0, 0, 0, 0}, {" ", 64, 0, 0, 0, 0}, {"↵", 36, 0, 0, 0, 0}}}};
#endif
};
extern CannedMessageModule *cannedMessageModule;
+6 -1
View File
@@ -58,10 +58,15 @@ void NodeInfoModule::sendOurNodeInfo(NodeNum dest, bool wantReplies, uint8_t cha
meshtastic_MeshPacket *NodeInfoModule::allocReply()
{
if (!airTime->isTxAllowedChannelUtil(false)) {
ignoreRequest = true; // Mark it as ignored for MeshModule
LOG_DEBUG("Skip sending NodeInfo due to > 40 percent channel util.\n");
return NULL;
}
uint32_t now = millis();
// If we sent our NodeInfo less than 5 min. ago, don't send it again as it may be still underway.
if (lastSentToMesh && (now - lastSentToMesh) < (5 * 60 * 1000)) {
LOG_DEBUG("Sending NodeInfo will be ignored since we just sent it.\n");
LOG_DEBUG("Skip sending NodeInfo since we just sent it less than 5 minutes ago.\n");
ignoreRequest = true; // Mark it as ignored for MeshModule
return NULL;
} else {
+24 -6
View File
@@ -55,6 +55,15 @@ bool PositionModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp, mes
isLocal = true;
if (config.position.fixed_position) {
LOG_DEBUG("Ignore incoming position update from myself except for time, because position.fixed_position is true\n");
#ifdef T_WATCH_S3
// Since we return early if position.fixed_position is true, set the T-Watch's RTC to the time received from the
// client device here
if (p.time && channels.getByIndex(mp.channel).role == meshtastic_Channel_Role_PRIMARY) {
trySetRtc(p, isLocal, true);
}
#endif
nodeDB->setLocalPosition(p, true);
return false;
} else {
@@ -71,8 +80,17 @@ bool PositionModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp, mes
p.time);
if (p.time && channels.getByIndex(mp.channel).role == meshtastic_Channel_Role_PRIMARY) {
bool force = false;
#ifdef T_WATCH_S3
// The T-Watch appears to "pause" its RTC when shut down, such that the time it reads upon powering on is the same as when
// it was shut down. So we need to force the update here, since otherwise RTC::perhapsSetRTC will ignore it because it
// will always be an equivalent or lesser RTCQuality (RTCQualityNTP or RTCQualityNet).
force = true;
#endif
// Set from phone RTC Quality to RTCQualityNTP since it should be approximately so
trySetRtc(p, isLocal);
trySetRtc(p, isLocal, force);
}
nodeDB->updatePosition(getFrom(&mp), p);
@@ -104,14 +122,14 @@ void PositionModule::alterReceivedProtobuf(meshtastic_MeshPacket &mp, meshtastic
}
}
void PositionModule::trySetRtc(meshtastic_Position p, bool isLocal)
void PositionModule::trySetRtc(meshtastic_Position p, bool isLocal, bool forceUpdate)
{
struct timeval tv;
uint32_t secs = p.time;
tv.tv_sec = secs;
tv.tv_usec = 0;
perhapsSetRTC(isLocal ? RTCQualityNTP : RTCQualityFromNet, &tv);
perhapsSetRTC(isLocal ? RTCQualityNTP : RTCQualityFromNet, &tv, forceUpdate);
}
meshtastic_MeshPacket *PositionModule::allocReply()
@@ -193,11 +211,11 @@ meshtastic_MeshPacket *PositionModule::allocReply()
// Strip out any time information before sending packets to other nodes - to keep the wire size small (and because other
// nodes shouldn't trust it anyways) Note: we allow a device with a local GPS to include the time, so that gpsless
// devices can get time.
if (getRTCQuality() < RTCQualityDevice) {
if (getRTCQuality() < RTCQualityGPS) {
LOG_INFO("Stripping time %u from position send\n", p.time);
p.time = 0;
} else {
p.time = getValidTime(RTCQualityDevice);
p.time = getValidTime(RTCQualityGPS);
LOG_INFO("Providing time to mesh %u\n", p.time);
}
@@ -436,4 +454,4 @@ void PositionModule::handleNewPosition()
}
}
#endif
#endif
+1 -1
View File
@@ -54,7 +54,7 @@ class PositionModule : public ProtobufModule<meshtastic_Position>, private concu
private:
struct SmartPosition getDistanceTraveledSinceLastSend(meshtastic_PositionLite currentPosition);
meshtastic_MeshPacket *allocAtakPli();
void trySetRtc(meshtastic_Position p, bool isLocal);
void trySetRtc(meshtastic_Position p, bool isLocal, bool forceUpdate = false);
uint32_t precision;
void sendLostAndFoundText();
@@ -92,7 +92,7 @@ bool AirQualityTelemetryModule::sendTelemetry(NodeNum dest, bool phoneOnly)
return false;
}
meshtastic_Telemetry m;
meshtastic_Telemetry m = meshtastic_Telemetry_init_zero;
m.time = getTime();
m.which_variant = meshtastic_Telemetry_air_quality_metrics_tag;
m.variant.air_quality_metrics.pm10_standard = data.pm10_standard;
+1 -1
View File
@@ -64,7 +64,7 @@ meshtastic_MeshPacket *DeviceTelemetryModule::allocReply()
meshtastic_Telemetry DeviceTelemetryModule::getDeviceTelemetry()
{
meshtastic_Telemetry t;
meshtastic_Telemetry t = meshtastic_Telemetry_init_zero;
t.time = getTime();
t.which_variant = meshtastic_Telemetry_device_metrics_tag;
+59 -3
View File
@@ -18,16 +18,21 @@
#include <OLEDDisplayUi.h>
// Sensors
#include "Sensor/AHT10.h"
#include "Sensor/BME280Sensor.h"
#include "Sensor/BME680Sensor.h"
#include "Sensor/BMP085Sensor.h"
#include "Sensor/BMP280Sensor.h"
#include "Sensor/DFRobotLarkSensor.h"
#include "Sensor/LPS22HBSensor.h"
#include "Sensor/MCP9808Sensor.h"
#include "Sensor/MLX90632Sensor.h"
#include "Sensor/OPT3001Sensor.h"
#include "Sensor/RCWL9620Sensor.h"
#include "Sensor/SHT31Sensor.h"
#include "Sensor/SHT4XSensor.h"
#include "Sensor/SHTC3Sensor.h"
#include "Sensor/TSL2591Sensor.h"
#include "Sensor/VEML7700Sensor.h"
BMP085Sensor bmp085Sensor;
@@ -39,8 +44,13 @@ SHTC3Sensor shtc3Sensor;
LPS22HBSensor lps22hbSensor;
SHT31Sensor sht31Sensor;
VEML7700Sensor veml7700Sensor;
TSL2591Sensor tsl2591Sensor;
OPT3001Sensor opt3001Sensor;
SHT4XSensor sht4xSensor;
RCWL9620Sensor rcwl9620Sensor;
AHT10Sensor aht10Sensor;
MLX90632Sensor mlx90632Sensor;
DFRobotLarkSensor dfRobotLarkSensor;
#define FAILED_STATE_SENSOR_READ_MULTIPLIER 10
#define DISPLAY_RECEIVEID_MEASUREMENTS_ON_SCREEN true
@@ -63,8 +73,8 @@ int32_t EnvironmentTelemetryModule::runOnce()
*/
// moduleConfig.telemetry.environment_measurement_enabled = 1;
// moduleConfig.telemetry.environment_screen_enabled = 1;
// moduleConfig.telemetry.environment_update_interval = 45;
// moduleConfig.telemetry.environment_screen_enabled = 1;
// moduleConfig.telemetry.environment_update_interval = 15;
if (!(moduleConfig.telemetry.environment_measurement_enabled || moduleConfig.telemetry.environment_screen_enabled)) {
// If this module is not enabled, and the user doesn't want the display screen don't waste any OSThread time on it
@@ -79,6 +89,8 @@ int32_t EnvironmentTelemetryModule::runOnce()
LOG_INFO("Environment Telemetry: Initializing\n");
// it's possible to have this module enabled, only for displaying values on the screen.
// therefore, we should only enable the sensor loop if measurement is also enabled
if (dfRobotLarkSensor.hasSensor())
result = dfRobotLarkSensor.runOnce();
if (bmp085Sensor.hasSensor())
result = bmp085Sensor.runOnce();
if (bmp280Sensor.hasSensor())
@@ -103,8 +115,16 @@ int32_t EnvironmentTelemetryModule::runOnce()
result = ina260Sensor.runOnce();
if (veml7700Sensor.hasSensor())
result = veml7700Sensor.runOnce();
if (tsl2591Sensor.hasSensor())
result = tsl2591Sensor.runOnce();
if (opt3001Sensor.hasSensor())
result = opt3001Sensor.runOnce();
if (rcwl9620Sensor.hasSensor())
result = rcwl9620Sensor.runOnce();
if (aht10Sensor.hasSensor())
result = aht10Sensor.runOnce();
if (mlx90632Sensor.hasSensor())
result = mlx90632Sensor.runOnce();
}
return result;
} else {
@@ -224,6 +244,10 @@ bool EnvironmentTelemetryModule::handleReceivedProtobuf(const meshtastic_MeshPac
t->variant.environment_metrics.temperature);
LOG_INFO("(Received from %s): voltage=%f, IAQ=%d, distance=%f, lux=%f\n", sender, t->variant.environment_metrics.voltage,
t->variant.environment_metrics.iaq, t->variant.environment_metrics.distance, t->variant.environment_metrics.lux);
LOG_INFO("(Received from %s): wind speed=%fm/s, direction=%d degrees\n", sender,
t->variant.environment_metrics.wind_speed, t->variant.environment_metrics.wind_direction);
#endif
// release previous packet before occupying a new spot
if (lastMeasurementPacket != nullptr)
@@ -237,12 +261,16 @@ bool EnvironmentTelemetryModule::handleReceivedProtobuf(const meshtastic_MeshPac
bool EnvironmentTelemetryModule::sendTelemetry(NodeNum dest, bool phoneOnly)
{
meshtastic_Telemetry m;
meshtastic_Telemetry m = meshtastic_Telemetry_init_zero;
bool valid = true;
bool hasSensor = false;
m.time = getTime();
m.which_variant = meshtastic_Telemetry_environment_metrics_tag;
if (dfRobotLarkSensor.hasSensor()) {
valid = valid && dfRobotLarkSensor.getMetrics(&m);
hasSensor = true;
}
if (sht31Sensor.hasSensor()) {
valid = valid && sht31Sensor.getMetrics(&m);
hasSensor = true;
@@ -287,10 +315,35 @@ bool EnvironmentTelemetryModule::sendTelemetry(NodeNum dest, bool phoneOnly)
valid = valid && veml7700Sensor.getMetrics(&m);
hasSensor = true;
}
if (tsl2591Sensor.hasSensor()) {
valid = valid && tsl2591Sensor.getMetrics(&m);
hasSensor = true;
}
if (opt3001Sensor.hasSensor()) {
valid = valid && opt3001Sensor.getMetrics(&m);
hasSensor = true;
}
if (mlx90632Sensor.hasSensor()) {
valid = valid && mlx90632Sensor.getMetrics(&m);
hasSensor = true;
}
if (rcwl9620Sensor.hasSensor()) {
valid = valid && rcwl9620Sensor.getMetrics(&m);
hasSensor = true;
}
if (aht10Sensor.hasSensor()) {
if (!bmp280Sensor.hasSensor()) {
valid = valid && aht10Sensor.getMetrics(&m);
hasSensor = true;
} else {
// prefer bmp280 temp if both sensors are present, fetch only humidity
meshtastic_Telemetry m_ahtx = meshtastic_Telemetry_init_zero;
LOG_INFO("AHTX0+BMP280 module detected: using temp from BMP280 and humy from AHTX0\n");
aht10Sensor.getMetrics(&m_ahtx);
m.variant.environment_metrics.relative_humidity = m_ahtx.variant.environment_metrics.relative_humidity;
}
}
valid = valid && hasSensor;
if (valid) {
@@ -301,6 +354,9 @@ bool EnvironmentTelemetryModule::sendTelemetry(NodeNum dest, bool phoneOnly)
LOG_INFO("(Sending): voltage=%f, IAQ=%d, distance=%f, lux=%f\n", m.variant.environment_metrics.voltage,
m.variant.environment_metrics.iaq, m.variant.environment_metrics.distance, m.variant.environment_metrics.lux);
LOG_INFO("(Sending): wind speed=%fm/s, direction=%d degrees\n", m.variant.environment_metrics.wind_speed,
m.variant.environment_metrics.wind_direction);
sensor_read_error_count = 0;
meshtastic_MeshPacket *p = allocDataProtobuf(m);
+1 -1
View File
@@ -165,7 +165,7 @@ bool PowerTelemetryModule::handleReceivedProtobuf(const meshtastic_MeshPacket &m
bool PowerTelemetryModule::sendTelemetry(NodeNum dest, bool phoneOnly)
{
meshtastic_Telemetry m;
meshtastic_Telemetry m = meshtastic_Telemetry_init_zero;
bool valid = false;
m.time = getTime();
m.which_variant = meshtastic_Telemetry_power_metrics_tag;
+41
View File
@@ -0,0 +1,41 @@
#include "configuration.h"
#if !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR
#include "../mesh/generated/meshtastic/telemetry.pb.h"
#include "AHT10.h"
#include "TelemetrySensor.h"
#include <Adafruit_AHTX0.h>
#include <typeinfo>
AHT10Sensor::AHT10Sensor() : TelemetrySensor(meshtastic_TelemetrySensorType_AHT10, "AHT10") {}
int32_t AHT10Sensor::runOnce()
{
LOG_INFO("Init sensor: %s\n", sensorName);
if (!hasSensor()) {
return DEFAULT_SENSOR_MINIMUM_WAIT_TIME_BETWEEN_READS;
}
aht10 = Adafruit_AHTX0();
status = aht10.begin(nodeTelemetrySensorsMap[sensorType].second, 0, nodeTelemetrySensorsMap[sensorType].first);
return initI2CSensor();
}
void AHT10Sensor::setup() {}
bool AHT10Sensor::getMetrics(meshtastic_Telemetry *measurement)
{
LOG_DEBUG("AHT10Sensor::getMetrics\n");
sensors_event_t humidity, temp;
aht10.getEvent(&humidity, &temp);
measurement->variant.environment_metrics.temperature = temp.temperature;
measurement->variant.environment_metrics.relative_humidity = humidity.relative_humidity;
return true;
}
#endif
+23
View File
@@ -0,0 +1,23 @@
#include "configuration.h"
#if !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR
#include "../mesh/generated/meshtastic/telemetry.pb.h"
#include "TelemetrySensor.h"
#include <Adafruit_AHTX0.h>
class AHT10Sensor : public TelemetrySensor
{
private:
Adafruit_AHTX0 aht10;
protected:
virtual void setup() override;
public:
AHT10Sensor();
virtual int32_t runOnce() override;
virtual bool getMetrics(meshtastic_Telemetry *measurement) override;
};
#endif
@@ -28,7 +28,7 @@ int32_t BME680Sensor::runOnce()
if (bme680.status == BSEC_OK) {
status = 1;
if (!bme680.setConfig(bsec_config_iaq)) {
if (!bme680.setConfig(bsec_config)) {
checkStatus("setConfig");
status = 0;
}
@@ -57,7 +57,7 @@ bool BME680Sensor::getMetrics(meshtastic_Telemetry *measurement)
measurement->variant.environment_metrics.temperature = bme680.getData(BSEC_OUTPUT_SENSOR_HEAT_COMPENSATED_TEMPERATURE).signal;
measurement->variant.environment_metrics.relative_humidity =
bme680.getData(BSEC_OUTPUT_SENSOR_HEAT_COMPENSATED_HUMIDITY).signal;
measurement->variant.environment_metrics.barometric_pressure = bme680.getData(BSEC_OUTPUT_RAW_PRESSURE).signal / 100.0F;
measurement->variant.environment_metrics.barometric_pressure = bme680.getData(BSEC_OUTPUT_RAW_PRESSURE).signal;
measurement->variant.environment_metrics.gas_resistance = bme680.getData(BSEC_OUTPUT_RAW_GAS).signal / 1000.0;
// Check if we need to save state to filesystem (every STATE_SAVE_PERIOD ms)
measurement->variant.environment_metrics.iaq = bme680.getData(BSEC_OUTPUT_IAQ).signal;
+3 -1
View File
@@ -8,7 +8,9 @@
#define STATE_SAVE_PERIOD UINT32_C(360 * 60 * 1000) // That's 6 hours worth of millis()
#include "bme680_iaq_33v_3s_4d/bsec_iaq.h"
const uint8_t bsec_config[] = {
#include "config/bme680/bme680_iaq_33v_3s_4d/bsec_iaq.txt"
};
class BME680Sensor : public TelemetrySensor
{
@@ -0,0 +1,53 @@
#include "configuration.h"
#if !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR
#include "../mesh/generated/meshtastic/telemetry.pb.h"
#include "DFRobotLarkSensor.h"
#include "TelemetrySensor.h"
#include "gps/GeoCoord.h"
#include <DFRobot_LarkWeatherStation.h>
#include <string>
DFRobotLarkSensor::DFRobotLarkSensor() : TelemetrySensor(meshtastic_TelemetrySensorType_DFROBOT_LARK, "DFROBOT_LARK") {}
int32_t DFRobotLarkSensor::runOnce()
{
LOG_INFO("Init sensor: %s\n", sensorName);
if (!hasSensor()) {
return DEFAULT_SENSOR_MINIMUM_WAIT_TIME_BETWEEN_READS;
}
lark = DFRobot_LarkWeatherStation_I2C(nodeTelemetrySensorsMap[sensorType].first, nodeTelemetrySensorsMap[sensorType].second);
if (lark.begin() == 0) // DFRobotLarkSensor init
{
LOG_DEBUG("DFRobotLarkSensor Init Succeed\n");
status = true;
} else {
LOG_ERROR("DFRobotLarkSensor Init Failed\n");
status = false;
}
return initI2CSensor();
}
void DFRobotLarkSensor::setup() {}
bool DFRobotLarkSensor::getMetrics(meshtastic_Telemetry *measurement)
{
measurement->variant.environment_metrics.temperature = lark.getValue("Temp").toFloat();
measurement->variant.environment_metrics.relative_humidity = lark.getValue("Humi").toFloat();
measurement->variant.environment_metrics.wind_speed = lark.getValue("Speed").toFloat();
measurement->variant.environment_metrics.wind_direction = GeoCoord::bearingToDegrees(lark.getValue("Dir").c_str());
measurement->variant.environment_metrics.barometric_pressure = lark.getValue("Pressure").toFloat();
LOG_INFO("Temperature: %f\n", measurement->variant.environment_metrics.temperature);
LOG_INFO("Humidity: %f\n", measurement->variant.environment_metrics.relative_humidity);
LOG_INFO("Wind Speed: %f\n", measurement->variant.environment_metrics.wind_speed);
LOG_INFO("Wind Direction: %d\n", measurement->variant.environment_metrics.wind_direction);
LOG_INFO("Barometric Pressure: %f\n", measurement->variant.environment_metrics.barometric_pressure);
return true;
}
#endif
@@ -0,0 +1,24 @@
#include "configuration.h"
#if !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR
#include "../mesh/generated/meshtastic/telemetry.pb.h"
#include "TelemetrySensor.h"
#include <DFRobot_LarkWeatherStation.h>
#include <string>
class DFRobotLarkSensor : public TelemetrySensor
{
private:
DFRobot_LarkWeatherStation_I2C lark = DFRobot_LarkWeatherStation_I2C();
protected:
virtual void setup() override;
public:
DFRobotLarkSensor();
virtual int32_t runOnce() override;
virtual bool getMetrics(meshtastic_Telemetry *measurement) override;
};
#endif
@@ -0,0 +1,40 @@
#include "configuration.h"
#if !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR
#include "../mesh/generated/meshtastic/telemetry.pb.h"
#include "MLX90632Sensor.h"
#include "TelemetrySensor.h"
MLX90632Sensor::MLX90632Sensor() : TelemetrySensor(meshtastic_TelemetrySensorType_MLX90632, "MLX90632") {}
int32_t MLX90632Sensor::runOnce()
{
LOG_INFO("Init sensor: %s\n", sensorName);
if (!hasSensor()) {
return DEFAULT_SENSOR_MINIMUM_WAIT_TIME_BETWEEN_READS;
}
MLX90632::status returnError;
if (mlx.begin(nodeTelemetrySensorsMap[sensorType].first, *nodeTelemetrySensorsMap[sensorType].second, returnError) ==
true) // MLX90632 init
{
LOG_DEBUG("MLX90632 Init Succeed\n");
status = true;
} else {
LOG_ERROR("MLX90632 Init Failed\n");
status = false;
}
return initI2CSensor();
}
void MLX90632Sensor::setup() {}
bool MLX90632Sensor::getMetrics(meshtastic_Telemetry *measurement)
{
measurement->variant.environment_metrics.temperature = mlx.getObjectTemp(); // Get the object temperature in Fahrenheit
return true;
}
#endif
@@ -0,0 +1,23 @@
#include "configuration.h"
#if !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR
#include "../mesh/generated/meshtastic/telemetry.pb.h"
#include "TelemetrySensor.h"
#include <SparkFun_MLX90632_Arduino_Library.h>
class MLX90632Sensor : public TelemetrySensor
{
private:
MLX90632 mlx = MLX90632();
protected:
virtual void setup() override;
public:
MLX90632Sensor();
virtual int32_t runOnce() override;
virtual bool getMetrics(meshtastic_Telemetry *measurement) override;
};
#endif
@@ -0,0 +1,44 @@
#include "OPT3001Sensor.h"
#include "../mesh/generated/meshtastic/telemetry.pb.h"
#include "TelemetrySensor.h"
#include "configuration.h"
#include <ClosedCube_OPT3001.h>
OPT3001Sensor::OPT3001Sensor() : TelemetrySensor(meshtastic_TelemetrySensorType_OPT3001, "OPT3001") {}
int32_t OPT3001Sensor::runOnce()
{
LOG_INFO("Init sensor: %s\n", sensorName);
if (!hasSensor()) {
return DEFAULT_SENSOR_MINIMUM_WAIT_TIME_BETWEEN_READS;
}
auto errorCode = opt3001.begin(nodeTelemetrySensorsMap[sensorType].first);
status = errorCode == NO_ERROR;
return initI2CSensor();
}
void OPT3001Sensor::setup()
{
OPT3001_Config newConfig;
newConfig.RangeNumber = B1100;
newConfig.ConvertionTime = B0;
newConfig.Latch = B1;
newConfig.ModeOfConversionOperation = B11;
OPT3001_ErrorCode errorConfig = opt3001.writeConfig(newConfig);
if (errorConfig != NO_ERROR) {
LOG_ERROR("OPT3001 configuration error #%d", errorConfig);
}
}
bool OPT3001Sensor::getMetrics(meshtastic_Telemetry *measurement)
{
OPT3001 result = opt3001.readResult();
measurement->variant.environment_metrics.lux = result.lux;
LOG_INFO("Lux: %f\n", measurement->variant.environment_metrics.lux);
return true;
}
@@ -0,0 +1,17 @@
#include "../mesh/generated/meshtastic/telemetry.pb.h"
#include "TelemetrySensor.h"
#include <ClosedCube_OPT3001.h>
class OPT3001Sensor : public TelemetrySensor
{
private:
ClosedCube_OPT3001 opt3001;
protected:
virtual void setup() override;
public:
OPT3001Sensor();
virtual int32_t runOnce() override;
virtual bool getMetrics(meshtastic_Telemetry *measurement) override;
};
@@ -0,0 +1,43 @@
#include "configuration.h"
#if !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR
#include "../mesh/generated/meshtastic/telemetry.pb.h"
#include "TSL2591Sensor.h"
#include "TelemetrySensor.h"
#include <Adafruit_TSL2591.h>
#include <typeinfo>
TSL2591Sensor::TSL2591Sensor() : TelemetrySensor(meshtastic_TelemetrySensorType_TSL25911FN, "TSL2591") {}
int32_t TSL2591Sensor::runOnce()
{
LOG_INFO("Init sensor: %s\n", sensorName);
if (!hasSensor()) {
return DEFAULT_SENSOR_MINIMUM_WAIT_TIME_BETWEEN_READS;
}
status = tsl.begin(nodeTelemetrySensorsMap[sensorType].second);
return initI2CSensor();
}
void TSL2591Sensor::setup()
{
tsl.setGain(TSL2591_GAIN_MED); // 25x gain
tsl.setTiming(TSL2591_INTEGRATIONTIME_300MS);
}
bool TSL2591Sensor::getMetrics(meshtastic_Telemetry *measurement)
{
uint32_t lum = tsl.getFullLuminosity();
uint16_t ir, full;
ir = lum >> 16;
full = lum & 0xFFFF;
measurement->variant.environment_metrics.lux = tsl.calculateLux(full, ir);
LOG_INFO("Lux: %f\n", measurement->variant.environment_metrics.lux);
return true;
}
#endif
@@ -0,0 +1,22 @@
#include "configuration.h"
#if !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR
#include "../mesh/generated/meshtastic/telemetry.pb.h"
#include "TelemetrySensor.h"
#include <Adafruit_TSL2591.h>
class TSL2591Sensor : public TelemetrySensor
{
private:
Adafruit_TSL2591 tsl;
protected:
virtual void setup() override;
public:
TSL2591Sensor();
virtual int32_t runOnce() override;
virtual bool getMetrics(meshtastic_Telemetry *measurement) override;
};
#endif
@@ -1,7 +1,11 @@
#include "VEML7700Sensor.h"
#include "configuration.h"
#if !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR
#include "../mesh/generated/meshtastic/telemetry.pb.h"
#include "TelemetrySensor.h"
#include "configuration.h"
#include "VEML7700Sensor.h"
#include <Adafruit_VEML7700.h>
#include <typeinfo>
@@ -57,4 +61,5 @@ bool VEML7700Sensor::getMetrics(meshtastic_Telemetry *measurement)
measurement->variant.environment_metrics.lux);
return true;
}
}
#endif
@@ -1,3 +1,7 @@
#include "configuration.h"
#if !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR
#include "../mesh/generated/meshtastic/telemetry.pb.h"
#include "TelemetrySensor.h"
#include <Adafruit_VEML7700.h>
@@ -19,4 +23,5 @@ class VEML7700Sensor : public TelemetrySensor
VEML7700Sensor();
virtual int32_t runOnce() override;
virtual bool getMetrics(meshtastic_Telemetry *measurement) override;
};
};
#endif
@@ -1,86 +0,0 @@
#if !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR
#include "bsec_iaq.h"
const uint8_t bsec_config_iaq[1974] = {
0, 0, 4, 2, 189, 1, 0, 0, 0, 0, 0, 0, 158, 7, 0, 0, 176, 0, 1, 0, 0, 192, 168, 71, 64,
49, 119, 76, 0, 0, 97, 69, 0, 0, 97, 69, 137, 65, 0, 191, 205, 204, 204, 190, 0, 0, 64, 191, 225, 122,
148, 190, 10, 0, 3, 0, 0, 0, 96, 64, 23, 183, 209, 56, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 205, 204, 204, 189, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 63, 0, 0, 0, 0, 0, 0, 128, 63,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 63, 0, 0, 0, 0, 0, 0, 128, 63, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 128, 63, 0, 0, 0, 0, 0, 0, 128, 63, 82, 73, 157, 188, 95, 41, 203, 61, 118, 224,
108, 63, 155, 230, 125, 63, 191, 14, 124, 63, 0, 0, 160, 65, 0, 0, 32, 66, 0, 0, 160, 65, 0, 0, 32,
66, 0, 0, 32, 66, 0, 0, 160, 65, 0, 0, 32, 66, 0, 0, 160, 65, 8, 0, 2, 0, 236, 81, 133, 66,
16, 0, 3, 0, 10, 215, 163, 60, 10, 215, 35, 59, 10, 215, 35, 59, 13, 0, 5, 0, 0, 0, 0, 0, 100,
35, 41, 29, 86, 88, 0, 9, 0, 229, 208, 34, 62, 0, 0, 0, 0, 0, 0, 0, 0, 218, 27, 156, 62, 225,
11, 67, 64, 0, 0, 160, 64, 0, 0, 0, 0, 0, 0, 0, 0, 94, 75, 72, 189, 93, 254, 159, 64, 66, 62,
160, 191, 0, 0, 0, 0, 0, 0, 0, 0, 33, 31, 180, 190, 138, 176, 97, 64, 65, 241, 99, 190, 0, 0, 0,
0, 0, 0, 0, 0, 167, 121, 71, 61, 165, 189, 41, 192, 184, 30, 189, 64, 12, 0, 10, 0, 0, 0, 0, 0,
0, 0, 0, 0, 13, 5, 11, 0, 1, 1, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 63, 0, 0, 128, 63, 0, 0, 128, 63, 0, 0, 128, 63, 0, 0,
128, 63, 0, 0, 128, 63, 0, 0, 128, 63, 0, 0, 128, 63, 0, 0, 128, 63, 0, 0, 128, 63, 0, 0, 128,
63, 0, 0, 128, 63, 0, 0, 128, 63, 0, 0, 128, 63, 0, 0, 128, 63, 0, 0, 128, 63, 0, 0, 128, 63,
0, 0, 128, 63, 0, 0, 128, 63, 0, 0, 128, 63, 0, 0, 128, 63, 0, 0, 128, 63, 0, 0, 128, 63, 0,
0, 128, 63, 0, 0, 128, 63, 0, 0, 128, 63, 0, 0, 128, 63, 0, 0, 128, 63, 0, 0, 128, 63, 0, 0,
128, 63, 0, 0, 128, 63, 0, 0, 128, 63, 0, 0, 128, 63, 0, 0, 128, 63, 0, 0, 128, 63, 0, 0, 128,
63, 0, 0, 128, 63, 0, 0, 128, 63, 0, 0, 128, 63, 0, 0, 128, 63, 0, 0, 128, 63, 0, 0, 128, 63,
0, 0, 128, 63, 0, 0, 128, 63, 0, 0, 128, 63, 0, 0, 128, 63, 0, 0, 128, 63, 0, 0, 128, 63, 0,
0, 128, 63, 0, 0, 128, 63, 0, 0, 128, 63, 0, 0, 128, 63, 0, 0, 128, 63, 0, 0, 128, 63, 0, 0,
128, 63, 0, 0, 128, 63, 0, 0, 128, 63, 0, 0, 128, 63, 0, 0, 128, 63, 0, 0, 128, 63, 0, 0, 128,
63, 0, 0, 128, 63, 0, 0, 128, 63, 0, 0, 128, 63, 0, 0, 128, 63, 0, 0, 128, 63, 0, 0, 128, 63,
0, 0, 128, 63, 0, 0, 128, 63, 0, 0, 128, 63, 0, 0, 128, 63, 0, 0, 128, 63, 0, 0, 128, 63, 0,
0, 128, 63, 0, 0, 128, 63, 0, 0, 128, 63, 0, 0, 128, 63, 0, 0, 128, 63, 0, 0, 128, 63, 0, 0,
128, 63, 0, 0, 128, 63, 0, 0, 128, 63, 0, 0, 128, 63, 0, 0, 128, 63, 0, 0, 128, 63, 0, 0, 128,
63, 0, 0, 128, 63, 0, 0, 128, 63, 0, 0, 128, 63, 0, 0, 128, 63, 0, 0, 128, 63, 0, 0, 128, 63,
0, 0, 128, 63, 0, 0, 128, 63, 0, 0, 128, 63, 0, 0, 128, 63, 0, 0, 128, 63, 0, 0, 128, 63, 0,
0, 128, 63, 0, 0, 128, 63, 0, 0, 128, 63, 0, 0, 128, 63, 0, 0, 128, 63, 0, 0, 128, 63, 0, 0,
128, 63, 0, 0, 128, 63, 0, 0, 128, 63, 0, 0, 128, 63, 0, 0, 128, 63, 0, 0, 128, 63, 0, 0, 128,
63, 0, 0, 128, 63, 0, 0, 128, 63, 0, 0, 128, 63, 0, 0, 128, 63, 0, 0, 128, 63, 0, 0, 128, 63,
0, 0, 128, 63, 0, 0, 128, 63, 0, 0, 128, 63, 0, 0, 128, 63, 0, 0, 128, 63, 0, 0, 128, 63, 0,
0, 128, 63, 0, 0, 128, 63, 0, 0, 128, 63, 0, 0, 128, 63, 0, 0, 128, 63, 0, 0, 128, 63, 0, 0,
128, 63, 0, 0, 128, 63, 0, 0, 128, 63, 0, 0, 128, 63, 0, 0, 128, 63, 0, 0, 128, 63, 0, 0, 128,
63, 0, 0, 128, 63, 0, 0, 128, 63, 0, 0, 128, 63, 0, 0, 128, 63, 0, 0, 128, 63, 0, 0, 128, 63,
0, 0, 128, 63, 0, 0, 128, 63, 0, 0, 128, 63, 0, 0, 128, 63, 0, 0, 128, 63, 0, 0, 128, 63, 0,
0, 128, 63, 0, 0, 128, 63, 0, 0, 128, 63, 0, 0, 128, 63, 0, 0, 128, 63, 0, 0, 128, 63, 0, 0,
128, 63, 0, 0, 128, 63, 0, 0, 128, 63, 0, 0, 128, 63, 0, 0, 128, 63, 0, 0, 128, 63, 0, 0, 128,
63, 0, 0, 128, 63, 0, 0, 128, 63, 0, 0, 128, 63, 0, 0, 128, 63, 0, 0, 128, 63, 0, 0, 128, 63,
0, 0, 128, 63, 0, 0, 128, 63, 0, 0, 128, 63, 0, 0, 128, 63, 0, 0, 128, 63, 0, 0, 128, 63, 0,
0, 128, 63, 0, 0, 128, 63, 0, 0, 128, 63, 0, 0, 128, 63, 0, 0, 128, 63, 0, 0, 128, 63, 0, 0,
128, 63, 0, 0, 128, 63, 0, 0, 128, 63, 0, 0, 128, 63, 0, 0, 128, 63, 0, 0, 128, 63, 0, 0, 128,
63, 0, 0, 128, 63, 0, 0, 128, 63, 0, 0, 128, 63, 0, 0, 128, 63, 0, 0, 128, 63, 0, 0, 128, 63,
0, 0, 128, 63, 0, 0, 128, 63, 0, 0, 128, 63, 0, 0, 128, 63, 0, 0, 128, 63, 0, 0, 128, 63, 0,
0, 128, 63, 0, 0, 128, 63, 0, 0, 128, 63, 0, 0, 128, 63, 0, 0, 128, 63, 0, 0, 128, 63, 0, 0,
128, 63, 0, 0, 128, 63, 0, 0, 128, 63, 0, 0, 128, 63, 0, 0, 128, 63, 0, 0, 128, 63, 0, 0, 128,
63, 0, 0, 128, 63, 0, 0, 128, 63, 0, 0, 128, 63, 0, 0, 128, 63, 0, 0, 128, 63, 0, 0, 128, 63,
0, 0, 128, 63, 0, 0, 128, 63, 0, 0, 128, 63, 0, 0, 128, 63, 0, 0, 128, 63, 0, 0, 128, 63, 0,
0, 128, 63, 0, 0, 128, 63, 0, 0, 128, 63, 0, 0, 128, 63, 0, 0, 128, 63, 0, 0, 128, 63, 0, 0,
128, 63, 0, 0, 128, 63, 0, 0, 128, 63, 0, 0, 128, 63, 0, 0, 128, 63, 0, 0, 128, 63, 0, 0, 128,
63, 0, 0, 128, 63, 0, 0, 128, 63, 0, 0, 128, 63, 0, 0, 128, 63, 0, 0, 128, 63, 0, 0, 128, 63,
0, 0, 128, 63, 0, 0, 128, 63, 0, 0, 128, 63, 0, 0, 128, 63, 0, 0, 128, 63, 0, 0, 128, 63, 0,
0, 128, 63, 0, 0, 128, 63, 0, 0, 128, 63, 0, 0, 128, 63, 0, 0, 128, 63, 0, 0, 128, 63, 0, 0,
128, 63, 0, 0, 128, 63, 0, 0, 128, 63, 0, 0, 128, 63, 0, 0, 128, 63, 0, 0, 128, 63, 0, 0, 128,
63, 0, 0, 128, 63, 0, 0, 128, 63, 0, 0, 128, 63, 0, 0, 128, 63, 0, 0, 128, 63, 0, 0, 128, 63,
0, 0, 128, 63, 0, 0, 128, 63, 0, 0, 128, 63, 10, 10, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 63, 0, 0, 128, 63,
0, 0, 128, 63, 0, 0, 128, 63, 0, 0, 128, 63, 0, 0, 128, 63, 0, 0, 128, 63, 0, 0, 128, 63, 0,
0, 128, 63, 0, 0, 128, 63, 0, 0, 128, 63, 0, 0, 128, 63, 0, 0, 128, 63, 0, 0, 0, 88, 1, 254,
0, 2, 1, 5, 48, 117, 100, 0, 44, 1, 112, 23, 151, 7, 132, 3, 197, 0, 92, 4, 144, 1, 64, 1, 64,
1, 144, 1, 48, 117, 48, 117, 48, 117, 48, 117, 100, 0, 100, 0, 100, 0, 48, 117, 48, 117, 48, 117, 100, 0,
100, 0, 48, 117, 48, 117, 8, 7, 8, 7, 8, 7, 8, 7, 8, 7, 100, 0, 100, 0, 100, 0, 100, 0, 48,
117, 48, 117, 48, 117, 100, 0, 100, 0, 100, 0, 48, 117, 48, 117, 100, 0, 100, 0, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 44, 1, 44, 1, 44, 1, 44, 1, 44, 1, 44, 1, 44, 1, 44, 1, 44, 1, 44, 1, 44,
1, 44, 1, 44, 1, 44, 1, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 112, 23, 112, 23, 112, 23, 112, 23,
8, 7, 8, 7, 8, 7, 8, 7, 112, 23, 112, 23, 112, 23, 112, 23, 112, 23, 112, 23, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 112, 23, 112, 23, 112, 23, 112, 23, 255, 255, 255, 255,
220, 5, 220, 5, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 220, 5, 220, 5, 220, 5, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 44, 1, 0, 5, 10, 5,
0, 2, 0, 10, 0, 30, 0, 5, 0, 5, 0, 5, 0, 5, 0, 5, 0, 5, 0, 64, 1, 100, 0, 100, 0,
100, 0, 200, 0, 200, 0, 200, 0, 64, 1, 64, 1, 64, 1, 10, 0, 0, 0, 0, 0, 21, 122, 0, 0};
#endif
@@ -1,7 +0,0 @@
#if !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR
#include <stdint.h>
extern const uint8_t bsec_config_iaq[1974];
#endif
+24 -4
View File
@@ -16,17 +16,37 @@ bool TraceRouteModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp, m
void TraceRouteModule::alterReceivedProtobuf(meshtastic_MeshPacket &p, meshtastic_RouteDiscovery *r)
{
auto &incoming = p.decoded;
// Only append an ID for the request (one way) and if we are not the destination (the reply will have our NodeNum already)
if (!incoming.request_id && p.to != nodeDB->getNodeNum()) {
appendMyID(r);
printRoute(r, p.from, NODENUM_BROADCAST);
// Only append IDs for the request (one way)
if (!incoming.request_id) {
// Insert unknown hops if necessary
insertUnknownHops(p, r);
// Don't add ourselves if we are the destination (the reply will have our NodeNum already)
if (p.to != nodeDB->getNodeNum()) {
appendMyID(r);
printRoute(r, p.from, NODENUM_BROADCAST);
}
// Set updated route to the payload of the to be flooded packet
p.decoded.payload.size =
pb_encode_to_bytes(p.decoded.payload.bytes, sizeof(p.decoded.payload.bytes), &meshtastic_RouteDiscovery_msg, r);
}
}
void TraceRouteModule::insertUnknownHops(meshtastic_MeshPacket &p, meshtastic_RouteDiscovery *r)
{
// Only insert unknown hops if hop_start is valid
if (p.hop_start != 0 && p.hop_limit <= p.hop_start) {
uint8_t hopsTaken = p.hop_start - p.hop_limit;
int8_t diff = hopsTaken - r->route_count;
for (uint8_t i = 0; i < diff; i++) {
if (r->route_count < sizeof(r->route) / sizeof(r->route[0])) {
r->route[r->route_count] = NODENUM_BROADCAST; // This will represent an unknown hop
r->route_count += 1;
}
}
}
}
void TraceRouteModule::appendMyID(meshtastic_RouteDiscovery *updated)
{
// Length of route array can normally not be exceeded due to the max. hop_limit of 7
+3
View File
@@ -19,6 +19,9 @@ class TraceRouteModule : public ProtobufModule<meshtastic_RouteDiscovery>
void alterReceivedProtobuf(meshtastic_MeshPacket &p, meshtastic_RouteDiscovery *r) override;
private:
// Call to add unknown hops (e.g. when a node couldn't decrypt it) to the route based on hopStart and current hopLimit
void insertUnknownHops(meshtastic_MeshPacket &p, meshtastic_RouteDiscovery *r);
// Call to add your ID to the route array of a RouteDiscovery message
void appendMyID(meshtastic_RouteDiscovery *r);
+2 -1
View File
@@ -396,6 +396,7 @@ bool MQTT::wantsLink() const
int32_t MQTT::runOnce()
{
#ifdef HAS_NETWORKING
if (!moduleConfig.mqtt.enabled || !(moduleConfig.mqtt.map_reporting_enabled || channels.anyMqttEnabled()))
return disable();
@@ -408,7 +409,7 @@ int32_t MQTT::runOnce()
publishQueuedMessages();
return 200;
}
#ifdef HAS_NETWORKING
else if (!pubSub.loop()) {
if (!wantConnection)
return 5000; // If we don't want connection now, check again in 5 secs
+3 -1
View File
@@ -157,7 +157,9 @@ void NimbleBluetooth::setup()
NimBLEDevice::setPower(ESP_PWR_LVL_P9);
if (config.bluetooth.mode != meshtastic_Config_BluetoothConfig_PairingMode_NO_PIN) {
NimBLEDevice::setSecurityAuth(true, true, true);
NimBLEDevice::setSecurityAuth(BLE_SM_PAIR_AUTHREQ_BOND | BLE_SM_PAIR_AUTHREQ_MITM | BLE_SM_PAIR_AUTHREQ_SC);
NimBLEDevice::setSecurityInitKey(BLE_SM_PAIR_KEY_DIST_ENC | BLE_SM_PAIR_KEY_DIST_ID);
NimBLEDevice::setSecurityRespKey(BLE_SM_PAIR_KEY_DIST_ENC | BLE_SM_PAIR_KEY_DIST_ID);
NimBLEDevice::setSecurityIOCap(BLE_HS_IO_DISPLAY_ONLY);
}
bleServer = NimBLEDevice::createServer();
+2
View File
@@ -145,6 +145,8 @@
#define HW_VENDOR meshtastic_HardwareModel_UNPHONE
#elif defined(WIPHONE)
#define HW_VENDOR meshtastic_HardwareModel_WIPHONE
#elif defined(RADIOMASTER_900_BANDIT_NANO)
#define HW_VENDOR meshtastic_HardwareModel_RADIOMASTER_900_BANDIT_NANO
#endif
// -----------------------------------------------------------------------------
+23
View File
@@ -0,0 +1,23 @@
// Free up some precious space in the iram0_0_seg memory segment
#include <stdint.h>
#include <esp_attr.h>
#include <esp_flash.h>
#include <spi_flash_chip_driver.h>
#define IRAM_SECTION section(".iram1.stub")
IRAM_ATTR esp_err_t stub_probe(esp_flash_t *chip, uint32_t flash_id)
{
return ESP_ERR_NOT_FOUND;
}
const spi_flash_chip_t stub_flash_chip __attribute__((IRAM_SECTION)) = {
.name = "stub",
.probe = stub_probe,
};
extern const spi_flash_chip_t __wrap_esp_flash_chip_gd __attribute__((IRAM_SECTION, alias("stub_flash_chip")));
extern const spi_flash_chip_t __wrap_esp_flash_chip_issi __attribute__((IRAM_SECTION, alias("stub_flash_chip")));
extern const spi_flash_chip_t __wrap_esp_flash_chip_winbond __attribute__((IRAM_SECTION, alias("stub_flash_chip")));
+23 -12
View File
@@ -24,17 +24,23 @@
#if !defined(CONFIG_IDF_TARGET_ESP32S2) && !MESHTASTIC_EXCLUDE_BLUETOOTH
void setBluetoothEnable(bool enable)
{
if (!isWifiAvailable() && config.bluetooth.enabled == true) {
if (!nimbleBluetooth) {
nimbleBluetooth = new NimbleBluetooth();
#ifndef MESHTASTIC_EXCLUDE_WIFI
if (!isWifiAvailable() && config.bluetooth.enabled == true)
#endif
#ifdef MESHTASTIC_EXCLUDE_WIFI
if (config.bluetooth.enabled == true)
#endif
{
if (!nimbleBluetooth) {
nimbleBluetooth = new NimbleBluetooth();
}
if (enable && !nimbleBluetooth->isActive()) {
nimbleBluetooth->setup();
}
// For ESP32, no way to recover from bluetooth shutdown without reboot
// BLE advertising automatically stops when MCU enters light-sleep(?)
// For deep-sleep, shutdown hardware with nimbleBluetooth->deinit(). Requires reboot to reverse
}
if (enable && !nimbleBluetooth->isActive()) {
nimbleBluetooth->setup();
}
// For ESP32, no way to recover from bluetooth shutdown without reboot
// BLE advertising automatically stops when MCU enters light-sleep(?)
// For deep-sleep, shutdown hardware with nimbleBluetooth->deinit(). Requires reboot to reverse
}
}
#else
void setBluetoothEnable(bool enable) {}
@@ -214,11 +220,16 @@ void cpuDeepSleep(uint32_t msecToWake)
#endif
// Not needed because both of the current boards have external pullups
// FIXME change polarity in hw so we can wake on ANY_HIGH instead - that would allow us to use all three buttons (instead of
// just the first) gpio_pullup_en((gpio_num_t)BUTTON_PIN);
// FIXME change polarity in hw so we can wake on ANY_HIGH instead - that would allow us to use all three buttons (instead
// of just the first) gpio_pullup_en((gpio_num_t)BUTTON_PIN);
#if SOC_PM_SUPPORT_EXT_WAKEUP
#ifdef CONFIG_IDF_TARGET_ESP32
// ESP_EXT1_WAKEUP_ALL_LOW has been deprecated since esp-idf v5.4 for any other target.
esp_sleep_enable_ext1_wakeup(gpioMask, ESP_EXT1_WAKEUP_ALL_LOW);
#else
esp_sleep_enable_ext1_wakeup(gpioMask, ESP_EXT1_WAKEUP_ANY_LOW);
#endif
#endif
#endif
+2
View File
@@ -56,6 +56,8 @@
#define HW_VENDOR meshtastic_HardwareModel_TWC_MESH_V4
#elif defined(NRF52_PROMICRO_DIY)
#define HW_VENDOR meshtastic_HardwareModel_NRF52_PROMICRO_DIY
#elif defined(WIO_WM1110)
#define HW_VENDOR meshtastic_HardwareModel_WIO_WM1110
#elif defined(PRIVATE_HW) || defined(FEATHER_DIY)
#define HW_VENDOR meshtastic_HardwareModel_PRIVATE_HW
#else
-9
View File
@@ -11,15 +11,6 @@
#define USB_VID 0x303a
#define USB_PID 0x1001
#define EXTERNAL_NUM_INTERRUPTS 46
#define NUM_DIGITAL_PINS 48
#define NUM_ANALOG_INPUTS 20
#define analogInputToDigitalPin(p) (((p) < 20) ? (analogChannelToDigitalPin(p)) : -1)
#define digitalPinToInterrupt(p) \
(((p) < 48) ? (p) : -1) // Maybe it should be <= 48 but this is from a trustworthy source so it is likely correct
#define digitalPinHasPWM(p) (p < 46)
// Serial
static const uint8_t TX = UART_TX;
static const uint8_t RX = UART_RX;
-9
View File
@@ -11,15 +11,6 @@
#define USB_VID 0x303a
#define USB_PID 0x1001
#define EXTERNAL_NUM_INTERRUPTS 46
#define NUM_DIGITAL_PINS 48
#define NUM_ANALOG_INPUTS 20
#define analogInputToDigitalPin(p) (((p) < 20) ? (analogChannelToDigitalPin(p)) : -1)
#define digitalPinToInterrupt(p) \
(((p) < 48) ? (p) : -1) // Maybe it should be <= 48 but this is from a trustworthy source so it is likely correct
#define digitalPinHasPWM(p) (p < 46)
// Serial
static const uint8_t TX = UART_TX;
static const uint8_t RX = UART_RX;
@@ -6,14 +6,6 @@
#define USB_VID 0x303a
#define USB_PID 0x1001
#define EXTERNAL_NUM_INTERRUPTS 46
#define NUM_DIGITAL_PINS 48
#define NUM_ANALOG_INPUTS 20
#define analogInputToDigitalPin(p) (((p) < 20) ? (analogChannelToDigitalPin(p)) : -1)
#define digitalPinToInterrupt(p) (((p) < 48) ? (p) : -1)
#define digitalPinHasPWM(p) (p < 46)
static const uint8_t TX = 43;
static const uint8_t RX = 44;
-8
View File
@@ -6,14 +6,6 @@
#define USB_VID 0x303a
#define USB_PID 0x1001
#define EXTERNAL_NUM_INTERRUPTS 46
#define NUM_DIGITAL_PINS 48
#define NUM_ANALOG_INPUTS 20
#define analogInputToDigitalPin(p) (((p) < 20) ? (analogChannelToDigitalPin(p)) : -1)
#define digitalPinToInterrupt(p) (((p) < 48) ? (p) : -1)
#define digitalPinHasPWM(p) (p < 46)
// The default Wire will be mapped to PMU and RTC
static const uint8_t SDA = 15;
static const uint8_t SCL = 16;
-8
View File
@@ -3,14 +3,6 @@
#include <stdint.h>
#define EXTERNAL_NUM_INTERRUPTS 22
#define NUM_DIGITAL_PINS 22
#define NUM_ANALOG_INPUTS 6
#define analogInputToDigitalPin(p) (((p) < NUM_ANALOG_INPUTS) ? (esp32_adc2gpio[(p)]) : -1)
#define digitalPinToInterrupt(p) (((p) < NUM_DIGITAL_PINS) ? (p) : -1)
#define digitalPinHasPWM(p) (p < EXTERNAL_NUM_INTERRUPTS)
static const uint8_t TX = 21;
static const uint8_t RX = 20;
+1 -1
View File
@@ -16,7 +16,7 @@
#define BATTERY_PIN 1 // A battery voltage measurement pin, voltage divider connected here to measure battery voltage
#define ADC_CHANNEL ADC1_GPIO1_CHANNEL
#define ADC_ATTENUATION ADC_ATTEN_DB_2_5 // lower dB for high resistance voltage divider
#define ADC_MULTIPLIER 4.9
#define ADC_MULTIPLIER 4.9 * 1.045
#define USE_SX1262
@@ -7,14 +7,6 @@
#define DISPLAY_HEIGHT 64
#define DISPLAY_WIDTH 128
#define EXTERNAL_NUM_INTERRUPTS 16
#define NUM_DIGITAL_PINS 40
#define NUM_ANALOG_INPUTS 16
#define analogInputToDigitalPin(p) (((p) < 20) ? (analogChannelToDigitalPin(p)) : -1)
#define digitalPinToInterrupt(p) (((p) < 40) ? (p) : -1)
#define digitalPinHasPWM(p) (p < 34)
static const uint8_t LED_BUILTIN = 35;
#define BUILTIN_LED LED_BUILTIN // backward compatibility
#define LED_BUILTIN LED_BUILTIN
@@ -7,14 +7,6 @@
#define DISPLAY_HEIGHT 64
#define DISPLAY_WIDTH 128
#define EXTERNAL_NUM_INTERRUPTS 16
#define NUM_DIGITAL_PINS 40
#define NUM_ANALOG_INPUTS 16
#define analogInputToDigitalPin(p) (((p) < 20) ? (analogChannelToDigitalPin(p)) : -1)
#define digitalPinToInterrupt(p) (((p) < 40) ? (p) : -1)
#define digitalPinHasPWM(p) (p < 34)
static const uint8_t LED_BUILTIN = 35;
#define BUILTIN_LED LED_BUILTIN // backward compatibility
#define LED_BUILTIN LED_BUILTIN

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