mirror of
https://github.com/meshtastic/firmware.git
synced 2026-02-02 07:01:54 +00:00
Compare commits
57 Commits
v2.1.15.cd
...
v2.1.20.47
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
470363d294 | ||
|
|
fb21bfe0f5 | ||
|
|
0739bc0cea | ||
|
|
1c74479555 | ||
|
|
084ad1b722 | ||
|
|
2486892e6d | ||
|
|
77efbb3f5d | ||
|
|
eb7025f1b1 | ||
|
|
69beef8310 | ||
|
|
468807466c | ||
|
|
8927cffd64 | ||
|
|
5995c7060d | ||
|
|
541291cc70 | ||
|
|
4306c32349 | ||
|
|
491fe52841 | ||
|
|
ad5de5a724 | ||
|
|
ab32503601 | ||
|
|
e4e26a819b | ||
|
|
6d97d5dfa2 | ||
|
|
c75965480f | ||
|
|
003047baaf | ||
|
|
4ace59fc18 | ||
|
|
aa0b56e947 | ||
|
|
42d79d012e | ||
|
|
d3e7e45ded | ||
|
|
0cca7751cd | ||
|
|
de53280ffc | ||
|
|
65aafe7ea1 | ||
|
|
6e96216ba3 | ||
|
|
da389eb787 | ||
|
|
d8ad2b3f48 | ||
|
|
97606cd382 | ||
|
|
5c34e36bec | ||
|
|
9c141919f6 | ||
|
|
b9ad274104 | ||
|
|
4ef61f0f15 | ||
|
|
c120549215 | ||
|
|
7ca2e818df | ||
|
|
f02923435b | ||
|
|
eb0a96a79e | ||
|
|
9e2b86b92c | ||
|
|
d0cf70c8b3 | ||
|
|
44a906dd01 | ||
|
|
ccb682bbb8 | ||
|
|
e677a02273 | ||
|
|
47168d5063 | ||
|
|
5591b9b9f5 | ||
|
|
a2c5b92840 | ||
|
|
685d27f566 | ||
|
|
f71869215d | ||
|
|
81f80546b4 | ||
|
|
44a54278b3 | ||
|
|
194833d77f | ||
|
|
207d421fca | ||
|
|
fb14487f2f | ||
|
|
365a91f3d9 | ||
|
|
5edc872c31 |
2
.github/ISSUE_TEMPLATE/Bug Report.yml
vendored
2
.github/ISSUE_TEMPLATE/Bug Report.yml
vendored
@@ -40,10 +40,12 @@ body:
|
||||
- T-Echo
|
||||
- Rak4631
|
||||
- Rak11200
|
||||
- Rak11310
|
||||
- Heltec v1
|
||||
- Heltec v2
|
||||
- Heltec v2.1
|
||||
- Heltec V3
|
||||
- Raspberry Pi Pico (W)
|
||||
- Relay v1
|
||||
- Relay v2
|
||||
- DIY
|
||||
|
||||
5
.github/pull_request_template.md
vendored
5
.github/pull_request_template.md
vendored
@@ -7,7 +7,8 @@
|
||||
is appreciated." This will allow other devs to potentially save you time by not accidentially duplicating work etc...
|
||||
- Please do not check in files that don't have real changes
|
||||
- Please do not reformat lines that you didn't have to change the code on
|
||||
- We recommend using the [Visual Studio Code](https://platformio.org/install/ide?install=vscode) editor and the 'clang-format' extension,
|
||||
because automatically follows our indentation rules and it's auto reformatting will not cause spurious changes to lines.
|
||||
- We recommend using the [Visual Studio Code](https://platformio.org/install/ide?install=vscode) editor along with the ['Trunk Check' extension](https://marketplace.visualstudio.com/items?itemName=trunk.io) (WSL2 is required on windows),
|
||||
because it automatically follows our indentation rules and its auto reformatting will not cause spurious changes to lines.
|
||||
- If your PR fixes a bug, mention "fixes #bugnum" somewhere in your pull request description.
|
||||
- If your other co-developers have comments on your PR please tweak as needed.
|
||||
- Please also enable "Allow edits by maintainers".
|
||||
|
||||
16
.github/workflows/main_matrix.yml
vendored
16
.github/workflows/main_matrix.yml
vendored
@@ -33,6 +33,8 @@ jobs:
|
||||
- board: m5stack-coreink
|
||||
- board: tbeam-s3-core
|
||||
- board: tlora-t3s3-v1
|
||||
- board: t-watch-s3
|
||||
- board: t-deck
|
||||
#- board: rak11310
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
@@ -66,6 +68,7 @@ jobs:
|
||||
- board: heltec-v2_1
|
||||
- board: tbeam0_7
|
||||
- board: meshtastic-diy-v1
|
||||
- board: hydra
|
||||
- board: meshtastic-dr-dev
|
||||
- board: nano-g1
|
||||
- board: station-g1
|
||||
@@ -83,8 +86,12 @@ jobs:
|
||||
include:
|
||||
- board: heltec-v3
|
||||
- board: heltec-wsl-v3
|
||||
- board: heltec-wireless-tracker
|
||||
- board: heltec-wireless-paper
|
||||
- board: tbeam-s3-core
|
||||
- board: tlora-t3s3-v1
|
||||
- board: t-watch-s3
|
||||
- board: t-deck
|
||||
uses: ./.github/workflows/build_esp32_s3.yml
|
||||
with:
|
||||
board: ${{ matrix.board }}
|
||||
@@ -97,6 +104,7 @@ jobs:
|
||||
include:
|
||||
- board: rak4631
|
||||
- board: rak4631_eink
|
||||
- board: monteops_hw1
|
||||
- board: t-echo
|
||||
- board: pca10059_diy_eink
|
||||
- board: feather_diy
|
||||
@@ -150,12 +158,14 @@ jobs:
|
||||
release/device-*.bat
|
||||
|
||||
- name: Docker login
|
||||
if: ${{ github.event_name != 'pull_request_target' && github.event_name != 'pull_request' }}
|
||||
uses: docker/login-action@v2
|
||||
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@v2
|
||||
|
||||
- name: Docker build and push tagged versions
|
||||
@@ -168,7 +178,7 @@ jobs:
|
||||
tags: meshtastic/device-simulator:${{ steps.version.outputs.version }}
|
||||
|
||||
- name: Docker build and push
|
||||
if: github.ref == 'refs/heads/master'
|
||||
if: ${{ github.ref == 'refs/heads/master' && github.event_name != 'pull_request_target' && github.event_name != 'pull_request' }}
|
||||
uses: docker/build-push-action@v3
|
||||
with:
|
||||
context: .
|
||||
@@ -246,7 +256,7 @@ jobs:
|
||||
|
||||
- name: Create request artifacts
|
||||
if: ${{ github.event_name == 'pull_request_target' || github.event_name == 'pull_request' }}
|
||||
uses: gavv/pull-request-artifacts@v1.0.0
|
||||
uses: gavv/pull-request-artifacts@v1.1.0
|
||||
with:
|
||||
commit: ${{ (github.event.pull_request_target || github.event.pull_request).head.sha }}
|
||||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
@@ -304,7 +314,7 @@ jobs:
|
||||
with:
|
||||
draft: true
|
||||
prerelease: true
|
||||
release_name: Meshtastic Firmware ${{ steps.version.outputs.version }}
|
||||
release_name: Meshtastic Firmware ${{ steps.version.outputs.version }} Alpha
|
||||
tag_name: v${{ steps.version.outputs.version }}
|
||||
body: |
|
||||
Autogenerated by github action, developer should edit as required before publishing...
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
; Common settings for ESP targes, mixin with extends = esp32_base
|
||||
[esp32_base]
|
||||
extends = arduino_base
|
||||
platform = platformio/espressif32@^6.2.0
|
||||
platform = platformio/espressif32@^6.3.2
|
||||
|
||||
build_src_filter =
|
||||
${arduino_base.build_src_filter} -<platform/nrf52/> -<platform/stm32wl> -<platform/rp2040> -<mesh/eth/>
|
||||
@@ -28,6 +28,7 @@ build_flags =
|
||||
-DCONFIG_BT_NIMBLE_ENABLED
|
||||
-DCONFIG_NIMBLE_CPP_LOG_LEVEL=2
|
||||
-DCONFIG_BT_NIMBLE_MAX_CCCDS=20
|
||||
-DCONFIG_BT_NIMBLE_HOST_TASK_STACK_SIZE=5120
|
||||
-DESP_OPENSSL_SUPPRESS_LEGACY_WARNING
|
||||
;-DDEBUG_HEAP
|
||||
|
||||
@@ -37,7 +38,7 @@ lib_deps =
|
||||
${environmental_base.lib_deps}
|
||||
https://github.com/meshtastic/esp32_https_server.git#23665b3adc080a311dcbb586ed5941b5f94d6ea2
|
||||
h2zero/NimBLE-Arduino@^1.4.0
|
||||
jgromes/RadioLib@^6.0.0
|
||||
jgromes/RadioLib@^6.1.0
|
||||
https://github.com/lewisxhe/XPowersLib.git#84b7373faea3118b6c37954d52f98b8a337148d6
|
||||
https://github.com/meshtastic/ESP32_Codec2.git#633326c78ac251c059ab3a8c430fcdf25b41672f
|
||||
|
||||
@@ -55,4 +56,4 @@ lib_ignore =
|
||||
|
||||
; customize the partition table
|
||||
; http://docs.platformio.org/en/latest/platforms/espressif32.html#partition-tables
|
||||
board_build.partitions = partition-table.csv
|
||||
board_build.partitions = partition-table.csv
|
||||
@@ -1,6 +1,6 @@
|
||||
[nrf52_base]
|
||||
; Instead of the standard nordicnrf52 platform, we use our fork which has our added variant files
|
||||
platform = platformio/nordicnrf52@^9.6.0
|
||||
platform = platformio/nordicnrf52@^10.0.0
|
||||
extends = arduino_base
|
||||
|
||||
build_type = debug ; I'm debugging with ICE a lot now
|
||||
@@ -9,12 +9,11 @@ build_flags =
|
||||
-Isrc/platform/nrf52
|
||||
|
||||
build_src_filter =
|
||||
${arduino_base.build_src_filter} -<platform/esp32/> -<platform/stm32wl> -<nimble/> -<mesh/api/> -<mesh/http/> -<modules/esp32> -<mqtt/> -<platform/rp2040> -<mesh/eth/>
|
||||
${arduino_base.build_src_filter} -<platform/esp32/> -<platform/stm32wl> -<nimble/> -<mesh/api/> -<mesh/http/> -<modules/esp32> -<platform/rp2040> -<mesh/eth/>
|
||||
|
||||
lib_deps=
|
||||
${arduino_base.lib_deps}
|
||||
jgromes/RadioLib@^6.0.0
|
||||
jgromes/RadioLib@^6.1.0
|
||||
|
||||
lib_ignore =
|
||||
BluetoothOTA
|
||||
|
||||
BluetoothOTA
|
||||
@@ -22,7 +22,7 @@ lib_deps =
|
||||
${env.lib_deps}
|
||||
${networking_base.lib_deps}
|
||||
rweather/Crypto@^0.4.0
|
||||
jgromes/RadioLib@^6.0.0
|
||||
jgromes/RadioLib@6.1.0
|
||||
|
||||
build_flags =
|
||||
${arduino_base.build_flags}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
[rp2040_base]
|
||||
platform = https://github.com/maxgerhardt/platform-raspberrypi.git#0c33219f53faa035e188925ea1324f472e8b93d2
|
||||
extends = arduino_base
|
||||
platform_packages = framework-arduinopico@https://github.com/earlephilhower/arduino-pico.git#3.2.1
|
||||
platform_packages = framework-arduinopico@https://github.com/earlephilhower/arduino-pico.git#3.2.2
|
||||
|
||||
board_build.core = earlephilhower
|
||||
board_build.filesystem_size = 0.5m
|
||||
@@ -12,7 +12,7 @@ build_flags =
|
||||
-D__PLAT_RP2040__
|
||||
# -D _POSIX_THREADS
|
||||
build_src_filter =
|
||||
${arduino_base.build_src_filter} -<platform/esp32/> -<nimble/> -<mesh/api/> -<mesh/http/> -<modules/esp32> -<mqtt/> -<platform/nrf52/> -<platform/stm32wl> -<mesh/eth/>
|
||||
${arduino_base.build_src_filter} -<platform/esp32/> -<nimble/> -<mesh/api/> -<mesh/http/> -<modules/esp32> -<platform/nrf52/> -<platform/stm32wl> -<mesh/eth/>
|
||||
|
||||
lib_ignore =
|
||||
BluetoothOTA
|
||||
@@ -20,5 +20,5 @@ lib_ignore =
|
||||
lib_deps =
|
||||
${arduino_base.lib_deps}
|
||||
${environmental_base.lib_deps}
|
||||
jgromes/RadioLib@^6.0.0
|
||||
jgromes/RadioLib@^6.1.0
|
||||
https://github.com/kokke/tiny-AES-c.git#f06ac37fc31dfdaca2e0d9bec83f90d5663c319b
|
||||
@@ -13,17 +13,17 @@ build_flags =
|
||||
-DVECT_TAB_OFFSET=0x08000000
|
||||
|
||||
build_src_filter =
|
||||
${arduino_base.build_src_filter} -<platform/esp32/> -<nimble/> -<mesh/api/> -<mesh/http/> -<modules/esp32> -<mesh/eth/> -<mqtt/> -<input> -<buzz> -<modules/Telemetry> -<platform/nrf52> -<platform/portduino> -<platform/rp2040>
|
||||
${arduino_base.build_src_filter} -<platform/esp32/> -<nimble/> -<mesh/api/> -<mesh/http/> -<modules/esp32> -<mesh/eth/> -<input> -<buzz> -<modules/Telemetry> -<platform/nrf52> -<platform/portduino> -<platform/rp2040>
|
||||
|
||||
board_upload.offset_address = 0x08000000
|
||||
upload_protocol = stlink
|
||||
|
||||
lib_deps =
|
||||
${env.lib_deps}
|
||||
jgromes/RadioLib@^6.0.0
|
||||
jgromes/RadioLib@^6.1.0
|
||||
https://github.com/kokke/tiny-AES-c.git#f06ac37fc31dfdaca2e0d9bec83f90d5663c319b
|
||||
https://github.com/littlefs-project/littlefs.git#v2.5.1
|
||||
https://github.com/stm32duino/STM32FreeRTOS.git#10.3.1
|
||||
|
||||
lib_ignore =
|
||||
https://github.com/mathertel/OneButton#2.1.0
|
||||
https://github.com/mathertel/OneButton#2.1.0
|
||||
38
boards/heltec_wireless_tracker.json
Normal file
38
boards/heltec_wireless_tracker.json
Normal file
@@ -0,0 +1,38 @@
|
||||
{
|
||||
"build": {
|
||||
"arduino": {
|
||||
"ldscript": "esp32s3_out.ld",
|
||||
"partitions": "default_8MB.csv"
|
||||
},
|
||||
"core": "esp32",
|
||||
"extra_flags": [
|
||||
"-DHELTEC_WIRELESS_TRACKER",
|
||||
"-DARDUINO_USB_CDC_ON_BOOT=1",
|
||||
"-DARDUINO_USB_MODE=0",
|
||||
"-DARDUINO_RUNNING_CORE=1",
|
||||
"-DARDUINO_EVENT_RUNNING_CORE=1"
|
||||
],
|
||||
"f_cpu": "240000000L",
|
||||
"f_flash": "80000000L",
|
||||
"flash_mode": "qio",
|
||||
"hwids": [["0x303A", "0x1001"]],
|
||||
"mcu": "esp32s3",
|
||||
"variant": "heltec_wireless_tracker"
|
||||
},
|
||||
"connectivity": ["wifi", "bluetooth", "lora"],
|
||||
"debug": {
|
||||
"openocd_target": "esp32s3.cfg"
|
||||
},
|
||||
"frameworks": ["arduino", "espidf"],
|
||||
"name": "Heltec Wireless Tracker",
|
||||
"upload": {
|
||||
"flash_size": "8MB",
|
||||
"maximum_ram_size": 327680,
|
||||
"maximum_size": 8388608,
|
||||
"wait_for_upload_port": true,
|
||||
"require_upload_port": true,
|
||||
"speed": 921600
|
||||
},
|
||||
"url": "https://heltec.org/project/wireless-tracker/",
|
||||
"vendor": "Heltec"
|
||||
}
|
||||
40
boards/t-deck.json
Normal file
40
boards/t-deck.json
Normal file
@@ -0,0 +1,40 @@
|
||||
{
|
||||
"build": {
|
||||
"arduino": {
|
||||
"ldscript": "esp32s3_out.ld"
|
||||
},
|
||||
"core": "esp32",
|
||||
"extra_flags": [
|
||||
"-DBOARD_HAS_PSRAM",
|
||||
"-DARDUINO_USB_CDC_ON_BOOT=1",
|
||||
"-DARDUINO_USB_MODE=0",
|
||||
"-DARDUINO_RUNNING_CORE=1",
|
||||
"-DARDUINO_EVENT_RUNNING_CORE=0"
|
||||
],
|
||||
"f_cpu": "240000000L",
|
||||
"f_flash": "80000000L",
|
||||
"flash_mode": "dio",
|
||||
"hwids": [["0x303A", "0x1001"]],
|
||||
"mcu": "esp32s3",
|
||||
"variant": "t-deck"
|
||||
},
|
||||
"connectivity": ["wifi", "bluetooth", "lora"],
|
||||
"debug": {
|
||||
"default_tool": "esp-builtin",
|
||||
"onboard_tools": ["esp-builtin"],
|
||||
"openocd_target": "esp32s3.cfg"
|
||||
},
|
||||
"frameworks": ["arduino", "espidf"],
|
||||
"name": "Espressif Systems LilyGO T-Deck (16 MB FLASH, 8 MB PSRAM)",
|
||||
"upload": {
|
||||
"flash_size": "16MB",
|
||||
"maximum_ram_size": 327680,
|
||||
"maximum_size": 16777216,
|
||||
"use_1200bps_touch": true,
|
||||
"wait_for_upload_port": true,
|
||||
"require_upload_port": true,
|
||||
"speed": 921600
|
||||
},
|
||||
"url": "https://www.lilygo.cc/en-pl/products/t-deck",
|
||||
"vendor": "LilyGO"
|
||||
}
|
||||
@@ -7,7 +7,10 @@
|
||||
"cpu": "cortex-m4",
|
||||
"extra_flags": "-DARDUINO_NRF52840_TTGO_EINK -DNRF52840_XXAA",
|
||||
"f_cpu": "64000000L",
|
||||
"hwids": [["0x239A", "0x4405"]],
|
||||
"hwids": [
|
||||
["0x239A", "0x4405"],
|
||||
["0x239A", "0x002A"]
|
||||
],
|
||||
"usb_product": "TTGO_eink",
|
||||
"mcu": "nrf52840",
|
||||
"variant": "t-echo",
|
||||
|
||||
38
boards/t-watch-s3.json
Normal file
38
boards/t-watch-s3.json
Normal file
@@ -0,0 +1,38 @@
|
||||
{
|
||||
"build": {
|
||||
"arduino": {
|
||||
"ldscript": "esp32s3_out.ld"
|
||||
},
|
||||
"core": "esp32",
|
||||
"extra_flags": [
|
||||
"-DBOARD_HAS_PSRAM",
|
||||
"-DT_WATCH_S3",
|
||||
"-DARDUINO_USB_CDC_ON_BOOT=1",
|
||||
"-DARDUINO_USB_MODE=0",
|
||||
"-DARDUINO_RUNNING_CORE=1",
|
||||
"-DARDUINO_EVENT_RUNNING_CORE=1"
|
||||
],
|
||||
"f_cpu": "240000000L",
|
||||
"f_flash": "80000000L",
|
||||
"flash_mode": "dio",
|
||||
"hwids": [["0X303A", "0x1001"]],
|
||||
"mcu": "esp32s3",
|
||||
"variant": "t-watch-s3"
|
||||
},
|
||||
"connectivity": ["wifi"],
|
||||
"debug": {
|
||||
"openocd_target": "esp32s3.cfg"
|
||||
},
|
||||
"frameworks": ["arduino"],
|
||||
"name": "LilyGo T-Watch 2020 V3",
|
||||
"upload": {
|
||||
"flash_size": "8MB",
|
||||
"maximum_ram_size": 327680,
|
||||
"maximum_size": 8388608,
|
||||
"require_upload_port": true,
|
||||
"use_1200bps_touch": true,
|
||||
"wait_for_upload_port": true
|
||||
},
|
||||
"url": "http://www.lilygo.cn/",
|
||||
"vendor": "LilyGo"
|
||||
}
|
||||
57
boards/xiao_ble_sense.json
Normal file
57
boards/xiao_ble_sense.json
Normal file
@@ -0,0 +1,57 @@
|
||||
{
|
||||
"build": {
|
||||
"arduino": {
|
||||
"ldscript": "nrf52840_s140_v7.ld"
|
||||
},
|
||||
"core": "nRF5",
|
||||
"cpu": "cortex-m4",
|
||||
"extra_flags": "-DARDUINO_MDBT50Q_RX -DNRF52840_XXAA",
|
||||
"f_cpu": "64000000L",
|
||||
"hwids": [
|
||||
["0x239A", "0x810B"],
|
||||
["0x239A", "0x010B"],
|
||||
["0x239A", "0x810C"]
|
||||
],
|
||||
"usb_product": "XIAO-BOOT",
|
||||
"mcu": "nrf52840",
|
||||
"variant": "Seeed_XIAO_nRF52840_Sense",
|
||||
"bsp": {
|
||||
"name": "adafruit"
|
||||
},
|
||||
"softdevice": {
|
||||
"sd_flags": "-DS140",
|
||||
"sd_name": "s140",
|
||||
"sd_version": "7.3.0",
|
||||
"sd_fwid": "0x0123"
|
||||
},
|
||||
"bootloader": {
|
||||
"settings_addr": "0xFF000"
|
||||
}
|
||||
},
|
||||
"connectivity": ["bluetooth"],
|
||||
"debug": {
|
||||
"jlink_device": "nRF52840_xxAA",
|
||||
"svd_path": "nrf52840.svd"
|
||||
},
|
||||
"frameworks": ["arduino"],
|
||||
"name": "Seeed Xiao BLE Sense",
|
||||
"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/Seeed-XIAO-BLE-Sense-nRF52840-p-5253.html",
|
||||
"vendor": "Seeed Studio"
|
||||
}
|
||||
@@ -9,6 +9,7 @@
|
||||
;default_envs = heltec-v1
|
||||
;default_envs = heltec-v2_0
|
||||
;default_envs = heltec-v2_1
|
||||
;default_envs = heltec-wireless-tracker
|
||||
;default_envs = tlora-v1
|
||||
;default_envs = tlora_v1_3
|
||||
;default_envs = tlora-v2
|
||||
@@ -56,6 +57,12 @@ build_flags = -Wno-missing-field-initializers
|
||||
-DRADIOLIB_EXCLUDE_MORSE
|
||||
-DRADIOLIB_EXCLUDE_RTTY
|
||||
-DRADIOLIB_EXCLUDE_SSTV
|
||||
-DRADIOLIB_EXCLUDE_AX25
|
||||
-DRADIOLIB_EXCLUDE_DIRECT_RECEIVE
|
||||
-DRADIOLIB_EXCLUDE_BELL
|
||||
-DRADIOLIB_EXCLUDE_PAGER
|
||||
-DRADIOLIB_EXCLUDE_FSK4
|
||||
-DRADIOLIB_EXCLUDE_APRS
|
||||
|
||||
monitor_speed = 115200
|
||||
|
||||
@@ -113,4 +120,5 @@ lib_deps =
|
||||
adafruit/Adafruit SHT31 Library@^2.2.0
|
||||
adafruit/Adafruit PM25 AQI Sensor@^1.0.6
|
||||
adafruit/Adafruit MPU6050@^2.2.4
|
||||
adafruit/Adafruit LIS3DH@^1.2.4
|
||||
adafruit/Adafruit LIS3DH@^1.2.4
|
||||
https://github.com/lewisxhe/BMA423_Library@^0.0.1
|
||||
Submodule protobufs updated: 8d17bb2452...f17298c2b0
@@ -6,10 +6,37 @@
|
||||
|
||||
#include <Adafruit_LIS3DH.h>
|
||||
#include <Adafruit_MPU6050.h>
|
||||
#include <Arduino.h>
|
||||
#include <Wire.h>
|
||||
#include <bma.h>
|
||||
|
||||
BMA423 bmaSensor;
|
||||
bool BMA_IRQ = false;
|
||||
|
||||
#define ACCELEROMETER_CHECK_INTERVAL_MS 100
|
||||
#define ACCELEROMETER_CLICK_THRESHOLD 40
|
||||
|
||||
uint16_t readRegister(uint8_t address, uint8_t reg, uint8_t *data, uint16_t len)
|
||||
{
|
||||
Wire.beginTransmission(address);
|
||||
Wire.write(reg);
|
||||
Wire.endTransmission();
|
||||
Wire.requestFrom((uint8_t)address, (uint8_t)len);
|
||||
uint8_t i = 0;
|
||||
while (Wire.available()) {
|
||||
data[i++] = Wire.read();
|
||||
}
|
||||
return 0; // Pass
|
||||
}
|
||||
|
||||
uint16_t writeRegister(uint8_t address, uint8_t reg, uint8_t *data, uint16_t len)
|
||||
{
|
||||
Wire.beginTransmission(address);
|
||||
Wire.write(reg);
|
||||
Wire.write(data, len);
|
||||
return (0 != Wire.endTransmission());
|
||||
}
|
||||
|
||||
namespace concurrency
|
||||
{
|
||||
class AccelerometerThread : public concurrency::OSThread
|
||||
@@ -29,10 +56,10 @@ class AccelerometerThread : public concurrency::OSThread
|
||||
return;
|
||||
}
|
||||
|
||||
accleremoter_type = type;
|
||||
acceleremoter_type = type;
|
||||
LOG_DEBUG("AccelerometerThread initializing\n");
|
||||
|
||||
if (accleremoter_type == ScanI2C::DeviceType::MPU6050 && mpu.begin(accelerometer_found.address)) {
|
||||
if (acceleremoter_type == ScanI2C::DeviceType::MPU6050 && mpu.begin(accelerometer_found.address)) {
|
||||
LOG_DEBUG("MPU6050 initializing\n");
|
||||
// setup motion detection
|
||||
mpu.setHighPassFilter(MPU6050_HIGHPASS_0_63_HZ);
|
||||
@@ -40,11 +67,60 @@ class AccelerometerThread : public concurrency::OSThread
|
||||
mpu.setMotionDetectionDuration(20);
|
||||
mpu.setInterruptPinLatch(true); // Keep it latched. Will turn off when reinitialized.
|
||||
mpu.setInterruptPinPolarity(true);
|
||||
} else if (accleremoter_type == ScanI2C::DeviceType::LIS3DH && lis.begin(accelerometer_found.address)) {
|
||||
} else if (acceleremoter_type == ScanI2C::DeviceType::LIS3DH && lis.begin(accelerometer_found.address)) {
|
||||
LOG_DEBUG("LIS3DH initializing\n");
|
||||
lis.setRange(LIS3DH_RANGE_2_G);
|
||||
// Adjust threshhold, higher numbers are less sensitive
|
||||
// Adjust threshold, higher numbers are less sensitive
|
||||
lis.setClick(config.device.double_tap_as_button_press ? 2 : 1, ACCELEROMETER_CLICK_THRESHOLD);
|
||||
} else if (acceleremoter_type == ScanI2C::DeviceType::BMA423 && bmaSensor.begin(readRegister, writeRegister, delay)) {
|
||||
LOG_DEBUG("BMA423 initializing\n");
|
||||
Acfg cfg;
|
||||
cfg.odr = BMA4_OUTPUT_DATA_RATE_100HZ;
|
||||
cfg.range = BMA4_ACCEL_RANGE_2G;
|
||||
cfg.bandwidth = BMA4_ACCEL_NORMAL_AVG4;
|
||||
cfg.perf_mode = BMA4_CONTINUOUS_MODE;
|
||||
bmaSensor.setAccelConfig(cfg);
|
||||
bmaSensor.enableAccel();
|
||||
|
||||
struct bma4_int_pin_config pin_config;
|
||||
pin_config.edge_ctrl = BMA4_LEVEL_TRIGGER;
|
||||
pin_config.lvl = BMA4_ACTIVE_HIGH;
|
||||
pin_config.od = BMA4_PUSH_PULL;
|
||||
pin_config.output_en = BMA4_OUTPUT_ENABLE;
|
||||
pin_config.input_en = BMA4_INPUT_DISABLE;
|
||||
// The correct trigger interrupt needs to be configured as needed
|
||||
bmaSensor.setINTPinConfig(pin_config, BMA4_INTR1_MAP);
|
||||
|
||||
#ifdef BMA423_INT
|
||||
pinMode(BMA4XX_INT, INPUT);
|
||||
attachInterrupt(
|
||||
BMA4XX_INT,
|
||||
[] {
|
||||
// Set interrupt to set irq value to true
|
||||
BMA_IRQ = true;
|
||||
},
|
||||
RISING); // Select the interrupt mode according to the actual circuit
|
||||
#endif
|
||||
|
||||
struct bma423_axes_remap remap_data;
|
||||
remap_data.x_axis = 0;
|
||||
remap_data.x_axis_sign = 1;
|
||||
remap_data.y_axis = 1;
|
||||
remap_data.y_axis_sign = 0;
|
||||
remap_data.z_axis = 2;
|
||||
remap_data.z_axis_sign = 1;
|
||||
// Need to raise the wrist function, need to set the correct axis
|
||||
bmaSensor.setRemapAxes(&remap_data);
|
||||
// sensor.enableFeature(BMA423_STEP_CNTR, true);
|
||||
bmaSensor.enableFeature(BMA423_TILT, true);
|
||||
bmaSensor.enableFeature(BMA423_WAKEUP, true);
|
||||
// sensor.resetStepCounter();
|
||||
|
||||
// Turn on feature interrupt
|
||||
bmaSensor.enableStepCountInterrupt();
|
||||
bmaSensor.enableTiltInterrupt();
|
||||
// It corresponds to isDoubleClick interrupt
|
||||
bmaSensor.enableWakeupInterrupt();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -53,9 +129,9 @@ class AccelerometerThread : public concurrency::OSThread
|
||||
{
|
||||
canSleep = true; // Assume we should not keep the board awake
|
||||
|
||||
if (accleremoter_type == ScanI2C::DeviceType::MPU6050 && mpu.getMotionInterruptStatus()) {
|
||||
if (acceleremoter_type == ScanI2C::DeviceType::MPU6050 && mpu.getMotionInterruptStatus()) {
|
||||
wakeScreen();
|
||||
} else if (accleremoter_type == ScanI2C::DeviceType::LIS3DH && lis.getClick() > 0) {
|
||||
} else if (acceleremoter_type == ScanI2C::DeviceType::LIS3DH && lis.getClick() > 0) {
|
||||
uint8_t click = lis.getClick();
|
||||
if (!config.device.double_tap_as_button_press) {
|
||||
wakeScreen();
|
||||
@@ -65,7 +141,13 @@ class AccelerometerThread : public concurrency::OSThread
|
||||
buttonPress();
|
||||
return 500;
|
||||
}
|
||||
} else if (acceleremoter_type == ScanI2C::DeviceType::BMA423 && bmaSensor.getINT()) {
|
||||
if (bmaSensor.isTilt() || bmaSensor.isDoubleClick()) {
|
||||
wakeScreen();
|
||||
return 500;
|
||||
}
|
||||
}
|
||||
|
||||
return ACCELEROMETER_CHECK_INTERVAL_MS;
|
||||
}
|
||||
|
||||
@@ -84,9 +166,9 @@ class AccelerometerThread : public concurrency::OSThread
|
||||
powerFSM.trigger(EVENT_PRESS);
|
||||
}
|
||||
|
||||
ScanI2C::DeviceType accleremoter_type;
|
||||
ScanI2C::DeviceType acceleremoter_type;
|
||||
Adafruit_MPU6050 mpu;
|
||||
Adafruit_LIS3DH lis;
|
||||
};
|
||||
|
||||
} // namespace concurrency
|
||||
} // namespace concurrency
|
||||
@@ -4,6 +4,7 @@
|
||||
#include "concurrency/OSThread.h"
|
||||
#include "configuration.h"
|
||||
#include "graphics/Screen.h"
|
||||
#include "main.h"
|
||||
#include "power.h"
|
||||
#include <OneButton.h>
|
||||
|
||||
@@ -98,8 +99,23 @@ class ButtonThread : public concurrency::OSThread
|
||||
userButtonTouch.tick();
|
||||
canSleep &= userButtonTouch.isIdle();
|
||||
#endif
|
||||
// if (!canSleep) LOG_DEBUG("Supressing sleep!\n");
|
||||
// if (!canSleep) LOG_DEBUG("Suppressing sleep!\n");
|
||||
// else LOG_DEBUG("sleep ok\n");
|
||||
#if defined(ST7735_CS) || defined(ILI9341_DRIVER) || defined(ST7789_CS)
|
||||
int x, y = 0;
|
||||
screen->getTouch(&x, &y);
|
||||
if (x > 0 && y > 0) {
|
||||
#ifdef T_WATCH_S3
|
||||
drv.setWaveform(0, 75);
|
||||
drv.setWaveform(1, 0); // end waveform
|
||||
drv.go();
|
||||
#endif
|
||||
LOG_DEBUG("touch %d %d\n", x, y);
|
||||
powerFSM.trigger(EVENT_PRESS);
|
||||
return 150; // Check for next touch every in 150ms
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
return 5;
|
||||
}
|
||||
@@ -157,7 +173,7 @@ class ButtonThread : public concurrency::OSThread
|
||||
digitalWrite(PIN_EINK_EN, digitalRead(PIN_EINK_EN) == LOW);
|
||||
#endif
|
||||
screen->print("Sent ad-hoc ping\n");
|
||||
service.refreshMyNodeInfo();
|
||||
service.refreshLocalMeshNode();
|
||||
service.sendNetworkPing(NODENUM_BROADCAST, true);
|
||||
}
|
||||
|
||||
@@ -196,4 +212,4 @@ class ButtonThread : public concurrency::OSThread
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace concurrency
|
||||
} // namespace concurrency
|
||||
@@ -8,6 +8,8 @@
|
||||
#ifdef SDCARD_USE_SPI1
|
||||
SPIClass SPI1(HSPI);
|
||||
#define SDHandler SPI1
|
||||
#else
|
||||
#define SDHandler SPI
|
||||
#endif
|
||||
|
||||
#endif // HAS_SDCARD
|
||||
|
||||
@@ -55,7 +55,7 @@ class GPSStatus : public Status
|
||||
#ifdef GPS_EXTRAVERBOSE
|
||||
LOG_WARN("Using fixed latitude\n");
|
||||
#endif
|
||||
meshtastic_NodeInfo *node = nodeDB.getNode(nodeDB.getNodeNum());
|
||||
meshtastic_NodeInfoLite *node = nodeDB.getMeshNode(nodeDB.getNodeNum());
|
||||
return node->position.latitude_i;
|
||||
} else {
|
||||
return p.latitude_i;
|
||||
@@ -68,7 +68,7 @@ class GPSStatus : public Status
|
||||
#ifdef GPS_EXTRAVERBOSE
|
||||
LOG_WARN("Using fixed longitude\n");
|
||||
#endif
|
||||
meshtastic_NodeInfo *node = nodeDB.getNode(nodeDB.getNodeNum());
|
||||
meshtastic_NodeInfoLite *node = nodeDB.getMeshNode(nodeDB.getNodeNum());
|
||||
return node->position.longitude_i;
|
||||
} else {
|
||||
return p.longitude_i;
|
||||
@@ -81,7 +81,7 @@ class GPSStatus : public Status
|
||||
#ifdef GPS_EXTRAVERBOSE
|
||||
LOG_WARN("Using fixed altitude\n");
|
||||
#endif
|
||||
meshtastic_NodeInfo *node = nodeDB.getNode(nodeDB.getNodeNum());
|
||||
meshtastic_NodeInfoLite *node = nodeDB.getMeshNode(nodeDB.getNodeNum());
|
||||
return node->position.altitude;
|
||||
} else {
|
||||
return p.altitude;
|
||||
@@ -106,7 +106,7 @@ class GPSStatus : public Status
|
||||
bool matches(const GPSStatus *newStatus) const
|
||||
{
|
||||
#ifdef GPS_EXTRAVERBOSE
|
||||
LOG_DEBUG("GPSStatus.match() new pos@%x to old pos@%x\n", newStatus->p.pos_timestamp, p.pos_timestamp);
|
||||
LOG_DEBUG("GPSStatus.match() new pos@%x to old pos@%x\n", newStatus->p.timestamp, p.timestamp);
|
||||
#endif
|
||||
return (newStatus->hasLock != hasLock || newStatus->isConnected != isConnected ||
|
||||
newStatus->isPowerSaving != isPowerSaving || newStatus->p.latitude_i != p.latitude_i ||
|
||||
@@ -149,4 +149,4 @@ class GPSStatus : public Status
|
||||
|
||||
} // namespace meshtastic
|
||||
|
||||
extern meshtastic::GPSStatus *gpsStatus;
|
||||
extern meshtastic::GPSStatus *gpsStatus;
|
||||
@@ -5,7 +5,8 @@
|
||||
* Schedule a callback to run. The callback must _not_ block, though it is called from regular thread level (not ISR)
|
||||
*
|
||||
* NOTE! xTimerPend... seems to ignore the time passed in on ESP32 and on NRF52
|
||||
* The reason this didn't work is bcause xTimerPednFunctCall really isn't a timer function at all - it just means run the callback
|
||||
* The reason this didn't work is because xTimerPednFunctCall really isn't a timer function at all - it just means run the
|
||||
callback
|
||||
* from the timer thread the next time you have spare cycles.
|
||||
*
|
||||
* @return true if successful, false if the timer fifo is too full.
|
||||
|
||||
@@ -50,7 +50,7 @@ XPowersLibInterface *PMU = NULL;
|
||||
#else
|
||||
|
||||
// Copy of the base class defined in axp20x.h.
|
||||
// I'd rather not inlude axp20x.h as it brings Wire dependency.
|
||||
// I'd rather not include axp20x.h as it brings Wire dependency.
|
||||
class HasBatteryLevel
|
||||
{
|
||||
public:
|
||||
@@ -540,10 +540,12 @@ int32_t Power::runOnce()
|
||||
LOG_DEBUG("Battery removed\n");
|
||||
}
|
||||
*/
|
||||
#ifndef T_WATCH_S3 // FIXME - why is this triggering on the T-Watch S3?
|
||||
if (PMU->isPekeyLongPressIrq()) {
|
||||
LOG_DEBUG("PEK long button press\n");
|
||||
screen->setOn(false);
|
||||
}
|
||||
#endif
|
||||
|
||||
PMU->clearIrqStatus();
|
||||
}
|
||||
@@ -681,7 +683,8 @@ bool Power::axpChipInit()
|
||||
// GNSS VDD 3300mV
|
||||
PMU->setPowerChannelVoltage(XPOWERS_ALDO3, 3300);
|
||||
PMU->enablePowerOutput(XPOWERS_ALDO3);
|
||||
} else if (HW_VENDOR == meshtastic_HardwareModel_LILYGO_TBEAM_S3_CORE) {
|
||||
} else if (HW_VENDOR == meshtastic_HardwareModel_LILYGO_TBEAM_S3_CORE ||
|
||||
HW_VENDOR == meshtastic_HardwareModel_T_WATCH_S3) {
|
||||
// t-beam s3 core
|
||||
/**
|
||||
* gnss module power channel
|
||||
@@ -712,10 +715,16 @@ bool Power::axpChipInit()
|
||||
PMU->setPowerChannelVoltage(XPOWERS_ALDO1, 3300);
|
||||
PMU->enablePowerOutput(XPOWERS_ALDO1);
|
||||
|
||||
// sdcard power channle
|
||||
// sdcard power channel
|
||||
PMU->setPowerChannelVoltage(XPOWERS_BLDO1, 3300);
|
||||
PMU->enablePowerOutput(XPOWERS_BLDO1);
|
||||
|
||||
#ifdef T_WATCH_S3
|
||||
// DRV2605 power channel
|
||||
PMU->setPowerChannelVoltage(XPOWERS_BLDO2, 3300);
|
||||
PMU->enablePowerOutput(XPOWERS_BLDO2);
|
||||
#endif
|
||||
|
||||
// PMU->setPowerChannelVoltage(XPOWERS_DCDC4, 3300);
|
||||
// PMU->enablePowerOutput(XPOWERS_DCDC4);
|
||||
|
||||
|
||||
@@ -352,5 +352,5 @@ void PowerFSM_setup()
|
||||
"mesh timeout");
|
||||
#endif
|
||||
|
||||
powerFSM.run_machine(); // run one interation of the state machine, so we run our on enter tasks for the initial DARK state
|
||||
powerFSM.run_machine(); // run one iteration of the state machine, so we run our on enter tasks for the initial DARK state
|
||||
}
|
||||
@@ -6,20 +6,22 @@ AirTime *airTime = NULL;
|
||||
|
||||
// Don't read out of this directly. Use the helper functions.
|
||||
|
||||
uint32_t air_period_tx[PERIODS_TO_LOG];
|
||||
uint32_t air_period_rx[PERIODS_TO_LOG];
|
||||
|
||||
void AirTime::logAirtime(reportTypes reportType, uint32_t airtime_ms)
|
||||
{
|
||||
|
||||
if (reportType == TX_LOG) {
|
||||
LOG_DEBUG("AirTime - Packet transmitted : %ums\n", airtime_ms);
|
||||
this->airtimes.periodTX[0] = this->airtimes.periodTX[0] + airtime_ms;
|
||||
myNodeInfo.air_period_tx[0] = myNodeInfo.air_period_tx[0] + airtime_ms;
|
||||
air_period_tx[0] = air_period_tx[0] + airtime_ms;
|
||||
|
||||
this->utilizationTX[this->getPeriodUtilHour()] = this->utilizationTX[this->getPeriodUtilHour()] + airtime_ms;
|
||||
|
||||
} else if (reportType == RX_LOG) {
|
||||
LOG_DEBUG("AirTime - Packet received : %ums\n", airtime_ms);
|
||||
this->airtimes.periodRX[0] = this->airtimes.periodRX[0] + airtime_ms;
|
||||
myNodeInfo.air_period_rx[0] = myNodeInfo.air_period_rx[0] + airtime_ms;
|
||||
air_period_rx[0] = air_period_rx[0] + airtime_ms;
|
||||
} else if (reportType == RX_ALL_LOG) {
|
||||
LOG_DEBUG("AirTime - Packet received (noise?) : %ums\n", airtime_ms);
|
||||
this->airtimes.periodRX_ALL[0] = this->airtimes.periodRX_ALL[0] + airtime_ms;
|
||||
@@ -55,16 +57,16 @@ void AirTime::airtimeRotatePeriod()
|
||||
this->airtimes.periodRX[i + 1] = this->airtimes.periodRX[i];
|
||||
this->airtimes.periodRX_ALL[i + 1] = this->airtimes.periodRX_ALL[i];
|
||||
|
||||
myNodeInfo.air_period_tx[i + 1] = this->airtimes.periodTX[i];
|
||||
myNodeInfo.air_period_rx[i + 1] = this->airtimes.periodRX[i];
|
||||
air_period_tx[i + 1] = this->airtimes.periodTX[i];
|
||||
air_period_rx[i + 1] = this->airtimes.periodRX[i];
|
||||
}
|
||||
|
||||
this->airtimes.periodTX[0] = 0;
|
||||
this->airtimes.periodRX[0] = 0;
|
||||
this->airtimes.periodRX_ALL[0] = 0;
|
||||
|
||||
myNodeInfo.air_period_tx[0] = 0;
|
||||
myNodeInfo.air_period_rx[0] = 0;
|
||||
air_period_tx[0] = 0;
|
||||
air_period_rx[0] = 0;
|
||||
|
||||
this->airtimes.lastPeriodIndex = this->currentPeriodIndex();
|
||||
}
|
||||
@@ -179,18 +181,17 @@ int32_t AirTime::runOnce()
|
||||
}
|
||||
|
||||
// Init airtime windows to all 0
|
||||
for (int i = 0; i < myNodeInfo.air_period_rx_count; i++) {
|
||||
for (int i = 0; i < PERIODS_TO_LOG; i++) {
|
||||
this->airtimes.periodTX[i] = 0;
|
||||
this->airtimes.periodRX[i] = 0;
|
||||
this->airtimes.periodRX_ALL[i] = 0;
|
||||
|
||||
// myNodeInfo.air_period_tx[i] = 0;
|
||||
// myNodeInfo.air_period_rx[i] = 0;
|
||||
// air_period_tx[i] = 0;
|
||||
// air_period_rx[i] = 0;
|
||||
}
|
||||
|
||||
firstTime = false;
|
||||
lastUtilPeriod = utilPeriod;
|
||||
|
||||
} else {
|
||||
this->airtimeRotatePeriod();
|
||||
|
||||
@@ -206,12 +207,6 @@ int32_t AirTime::runOnce()
|
||||
|
||||
this->utilizationTX[utilPeriodTX] = 0;
|
||||
}
|
||||
|
||||
// Update channel_utilization every second.
|
||||
myNodeInfo.channel_utilization = airTime->channelUtilizationPercent();
|
||||
|
||||
// Update channel_utilization every second.
|
||||
myNodeInfo.air_util_tx = airTime->utilizationTXPercent();
|
||||
}
|
||||
/*
|
||||
LOG_DEBUG("utilPeriodTX %d TX Airtime %3.2f%\n", utilPeriodTX, airTime->utilizationTXPercent());
|
||||
@@ -223,4 +218,4 @@ int32_t AirTime::runOnce()
|
||||
LOG_DEBUG("\n");
|
||||
*/
|
||||
return (1000 * 1);
|
||||
}
|
||||
}
|
||||
@@ -17,9 +17,9 @@
|
||||
|
||||
Example analytics:
|
||||
|
||||
TX_LOG + RX_LOG = Total air time for a perticular meshtastic channel.
|
||||
TX_LOG + RX_LOG = Total air time for a particular meshtastic channel.
|
||||
|
||||
TX_LOG + RX_LOG = Total air time for a perticular meshtastic channel, including
|
||||
TX_LOG + RX_LOG = Total air time for a particular meshtastic channel, including
|
||||
other lora radios.
|
||||
|
||||
RX_ALL_LOG - RX_LOG = Other lora radios on our frequency channel.
|
||||
|
||||
@@ -18,7 +18,7 @@ namespace concurrency
|
||||
*
|
||||
* Useful for they top level loop() delay call to keep the CPU powered down until our next scheduled event or some external event.
|
||||
*
|
||||
* This is implmented for FreeRTOS but should be easy to port to other operating systems.
|
||||
* This is implemented for FreeRTOS but should be easy to port to other operating systems.
|
||||
*/
|
||||
class InterruptableDelay
|
||||
{
|
||||
|
||||
@@ -123,6 +123,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
// -----------------------------------------------------------------------------
|
||||
#define MPU6050_ADDR 0x68
|
||||
#define LIS3DH_ADR 0x18
|
||||
#define BMA423_ADDR 0x19
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// LED
|
||||
@@ -193,4 +194,4 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
#ifndef HW_VENDOR
|
||||
#error HW_VENDOR must be defined
|
||||
#endif
|
||||
#endif
|
||||
@@ -36,8 +36,8 @@ ScanI2C::FoundDevice ScanI2C::firstKeyboard() const
|
||||
|
||||
ScanI2C::FoundDevice ScanI2C::firstAccelerometer() const
|
||||
{
|
||||
ScanI2C::DeviceType types[] = {MPU6050, LIS3DH};
|
||||
return firstOfOrNONE(2, types);
|
||||
ScanI2C::DeviceType types[] = {MPU6050, LIS3DH, BMA423};
|
||||
return firstOfOrNONE(3, types);
|
||||
}
|
||||
|
||||
ScanI2C::FoundDevice ScanI2C::find(ScanI2C::DeviceType) const
|
||||
@@ -73,4 +73,4 @@ bool ScanI2C::DeviceAddress::operator<(const ScanI2C::DeviceAddress &other) cons
|
||||
|| (port != NO_I2C && other.port != NO_I2C && (address < other.address));
|
||||
}
|
||||
|
||||
ScanI2C::FoundDevice::FoundDevice(ScanI2C::DeviceType type, ScanI2C::DeviceAddress address) : type(type), address(address) {}
|
||||
ScanI2C::FoundDevice::FoundDevice(ScanI2C::DeviceType type, ScanI2C::DeviceAddress address) : type(type), address(address) {}
|
||||
@@ -33,7 +33,10 @@ class ScanI2C
|
||||
PMSA0031,
|
||||
MPU6050,
|
||||
LIS3DH,
|
||||
BMA423,
|
||||
#ifdef HAS_NCP5623
|
||||
NCP5623,
|
||||
#endif
|
||||
} DeviceType;
|
||||
|
||||
// typedef uint8_t DeviceAddress;
|
||||
@@ -95,4 +98,4 @@ class ScanI2C
|
||||
|
||||
private:
|
||||
bool shouldSuppressScreen = false;
|
||||
};
|
||||
};
|
||||
@@ -212,9 +212,10 @@ void ScanI2CTwoWire::scanPort(I2CPort port)
|
||||
}
|
||||
break;
|
||||
|
||||
SCAN_SIMPLE_CASE(ST7567_ADDRESS, SCREEN_ST7567, "st7567 display found\n")
|
||||
SCAN_SIMPLE_CASE(ST7567_ADDRESS, SCREEN_ST7567, "st7567 display found\n");
|
||||
#ifdef HAS_NCP5623
|
||||
SCAN_SIMPLE_CASE(NCP5623_ADDR, NCP5623, "NCP5623 RGB LED found\n");
|
||||
|
||||
#endif
|
||||
#ifdef HAS_PMU
|
||||
SCAN_SIMPLE_CASE(XPOWERS_AXP192_AXP2101_ADDRESS, PMU_AXP192_AXP2101, "axp192/axp2101 PMU found\n")
|
||||
#endif
|
||||
@@ -273,6 +274,7 @@ void ScanI2CTwoWire::scanPort(I2CPort port)
|
||||
|
||||
SCAN_SIMPLE_CASE(PMSA0031_ADDR, PMSA0031, "PMSA0031 air quality sensor found\n")
|
||||
SCAN_SIMPLE_CASE(MPU6050_ADDR, MPU6050, "MPU6050 accelerometer found\n");
|
||||
SCAN_SIMPLE_CASE(BMA423_ADDR, BMA423, "BMA423 accelerometer found\n");
|
||||
|
||||
default:
|
||||
LOG_INFO("Device found at address 0x%x was not able to be enumerated\n", addr.address);
|
||||
@@ -305,4 +307,4 @@ TwoWire *ScanI2CTwoWire::fetchI2CBus(ScanI2C::DeviceAddress address) const
|
||||
size_t ScanI2CTwoWire::countDevices() const
|
||||
{
|
||||
return foundDevices.size();
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
#include "../configuration.h"
|
||||
|
||||
#ifdef RAK4630
|
||||
#ifdef RAK_4631
|
||||
#include "../main.h"
|
||||
#include <SPI.h>
|
||||
|
||||
@@ -64,4 +64,4 @@ void scanEInkDevice(void)
|
||||
LOG_DEBUG("EInk display not found\n");
|
||||
SPI1.end();
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
@@ -9,4 +9,4 @@
|
||||
|
||||
/// Record an error that should be reported via analytics
|
||||
void recordCriticalError(meshtastic_CriticalErrorCode code = meshtastic_CriticalErrorCode_UNSPECIFIED, uint32_t address = 0,
|
||||
const char *filename = NULL);
|
||||
const char *filename = NULL);
|
||||
@@ -12,7 +12,7 @@
|
||||
#include <freertos/task.h>
|
||||
#endif
|
||||
|
||||
#if defined(ARDUINO_NRF52_ADAFRUIT) || defined(ARDUINO_ARCH_STM32)
|
||||
#if defined(ARDUINO_NRF52_ADAFRUIT) || defined(ARDUINO_ARCH_STM32) || defined(ARDUINO_ARCH_RP2040)
|
||||
#define HAS_FREE_RTOS
|
||||
|
||||
#include <FreeRTOS.h>
|
||||
@@ -44,4 +44,4 @@ typedef uint32_t BaseType_t;
|
||||
|
||||
enum eNotifyAction { eNoAction, eSetValueWithoutOverwrite, eSetValueWithOverwrite };
|
||||
|
||||
#endif
|
||||
#endif
|
||||
461
src/gps/GPS.cpp
461
src/gps/GPS.cpp
@@ -4,6 +4,10 @@
|
||||
#include "configuration.h"
|
||||
#include "sleep.h"
|
||||
|
||||
#ifndef GPS_RESET_MODE
|
||||
#define GPS_RESET_MODE HIGH
|
||||
#endif
|
||||
|
||||
// If we have a serial GPS port it will not be null
|
||||
#ifdef GPS_SERIAL_NUM
|
||||
HardwareSerial _serial_gps_real(GPS_SERIAL_NUM);
|
||||
@@ -21,11 +25,29 @@ GPS *gps;
|
||||
/// only init that port once.
|
||||
static bool didSerialInit;
|
||||
|
||||
bool GPS::getACK(uint8_t c, uint8_t i)
|
||||
struct uBloxGnssModelInfo info;
|
||||
uint8_t uBloxProtocolVersion;
|
||||
|
||||
void GPS::UBXChecksum(byte *message, size_t length)
|
||||
{
|
||||
uint8_t CK_A = 0, CK_B = 0;
|
||||
|
||||
// Calculate the checksum, starting from the CLASS field (which is message[2])
|
||||
for (size_t i = 2; i < length - 2; i++) {
|
||||
CK_A = (CK_A + message[i]) & 0xFF;
|
||||
CK_B = (CK_B + CK_A) & 0xFF;
|
||||
}
|
||||
|
||||
// Place the calculated checksum values in the message
|
||||
message[length - 2] = CK_A;
|
||||
message[length - 1] = CK_B;
|
||||
}
|
||||
|
||||
bool GPS::getACK(uint8_t class_id, uint8_t msg_id)
|
||||
{
|
||||
uint8_t b;
|
||||
uint8_t ack = 0;
|
||||
const uint8_t ackP[2] = {c, i};
|
||||
const uint8_t ackP[2] = {class_id, msg_id};
|
||||
uint8_t buf[10] = {0xB5, 0x62, 0x05, 0x01, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00};
|
||||
unsigned long startTime = millis();
|
||||
|
||||
@@ -42,17 +64,23 @@ bool GPS::getACK(uint8_t c, uint8_t i)
|
||||
|
||||
while (1) {
|
||||
if (ack > 9) {
|
||||
return true;
|
||||
// LOG_INFO("Got ACK for class %02X message %02X\n", class_id, msg_id);
|
||||
return true; // ACK received
|
||||
}
|
||||
if (millis() - startTime > 1000) {
|
||||
return false;
|
||||
if (millis() - startTime > 3000) {
|
||||
LOG_WARN("No response for class %02X message %02X\n", class_id, msg_id);
|
||||
return false; // No response received within 3 seconds
|
||||
}
|
||||
if (_serial_gps->available()) {
|
||||
b = _serial_gps->read();
|
||||
if (b == buf[ack]) {
|
||||
ack++;
|
||||
} else {
|
||||
ack = 0;
|
||||
ack = 0; // Reset the acknowledgement counter
|
||||
if (buf[3] == 0x00) { // UBX-ACK-NAK message
|
||||
LOG_WARN("Got NAK for class %02X message %02X\n", class_id, msg_id);
|
||||
return false; // NAK received
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -73,7 +101,7 @@ int GPS::getAck(uint8_t *buffer, uint16_t size, uint8_t requestedClass, uint8_t
|
||||
uint32_t startTime = millis();
|
||||
uint16_t needRead;
|
||||
|
||||
while (millis() - startTime < 800) {
|
||||
while (millis() - startTime < 1200) {
|
||||
while (_serial_gps->available()) {
|
||||
int c = _serial_gps->read();
|
||||
switch (ubxFrameCounter) {
|
||||
@@ -108,12 +136,12 @@ int GPS::getAck(uint8_t *buffer, uint16_t size, uint8_t requestedClass, uint8_t
|
||||
}
|
||||
break;
|
||||
case 4:
|
||||
// Payload lenght lsb
|
||||
// Payload length lsb
|
||||
needRead = c;
|
||||
ubxFrameCounter++;
|
||||
break;
|
||||
case 5:
|
||||
// Payload lenght msb
|
||||
// Payload length msb
|
||||
needRead |= (c << 8);
|
||||
ubxFrameCounter++;
|
||||
break;
|
||||
@@ -126,7 +154,7 @@ int GPS::getAck(uint8_t *buffer, uint16_t size, uint8_t requestedClass, uint8_t
|
||||
if (_serial_gps->readBytes(buffer, needRead) != needRead) {
|
||||
ubxFrameCounter = 0;
|
||||
} else {
|
||||
// return payload lenght
|
||||
// return payload length
|
||||
return needRead;
|
||||
}
|
||||
break;
|
||||
@@ -160,10 +188,14 @@ bool GPS::setupGPS()
|
||||
config.position.tx_gpio = GPS_TX_PIN;
|
||||
#endif
|
||||
|
||||
//#define BAUD_RATE 115200
|
||||
// ESP32 has a special set of parameters vs other arduino ports
|
||||
#if defined(ARCH_ESP32)
|
||||
if (config.position.rx_gpio)
|
||||
if (config.position.rx_gpio) {
|
||||
LOG_DEBUG("Using GPIO%d for GPS RX\n", config.position.rx_gpio);
|
||||
LOG_DEBUG("Using GPIO%d for GPS TX\n", config.position.tx_gpio);
|
||||
_serial_gps->begin(GPS_BAUDRATE, SERIAL_8N1, config.position.rx_gpio, config.position.tx_gpio);
|
||||
}
|
||||
#else
|
||||
_serial_gps->begin(GPS_BAUDRATE);
|
||||
#endif
|
||||
@@ -190,8 +222,131 @@ bool GPS::setupGPS()
|
||||
// Switch to Vehicle Mode, since SoftRF enables Aviation < 2g
|
||||
_serial_gps->write("$PCAS11,3*1E\r\n");
|
||||
delay(250);
|
||||
} else if (gnssModel == GNSS_MODEL_UC6850) {
|
||||
|
||||
// use GPS + GLONASS
|
||||
_serial_gps->write("$CFGSYS,h15\r\n");
|
||||
delay(250);
|
||||
} else if (gnssModel == GNSS_MODEL_UBLOX) {
|
||||
|
||||
// Configure GNSS system to GPS+SBAS+GLONASS (Module may restart after this command)
|
||||
// We need set it because by default it is GPS only, and we want to use GLONASS too
|
||||
// Also we need SBAS for better accuracy and extra features
|
||||
// ToDo: Dynamic configure GNSS systems depending of LoRa region
|
||||
byte _message_GNSS[36] = {
|
||||
0xb5, 0x62, // Sync message for UBX protocol
|
||||
0x06, 0x3e, // Message class and ID (UBX-CFG-GNSS)
|
||||
0x1c, 0x00, // Length of payload (28 bytes)
|
||||
0x00, // msgVer (0 for this version)
|
||||
0x00, // numTrkChHw (max number of hardware channels, read only, so it's always 0)
|
||||
0xff, // numTrkChUse (max number of channels to use, 0xff = max available)
|
||||
0x03, // numConfigBlocks (number of GNSS systems), most modules support maximum 3 GNSS systems
|
||||
// GNSS config format: gnssId, resTrkCh, maxTrkCh, reserved1, flags
|
||||
0x00, 0x08, 0x10, 0x00, 0x01, 0x00, 0x01, 0x01, // GPS
|
||||
0x01, 0x01, 0x03, 0x00, 0x01, 0x00, 0x01, 0x01, // SBAS
|
||||
0x06, 0x08, 0x0e, 0x00, 0x01, 0x00, 0x01, 0x01, // GLONASS
|
||||
0x00, 0x00 // Checksum (to be calculated below)
|
||||
};
|
||||
|
||||
// Calculate the checksum and update the message.
|
||||
UBXChecksum(_message_GNSS, sizeof(_message_GNSS));
|
||||
|
||||
// Send the message to the module
|
||||
_serial_gps->write(_message_GNSS, sizeof(_message_GNSS));
|
||||
|
||||
if (!getACK(0x06, 0x3e)) {
|
||||
// It's not critical if the module doesn't acknowledge this configuration.
|
||||
// The module should operate adequately with its factory or previously saved settings.
|
||||
// It appears that there is a firmware bug in some GPS modules: When an attempt is made
|
||||
// to overwrite a saved state with identical values, no ACK/NAK is received, contrary to
|
||||
// what is specified in the Ublox documentation.
|
||||
// There is also a possibility that the module may be GPS-only.
|
||||
LOG_INFO("Unable to reconfigure GNSS - defaults maintained. Is this module GPS-only?\n");
|
||||
return true;
|
||||
} else {
|
||||
LOG_INFO("GNSS configured for GPS+SBAS+GLONASS. Pause for 0.75s before sending next command.\n");
|
||||
// Documentation say, we need wait atleast 0.5s after reconfiguration of GNSS module, before sending next commands
|
||||
delay(750);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Enable interference resistance, because we are using LoRa, WiFi and Bluetooth on same board,
|
||||
// and we need to reduce interference from them
|
||||
byte _message_JAM[16] = {
|
||||
0xB5, 0x62, // UBX protocol sync characters
|
||||
0x06, 0x39, // Message class and ID (UBX-CFG-ITFM)
|
||||
0x08, 0x00, // Length of payload (8 bytes)
|
||||
// bbThreshold (Broadband jamming detection threshold) is set to 0x3F (63 in decimal)
|
||||
// cwThreshold (CW jamming detection threshold) is set to 0x10 (16 in decimal)
|
||||
// algorithmBits (Reserved algorithm settings) is set to 0x16B156 as recommended
|
||||
// enable (Enable interference detection) is set to 1 (enabled)
|
||||
0x3F, 0x10, 0xB1, 0x56, // config: Interference config word
|
||||
// generalBits (General settings) is set to 0x31E as recommended
|
||||
// antSetting (Antenna setting, 0=unknown, 1=passive, 2=active) is set to 0 (unknown)
|
||||
// ToDo: Set to 1 (passive) or 2 (active) if known, for example from UBX-MON-HW, or from board info
|
||||
// enable2 (Set to 1 to scan auxiliary bands, u-blox 8 / u-blox M8 only, otherwise ignored) is set to 1
|
||||
// (enabled)
|
||||
0x1E, 0x03, 0x00, 0x01, // config2: Extra settings for jamming/interference monitor
|
||||
0x00, 0x00 // Checksum (calculated below)
|
||||
};
|
||||
|
||||
// Calculate the checksum and update the message.
|
||||
UBXChecksum(_message_JAM, sizeof(_message_JAM));
|
||||
|
||||
// Send the message to the module
|
||||
_serial_gps->write(_message_JAM, sizeof(_message_JAM));
|
||||
|
||||
if (!getACK(0x06, 0x39)) {
|
||||
LOG_WARN("Unable to enable interference resistance.\n");
|
||||
return true;
|
||||
}
|
||||
|
||||
// Configure navigation engine expert settings:
|
||||
byte _message_NAVX5[48] = {
|
||||
0xb5, 0x62, // UBX protocol sync characters
|
||||
0x06, 0x23, // Message class and ID (UBX-CFG-NAVX5)
|
||||
0x28, 0x00, // Length of payload (40 bytes)
|
||||
0x00, 0x00, // msgVer (0 for this version)
|
||||
// minMax flag = 1: apply min/max SVs settings
|
||||
// minCno flag = 1: apply minimum C/N0 setting
|
||||
// initial3dfix flag = 0: apply initial 3D fix settings
|
||||
// aop flag = 1: apply aopCfg (useAOP flag) settings (AssistNow Autonomous)
|
||||
0x1B, 0x00, // mask1 (First parameters bitmask)
|
||||
// adr flag = 0: apply ADR sensor fusion on/off setting (useAdr flag)
|
||||
// If firmware is not ADR/UDR, enabling this flag will fail configuration
|
||||
// ToDo: check this with UBX-MON-VER
|
||||
0x00, 0x00, 0x00, 0x00, // mask2 (Second parameters bitmask)
|
||||
0x00, 0x00, // Reserved
|
||||
0x03, // minSVs (Minimum number of satellites for navigation) = 3
|
||||
0x10, // maxSVs (Maximum number of satellites for navigation) = 16
|
||||
0x06, // minCNO (Minimum satellite signal level for navigation) = 6 dBHz
|
||||
0x00, // Reserved
|
||||
0x00, // iniFix3D (Initial fix must be 3D) = 0 (disabled)
|
||||
0x00, 0x00, // Reserved
|
||||
0x00, // ackAiding (Issue acknowledgements for assistance message input) = 0 (disabled)
|
||||
0x00, 0x00, // Reserved
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Reserved
|
||||
0x00, // Reserved
|
||||
0x01, // aopCfg (AssistNow Autonomous configuration) = 1 (enabled)
|
||||
0x00, 0x00, // Reserved
|
||||
0x00, 0x00, // Reserved
|
||||
0x00, 0x00, 0x00, 0x00, // Reserved
|
||||
0x00, 0x00, 0x00, // Reserved
|
||||
0x01, // useAdr (Enable/disable ADR sensor fusion) = 1 (enabled)
|
||||
0x00, 0x00 // Checksum (calculated below)
|
||||
};
|
||||
|
||||
// Calculate the checksum and update the message.
|
||||
UBXChecksum(_message_NAVX5, sizeof(_message_NAVX5));
|
||||
|
||||
// Send the message to the module
|
||||
_serial_gps->write(_message_NAVX5, sizeof(_message_NAVX5));
|
||||
|
||||
if (!getACK(0x06, 0x23)) {
|
||||
LOG_WARN("Unable to configure extra settings.\n");
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
tips: NMEA Only should not be set here, otherwise initializing Ublox gnss module again after
|
||||
setting will not output command messages in UART1, resulting in unrecognized module information
|
||||
@@ -208,57 +363,205 @@ bool GPS::setupGPS()
|
||||
|
||||
// ublox-M10S can be compatible with UBLOX traditional protocol, so the following sentence settings are also valid
|
||||
|
||||
// disable GGL
|
||||
byte _message_GGL[] = {0xB5, 0x62, 0x06, 0x01, 0x08, 0x00, 0xF0, 0x01,
|
||||
0x01, 0x00, 0x01, 0x01, 0x01, 0x01, 0x05, 0x3A};
|
||||
// Set GPS update rate to 1Hz
|
||||
// Lowering the update rate helps to save power.
|
||||
// Additionally, for some new modules like the M9/M10, an update rate lower than 5Hz
|
||||
// is recommended to avoid a known issue with satellites disappearing.
|
||||
byte _message_1Hz[] = {
|
||||
0xB5, 0x62, // UBX protocol sync characters
|
||||
0x06, 0x08, // Message class and ID (UBX-CFG-RATE)
|
||||
0x06, 0x00, // Length of payload (6 bytes)
|
||||
0xE8, 0x03, // Measurement Rate (1000ms for 1Hz)
|
||||
0x01, 0x00, // Navigation rate, always 1 in GPS mode
|
||||
0x01, 0x00, // Time reference
|
||||
0x00, 0x00 // Placeholder for checksum, will be calculated next
|
||||
};
|
||||
|
||||
// Calculate the checksum and update the message.
|
||||
UBXChecksum(_message_1Hz, sizeof(_message_1Hz));
|
||||
|
||||
// Send the message to the module
|
||||
_serial_gps->write(_message_1Hz, sizeof(_message_1Hz));
|
||||
|
||||
if (!getACK(0x06, 0x08)) {
|
||||
LOG_WARN("Unable to set GPS update rate.\n");
|
||||
return true;
|
||||
}
|
||||
|
||||
// Disable GGL. GGL - Geographic position (latitude and longitude), which provides the current geographical
|
||||
// coordinates.
|
||||
byte _message_GGL[] = {
|
||||
0xB5, 0x62, // UBX sync characters
|
||||
0x06, 0x01, // Message class and ID (UBX-CFG-MSG)
|
||||
0x08, 0x00, // Length of payload (8 bytes)
|
||||
0xF0, 0x01, // NMEA ID for GLL
|
||||
0x01, // I/O Target 0=I/O, 1=UART1, 2=UART2, 3=USB, 4=SPI
|
||||
0x00, // Disable
|
||||
0x01, 0x01, 0x01, 0x01, // Reserved
|
||||
0x00, 0x00 // CK_A and CK_B (Checksum)
|
||||
};
|
||||
|
||||
// Calculate the checksum and update the message.
|
||||
UBXChecksum(_message_GGL, sizeof(_message_GGL));
|
||||
|
||||
// Send the message to the module
|
||||
_serial_gps->write(_message_GGL, sizeof(_message_GGL));
|
||||
|
||||
if (!getACK(0x06, 0x01)) {
|
||||
LOG_WARN("Unable to disable NMEA GGL.\n");
|
||||
return true;
|
||||
}
|
||||
|
||||
// disable GSA
|
||||
byte _message_GSA[] = {0xB5, 0x62, 0x06, 0x01, 0x08, 0x00, 0xF0, 0x02,
|
||||
0x01, 0x00, 0x01, 0x01, 0x01, 0x01, 0x06, 0x41};
|
||||
// Enable GSA. GSA - GPS DOP and active satellites, used for detailing the satellites used in the positioning and
|
||||
// the DOP (Dilution of Precision)
|
||||
byte _message_GSA[] = {
|
||||
0xB5, 0x62, // UBX sync characters
|
||||
0x06, 0x01, // Message class and ID (UBX-CFG-MSG)
|
||||
0x08, 0x00, // Length of payload (8 bytes)
|
||||
0xF0, 0x02, // NMEA ID for GSA
|
||||
0x01, // I/O Target 0=I/O, 1=UART1, 2=UART2, 3=USB, 4=SPI
|
||||
0x01, // Enable
|
||||
0x01, 0x01, 0x01, 0x01, // Reserved
|
||||
0x00, 0x00 // CK_A and CK_B (Checksum)
|
||||
};
|
||||
UBXChecksum(_message_GSA, sizeof(_message_GSA));
|
||||
_serial_gps->write(_message_GSA, sizeof(_message_GSA));
|
||||
if (!getACK(0x06, 0x01)) {
|
||||
LOG_WARN("Unable to disable NMEA GSA.\n");
|
||||
LOG_WARN("Unable to Enable NMEA GSA.\n");
|
||||
return true;
|
||||
}
|
||||
|
||||
// disable GSV
|
||||
byte _message_GSV[] = {0xB5, 0x62, 0x06, 0x01, 0x08, 0x00, 0xF0, 0x03,
|
||||
0x01, 0x00, 0x01, 0x01, 0x01, 0x01, 0x07, 0x48};
|
||||
// Disable GSV. GSV - Satellites in view, details the number and location of satellites in view.
|
||||
byte _message_GSV[] = {
|
||||
0xB5, 0x62, // UBX sync characters
|
||||
0x06, 0x01, // Message class and ID (UBX-CFG-MSG)
|
||||
0x08, 0x00, // Length of payload (8 bytes)
|
||||
0xF0, 0x03, // NMEA ID for GSV
|
||||
0x01, // I/O Target 0=I/O, 1=UART1, 2=UART2, 3=USB, 4=SPI
|
||||
0x00, // Disable
|
||||
0x01, 0x01, 0x01, 0x01, // Reserved
|
||||
0x00, 0x00 // CK_A and CK_B (Checksum)
|
||||
};
|
||||
UBXChecksum(_message_GSV, sizeof(_message_GSV));
|
||||
_serial_gps->write(_message_GSV, sizeof(_message_GSV));
|
||||
if (!getACK(0x06, 0x01)) {
|
||||
LOG_WARN("Unable to disable NMEA GSV.\n");
|
||||
return true;
|
||||
}
|
||||
|
||||
// disable VTG
|
||||
byte _message_VTG[] = {0xB5, 0x62, 0x06, 0x01, 0x08, 0x00, 0xF0, 0x05,
|
||||
0x01, 0x00, 0x01, 0x01, 0x01, 0x01, 0x09, 0x56};
|
||||
// Disable VTG. VTG - Track made good and ground speed, which provides course and speed information relative to
|
||||
// the ground.
|
||||
byte _message_VTG[] = {
|
||||
0xB5, 0x62, // UBX sync characters
|
||||
0x06, 0x01, // Message class and ID (UBX-CFG-MSG)
|
||||
0x08, 0x00, // Length of payload (8 bytes)
|
||||
0xF0, 0x05, // NMEA ID for VTG
|
||||
0x01, // I/O Target 0=I/O, 1=UART1, 2=UART2, 3=USB, 4=SPI
|
||||
0x00, // Disable
|
||||
0x01, 0x01, 0x01, 0x01, // Reserved
|
||||
0x00, 0x00 // CK_A and CK_B (Checksum)
|
||||
};
|
||||
UBXChecksum(_message_VTG, sizeof(_message_VTG));
|
||||
_serial_gps->write(_message_VTG, sizeof(_message_VTG));
|
||||
if (!getACK(0x06, 0x01)) {
|
||||
LOG_WARN("Unable to disable NMEA VTG.\n");
|
||||
return true;
|
||||
}
|
||||
|
||||
// enable RMC
|
||||
byte _message_RMC[] = {0xB5, 0x62, 0x06, 0x01, 0x08, 0x00, 0xF0, 0x04,
|
||||
0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x09, 0x54};
|
||||
// Enable RMC. RMC - Recommended Minimum data, the essential gps pvt (position, velocity, time) data.
|
||||
byte _message_RMC[] = {
|
||||
0xB5, 0x62, // UBX sync characters
|
||||
0x06, 0x01, // Message class and ID (UBX-CFG-MSG)
|
||||
0x08, 0x00, // Length of payload (8 bytes)
|
||||
0xF0, 0x04, // NMEA ID for RMC
|
||||
0x01, // I/O Target 0=I/O, 1=UART1, 2=UART2, 3=USB, 4=SPI
|
||||
0x01, // Enable
|
||||
0x01, 0x01, 0x01, 0x01, // Reserved
|
||||
0x00, 0x00 // CK_A and CK_B (Checksum)
|
||||
};
|
||||
UBXChecksum(_message_RMC, sizeof(_message_RMC));
|
||||
_serial_gps->write(_message_RMC, sizeof(_message_RMC));
|
||||
if (!getACK(0x06, 0x01)) {
|
||||
LOG_WARN("Unable to enable NMEA RMC.\n");
|
||||
return true;
|
||||
}
|
||||
|
||||
// enable GGA
|
||||
byte _message_GGA[] = {0xB5, 0x62, 0x06, 0x01, 0x08, 0x00, 0xF0, 0x00,
|
||||
0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x05, 0x38};
|
||||
// Enable GGA. GGA - Global Positioning System Fix Data, which provides 3D location and accuracy data.
|
||||
byte _message_GGA[] = {
|
||||
0xB5, 0x62, // UBX sync characters
|
||||
0x06, 0x01, // Message class and ID (UBX-CFG-MSG)
|
||||
0x08, 0x00, // Length of payload (8 bytes)
|
||||
0xF0, 0x00, // NMEA ID for GGA
|
||||
0x01, // I/O Target 0=I/O, 1=UART1, 2=UART2, 3=USB, 4=SPI
|
||||
0x01, // Enable
|
||||
0x01, 0x01, 0x01, 0x01, // Reserved
|
||||
0x00, 0x00 // CK_A and CK_B (Checksum)
|
||||
};
|
||||
UBXChecksum(_message_GGA, sizeof(_message_GGA));
|
||||
_serial_gps->write(_message_GGA, sizeof(_message_GGA));
|
||||
if (!getACK(0x06, 0x01)) {
|
||||
LOG_WARN("Unable to enable NMEA GGA.\n");
|
||||
return true;
|
||||
}
|
||||
|
||||
// The Power Management configuration allows the GPS module to operate in different power modes for optimized power
|
||||
// consumption.
|
||||
// The modes supported are:
|
||||
// 0x00 = Full power: The module operates at full power with no power saving.
|
||||
// 0x01 = Balanced: The module dynamically adjusts the tracking behavior to balance power consumption.
|
||||
// 0x02 = Interval: The module operates in a periodic mode, cycling between tracking and power saving states.
|
||||
// 0x03 = Aggressive with 1 Hz: The module operates in a power saving mode with a 1 Hz update rate.
|
||||
// 0x04 = Aggressive with 2 Hz: The module operates in a power saving mode with a 2 Hz update rate.
|
||||
// 0x05 = Aggressive with 4 Hz: The module operates in a power saving mode with a 4 Hz update rate.
|
||||
// The 'period' field specifies the position update and search period. It is only valid when the powerSetupValue is
|
||||
// set to Interval; otherwise, it must be set to '0'. The 'onTime' field specifies the duration of the ON phase and
|
||||
// must be smaller than the period. It is only valid when the powerSetupValue is set to Interval; otherwise, it must
|
||||
// be set to '0'.
|
||||
byte UBX_CFG_PMS[14] = {
|
||||
0xB5, 0x62, // UBX sync characters
|
||||
0x06, 0x86, // Message class and ID (UBX-CFG-PMS)
|
||||
0x06, 0x00, // Length of payload (6 bytes)
|
||||
0x00, // Version (0)
|
||||
0x03, // Power setup value
|
||||
0x00, 0x00, // period: not applicable, set to 0
|
||||
0x00, 0x00, // onTime: not applicable, set to 0
|
||||
0x00, 0x00 // Placeholder for checksum, will be calculated next
|
||||
};
|
||||
|
||||
// Calculate the checksum and update the message
|
||||
UBXChecksum(UBX_CFG_PMS, sizeof(UBX_CFG_PMS));
|
||||
|
||||
// Send the message to the module
|
||||
_serial_gps->write(UBX_CFG_PMS, sizeof(UBX_CFG_PMS));
|
||||
if (!getACK(0x06, 0x86)) {
|
||||
LOG_WARN("Unable to enable powersaving for GPS.\n");
|
||||
return true;
|
||||
}
|
||||
|
||||
// We need save configuration to flash to make our config changes persistent
|
||||
byte _message_SAVE[21] = {
|
||||
0xB5, 0x62, // UBX protocol header
|
||||
0x06, 0x09, // UBX class ID (Configuration Input Messages), message ID (UBX-CFG-CFG)
|
||||
0x0D, 0x00, // Length of payload (13 bytes)
|
||||
0x00, 0x00, 0x00, 0x00, // clearMask: no sections cleared
|
||||
0xFF, 0xFF, 0x00, 0x00, // saveMask: save all sections
|
||||
0x00, 0x00, 0x00, 0x00, // loadMask: no sections loaded
|
||||
0x0F, // deviceMask: BBR, Flash, EEPROM, and SPI Flash
|
||||
0x00, 0x00 // Checksum (calculated below)
|
||||
};
|
||||
|
||||
// Calculate the checksum and update the message.
|
||||
UBXChecksum(_message_SAVE, sizeof(_message_SAVE));
|
||||
|
||||
// Send the message to the module
|
||||
_serial_gps->write(_message_SAVE, sizeof(_message_SAVE));
|
||||
|
||||
if (!getACK(0x06, 0x09)) {
|
||||
LOG_WARN("Unable to save GNSS module configuration.\n");
|
||||
return true;
|
||||
} else {
|
||||
LOG_INFO("GNSS module configuration saved!\n");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -269,22 +572,21 @@ bool GPS::setupGPS()
|
||||
bool GPS::setup()
|
||||
{
|
||||
// Master power for the GPS
|
||||
#ifdef PIN_GPS_EN
|
||||
digitalWrite(PIN_GPS_EN, 1);
|
||||
pinMode(PIN_GPS_EN, OUTPUT);
|
||||
#endif
|
||||
|
||||
#ifdef HAS_PMU
|
||||
#if defined(HAS_PMU) || defined(PIN_GPS_EN)
|
||||
if (config.position.gps_enabled) {
|
||||
#ifdef PIN_GPS_EN
|
||||
pinMode(PIN_GPS_EN, OUTPUT);
|
||||
#endif
|
||||
setGPSPower(true);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef PIN_GPS_RESET
|
||||
digitalWrite(PIN_GPS_RESET, 1); // assert for 10ms
|
||||
digitalWrite(PIN_GPS_RESET, GPS_RESET_MODE); // assert for 10ms
|
||||
pinMode(PIN_GPS_RESET, OUTPUT);
|
||||
delay(10);
|
||||
digitalWrite(PIN_GPS_RESET, 0);
|
||||
digitalWrite(PIN_GPS_RESET, !GPS_RESET_MODE);
|
||||
#endif
|
||||
setAwake(true); // Wake GPS power before doing any init
|
||||
bool ok = setupGPS();
|
||||
@@ -384,7 +686,7 @@ void GPS::setAwake(bool on)
|
||||
}
|
||||
}
|
||||
|
||||
/** Get how long we should stay looking for each aquisition in msecs
|
||||
/** Get how long we should stay looking for each acquisition in msecs
|
||||
*/
|
||||
uint32_t GPS::getWakeTime() const
|
||||
{
|
||||
@@ -555,21 +857,17 @@ int GPS::prepareDeepSleep(void *unused)
|
||||
|
||||
GnssModel_t GPS::probe()
|
||||
{
|
||||
// return immediately if the model is set by the variant.h file
|
||||
#ifdef GPS_UBLOX
|
||||
return GNSS_MODEL_UBLOX;
|
||||
#elif defined(GPS_L76K)
|
||||
return GNSS_MODEL_MTK;
|
||||
#else
|
||||
// we use autodetect, only T-BEAM S3 for now...
|
||||
uint8_t buffer[256];
|
||||
/*
|
||||
* The GNSS module information variable is temporarily placed inside the function body,
|
||||
* if it needs to be used elsewhere, it can be moved to the outside
|
||||
* */
|
||||
struct uBloxGnssModelInfo info;
|
||||
|
||||
memset(&info, 0, sizeof(struct uBloxGnssModelInfo));
|
||||
// return immediately if the model is set by the variant.h file
|
||||
//#ifdef GPS_UBLOX (unless it's a ublox, because we might want to know the module info!
|
||||
// return GNSS_MODEL_UBLOX; think about removing this macro and return)
|
||||
#if defined(GPS_L76K)
|
||||
return GNSS_MODEL_MTK;
|
||||
#elif defined(GPS_UC6580)
|
||||
_serial_gps->updateBaudRate(115200);
|
||||
return GNSS_MODEL_UC6850;
|
||||
#else
|
||||
uint8_t buffer[384] = {0};
|
||||
|
||||
// Close all NMEA sentences , Only valid for MTK platform
|
||||
_serial_gps->write("$PCAS03,0,0,0,0,0,0,0,0,0,0,,,0,0*02\r\n");
|
||||
@@ -597,31 +895,37 @@ GnssModel_t GPS::probe()
|
||||
uint8_t cfg_rate[] = {0xB5, 0x62, 0x06, 0x08, 0x00, 0x00, 0x0E, 0x30};
|
||||
_serial_gps->write(cfg_rate, sizeof(cfg_rate));
|
||||
// Check that the returned response class and message ID are correct
|
||||
if (!getAck(buffer, 256, 0x06, 0x08)) {
|
||||
if (!getAck(buffer, 384, 0x06, 0x08)) {
|
||||
LOG_WARN("Failed to find UBlox & MTK GNSS Module\n");
|
||||
return GNSS_MODEL_UNKONW;
|
||||
return GNSS_MODEL_UNKNOWN;
|
||||
}
|
||||
|
||||
memset(buffer, 0, sizeof(buffer));
|
||||
byte _message_MONVER[8] = {
|
||||
0xB5, 0x62, // Sync message for UBX protocol
|
||||
0x0A, 0x04, // Message class and ID (UBX-MON-VER)
|
||||
0x00, 0x00, // Length of payload (we're asking for an answer, so no payload)
|
||||
0x00, 0x00 // Checksum
|
||||
};
|
||||
// Get Ublox gnss module hardware and software info
|
||||
uint8_t cfg_get_hw[] = {0xB5, 0x62, 0x0A, 0x04, 0x00, 0x00, 0x0E, 0x34};
|
||||
_serial_gps->write(cfg_get_hw, sizeof(cfg_get_hw));
|
||||
UBXChecksum(_message_MONVER, sizeof(_message_MONVER));
|
||||
_serial_gps->write(_message_MONVER, sizeof(_message_MONVER));
|
||||
|
||||
uint16_t len = getAck(buffer, 256, 0x0A, 0x04);
|
||||
uint16_t len = getAck(buffer, 384, 0x0A, 0x04);
|
||||
if (len) {
|
||||
|
||||
// LOG_DEBUG("monver reply size = %d\n", len);
|
||||
uint16_t position = 0;
|
||||
for (int i = 0; i < 30; i++) {
|
||||
info.swVersion[i] = buffer[position];
|
||||
position++;
|
||||
}
|
||||
for (int i = 0; i < 10; i++) {
|
||||
info.hwVersion[i] = buffer[position];
|
||||
info.hwVersion[i] = buffer[position - 1];
|
||||
position++;
|
||||
}
|
||||
|
||||
while (len >= position + 30) {
|
||||
for (int i = 0; i < 30; i++) {
|
||||
info.extension[info.extensionNo][i] = buffer[position];
|
||||
info.extension[info.extensionNo][i] = buffer[position - 1];
|
||||
position++;
|
||||
}
|
||||
info.extensionNo++;
|
||||
@@ -631,6 +935,7 @@ GnssModel_t GPS::probe()
|
||||
|
||||
LOG_DEBUG("Module Info : \n");
|
||||
LOG_DEBUG("Soft version: %s\n", info.swVersion);
|
||||
LOG_DEBUG("first char is %c\n", (char)info.swVersion[0]);
|
||||
LOG_DEBUG("Hard version: %s\n", info.hwVersion);
|
||||
LOG_DEBUG("Extensions:%d\n", info.extensionNo);
|
||||
for (int i = 0; i < info.extensionNo; i++) {
|
||||
@@ -641,19 +946,29 @@ GnssModel_t GPS::probe()
|
||||
|
||||
// tips: extensionNo field is 0 on some 6M GNSS modules
|
||||
for (int i = 0; i < info.extensionNo; ++i) {
|
||||
if (!strncmp(info.extension[i], "OD=", 3)) {
|
||||
strncpy((char *)buffer, &(info.extension[i][3]), sizeof(buffer));
|
||||
LOG_DEBUG("GetModel:%s\n", (char *)buffer);
|
||||
if (!strncmp(info.extension[i], "MOD=", 4)) {
|
||||
strncpy((char *)buffer, &(info.extension[i][4]), sizeof(buffer));
|
||||
// LOG_DEBUG("GetModel:%s\n", (char *)buffer);
|
||||
if (strlen((char *)buffer)) {
|
||||
LOG_INFO("UBlox GNSS init succeeded, using UBlox %s GNSS Module\n", (char *)buffer);
|
||||
} else {
|
||||
LOG_INFO("UBlox GNSS init succeeded, using UBlox GNSS Module\n");
|
||||
}
|
||||
} else if (!strncmp(info.extension[i], "PROTVER=", 8)) {
|
||||
char *ptr = nullptr;
|
||||
memset(buffer, 0, sizeof(buffer));
|
||||
strncpy((char *)buffer, &(info.extension[i][8]), sizeof(buffer));
|
||||
LOG_DEBUG("Protocol Version:%s\n", (char *)buffer);
|
||||
if (strlen((char *)buffer)) {
|
||||
uBloxProtocolVersion = strtoul((char *)buffer, &ptr, 10);
|
||||
LOG_DEBUG("ProtVer=%d\n", uBloxProtocolVersion);
|
||||
} else {
|
||||
uBloxProtocolVersion = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (strlen((char *)buffer)) {
|
||||
LOG_INFO("UBlox GNSS init succeeded, using UBlox %s GNSS Module\n", buffer);
|
||||
} else {
|
||||
LOG_INFO("UBlox GNSS init succeeded, using UBlox GNSS Module\n");
|
||||
}
|
||||
|
||||
return GNSS_MODEL_UBLOX;
|
||||
#endif
|
||||
}
|
||||
@@ -675,8 +990,8 @@ GPS *createGps()
|
||||
LOG_DEBUG("Using MSL altitude model\n");
|
||||
#endif
|
||||
if (GPS::_serial_gps) {
|
||||
// Some boards might have only the TX line from the GPS connected, in that case, we can't configure it at all. Just
|
||||
// assume NMEA at 9600 baud.
|
||||
// Some boards might have only the TX line from the GPS connected, in that case, we can't configure it at all.
|
||||
// Just assume NMEA at 9600 baud.
|
||||
GPS *new_gps = new NMEAGPS();
|
||||
new_gps->setup();
|
||||
return new_gps;
|
||||
@@ -688,4 +1003,4 @@ GPS *createGps()
|
||||
}
|
||||
return nullptr;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
@@ -14,7 +14,8 @@ struct uBloxGnssModelInfo {
|
||||
typedef enum {
|
||||
GNSS_MODEL_MTK,
|
||||
GNSS_MODEL_UBLOX,
|
||||
GNSS_MODEL_UNKONW,
|
||||
GNSS_MODEL_UC6850,
|
||||
GNSS_MODEL_UNKNOWN,
|
||||
} GnssModel_t;
|
||||
|
||||
// Generate a string representation of DOP
|
||||
@@ -139,6 +140,9 @@ class GPS : private concurrency::OSThread
|
||||
/// always returns 0 to indicate okay to sleep
|
||||
int prepareDeepSleep(void *unused);
|
||||
|
||||
// Calculate checksum
|
||||
void UBXChecksum(byte *message, size_t length);
|
||||
|
||||
/**
|
||||
* Switch the GPS into a mode where we are actively looking for a lock, or alternatively switch GPS into a low power mode
|
||||
*
|
||||
@@ -164,6 +168,7 @@ class GPS : private concurrency::OSThread
|
||||
virtual int32_t runOnce() override;
|
||||
|
||||
// Get GNSS model
|
||||
String getNMEA();
|
||||
GnssModel_t probe();
|
||||
|
||||
int getAck(uint8_t *buffer, uint16_t size, uint8_t requestedClass, uint8_t requestedID);
|
||||
@@ -172,11 +177,11 @@ class GPS : private concurrency::OSThread
|
||||
uint8_t fixeddelayCtr = 0;
|
||||
|
||||
protected:
|
||||
GnssModel_t gnssModel = GNSS_MODEL_UNKONW;
|
||||
GnssModel_t gnssModel = GNSS_MODEL_UNKNOWN;
|
||||
};
|
||||
|
||||
// Creates an instance of the GPS class.
|
||||
// Returns the new instance or null if the GPS is not present.
|
||||
GPS *createGps();
|
||||
|
||||
extern GPS *gps;
|
||||
extern GPS *gps;
|
||||
@@ -12,7 +12,7 @@ GeoCoord::GeoCoord(int32_t lat, int32_t lon, int32_t alt) : _latitude(lat), _lon
|
||||
|
||||
GeoCoord::GeoCoord(float lat, float lon, int32_t alt) : _altitude(alt)
|
||||
{
|
||||
// Change decimial reprsentation to int32_t. I.e., 12.345 becomes 123450000
|
||||
// Change decimial representation to int32_t. I.e., 12.345 becomes 123450000
|
||||
_latitude = int32_t(lat * 1e+7);
|
||||
_longitude = int32_t(lon * 1e+7);
|
||||
GeoCoord::setCoords();
|
||||
@@ -20,7 +20,7 @@ GeoCoord::GeoCoord(float lat, float lon, int32_t alt) : _altitude(alt)
|
||||
|
||||
GeoCoord::GeoCoord(double lat, double lon, int32_t alt) : _altitude(alt)
|
||||
{
|
||||
// Change decimial reprsentation to int32_t. I.e., 12.345 becomes 123450000
|
||||
// Change decimial representation to int32_t. I.e., 12.345 becomes 123450000
|
||||
_latitude = int32_t(lat * 1e+7);
|
||||
_longitude = int32_t(lon * 1e+7);
|
||||
GeoCoord::setCoords();
|
||||
@@ -41,7 +41,7 @@ void GeoCoord::setCoords()
|
||||
|
||||
void GeoCoord::updateCoords(int32_t lat, int32_t lon, int32_t alt)
|
||||
{
|
||||
// If marked dirty or new coordiantes
|
||||
// If marked dirty or new coordinates
|
||||
if (_dirty || _latitude != lat || _longitude != lon || _altitude != alt) {
|
||||
_dirty = true;
|
||||
_latitude = lat;
|
||||
@@ -55,7 +55,7 @@ void GeoCoord::updateCoords(const double lat, const double lon, const int32_t al
|
||||
{
|
||||
int32_t iLat = lat * 1e+7;
|
||||
int32_t iLon = lon * 1e+7;
|
||||
// If marked dirty or new coordiantes
|
||||
// If marked dirty or new coordinates
|
||||
if (_dirty || _latitude != iLat || _longitude != iLon || _altitude != alt) {
|
||||
_dirty = true;
|
||||
_latitude = iLat;
|
||||
@@ -69,7 +69,7 @@ void GeoCoord::updateCoords(const float lat, const float lon, const int32_t alt)
|
||||
{
|
||||
int32_t iLat = lat * 1e+7;
|
||||
int32_t iLon = lon * 1e+7;
|
||||
// If marked dirty or new coordiantes
|
||||
// If marked dirty or new coordinates
|
||||
if (_dirty || _latitude != iLat || _longitude != iLon || _altitude != alt) {
|
||||
_dirty = true;
|
||||
_latitude = iLat;
|
||||
@@ -217,7 +217,7 @@ void GeoCoord::latLongToOSGR(const double lat, const double lon, OSGR &osgr)
|
||||
double eta2 = v / rho - 1;
|
||||
double mA = (1 + n + (5 / 4) * n * n + (5 / 4) * n * n * n) * (phi - phi0);
|
||||
double mB = (3 * n + 3 * n * n + (21 / 8) * n * n * n) * sin(phi - phi0) * cos(phi + phi0);
|
||||
// loss of precision in mC & mD due to floating point rounding can cause innaccuracy of northing by a few meters
|
||||
// loss of precision in mC & mD due to floating point rounding can cause inaccuracy of northing by a few meters
|
||||
double mC = (15 / 8 * n * n + 15 / 8 * n * n * n) * sin(2 * (phi - phi0)) * cos(2 * (phi + phi0));
|
||||
double mD = (35 / 24) * n * n * n * sin(3 * (phi - phi0)) * cos(3 * (phi + phi0));
|
||||
double m = b * f0 * (mA - mB + mC - mD);
|
||||
|
||||
@@ -65,7 +65,7 @@ struct MGRS {
|
||||
uint32_t northing;
|
||||
};
|
||||
|
||||
// A struct to hold the data for a OSGR coordiante
|
||||
// A struct to hold the data for a OSGR coordinate
|
||||
struct OSGR {
|
||||
char e100k;
|
||||
char n100k;
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
* -------------------------------------------
|
||||
*/
|
||||
|
||||
uint32_t printWPL(char *buf, size_t bufsz, const meshtastic_Position &pos, const char *name, bool isCaltopoMode)
|
||||
uint32_t printWPL(char *buf, size_t bufsz, const meshtastic_PositionLite &pos, const char *name, bool isCaltopoMode)
|
||||
{
|
||||
GeoCoord geoCoord(pos.latitude_i, pos.longitude_i, pos.altitude);
|
||||
char type = isCaltopoMode ? 'P' : 'N';
|
||||
@@ -34,6 +34,21 @@ uint32_t printWPL(char *buf, size_t bufsz, const meshtastic_Position &pos, const
|
||||
return len;
|
||||
}
|
||||
|
||||
uint32_t printWPL(char *buf, size_t bufsz, const meshtastic_Position &pos, const char *name, bool isCaltopoMode)
|
||||
{
|
||||
GeoCoord geoCoord(pos.latitude_i, pos.longitude_i, pos.altitude);
|
||||
char type = isCaltopoMode ? 'P' : 'N';
|
||||
uint32_t len = snprintf(buf, bufsz, "$G%cWPL,%02d%07.4f,%c,%03d%07.4f,%c,%s", type, geoCoord.getDMSLatDeg(),
|
||||
(abs(geoCoord.getLatitude()) - geoCoord.getDMSLatDeg() * 1e+7) * 6e-6, geoCoord.getDMSLatCP(),
|
||||
geoCoord.getDMSLonDeg(), (abs(geoCoord.getLongitude()) - geoCoord.getDMSLonDeg() * 1e+7) * 6e-6,
|
||||
geoCoord.getDMSLonCP(), name);
|
||||
uint32_t chk = 0;
|
||||
for (uint32_t i = 1; i < len; i++) {
|
||||
chk ^= buf[i];
|
||||
}
|
||||
len += snprintf(buf + len, bufsz - len, "*%02X\r\n", chk);
|
||||
return len;
|
||||
}
|
||||
/* -------------------------------------------
|
||||
* 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
|
||||
* | | | | | | | | | | | | | | |
|
||||
|
||||
@@ -4,4 +4,5 @@
|
||||
#include <Arduino.h>
|
||||
|
||||
uint32_t printWPL(char *buf, size_t bufsz, const meshtastic_Position &pos, const char *name, bool isCaltopoMode = false);
|
||||
uint32_t printGGA(char *buf, size_t bufsz, const meshtastic_Position &pos);
|
||||
uint32_t printWPL(char *buf, size_t bufsz, const meshtastic_PositionLite &pos, const char *name, bool isCaltopoMode = false);
|
||||
uint32_t printGGA(char *buf, size_t bufsz, const meshtastic_Position &pos);
|
||||
@@ -19,7 +19,7 @@ static uint64_t zeroOffsetSecs; // GPS based time in secs since 1970 - only upda
|
||||
|
||||
void readFromRTC()
|
||||
{
|
||||
struct timeval tv; /* btw settimeofday() is helpfull here too*/
|
||||
struct timeval tv; /* btw settimeofday() is helpful here too*/
|
||||
#ifdef RV3028_RTC
|
||||
if (rtc_found.address == RV3028_RTC) {
|
||||
uint32_t now = millis();
|
||||
|
||||
@@ -7,6 +7,10 @@
|
||||
#include "main.h"
|
||||
#include <SPI.h>
|
||||
|
||||
// #ifdef HELTEC_WIRELESS_PAPER
|
||||
// SPIClass *hspi = NULL;
|
||||
// #endif
|
||||
|
||||
#define COLORED GxEPD_BLACK
|
||||
#define UNCOLORED GxEPD_WHITE
|
||||
|
||||
@@ -19,13 +23,13 @@
|
||||
#define TECHO_DISPLAY_MODEL GxEPD2_213_BN
|
||||
|
||||
// 4.2 inch 300x400 - GxEPD2_420_M01
|
||||
//#define TECHO_DISPLAY_MODEL GxEPD2_420_M01
|
||||
// #define TECHO_DISPLAY_MODEL GxEPD2_420_M01
|
||||
|
||||
// 2.9 inch 296x128 - GxEPD2_290_T5D
|
||||
//#define TECHO_DISPLAY_MODEL GxEPD2_290_T5D
|
||||
// #define TECHO_DISPLAY_MODEL GxEPD2_290_T5D
|
||||
|
||||
// 1.54 inch 200x200 - GxEPD2_154_M09
|
||||
//#define TECHO_DISPLAY_MODEL GxEPD2_154_M09
|
||||
// #define TECHO_DISPLAY_MODEL GxEPD2_154_M09
|
||||
|
||||
#elif defined(MAKERPYTHON)
|
||||
// 2.9 inch 296x128 - GxEPD2_290_T5D
|
||||
@@ -41,6 +45,9 @@
|
||||
// 1.54 inch 200x200 - GxEPD2_154_M09
|
||||
#define TECHO_DISPLAY_MODEL GxEPD2_154_M09
|
||||
|
||||
#elif defined(HELTEC_WIRELESS_PAPER)
|
||||
//#define TECHO_DISPLAY_MODEL GxEPD2_213_T5D
|
||||
#define TECHO_DISPLAY_MODEL GxEPD2_213_BN
|
||||
#endif
|
||||
|
||||
GxEPD2_BW<TECHO_DISPLAY_MODEL, TECHO_DISPLAY_MODEL::HEIGHT> *adafruitDisplay;
|
||||
@@ -62,6 +69,10 @@ EInkDisplay::EInkDisplay(uint8_t address, int sda, int scl, OLEDDISPLAY_GEOMETRY
|
||||
|
||||
// GxEPD2_154_M09
|
||||
// setGeometry(GEOMETRY_RAWMODE, 200, 200);
|
||||
|
||||
#elif defined(HELTEC_WIRELESS_PAPER)
|
||||
// setGeometry(GEOMETRY_RAWMODE, 212, 104);
|
||||
setGeometry(GEOMETRY_RAWMODE, 250, 122);
|
||||
#elif defined(MAKERPYTHON)
|
||||
// GxEPD2_290_T5D
|
||||
setGeometry(GEOMETRY_RAWMODE, 296, 128);
|
||||
@@ -109,7 +120,7 @@ bool EInkDisplay::forceDisplay(uint32_t msecLimit)
|
||||
for (uint32_t y = 0; y < displayHeight; y++) {
|
||||
for (uint32_t x = 0; x < displayWidth; x++) {
|
||||
|
||||
// get src pixel in the page based ordering the OLED lib uses FIXME, super inefficent
|
||||
// get src pixel in the page based ordering the OLED lib uses FIXME, super inefficient
|
||||
auto b = buffer[x + (y / 8) * displayWidth];
|
||||
auto isset = b & (1 << (y & 7));
|
||||
adafruitDisplay->drawPixel(x, y, isset ? COLORED : UNCOLORED);
|
||||
@@ -218,6 +229,16 @@ bool EInkDisplay::connect()
|
||||
(void)adafruitDisplay;
|
||||
}
|
||||
}
|
||||
#elif defined(HELTEC_WIRELESS_PAPER)
|
||||
{
|
||||
auto lowLevel = new TECHO_DISPLAY_MODEL(PIN_EINK_CS, PIN_EINK_DC, PIN_EINK_RES, PIN_EINK_BUSY);
|
||||
adafruitDisplay = new GxEPD2_BW<TECHO_DISPLAY_MODEL, TECHO_DISPLAY_MODEL::HEIGHT>(*lowLevel);
|
||||
// hspi = new SPIClass(HSPI);
|
||||
// hspi->begin(PIN_EINK_SCLK, -1, PIN_EINK_MOSI, PIN_EINK_CS); // SCLK, MISO, MOSI, SS
|
||||
adafruitDisplay->init(115200, true, 10, false, SPI, SPISettings(6000000, MSBFIRST, SPI_MODE0));
|
||||
adafruitDisplay->setRotation(3);
|
||||
adafruitDisplay->setPartialWindow(0, 0, displayWidth, displayHeight);
|
||||
}
|
||||
#elif defined(PCA10059)
|
||||
{
|
||||
auto lowLevel = new TECHO_DISPLAY_MODEL(PIN_EINK_CS, PIN_EINK_DC, PIN_EINK_RES, PIN_EINK_BUSY);
|
||||
|
||||
7
src/graphics/RAKled.h
Normal file
7
src/graphics/RAKled.h
Normal file
@@ -0,0 +1,7 @@
|
||||
#include "main.h"
|
||||
|
||||
#ifdef HAS_NCP5623
|
||||
#include <NCP5623.h>
|
||||
extern NCP5623 rgb;
|
||||
|
||||
#endif
|
||||
@@ -27,6 +27,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#include "GPS.h"
|
||||
#include "MeshService.h"
|
||||
#include "NodeDB.h"
|
||||
#include "error.h"
|
||||
#include "gps/GeoCoord.h"
|
||||
#include "gps/RTC.h"
|
||||
#include "graphics/images.h"
|
||||
@@ -101,7 +102,7 @@ static uint16_t displayWidth, displayHeight;
|
||||
#define SCREEN_WIDTH displayWidth
|
||||
#define SCREEN_HEIGHT displayHeight
|
||||
|
||||
#if defined(USE_EINK) || defined(ILI9341_DRIVER) || defined(ST7735_CS)
|
||||
#if defined(USE_EINK) || defined(ILI9341_DRIVER) || defined(ST7735_CS) || defined(ST7789_CS)
|
||||
// The screen is bigger so use bigger fonts
|
||||
#define FONT_SMALL ArialMT_Plain_16 // Height: 19
|
||||
#define FONT_MEDIUM ArialMT_Plain_24 // Height: 28
|
||||
@@ -295,7 +296,7 @@ static void drawModuleFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int
|
||||
static void drawFrameBluetooth(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y)
|
||||
{
|
||||
int x_offset = display->width() / 2;
|
||||
int y_offset = display->height() == 64 ? 0 : 32;
|
||||
int y_offset = display->height() <= 80 ? 0 : 32;
|
||||
display->setTextAlignment(TEXT_ALIGN_CENTER);
|
||||
display->setFont(FONT_MEDIUM);
|
||||
display->drawString(x_offset + x, y_offset + y, "Bluetooth");
|
||||
@@ -319,18 +320,18 @@ static void drawFrameBluetooth(OLEDDisplay *display, OLEDDisplayUiState *state,
|
||||
|
||||
static void drawFrameShutdown(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y)
|
||||
{
|
||||
uint16_t x_offset = display->width() / 2;
|
||||
display->setTextAlignment(TEXT_ALIGN_CENTER);
|
||||
|
||||
display->setFont(FONT_MEDIUM);
|
||||
display->drawString(64 + x, 26 + y, "Shutting down...");
|
||||
display->drawString(x_offset + x, 26 + y, "Shutting down...");
|
||||
}
|
||||
|
||||
static void drawFrameReboot(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y)
|
||||
{
|
||||
uint16_t x_offset = display->width() / 2;
|
||||
display->setTextAlignment(TEXT_ALIGN_CENTER);
|
||||
|
||||
display->setFont(FONT_MEDIUM);
|
||||
display->drawString(64 + x, 26 + y, "Rebooting...");
|
||||
display->drawString(x_offset + x, 26 + y, "Rebooting...");
|
||||
}
|
||||
|
||||
static void drawFrameFirmware(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y)
|
||||
@@ -352,14 +353,14 @@ static void drawCriticalFaultFrame(OLEDDisplay *display, OLEDDisplayUiState *sta
|
||||
display->setFont(FONT_MEDIUM);
|
||||
|
||||
char tempBuf[24];
|
||||
snprintf(tempBuf, sizeof(tempBuf), "Critical fault #%d", myNodeInfo.error_code);
|
||||
snprintf(tempBuf, sizeof(tempBuf), "Critical fault #%d", error_code);
|
||||
display->drawString(0 + x, 0 + y, tempBuf);
|
||||
display->setTextAlignment(TEXT_ALIGN_LEFT);
|
||||
display->setFont(FONT_SMALL);
|
||||
display->drawString(0 + x, FONT_HEIGHT_MEDIUM + y, "For help, please visit \nmeshtastic.org");
|
||||
}
|
||||
|
||||
// Ignore messages orginating from phone (from the current node 0x0) unless range test or store and forward module are enabled
|
||||
// Ignore messages originating from phone (from the current node 0x0) unless range test or store and forward module are enabled
|
||||
static bool shouldDrawMessage(const meshtastic_MeshPacket *packet)
|
||||
{
|
||||
return packet->from != 0 && !moduleConfig.range_test.enabled && !moduleConfig.store_forward.enabled;
|
||||
@@ -372,7 +373,7 @@ static void drawTextMessageFrame(OLEDDisplay *display, OLEDDisplayUiState *state
|
||||
static char tempBuf[237];
|
||||
|
||||
meshtastic_MeshPacket &mp = devicestate.rx_text_message;
|
||||
meshtastic_NodeInfo *node = nodeDB.getNode(getFrom(&mp));
|
||||
meshtastic_NodeInfoLite *node = nodeDB.getMeshNode(getFrom(&mp));
|
||||
// LOG_DEBUG("drawing text message from 0x%x: %s\n", mp.from,
|
||||
// mp.decoded.variant.data.decoded.bytes);
|
||||
|
||||
@@ -410,7 +411,7 @@ static void drawWaypointFrame(OLEDDisplay *display, OLEDDisplayUiState *state, i
|
||||
static char tempBuf[237];
|
||||
|
||||
meshtastic_MeshPacket &mp = devicestate.rx_waypoint;
|
||||
meshtastic_NodeInfo *node = nodeDB.getNode(getFrom(&mp));
|
||||
meshtastic_NodeInfoLite *node = nodeDB.getMeshNode(getFrom(&mp));
|
||||
|
||||
display->setTextAlignment(TEXT_ALIGN_LEFT);
|
||||
display->setFont(FONT_SMALL);
|
||||
@@ -441,7 +442,7 @@ static void drawWaypointFrame(OLEDDisplay *display, OLEDDisplayUiState *state, i
|
||||
}
|
||||
}
|
||||
|
||||
/// Draw a series of fields in a column, wrapping to multiple colums if needed
|
||||
/// Draw a series of fields in a column, wrapping to multiple columns if needed
|
||||
static void drawColumns(OLEDDisplay *display, int16_t x, int16_t y, const char **fields)
|
||||
{
|
||||
// The coordinates define the left starting point of the text
|
||||
@@ -491,7 +492,7 @@ static void drawNodes(OLEDDisplay *display, int16_t x, int16_t y, NodeStatus *no
|
||||
{
|
||||
char usersString[20];
|
||||
snprintf(usersString, sizeof(usersString), "%d/%d", nodeStatus->getNumOnline(), nodeStatus->getNumTotal());
|
||||
#if defined(USE_EINK) || defined(ILI9341_DRIVER) || defined(ST7735_CS)
|
||||
#if defined(USE_EINK) || defined(ILI9341_DRIVER) || defined(ST7735_CS) || defined(ST7789_CS)
|
||||
display->drawFastImage(x, y + 3, 8, 8, imgUser);
|
||||
#else
|
||||
display->drawFastImage(x, y, 8, 8, imgUser);
|
||||
@@ -792,16 +793,16 @@ static void drawNodeInfo(OLEDDisplay *display, OLEDDisplayUiState *state, int16_
|
||||
if (state->currentFrame != prevFrame) {
|
||||
prevFrame = state->currentFrame;
|
||||
|
||||
nodeIndex = (nodeIndex + 1) % nodeDB.getNumNodes();
|
||||
meshtastic_NodeInfo *n = nodeDB.getNodeByIndex(nodeIndex);
|
||||
nodeIndex = (nodeIndex + 1) % nodeDB.getNumMeshNodes();
|
||||
meshtastic_NodeInfoLite *n = nodeDB.getMeshNodeByIndex(nodeIndex);
|
||||
if (n->num == nodeDB.getNodeNum()) {
|
||||
// Don't show our node, just skip to next
|
||||
nodeIndex = (nodeIndex + 1) % nodeDB.getNumNodes();
|
||||
n = nodeDB.getNodeByIndex(nodeIndex);
|
||||
nodeIndex = (nodeIndex + 1) % nodeDB.getNumMeshNodes();
|
||||
n = nodeDB.getMeshNodeByIndex(nodeIndex);
|
||||
}
|
||||
}
|
||||
|
||||
meshtastic_NodeInfo *node = nodeDB.getNodeByIndex(nodeIndex);
|
||||
meshtastic_NodeInfoLite *node = nodeDB.getMeshNodeByIndex(nodeIndex);
|
||||
|
||||
display->setFont(FONT_SMALL);
|
||||
|
||||
@@ -835,7 +836,7 @@ static void drawNodeInfo(OLEDDisplay *display, OLEDDisplayUiState *state, int16_
|
||||
|
||||
static char distStr[20];
|
||||
strncpy(distStr, "? km", sizeof(distStr)); // might not have location data
|
||||
meshtastic_NodeInfo *ourNode = nodeDB.getNode(nodeDB.getNodeNum());
|
||||
meshtastic_NodeInfoLite *ourNode = nodeDB.getMeshNode(nodeDB.getNodeNum());
|
||||
const char *fields[] = {username, distStr, signalStr, lastStr, NULL};
|
||||
int16_t compassX = 0, compassY = 0;
|
||||
|
||||
@@ -850,14 +851,14 @@ static void drawNodeInfo(OLEDDisplay *display, OLEDDisplayUiState *state, int16_
|
||||
bool hasNodeHeading = false;
|
||||
|
||||
if (ourNode && hasValidPosition(ourNode)) {
|
||||
meshtastic_Position &op = ourNode->position;
|
||||
meshtastic_PositionLite &op = ourNode->position;
|
||||
float myHeading = estimatedHeading(DegD(op.latitude_i), DegD(op.longitude_i));
|
||||
drawCompassNorth(display, compassX, compassY, myHeading);
|
||||
|
||||
if (hasValidPosition(node)) {
|
||||
// display direction toward node
|
||||
hasNodeHeading = true;
|
||||
meshtastic_Position &p = node->position;
|
||||
meshtastic_PositionLite &p = node->position;
|
||||
float d =
|
||||
GeoCoord::latLongToMeter(DegD(p.latitude_i), DegD(p.longitude_i), DegD(op.latitude_i), DegD(op.longitude_i));
|
||||
|
||||
@@ -943,6 +944,9 @@ void Screen::handleSetOn(bool on)
|
||||
if (on != screenOn) {
|
||||
if (on) {
|
||||
LOG_INFO("Turning on screen\n");
|
||||
#ifdef T_WATCH_S3
|
||||
PMU->enablePowerOutput(XPOWERS_ALDO2);
|
||||
#endif
|
||||
dispdev.displayOn();
|
||||
dispdev.displayOn();
|
||||
enabled = true;
|
||||
@@ -951,6 +955,9 @@ void Screen::handleSetOn(bool on)
|
||||
} else {
|
||||
LOG_INFO("Turning off screen\n");
|
||||
dispdev.displayOff();
|
||||
#ifdef T_WATCH_S3
|
||||
PMU->disablePowerOutput(XPOWERS_ALDO2);
|
||||
#endif
|
||||
enabled = false;
|
||||
}
|
||||
screenOn = on;
|
||||
@@ -1239,9 +1246,9 @@ void Screen::setFrames()
|
||||
#endif
|
||||
|
||||
// We don't show the node info our our node (if we have it yet - we should)
|
||||
size_t numnodes = nodeDB.getNumNodes();
|
||||
if (numnodes > 0)
|
||||
numnodes--;
|
||||
size_t numMeshNodes = nodeDB.getNumMeshNodes();
|
||||
if (numMeshNodes > 0)
|
||||
numMeshNodes--;
|
||||
|
||||
size_t numframes = 0;
|
||||
|
||||
@@ -1258,7 +1265,7 @@ void Screen::setFrames()
|
||||
LOG_DEBUG("Added modules. numframes: %d\n", numframes);
|
||||
|
||||
// If we have a critical fault, show it first
|
||||
if (myNodeInfo.error_code)
|
||||
if (error_code)
|
||||
normalFrames[numframes++] = drawCriticalFaultFrame;
|
||||
|
||||
// If we have a text message - show it next, unless it's a phone message and we aren't using any special modules
|
||||
@@ -1272,7 +1279,7 @@ void Screen::setFrames()
|
||||
|
||||
// then all the nodes
|
||||
// We only show a few nodes in our scrolling list - because meshes with many nodes would have too many screens
|
||||
size_t numToShow = min(numnodes, 4U);
|
||||
size_t numToShow = min(numMeshNodes, 4U);
|
||||
for (size_t i = 0; i < numToShow; i++)
|
||||
normalFrames[numframes++] = drawNodeInfo;
|
||||
|
||||
@@ -1481,7 +1488,7 @@ void DebugInfo::drawFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16
|
||||
#ifdef ARCH_ESP32
|
||||
if (millis() - storeForwardModule->lastHeartbeat >
|
||||
(storeForwardModule->heartbeatInterval * 1200)) { // no heartbeat, overlap a bit
|
||||
#if defined(USE_EINK) || defined(ILI9341_DRIVER) || defined(ST7735_CS)
|
||||
#if defined(USE_EINK) || defined(ILI9341_DRIVER) || defined(ST7735_CS) || defined(ST7789_CS)
|
||||
display->drawFastImage(x + SCREEN_WIDTH - 14 - display->getStringWidth(ourId), y + 3 + FONT_HEIGHT_SMALL, 12, 8,
|
||||
imgQuestionL1);
|
||||
display->drawFastImage(x + SCREEN_WIDTH - 14 - display->getStringWidth(ourId), y + 11 + FONT_HEIGHT_SMALL, 12, 8,
|
||||
@@ -1491,7 +1498,7 @@ void DebugInfo::drawFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16
|
||||
imgQuestion);
|
||||
#endif
|
||||
} else {
|
||||
#if defined(USE_EINK) || defined(ILI9341_DRIVER) || defined(ST7735_CS)
|
||||
#if defined(USE_EINK) || defined(ILI9341_DRIVER) || defined(ST7735_CS) || defined(ST7789_CS)
|
||||
display->drawFastImage(x + SCREEN_WIDTH - 18 - display->getStringWidth(ourId), y + 3 + FONT_HEIGHT_SMALL, 16, 8,
|
||||
imgSFL1);
|
||||
display->drawFastImage(x + SCREEN_WIDTH - 18 - display->getStringWidth(ourId), y + 11 + FONT_HEIGHT_SMALL, 16, 8,
|
||||
@@ -1503,7 +1510,7 @@ void DebugInfo::drawFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16
|
||||
}
|
||||
#endif
|
||||
} else {
|
||||
#if defined(USE_EINK) || defined(ILI9341_DRIVER) || defined(ST7735_CS)
|
||||
#if defined(USE_EINK) || defined(ILI9341_DRIVER) || defined(ST7735_CS) || defined(ST7789_CS)
|
||||
display->drawFastImage(x + SCREEN_WIDTH - 14 - display->getStringWidth(ourId), y + 3 + FONT_HEIGHT_SMALL, 12, 8,
|
||||
imgInfoL1);
|
||||
display->drawFastImage(x + SCREEN_WIDTH - 14 - display->getStringWidth(ourId), y + 11 + FONT_HEIGHT_SMALL, 12, 8,
|
||||
@@ -1788,7 +1795,7 @@ void DebugInfo::drawFrameSettings(OLEDDisplay *display, OLEDDisplayUiState *stat
|
||||
heartbeat = !heartbeat;
|
||||
#endif
|
||||
}
|
||||
// adjust Brightness cycle trough 1 to 254 as long as attachDuringLongPress is true
|
||||
// adjust Brightness cycle through 1 to 254 as long as attachDuringLongPress is true
|
||||
void Screen::adjustBrightness()
|
||||
{
|
||||
if (!useDisplay)
|
||||
@@ -1849,4 +1856,4 @@ int Screen::handleUIFrameEvent(const UIFrameEvent *event)
|
||||
} // namespace graphics
|
||||
#else
|
||||
graphics::Screen::Screen(ScanI2C::DeviceAddress, meshtastic_Config_DisplayConfig_OledType, OLEDDISPLAY_GEOMETRY) {}
|
||||
#endif // HAS_SCREEN
|
||||
#endif // HAS_SCREEN
|
||||
@@ -313,6 +313,13 @@ class Screen : public concurrency::OSThread
|
||||
|
||||
void setWelcomeFrames();
|
||||
|
||||
void getTouch(int *x, int *y)
|
||||
{
|
||||
#if defined(ST7735_CS) || defined(ILI9341_DRIVER) || defined(ST7789_CS)
|
||||
dispdev.getTouch(x, y);
|
||||
#endif
|
||||
};
|
||||
|
||||
protected:
|
||||
/// Updates the UI.
|
||||
//
|
||||
@@ -380,7 +387,7 @@ class Screen : public concurrency::OSThread
|
||||
SH1106Wire dispdev;
|
||||
#elif defined(USE_SSD1306)
|
||||
SSD1306Wire dispdev;
|
||||
#elif defined(ST7735_CS) || defined(ILI9341_DRIVER)
|
||||
#elif defined(ST7735_CS) || defined(ILI9341_DRIVER) || defined(ST7789_CS)
|
||||
TFTDisplay dispdev;
|
||||
#elif defined(USE_EINK)
|
||||
EInkDisplay dispdev;
|
||||
@@ -394,4 +401,4 @@ class Screen : public concurrency::OSThread
|
||||
};
|
||||
|
||||
} // namespace graphics
|
||||
#endif
|
||||
#endif
|
||||
@@ -1,12 +1,233 @@
|
||||
#include "configuration.h"
|
||||
|
||||
#if defined(ST7735_CS) || defined(ILI9341_DRIVER)
|
||||
#ifndef TFT_BACKLIGHT_ON
|
||||
#define TFT_BACKLIGHT_ON HIGH
|
||||
#endif
|
||||
|
||||
// convert 24-bit color to 16-bit (56K)
|
||||
#define COLOR565(r, g, b) (((r & 0xF8) << 8) | ((g & 0xFC) << 3) | ((b & 0xF8) >> 3))
|
||||
#define TFT_MESH COLOR565(0x67, 0xEA, 0x94)
|
||||
|
||||
#if defined(ST7735S)
|
||||
#include <LovyanGFX.hpp> // Graphics and font library for ST7735 driver chip
|
||||
|
||||
#if defined(ST7735_BACKLIGHT_EN) && !defined(TFT_BL)
|
||||
#define TFT_BL ST7735_BACKLIGHT_EN
|
||||
#endif
|
||||
|
||||
class LGFX : public lgfx::LGFX_Device
|
||||
{
|
||||
lgfx::Panel_ST7735S _panel_instance;
|
||||
lgfx::Bus_SPI _bus_instance;
|
||||
lgfx::Light_PWM _light_instance;
|
||||
|
||||
public:
|
||||
LGFX(void)
|
||||
{
|
||||
{
|
||||
auto cfg = _bus_instance.config();
|
||||
|
||||
// configure SPI
|
||||
cfg.spi_host = ST7735_SPI_HOST; // ESP32-S2,S3,C3 : SPI2_HOST or SPI3_HOST / ESP32 : VSPI_HOST or HSPI_HOST
|
||||
cfg.spi_mode = 0;
|
||||
cfg.freq_write = SPI_FREQUENCY; // SPI clock for transmission (up to 80MHz, rounded to the value obtained by dividing
|
||||
// 80MHz by an integer)
|
||||
cfg.freq_read = SPI_READ_FREQUENCY; // SPI clock when receiving
|
||||
cfg.spi_3wire = false; // Set to true if reception is done on the MOSI pin
|
||||
cfg.use_lock = true; // Set to true to use transaction locking
|
||||
cfg.dma_channel = SPI_DMA_CH_AUTO; // SPI_DMA_CH_AUTO; // Set DMA channel to use (0=not use DMA / 1=1ch / 2=ch /
|
||||
// SPI_DMA_CH_AUTO=auto setting)
|
||||
cfg.pin_sclk = ST7735_SCK; // Set SPI SCLK pin number
|
||||
cfg.pin_mosi = ST7735_SDA; // Set SPI MOSI pin number
|
||||
cfg.pin_miso = ST7735_MISO; // Set SPI MISO pin number (-1 = disable)
|
||||
cfg.pin_dc = ST7735_RS; // Set SPI DC pin number (-1 = disable)
|
||||
|
||||
_bus_instance.config(cfg); // applies the set value to the bus.
|
||||
_panel_instance.setBus(&_bus_instance); // set the bus on the panel.
|
||||
}
|
||||
|
||||
{ // Set the display panel control.
|
||||
auto cfg = _panel_instance.config(); // Gets a structure for display panel settings.
|
||||
|
||||
cfg.pin_cs = ST7735_CS; // Pin number where CS is connected (-1 = disable)
|
||||
cfg.pin_rst = ST7735_RESET; // Pin number where RST is connected (-1 = disable)
|
||||
cfg.pin_busy = ST7735_BUSY; // Pin number where BUSY is connected (-1 = disable)
|
||||
|
||||
// The following setting values are general initial values for each panel, so please comment out any
|
||||
// unknown items and try them.
|
||||
|
||||
cfg.panel_width = TFT_WIDTH; // actual displayable width
|
||||
cfg.panel_height = TFT_HEIGHT; // actual displayable height
|
||||
cfg.offset_x = TFT_OFFSET_X; // Panel offset amount in X direction
|
||||
cfg.offset_y = TFT_OFFSET_Y; // Panel offset amount in Y direction
|
||||
cfg.offset_rotation = 0; // Rotation direction value offset 0~7 (4~7 is upside down)
|
||||
cfg.dummy_read_pixel = 8; // Number of bits for dummy read before pixel readout
|
||||
cfg.dummy_read_bits = 1; // Number of bits for dummy read before non-pixel data read
|
||||
cfg.readable = true; // Set to true if data can be read
|
||||
cfg.invert = true; // Set to true if the light/darkness of the panel is reversed
|
||||
cfg.rgb_order = false; // Set to true if the panel's red and blue are swapped
|
||||
cfg.dlen_16bit =
|
||||
false; // Set to true for panels that transmit data length in 16-bit units with 16-bit parallel or SPI
|
||||
cfg.bus_shared = true; // If the bus is shared with the SD card, set to true (bus control with drawJpgFile etc.)
|
||||
|
||||
// Set the following only when the display is shifted with a driver with a variable number of pixels, such as the
|
||||
// ST7735 or ILI9163.
|
||||
cfg.memory_width = TFT_WIDTH; // Maximum width supported by the driver IC
|
||||
cfg.memory_height = TFT_HEIGHT; // Maximum height supported by the driver IC
|
||||
_panel_instance.config(cfg);
|
||||
}
|
||||
|
||||
// Set the backlight control
|
||||
{
|
||||
auto cfg = _light_instance.config(); // Gets a structure for backlight settings.
|
||||
|
||||
cfg.pin_bl = ST7735_BL; // Pin number to which the backlight is connected
|
||||
cfg.invert = true; // true to invert the brightness of the backlight
|
||||
// cfg.freq = 44100; // PWM frequency of backlight
|
||||
// cfg.pwm_channel = 1; // PWM channel number to use
|
||||
|
||||
_light_instance.config(cfg);
|
||||
_panel_instance.setLight(&_light_instance); // Set the backlight on the panel.
|
||||
}
|
||||
|
||||
setPanel(&_panel_instance);
|
||||
}
|
||||
};
|
||||
|
||||
static LGFX tft;
|
||||
|
||||
#elif defined(ST7789_CS)
|
||||
#include <LovyanGFX.hpp> // Graphics and font library for ST7735 driver chip
|
||||
|
||||
#if defined(ST7789_BACKLIGHT_EN) && !defined(TFT_BL)
|
||||
#define TFT_BL ST7789_BACKLIGHT_EN
|
||||
#endif
|
||||
|
||||
class LGFX : public lgfx::LGFX_Device
|
||||
{
|
||||
lgfx::Panel_ST7789 _panel_instance;
|
||||
lgfx::Bus_SPI _bus_instance;
|
||||
lgfx::Light_PWM _light_instance;
|
||||
#ifdef T_WATCH_S3
|
||||
lgfx::Touch_FT5x06 _touch_instance;
|
||||
#else
|
||||
lgfx::Touch_GT911 _touch_instance;
|
||||
#endif
|
||||
|
||||
public:
|
||||
LGFX(void)
|
||||
{
|
||||
{
|
||||
auto cfg = _bus_instance.config();
|
||||
|
||||
// SPI
|
||||
cfg.spi_host = ST7789_SPI_HOST;
|
||||
cfg.spi_mode = 0;
|
||||
cfg.freq_write = SPI_FREQUENCY; // SPI clock for transmission (up to 80MHz, rounded to the value obtained by dividing
|
||||
// 80MHz by an integer)
|
||||
cfg.freq_read = SPI_READ_FREQUENCY; // SPI clock when receiving
|
||||
cfg.spi_3wire = false; // Set to true if reception is done on the MOSI pin
|
||||
cfg.use_lock = true; // Set to true to use transaction locking
|
||||
cfg.dma_channel = SPI_DMA_CH_AUTO; // SPI_DMA_CH_AUTO; // Set DMA channel to use (0=not use DMA / 1=1ch / 2=ch /
|
||||
cfg.pin_sclk = ST7789_SCK; // Set SPI SCLK pin number
|
||||
cfg.pin_mosi = ST7789_SDA; // Set SPI MOSI pin number
|
||||
cfg.pin_miso = ST7789_MISO; // Set SPI MISO pin number (-1 = disable)
|
||||
cfg.pin_dc = ST7789_RS; // Set SPI DC pin number (-1 = disable)
|
||||
|
||||
_bus_instance.config(cfg); // applies the set value to the bus.
|
||||
_panel_instance.setBus(&_bus_instance); // set the bus on the panel.
|
||||
}
|
||||
|
||||
{ // Set the display panel control.
|
||||
auto cfg = _panel_instance.config(); // Gets a structure for display panel settings.
|
||||
|
||||
cfg.pin_cs = ST7789_CS; // Pin number where CS is connected (-1 = disable)
|
||||
cfg.pin_rst = -1; // Pin number where RST is connected (-1 = disable)
|
||||
cfg.pin_busy = -1; // Pin number where BUSY is connected (-1 = disable)
|
||||
|
||||
// The following setting values are general initial values for each panel, so please comment out any
|
||||
// unknown items and try them.
|
||||
|
||||
cfg.panel_width = TFT_WIDTH; // actual displayable width
|
||||
cfg.panel_height = TFT_HEIGHT; // actual displayable height
|
||||
cfg.offset_x = TFT_OFFSET_X; // Panel offset amount in X direction
|
||||
cfg.offset_y = TFT_OFFSET_Y; // Panel offset amount in Y direction
|
||||
cfg.offset_rotation = 0; // Rotation direction value offset 0~7 (4~7 is mirrored)
|
||||
cfg.dummy_read_pixel = 9; // Number of bits for dummy read before pixel readout
|
||||
cfg.dummy_read_bits = 1; // Number of bits for dummy read before non-pixel data read
|
||||
cfg.readable = true; // Set to true if data can be read
|
||||
cfg.invert = true; // Set to true if the light/darkness of the panel is reversed
|
||||
cfg.rgb_order = false; // Set to true if the panel's red and blue are swapped
|
||||
cfg.dlen_16bit =
|
||||
false; // Set to true for panels that transmit data length in 16-bit units with 16-bit parallel or SPI
|
||||
cfg.bus_shared = true; // If the bus is shared with the SD card, set to true (bus control with drawJpgFile etc.)
|
||||
|
||||
// Set the following only when the display is shifted with a driver with a variable number of pixels, such as the
|
||||
// ST7735 or ILI9163.
|
||||
// cfg.memory_width = TFT_WIDTH; // Maximum width supported by the driver IC
|
||||
// cfg.memory_height = TFT_HEIGHT; // Maximum height supported by the driver IC
|
||||
_panel_instance.config(cfg);
|
||||
}
|
||||
|
||||
// Set the backlight control. (delete if not necessary)
|
||||
{
|
||||
auto cfg = _light_instance.config(); // Gets a structure for backlight settings.
|
||||
|
||||
cfg.pin_bl = ST7789_BL; // Pin number to which the backlight is connected
|
||||
cfg.invert = true; // true to invert the brightness of the backlight
|
||||
// cfg.pwm_channel = 0;
|
||||
|
||||
_light_instance.config(cfg);
|
||||
_panel_instance.setLight(&_light_instance); // Set the backlight on the panel.
|
||||
}
|
||||
|
||||
// Configure settings for touch screen control.
|
||||
{
|
||||
auto cfg = _touch_instance.config();
|
||||
|
||||
cfg.pin_cs = -1;
|
||||
cfg.x_min = 0;
|
||||
cfg.x_max = TFT_HEIGHT - 1;
|
||||
cfg.y_min = 0;
|
||||
cfg.y_max = TFT_WIDTH - 1;
|
||||
cfg.pin_int = SCREEN_TOUCH_INT;
|
||||
cfg.bus_shared = true;
|
||||
cfg.offset_rotation = 0;
|
||||
// cfg.freq = 2500000;
|
||||
|
||||
// I2C
|
||||
cfg.i2c_port = 1;
|
||||
cfg.i2c_addr = TOUCH_SLAVE_ADDRESS;
|
||||
#ifdef SCREEN_TOUCH_USE_I2C1
|
||||
cfg.pin_sda = I2C_SDA1;
|
||||
cfg.pin_scl = I2C_SCL1;
|
||||
#else
|
||||
cfg.pin_sda = I2C_SDA;
|
||||
cfg.pin_scl = I2C_SCL;
|
||||
#endif
|
||||
cfg.freq = 400000;
|
||||
|
||||
_touch_instance.config(cfg);
|
||||
_panel_instance.setTouch(&_touch_instance);
|
||||
}
|
||||
|
||||
setPanel(&_panel_instance); // Sets the panel to use.
|
||||
}
|
||||
};
|
||||
|
||||
static LGFX tft;
|
||||
|
||||
#elif defined(ST7735_CS) || defined(ILI9341_DRIVER)
|
||||
#include <TFT_eSPI.h> // Graphics and font library for ILI9341 driver chip
|
||||
|
||||
static TFT_eSPI tft = TFT_eSPI(); // Invoke library, pins defined in User_Setup.h
|
||||
|
||||
#endif
|
||||
|
||||
#if defined(ST7735_CS) || defined(ST7789_CS) || defined(ILI9341_DRIVER)
|
||||
#include "SPILock.h"
|
||||
#include "TFTDisplay.h"
|
||||
#include <SPI.h>
|
||||
#include <TFT_eSPI.h> // Graphics and font library for ST7735 driver chip
|
||||
|
||||
static TFT_eSPI tft = TFT_eSPI(); // Invoke library, pins defined in User_Setup.h
|
||||
|
||||
TFTDisplay::TFTDisplay(uint8_t address, int sda, int scl, OLEDDISPLAY_GEOMETRY geometry, HW_I2C i2cBus)
|
||||
{
|
||||
@@ -30,7 +251,7 @@ void TFTDisplay::display(void)
|
||||
auto isset = buffer[x + (y / 8) * displayWidth] & (1 << (y & 7));
|
||||
auto dblbuf_isset = buffer_back[x + (y / 8) * displayWidth] & (1 << (y & 7));
|
||||
if (isset != dblbuf_isset) {
|
||||
tft.drawPixel(x, y, isset ? TFT_WHITE : TFT_BLACK);
|
||||
tft.drawPixel(x, y, isset ? TFT_MESH : TFT_BLACK);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -46,8 +267,31 @@ void TFTDisplay::display(void)
|
||||
// Send a command to the display (low level function)
|
||||
void TFTDisplay::sendCommand(uint8_t com)
|
||||
{
|
||||
(void)com;
|
||||
// Drop all commands to device (we just update the buffer)
|
||||
// handle display on/off directly
|
||||
switch (com) {
|
||||
case DISPLAYON: {
|
||||
#if defined(TFT_BL) && defined(TFT_BACKLIGHT_ON)
|
||||
digitalWrite(TFT_BL, TFT_BACKLIGHT_ON);
|
||||
#endif
|
||||
#ifdef VTFT_CTRL
|
||||
digitalWrite(VTFT_CTRL, LOW);
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
case DISPLAYOFF: {
|
||||
#if defined(TFT_BL) && defined(TFT_BACKLIGHT_ON)
|
||||
digitalWrite(TFT_BL, !TFT_BACKLIGHT_ON);
|
||||
#endif
|
||||
#ifdef VTFT_CTRL
|
||||
digitalWrite(VTFT_CTRL, HIGH);
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
// Drop all other commands to device (we just update the buffer)
|
||||
}
|
||||
|
||||
void TFTDisplay::setDetected(uint8_t detected)
|
||||
@@ -62,23 +306,28 @@ bool TFTDisplay::connect()
|
||||
LOG_INFO("Doing TFT init\n");
|
||||
|
||||
#ifdef TFT_BL
|
||||
digitalWrite(TFT_BL, HIGH);
|
||||
digitalWrite(TFT_BL, TFT_BACKLIGHT_ON);
|
||||
pinMode(TFT_BL, OUTPUT);
|
||||
#endif
|
||||
|
||||
#ifdef ST7735_BACKLIGHT_EN
|
||||
digitalWrite(ST7735_BACKLIGHT_EN, HIGH);
|
||||
pinMode(ST7735_BACKLIGHT_EN, OUTPUT);
|
||||
#endif
|
||||
tft.init();
|
||||
#ifdef M5STACK
|
||||
tft.setRotation(1); // M5Stack has the TFT in landscape
|
||||
#if defined(M5STACK) || defined(T_DECK)
|
||||
tft.setRotation(1); // M5Stack/T-Deck have the TFT in landscape
|
||||
#elif defined(T_WATCH_S3)
|
||||
tft.setRotation(0); // T-Watch S3 has the TFT in portrait
|
||||
#else
|
||||
tft.setRotation(3); // Orient horizontal and wide underneath the silkscreen name label
|
||||
#endif
|
||||
tft.fillScreen(TFT_BLACK);
|
||||
// tft.drawRect(0, 0, 40, 10, TFT_PURPLE); // wide rectangle in upper left
|
||||
return true;
|
||||
}
|
||||
|
||||
// Get touch coords from the display
|
||||
void TFTDisplay::getTouch(int *x, int *y)
|
||||
{
|
||||
#ifndef M5STACK
|
||||
tft.getTouch(x, y);
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -7,7 +7,6 @@
|
||||
*
|
||||
* Remaining TODO:
|
||||
* optimize display() to only draw changed pixels (see other OLED subclasses for examples)
|
||||
* implement displayOn/displayOff to turn off the TFT device (and backlight)
|
||||
* Use the fast NRF52 SPI API rather than the slow standard arduino version
|
||||
*
|
||||
* turn radio back on - currently with both on spi bus is fucked? or are we leaving chip select asserted?
|
||||
@@ -29,6 +28,8 @@ class TFTDisplay : public OLEDDisplay
|
||||
*/
|
||||
void setDetected(uint8_t detected);
|
||||
|
||||
void getTouch(int *x, int *y);
|
||||
|
||||
protected:
|
||||
// the header size of the buffer used, e.g. for the SPI command header
|
||||
virtual int getBufferOffset(void) override { return 0; }
|
||||
@@ -38,4 +39,4 @@ class TFTDisplay : public OLEDDisplay
|
||||
|
||||
// Connect to the display
|
||||
virtual bool connect() override;
|
||||
};
|
||||
};
|
||||
@@ -14,7 +14,7 @@ const uint8_t imgUser[] PROGMEM = {0x3C, 0x42, 0x99, 0xA5, 0xA5, 0x99, 0x42, 0x3
|
||||
const uint8_t imgPositionEmpty[] PROGMEM = {0x20, 0x30, 0x28, 0x24, 0x42, 0xFF};
|
||||
const uint8_t imgPositionSolid[] PROGMEM = {0x20, 0x30, 0x38, 0x3C, 0x7E, 0xFF};
|
||||
|
||||
#if defined(USE_EINK) || defined(ILI9341_DRIVER) || defined(ST7735_CS)
|
||||
#if defined(USE_EINK) || defined(ILI9341_DRIVER) || defined(ST7735_CS) || defined(ST7789_CS)
|
||||
const uint8_t imgQuestionL1[] PROGMEM = {0xff, 0x01, 0x01, 0x32, 0x7b, 0x49, 0x49, 0x6f, 0x26, 0x01, 0x01, 0xff};
|
||||
const uint8_t imgQuestionL2[] PROGMEM = {0x0f, 0x08, 0x08, 0x08, 0x06, 0x0f, 0x0f, 0x06, 0x08, 0x08, 0x08, 0x0f};
|
||||
const uint8_t imgInfoL1[] PROGMEM = {0xff, 0x01, 0x01, 0x01, 0x1e, 0x7f, 0x1e, 0x01, 0x01, 0x01, 0x01, 0xff};
|
||||
|
||||
90
src/main.cpp
90
src/main.cpp
@@ -19,6 +19,7 @@
|
||||
#include "detect/ScanI2CTwoWire.h"
|
||||
#include "detect/axpDebug.h"
|
||||
#include "detect/einkScan.h"
|
||||
#include "graphics/RAKled.h"
|
||||
#include "graphics/Screen.h"
|
||||
#include "main.h"
|
||||
#include "mesh/generated/meshtastic/config.pb.h"
|
||||
@@ -46,13 +47,12 @@ NRF52Bluetooth *nrf52Bluetooth;
|
||||
|
||||
#if HAS_WIFI
|
||||
#include "mesh/api/WiFiServerAPI.h"
|
||||
#include "mqtt/MQTT.h"
|
||||
#endif
|
||||
|
||||
#if HAS_ETHERNET
|
||||
#include "mesh/api/ethServerAPI.h"
|
||||
#include "mqtt/MQTT.h"
|
||||
#endif
|
||||
#include "mqtt/MQTT.h"
|
||||
|
||||
#include "LLCC68Interface.h"
|
||||
#include "RF95Interface.h"
|
||||
@@ -110,6 +110,11 @@ ScanI2C::FoundDevice rgb_found = ScanI2C::FoundDevice(ScanI2C::DeviceType::NONE,
|
||||
ATECCX08A atecc;
|
||||
#endif
|
||||
|
||||
#ifdef T_WATCH_S3
|
||||
Adafruit_DRV2605 drv;
|
||||
#endif
|
||||
bool isVibrating = false;
|
||||
|
||||
bool eink_found = true;
|
||||
|
||||
uint32_t serialSinceMsec;
|
||||
@@ -169,7 +174,7 @@ SPISettings spiSettings(4000000, MSBFIRST, SPI_MODE0);
|
||||
RadioInterface *rIf = NULL;
|
||||
|
||||
/**
|
||||
* Some platforms (nrf52) might provide an alterate version that supresses calling delay from sleep.
|
||||
* Some platforms (nrf52) might provide an alterate version that suppresses calling delay from sleep.
|
||||
*/
|
||||
__attribute__((weak, noinline)) bool loopCanSleep()
|
||||
{
|
||||
@@ -214,6 +219,16 @@ void setup()
|
||||
digitalWrite(VEXT_ENABLE, 0); // turn on the display power
|
||||
#endif
|
||||
|
||||
#ifdef VGNSS_CTRL
|
||||
pinMode(VGNSS_CTRL, OUTPUT);
|
||||
digitalWrite(VGNSS_CTRL, LOW);
|
||||
#endif
|
||||
|
||||
#if defined(VTFT_CTRL)
|
||||
pinMode(VTFT_CTRL, OUTPUT);
|
||||
digitalWrite(VTFT_CTRL, LOW);
|
||||
#endif
|
||||
|
||||
#ifdef RESET_OLED
|
||||
pinMode(RESET_OLED, OUTPUT);
|
||||
digitalWrite(RESET_OLED, 1);
|
||||
@@ -240,6 +255,19 @@ void setup()
|
||||
|
||||
fsInit();
|
||||
|
||||
#if defined(_SEEED_XIAO_NRF52840_SENSE_H_)
|
||||
|
||||
pinMode(CHARGE_LED, INPUT); // sets to detect if charge LED is on or off to see if USB is plugged in
|
||||
|
||||
pinMode(HICHG, OUTPUT);
|
||||
digitalWrite(HICHG, LOW); // 100 mA charging current if set to LOW and 50mA (actually about 20mA) if set to HIGH
|
||||
|
||||
pinMode(BAT_READ, OUTPUT);
|
||||
digitalWrite(BAT_READ, LOW); // This is pin P0_14 = 14 and by pullling low to GND it provices path to read on pin 32 (P0,31)
|
||||
// PIN_VBAT the voltage from divider on XIAO board
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef I2C_SDA1
|
||||
Wire1.begin(I2C_SDA1, I2C_SCL1);
|
||||
#endif
|
||||
@@ -260,9 +288,16 @@ void setup()
|
||||
#endif
|
||||
|
||||
#ifdef RAK4630
|
||||
#ifdef PIN_3V3_EN
|
||||
// We need to enable 3.3V periphery in order to scan it
|
||||
pinMode(PIN_3V3_EN, OUTPUT);
|
||||
digitalWrite(PIN_3V3_EN, HIGH);
|
||||
#endif
|
||||
#ifndef USE_EINK
|
||||
// RAK-12039 set pin for Air quality sensor
|
||||
pinMode(AQ_SET_PIN, OUTPUT);
|
||||
digitalWrite(AQ_SET_PIN, HIGH);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// Currently only the tbeam has a PMU
|
||||
@@ -345,15 +380,25 @@ void setup()
|
||||
|
||||
pmu_found = i2cScanner->exists(ScanI2C::DeviceType::PMU_AXP192_AXP2101);
|
||||
|
||||
/*
|
||||
* There are a bunch of sensors that have no further logic than to be found and stuffed into the
|
||||
* nodeTelemetrySensorsMap singleton. This wraps that logic in a temporary scope to declare the temporary field
|
||||
* "found".
|
||||
*/
|
||||
/*
|
||||
* There are a bunch of sensors that have no further logic than to be found and stuffed into the
|
||||
* nodeTelemetrySensorsMap singleton. This wraps that logic in a temporary scope to declare the temporary field
|
||||
* "found".
|
||||
*/
|
||||
|
||||
// Only one supported RGB LED currently
|
||||
// Only one supported RGB LED currently
|
||||
#ifdef HAS_NCP5623
|
||||
rgb_found = i2cScanner->find(ScanI2C::DeviceType::NCP5623);
|
||||
|
||||
// Start the RGB LED at 50%
|
||||
|
||||
if (rgb_found.type == ScanI2C::NCP5623) {
|
||||
rgb.begin();
|
||||
rgb.setCurrent(10);
|
||||
rgb.setColor(128, 128, 128);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if !defined(ARCH_PORTDUINO) && !defined(ARCH_STM32WL)
|
||||
auto acc_info = i2cScanner->firstAccelerometer();
|
||||
accelerometer_found = acc_info.type != ScanI2C::DeviceType::NONE ? acc_info.address : accelerometer_found;
|
||||
@@ -445,10 +490,19 @@ void setup()
|
||||
|
||||
#if !defined(ARCH_PORTDUINO) && !defined(ARCH_STM32WL)
|
||||
if (acc_info.type != ScanI2C::DeviceType::NONE) {
|
||||
config.display.wake_on_tap_or_motion = true;
|
||||
moduleConfig.external_notification.enabled = true;
|
||||
accelerometerThread = new AccelerometerThread(acc_info.type);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef T_WATCH_S3
|
||||
drv.begin();
|
||||
drv.selectLibrary(1);
|
||||
// I2C trigger by sending 'go' command
|
||||
drv.setMode(DRV2605_MODE_INTTRIG);
|
||||
#endif
|
||||
|
||||
// Init our SPI controller (must be before screen and lora)
|
||||
initSPI();
|
||||
#ifdef ARCH_RP2040
|
||||
@@ -501,7 +555,7 @@ void setup()
|
||||
|
||||
// Don't call screen setup until after nodedb is setup (because we need
|
||||
// the current region name)
|
||||
#if defined(ST7735_CS) || defined(USE_EINK) || defined(ILI9341_DRIVER)
|
||||
#if defined(ST7735_CS) || defined(USE_EINK) || defined(ILI9341_DRIVER) || defined(ST7789_CS)
|
||||
screen->setup();
|
||||
#else
|
||||
if (screen_found.port != ScanI2C::I2CPort::NO_I2C)
|
||||
@@ -638,9 +692,7 @@ void setup()
|
||||
}
|
||||
}
|
||||
|
||||
#if HAS_WIFI || HAS_ETHERNET
|
||||
mqttInit();
|
||||
#endif
|
||||
|
||||
#ifndef ARCH_PORTDUINO
|
||||
// Initialize Wifi
|
||||
@@ -667,12 +719,10 @@ void setup()
|
||||
else {
|
||||
router->addInterface(rIf);
|
||||
|
||||
// Calculate and save the bit rate to myNodeInfo
|
||||
// TODO: This needs to be added what ever method changes the channel from the phone.
|
||||
myNodeInfo.bitrate =
|
||||
(float(meshtastic_Constants_DATA_PAYLOAD_LEN) / (float(rIf->getPacketTime(meshtastic_Constants_DATA_PAYLOAD_LEN)))) *
|
||||
1000;
|
||||
LOG_DEBUG("myNodeInfo.bitrate = %f bytes / sec\n", myNodeInfo.bitrate);
|
||||
// Log bit rate to debug output
|
||||
LOG_DEBUG("LoRA bitrate = %f bytes / sec\n", (float(meshtastic_Constants_DATA_PAYLOAD_LEN) /
|
||||
(float(rIf->getPacketTime(meshtastic_Constants_DATA_PAYLOAD_LEN)))) *
|
||||
1000);
|
||||
}
|
||||
|
||||
// This must be _after_ service.init because we need our preferences loaded from flash to have proper timeout values
|
||||
@@ -687,13 +737,13 @@ uint32_t rebootAtMsec; // If not zero we will reboot at this time (used to reb
|
||||
uint32_t shutdownAtMsec; // If not zero we will shutdown at this time (used to shutdown from python or mobile client)
|
||||
|
||||
// If a thread does something that might need for it to be rescheduled ASAP it can set this flag
|
||||
// This will supress the current delay and instead try to run ASAP.
|
||||
// This will suppress the current delay and instead try to run ASAP.
|
||||
bool runASAP;
|
||||
|
||||
extern meshtastic_DeviceMetadata getDeviceMetadata()
|
||||
{
|
||||
meshtastic_DeviceMetadata deviceMetadata;
|
||||
strncpy(deviceMetadata.firmware_version, myNodeInfo.firmware_version, 18);
|
||||
strncpy(deviceMetadata.firmware_version, optstr(APP_VERSION), sizeof(deviceMetadata.firmware_version));
|
||||
deviceMetadata.device_state_version = DEVICESTATE_CUR_VER;
|
||||
deviceMetadata.canShutdown = pmu_found || HAS_CPU_SHUTDOWN;
|
||||
deviceMetadata.hasBluetooth = HAS_BLUETOOTH;
|
||||
|
||||
@@ -38,6 +38,12 @@ extern bool isUSBPowered;
|
||||
extern ATECCX08A atecc;
|
||||
#endif
|
||||
|
||||
#ifdef T_WATCH_S3
|
||||
#include <Adafruit_DRV2605.h>
|
||||
extern Adafruit_DRV2605 drv;
|
||||
#endif
|
||||
extern bool isVibrating;
|
||||
|
||||
extern int TCPPort; // set by Portduino
|
||||
|
||||
// Global Screen singleton.
|
||||
@@ -59,7 +65,7 @@ extern uint32_t shutdownAtMsec;
|
||||
extern uint32_t serialSinceMsec;
|
||||
|
||||
// If a thread does something that might need for it to be rescheduled ASAP it can set this flag
|
||||
// This will supress the current delay and instead try to run ASAP.
|
||||
// This will suppress the current delay and instead try to run ASAP.
|
||||
extern bool runASAP;
|
||||
|
||||
void nrf52Setup(), esp32Setup(), nrf52Loop(), esp32Loop(), clearBonds();
|
||||
|
||||
@@ -20,7 +20,7 @@ class Channels
|
||||
/// The index of the primary channel
|
||||
ChannelIndex primaryIndex = 0;
|
||||
|
||||
/** The channel index that was requested for sending/receving. Note: if this channel is a secondary
|
||||
/** The channel index that was requested for sending/receiving. Note: if this channel is a secondary
|
||||
channel and does not have a PSK, we will use the PSK from the primary channel. If this channel is disabled
|
||||
no sending or receiving will be allowed */
|
||||
ChannelIndex activeChannelIndex = 0;
|
||||
|
||||
@@ -21,7 +21,12 @@ bool FloodingRouter::shouldFilterReceived(const meshtastic_MeshPacket *p)
|
||||
{
|
||||
if (wasSeenRecently(p)) { // Note: this will also add a recent packet record
|
||||
printPacket("Ignoring incoming msg, because we've already seen it", p);
|
||||
Router::cancelSending(p->from, p->id); // cancel rebroadcast of this message *if* there was already one
|
||||
if (config.device.role != meshtastic_Config_DeviceConfig_Role_ROUTER &&
|
||||
config.device.role != meshtastic_Config_DeviceConfig_Role_ROUTER_CLIENT &&
|
||||
config.device.role != meshtastic_Config_DeviceConfig_Role_REPEATER) {
|
||||
// cancel rebroadcast of this message *if* there was already one, unless we're a router/repeater!
|
||||
Router::cancelSending(p->from, p->id);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -48,7 +48,7 @@ class FloodingRouter : public Router, protected PacketHistory
|
||||
/**
|
||||
* Should this incoming filter be dropped?
|
||||
*
|
||||
* Called immedately on receiption, before any further processing.
|
||||
* Called immediately on reception, before any further processing.
|
||||
* @return true to abandon the packet
|
||||
*/
|
||||
virtual bool shouldFilterReceived(const meshtastic_MeshPacket *p) override;
|
||||
|
||||
@@ -18,7 +18,7 @@ meshtastic_MeshPacket *MeshModule::currentReply;
|
||||
|
||||
MeshModule::MeshModule(const char *_name) : name(_name)
|
||||
{
|
||||
// Can't trust static initalizer order, so we check each time
|
||||
// Can't trust static initializer order, so we check each time
|
||||
if (!modules)
|
||||
modules = new std::vector<MeshModule *>();
|
||||
|
||||
@@ -39,7 +39,7 @@ meshtastic_MeshPacket *MeshModule::allocAckNak(meshtastic_Routing_Error err, Nod
|
||||
c.error_reason = err;
|
||||
c.which_variant = meshtastic_Routing_error_reason_tag;
|
||||
|
||||
// Now that we have moded sendAckNak up one level into the class heirarchy we can no longer assume we are a RoutingPlugin
|
||||
// Now that we have moded sendAckNak up one level into the class hierarchy we can no longer assume we are a RoutingPlugin
|
||||
// So we manually call pb_encode_to_bytes and specify routing port number
|
||||
// auto p = allocDataProtobuf(c);
|
||||
meshtastic_MeshPacket *p = router->allocForSending();
|
||||
@@ -169,7 +169,7 @@ void MeshModule::callPlugins(const meshtastic_MeshPacket &mp, RxSource src)
|
||||
// Note: if the message started with the local node or a module asked to ignore the request, we don't want to send a
|
||||
// no response reply
|
||||
|
||||
// No one wanted to reply to this requst, tell the requster that happened
|
||||
// No one wanted to reply to this request, tell the requster that happened
|
||||
LOG_DEBUG("No one responded, send a nak\n");
|
||||
|
||||
// SECURITY NOTE! I considered sending back a different error code if we didn't find the psk (i.e. !isDecoded)
|
||||
|
||||
@@ -47,7 +47,7 @@ typedef struct _UIFrameEvent {
|
||||
* A key concept for this is that your module should use a particular "portnum" for each message type you want to receive
|
||||
* and handle.
|
||||
*
|
||||
* Interally we use modules to implement the core meshtastic text messaging and gps position sharing features. You
|
||||
* Internally we use modules to implement the core meshtastic text messaging and gps position sharing features. You
|
||||
* can use these classes as examples for how to write your own custom module. See here: (FIXME)
|
||||
*/
|
||||
class MeshModule
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#include "NodeDB.h"
|
||||
#include "PowerFSM.h"
|
||||
#include "RTC.h"
|
||||
#include "TypeConversions.h"
|
||||
#include "main.h"
|
||||
#include "mesh-pb-constants.h"
|
||||
#include "modules/NodeInfoModule.h"
|
||||
@@ -33,7 +34,7 @@ arbitrating to select a node number and keeping the current nodedb.
|
||||
|
||||
/* Broadcast when a newly powered mesh node wants to find a node num it can use
|
||||
|
||||
The algoritm is as follows:
|
||||
The algorithm is as follows:
|
||||
* when a node starts up, it broadcasts their user and the normal flow is for all other nodes to reply with their User as well (so
|
||||
the new node can build its node db)
|
||||
* If a node ever receives a User (not just the first broadcast) message where the sender node number equals our node number, that
|
||||
@@ -51,13 +52,18 @@ FIXME in the initial proof of concept we just skip the entire want/deny flow and
|
||||
|
||||
MeshService service;
|
||||
|
||||
static MemoryDynamic<meshtastic_MqttClientProxyMessage> staticMqttClientProxyMessagePool;
|
||||
|
||||
static MemoryDynamic<meshtastic_QueueStatus> staticQueueStatusPool;
|
||||
|
||||
Allocator<meshtastic_MqttClientProxyMessage> &mqttClientProxyMessagePool = staticMqttClientProxyMessagePool;
|
||||
|
||||
Allocator<meshtastic_QueueStatus> &queueStatusPool = staticQueueStatusPool;
|
||||
|
||||
#include "Router.h"
|
||||
|
||||
MeshService::MeshService() : toPhoneQueue(MAX_RX_TOPHONE), toPhoneQueueStatusQueue(MAX_RX_TOPHONE)
|
||||
MeshService::MeshService()
|
||||
: toPhoneQueue(MAX_RX_TOPHONE), toPhoneQueueStatusQueue(MAX_RX_TOPHONE), toPhoneMqttProxyQueue(MAX_RX_TOPHONE)
|
||||
{
|
||||
lastQueueStatus = {0, 0, 16, 0};
|
||||
}
|
||||
@@ -76,7 +82,8 @@ int MeshService::handleFromRadio(const meshtastic_MeshPacket *mp)
|
||||
powerFSM.trigger(EVENT_PACKET_FOR_PHONE); // Possibly keep the node from sleeping
|
||||
|
||||
nodeDB.updateFrom(*mp); // update our DB state based off sniffing every RX packet from the radio
|
||||
if (mp->which_payload_variant == meshtastic_MeshPacket_decoded_tag && !nodeDB.getNode(mp->from)->has_user && nodeInfoModule) {
|
||||
if (mp->which_payload_variant == meshtastic_MeshPacket_decoded_tag && !nodeDB.getMeshNode(mp->from)->has_user &&
|
||||
nodeInfoModule) {
|
||||
LOG_INFO("Heard a node on channel %d we don't know, sending NodeInfo and asking for a response.\n", mp->channel);
|
||||
nodeInfoModule->sendOurNodeInfo(mp->from, true, mp->channel);
|
||||
}
|
||||
@@ -236,7 +243,8 @@ void MeshService::sendToMesh(meshtastic_MeshPacket *p, RxSource src, bool ccToPh
|
||||
|
||||
void MeshService::sendNetworkPing(NodeNum dest, bool wantReplies)
|
||||
{
|
||||
meshtastic_NodeInfo *node = nodeDB.getNode(nodeDB.getNodeNum());
|
||||
meshtastic_NodeInfoLite *node = nodeDB.getMeshNode(nodeDB.getNodeNum());
|
||||
|
||||
assert(node);
|
||||
|
||||
if (hasValidPosition(node)) {
|
||||
@@ -266,9 +274,23 @@ void MeshService::sendToPhone(meshtastic_MeshPacket *p)
|
||||
fromNum++;
|
||||
}
|
||||
|
||||
meshtastic_NodeInfo *MeshService::refreshMyNodeInfo()
|
||||
void MeshService::sendMqttMessageToClientProxy(meshtastic_MqttClientProxyMessage *m)
|
||||
{
|
||||
meshtastic_NodeInfo *node = nodeDB.getNode(nodeDB.getNodeNum());
|
||||
LOG_DEBUG("Sending mqtt message on topic '%s' to client for proxying to server\n", m->topic);
|
||||
if (toPhoneMqttProxyQueue.numFree() == 0) {
|
||||
LOG_WARN("MqttClientProxyMessagePool queue is full, discarding oldest\n");
|
||||
meshtastic_MqttClientProxyMessage *d = toPhoneMqttProxyQueue.dequeuePtr(0);
|
||||
if (d)
|
||||
releaseMqttClientProxyMessageToPool(d);
|
||||
}
|
||||
|
||||
assert(toPhoneMqttProxyQueue.enqueue(m, 0));
|
||||
fromNum++;
|
||||
}
|
||||
|
||||
meshtastic_NodeInfoLite *MeshService::refreshLocalMeshNode()
|
||||
{
|
||||
meshtastic_NodeInfoLite *node = nodeDB.getMeshNode(nodeDB.getNodeNum());
|
||||
assert(node);
|
||||
|
||||
// We might not have a position yet for our local node, in that case, at least try to send the time
|
||||
@@ -277,7 +299,7 @@ meshtastic_NodeInfo *MeshService::refreshMyNodeInfo()
|
||||
node->has_position = true;
|
||||
}
|
||||
|
||||
meshtastic_Position &position = node->position;
|
||||
meshtastic_PositionLite &position = node->position;
|
||||
|
||||
// Update our local node info with our time (even if we don't decide to update anyone else)
|
||||
node->last_heard =
|
||||
@@ -293,7 +315,7 @@ meshtastic_NodeInfo *MeshService::refreshMyNodeInfo()
|
||||
int MeshService::onGPSChanged(const meshtastic::GPSStatus *newStatus)
|
||||
{
|
||||
// Update our local node info with our position (even if we don't decide to update anyone else)
|
||||
meshtastic_NodeInfo *node = refreshMyNodeInfo();
|
||||
meshtastic_NodeInfoLite *node = refreshLocalMeshNode();
|
||||
meshtastic_Position pos = meshtastic_Position_init_default;
|
||||
|
||||
if (newStatus->getHasLock()) {
|
||||
@@ -307,12 +329,12 @@ int MeshService::onGPSChanged(const meshtastic::GPSStatus *newStatus)
|
||||
#endif
|
||||
if (config.position.fixed_position) {
|
||||
LOG_WARN("Using fixed position\n");
|
||||
pos = node->position;
|
||||
pos = ConvertToPosition(node->position);
|
||||
}
|
||||
}
|
||||
|
||||
// Finally add a fresh timestamp and battery level reading
|
||||
// I KNOW this is redundant with refreshMyNodeInfo() above, but these are
|
||||
// I KNOW this is redundant with refreshLocalMeshNode() above, but these are
|
||||
// inexpensive nonblocking calls and can be refactored in due course
|
||||
pos.time = getValidTime(RTCQualityGPS);
|
||||
|
||||
@@ -329,4 +351,4 @@ int MeshService::onGPSChanged(const meshtastic::GPSStatus *newStatus)
|
||||
bool MeshService::isToPhoneQueueEmpty()
|
||||
{
|
||||
return toPhoneQueue.isEmpty();
|
||||
}
|
||||
}
|
||||
@@ -15,6 +15,7 @@
|
||||
#endif
|
||||
|
||||
extern Allocator<meshtastic_QueueStatus> &queueStatusPool;
|
||||
extern Allocator<meshtastic_MqttClientProxyMessage> &mqttClientProxyMessagePool;
|
||||
|
||||
/**
|
||||
* Top level app for this service. keeps the mesh, the radio config and the queue of received packets.
|
||||
@@ -34,6 +35,9 @@ class MeshService
|
||||
// keep list of QueueStatus packets to be send to the phone
|
||||
PointerQueue<meshtastic_QueueStatus> toPhoneQueueStatusQueue;
|
||||
|
||||
// keep list of MqttClientProxyMessages to be send to the client for delivery
|
||||
PointerQueue<meshtastic_MqttClientProxyMessage> toPhoneMqttProxyQueue;
|
||||
|
||||
// This holds the last QueueStatus send
|
||||
meshtastic_QueueStatus lastQueueStatus;
|
||||
|
||||
@@ -67,9 +71,15 @@ class MeshService
|
||||
/// Return the next QueueStatus packet destined to the phone.
|
||||
meshtastic_QueueStatus *getQueueStatusForPhone() { return toPhoneQueueStatusQueue.dequeuePtr(0); }
|
||||
|
||||
/// Return the next MqttClientProxyMessage packet destined to the phone.
|
||||
meshtastic_MqttClientProxyMessage *getMqttClientProxyMessageForPhone() { return toPhoneMqttProxyQueue.dequeuePtr(0); }
|
||||
|
||||
// Release QueueStatus packet to pool
|
||||
void releaseQueueStatusToPool(meshtastic_QueueStatus *p) { queueStatusPool.release(p); }
|
||||
|
||||
// Release MqttClientProxyMessage packet to pool
|
||||
void releaseMqttClientProxyMessageToPool(meshtastic_MqttClientProxyMessage *p) { mqttClientProxyMessagePool.release(p); }
|
||||
|
||||
/**
|
||||
* Given a ToRadio buffer parse it and properly handle it (setup radio, owner or send packet into the mesh)
|
||||
* Called by PhoneAPI.handleToRadio. Note: p is a scratch buffer, this function is allowed to write to it but it can not keep
|
||||
@@ -98,16 +108,19 @@ class MeshService
|
||||
bool cancelSending(PacketId id);
|
||||
|
||||
/// Pull the latest power and time info into my nodeinfo
|
||||
meshtastic_NodeInfo *refreshMyNodeInfo();
|
||||
meshtastic_NodeInfoLite *refreshLocalMeshNode();
|
||||
|
||||
/// Send a packet to the phone
|
||||
void sendToPhone(meshtastic_MeshPacket *p);
|
||||
|
||||
/// Send an MQTT message to the phone for client proxying
|
||||
void sendMqttMessageToClientProxy(meshtastic_MqttClientProxyMessage *m);
|
||||
|
||||
bool isToPhoneQueueEmpty();
|
||||
|
||||
private:
|
||||
/// Called when our gps position has changed - updates nodedb and sends Location message out into the mesh
|
||||
/// returns 0 to allow futher processing
|
||||
/// returns 0 to allow further processing
|
||||
int onGPSChanged(const meshtastic::GPSStatus *arg);
|
||||
|
||||
/// Handle a packet that just arrived from the radio. This method does _ReliableRouternot_ free the provided packet. If it
|
||||
@@ -118,4 +131,4 @@ class MeshService
|
||||
ErrorCode sendQueueStatusToPhone(const meshtastic_QueueStatus &qs, ErrorCode res, uint32_t mesh_packet_id);
|
||||
};
|
||||
|
||||
extern MeshService service;
|
||||
extern MeshService service;
|
||||
@@ -13,7 +13,7 @@ typedef uint32_t PacketId; // A packet sequence number
|
||||
#define ERRNO_OK 0
|
||||
#define ERRNO_NO_INTERFACES 33
|
||||
#define ERRNO_UNKNOWN 32 // pick something that doesn't conflict with RH_ROUTER_ERROR_UNABLE_TO_DELIVER
|
||||
#define ERRNO_DISABLED 34 // the itnerface is disabled
|
||||
#define ERRNO_DISABLED 34 // the interface is disabled
|
||||
|
||||
/*
|
||||
* Source of a received message
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
#include "PowerFSM.h"
|
||||
#include "RTC.h"
|
||||
#include "Router.h"
|
||||
#include "TypeConversions.h"
|
||||
#include "error.h"
|
||||
#include "main.h"
|
||||
#include "mesh-pb-constants.h"
|
||||
@@ -55,10 +56,18 @@ extern void getMacAddr(uint8_t *dmac);
|
||||
* we use !macaddr (no colons).
|
||||
*/
|
||||
meshtastic_User &owner = devicestate.owner;
|
||||
meshtastic_Position localPosition = meshtastic_Position_init_default;
|
||||
meshtastic_CriticalErrorCode error_code =
|
||||
meshtastic_CriticalErrorCode_NONE; // For the error code, only show values from this boot (discard value from flash)
|
||||
uint32_t error_address = 0;
|
||||
|
||||
static uint8_t ourMacAddr[6];
|
||||
|
||||
NodeDB::NodeDB() : nodes(devicestate.node_db), numNodes(&devicestate.node_db_count) {}
|
||||
NodeDB::NodeDB()
|
||||
: nodes(devicestate.node_db), numNodes(&devicestate.node_db_count), meshNodes(devicestate.node_db_lite),
|
||||
numMeshNodes(&devicestate.node_db_lite_count)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Most (but not always) of the time we want to treat packets 'from' the local phone (where from == 0), as if they originated on
|
||||
@@ -129,7 +138,7 @@ bool NodeDB::factoryReset()
|
||||
// third, write everything to disk
|
||||
saveToDisk();
|
||||
#ifdef ARCH_ESP32
|
||||
// This will erase what's in NVS including ssl keys, persistant variables and ble pairing
|
||||
// This will erase what's in NVS including ssl keys, persistent variables and ble pairing
|
||||
nvs_flash_erase();
|
||||
#endif
|
||||
#ifdef ARCH_NRF52
|
||||
@@ -156,6 +165,7 @@ void NodeDB::installDefaultConfig()
|
||||
config.has_network = true;
|
||||
config.has_bluetooth = true;
|
||||
config.device.rebroadcast_mode = meshtastic_Config_DeviceConfig_RebroadcastMode_ALL;
|
||||
|
||||
config.lora.sx126x_rx_boosted_gain = false;
|
||||
config.lora.tx_enabled =
|
||||
true; // FIXME: maybe false in the future, and setting region to enable it. (unset region forces it off)
|
||||
@@ -175,7 +185,7 @@ void NodeDB::installDefaultConfig()
|
||||
// FIXME: Default to bluetooth capability of platform as default
|
||||
config.bluetooth.enabled = true;
|
||||
config.bluetooth.fixed_pin = defaultBLEPin;
|
||||
#if defined(ST7735_CS) || defined(USE_EINK) || defined(ILI9341_DRIVER)
|
||||
#if defined(ST7735_CS) || defined(USE_EINK) || defined(ILI9341_DRIVER) || defined(ST7789_CS)
|
||||
bool hasScreen = true;
|
||||
#else
|
||||
bool hasScreen = screen_found.port != ScanI2C::I2CPort::NO_I2C;
|
||||
@@ -186,6 +196,11 @@ void NodeDB::installDefaultConfig()
|
||||
config.position.position_flags =
|
||||
(meshtastic_Config_PositionConfig_PositionFlags_ALTITUDE | meshtastic_Config_PositionConfig_PositionFlags_ALTITUDE_MSL);
|
||||
|
||||
#ifdef T_WATCH_S3
|
||||
config.display.screen_on_secs = 30;
|
||||
config.display.wake_on_tap_or_motion = true;
|
||||
#endif
|
||||
|
||||
initConfigIntervals();
|
||||
}
|
||||
|
||||
@@ -216,6 +231,19 @@ void NodeDB::installDefaultModuleConfig()
|
||||
moduleConfig.has_store_forward = true;
|
||||
moduleConfig.has_telemetry = true;
|
||||
moduleConfig.has_external_notification = true;
|
||||
#if defined(RAK4630) || defined(RAK11310)
|
||||
// Default to RAK led pin 2 (blue)
|
||||
moduleConfig.external_notification.enabled = true;
|
||||
moduleConfig.external_notification.output = PIN_LED2;
|
||||
moduleConfig.external_notification.active = true;
|
||||
moduleConfig.external_notification.alert_message = true;
|
||||
moduleConfig.external_notification.output_ms = 1000;
|
||||
moduleConfig.external_notification.nag_timeout = 60;
|
||||
#endif
|
||||
#ifdef T_WATCH_S3
|
||||
// Don't worry about the other settings, we'll use the DRV2056 behavior for notifications
|
||||
moduleConfig.external_notification.enabled = true;
|
||||
#endif
|
||||
moduleConfig.has_canned_message = true;
|
||||
|
||||
strncpy(moduleConfig.mqtt.address, default_mqtt_address, sizeof(moduleConfig.mqtt.address));
|
||||
@@ -258,6 +286,9 @@ void NodeDB::resetNodes()
|
||||
{
|
||||
devicestate.node_db_count = 0;
|
||||
memset(devicestate.node_db, 0, sizeof(devicestate.node_db));
|
||||
|
||||
devicestate.node_db_lite_count = 0;
|
||||
memset(devicestate.node_db_lite, 0, sizeof(devicestate.node_db_lite));
|
||||
saveDeviceStateToDisk();
|
||||
}
|
||||
|
||||
@@ -272,17 +303,12 @@ void NodeDB::installDefaultDeviceState()
|
||||
devicestate.has_my_node = true;
|
||||
devicestate.has_owner = true;
|
||||
devicestate.node_db_count = 0;
|
||||
devicestate.node_db_lite_count = 0;
|
||||
devicestate.version = DEVICESTATE_CUR_VER;
|
||||
devicestate.receive_queue_count = 0; // Not yet implemented FIXME
|
||||
|
||||
// default to no GPS, until one has been found by probing
|
||||
myNodeInfo.has_gps = false;
|
||||
myNodeInfo.message_timeout_msec = FLOOD_EXPIRE_TIME;
|
||||
generatePacketId(); // FIXME - ugly way to init current_packet_id;
|
||||
|
||||
// Init our blank owner info to reasonable defaults
|
||||
getMacAddr(ourMacAddr);
|
||||
|
||||
// Set default owner name
|
||||
pickNewNodeNum(); // based on macaddr now
|
||||
snprintf(owner.long_name, sizeof(owner.long_name), "Meshtastic %02x%02x", ourMacAddr[4], ourMacAddr[5]);
|
||||
@@ -303,28 +329,34 @@ void NodeDB::init()
|
||||
|
||||
int saveWhat = 0;
|
||||
|
||||
myNodeInfo.max_channels = MAX_NUM_CHANNELS; // tell others the max # of channels we can understand
|
||||
|
||||
myNodeInfo.error_code =
|
||||
meshtastic_CriticalErrorCode_NONE; // For the error code, only show values from this boot (discard value from flash)
|
||||
myNodeInfo.error_address = 0;
|
||||
|
||||
// likewise - we always want the app requirements to come from the running appload
|
||||
myNodeInfo.min_app_version = 20300; // format is Mmmss (where M is 1+the numeric major number. i.e. 20120 means 1.1.20
|
||||
|
||||
myNodeInfo.min_app_version = 20300; // format is Mmmss (where M is 1+the numeric major number. i.e. 20120 means 1.1.20
|
||||
myNodeInfo.max_channels = MAX_NUM_CHANNELS; // tell others the max # of channels we can understand
|
||||
// Note! We do this after loading saved settings, so that if somehow an invalid nodenum was stored in preferences we won't
|
||||
// keep using that nodenum forever. Crummy guess at our nodenum (but we will check against the nodedb to avoid conflicts)
|
||||
strncpy(myNodeInfo.firmware_version, optstr(APP_VERSION), sizeof(myNodeInfo.firmware_version));
|
||||
pickNewNodeNum();
|
||||
|
||||
// Set our board type so we can share it with others
|
||||
owner.hw_model = HW_VENDOR;
|
||||
|
||||
// Include our owner in the node db under our nodenum
|
||||
meshtastic_NodeInfo *info = getOrCreateNode(getNodeNum());
|
||||
meshtastic_NodeInfoLite *info = getOrCreateMeshNode(getNodeNum());
|
||||
info->user = owner;
|
||||
info->has_user = true;
|
||||
|
||||
strncpy(myNodeInfo.firmware_version, optstr(APP_VERSION), sizeof(myNodeInfo.firmware_version));
|
||||
if (*numNodes > 0) {
|
||||
LOG_DEBUG("Legacy NodeDB detected... Migrating to NodeDBLite\n");
|
||||
uint32_t readIndex = 0;
|
||||
const meshtastic_NodeInfo *oldNodeInfo = nodeDB.readNextNodeInfo(readIndex);
|
||||
while (oldNodeInfo != NULL) {
|
||||
migrateToNodeInfoLite(oldNodeInfo);
|
||||
oldNodeInfo = nodeDB.readNextNodeInfo(readIndex);
|
||||
}
|
||||
LOG_DEBUG("Migration complete! Clearing out legacy NodeDB...\n");
|
||||
devicestate.node_db_count = 0;
|
||||
memset(devicestate.node_db, 0, sizeof(devicestate.node_db));
|
||||
}
|
||||
|
||||
#ifdef ARCH_ESP32
|
||||
Preferences preferences;
|
||||
@@ -332,11 +364,6 @@ void NodeDB::init()
|
||||
myNodeInfo.reboot_count = preferences.getUInt("rebootCounter", 0);
|
||||
preferences.end();
|
||||
LOG_DEBUG("Number of Device Reboots: %d\n", myNodeInfo.reboot_count);
|
||||
|
||||
/* The ESP32 has a wifi radio. This will need to be modified at some point so
|
||||
* the test isn't so simplistic.
|
||||
*/
|
||||
myNodeInfo.has_wifi = true;
|
||||
#endif
|
||||
|
||||
resetRadioConfig(); // If bogus settings got saved, then fix them
|
||||
@@ -367,17 +394,19 @@ void NodeDB::pickNewNodeNum()
|
||||
{
|
||||
NodeNum r = myNodeInfo.my_node_num;
|
||||
|
||||
// If we don't have a nodenum at app - pick an initial nodenum based on the macaddr
|
||||
if (r == 0)
|
||||
r = (ourMacAddr[2] << 24) | (ourMacAddr[3] << 16) | (ourMacAddr[4] << 8) | ourMacAddr[5];
|
||||
getMacAddr(ourMacAddr); // Make sure ourMacAddr is set
|
||||
|
||||
// Pick an initial nodenum based on the macaddr
|
||||
r = (ourMacAddr[2] << 24) | (ourMacAddr[3] << 16) | (ourMacAddr[4] << 8) | ourMacAddr[5];
|
||||
|
||||
if (r == NODENUM_BROADCAST || r < NUM_RESERVED)
|
||||
r = NUM_RESERVED; // don't pick a reserved node number
|
||||
|
||||
meshtastic_NodeInfo *found;
|
||||
while ((found = getNode(r)) && memcmp(found->user.macaddr, owner.macaddr, sizeof(owner.macaddr))) {
|
||||
meshtastic_NodeInfoLite *found;
|
||||
while ((found = getMeshNode(r)) && memcmp(found->user.macaddr, owner.macaddr, sizeof(owner.macaddr))) {
|
||||
// FIXME: input for random() is int, so NODENUM_BROADCAST becomes -1
|
||||
NodeNum n = random(NUM_RESERVED, NODENUM_BROADCAST); // try a new random choice
|
||||
LOG_DEBUG("NOTE! Our desired nodenum 0x%x is in use, so trying for 0x%x\n", r, n);
|
||||
LOG_WARN("NOTE! Our desired nodenum 0x%x is in use, so trying for 0x%x\n", r, n);
|
||||
r = n;
|
||||
}
|
||||
|
||||
@@ -584,7 +613,7 @@ void NodeDB::saveToDisk(int saveWhat)
|
||||
}
|
||||
}
|
||||
|
||||
const meshtastic_NodeInfo *NodeDB::readNextInfo(uint32_t &readIndex)
|
||||
const meshtastic_NodeInfo *NodeDB::readNextNodeInfo(uint32_t &readIndex)
|
||||
{
|
||||
if (readIndex < *numNodes)
|
||||
return &nodes[readIndex++];
|
||||
@@ -592,8 +621,16 @@ const meshtastic_NodeInfo *NodeDB::readNextInfo(uint32_t &readIndex)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const meshtastic_NodeInfoLite *NodeDB::readNextMeshNode(uint32_t &readIndex)
|
||||
{
|
||||
if (readIndex < *numMeshNodes)
|
||||
return &meshNodes[readIndex++];
|
||||
else
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/// Given a node, return how many seconds in the past (vs now) that we last heard from it
|
||||
uint32_t sinceLastSeen(const meshtastic_NodeInfo *n)
|
||||
uint32_t sinceLastSeen(const meshtastic_NodeInfoLite *n)
|
||||
{
|
||||
uint32_t now = getTime();
|
||||
|
||||
@@ -617,13 +654,13 @@ uint32_t sinceReceived(const meshtastic_MeshPacket *p)
|
||||
|
||||
#define NUM_ONLINE_SECS (60 * 60 * 2) // 2 hrs to consider someone offline
|
||||
|
||||
size_t NodeDB::getNumOnlineNodes()
|
||||
size_t NodeDB::getNumOnlineMeshNodes()
|
||||
{
|
||||
size_t numseen = 0;
|
||||
|
||||
// FIXME this implementation is kinda expensive
|
||||
for (int i = 0; i < *numNodes; i++)
|
||||
if (sinceLastSeen(&nodes[i]) < NUM_ONLINE_SECS)
|
||||
for (int i = 0; i < *numMeshNodes; i++)
|
||||
if (sinceLastSeen(&meshNodes[i]) < NUM_ONLINE_SECS)
|
||||
numseen++;
|
||||
|
||||
return numseen;
|
||||
@@ -635,7 +672,7 @@ size_t NodeDB::getNumOnlineNodes()
|
||||
*/
|
||||
void NodeDB::updatePosition(uint32_t nodeId, const meshtastic_Position &p, RxSource src)
|
||||
{
|
||||
meshtastic_NodeInfo *info = getOrCreateNode(nodeId);
|
||||
meshtastic_NodeInfoLite *info = getOrCreateMeshNode(nodeId);
|
||||
if (!info) {
|
||||
return;
|
||||
}
|
||||
@@ -644,7 +681,9 @@ void NodeDB::updatePosition(uint32_t nodeId, const meshtastic_Position &p, RxSou
|
||||
// Local packet, fully authoritative
|
||||
LOG_INFO("updatePosition LOCAL pos@%x, time=%u, latI=%d, lonI=%d, alt=%d\n", p.timestamp, p.time, p.latitude_i,
|
||||
p.longitude_i, p.altitude);
|
||||
info->position = p;
|
||||
|
||||
info->position = ConvertToPositionLite(p);
|
||||
localPosition = p;
|
||||
} else if ((p.time > 0) && !p.latitude_i && !p.longitude_i && !p.timestamp && !p.location_source) {
|
||||
// FIXME SPECIAL TIME SETTING PACKET FROM EUD TO RADIO
|
||||
// (stop-gap fix for issue #900)
|
||||
@@ -662,7 +701,7 @@ void NodeDB::updatePosition(uint32_t nodeId, const meshtastic_Position &p, RxSou
|
||||
uint32_t tmp_time = info->position.time;
|
||||
|
||||
// Next, update atomically
|
||||
info->position = p;
|
||||
info->position = ConvertToPositionLite(p);
|
||||
|
||||
// Last, restore any fields that may have been overwritten
|
||||
if (!info->position.time)
|
||||
@@ -678,7 +717,7 @@ void NodeDB::updatePosition(uint32_t nodeId, const meshtastic_Position &p, RxSou
|
||||
*/
|
||||
void NodeDB::updateTelemetry(uint32_t nodeId, const meshtastic_Telemetry &t, RxSource src)
|
||||
{
|
||||
meshtastic_NodeInfo *info = getOrCreateNode(nodeId);
|
||||
meshtastic_NodeInfoLite *info = getOrCreateMeshNode(nodeId);
|
||||
// Environment metrics should never go to NodeDb but we'll safegaurd anyway
|
||||
if (!info || t.which_variant != meshtastic_Telemetry_device_metrics_tag) {
|
||||
return;
|
||||
@@ -700,7 +739,7 @@ void NodeDB::updateTelemetry(uint32_t nodeId, const meshtastic_Telemetry &t, RxS
|
||||
*/
|
||||
bool NodeDB::updateUser(uint32_t nodeId, const meshtastic_User &p)
|
||||
{
|
||||
meshtastic_NodeInfo *info = getOrCreateNode(nodeId);
|
||||
meshtastic_NodeInfoLite *info = getOrCreateMeshNode(nodeId);
|
||||
if (!info) {
|
||||
return false;
|
||||
}
|
||||
@@ -733,7 +772,7 @@ void NodeDB::updateFrom(const meshtastic_MeshPacket &mp)
|
||||
if (mp.which_payload_variant == meshtastic_MeshPacket_decoded_tag && mp.from) {
|
||||
LOG_DEBUG("Update DB node 0x%x, rx_time=%u, channel=%d\n", mp.from, mp.rx_time, mp.channel);
|
||||
|
||||
meshtastic_NodeInfo *info = getOrCreateNode(getFrom(&mp));
|
||||
meshtastic_NodeInfoLite *info = getOrCreateMeshNode(getFrom(&mp));
|
||||
if (!info) {
|
||||
return;
|
||||
}
|
||||
@@ -750,9 +789,9 @@ void NodeDB::updateFrom(const meshtastic_MeshPacket &mp)
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t NodeDB::getNodeChannel(NodeNum n)
|
||||
uint8_t NodeDB::getMeshNodeChannel(NodeNum n)
|
||||
{
|
||||
meshtastic_NodeInfo *info = getNode(n);
|
||||
meshtastic_NodeInfoLite *info = getMeshNode(n);
|
||||
if (!info) {
|
||||
return 0; // defaults to PRIMARY
|
||||
}
|
||||
@@ -761,7 +800,7 @@ uint8_t NodeDB::getNodeChannel(NodeNum n)
|
||||
|
||||
/// Find a node in our DB, return null for missing
|
||||
/// NOTE: This function might be called from an ISR
|
||||
meshtastic_NodeInfo *NodeDB::getNode(NodeNum n)
|
||||
meshtastic_NodeInfo *NodeDB::getNodeInfo(NodeNum n)
|
||||
{
|
||||
for (int i = 0; i < *numNodes; i++)
|
||||
if (nodes[i].num == n)
|
||||
@@ -770,38 +809,100 @@ meshtastic_NodeInfo *NodeDB::getNode(NodeNum n)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/// Find a node in our DB, create an empty NodeInfo if missing
|
||||
meshtastic_NodeInfo *NodeDB::getOrCreateNode(NodeNum n)
|
||||
/// Find a node in our DB, return null for missing
|
||||
/// NOTE: This function might be called from an ISR
|
||||
meshtastic_NodeInfoLite *NodeDB::getMeshNode(NodeNum n)
|
||||
{
|
||||
meshtastic_NodeInfo *info = getNode(n);
|
||||
for (int i = 0; i < *numMeshNodes; i++)
|
||||
if (meshNodes[i].num == n)
|
||||
return &meshNodes[i];
|
||||
|
||||
if (!info) {
|
||||
if ((*numNodes >= MAX_NUM_NODES) || (memGet.getFreeHeap() < meshtastic_NodeInfo_size * 3)) {
|
||||
screen->print("warning: node_db full! erasing oldest entry\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/// Find a node in our DB, create an empty NodeInfo if missing
|
||||
meshtastic_NodeInfoLite *NodeDB::getOrCreateMeshNode(NodeNum n)
|
||||
{
|
||||
meshtastic_NodeInfoLite *lite = getMeshNode(n);
|
||||
|
||||
if (!lite) {
|
||||
if ((*numMeshNodes >= MAX_NUM_NODES) || (memGet.getFreeHeap() < meshtastic_NodeInfoLite_size * 3)) {
|
||||
screen->print("warning: node_db_lite full! erasing oldest entry\n");
|
||||
// look for oldest node and erase it
|
||||
uint32_t oldest = UINT32_MAX;
|
||||
int oldestIndex = -1;
|
||||
for (int i = 0; i < *numNodes; i++) {
|
||||
if (nodes[i].last_heard < oldest) {
|
||||
oldest = nodes[i].last_heard;
|
||||
for (int i = 0; i < *numMeshNodes; i++) {
|
||||
if (meshNodes[i].last_heard < oldest) {
|
||||
oldest = meshNodes[i].last_heard;
|
||||
oldestIndex = i;
|
||||
}
|
||||
}
|
||||
// Shove the remaining nodes down the chain
|
||||
for (int i = oldestIndex; i < *numNodes - 1; i++) {
|
||||
nodes[i] = nodes[i + 1];
|
||||
for (int i = oldestIndex; i < *numMeshNodes - 1; i++) {
|
||||
meshNodes[i] = meshNodes[i + 1];
|
||||
}
|
||||
(*numNodes)--;
|
||||
(*numMeshNodes)--;
|
||||
}
|
||||
// add the node at the end
|
||||
info = &nodes[(*numNodes)++];
|
||||
lite = &meshNodes[(*numMeshNodes)++];
|
||||
|
||||
// everything is missing except the nodenum
|
||||
memset(info, 0, sizeof(*info));
|
||||
info->num = n;
|
||||
memset(lite, 0, sizeof(*lite));
|
||||
lite->num = n;
|
||||
}
|
||||
|
||||
return info;
|
||||
return lite;
|
||||
}
|
||||
|
||||
void NodeDB::migrateToNodeInfoLite(const meshtastic_NodeInfo *node)
|
||||
{
|
||||
meshtastic_NodeInfoLite *lite = getMeshNode(node->num);
|
||||
|
||||
if (!lite) {
|
||||
if ((*numMeshNodes >= MAX_NUM_NODES) || (memGet.getFreeHeap() < meshtastic_NodeInfoLite_size * 3)) {
|
||||
screen->print("warning: node_db_lite full! erasing oldest entry\n");
|
||||
// look for oldest node and erase it
|
||||
uint32_t oldest = UINT32_MAX;
|
||||
int oldestIndex = -1;
|
||||
for (int i = 0; i < *numMeshNodes; i++) {
|
||||
if (meshNodes[i].last_heard < oldest) {
|
||||
oldest = meshNodes[i].last_heard;
|
||||
oldestIndex = i;
|
||||
}
|
||||
}
|
||||
// Shove the remaining nodes down the chain
|
||||
for (int i = oldestIndex; i < *numMeshNodes - 1; i++) {
|
||||
meshNodes[i] = meshNodes[i + 1];
|
||||
}
|
||||
(*numMeshNodes)--;
|
||||
}
|
||||
// add the node at the end
|
||||
lite = &meshNodes[(*numMeshNodes)++];
|
||||
|
||||
// everything is missing except the nodenum
|
||||
memset(lite, 0, sizeof(*lite));
|
||||
lite->num = node->num;
|
||||
lite->snr = node->snr;
|
||||
lite->last_heard = node->last_heard;
|
||||
lite->channel = node->channel;
|
||||
|
||||
if (node->has_position) {
|
||||
lite->has_position = true;
|
||||
lite->position.latitude_i = node->position.latitude_i;
|
||||
lite->position.longitude_i = node->position.longitude_i;
|
||||
lite->position.altitude = node->position.altitude;
|
||||
lite->position.location_source = node->position.location_source;
|
||||
lite->position.time = node->position.time;
|
||||
}
|
||||
if (node->has_user) {
|
||||
lite->has_user = true;
|
||||
lite->user = node->user;
|
||||
}
|
||||
if (node->has_device_metrics) {
|
||||
lite->has_device_metrics = true;
|
||||
lite->device_metrics = node->device_metrics;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Record an error that should be reported via analytics
|
||||
@@ -817,13 +918,12 @@ void recordCriticalError(meshtastic_CriticalErrorCode code, uint32_t address, co
|
||||
}
|
||||
|
||||
// Record error to DB
|
||||
myNodeInfo.error_code = code;
|
||||
myNodeInfo.error_address = address;
|
||||
myNodeInfo.error_count++;
|
||||
error_code = code;
|
||||
error_address = address;
|
||||
|
||||
// Currently portuino is mostly used for simulation. Make sue the user notices something really bad happend
|
||||
// Currently portuino is mostly used for simulation. Make sure the user notices something really bad happened
|
||||
#ifdef ARCH_PORTDUINO
|
||||
LOG_ERROR("A critical failure occurred, portduino is exiting...");
|
||||
exit(2);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
@@ -7,6 +7,7 @@
|
||||
#include "MeshTypes.h"
|
||||
#include "NodeStatus.h"
|
||||
#include "mesh-pb-constants.h"
|
||||
#include "mesh/generated/meshtastic/mesh.pb.h" // For CriticalErrorCode
|
||||
|
||||
/*
|
||||
DeviceState versions used to be defined in the .proto file but really only this function cares. So changed to a
|
||||
@@ -28,9 +29,10 @@ extern meshtastic_LocalConfig config;
|
||||
extern meshtastic_LocalModuleConfig moduleConfig;
|
||||
extern meshtastic_OEMStore oemStore;
|
||||
extern meshtastic_User &owner;
|
||||
extern meshtastic_Position localPosition;
|
||||
|
||||
/// Given a node, return how many seconds in the past (vs now) that we last heard from it
|
||||
uint32_t sinceLastSeen(const meshtastic_NodeInfo *n);
|
||||
uint32_t sinceLastSeen(const meshtastic_NodeInfoLite *n);
|
||||
|
||||
/// Given a packet, return how many seconds in the past (vs now) it was received
|
||||
uint32_t sinceReceived(const meshtastic_MeshPacket *p);
|
||||
@@ -46,12 +48,15 @@ class NodeDB
|
||||
meshtastic_NodeInfo *nodes;
|
||||
pb_size_t *numNodes;
|
||||
|
||||
meshtastic_NodeInfoLite *meshNodes;
|
||||
pb_size_t *numMeshNodes;
|
||||
|
||||
public:
|
||||
bool updateGUI = false; // we think the gui should definitely be redrawn, screen will clear this once handled
|
||||
meshtastic_NodeInfo *updateGUIforNode = NULL; // if currently showing this node, we think you should update the GUI
|
||||
meshtastic_NodeInfoLite *updateGUIforNode = NULL; // if currently showing this node, we think you should update the GUI
|
||||
Observable<const meshtastic::NodeStatus *> newStatus;
|
||||
|
||||
/// don't do mesh based algoritm for node id assignment (initially)
|
||||
/// don't do mesh based algorithm for node id assignment (initially)
|
||||
/// instead just store in flash - possibly even in the initial alpha release do this hack
|
||||
NodeDB();
|
||||
|
||||
@@ -89,8 +94,6 @@ class NodeDB
|
||||
/// @return our node number
|
||||
NodeNum getNodeNum() { return myNodeInfo.my_node_num; }
|
||||
|
||||
size_t getNumNodes() { return *numNodes; }
|
||||
|
||||
/// if returns false, that means our node should send a DenyNodeNum response. If true, we think the number is okay for use
|
||||
// bool handleWantNodeNum(NodeNum n);
|
||||
|
||||
@@ -102,26 +105,14 @@ class NodeDB
|
||||
their denial?)
|
||||
*/
|
||||
|
||||
/// Allow the bluetooth layer to read our next nodeinfo record, or NULL if done reading
|
||||
const meshtastic_NodeInfo *readNextInfo(uint32_t &readIndex);
|
||||
|
||||
/// pick a provisional nodenum we hope no one is using
|
||||
void pickNewNodeNum();
|
||||
|
||||
// get channel channel index we heard a nodeNum on, defaults to 0 if not found
|
||||
uint8_t getNodeChannel(NodeNum n);
|
||||
|
||||
/// Find a node in our DB, return null for missing
|
||||
meshtastic_NodeInfo *getNode(NodeNum n);
|
||||
|
||||
meshtastic_NodeInfo *getNodeByIndex(size_t x)
|
||||
{
|
||||
assert(x < *numNodes);
|
||||
return &nodes[x];
|
||||
}
|
||||
uint8_t getMeshNodeChannel(NodeNum n);
|
||||
|
||||
/// Return the number of nodes we've heard from recently (within the last 2 hrs?)
|
||||
size_t getNumOnlineNodes();
|
||||
size_t getNumOnlineMeshNodes();
|
||||
|
||||
void initConfigIntervals(), initModuleConfigIntervals(), resetNodes();
|
||||
|
||||
@@ -132,15 +123,38 @@ class NodeDB
|
||||
|
||||
void installRoleDefaults(meshtastic_Config_DeviceConfig_Role role);
|
||||
|
||||
const meshtastic_NodeInfoLite *readNextMeshNode(uint32_t &readIndex);
|
||||
|
||||
meshtastic_NodeInfoLite *getMeshNodeByIndex(size_t x)
|
||||
{
|
||||
assert(x < *numMeshNodes);
|
||||
return &meshNodes[x];
|
||||
}
|
||||
|
||||
meshtastic_NodeInfoLite *getMeshNode(NodeNum n);
|
||||
size_t getNumMeshNodes() { return *numMeshNodes; }
|
||||
|
||||
private:
|
||||
/// Find a node in our DB, create an empty NodeInfo if missing
|
||||
meshtastic_NodeInfo *getOrCreateNode(NodeNum n);
|
||||
/// Find a node in our DB, create an empty NodeInfoLite if missing
|
||||
meshtastic_NodeInfoLite *getOrCreateMeshNode(NodeNum n);
|
||||
void migrateToNodeInfoLite(const meshtastic_NodeInfo *node);
|
||||
/// Find a node in our DB, return null for missing
|
||||
meshtastic_NodeInfo *getNodeInfo(NodeNum n);
|
||||
/// Allow the bluetooth layer to read our next nodeinfo record, or NULL if done reading
|
||||
const meshtastic_NodeInfo *readNextNodeInfo(uint32_t &readIndex);
|
||||
size_t getNumNodes() { return *numNodes; }
|
||||
|
||||
meshtastic_NodeInfo *getNodeByIndex(size_t x)
|
||||
{
|
||||
assert(x < *numNodes);
|
||||
return &nodes[x];
|
||||
}
|
||||
|
||||
/// Notify observers of changes to the DB
|
||||
void notifyObservers(bool forceUpdate = false)
|
||||
{
|
||||
// Notify observers of the current node state
|
||||
const meshtastic::NodeStatus status = meshtastic::NodeStatus(getNumOnlineNodes(), getNumNodes(), forceUpdate);
|
||||
const meshtastic::NodeStatus status = meshtastic::NodeStatus(getNumOnlineMeshNodes(), getNumMeshNodes(), forceUpdate);
|
||||
newStatus.notifyObservers(&status);
|
||||
}
|
||||
|
||||
@@ -218,12 +232,23 @@ static inline bool hasValidPosition(const meshtastic_NodeInfo *n)
|
||||
{
|
||||
return n->has_position && (n->position.latitude_i != 0 || n->position.longitude_i != 0);
|
||||
}
|
||||
static inline bool hasValidPosition(const meshtastic_NodeInfoLite *n)
|
||||
{
|
||||
return n->has_position && (n->position.latitude_i != 0 || n->position.longitude_i != 0);
|
||||
}
|
||||
|
||||
/** The current change # for radio settings. Starts at 0 on boot and any time the radio settings
|
||||
* might have changed is incremented. Allows others to detect they might now be on a new channel.
|
||||
*/
|
||||
extern uint32_t radioGeneration;
|
||||
|
||||
extern meshtastic_CriticalErrorCode error_code;
|
||||
|
||||
/*
|
||||
* A numeric error address (nonzero if available)
|
||||
*/
|
||||
extern uint32_t error_address;
|
||||
|
||||
#define Module_Config_size \
|
||||
(ModuleConfig_CannedMessageConfig_size + ModuleConfig_ExternalNotificationConfig_size + ModuleConfig_MQTTConfig_size + \
|
||||
ModuleConfig_RangeTestConfig_size + ModuleConfig_SerialConfig_size + ModuleConfig_StoreForwardConfig_size + \
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include "NodeDB.h"
|
||||
#include "PowerFSM.h"
|
||||
#include "RadioInterface.h"
|
||||
#include "TypeConversions.h"
|
||||
#include "configuration.h"
|
||||
#include "main.h"
|
||||
#include "xmodem.h"
|
||||
@@ -17,6 +18,8 @@
|
||||
#error ToRadio is too big
|
||||
#endif
|
||||
|
||||
#include "mqtt/MQTT.h"
|
||||
|
||||
PhoneAPI::PhoneAPI()
|
||||
{
|
||||
lastContactMsec = millis();
|
||||
@@ -40,7 +43,7 @@ void PhoneAPI::handleStartConfig()
|
||||
state = STATE_SEND_MY_INFO;
|
||||
|
||||
LOG_INFO("Starting API client config\n");
|
||||
nodeInfoForPhone = NULL; // Don't keep returning old nodeinfos
|
||||
nodeInfoForPhone.num = 0; // Don't keep returning old nodeinfos
|
||||
resetReadIndex();
|
||||
}
|
||||
|
||||
@@ -53,6 +56,7 @@ void PhoneAPI::close()
|
||||
unobserve(&xModem.packetReady);
|
||||
releasePhonePacket(); // Don't leak phone packets on shutdown
|
||||
releaseQueueStatusPhonePacket();
|
||||
releaseMqttClientProxyPhonePacket();
|
||||
|
||||
onConnectionChanged(false);
|
||||
}
|
||||
@@ -97,6 +101,12 @@ bool PhoneAPI::handleToRadio(const uint8_t *buf, size_t bufLength)
|
||||
LOG_INFO("Got xmodem packet\n");
|
||||
xModem.handlePacket(toRadioScratch.xmodemPacket);
|
||||
break;
|
||||
case meshtastic_ToRadio_mqttClientProxyMessage_tag:
|
||||
LOG_INFO("Got MqttClientProxy message\n");
|
||||
if (mqtt && moduleConfig.mqtt.proxy_to_client_enabled) {
|
||||
mqtt->onClientProxyReceive(toRadioScratch.mqttClientProxyMessage);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
// Ignore nop messages
|
||||
// LOG_DEBUG("Error: unexpected ToRadio variant\n");
|
||||
@@ -143,25 +153,23 @@ size_t PhoneAPI::getFromRadio(uint8_t *buf)
|
||||
LOG_INFO("getFromRadio=STATE_SEND_MY_INFO\n");
|
||||
// If the user has specified they don't want our node to share its location, make sure to tell the phone
|
||||
// app not to send locations on our behalf.
|
||||
myNodeInfo.has_gps = gps && gps->isConnected(); // Update with latest GPS connect info
|
||||
fromRadioScratch.which_payload_variant = meshtastic_FromRadio_my_info_tag;
|
||||
fromRadioScratch.my_info = myNodeInfo;
|
||||
state = STATE_SEND_NODEINFO;
|
||||
|
||||
service.refreshMyNodeInfo(); // Update my NodeInfo because the client will be asking for it soon.
|
||||
service.refreshLocalMeshNode(); // Update my NodeInfo because the client will be asking for it soon.
|
||||
break;
|
||||
|
||||
case STATE_SEND_NODEINFO: {
|
||||
LOG_INFO("getFromRadio=STATE_SEND_NODEINFO\n");
|
||||
const meshtastic_NodeInfo *info = nodeInfoForPhone;
|
||||
nodeInfoForPhone = NULL; // We just consumed a nodeinfo, will need a new one next time
|
||||
|
||||
if (info) {
|
||||
LOG_INFO("Sending nodeinfo: num=0x%x, lastseen=%u, id=%s, name=%s\n", info->num, info->last_heard, info->user.id,
|
||||
info->user.long_name);
|
||||
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 = *info;
|
||||
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;
|
||||
@@ -296,12 +304,16 @@ size_t PhoneAPI::getFromRadio(uint8_t *buf)
|
||||
break;
|
||||
|
||||
case STATE_SEND_PACKETS:
|
||||
// Do we have a message from the mesh?
|
||||
// Do we have a message from the mesh or packet from the local device?
|
||||
LOG_INFO("getFromRadio=STATE_SEND_PACKETS\n");
|
||||
if (queueStatusPacketForPhone) {
|
||||
fromRadioScratch.which_payload_variant = meshtastic_FromRadio_queueStatus_tag;
|
||||
fromRadioScratch.queueStatus = *queueStatusPacketForPhone;
|
||||
releaseQueueStatusPhonePacket();
|
||||
} else if (mqttClientProxyMessageForPhone) {
|
||||
fromRadioScratch.which_payload_variant = meshtastic_FromRadio_mqttClientProxyMessage_tag;
|
||||
fromRadioScratch.mqttClientProxyMessage = *mqttClientProxyMessageForPhone;
|
||||
releaseMqttClientProxyPhonePacket();
|
||||
} else if (xmodemPacketForPhone.control != meshtastic_XModem_Control_NUL) {
|
||||
fromRadioScratch.which_payload_variant = meshtastic_FromRadio_xmodemPacket_tag;
|
||||
fromRadioScratch.xmodemPacket = xmodemPacketForPhone;
|
||||
@@ -354,6 +366,14 @@ void PhoneAPI::releaseQueueStatusPhonePacket()
|
||||
}
|
||||
}
|
||||
|
||||
void PhoneAPI::releaseMqttClientProxyPhonePacket()
|
||||
{
|
||||
if (mqttClientProxyMessageForPhone) {
|
||||
service.releaseMqttClientProxyMessageToPool(mqttClientProxyMessageForPhone);
|
||||
mqttClientProxyMessageForPhone = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if we have data available to send to the phone
|
||||
*/
|
||||
@@ -371,14 +391,20 @@ bool PhoneAPI::available()
|
||||
return true;
|
||||
|
||||
case STATE_SEND_NODEINFO:
|
||||
if (!nodeInfoForPhone)
|
||||
nodeInfoForPhone = nodeDB.readNextInfo(readIndex);
|
||||
if (nodeInfoForPhone.num == 0) {
|
||||
auto nextNode = nodeDB.readNextMeshNode(readIndex);
|
||||
if (nextNode) {
|
||||
nodeInfoForPhone = ConvertToNodeInfo(nextNode);
|
||||
}
|
||||
}
|
||||
return true; // Always say we have something, because we might need to advance our state machine
|
||||
|
||||
case STATE_SEND_PACKETS: {
|
||||
if (!queueStatusPacketForPhone)
|
||||
queueStatusPacketForPhone = service.getQueueStatusForPhone();
|
||||
bool hasPacket = !!queueStatusPacketForPhone;
|
||||
if (!mqttClientProxyMessageForPhone)
|
||||
mqttClientProxyMessageForPhone = service.getMqttClientProxyMessageForPhone();
|
||||
bool hasPacket = !!queueStatusPacketForPhone || !!mqttClientProxyMessageForPhone;
|
||||
if (hasPacket)
|
||||
return true;
|
||||
|
||||
@@ -427,4 +453,4 @@ int PhoneAPI::onNotify(uint32_t newValue)
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
@@ -50,8 +50,11 @@ class PhoneAPI
|
||||
// Keep QueueStatus packet just as packetForPhone
|
||||
meshtastic_QueueStatus *queueStatusPacketForPhone = NULL;
|
||||
|
||||
// Keep MqttClientProxyMessage packet just as packetForPhone
|
||||
meshtastic_MqttClientProxyMessage *mqttClientProxyMessageForPhone = NULL;
|
||||
|
||||
/// We temporarily keep the nodeInfo here between the call to available and getFromRadio
|
||||
const meshtastic_NodeInfo *nodeInfoForPhone = NULL;
|
||||
meshtastic_NodeInfo nodeInfoForPhone = meshtastic_NodeInfo_init_default;
|
||||
|
||||
meshtastic_ToRadio toRadioScratch = {
|
||||
0}; // this is a static scratch object, any data must be copied elsewhere before returning
|
||||
@@ -126,6 +129,8 @@ class PhoneAPI
|
||||
|
||||
void releaseQueueStatusPhonePacket();
|
||||
|
||||
void releaseMqttClientProxyPhonePacket();
|
||||
|
||||
/// begin a new connection
|
||||
void handleStartConfig();
|
||||
|
||||
|
||||
@@ -52,7 +52,7 @@ template <class T> class ProtobufModule : protected SinglePortModule
|
||||
*/
|
||||
const char *getSenderShortName(const meshtastic_MeshPacket &mp)
|
||||
{
|
||||
auto node = nodeDB.getNode(getFrom(&mp));
|
||||
auto node = nodeDB.getMeshNode(getFrom(&mp));
|
||||
const char *sender = (node) ? node->user.short_name : "???";
|
||||
return sender;
|
||||
}
|
||||
@@ -86,4 +86,4 @@ template <class T> class ProtobufModule : protected SinglePortModule
|
||||
|
||||
return handleReceivedProtobuf(mp, decoded) ? ProcessMessage::STOP : ProcessMessage::CONTINUE;
|
||||
}
|
||||
};
|
||||
};
|
||||
@@ -192,7 +192,7 @@ bool RF95Interface::isChannelActive()
|
||||
return false;
|
||||
}
|
||||
|
||||
/** Could we send right now (i.e. either not actively receving or transmitting)? */
|
||||
/** Could we send right now (i.e. either not actively receiving or transmitting)? */
|
||||
bool RF95Interface::isActivelyReceiving()
|
||||
{
|
||||
return lora->isReceiving();
|
||||
@@ -201,7 +201,7 @@ bool RF95Interface::isActivelyReceiving()
|
||||
bool RF95Interface::sleep()
|
||||
{
|
||||
// put chipset into sleep mode
|
||||
setStandby(); // First cancel any active receving/sending
|
||||
setStandby(); // First cancel any active receiving/sending
|
||||
lora->sleep();
|
||||
|
||||
return true;
|
||||
|
||||
@@ -196,8 +196,9 @@ uint32_t RadioInterface::getRetransmissionMsec(const meshtastic_MeshPacket *p)
|
||||
// LOG_DEBUG("Waiting for flooding message with airtime %d and slotTime is %d\n", packetAirtime, slotTimeMsec);
|
||||
float channelUtil = airTime->channelUtilizationPercent();
|
||||
uint8_t CWsize = map(channelUtil, 0, 100, CWmin, CWmax);
|
||||
// Assuming we pick max. of CWsize and there will be a receiver with SNR at half the range
|
||||
return 2 * packetAirtime + (pow(2, CWsize) + pow(2, int((CWmax + CWmin) / 2))) * slotTimeMsec + PROCESSING_TIME_MSEC;
|
||||
// Assuming we pick max. of CWsize and there will be a client with SNR at half the range
|
||||
return 2 * packetAirtime + (pow(2, CWsize) + 2 * CWmax + pow(2, int((CWmax + CWmin) / 2))) * slotTimeMsec +
|
||||
PROCESSING_TIME_MSEC;
|
||||
}
|
||||
|
||||
/** The delay to use when we want to send something */
|
||||
@@ -232,7 +233,8 @@ uint32_t RadioInterface::getTxDelayMsecWeighted(float snr)
|
||||
delay = random(0, 2 * CWsize) * slotTimeMsec;
|
||||
LOG_DEBUG("rx_snr found in packet. As a router, setting tx delay:%d\n", delay);
|
||||
} else {
|
||||
delay = random(0, pow(2, CWsize)) * slotTimeMsec;
|
||||
// offset the maximum delay for routers: (2 * CWmax * slotTimeMsec)
|
||||
delay = (2 * CWmax * slotTimeMsec) + random(0, pow(2, CWsize)) * slotTimeMsec;
|
||||
LOG_DEBUG("rx_snr found in packet. Setting tx delay:%d\n", delay);
|
||||
}
|
||||
|
||||
@@ -306,7 +308,7 @@ bool RadioInterface::init()
|
||||
preflightSleepObserver.observe(&preflightSleep);
|
||||
notifyDeepSleepObserver.observe(¬ifyDeepSleep);
|
||||
|
||||
// we now expect interfaces to operate in promiscous mode
|
||||
// we now expect interfaces to operate in promiscuous mode
|
||||
// radioIf.setThisAddress(nodeDB.getNodeNum()); // Note: we must do this here, because the nodenum isn't inited at constructor
|
||||
// time.
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
|
||||
/**
|
||||
* This structure has to exactly match the wire layout when sent over the radio link. Used to keep compatibility
|
||||
* wtih the old radiohead implementation.
|
||||
* with the old radiohead implementation.
|
||||
*/
|
||||
typedef struct {
|
||||
NodeNum to, from; // can be 1 byte or four bytes
|
||||
@@ -75,7 +75,7 @@ class RadioInterface
|
||||
uint32_t lastTxStart = 0L;
|
||||
|
||||
/**
|
||||
* A temporary buffer used for sending/receving packets, sized to hold the biggest buffer we might need
|
||||
* A temporary buffer used for sending/receiving packets, sized to hold the biggest buffer we might need
|
||||
* */
|
||||
uint8_t radiobuf[MAX_RHPACKETLEN];
|
||||
|
||||
@@ -198,7 +198,7 @@ class RadioInterface
|
||||
virtual void saveFreq(float savedFreq);
|
||||
|
||||
/**
|
||||
* Save the chanel we selected for later reuse.
|
||||
* Save the channel we selected for later reuse.
|
||||
*/
|
||||
virtual void saveChannelNum(uint32_t savedChannelNum);
|
||||
|
||||
@@ -206,7 +206,7 @@ class RadioInterface
|
||||
/**
|
||||
* Convert our modemConfig enum into wf, sf, etc...
|
||||
*
|
||||
* These paramaters will be pull from the channelSettings global
|
||||
* These parameters will be pull from the channelSettings global
|
||||
*/
|
||||
void applyModemConfig();
|
||||
|
||||
|
||||
@@ -68,7 +68,7 @@ void INTERRUPT_ATTR RadioLibInterface::isrTxLevel0()
|
||||
*/
|
||||
RadioLibInterface *RadioLibInterface::instance;
|
||||
|
||||
/** Could we send right now (i.e. either not actively receving or transmitting)? */
|
||||
/** Could we send right now (i.e. either not actively receiving or transmitting)? */
|
||||
bool RadioLibInterface::canSendImmediately()
|
||||
{
|
||||
// We wait _if_ we are partially though receiving a packet (rather than just merely waiting for one).
|
||||
|
||||
@@ -12,9 +12,7 @@ extern "C" {
|
||||
#include "mesh/compression/unishox2.h"
|
||||
}
|
||||
|
||||
#if HAS_WIFI || HAS_ETHERNET
|
||||
#include "mqtt/MQTT.h"
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Router todo
|
||||
@@ -178,7 +176,7 @@ ErrorCode Router::sendLocal(meshtastic_MeshPacket *p, RxSource src)
|
||||
}
|
||||
|
||||
if (!p->channel) { // don't override if a channel was requested
|
||||
p->channel = nodeDB.getNodeChannel(p->to);
|
||||
p->channel = nodeDB.getMeshNodeChannel(p->to);
|
||||
LOG_DEBUG("localSend to channel %d\n", p->channel);
|
||||
}
|
||||
|
||||
@@ -248,7 +246,6 @@ ErrorCode Router::send(meshtastic_MeshPacket *p)
|
||||
|
||||
bool shouldActuallyEncrypt = true;
|
||||
|
||||
#if HAS_WIFI || HAS_ETHERNET
|
||||
if (moduleConfig.mqtt.enabled) {
|
||||
// check if we should send decrypted packets to mqtt
|
||||
|
||||
@@ -272,7 +269,6 @@ ErrorCode Router::send(meshtastic_MeshPacket *p)
|
||||
if (mqtt && !shouldActuallyEncrypt)
|
||||
mqtt->onSend(*p, chIndex);
|
||||
}
|
||||
#endif
|
||||
|
||||
auto encodeResult = perhapsEncode(p);
|
||||
if (encodeResult != meshtastic_Routing_Error_NONE) {
|
||||
@@ -280,14 +276,12 @@ ErrorCode Router::send(meshtastic_MeshPacket *p)
|
||||
return encodeResult; // FIXME - this isn't a valid ErrorCode
|
||||
}
|
||||
|
||||
#if HAS_WIFI || HAS_ETHERNET
|
||||
if (moduleConfig.mqtt.enabled) {
|
||||
// the packet is now encrypted.
|
||||
// check if we should send encrypted packets to mqtt
|
||||
if (mqtt && shouldActuallyEncrypt)
|
||||
mqtt->onSend(*p, chIndex);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
assert(iface); // This should have been detected already in sendLocal (or we just received a packet from outside)
|
||||
@@ -405,7 +399,7 @@ meshtastic_Routing_Error perhapsEncode(meshtastic_MeshPacket *p)
|
||||
if (compressed_len >= p->decoded.payload.size) {
|
||||
|
||||
LOG_DEBUG("Not using compressing message.\n");
|
||||
// Set the uncompressed payload varient anyway. Shouldn't hurt?
|
||||
// Set the uncompressed payload variant anyway. Shouldn't hurt?
|
||||
// p->decoded.which_payloadVariant = Data_payload_tag;
|
||||
|
||||
// Otherwise we use the compressor
|
||||
@@ -493,4 +487,4 @@ void Router::perhapsHandleReceived(meshtastic_MeshPacket *p)
|
||||
handleReceived(p);
|
||||
|
||||
packetPool.release(p);
|
||||
}
|
||||
}
|
||||
@@ -90,7 +90,7 @@ class Router : protected concurrency::OSThread
|
||||
*
|
||||
* FIXME, move this into the new RoutingModule and do the filtering there using the regular module logic
|
||||
*
|
||||
* Called immedately on receiption, before any further processing.
|
||||
* Called immediately on reception, before any further processing.
|
||||
* @return true to abandon the packet
|
||||
*/
|
||||
virtual bool shouldFilterReceived(const meshtastic_MeshPacket *p) { return false; }
|
||||
|
||||
@@ -70,12 +70,25 @@ template <typename T> bool SX126xInterface<T>::init()
|
||||
#endif
|
||||
|
||||
#if defined(SX126X_TXEN) && (SX126X_TXEN != RADIOLIB_NC)
|
||||
// lora.begin sets Dio2 as RF switch control, which is not true if we are manually controlling RX and TX
|
||||
// If SX126X_TXEN is connected to the MCU, we are manually controlling RX and TX.
|
||||
// But lora.begin (called above) sets Dio2 as RF switch control, which is not true here, so set it back to false.
|
||||
if (res == RADIOLIB_ERR_NONE) {
|
||||
LOG_DEBUG("SX126X_TX/RX EN pins defined. Setting RF Switch: RXEN=%i, TXEN=%i\n", SX126X_RXEN, SX126X_TXEN);
|
||||
LOG_DEBUG("SX126X_TXEN pin defined. Setting RF Switch: RXEN=%i, TXEN=%i\n", SX126X_RXEN, SX126X_TXEN);
|
||||
res = lora.setDio2AsRfSwitch(false);
|
||||
lora.setRfSwitchPins(SX126X_RXEN, SX126X_TXEN);
|
||||
}
|
||||
#elif defined(SX126X_RXEN) && (SX126X_RXEN != RADIOLIB_NC && defined(E22_TXEN_CONNECTED_TO_DIO2))
|
||||
// Otherwise, if SX126X_RXEN is connected to the MCU, and E22_TXEN_CONNECTED_TO_DIO2 is defined, we are letting the
|
||||
// E22 control RX and TX via DIO2. In this configuration, the E22's TXEN and DIO2 pins are connected to each other,
|
||||
// but not to the MCU.
|
||||
// However, we must still connect the E22's RXEN pin to the MCU, define SX126X_RXEN accordingly, and then call
|
||||
// setRfSwitchPins, otherwise RX sensitivity (observed via RSSI) is greatly diminished.
|
||||
LOG_DEBUG("SX126X_RXEN and E22_TXEN_CONNECTED_TO_DIO2 are defined; value of res: %d", res);
|
||||
if (res == RADIOLIB_ERR_NONE) {
|
||||
LOG_DEBUG("SX126X_TXEN is RADIOLIB_NC, but SX126X_RXEN and E22_TXEN_CONNECTED_TO_DIO2 are both defined; calling "
|
||||
"lora.setRfSwitchPins.");
|
||||
lora.setRfSwitchPins(SX126X_RXEN, SX126X_TXEN);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (config.lora.sx126x_rx_boosted_gain) {
|
||||
@@ -249,7 +262,7 @@ template <typename T> bool SX126xInterface<T>::isChannelActive()
|
||||
return false;
|
||||
}
|
||||
|
||||
/** Could we send right now (i.e. either not actively receving or transmitting)? */
|
||||
/** Could we send right now (i.e. either not actively receiving or transmitting)? */
|
||||
template <typename T> bool SX126xInterface<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
|
||||
@@ -299,4 +312,4 @@ template <typename T> bool SX126xInterface<T>::sleep()
|
||||
#endif
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -242,7 +242,7 @@ template <typename T> bool SX128xInterface<T>::isChannelActive()
|
||||
return false;
|
||||
}
|
||||
|
||||
/** Could we send right now (i.e. either not actively receving or transmitting)? */
|
||||
/** Could we send right now (i.e. either not actively receiving or transmitting)? */
|
||||
template <typename T> bool SX128xInterface<T>::isActivelyReceiving()
|
||||
{
|
||||
uint16_t irq = lora.getIrqStatus();
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
#include "Router.h"
|
||||
|
||||
/**
|
||||
* Most modules are only interested in sending/receving one particular portnum. This baseclass simplifies that common
|
||||
* Most modules are only interested in sending/receiving one particular portnum. This baseclass simplifies that common
|
||||
* case.
|
||||
*/
|
||||
class SinglePortModule : public MeshModule
|
||||
|
||||
54
src/mesh/TypeConversions.h
Normal file
54
src/mesh/TypeConversions.h
Normal file
@@ -0,0 +1,54 @@
|
||||
#include "mesh/generated/meshtastic/deviceonly.pb.h"
|
||||
#include "mesh/generated/meshtastic/mesh.pb.h"
|
||||
|
||||
inline static meshtastic_NodeInfo ConvertToNodeInfo(const meshtastic_NodeInfoLite *lite)
|
||||
{
|
||||
meshtastic_NodeInfo info = meshtastic_NodeInfo_init_default;
|
||||
|
||||
info.num = lite->num;
|
||||
info.snr = lite->snr;
|
||||
info.last_heard = lite->last_heard;
|
||||
info.channel = lite->channel;
|
||||
|
||||
if (lite->has_position) {
|
||||
info.has_position = true;
|
||||
info.position.latitude_i = lite->position.latitude_i;
|
||||
info.position.longitude_i = lite->position.longitude_i;
|
||||
info.position.altitude = lite->position.altitude;
|
||||
info.position.location_source = lite->position.location_source;
|
||||
info.position.time = lite->position.time;
|
||||
}
|
||||
if (lite->has_user) {
|
||||
info.has_user = true;
|
||||
info.user = lite->user;
|
||||
}
|
||||
if (lite->has_device_metrics) {
|
||||
info.has_device_metrics = true;
|
||||
info.device_metrics = lite->device_metrics;
|
||||
}
|
||||
return info;
|
||||
}
|
||||
|
||||
inline static meshtastic_PositionLite ConvertToPositionLite(meshtastic_Position position)
|
||||
{
|
||||
meshtastic_PositionLite lite = meshtastic_PositionLite_init_default;
|
||||
lite.latitude_i = position.latitude_i;
|
||||
lite.longitude_i = position.longitude_i;
|
||||
lite.altitude = position.altitude;
|
||||
lite.location_source = position.location_source;
|
||||
lite.time = position.time;
|
||||
|
||||
return lite;
|
||||
}
|
||||
|
||||
inline static meshtastic_Position ConvertToPosition(meshtastic_PositionLite lite)
|
||||
{
|
||||
meshtastic_Position position = meshtastic_Position_init_default;
|
||||
position.latitude_i = lite.latitude_i;
|
||||
position.longitude_i = lite.longitude_i;
|
||||
position.altitude = lite.altitude;
|
||||
position.location_source = lite.location_source;
|
||||
position.time = lite.time;
|
||||
|
||||
return position;
|
||||
}
|
||||
@@ -57,7 +57,7 @@ uint8_t usx_code_94[94];
|
||||
uint8_t usx_vcodes[] = {0x00, 0x40, 0x60, 0x80, 0x90, 0xA0, 0xB0, 0xC0, 0xD0, 0xD8, 0xE0, 0xE4, 0xE8, 0xEC,
|
||||
0xEE, 0xF0, 0xF2, 0xF4, 0xF6, 0xF7, 0xF8, 0xF9, 0xFA, 0xFB, 0xFC, 0xFD, 0xFE, 0xFF};
|
||||
|
||||
/// Length of each veritical code
|
||||
/// Length of each vertical code
|
||||
uint8_t usx_vcode_lens[] = {2, 3, 3, 4, 4, 4, 4, 4, 5, 5, 6, 6, 6, 7, 7, 7, 7, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8};
|
||||
|
||||
/// Vertical Codes and Set number for frequent sequences in sets USX_SYM and USX_NUM. First 3 bits indicate set (USX_SYM/USX_NUM)
|
||||
@@ -188,7 +188,7 @@ int append_switch_code(char *out, int olen, int ol, uint8_t state)
|
||||
return ol;
|
||||
}
|
||||
|
||||
/// Appends given horizontal and veritical code bits to out
|
||||
/// Appends given horizontal and vertical code bits to out
|
||||
int append_code(char *out, int olen, int ol, uint8_t code, uint8_t *state, const uint8_t usx_hcodes[],
|
||||
const uint8_t usx_hcode_lens[])
|
||||
{
|
||||
@@ -888,7 +888,7 @@ int read8bitCode(const char *in, int len, int bit_no)
|
||||
return code;
|
||||
}
|
||||
|
||||
/// The list of veritical codes is split into 5 sections. Used by readVCodeIdx()
|
||||
/// The list of vertical codes is split into 5 sections. Used by readVCodeIdx()
|
||||
#define SECTION_COUNT 5
|
||||
/// Used by readVCodeIdx() for finding the section under which the code read using read8bitCode() falls
|
||||
uint8_t usx_vsections[] = {0x7F, 0xBF, 0xDF, 0xEF, 0xFF};
|
||||
@@ -915,7 +915,7 @@ uint8_t usx_vcode_lookup[36] = {(1 << 5) + 0, (1 << 5) + 0, (2 << 5) + 1, (2
|
||||
/// compared to using a 256 uint8_t buffer to decode the next 8 bits read by read8bitCode() \n
|
||||
/// by splitting the list of vertical codes. \n
|
||||
/// Decoder is designed for using less memory, not speed. \n
|
||||
/// Returns the veritical code index or 99 if match could not be found. \n
|
||||
/// Returns the vertical code index or 99 if match could not be found. \n
|
||||
/// Also updates bit_no_p with how many ever bits used by the vertical code.
|
||||
int readVCodeIdx(const char *in, int len, int *bit_no_p)
|
||||
{
|
||||
|
||||
@@ -198,45 +198,45 @@
|
||||
2, 2, 2, 2, 0 \
|
||||
}
|
||||
|
||||
/// Default frequently occuring sequences. When composition of text is know beforehand, the other sequences in this section can be
|
||||
/// used to achieve more compression.
|
||||
/// Default frequently occurring sequences. When composition of text is know beforehand, the other sequences in this section can
|
||||
/// be used to achieve more compression.
|
||||
#define USX_FREQ_SEQ_DFLT \
|
||||
(const char *[]) \
|
||||
{ \
|
||||
"\": \"", "\": ", "</", "=\"", "\":\"", "://" \
|
||||
}
|
||||
/// Frequently occuring sequences in text content
|
||||
/// Frequently occurring sequences in text content
|
||||
#define USX_FREQ_SEQ_TXT \
|
||||
(const char *[]) \
|
||||
{ \
|
||||
" the ", " and ", "tion", " with", "ing", "ment" \
|
||||
}
|
||||
/// Frequently occuring sequences in URL content
|
||||
/// Frequently occurring sequences in URL content
|
||||
#define USX_FREQ_SEQ_URL \
|
||||
(const char *[]) \
|
||||
{ \
|
||||
"https://", "www.", ".com", "http://", ".org", ".net" \
|
||||
}
|
||||
/// Frequently occuring sequences in JSON content
|
||||
/// Frequently occurring sequences in JSON content
|
||||
#define USX_FREQ_SEQ_JSON \
|
||||
(const char *[]) \
|
||||
{ \
|
||||
"\": \"", "\": ", "\",", "}}}", "\":\"", "}}" \
|
||||
}
|
||||
/// Frequently occuring sequences in HTML content
|
||||
/// Frequently occurring sequences in HTML content
|
||||
#define USX_FREQ_SEQ_HTML \
|
||||
(const char *[]) \
|
||||
{ \
|
||||
"</", "=\"", "div", "href", "class", "<p>" \
|
||||
}
|
||||
/// Frequently occuring sequences in XML content
|
||||
/// Frequently occurring sequences in XML content
|
||||
#define USX_FREQ_SEQ_XML \
|
||||
(const char *[]) \
|
||||
{ \
|
||||
"</", "=\"", "\">", "<?xml version=\"1.0\"", "xmlns:", "://" \
|
||||
}
|
||||
|
||||
/// Commonly occuring templates (ISO Date/Time, ISO Date, US Phone number, ISO Time, Unused)
|
||||
/// Commonly occurring templates (ISO Date/Time, ISO Date, US Phone number, ISO Time, Unused)
|
||||
#define USX_TEMPLATES \
|
||||
(const char *[]) \
|
||||
{ \
|
||||
@@ -333,8 +333,8 @@ extern int unishox2_decompress_simple(const char *in, int len, char *out);
|
||||
* @param[in] olen length of 'out' buffer in bytes. Can be omitted if sufficient buffer is provided
|
||||
* @param[in] usx_hcodes Horizontal codes (array of bytes). See macro section for samples.
|
||||
* @param[in] usx_hcode_lens Length of each element in usx_hcodes array
|
||||
* @param[in] usx_freq_seq Frequently occuring sequences. See USX_FREQ_SEQ_* macros for samples
|
||||
* @param[in] usx_templates Templates of frequently occuring patterns. See USX_TEMPLATES macro.
|
||||
* @param[in] usx_freq_seq Frequently occurring sequences. See USX_FREQ_SEQ_* macros for samples
|
||||
* @param[in] usx_templates Templates of frequently occurring patterns. See USX_TEMPLATES macro.
|
||||
*/
|
||||
extern int unishox2_compress(const char *in, int len, UNISHOX_API_OUT_AND_LEN(char *out, int olen),
|
||||
const unsigned char usx_hcodes[], const unsigned char usx_hcode_lens[], const char *usx_freq_seq[],
|
||||
@@ -352,8 +352,8 @@ extern int unishox2_compress(const char *in, int len, UNISHOX_API_OUT_AND_LEN(ch
|
||||
* @param[in] olen length of 'out' buffer in bytes. Can be omitted if sufficient buffer is provided
|
||||
* @param[in] usx_hcodes Horizontal codes (array of bytes). See macro section for samples.
|
||||
* @param[in] usx_hcode_lens Length of each element in usx_hcodes array
|
||||
* @param[in] usx_freq_seq Frequently occuring sequences. See USX_FREQ_SEQ_* macros for samples
|
||||
* @param[in] usx_templates Templates of frequently occuring patterns. See USX_TEMPLATES macro.
|
||||
* @param[in] usx_freq_seq Frequently occurring sequences. See USX_FREQ_SEQ_* macros for samples
|
||||
* @param[in] usx_templates Templates of frequently occurring patterns. See USX_TEMPLATES macro.
|
||||
*/
|
||||
extern int unishox2_decompress(const char *in, int len, UNISHOX_API_OUT_AND_LEN(char *out, int olen),
|
||||
const unsigned char usx_hcodes[], const unsigned char usx_hcode_lens[], const char *usx_freq_seq[],
|
||||
@@ -373,7 +373,7 @@ extern int unishox2_compress_lines(const char *in, int len, UNISHOX_API_OUT_AND_
|
||||
const char *usx_freq_seq[], const char *usx_templates[], struct us_lnk_lst *prev_lines);
|
||||
/**
|
||||
* More Comprehensive API for de-compressing array of strings \n
|
||||
* This function is not be used in conjuction with unishox2_compress_lines()
|
||||
* This function is not be used in conjunction with unishox2_compress_lines()
|
||||
*
|
||||
* See unishox2_decompress() function for parameter definitions. \n
|
||||
* Typically an array is compressed using unishox2_compress_lines() and \n
|
||||
|
||||
@@ -68,7 +68,7 @@ static int32_t reconnectETH()
|
||||
}
|
||||
|
||||
// FIXME this is kinda yucky, instead we should just have an observable for 'wifireconnected'
|
||||
if (mqtt && !mqtt->connected()) {
|
||||
if (mqtt && !moduleConfig.mqtt.proxy_to_client_enabled && !mqtt->isConnectedDirectly()) {
|
||||
mqtt->reconnect();
|
||||
}
|
||||
}
|
||||
@@ -87,7 +87,6 @@ static int32_t reconnectETH()
|
||||
perhapsSetRTC(RTCQualityNTP, &tv);
|
||||
|
||||
ntp_renew = millis() + 43200 * 1000; // success, refresh every 12 hours
|
||||
|
||||
} else {
|
||||
LOG_ERROR("NTP Update failed\n");
|
||||
ntp_renew = millis() + 300 * 1000; // failure, retry every 5 minutes
|
||||
@@ -98,6 +97,11 @@ static int32_t reconnectETH()
|
||||
return 5000; // every 5 seconds
|
||||
}
|
||||
|
||||
static uint32_t bigToLittleEndian(uint32_t value)
|
||||
{
|
||||
return ((value >> 24) & 0xFF) | ((value >> 8) & 0xFF00) | ((value << 8) & 0xFF0000) | ((value << 24) & 0xFF000000);
|
||||
}
|
||||
|
||||
// Startup Ethernet
|
||||
bool initEthernet()
|
||||
{
|
||||
@@ -126,7 +130,15 @@ bool initEthernet()
|
||||
status = Ethernet.begin(mac);
|
||||
} else if (config.network.address_mode == meshtastic_Config_NetworkConfig_AddressMode_STATIC) {
|
||||
LOG_INFO("starting Ethernet Static\n");
|
||||
Ethernet.begin(mac, config.network.ipv4_config.ip, config.network.ipv4_config.dns, config.network.ipv4_config.subnet);
|
||||
|
||||
IPAddress ip = IPAddress(bigToLittleEndian(config.network.ipv4_config.ip));
|
||||
IPAddress dns = IPAddress(bigToLittleEndian(config.network.ipv4_config.dns));
|
||||
IPAddress gateway = IPAddress(bigToLittleEndian(config.network.ipv4_config.gateway));
|
||||
IPAddress subnet = IPAddress(bigToLittleEndian(config.network.ipv4_config.subnet));
|
||||
|
||||
Ethernet.begin(mac, ip, dns, gateway, subnet);
|
||||
|
||||
status = 1;
|
||||
} else {
|
||||
LOG_INFO("Ethernet Disabled\n");
|
||||
return false;
|
||||
@@ -157,7 +169,6 @@ bool initEthernet()
|
||||
ethEvent = new Periodic("ethConnect", reconnectETH);
|
||||
|
||||
return true;
|
||||
|
||||
} else {
|
||||
LOG_INFO("Not using Ethernet\n");
|
||||
return false;
|
||||
|
||||
@@ -9,6 +9,12 @@
|
||||
PB_BIND(meshtastic_DeviceState, meshtastic_DeviceState, 4)
|
||||
|
||||
|
||||
PB_BIND(meshtastic_NodeInfoLite, meshtastic_NodeInfoLite, AUTO)
|
||||
|
||||
|
||||
PB_BIND(meshtastic_PositionLite, meshtastic_PositionLite, AUTO)
|
||||
|
||||
|
||||
PB_BIND(meshtastic_ChannelFile, meshtastic_ChannelFile, 2)
|
||||
|
||||
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -163,7 +163,7 @@ extern const pb_msgdesc_t meshtastic_LocalModuleConfig_msg;
|
||||
|
||||
/* Maximum encoded size of messages (where known) */
|
||||
#define meshtastic_LocalConfig_size 461
|
||||
#define meshtastic_LocalModuleConfig_size 545
|
||||
#define meshtastic_LocalModuleConfig_size 547
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /* extern "C" */
|
||||
|
||||
@@ -24,6 +24,9 @@ PB_BIND(meshtastic_Data, meshtastic_Data, 2)
|
||||
PB_BIND(meshtastic_Waypoint, meshtastic_Waypoint, AUTO)
|
||||
|
||||
|
||||
PB_BIND(meshtastic_MqttClientProxyMessage, meshtastic_MqttClientProxyMessage, 2)
|
||||
|
||||
|
||||
PB_BIND(meshtastic_MeshPacket, meshtastic_MeshPacket, 2)
|
||||
|
||||
|
||||
|
||||
@@ -97,6 +97,14 @@ typedef enum _meshtastic_HardwareModel {
|
||||
meshtastic_HardwareModel_BETAFPV_900_NANO_TX = 46,
|
||||
/* Raspberry Pi Pico (W) with Waveshare SX1262 LoRa Node Module */
|
||||
meshtastic_HardwareModel_RPI_PICO = 47,
|
||||
/* Heltec Wireless Tracker with ESP32-S3 CPU, built-in GPS, and TFT */
|
||||
meshtastic_HardwareModel_HELTEC_WIRELESS_TRACKER = 48,
|
||||
/* Heltec Wireless Paper with ESP32-S3 CPU and E-Ink display */
|
||||
meshtastic_HardwareModel_HELTEC_WIRELESS_PAPER = 49,
|
||||
/* LilyGo T-Deck with ESP32-S3 CPU, Keyboard, and IPS display */
|
||||
meshtastic_HardwareModel_T_DECK = 50,
|
||||
/* LilyGo T-Watch S3 with ESP32-S3 CPU and IPS display */
|
||||
meshtastic_HardwareModel_T_WATCH_S3 = 51,
|
||||
/* ------------------------------------------------------------------------------------------------------------------------------------------
|
||||
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.
|
||||
------------------------------------------------------------------------------------------------------------------------------------------ */
|
||||
@@ -372,7 +380,8 @@ typedef struct _meshtastic_User {
|
||||
/* A VERY short name, ideally two characters.
|
||||
Suitable for a tiny OLED screen */
|
||||
char short_name[5];
|
||||
/* This is the addr of the radio.
|
||||
/* Deprecated in Meshtastic 2.1.x
|
||||
This is the addr of the radio.
|
||||
Not populated by the phone, but added by the esp32 when broadcasting */
|
||||
pb_byte_t macaddr[6];
|
||||
/* TBEAM, HELTEC, etc...
|
||||
@@ -461,6 +470,22 @@ typedef struct _meshtastic_Waypoint {
|
||||
uint32_t icon;
|
||||
} meshtastic_Waypoint;
|
||||
|
||||
typedef PB_BYTES_ARRAY_T(435) meshtastic_MqttClientProxyMessage_data_t;
|
||||
/* This message will be proxied over the PhoneAPI for the client to deliver to the MQTT server */
|
||||
typedef struct _meshtastic_MqttClientProxyMessage {
|
||||
/* The MQTT topic this message will be sent /received on */
|
||||
char topic[60];
|
||||
pb_size_t which_payload_variant;
|
||||
union {
|
||||
/* Bytes */
|
||||
meshtastic_MqttClientProxyMessage_data_t data;
|
||||
/* Text */
|
||||
char text[435];
|
||||
} payload_variant;
|
||||
/* Whether the message should be retained (or not) */
|
||||
bool retained;
|
||||
} meshtastic_MqttClientProxyMessage;
|
||||
|
||||
typedef PB_BYTES_ARRAY_T(256) meshtastic_MeshPacket_encrypted_t;
|
||||
/* A packet envelope sent/received over the mesh
|
||||
only payload_variant is sent in the payload portion of the LORA packet.
|
||||
@@ -578,12 +603,15 @@ typedef struct _meshtastic_MyNodeInfo {
|
||||
/* Tells the phone what our node number is, default starting value is
|
||||
lowbyte of macaddr, but it will be fixed if that is already in use */
|
||||
uint32_t my_node_num;
|
||||
/* Note: This flag merely means we detected a hardware GPS in our node.
|
||||
/* Deprecated in 2.1.x (Source from device_metadata)
|
||||
Note: This flag merely means we detected a hardware GPS in our node.
|
||||
Not the same as UserPreferences.location_sharing */
|
||||
bool has_gps;
|
||||
/* The maximum number of 'software' channels that can be set on this node. */
|
||||
/* Deprecated in 2.1.x
|
||||
The maximum number of 'software' channels that can be set on this node. */
|
||||
uint32_t max_channels;
|
||||
/* 0.0.5 etc... */
|
||||
/* Deprecated in 2.1.x (Source from device_metadata)
|
||||
0.0.5 etc... */
|
||||
char firmware_version[18];
|
||||
/* An error message we'd like to report back to the mothership through analytics.
|
||||
It indicates a serious bug occurred on the device, the device coped with it,
|
||||
@@ -600,9 +628,11 @@ typedef struct _meshtastic_MyNodeInfo {
|
||||
/* The total number of reboots this node has ever encountered
|
||||
(well - since the last time we discarded preferences) */
|
||||
uint32_t reboot_count;
|
||||
/* Calculated bitrate of the current channel (in Bytes Per Second) */
|
||||
/* Deprecated in 2.1.x
|
||||
Calculated bitrate of the current channel (in Bytes Per Second) */
|
||||
float bitrate;
|
||||
/* How long before we consider a message abandoned and we can clear our
|
||||
/* Deprecated in 2.1.x
|
||||
How long before we consider a message abandoned and we can clear our
|
||||
caches of any messages in flight Normally quite large to handle the worst case
|
||||
message delivery time, 5 minutes.
|
||||
Formerly called FLOOD_EXPIRE_TIME in the device code */
|
||||
@@ -610,17 +640,22 @@ typedef struct _meshtastic_MyNodeInfo {
|
||||
/* The minimum app version that can talk to this device.
|
||||
Phone/PC apps should compare this to their build number and if too low tell the user they must update their app */
|
||||
uint32_t min_app_version;
|
||||
/* 24 time windows of 1hr each with the airtime transmitted out of the device per hour. */
|
||||
/* Deprecated in 2.1.x (Only used on device to keep track of utilization)
|
||||
24 time windows of 1hr each with the airtime transmitted out of the device per hour. */
|
||||
pb_size_t air_period_tx_count;
|
||||
uint32_t air_period_tx[8];
|
||||
/* 24 time windows of 1hr each with the airtime of valid packets for your mesh. */
|
||||
/* Deprecated in 2.1.x (Only used on device to keep track of utilization)
|
||||
24 time windows of 1hr each with the airtime of valid packets for your mesh. */
|
||||
pb_size_t air_period_rx_count;
|
||||
uint32_t air_period_rx[8];
|
||||
/* Is the device wifi capable? */
|
||||
/* Deprecated in 2.1.x (Source from DeviceMetadata instead)
|
||||
Is the device wifi capable? */
|
||||
bool has_wifi;
|
||||
/* Utilization for the current channel, including well formed TX, RX and malformed RX (aka noise). */
|
||||
/* Deprecated in 2.1.x (Source from DeviceMetrics telemetry payloads)
|
||||
Utilization for the current channel, including well formed TX, RX and malformed RX (aka noise). */
|
||||
float channel_utilization;
|
||||
/* Percent of airtime for transmission used within the last hour. */
|
||||
/* Deprecated in 2.1.x (Source from DeviceMetrics telemetry payloads)
|
||||
Percent of airtime for transmission used within the last hour. */
|
||||
float air_util_tx;
|
||||
} meshtastic_MyNodeInfo;
|
||||
|
||||
@@ -672,6 +707,8 @@ typedef struct _meshtastic_ToRadio {
|
||||
(Sending this message is optional for clients) */
|
||||
bool disconnect;
|
||||
meshtastic_XModem xmodemPacket;
|
||||
/* MQTT Client Proxy Message (for client / phone subscribed to MQTT sending to device) */
|
||||
meshtastic_MqttClientProxyMessage mqttClientProxyMessage;
|
||||
};
|
||||
} meshtastic_ToRadio;
|
||||
|
||||
@@ -769,6 +806,8 @@ typedef struct _meshtastic_FromRadio {
|
||||
meshtastic_XModem xmodemPacket;
|
||||
/* Device metadata message */
|
||||
meshtastic_DeviceMetadata metadata;
|
||||
/* MQTT Client Proxy Message (device sending to client / phone for publishing to MQTT) */
|
||||
meshtastic_MqttClientProxyMessage mqttClientProxyMessage;
|
||||
};
|
||||
} meshtastic_FromRadio;
|
||||
|
||||
@@ -825,6 +864,7 @@ extern "C" {
|
||||
#define meshtastic_Data_portnum_ENUMTYPE meshtastic_PortNum
|
||||
|
||||
|
||||
|
||||
#define meshtastic_MeshPacket_priority_ENUMTYPE meshtastic_MeshPacket_Priority
|
||||
#define meshtastic_MeshPacket_delayed_ENUMTYPE meshtastic_MeshPacket_Delayed
|
||||
|
||||
@@ -851,6 +891,7 @@ extern "C" {
|
||||
#define meshtastic_Routing_init_default {0, {meshtastic_RouteDiscovery_init_default}}
|
||||
#define meshtastic_Data_init_default {_meshtastic_PortNum_MIN, {0, {0}}, 0, 0, 0, 0, 0, 0}
|
||||
#define meshtastic_Waypoint_init_default {0, 0, 0, 0, 0, "", "", 0}
|
||||
#define meshtastic_MqttClientProxyMessage_init_default {"", 0, {{0, {0}}}, 0}
|
||||
#define meshtastic_MeshPacket_init_default {0, 0, 0, 0, {meshtastic_Data_init_default}, 0, 0, 0, 0, 0, _meshtastic_MeshPacket_Priority_MIN, 0, _meshtastic_MeshPacket_Delayed_MIN}
|
||||
#define meshtastic_NodeInfo_init_default {0, false, meshtastic_User_init_default, false, meshtastic_Position_init_default, 0, 0, false, meshtastic_DeviceMetrics_init_default, 0}
|
||||
#define meshtastic_MyNodeInfo_init_default {0, 0, 0, "", _meshtastic_CriticalErrorCode_MIN, 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}
|
||||
@@ -868,6 +909,7 @@ extern "C" {
|
||||
#define meshtastic_Routing_init_zero {0, {meshtastic_RouteDiscovery_init_zero}}
|
||||
#define meshtastic_Data_init_zero {_meshtastic_PortNum_MIN, {0, {0}}, 0, 0, 0, 0, 0, 0}
|
||||
#define meshtastic_Waypoint_init_zero {0, 0, 0, 0, 0, "", "", 0}
|
||||
#define meshtastic_MqttClientProxyMessage_init_zero {"", 0, {{0, {0}}}, 0}
|
||||
#define meshtastic_MeshPacket_init_zero {0, 0, 0, 0, {meshtastic_Data_init_zero}, 0, 0, 0, 0, 0, _meshtastic_MeshPacket_Priority_MIN, 0, _meshtastic_MeshPacket_Delayed_MIN}
|
||||
#define meshtastic_NodeInfo_init_zero {0, false, meshtastic_User_init_zero, false, meshtastic_Position_init_zero, 0, 0, false, meshtastic_DeviceMetrics_init_zero, 0}
|
||||
#define meshtastic_MyNodeInfo_init_zero {0, 0, 0, "", _meshtastic_CriticalErrorCode_MIN, 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}
|
||||
@@ -929,6 +971,10 @@ extern "C" {
|
||||
#define meshtastic_Waypoint_name_tag 6
|
||||
#define meshtastic_Waypoint_description_tag 7
|
||||
#define meshtastic_Waypoint_icon_tag 8
|
||||
#define meshtastic_MqttClientProxyMessage_topic_tag 1
|
||||
#define meshtastic_MqttClientProxyMessage_data_tag 2
|
||||
#define meshtastic_MqttClientProxyMessage_text_tag 3
|
||||
#define meshtastic_MqttClientProxyMessage_retained_tag 4
|
||||
#define meshtastic_MeshPacket_from_tag 1
|
||||
#define meshtastic_MeshPacket_to_tag 2
|
||||
#define meshtastic_MeshPacket_channel_tag 3
|
||||
@@ -977,6 +1023,7 @@ extern "C" {
|
||||
#define meshtastic_ToRadio_want_config_id_tag 3
|
||||
#define meshtastic_ToRadio_disconnect_tag 4
|
||||
#define meshtastic_ToRadio_xmodemPacket_tag 5
|
||||
#define meshtastic_ToRadio_mqttClientProxyMessage_tag 6
|
||||
#define meshtastic_Compressed_portnum_tag 1
|
||||
#define meshtastic_Compressed_data_tag 2
|
||||
#define meshtastic_Neighbor_node_id_tag 1
|
||||
@@ -1007,6 +1054,7 @@ extern "C" {
|
||||
#define meshtastic_FromRadio_queueStatus_tag 11
|
||||
#define meshtastic_FromRadio_xmodemPacket_tag 12
|
||||
#define meshtastic_FromRadio_metadata_tag 13
|
||||
#define meshtastic_FromRadio_mqttClientProxyMessage_tag 14
|
||||
|
||||
/* Struct field encoding specification for nanopb */
|
||||
#define meshtastic_Position_FIELDLIST(X, a) \
|
||||
@@ -1083,6 +1131,14 @@ X(a, STATIC, SINGULAR, FIXED32, icon, 8)
|
||||
#define meshtastic_Waypoint_CALLBACK NULL
|
||||
#define meshtastic_Waypoint_DEFAULT NULL
|
||||
|
||||
#define meshtastic_MqttClientProxyMessage_FIELDLIST(X, a) \
|
||||
X(a, STATIC, SINGULAR, STRING, topic, 1) \
|
||||
X(a, STATIC, ONEOF, BYTES, (payload_variant,data,payload_variant.data), 2) \
|
||||
X(a, STATIC, ONEOF, STRING, (payload_variant,text,payload_variant.text), 3) \
|
||||
X(a, STATIC, SINGULAR, BOOL, retained, 4)
|
||||
#define meshtastic_MqttClientProxyMessage_CALLBACK NULL
|
||||
#define meshtastic_MqttClientProxyMessage_DEFAULT NULL
|
||||
|
||||
#define meshtastic_MeshPacket_FIELDLIST(X, a) \
|
||||
X(a, STATIC, SINGULAR, FIXED32, from, 1) \
|
||||
X(a, STATIC, SINGULAR, FIXED32, to, 2) \
|
||||
@@ -1164,7 +1220,8 @@ X(a, STATIC, ONEOF, MESSAGE, (payload_variant,moduleConfig,moduleConfig),
|
||||
X(a, STATIC, ONEOF, MESSAGE, (payload_variant,channel,channel), 10) \
|
||||
X(a, STATIC, ONEOF, MESSAGE, (payload_variant,queueStatus,queueStatus), 11) \
|
||||
X(a, STATIC, ONEOF, MESSAGE, (payload_variant,xmodemPacket,xmodemPacket), 12) \
|
||||
X(a, STATIC, ONEOF, MESSAGE, (payload_variant,metadata,metadata), 13)
|
||||
X(a, STATIC, ONEOF, MESSAGE, (payload_variant,metadata,metadata), 13) \
|
||||
X(a, STATIC, ONEOF, MESSAGE, (payload_variant,mqttClientProxyMessage,mqttClientProxyMessage), 14)
|
||||
#define meshtastic_FromRadio_CALLBACK NULL
|
||||
#define meshtastic_FromRadio_DEFAULT NULL
|
||||
#define meshtastic_FromRadio_payload_variant_packet_MSGTYPE meshtastic_MeshPacket
|
||||
@@ -1177,16 +1234,19 @@ X(a, STATIC, ONEOF, MESSAGE, (payload_variant,metadata,metadata), 13)
|
||||
#define meshtastic_FromRadio_payload_variant_queueStatus_MSGTYPE meshtastic_QueueStatus
|
||||
#define meshtastic_FromRadio_payload_variant_xmodemPacket_MSGTYPE meshtastic_XModem
|
||||
#define meshtastic_FromRadio_payload_variant_metadata_MSGTYPE meshtastic_DeviceMetadata
|
||||
#define meshtastic_FromRadio_payload_variant_mqttClientProxyMessage_MSGTYPE meshtastic_MqttClientProxyMessage
|
||||
|
||||
#define meshtastic_ToRadio_FIELDLIST(X, a) \
|
||||
X(a, STATIC, ONEOF, MESSAGE, (payload_variant,packet,packet), 1) \
|
||||
X(a, STATIC, ONEOF, UINT32, (payload_variant,want_config_id,want_config_id), 3) \
|
||||
X(a, STATIC, ONEOF, BOOL, (payload_variant,disconnect,disconnect), 4) \
|
||||
X(a, STATIC, ONEOF, MESSAGE, (payload_variant,xmodemPacket,xmodemPacket), 5)
|
||||
X(a, STATIC, ONEOF, MESSAGE, (payload_variant,xmodemPacket,xmodemPacket), 5) \
|
||||
X(a, STATIC, ONEOF, MESSAGE, (payload_variant,mqttClientProxyMessage,mqttClientProxyMessage), 6)
|
||||
#define meshtastic_ToRadio_CALLBACK NULL
|
||||
#define meshtastic_ToRadio_DEFAULT NULL
|
||||
#define meshtastic_ToRadio_payload_variant_packet_MSGTYPE meshtastic_MeshPacket
|
||||
#define meshtastic_ToRadio_payload_variant_xmodemPacket_MSGTYPE meshtastic_XModem
|
||||
#define meshtastic_ToRadio_payload_variant_mqttClientProxyMessage_MSGTYPE meshtastic_MqttClientProxyMessage
|
||||
|
||||
#define meshtastic_Compressed_FIELDLIST(X, a) \
|
||||
X(a, STATIC, SINGULAR, UENUM, portnum, 1) \
|
||||
@@ -1228,6 +1288,7 @@ extern const pb_msgdesc_t meshtastic_RouteDiscovery_msg;
|
||||
extern const pb_msgdesc_t meshtastic_Routing_msg;
|
||||
extern const pb_msgdesc_t meshtastic_Data_msg;
|
||||
extern const pb_msgdesc_t meshtastic_Waypoint_msg;
|
||||
extern const pb_msgdesc_t meshtastic_MqttClientProxyMessage_msg;
|
||||
extern const pb_msgdesc_t meshtastic_MeshPacket_msg;
|
||||
extern const pb_msgdesc_t meshtastic_NodeInfo_msg;
|
||||
extern const pb_msgdesc_t meshtastic_MyNodeInfo_msg;
|
||||
@@ -1247,6 +1308,7 @@ extern const pb_msgdesc_t meshtastic_DeviceMetadata_msg;
|
||||
#define meshtastic_Routing_fields &meshtastic_Routing_msg
|
||||
#define meshtastic_Data_fields &meshtastic_Data_msg
|
||||
#define meshtastic_Waypoint_fields &meshtastic_Waypoint_msg
|
||||
#define meshtastic_MqttClientProxyMessage_fields &meshtastic_MqttClientProxyMessage_msg
|
||||
#define meshtastic_MeshPacket_fields &meshtastic_MeshPacket_msg
|
||||
#define meshtastic_NodeInfo_fields &meshtastic_NodeInfo_msg
|
||||
#define meshtastic_MyNodeInfo_fields &meshtastic_MyNodeInfo_msg
|
||||
@@ -1263,9 +1325,10 @@ extern const pb_msgdesc_t meshtastic_DeviceMetadata_msg;
|
||||
#define meshtastic_Compressed_size 243
|
||||
#define meshtastic_Data_size 270
|
||||
#define meshtastic_DeviceMetadata_size 46
|
||||
#define meshtastic_FromRadio_size 330
|
||||
#define meshtastic_FromRadio_size 510
|
||||
#define meshtastic_LogRecord_size 81
|
||||
#define meshtastic_MeshPacket_size 321
|
||||
#define meshtastic_MqttClientProxyMessage_size 501
|
||||
#define meshtastic_MyNodeInfo_size 179
|
||||
#define meshtastic_NeighborInfo_size 142
|
||||
#define meshtastic_Neighbor_size 11
|
||||
@@ -1274,7 +1337,7 @@ extern const pb_msgdesc_t meshtastic_DeviceMetadata_msg;
|
||||
#define meshtastic_QueueStatus_size 23
|
||||
#define meshtastic_RouteDiscovery_size 40
|
||||
#define meshtastic_Routing_size 42
|
||||
#define meshtastic_ToRadio_size 324
|
||||
#define meshtastic_ToRadio_size 504
|
||||
#define meshtastic_User_size 77
|
||||
#define meshtastic_Waypoint_size 165
|
||||
|
||||
|
||||
@@ -39,6 +39,9 @@ PB_BIND(meshtastic_ModuleConfig_TelemetryConfig, meshtastic_ModuleConfig_Telemet
|
||||
PB_BIND(meshtastic_ModuleConfig_CannedMessageConfig, meshtastic_ModuleConfig_CannedMessageConfig, AUTO)
|
||||
|
||||
|
||||
PB_BIND(meshtastic_ModuleConfig_AmbientLightingConfig, meshtastic_ModuleConfig_AmbientLightingConfig, AUTO)
|
||||
|
||||
|
||||
PB_BIND(meshtastic_RemoteHardwarePin, meshtastic_RemoteHardwarePin, AUTO)
|
||||
|
||||
|
||||
|
||||
@@ -112,6 +112,8 @@ typedef struct _meshtastic_ModuleConfig_MQTTConfig {
|
||||
/* The root topic to use for MQTT messages. Default is "msh".
|
||||
This is useful if you want to use a single MQTT server for multiple meshtastic networks and separate them via ACLs */
|
||||
char root[16];
|
||||
/* If true, we can use the connected phone / client to proxy messages to MQTT instead of a direct connection */
|
||||
bool proxy_to_client_enabled;
|
||||
} meshtastic_ModuleConfig_MQTTConfig;
|
||||
|
||||
/* NeighborInfoModule Config */
|
||||
@@ -279,6 +281,20 @@ typedef struct _meshtastic_ModuleConfig_CannedMessageConfig {
|
||||
bool send_bell;
|
||||
} meshtastic_ModuleConfig_CannedMessageConfig;
|
||||
|
||||
/* Ambient Lighting Module - Settings for control of onboard LEDs to allow users to adjust the brightness levels and respective color levels.
|
||||
Initially created for the RAK14001 RGB LED module. */
|
||||
typedef struct _meshtastic_ModuleConfig_AmbientLightingConfig {
|
||||
/* Sets LED to on or off. */
|
||||
bool led_state;
|
||||
/* Sets the overall current for the LED, firmware side range for the RAK14001 is 1-31, but users should be given a range of 0-100% */
|
||||
uint8_t current;
|
||||
uint8_t red; /* Red level */
|
||||
/* Sets the green level of the LED, firmware side values are 0-255, but users should be given a range of 0-100% */
|
||||
uint8_t green; /* Green level */
|
||||
/* Sets the blue level of the LED, firmware side values are 0-255, but users should be given a range of 0-100% */
|
||||
uint8_t blue; /* Blue level */
|
||||
} meshtastic_ModuleConfig_AmbientLightingConfig;
|
||||
|
||||
/* A GPIO pin definition for remote hardware module */
|
||||
typedef struct _meshtastic_RemoteHardwarePin {
|
||||
/* GPIO Pin number (must match Arduino) */
|
||||
@@ -324,6 +340,8 @@ typedef struct _meshtastic_ModuleConfig {
|
||||
meshtastic_ModuleConfig_RemoteHardwareConfig remote_hardware;
|
||||
/* TODO: REPLACE */
|
||||
meshtastic_ModuleConfig_NeighborInfoConfig neighbor_info;
|
||||
/* TODO: REPLACE */
|
||||
meshtastic_ModuleConfig_AmbientLightingConfig ambient_lighting;
|
||||
} payload_variant;
|
||||
} meshtastic_ModuleConfig;
|
||||
|
||||
@@ -370,12 +388,13 @@ extern "C" {
|
||||
#define meshtastic_ModuleConfig_CannedMessageConfig_inputbroker_event_ccw_ENUMTYPE meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar
|
||||
#define meshtastic_ModuleConfig_CannedMessageConfig_inputbroker_event_press_ENUMTYPE meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar
|
||||
|
||||
|
||||
#define meshtastic_RemoteHardwarePin_type_ENUMTYPE meshtastic_RemoteHardwarePinType
|
||||
|
||||
|
||||
/* Initializer values for message structs */
|
||||
#define meshtastic_ModuleConfig_init_default {0, {meshtastic_ModuleConfig_MQTTConfig_init_default}}
|
||||
#define meshtastic_ModuleConfig_MQTTConfig_init_default {0, "", "", "", 0, 0, 0, ""}
|
||||
#define meshtastic_ModuleConfig_MQTTConfig_init_default {0, "", "", "", 0, 0, 0, "", 0}
|
||||
#define meshtastic_ModuleConfig_RemoteHardwareConfig_init_default {0, 0, 0, {meshtastic_RemoteHardwarePin_init_default, meshtastic_RemoteHardwarePin_init_default, meshtastic_RemoteHardwarePin_init_default, meshtastic_RemoteHardwarePin_init_default}}
|
||||
#define meshtastic_ModuleConfig_NeighborInfoConfig_init_default {0, 0}
|
||||
#define meshtastic_ModuleConfig_AudioConfig_init_default {0, 0, _meshtastic_ModuleConfig_AudioConfig_Audio_Baud_MIN, 0, 0, 0, 0}
|
||||
@@ -385,9 +404,10 @@ extern "C" {
|
||||
#define meshtastic_ModuleConfig_RangeTestConfig_init_default {0, 0, 0}
|
||||
#define meshtastic_ModuleConfig_TelemetryConfig_init_default {0, 0, 0, 0, 0, 0, 0}
|
||||
#define meshtastic_ModuleConfig_CannedMessageConfig_init_default {0, 0, 0, 0, _meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_MIN, _meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_MIN, _meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_MIN, 0, 0, "", 0}
|
||||
#define meshtastic_ModuleConfig_AmbientLightingConfig_init_default {0, 0, 0, 0, 0}
|
||||
#define meshtastic_RemoteHardwarePin_init_default {0, "", _meshtastic_RemoteHardwarePinType_MIN}
|
||||
#define meshtastic_ModuleConfig_init_zero {0, {meshtastic_ModuleConfig_MQTTConfig_init_zero}}
|
||||
#define meshtastic_ModuleConfig_MQTTConfig_init_zero {0, "", "", "", 0, 0, 0, ""}
|
||||
#define meshtastic_ModuleConfig_MQTTConfig_init_zero {0, "", "", "", 0, 0, 0, "", 0}
|
||||
#define meshtastic_ModuleConfig_RemoteHardwareConfig_init_zero {0, 0, 0, {meshtastic_RemoteHardwarePin_init_zero, meshtastic_RemoteHardwarePin_init_zero, meshtastic_RemoteHardwarePin_init_zero, meshtastic_RemoteHardwarePin_init_zero}}
|
||||
#define meshtastic_ModuleConfig_NeighborInfoConfig_init_zero {0, 0}
|
||||
#define meshtastic_ModuleConfig_AudioConfig_init_zero {0, 0, _meshtastic_ModuleConfig_AudioConfig_Audio_Baud_MIN, 0, 0, 0, 0}
|
||||
@@ -397,6 +417,7 @@ extern "C" {
|
||||
#define meshtastic_ModuleConfig_RangeTestConfig_init_zero {0, 0, 0}
|
||||
#define meshtastic_ModuleConfig_TelemetryConfig_init_zero {0, 0, 0, 0, 0, 0, 0}
|
||||
#define meshtastic_ModuleConfig_CannedMessageConfig_init_zero {0, 0, 0, 0, _meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_MIN, _meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_MIN, _meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_MIN, 0, 0, "", 0}
|
||||
#define meshtastic_ModuleConfig_AmbientLightingConfig_init_zero {0, 0, 0, 0, 0}
|
||||
#define meshtastic_RemoteHardwarePin_init_zero {0, "", _meshtastic_RemoteHardwarePinType_MIN}
|
||||
|
||||
/* Field tags (for use in manual encoding/decoding) */
|
||||
@@ -408,6 +429,7 @@ extern "C" {
|
||||
#define meshtastic_ModuleConfig_MQTTConfig_json_enabled_tag 6
|
||||
#define meshtastic_ModuleConfig_MQTTConfig_tls_enabled_tag 7
|
||||
#define meshtastic_ModuleConfig_MQTTConfig_root_tag 8
|
||||
#define meshtastic_ModuleConfig_MQTTConfig_proxy_to_client_enabled_tag 9
|
||||
#define meshtastic_ModuleConfig_NeighborInfoConfig_enabled_tag 1
|
||||
#define meshtastic_ModuleConfig_NeighborInfoConfig_update_interval_tag 2
|
||||
#define meshtastic_ModuleConfig_AudioConfig_codec2_enabled_tag 1
|
||||
@@ -465,6 +487,11 @@ extern "C" {
|
||||
#define meshtastic_ModuleConfig_CannedMessageConfig_enabled_tag 9
|
||||
#define meshtastic_ModuleConfig_CannedMessageConfig_allow_input_source_tag 10
|
||||
#define meshtastic_ModuleConfig_CannedMessageConfig_send_bell_tag 11
|
||||
#define meshtastic_ModuleConfig_AmbientLightingConfig_led_state_tag 1
|
||||
#define meshtastic_ModuleConfig_AmbientLightingConfig_current_tag 2
|
||||
#define meshtastic_ModuleConfig_AmbientLightingConfig_red_tag 3
|
||||
#define meshtastic_ModuleConfig_AmbientLightingConfig_green_tag 4
|
||||
#define meshtastic_ModuleConfig_AmbientLightingConfig_blue_tag 5
|
||||
#define meshtastic_RemoteHardwarePin_gpio_pin_tag 1
|
||||
#define meshtastic_RemoteHardwarePin_name_tag 2
|
||||
#define meshtastic_RemoteHardwarePin_type_tag 3
|
||||
@@ -481,6 +508,7 @@ extern "C" {
|
||||
#define meshtastic_ModuleConfig_audio_tag 8
|
||||
#define meshtastic_ModuleConfig_remote_hardware_tag 9
|
||||
#define meshtastic_ModuleConfig_neighbor_info_tag 10
|
||||
#define meshtastic_ModuleConfig_ambient_lighting_tag 11
|
||||
|
||||
/* Struct field encoding specification for nanopb */
|
||||
#define meshtastic_ModuleConfig_FIELDLIST(X, a) \
|
||||
@@ -493,7 +521,8 @@ X(a, STATIC, ONEOF, MESSAGE, (payload_variant,telemetry,payload_variant.te
|
||||
X(a, STATIC, ONEOF, MESSAGE, (payload_variant,canned_message,payload_variant.canned_message), 7) \
|
||||
X(a, STATIC, ONEOF, MESSAGE, (payload_variant,audio,payload_variant.audio), 8) \
|
||||
X(a, STATIC, ONEOF, MESSAGE, (payload_variant,remote_hardware,payload_variant.remote_hardware), 9) \
|
||||
X(a, STATIC, ONEOF, MESSAGE, (payload_variant,neighbor_info,payload_variant.neighbor_info), 10)
|
||||
X(a, STATIC, ONEOF, MESSAGE, (payload_variant,neighbor_info,payload_variant.neighbor_info), 10) \
|
||||
X(a, STATIC, ONEOF, MESSAGE, (payload_variant,ambient_lighting,payload_variant.ambient_lighting), 11)
|
||||
#define meshtastic_ModuleConfig_CALLBACK NULL
|
||||
#define meshtastic_ModuleConfig_DEFAULT NULL
|
||||
#define meshtastic_ModuleConfig_payload_variant_mqtt_MSGTYPE meshtastic_ModuleConfig_MQTTConfig
|
||||
@@ -506,6 +535,7 @@ X(a, STATIC, ONEOF, MESSAGE, (payload_variant,neighbor_info,payload_varian
|
||||
#define meshtastic_ModuleConfig_payload_variant_audio_MSGTYPE meshtastic_ModuleConfig_AudioConfig
|
||||
#define meshtastic_ModuleConfig_payload_variant_remote_hardware_MSGTYPE meshtastic_ModuleConfig_RemoteHardwareConfig
|
||||
#define meshtastic_ModuleConfig_payload_variant_neighbor_info_MSGTYPE meshtastic_ModuleConfig_NeighborInfoConfig
|
||||
#define meshtastic_ModuleConfig_payload_variant_ambient_lighting_MSGTYPE meshtastic_ModuleConfig_AmbientLightingConfig
|
||||
|
||||
#define meshtastic_ModuleConfig_MQTTConfig_FIELDLIST(X, a) \
|
||||
X(a, STATIC, SINGULAR, BOOL, enabled, 1) \
|
||||
@@ -515,7 +545,8 @@ X(a, STATIC, SINGULAR, STRING, password, 4) \
|
||||
X(a, STATIC, SINGULAR, BOOL, encryption_enabled, 5) \
|
||||
X(a, STATIC, SINGULAR, BOOL, json_enabled, 6) \
|
||||
X(a, STATIC, SINGULAR, BOOL, tls_enabled, 7) \
|
||||
X(a, STATIC, SINGULAR, STRING, root, 8)
|
||||
X(a, STATIC, SINGULAR, STRING, root, 8) \
|
||||
X(a, STATIC, SINGULAR, BOOL, proxy_to_client_enabled, 9)
|
||||
#define meshtastic_ModuleConfig_MQTTConfig_CALLBACK NULL
|
||||
#define meshtastic_ModuleConfig_MQTTConfig_DEFAULT NULL
|
||||
|
||||
@@ -616,6 +647,15 @@ X(a, STATIC, SINGULAR, BOOL, send_bell, 11)
|
||||
#define meshtastic_ModuleConfig_CannedMessageConfig_CALLBACK NULL
|
||||
#define meshtastic_ModuleConfig_CannedMessageConfig_DEFAULT NULL
|
||||
|
||||
#define meshtastic_ModuleConfig_AmbientLightingConfig_FIELDLIST(X, a) \
|
||||
X(a, STATIC, SINGULAR, BOOL, led_state, 1) \
|
||||
X(a, STATIC, SINGULAR, UINT32, current, 2) \
|
||||
X(a, STATIC, SINGULAR, UINT32, red, 3) \
|
||||
X(a, STATIC, SINGULAR, UINT32, green, 4) \
|
||||
X(a, STATIC, SINGULAR, UINT32, blue, 5)
|
||||
#define meshtastic_ModuleConfig_AmbientLightingConfig_CALLBACK NULL
|
||||
#define meshtastic_ModuleConfig_AmbientLightingConfig_DEFAULT NULL
|
||||
|
||||
#define meshtastic_RemoteHardwarePin_FIELDLIST(X, a) \
|
||||
X(a, STATIC, SINGULAR, UINT32, gpio_pin, 1) \
|
||||
X(a, STATIC, SINGULAR, STRING, name, 2) \
|
||||
@@ -634,6 +674,7 @@ extern const pb_msgdesc_t meshtastic_ModuleConfig_StoreForwardConfig_msg;
|
||||
extern const pb_msgdesc_t meshtastic_ModuleConfig_RangeTestConfig_msg;
|
||||
extern const pb_msgdesc_t meshtastic_ModuleConfig_TelemetryConfig_msg;
|
||||
extern const pb_msgdesc_t meshtastic_ModuleConfig_CannedMessageConfig_msg;
|
||||
extern const pb_msgdesc_t meshtastic_ModuleConfig_AmbientLightingConfig_msg;
|
||||
extern const pb_msgdesc_t meshtastic_RemoteHardwarePin_msg;
|
||||
|
||||
/* Defines for backwards compatibility with code written before nanopb-0.4.0 */
|
||||
@@ -648,20 +689,22 @@ extern const pb_msgdesc_t meshtastic_RemoteHardwarePin_msg;
|
||||
#define meshtastic_ModuleConfig_RangeTestConfig_fields &meshtastic_ModuleConfig_RangeTestConfig_msg
|
||||
#define meshtastic_ModuleConfig_TelemetryConfig_fields &meshtastic_ModuleConfig_TelemetryConfig_msg
|
||||
#define meshtastic_ModuleConfig_CannedMessageConfig_fields &meshtastic_ModuleConfig_CannedMessageConfig_msg
|
||||
#define meshtastic_ModuleConfig_AmbientLightingConfig_fields &meshtastic_ModuleConfig_AmbientLightingConfig_msg
|
||||
#define meshtastic_RemoteHardwarePin_fields &meshtastic_RemoteHardwarePin_msg
|
||||
|
||||
/* Maximum encoded size of messages (where known) */
|
||||
#define meshtastic_ModuleConfig_AmbientLightingConfig_size 14
|
||||
#define meshtastic_ModuleConfig_AudioConfig_size 19
|
||||
#define meshtastic_ModuleConfig_CannedMessageConfig_size 49
|
||||
#define meshtastic_ModuleConfig_ExternalNotificationConfig_size 40
|
||||
#define meshtastic_ModuleConfig_MQTTConfig_size 220
|
||||
#define meshtastic_ModuleConfig_MQTTConfig_size 222
|
||||
#define meshtastic_ModuleConfig_NeighborInfoConfig_size 8
|
||||
#define meshtastic_ModuleConfig_RangeTestConfig_size 10
|
||||
#define meshtastic_ModuleConfig_RemoteHardwareConfig_size 96
|
||||
#define meshtastic_ModuleConfig_SerialConfig_size 28
|
||||
#define meshtastic_ModuleConfig_StoreForwardConfig_size 22
|
||||
#define meshtastic_ModuleConfig_TelemetryConfig_size 26
|
||||
#define meshtastic_ModuleConfig_size 223
|
||||
#define meshtastic_ModuleConfig_size 225
|
||||
#define meshtastic_RemoteHardwarePin_size 21
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
||||
@@ -54,6 +54,8 @@ typedef enum _meshtastic_PortNum {
|
||||
/* Audio Payloads.
|
||||
Encapsulated codec2 packets. On 2.4 GHZ Bandwidths only for now */
|
||||
meshtastic_PortNum_AUDIO_APP = 9,
|
||||
/* Payloads for clients with a network connection proxying MQTT pub/sub to the device */
|
||||
meshtastic_PortNum_MQTT_CLIENT_PROXY_APP = 10,
|
||||
/* Provides a 'ping' service that replies to any packet it receives.
|
||||
Also serves as a small example module. */
|
||||
meshtastic_PortNum_REPLY_APP = 32,
|
||||
|
||||
@@ -63,7 +63,7 @@ typedef struct _meshtastic_EnvironmentMetrics {
|
||||
float relative_humidity;
|
||||
/* Barometric pressure in hPA measured */
|
||||
float barometric_pressure;
|
||||
/* Gas resistance in mOhm measured */
|
||||
/* Gas resistance in MOhm measured */
|
||||
float gas_resistance;
|
||||
/* Voltage measured */
|
||||
float voltage;
|
||||
|
||||
@@ -165,7 +165,7 @@ void handleAPIv1FromRadio(HTTPRequest *req, HTTPResponse *res)
|
||||
|
||||
if (params->getQueryParameter("all", valueAll)) {
|
||||
|
||||
// If all is ture, return all the buffers we have available
|
||||
// If all is true, return all the buffers we have available
|
||||
// to us at this point in time.
|
||||
if (valueAll == "true") {
|
||||
while (len) {
|
||||
@@ -179,7 +179,7 @@ void handleAPIv1FromRadio(HTTPRequest *req, HTTPResponse *res)
|
||||
res->write(txBuf, len);
|
||||
}
|
||||
|
||||
// the param "all" was not spcified. Return just one protobuf
|
||||
// the param "all" was not specified. Return just one protobuf
|
||||
} else {
|
||||
len = webAPI.getFromRadio(txBuf);
|
||||
res->write(txBuf, len);
|
||||
@@ -460,7 +460,7 @@ void handleFormUpload(HTTPRequest *req, HTTPResponse *res)
|
||||
HTTPBodyParser *parser;
|
||||
std::string contentType = req->getHeader("Content-Type");
|
||||
|
||||
// The content type may have additional properties after a semicolon, for exampel:
|
||||
// The content type may have additional properties after a semicolon, for example:
|
||||
// Content-Type: text/html;charset=utf-8
|
||||
// Content-Type: multipart/form-data;boundary=------s0m3w31rdch4r4c73rs
|
||||
// As we're interested only in the actual mime _type_, we strip everything after the
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
#include "esp_task_wdt.h"
|
||||
#endif
|
||||
|
||||
// Persistant Data Storage
|
||||
// Persistent Data Storage
|
||||
#include <Preferences.h>
|
||||
Preferences prefs;
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@ size_t pb_encode_to_bytes(uint8_t *destbuf, size_t destbufsize, const pb_msgdesc
|
||||
if (!pb_encode(&stream, fields, src_struct)) {
|
||||
LOG_ERROR("Panic: can't encode protobuf reason='%s'\n", PB_GET_ERROR(&stream));
|
||||
assert(
|
||||
0); // If this asser fails it probably means you made a field too large for the max limits specified in mesh.options
|
||||
0); // If this assert fails it probably means you made a field too large for the max limits specified in mesh.options
|
||||
} else {
|
||||
return stream.bytes_written;
|
||||
}
|
||||
|
||||
@@ -16,9 +16,7 @@
|
||||
#include "unistd.h"
|
||||
#endif
|
||||
|
||||
#if HAS_WIFI || HAS_ETHERNET
|
||||
#include "mqtt/MQTT.h"
|
||||
#endif
|
||||
|
||||
#define DEFAULT_REBOOT_SECONDS 7
|
||||
|
||||
@@ -567,7 +565,7 @@ 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();
|
||||
conn.wifi.status.is_mqtt_connected = mqtt && mqtt->connected();
|
||||
conn.wifi.status.is_mqtt_connected = mqtt && mqtt->isConnectedDirectly();
|
||||
conn.wifi.status.is_syslog_connected = false; // FIXME wire this up
|
||||
}
|
||||
#endif
|
||||
@@ -578,7 +576,7 @@ void AdminModule::handleGetDeviceConnectionStatus(const meshtastic_MeshPacket &r
|
||||
if (Ethernet.linkStatus() == LinkON) {
|
||||
conn.ethernet.status.is_connected = true;
|
||||
conn.ethernet.status.ip_address = Ethernet.localIP();
|
||||
conn.ethernet.status.is_mqtt_connected = mqtt && mqtt->connected();
|
||||
conn.ethernet.status.is_mqtt_connected = mqtt && mqtt->isConnectedDirectly();
|
||||
conn.ethernet.status.is_syslog_connected = false; // FIXME wire this up
|
||||
} else {
|
||||
conn.ethernet.status.is_connected = false;
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
#include "FSCommon.h"
|
||||
#include "MeshService.h"
|
||||
#include "NodeDB.h"
|
||||
#include "PowerFSM.h" // neede for button bypass
|
||||
#include "PowerFSM.h" // needed for button bypass
|
||||
#include "detect/ScanI2C.h"
|
||||
#include "mesh/generated/meshtastic/cannedmessages.pb.h"
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
#include "graphics/fonts/OLEDDisplayFontsUA.h"
|
||||
#endif
|
||||
|
||||
#if defined(USE_EINK) || defined(ILI9341_DRIVER) || defined(ST7735_CS)
|
||||
#if defined(USE_EINK) || defined(ILI9341_DRIVER) || defined(ST7735_CS) || defined(ST7789_CS)
|
||||
// The screen is bigger so use bigger fonts
|
||||
#define FONT_SMALL ArialMT_Plain_16
|
||||
#define FONT_MEDIUM ArialMT_Plain_24
|
||||
@@ -123,8 +123,8 @@ int CannedMessageModule::splitConfiguredMessages()
|
||||
int CannedMessageModule::handleInputEvent(const InputEvent *event)
|
||||
{
|
||||
if ((strlen(moduleConfig.canned_message.allow_input_source) > 0) &&
|
||||
(strcmp(moduleConfig.canned_message.allow_input_source, event->source) != 0) &&
|
||||
(strcmp(moduleConfig.canned_message.allow_input_source, "_any") != 0)) {
|
||||
(strcasecmp(moduleConfig.canned_message.allow_input_source, event->source) != 0) &&
|
||||
(strcasecmp(moduleConfig.canned_message.allow_input_source, "_any") != 0)) {
|
||||
// Event source is not accepted.
|
||||
// Event only accepted if source matches the configured one, or
|
||||
// the configured one is "_any" (or if there is no configured
|
||||
@@ -305,13 +305,14 @@ int32_t CannedMessageModule::runOnce()
|
||||
switch (this->payload) {
|
||||
case 0xb4: // left
|
||||
if (this->destSelect) {
|
||||
size_t numNodes = nodeDB.getNumNodes();
|
||||
size_t numMeshNodes = nodeDB.getNumMeshNodes();
|
||||
if (this->dest == NODENUM_BROADCAST) {
|
||||
this->dest = nodeDB.getNodeNum();
|
||||
}
|
||||
for (unsigned int i = 0; i < numNodes; i++) {
|
||||
if (nodeDB.getNodeByIndex(i)->num == this->dest) {
|
||||
this->dest = (i > 0) ? nodeDB.getNodeByIndex(i - 1)->num : nodeDB.getNodeByIndex(numNodes - 1)->num;
|
||||
for (unsigned int i = 0; i < numMeshNodes; i++) {
|
||||
if (nodeDB.getMeshNodeByIndex(i)->num == this->dest) {
|
||||
this->dest =
|
||||
(i > 0) ? nodeDB.getMeshNodeByIndex(i - 1)->num : nodeDB.getMeshNodeByIndex(numMeshNodes - 1)->num;
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -326,13 +327,14 @@ int32_t CannedMessageModule::runOnce()
|
||||
break;
|
||||
case 0xb7: // right
|
||||
if (this->destSelect) {
|
||||
size_t numNodes = nodeDB.getNumNodes();
|
||||
size_t numMeshNodes = nodeDB.getNumMeshNodes();
|
||||
if (this->dest == NODENUM_BROADCAST) {
|
||||
this->dest = nodeDB.getNodeNum();
|
||||
}
|
||||
for (unsigned int i = 0; i < numNodes; i++) {
|
||||
if (nodeDB.getNodeByIndex(i)->num == this->dest) {
|
||||
this->dest = (i < numNodes - 1) ? nodeDB.getNodeByIndex(i + 1)->num : nodeDB.getNodeByIndex(0)->num;
|
||||
for (unsigned int i = 0; i < numMeshNodes; i++) {
|
||||
if (nodeDB.getMeshNodeByIndex(i)->num == this->dest) {
|
||||
this->dest =
|
||||
(i < numMeshNodes - 1) ? nodeDB.getMeshNodeByIndex(i + 1)->num : nodeDB.getMeshNodeByIndex(0)->num;
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -409,7 +411,7 @@ const char *CannedMessageModule::getNodeName(NodeNum node)
|
||||
if (node == NODENUM_BROADCAST) {
|
||||
return "Broadcast";
|
||||
} else {
|
||||
meshtastic_NodeInfo *info = nodeDB.getNode(node);
|
||||
meshtastic_NodeInfoLite *info = nodeDB.getMeshNode(node);
|
||||
if (info != NULL) {
|
||||
return info->user.long_name;
|
||||
} else {
|
||||
|
||||
@@ -10,8 +10,8 @@
|
||||
|
||||
#include "main.h"
|
||||
|
||||
#ifdef RAK4630
|
||||
#include <NCP5623.h>
|
||||
#ifdef HAS_NCP5623
|
||||
#include <graphics/RAKled.h>
|
||||
NCP5623 rgb;
|
||||
|
||||
uint8_t red = 0;
|
||||
@@ -84,7 +84,7 @@ int32_t ExternalNotificationModule::runOnce()
|
||||
millis()) {
|
||||
getExternal(2) ? setExternalOff(2) : setExternalOn(2);
|
||||
}
|
||||
#ifdef RAK4630
|
||||
#ifdef HAS_NCP5623
|
||||
if (rgb_found.type == ScanI2C::NCP5623) {
|
||||
green = (green + 50) % 255;
|
||||
red = abs(red - green) % 255;
|
||||
@@ -93,6 +93,10 @@ int32_t ExternalNotificationModule::runOnce()
|
||||
rgb.setColor(red, green, blue);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef T_WATCH_S3
|
||||
drv.go();
|
||||
#endif
|
||||
}
|
||||
|
||||
// now let the PWM buzzer play
|
||||
@@ -124,14 +128,18 @@ void ExternalNotificationModule::setExternalOn(uint8_t index)
|
||||
digitalWrite(moduleConfig.external_notification.output_buzzer, true);
|
||||
break;
|
||||
default:
|
||||
digitalWrite(output, (moduleConfig.external_notification.active ? true : false));
|
||||
if (output > 0)
|
||||
digitalWrite(output, (moduleConfig.external_notification.active ? true : false));
|
||||
break;
|
||||
}
|
||||
#ifdef RAK4630
|
||||
#ifdef HAS_NCP5623
|
||||
if (rgb_found.type == ScanI2C::NCP5623) {
|
||||
rgb.setColor(red, green, blue);
|
||||
}
|
||||
#endif
|
||||
#ifdef T_WATCH_S3
|
||||
drv.go();
|
||||
#endif
|
||||
}
|
||||
|
||||
void ExternalNotificationModule::setExternalOff(uint8_t index)
|
||||
@@ -149,11 +157,12 @@ void ExternalNotificationModule::setExternalOff(uint8_t index)
|
||||
digitalWrite(moduleConfig.external_notification.output_buzzer, false);
|
||||
break;
|
||||
default:
|
||||
digitalWrite(output, (moduleConfig.external_notification.active ? false : true));
|
||||
if (output > 0)
|
||||
digitalWrite(output, (moduleConfig.external_notification.active ? false : true));
|
||||
break;
|
||||
}
|
||||
|
||||
#ifdef RAK4630
|
||||
#ifdef HAS_NCP5623
|
||||
if (rgb_found.type == ScanI2C::NCP5623) {
|
||||
red = 0;
|
||||
green = 0;
|
||||
@@ -161,6 +170,9 @@ void ExternalNotificationModule::setExternalOff(uint8_t index)
|
||||
rgb.setColor(red, green, blue);
|
||||
}
|
||||
#endif
|
||||
#ifdef T_WATCH_S3
|
||||
drv.stop();
|
||||
#endif
|
||||
}
|
||||
|
||||
bool ExternalNotificationModule::getExternal(uint8_t index)
|
||||
@@ -174,6 +186,9 @@ void ExternalNotificationModule::stopNow()
|
||||
nagCycleCutoff = 1; // small value
|
||||
isNagging = false;
|
||||
setIntervalFromNow(0);
|
||||
#ifdef T_WATCH_S3
|
||||
drv.stop();
|
||||
#endif
|
||||
}
|
||||
|
||||
ExternalNotificationModule::ExternalNotificationModule()
|
||||
@@ -185,7 +200,6 @@ ExternalNotificationModule::ExternalNotificationModule()
|
||||
without having to configure it from the PythonAPI or WebUI.
|
||||
*/
|
||||
|
||||
// moduleConfig.external_notification.enabled = true;
|
||||
// moduleConfig.external_notification.alert_message = true;
|
||||
// moduleConfig.external_notification.alert_message_buzzer = true;
|
||||
// moduleConfig.external_notification.alert_message_vibra = true;
|
||||
@@ -213,8 +227,10 @@ ExternalNotificationModule::ExternalNotificationModule()
|
||||
: EXT_NOTIFICATION_MODULE_OUTPUT;
|
||||
|
||||
// Set the direction of a pin
|
||||
LOG_INFO("Using Pin %i in digital mode\n", output);
|
||||
pinMode(output, OUTPUT);
|
||||
if (output > 0) {
|
||||
LOG_INFO("Using Pin %i in digital mode\n", output);
|
||||
pinMode(output, OUTPUT);
|
||||
}
|
||||
setExternalOff(0);
|
||||
externalTurnedOn[0] = 0;
|
||||
if (moduleConfig.external_notification.output_vibra) {
|
||||
@@ -235,7 +251,7 @@ ExternalNotificationModule::ExternalNotificationModule()
|
||||
LOG_INFO("Using Pin %i in PWM mode\n", config.device.buzzer_gpio);
|
||||
}
|
||||
}
|
||||
#ifdef RAK4630
|
||||
#ifdef HAS_NCP5623
|
||||
if (rgb_found.type == ScanI2C::NCP5623) {
|
||||
rgb.begin();
|
||||
rgb.setCurrent(10);
|
||||
@@ -250,7 +266,12 @@ ExternalNotificationModule::ExternalNotificationModule()
|
||||
ProcessMessage ExternalNotificationModule::handleReceived(const meshtastic_MeshPacket &mp)
|
||||
{
|
||||
if (moduleConfig.external_notification.enabled) {
|
||||
|
||||
#if T_WATCH_S3
|
||||
drv.setWaveform(0, 75);
|
||||
drv.setWaveform(1, 56);
|
||||
drv.setWaveform(2, 0);
|
||||
drv.go();
|
||||
#endif
|
||||
if (getFrom(&mp) != nodeDB.getNodeNum()) {
|
||||
|
||||
// Check if the message contains a bell character. Don't do this loop for every pin, just once.
|
||||
@@ -343,7 +364,6 @@ ProcessMessage ExternalNotificationModule::handleReceived(const meshtastic_MeshP
|
||||
}
|
||||
setIntervalFromNow(0); // run once so we know if we should do something
|
||||
}
|
||||
|
||||
} else {
|
||||
LOG_INFO("External Notification Module Disabled\n");
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
#include "NodeDB.h"
|
||||
#include "RTC.h"
|
||||
#include "Router.h"
|
||||
#include "TypeConversions.h"
|
||||
#include "airtime.h"
|
||||
#include "configuration.h"
|
||||
#include "gps/GeoCoord.h"
|
||||
@@ -56,55 +57,57 @@ bool PositionModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp, mes
|
||||
|
||||
meshtastic_MeshPacket *PositionModule::allocReply()
|
||||
{
|
||||
meshtastic_NodeInfo *node = service.refreshMyNodeInfo(); // should guarantee there is now a position
|
||||
meshtastic_NodeInfoLite *node = service.refreshLocalMeshNode(); // should guarantee there is now a position
|
||||
assert(node->has_position);
|
||||
|
||||
node->position.seq_number++;
|
||||
|
||||
// configuration of POSITION packet
|
||||
// consider making this a function argument?
|
||||
uint32_t pos_flags = config.position.position_flags;
|
||||
|
||||
// Populate a Position struct with ONLY the requested fields
|
||||
meshtastic_Position p = meshtastic_Position_init_default; // Start with an empty structure
|
||||
if (localPosition.latitude_i == 0 && localPosition.longitude_i == 0) {
|
||||
localPosition = ConvertToPosition(node->position);
|
||||
}
|
||||
localPosition.seq_number++;
|
||||
|
||||
// lat/lon are unconditionally included - IF AVAILABLE!
|
||||
p.latitude_i = node->position.latitude_i;
|
||||
p.longitude_i = node->position.longitude_i;
|
||||
p.time = node->position.time;
|
||||
p.latitude_i = localPosition.latitude_i;
|
||||
p.longitude_i = localPosition.longitude_i;
|
||||
p.time = localPosition.time;
|
||||
|
||||
if (pos_flags & meshtastic_Config_PositionConfig_PositionFlags_ALTITUDE) {
|
||||
if (pos_flags & meshtastic_Config_PositionConfig_PositionFlags_ALTITUDE_MSL)
|
||||
p.altitude = node->position.altitude;
|
||||
p.altitude = localPosition.altitude;
|
||||
else
|
||||
p.altitude_hae = node->position.altitude_hae;
|
||||
p.altitude_hae = localPosition.altitude_hae;
|
||||
|
||||
if (pos_flags & meshtastic_Config_PositionConfig_PositionFlags_GEOIDAL_SEPARATION)
|
||||
p.altitude_geoidal_separation = node->position.altitude_geoidal_separation;
|
||||
p.altitude_geoidal_separation = localPosition.altitude_geoidal_separation;
|
||||
}
|
||||
|
||||
if (pos_flags & meshtastic_Config_PositionConfig_PositionFlags_DOP) {
|
||||
if (pos_flags & meshtastic_Config_PositionConfig_PositionFlags_HVDOP) {
|
||||
p.HDOP = node->position.HDOP;
|
||||
p.VDOP = node->position.VDOP;
|
||||
p.HDOP = localPosition.HDOP;
|
||||
p.VDOP = localPosition.VDOP;
|
||||
} else
|
||||
p.PDOP = node->position.PDOP;
|
||||
p.PDOP = localPosition.PDOP;
|
||||
}
|
||||
|
||||
if (pos_flags & meshtastic_Config_PositionConfig_PositionFlags_SATINVIEW)
|
||||
p.sats_in_view = node->position.sats_in_view;
|
||||
p.sats_in_view = localPosition.sats_in_view;
|
||||
|
||||
if (pos_flags & meshtastic_Config_PositionConfig_PositionFlags_TIMESTAMP)
|
||||
p.timestamp = node->position.timestamp;
|
||||
p.timestamp = localPosition.timestamp;
|
||||
|
||||
if (pos_flags & meshtastic_Config_PositionConfig_PositionFlags_SEQ_NO)
|
||||
p.seq_number = node->position.seq_number;
|
||||
p.seq_number = localPosition.seq_number;
|
||||
|
||||
if (pos_flags & meshtastic_Config_PositionConfig_PositionFlags_HEADING)
|
||||
p.ground_track = node->position.ground_track;
|
||||
p.ground_track = localPosition.ground_track;
|
||||
|
||||
if (pos_flags & meshtastic_Config_PositionConfig_PositionFlags_SPEED)
|
||||
p.ground_speed = node->position.ground_speed;
|
||||
p.ground_speed = localPosition.ground_speed;
|
||||
|
||||
// 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
|
||||
@@ -144,7 +147,7 @@ void PositionModule::sendOurPosition(NodeNum dest, bool wantReplies, uint8_t cha
|
||||
|
||||
int32_t PositionModule::runOnce()
|
||||
{
|
||||
meshtastic_NodeInfo *node = nodeDB.getNode(nodeDB.getNodeNum());
|
||||
meshtastic_NodeInfoLite *node = nodeDB.getMeshNode(nodeDB.getNodeNum());
|
||||
|
||||
// We limit our GPS broadcasts to a max rate
|
||||
uint32_t now = millis();
|
||||
@@ -164,14 +167,14 @@ int32_t PositionModule::runOnce()
|
||||
bool requestReplies = currentGeneration != radioGeneration;
|
||||
currentGeneration = radioGeneration;
|
||||
|
||||
LOG_INFO("Sending pos@%x:6 to mesh (wantReplies=%d)\n", node->position.timestamp, requestReplies);
|
||||
LOG_INFO("Sending pos@%x:6 to mesh (wantReplies=%d)\n", localPosition.timestamp, requestReplies);
|
||||
sendOurPosition(NODENUM_BROADCAST, requestReplies);
|
||||
}
|
||||
}
|
||||
} else if (config.position.position_broadcast_smart_enabled) {
|
||||
// Only send packets if the channel is less than 25% utilized or we're a tracker.
|
||||
if (airTime->isTxAllowedChannelUtil(config.device.role != meshtastic_Config_DeviceConfig_Role_TRACKER)) {
|
||||
meshtastic_NodeInfo *node2 = service.refreshMyNodeInfo(); // should guarantee there is now a position
|
||||
meshtastic_NodeInfoLite *node2 = service.refreshLocalMeshNode(); // should guarantee there is now a position
|
||||
|
||||
if (hasValidPosition(node2)) {
|
||||
// The minimum distance to travel before we are able to send a new position packet.
|
||||
@@ -193,7 +196,7 @@ int32_t PositionModule::runOnce()
|
||||
|
||||
LOG_INFO("Sending smart pos@%x:6 to mesh (distanceTraveled=%fm, minDistanceThreshold=%im, timeElapsed=%ims, "
|
||||
"minTimeInterval=%ims)\n",
|
||||
node2->position.timestamp, abs(distanceTraveledSinceLastSend), distanceTravelThreshold,
|
||||
localPosition.timestamp, abs(distanceTraveledSinceLastSend), distanceTravelThreshold,
|
||||
msSinceLastSend, minimumTimeThreshold);
|
||||
sendOurPosition(NODENUM_BROADCAST, requestReplies);
|
||||
|
||||
@@ -211,4 +214,4 @@ int32_t PositionModule::runOnce()
|
||||
}
|
||||
|
||||
return 5000; // to save power only wake for our callback occasionally
|
||||
}
|
||||
}
|
||||
@@ -54,7 +54,7 @@ int32_t RangeTestModule::runOnce()
|
||||
if (moduleConfig.range_test.sender) {
|
||||
LOG_INFO("Initializing Range Test Module -- Sender\n");
|
||||
started = millis(); // make a note of when we started
|
||||
return (5000); // Sending first message 5 seconds after initilization.
|
||||
return (5000); // Sending first message 5 seconds after initialization.
|
||||
} else {
|
||||
LOG_INFO("Initializing Range Test Module -- Receiver\n");
|
||||
return disable();
|
||||
@@ -138,7 +138,7 @@ ProcessMessage RangeTestModuleRadio::handleReceived(const meshtastic_MeshPacket
|
||||
}
|
||||
|
||||
/*
|
||||
NodeInfo *n = nodeDB.getNode(getFrom(&mp));
|
||||
NodeInfoLite *n = nodeDB.getMeshNode(getFrom(&mp));
|
||||
|
||||
LOG_DEBUG("-----------------------------------------\n");
|
||||
LOG_DEBUG("p.payload.bytes \"%s\"\n", p.payload.bytes);
|
||||
@@ -147,12 +147,11 @@ ProcessMessage RangeTestModuleRadio::handleReceived(const meshtastic_MeshPacket
|
||||
LOG_DEBUG("mp.from %d\n", mp.from);
|
||||
LOG_DEBUG("mp.rx_snr %f\n", mp.rx_snr);
|
||||
LOG_DEBUG("mp.hop_limit %d\n", mp.hop_limit);
|
||||
// LOG_DEBUG("mp.decoded.position.latitude_i %d\n", mp.decoded.position.latitude_i); // Depricated
|
||||
// LOG_DEBUG("mp.decoded.position.longitude_i %d\n", mp.decoded.position.longitude_i); // Depricated
|
||||
// LOG_DEBUG("mp.decoded.position.latitude_i %d\n", mp.decoded.position.latitude_i); // Deprecated
|
||||
// LOG_DEBUG("mp.decoded.position.longitude_i %d\n", mp.decoded.position.longitude_i); // Deprecated
|
||||
LOG_DEBUG("---- Node Information of Received Packet (mp.from):\n");
|
||||
LOG_DEBUG("n->user.long_name %s\n", n->user.long_name);
|
||||
LOG_DEBUG("n->user.short_name %s\n", n->user.short_name);
|
||||
LOG_DEBUG("n->user.macaddr %X\n", n->user.macaddr);
|
||||
LOG_DEBUG("n->has_position %d\n", n->has_position);
|
||||
LOG_DEBUG("n->position.latitude_i %d\n", n->position.latitude_i);
|
||||
LOG_DEBUG("n->position.longitude_i %d\n", n->position.longitude_i);
|
||||
@@ -178,7 +177,7 @@ bool RangeTestModuleRadio::appendFile(const meshtastic_MeshPacket &mp)
|
||||
#ifdef ARCH_ESP32
|
||||
auto &p = mp.decoded;
|
||||
|
||||
meshtastic_NodeInfo *n = nodeDB.getNode(getFrom(&mp));
|
||||
meshtastic_NodeInfoLite *n = nodeDB.getMeshNode(getFrom(&mp));
|
||||
/*
|
||||
LOG_DEBUG("-----------------------------------------\n");
|
||||
LOG_DEBUG("p.payload.bytes \"%s\"\n", p.payload.bytes);
|
||||
@@ -187,12 +186,11 @@ bool RangeTestModuleRadio::appendFile(const meshtastic_MeshPacket &mp)
|
||||
LOG_DEBUG("mp.from %d\n", mp.from);
|
||||
LOG_DEBUG("mp.rx_snr %f\n", mp.rx_snr);
|
||||
LOG_DEBUG("mp.hop_limit %d\n", mp.hop_limit);
|
||||
// LOG_DEBUG("mp.decoded.position.latitude_i %d\n", mp.decoded.position.latitude_i); // Depricated
|
||||
// LOG_DEBUG("mp.decoded.position.longitude_i %d\n", mp.decoded.position.longitude_i); // Depricated
|
||||
// LOG_DEBUG("mp.decoded.position.latitude_i %d\n", mp.decoded.position.latitude_i); // Deprecated
|
||||
// LOG_DEBUG("mp.decoded.position.longitude_i %d\n", mp.decoded.position.longitude_i); // Deprecated
|
||||
LOG_DEBUG("---- Node Information of Received Packet (mp.from):\n");
|
||||
LOG_DEBUG("n->user.long_name %s\n", n->user.long_name);
|
||||
LOG_DEBUG("n->user.short_name %s\n", n->user.short_name);
|
||||
LOG_DEBUG("n->user.macaddr %X\n", n->user.macaddr);
|
||||
LOG_DEBUG("n->has_position %d\n", n->has_position);
|
||||
LOG_DEBUG("n->position.latitude_i %d\n", n->position.latitude_i);
|
||||
LOG_DEBUG("n->position.longitude_i %d\n", n->position.longitude_i);
|
||||
@@ -286,4 +284,4 @@ bool RangeTestModuleRadio::appendFile(const meshtastic_MeshPacket &mp)
|
||||
#endif
|
||||
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
@@ -33,11 +33,11 @@
|
||||
to your device.
|
||||
|
||||
TODO (in this order):
|
||||
* Define a verbose RX mode to report on mesh and packet infomration.
|
||||
* Define a verbose RX mode to report on mesh and packet information.
|
||||
- This won't happen any time soon.
|
||||
|
||||
KNOWN PROBLEMS
|
||||
* Until the module is initilized by the startup sequence, the TX pin is in a floating
|
||||
* Until the module is initialized by the startup sequence, the TX pin is in a floating
|
||||
state. Device connected to that pin may see this as "noise".
|
||||
* Will not work on Linux device targets.
|
||||
|
||||
@@ -161,18 +161,18 @@ int32_t SerialModule::runOnce()
|
||||
// in NMEA mode send out GGA every 2 seconds, Don't read from Port
|
||||
if (millis() - lastNmeaTime > 2000) {
|
||||
lastNmeaTime = millis();
|
||||
printGGA(outbuf, sizeof(outbuf), nodeDB.getNode(myNodeInfo.my_node_num)->position);
|
||||
printGGA(outbuf, sizeof(outbuf), localPosition);
|
||||
serialPrint->printf("%s", outbuf);
|
||||
}
|
||||
} else if (moduleConfig.serial.mode == meshtastic_ModuleConfig_SerialConfig_Serial_Mode_CALTOPO) {
|
||||
if (millis() - lastNmeaTime > 10000) {
|
||||
lastNmeaTime = millis();
|
||||
uint32_t readIndex = 0;
|
||||
const meshtastic_NodeInfo *tempNodeInfo = nodeDB.readNextInfo(readIndex);
|
||||
const meshtastic_NodeInfoLite *tempNodeInfo = nodeDB.readNextMeshNode(readIndex);
|
||||
while (tempNodeInfo != NULL && tempNodeInfo->has_user && hasValidPosition(tempNodeInfo)) {
|
||||
printWPL(outbuf, sizeof(outbuf), tempNodeInfo->position, tempNodeInfo->user.long_name, true);
|
||||
serialPrint->printf("%s", outbuf);
|
||||
tempNodeInfo = nodeDB.readNextInfo(readIndex);
|
||||
tempNodeInfo = nodeDB.readNextMeshNode(readIndex);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -251,7 +251,7 @@ ProcessMessage SerialModuleRadio::handleReceived(const meshtastic_MeshPacket &mp
|
||||
moduleConfig.serial.mode == meshtastic_ModuleConfig_SerialConfig_Serial_Mode_SIMPLE) {
|
||||
serialPrint->printf("%s", p.payload.bytes);
|
||||
} else if (moduleConfig.serial.mode == meshtastic_ModuleConfig_SerialConfig_Serial_Mode_TEXTMSG) {
|
||||
meshtastic_NodeInfo *node = nodeDB.getNode(getFrom(&mp));
|
||||
meshtastic_NodeInfoLite *node = nodeDB.getMeshNode(getFrom(&mp));
|
||||
String sender = (node && node->has_user) ? node->user.short_name : "???";
|
||||
serialPrint->println();
|
||||
serialPrint->printf("%s: %s", sender, p.payload.bytes);
|
||||
@@ -267,7 +267,7 @@ ProcessMessage SerialModuleRadio::handleReceived(const meshtastic_MeshPacket &mp
|
||||
decoded = &scratch;
|
||||
}
|
||||
// send position packet as WPL to the serial port
|
||||
printWPL(outbuf, sizeof(outbuf), *decoded, nodeDB.getNode(getFrom(&mp))->user.long_name,
|
||||
printWPL(outbuf, sizeof(outbuf), *decoded, nodeDB.getMeshNode(getFrom(&mp))->user.long_name,
|
||||
moduleConfig.serial.mode == meshtastic_ModuleConfig_SerialConfig_Serial_Mode_CALTOPO);
|
||||
serialPrint->printf("%s", outbuf);
|
||||
}
|
||||
@@ -312,4 +312,4 @@ uint32_t SerialModule::getBaudRate()
|
||||
}
|
||||
return BAUD;
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
@@ -50,14 +50,14 @@ bool DeviceTelemetryModule::sendTelemetry(NodeNum dest, bool phoneOnly)
|
||||
t.time = getTime();
|
||||
t.which_variant = meshtastic_Telemetry_device_metrics_tag;
|
||||
|
||||
t.variant.device_metrics.air_util_tx = myNodeInfo.air_util_tx;
|
||||
t.variant.device_metrics.air_util_tx = airTime->utilizationTXPercent();
|
||||
if (powerStatus->getIsCharging()) {
|
||||
t.variant.device_metrics.battery_level = MAGIC_USB_BATTERY_LEVEL;
|
||||
} else {
|
||||
t.variant.device_metrics.battery_level = powerStatus->getBatteryChargePercent();
|
||||
}
|
||||
|
||||
t.variant.device_metrics.channel_utilization = myNodeInfo.channel_utilization;
|
||||
t.variant.device_metrics.channel_utilization = airTime->channelUtilizationPercent();
|
||||
t.variant.device_metrics.voltage = powerStatus->getBatteryVoltageMv() / 1000.0;
|
||||
|
||||
LOG_INFO("(Sending): air_util_tx=%f, channel_utilization=%f, battery_level=%i, voltage=%f\n",
|
||||
@@ -78,4 +78,4 @@ bool DeviceTelemetryModule::sendTelemetry(NodeNum dest, bool phoneOnly)
|
||||
service.sendToMesh(p, RX_SRC_LOCAL, true);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -86,6 +86,10 @@ int32_t EnvironmentTelemetryModule::runOnce()
|
||||
result = lps22hbSensor.runOnce();
|
||||
if (sht31Sensor.hasSensor())
|
||||
result = sht31Sensor.runOnce();
|
||||
if (ina219Sensor.hasSensor() && !ina219Sensor.isInitialized())
|
||||
result = ina219Sensor.runOnce();
|
||||
if (ina260Sensor.hasSensor() && !ina260Sensor.isInitialized())
|
||||
result = ina260Sensor.runOnce();
|
||||
}
|
||||
return result;
|
||||
} else {
|
||||
|
||||
@@ -36,8 +36,6 @@ int32_t StoreForwardModule::runOnce()
|
||||
this->packetHistoryTXQueue_index++;
|
||||
}
|
||||
}
|
||||
LOG_DEBUG("*** SF bitrate = %f bytes / sec\n", myNodeInfo.bitrate);
|
||||
|
||||
} else if ((millis() - lastHeartbeat > (heartbeatInterval * 1000)) && airTime->isTxAllowedChannelUtil(true)) {
|
||||
lastHeartbeat = millis();
|
||||
LOG_INFO("*** Sending heartbeat\n");
|
||||
@@ -64,7 +62,7 @@ void StoreForwardModule::populatePSRAM()
|
||||
https://learn.upesy.com/en/programmation/psram.html#psram-tab
|
||||
*/
|
||||
|
||||
LOG_DEBUG("*** Before PSRAM initilization: heap %d/%d PSRAM %d/%d\n", memGet.getFreeHeap(), memGet.getHeapSize(),
|
||||
LOG_DEBUG("*** Before PSRAM initialization: heap %d/%d PSRAM %d/%d\n", memGet.getFreeHeap(), memGet.getHeapSize(),
|
||||
memGet.getFreePsram(), memGet.getPsramSize());
|
||||
|
||||
this->packetHistoryTXQueue =
|
||||
@@ -79,7 +77,7 @@ void StoreForwardModule::populatePSRAM()
|
||||
|
||||
this->packetHistory = static_cast<PacketHistoryStruct *>(ps_calloc(numberOfPackets, sizeof(PacketHistoryStruct)));
|
||||
|
||||
LOG_DEBUG("*** After PSRAM initilization: heap %d/%d PSRAM %d/%d\n", memGet.getFreeHeap(), memGet.getHeapSize(),
|
||||
LOG_DEBUG("*** After PSRAM initialization: heap %d/%d PSRAM %d/%d\n", memGet.getFreeHeap(), memGet.getHeapSize(),
|
||||
memGet.getFreePsram(), memGet.getPsramSize());
|
||||
LOG_DEBUG("*** numberOfPackets for packetHistory - %u\n", numberOfPackets);
|
||||
}
|
||||
@@ -266,7 +264,6 @@ ProcessMessage StoreForwardModule::handleReceived(const meshtastic_MeshPacket &m
|
||||
storeForwardModule->historyAdd(mp);
|
||||
LOG_INFO("*** S&F stored. Message history contains %u records now.\n", this->packetHistoryCurrent);
|
||||
}
|
||||
|
||||
} else if (mp.decoded.portnum == meshtastic_PortNum_STORE_FORWARD_APP) {
|
||||
auto &p = mp.decoded;
|
||||
meshtastic_StoreAndForward scratch;
|
||||
|
||||
@@ -282,7 +282,7 @@ JSONValue *JSONValue::Parse(const char **data)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Ran out of possibilites, it's bad!
|
||||
// Ran out of possibilities, it's bad!
|
||||
else {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@@ -25,12 +25,16 @@ Allocator<meshtastic_ServiceEnvelope> &mqttPool = staticMqttPool;
|
||||
|
||||
void MQTT::mqttCallback(char *topic, byte *payload, unsigned int length)
|
||||
{
|
||||
mqtt->onPublish(topic, payload, length);
|
||||
mqtt->onReceive(topic, payload, length);
|
||||
}
|
||||
|
||||
void MQTT::onPublish(char *topic, byte *payload, unsigned int length)
|
||||
void MQTT::onClientProxyReceive(meshtastic_MqttClientProxyMessage msg)
|
||||
{
|
||||
onReceive(msg.topic, msg.payload_variant.data.bytes, msg.payload_variant.data.size);
|
||||
}
|
||||
|
||||
void MQTT::onReceive(char *topic, byte *payload, size_t length)
|
||||
{
|
||||
// parsing ServiceEnvelope
|
||||
meshtastic_ServiceEnvelope e = meshtastic_ServiceEnvelope_init_default;
|
||||
|
||||
if (moduleConfig.mqtt.json_enabled && (strncmp(topic, jsonTopic.c_str(), jsonTopic.length()) == 0)) {
|
||||
@@ -153,10 +157,13 @@ void mqttInit()
|
||||
new MQTT();
|
||||
}
|
||||
|
||||
#ifdef HAS_NETWORKING
|
||||
MQTT::MQTT() : concurrency::OSThread("mqtt"), pubSub(mqttClient), mqttQueue(MAX_MQTT_QUEUE)
|
||||
#else
|
||||
MQTT::MQTT() : concurrency::OSThread("mqtt"), mqttQueue(MAX_MQTT_QUEUE)
|
||||
#endif
|
||||
{
|
||||
if (moduleConfig.mqtt.enabled) {
|
||||
|
||||
assert(!mqtt);
|
||||
mqtt = this;
|
||||
|
||||
@@ -170,22 +177,77 @@ MQTT::MQTT() : concurrency::OSThread("mqtt"), pubSub(mqttClient), mqttQueue(MAX_
|
||||
jsonTopic = "msh" + jsonTopic;
|
||||
}
|
||||
|
||||
pubSub.setCallback(mqttCallback);
|
||||
|
||||
#ifdef HAS_NETWORKING
|
||||
if (!moduleConfig.mqtt.proxy_to_client_enabled)
|
||||
pubSub.setCallback(mqttCallback);
|
||||
#endif
|
||||
// preflightSleepObserver.observe(&preflightSleep);
|
||||
} else {
|
||||
disable();
|
||||
}
|
||||
}
|
||||
|
||||
bool MQTT::connected()
|
||||
bool MQTT::isConnectedDirectly()
|
||||
{
|
||||
#ifdef HAS_NETWORKING
|
||||
return pubSub.connected();
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool MQTT::publish(const char *topic, const char *payload, bool retained)
|
||||
{
|
||||
if (moduleConfig.mqtt.proxy_to_client_enabled) {
|
||||
meshtastic_MqttClientProxyMessage *msg = mqttClientProxyMessagePool.allocZeroed();
|
||||
msg->which_payload_variant = meshtastic_MqttClientProxyMessage_text_tag;
|
||||
strcpy(msg->topic, topic);
|
||||
strcpy(msg->payload_variant.text, payload);
|
||||
msg->retained = retained;
|
||||
service.sendMqttMessageToClientProxy(msg);
|
||||
return true;
|
||||
}
|
||||
#ifdef HAS_NETWORKING
|
||||
else if (isConnectedDirectly()) {
|
||||
return pubSub.publish(topic, payload, retained);
|
||||
}
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
|
||||
bool MQTT::publish(const char *topic, const uint8_t *payload, size_t length, bool retained)
|
||||
{
|
||||
if (moduleConfig.mqtt.proxy_to_client_enabled) {
|
||||
meshtastic_MqttClientProxyMessage *msg = mqttClientProxyMessagePool.allocZeroed();
|
||||
msg->which_payload_variant = meshtastic_MqttClientProxyMessage_data_tag;
|
||||
strcpy(msg->topic, topic);
|
||||
msg->payload_variant.data.size = length;
|
||||
memcpy(msg->payload_variant.data.bytes, payload, length);
|
||||
msg->retained = retained;
|
||||
service.sendMqttMessageToClientProxy(msg);
|
||||
return true;
|
||||
}
|
||||
#ifdef HAS_NETWORKING
|
||||
else if (isConnectedDirectly()) {
|
||||
return pubSub.publish(topic, payload, length, retained);
|
||||
}
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
|
||||
void MQTT::reconnect()
|
||||
{
|
||||
if (wantsLink()) {
|
||||
if (moduleConfig.mqtt.proxy_to_client_enabled) {
|
||||
LOG_INFO("MQTT connecting via client proxy instead...\n");
|
||||
enabled = true;
|
||||
runASAP = true;
|
||||
reconnectCount = 0;
|
||||
|
||||
publishStatus();
|
||||
return; // Don't try to connect directly to the server
|
||||
}
|
||||
#ifdef HAS_NETWORKING
|
||||
// Defaults
|
||||
int serverPort = 1883;
|
||||
const char *serverAddr = default_mqtt_address;
|
||||
@@ -197,7 +259,6 @@ void MQTT::reconnect()
|
||||
mqttUsername = moduleConfig.mqtt.username;
|
||||
mqttPassword = moduleConfig.mqtt.password;
|
||||
}
|
||||
|
||||
#if HAS_WIFI && !defined(ARCH_PORTDUINO)
|
||||
if (moduleConfig.mqtt.tls_enabled) {
|
||||
// change default for encrypted to 8883
|
||||
@@ -214,7 +275,7 @@ void MQTT::reconnect()
|
||||
LOG_INFO("Using non-TLS-encrypted session\n");
|
||||
pubSub.setClient(mqttClient);
|
||||
}
|
||||
#else
|
||||
#elif HAS_NETWORKING
|
||||
pubSub.setClient(mqttClient);
|
||||
#endif
|
||||
|
||||
@@ -229,8 +290,9 @@ void MQTT::reconnect()
|
||||
pubSub.setServer(serverAddr, serverPort);
|
||||
pubSub.setBufferSize(512);
|
||||
|
||||
LOG_INFO("Connecting to MQTT server %s, port: %d, username: %s, password: %s\n", serverAddr, serverPort, mqttUsername,
|
||||
mqttPassword);
|
||||
LOG_INFO("Attempting to connect directly to MQTT server %s, port: %d, username: %s, password: %s\n", serverAddr,
|
||||
serverPort, mqttUsername, mqttPassword);
|
||||
|
||||
auto myStatus = (statusTopic + owner.id);
|
||||
bool connected = pubSub.connect(owner.id, mqttUsername, mqttPassword, myStatus.c_str(), 1, true, "offline");
|
||||
if (connected) {
|
||||
@@ -239,15 +301,12 @@ void MQTT::reconnect()
|
||||
runASAP = true;
|
||||
reconnectCount = 0;
|
||||
|
||||
/// FIXME, include more information in the status text
|
||||
bool ok = pubSub.publish(myStatus.c_str(), "online", true);
|
||||
LOG_INFO("published %d\n", ok);
|
||||
|
||||
publishStatus();
|
||||
sendSubscriptions();
|
||||
} else {
|
||||
#if HAS_WIFI && !defined(ARCH_PORTDUINO)
|
||||
reconnectCount++;
|
||||
LOG_ERROR("Failed to contact MQTT server (%d/%d)...\n", reconnectCount, reconnectMax);
|
||||
LOG_ERROR("Failed to contact MQTT server directly (%d/%d)...\n", reconnectCount, reconnectMax);
|
||||
if (reconnectCount >= reconnectMax) {
|
||||
needReconnect = true;
|
||||
wifiReconnect->setIntervalFromNow(0);
|
||||
@@ -255,11 +314,13 @@ void MQTT::reconnect()
|
||||
}
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
void MQTT::sendSubscriptions()
|
||||
{
|
||||
#ifdef HAS_NETWORKING
|
||||
size_t numChan = channels.getNumChannels();
|
||||
for (size_t i = 0; i < numChan; i++) {
|
||||
auto &ch = channels.getByIndex(i);
|
||||
@@ -274,6 +335,7 @@ void MQTT::sendSubscriptions()
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
bool MQTT::wantsLink() const
|
||||
@@ -291,60 +353,44 @@ bool MQTT::wantsLink() const
|
||||
}
|
||||
}
|
||||
}
|
||||
if (hasChannel && moduleConfig.mqtt.proxy_to_client_enabled)
|
||||
return true;
|
||||
|
||||
#if HAS_WIFI
|
||||
return hasChannel && WiFi.isConnected();
|
||||
#endif
|
||||
#if HAS_ETHERNET
|
||||
return hasChannel && (Ethernet.linkStatus() == LinkON);
|
||||
return hasChannel && Ethernet.linkStatus() == LinkON;
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
|
||||
int32_t MQTT::runOnce()
|
||||
{
|
||||
if (!moduleConfig.mqtt.enabled) {
|
||||
if (!moduleConfig.mqtt.enabled)
|
||||
return disable();
|
||||
}
|
||||
|
||||
bool wantConnection = wantsLink();
|
||||
|
||||
// If connected poll rapidly, otherwise only occasionally check for a wifi connection change and ability to contact server
|
||||
if (!pubSub.loop()) {
|
||||
if (wantConnection) {
|
||||
if (moduleConfig.mqtt.proxy_to_client_enabled) {
|
||||
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
|
||||
else {
|
||||
reconnect();
|
||||
|
||||
// If we succeeded, empty the queue one by one and start reading rapidly, else try again in 30 seconds (TCP
|
||||
// connections are EXPENSIVE so try rarely)
|
||||
if (pubSub.connected()) {
|
||||
if (!mqttQueue.isEmpty()) {
|
||||
// FIXME - this size calculation is super sloppy, but it will go away once we dynamically alloc meshpackets
|
||||
meshtastic_ServiceEnvelope *env = mqttQueue.dequeuePtr(0);
|
||||
static uint8_t bytes[meshtastic_MeshPacket_size + 64];
|
||||
size_t numBytes = pb_encode_to_bytes(bytes, sizeof(bytes), &meshtastic_ServiceEnvelope_msg, env);
|
||||
|
||||
std::string topic = cryptTopic + env->channel_id + "/" + owner.id;
|
||||
LOG_INFO("publish %s, %u bytes from queue\n", topic.c_str(), numBytes);
|
||||
|
||||
pubSub.publish(topic.c_str(), bytes, numBytes, false);
|
||||
|
||||
if (moduleConfig.mqtt.json_enabled) {
|
||||
// handle json topic
|
||||
auto jsonString = this->downstreamPacketToJson(env->packet);
|
||||
if (jsonString.length() != 0) {
|
||||
std::string topicJson = jsonTopic + env->channel_id + "/" + owner.id;
|
||||
LOG_INFO("JSON publish message to %s, %u bytes: %s\n", topicJson.c_str(), jsonString.length(),
|
||||
jsonString.c_str());
|
||||
pubSub.publish(topicJson.c_str(), jsonString.c_str(), false);
|
||||
}
|
||||
}
|
||||
mqttPool.release(env);
|
||||
}
|
||||
if (isConnectedDirectly()) {
|
||||
publishQueuedMessages();
|
||||
return 200;
|
||||
} else {
|
||||
} else
|
||||
return 30000;
|
||||
}
|
||||
} else
|
||||
return 5000; // If we don't want connection now, check again in 5 secs
|
||||
}
|
||||
} else {
|
||||
// we are connected to server, check often for new requests on the TCP port
|
||||
if (!wantConnection) {
|
||||
@@ -355,6 +401,44 @@ int32_t MQTT::runOnce()
|
||||
powerFSM.trigger(EVENT_CONTACT_FROM_PHONE); // Suppress entering light sleep (because that would turn off bluetooth)
|
||||
return 20;
|
||||
}
|
||||
#endif
|
||||
return 30000;
|
||||
}
|
||||
|
||||
/// FIXME, include more information in the status text
|
||||
void MQTT::publishStatus()
|
||||
{
|
||||
auto myStatus = (statusTopic + owner.id);
|
||||
bool ok = publish(myStatus.c_str(), "online", true);
|
||||
LOG_INFO("published online=%d\n", ok);
|
||||
}
|
||||
|
||||
void MQTT::publishQueuedMessages()
|
||||
{
|
||||
if (!mqttQueue.isEmpty()) {
|
||||
LOG_DEBUG("Publishing enqueued MQTT message\n");
|
||||
// FIXME - this size calculation is super sloppy, but it will go away once we dynamically alloc meshpackets
|
||||
meshtastic_ServiceEnvelope *env = mqttQueue.dequeuePtr(0);
|
||||
static uint8_t bytes[meshtastic_MeshPacket_size + 64];
|
||||
size_t numBytes = pb_encode_to_bytes(bytes, sizeof(bytes), &meshtastic_ServiceEnvelope_msg, env);
|
||||
|
||||
std::string topic = cryptTopic + env->channel_id + "/" + owner.id;
|
||||
LOG_INFO("publish %s, %u bytes from queue\n", topic.c_str(), numBytes);
|
||||
|
||||
publish(topic.c_str(), bytes, numBytes, false);
|
||||
|
||||
if (moduleConfig.mqtt.json_enabled) {
|
||||
// handle json topic
|
||||
auto jsonString = this->meshPacketToJson(env->packet);
|
||||
if (jsonString.length() != 0) {
|
||||
std::string topicJson = jsonTopic + env->channel_id + "/" + owner.id;
|
||||
LOG_INFO("JSON publish message to %s, %u bytes: %s\n", topicJson.c_str(), jsonString.length(),
|
||||
jsonString.c_str());
|
||||
publish(topicJson.c_str(), jsonString.c_str(), false);
|
||||
}
|
||||
}
|
||||
mqttPool.release(env);
|
||||
}
|
||||
}
|
||||
|
||||
void MQTT::onSend(const meshtastic_MeshPacket &mp, ChannelIndex chIndex)
|
||||
@@ -368,27 +452,26 @@ void MQTT::onSend(const meshtastic_MeshPacket &mp, ChannelIndex chIndex)
|
||||
env->channel_id = (char *)channelId;
|
||||
env->gateway_id = owner.id;
|
||||
env->packet = (meshtastic_MeshPacket *)∓
|
||||
LOG_DEBUG("MQTT onSend - Publishing portnum %i message\n", env->packet->decoded.portnum);
|
||||
|
||||
// don't bother sending if not connected...
|
||||
if (pubSub.connected()) {
|
||||
|
||||
if (moduleConfig.mqtt.proxy_to_client_enabled || this->isConnectedDirectly()) {
|
||||
// FIXME - this size calculation is super sloppy, but it will go away once we dynamically alloc meshpackets
|
||||
static uint8_t bytes[meshtastic_MeshPacket_size + 64];
|
||||
size_t numBytes = pb_encode_to_bytes(bytes, sizeof(bytes), &meshtastic_ServiceEnvelope_msg, env);
|
||||
|
||||
std::string topic = cryptTopic + channelId + "/" + owner.id;
|
||||
LOG_DEBUG("publish %s, %u bytes\n", topic.c_str(), numBytes);
|
||||
LOG_DEBUG("MQTT Publish %s, %u bytes\n", topic.c_str(), numBytes);
|
||||
|
||||
pubSub.publish(topic.c_str(), bytes, numBytes, false);
|
||||
publish(topic.c_str(), bytes, numBytes, false);
|
||||
|
||||
if (moduleConfig.mqtt.json_enabled) {
|
||||
// handle json topic
|
||||
auto jsonString = this->downstreamPacketToJson((meshtastic_MeshPacket *)&mp);
|
||||
auto jsonString = this->meshPacketToJson((meshtastic_MeshPacket *)&mp);
|
||||
if (jsonString.length() != 0) {
|
||||
std::string topicJson = jsonTopic + channelId + "/" + owner.id;
|
||||
LOG_INFO("JSON publish message to %s, %u bytes: %s\n", topicJson.c_str(), jsonString.length(),
|
||||
jsonString.c_str());
|
||||
pubSub.publish(topicJson.c_str(), jsonString.c_str(), false);
|
||||
publish(topicJson.c_str(), jsonString.c_str(), false);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@@ -408,7 +491,7 @@ void MQTT::onSend(const meshtastic_MeshPacket &mp, ChannelIndex chIndex)
|
||||
}
|
||||
|
||||
// converts a downstream packet into a json message
|
||||
std::string MQTT::downstreamPacketToJson(meshtastic_MeshPacket *mp)
|
||||
std::string MQTT::meshPacketToJson(meshtastic_MeshPacket *mp)
|
||||
{
|
||||
// the created jsonObj is immutable after creation, so
|
||||
// we need to do the heavy lifting before assembling it.
|
||||
|
||||
@@ -5,15 +5,20 @@
|
||||
#include "concurrency/OSThread.h"
|
||||
#include "mesh/Channels.h"
|
||||
#include "mesh/generated/meshtastic/mqtt.pb.h"
|
||||
#include <PubSubClient.h>
|
||||
#if HAS_WIFI
|
||||
#include <WiFiClient.h>
|
||||
#define HAS_NETWORKING 1
|
||||
#if !defined(ARCH_PORTDUINO)
|
||||
#include <WiFiClientSecure.h>
|
||||
#endif
|
||||
#endif
|
||||
#if HAS_ETHERNET
|
||||
#include <EthernetClient.h>
|
||||
#define HAS_NETWORKING 1
|
||||
#endif
|
||||
|
||||
#ifdef HAS_NETWORKING
|
||||
#include <PubSubClient.h>
|
||||
#endif
|
||||
|
||||
#define MAX_MQTT_QUEUE 16
|
||||
@@ -35,12 +40,9 @@ class MQTT : private concurrency::OSThread
|
||||
#if HAS_ETHERNET
|
||||
EthernetClient mqttClient;
|
||||
#endif
|
||||
#if !defined(DEBUG_HEAP_MQTT)
|
||||
PubSubClient pubSub;
|
||||
|
||||
public:
|
||||
#else
|
||||
public:
|
||||
#ifdef HAS_NETWORKING
|
||||
PubSubClient pubSub;
|
||||
#endif
|
||||
MQTT();
|
||||
@@ -51,7 +53,7 @@ class MQTT : private concurrency::OSThread
|
||||
* @param chIndex the index of the channel for this message
|
||||
*
|
||||
* Note: for messages we are forwarding on the mesh that we can't find the channel for (because we don't have the keys), we
|
||||
* can not forward those messages to the cloud - becuase no way to find a global channel ID.
|
||||
* can not forward those messages to the cloud - because no way to find a global channel ID.
|
||||
*/
|
||||
void onSend(const meshtastic_MeshPacket &mp, ChannelIndex chIndex);
|
||||
|
||||
@@ -59,7 +61,13 @@ class MQTT : private concurrency::OSThread
|
||||
*/
|
||||
void reconnect();
|
||||
|
||||
bool connected();
|
||||
bool isConnectedDirectly();
|
||||
|
||||
bool publish(const char *topic, const char *payload, bool retained);
|
||||
|
||||
bool publish(const char *topic, const uint8_t *payload, size_t length, const bool retained);
|
||||
|
||||
void onClientProxyReceive(meshtastic_MqttClientProxyMessage msg);
|
||||
|
||||
protected:
|
||||
PointerQueue<meshtastic_ServiceEnvelope> mqttQueue;
|
||||
@@ -80,14 +88,17 @@ class MQTT : private concurrency::OSThread
|
||||
*/
|
||||
void sendSubscriptions();
|
||||
|
||||
/// Just C glue to call onPublish
|
||||
/// Callback for direct mqtt subscription messages
|
||||
static void mqttCallback(char *topic, byte *payload, unsigned int length);
|
||||
|
||||
/// Called when a new publish arrives from the MQTT server
|
||||
void onPublish(char *topic, byte *payload, unsigned int length);
|
||||
void onReceive(char *topic, byte *payload, size_t length);
|
||||
|
||||
/// Called when a new publish arrives from the MQTT server
|
||||
std::string downstreamPacketToJson(meshtastic_MeshPacket *mp);
|
||||
std::string meshPacketToJson(meshtastic_MeshPacket *mp);
|
||||
|
||||
void publishStatus();
|
||||
void publishQueuedMessages();
|
||||
|
||||
/// Return 0 if sleep is okay, veto sleep if we are connected to pubsub server
|
||||
// int preflightSleepCb(void *unused = NULL) { return pubSub.connected() ? 1 : 0; }
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
#include "PowerFSM.h" // FIXME - someday I want to make this OTA thing a separate lb at at that point it can't touch this
|
||||
|
||||
/**
|
||||
* A characterstic with a set of overridable callbacks
|
||||
* A characteristic with a set of overridable callbacks
|
||||
*/
|
||||
class CallbackCharacteristic : public BLECharacteristic, public BLECharacteristicCallbacks
|
||||
{
|
||||
|
||||
@@ -46,6 +46,10 @@
|
||||
#if defined(HAS_AXP192) || defined(HAS_AXP2101)
|
||||
#define HAS_PMU
|
||||
#endif
|
||||
|
||||
#ifdef PIN_BUTTON_TOUCH
|
||||
#define BUTTON_PIN_TOUCH PIN_BUTTON_TOUCH
|
||||
#endif
|
||||
//
|
||||
// set HW_VENDOR
|
||||
//
|
||||
@@ -81,6 +85,10 @@
|
||||
#define HW_VENDOR meshtastic_HardwareModel_TLORA_V2_1_1P6
|
||||
#elif defined(TLORA_V2_1_18)
|
||||
#define HW_VENDOR meshtastic_HardwareModel_TLORA_V2_1_1P8
|
||||
#elif defined(T_DECK)
|
||||
#define HW_VENDOR meshtastic_HardwareModel_T_DECK
|
||||
#elif defined(T_WATCH_S3)
|
||||
#define HW_VENDOR meshtastic_HardwareModel_T_WATCH_S3
|
||||
#elif defined(GENIEBLOCKS)
|
||||
#define HW_VENDOR meshtastic_HardwareModel_GENIEBLOCKS
|
||||
#elif defined(PRIVATE_HW)
|
||||
@@ -97,6 +105,10 @@
|
||||
#define HW_VENDOR meshtastic_HardwareModel_HELTEC_V3
|
||||
#elif defined(HELTEC_WSL_V3)
|
||||
#define HW_VENDOR meshtastic_HardwareModel_HELTEC_WSL_V3
|
||||
#elif defined(HELTEC_WIRELESS_TRACKER)
|
||||
#define HW_VENDOR meshtastic_HardwareModel_HELTEC_WIRELESS_TRACKER
|
||||
#elif defined(HELTEC_WIRELESS_PAPER)
|
||||
#define HW_VENDOR meshtastic_HardwareModel_HELTEC_WIRELESS_PAPER
|
||||
#elif defined(TLORA_T3S3_V1)
|
||||
#define HW_VENDOR meshtastic_HardwareModel_TLORA_T3_S3
|
||||
#elif defined(BETAFPV_2400_TX)
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user