Merge branch 'master' of https://github.com/HarukiToreda/ESP32-CardKB-Fix
This commit is contained in:
@@ -11,23 +11,11 @@ runs:
|
|||||||
ref: ${{github.event.pull_request.head.ref}}
|
ref: ${{github.event.pull_request.head.ref}}
|
||||||
repository: ${{github.event.pull_request.head.repo.full_name}}
|
repository: ${{github.event.pull_request.head.repo.full_name}}
|
||||||
|
|
||||||
- name: Install cppcheck
|
- name: Install dependencies
|
||||||
shell: bash
|
shell: bash
|
||||||
run: |
|
run: |
|
||||||
sudo apt-get install -y cppcheck
|
sudo apt-get -y update
|
||||||
|
sudo apt-get install -y cppcheck libbluetooth-dev libgpiod-dev libyaml-cpp-dev
|
||||||
- name: Install libbluetooth
|
|
||||||
shell: bash
|
|
||||||
run: |
|
|
||||||
sudo apt-get install -y libbluetooth-dev
|
|
||||||
- name: Install libgpiod
|
|
||||||
shell: bash
|
|
||||||
run: |
|
|
||||||
sudo apt-get install -y libgpiod-dev
|
|
||||||
- name: Install libyaml-cpp
|
|
||||||
shell: bash
|
|
||||||
run: |
|
|
||||||
sudo apt-get install -y libyaml-cpp-dev
|
|
||||||
|
|
||||||
- name: Setup Python
|
- name: Setup Python
|
||||||
uses: actions/setup-python@v5
|
uses: actions/setup-python@v5
|
||||||
|
|||||||
@@ -0,0 +1,85 @@
|
|||||||
|
name: Build Native
|
||||||
|
|
||||||
|
on: workflow_call
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: write
|
||||||
|
packages: write
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build-native:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Install libbluetooth
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
sudo apt-get update
|
||||||
|
sudo apt-get install -y libbluetooth-dev libgpiod-dev libyaml-cpp-dev openssl libssl-dev libulfius-dev liborcania-dev
|
||||||
|
|
||||||
|
- name: Checkout code
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
submodules: recursive
|
||||||
|
ref: ${{github.event.pull_request.head.ref}}
|
||||||
|
repository: ${{github.event.pull_request.head.repo.full_name}}
|
||||||
|
|
||||||
|
- name: Upgrade python tools
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
python -m pip install --upgrade pip
|
||||||
|
pip install -U platformio adafruit-nrfutil
|
||||||
|
pip install -U meshtastic --pre
|
||||||
|
|
||||||
|
- name: Upgrade platformio
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
pio upgrade
|
||||||
|
|
||||||
|
- name: Build Native
|
||||||
|
run: bin/build-native.sh
|
||||||
|
|
||||||
|
- name: Get release version string
|
||||||
|
run: echo "version=$(./bin/buildinfo.py long)" >> $GITHUB_OUTPUT
|
||||||
|
id: version
|
||||||
|
|
||||||
|
- name: Store binaries as an artifact
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: firmware-native-${{ steps.version.outputs.version }}.zip
|
||||||
|
overwrite: true
|
||||||
|
path: |
|
||||||
|
release/meshtasticd_linux_x86_64
|
||||||
|
bin/config-dist.yaml
|
||||||
|
|
||||||
|
- name: Docker login
|
||||||
|
if: ${{ github.event_name != 'pull_request_target' && github.event_name != 'pull_request' }}
|
||||||
|
uses: docker/login-action@v3
|
||||||
|
continue-on-error: true # FIXME: Failing docker login auth
|
||||||
|
with:
|
||||||
|
username: meshtastic
|
||||||
|
password: ${{ secrets.DOCKER_FIRMWARE_TOKEN }}
|
||||||
|
|
||||||
|
- name: Docker setup
|
||||||
|
if: ${{ github.event_name != 'pull_request_target' && github.event_name != 'pull_request' }}
|
||||||
|
continue-on-error: true # FIXME: Failing docker login auth
|
||||||
|
uses: docker/setup-buildx-action@v3
|
||||||
|
|
||||||
|
- name: Docker build and push tagged versions
|
||||||
|
if: ${{ github.event_name == 'workflow_dispatch' }}
|
||||||
|
continue-on-error: true # FIXME: Failing docker login auth
|
||||||
|
uses: docker/build-push-action@v5
|
||||||
|
with:
|
||||||
|
context: .
|
||||||
|
file: ./Dockerfile
|
||||||
|
push: true
|
||||||
|
tags: meshtastic/device-simulator:${{ steps.version.outputs.version }}
|
||||||
|
|
||||||
|
- name: Docker build and push
|
||||||
|
if: ${{ github.ref == 'refs/heads/master' && github.event_name != 'pull_request_target' && github.event_name != 'pull_request' }}
|
||||||
|
continue-on-error: true # FIXME: Failing docker login auth
|
||||||
|
uses: docker/build-push-action@v5
|
||||||
|
with:
|
||||||
|
context: .
|
||||||
|
file: ./Dockerfile
|
||||||
|
push: true
|
||||||
|
tags: meshtastic/device-simulator:latest
|
||||||
@@ -109,67 +109,8 @@ jobs:
|
|||||||
package-raspbian-armv7l:
|
package-raspbian-armv7l:
|
||||||
uses: ./.github/workflows/package_raspbian_armv7l.yml
|
uses: ./.github/workflows/package_raspbian_armv7l.yml
|
||||||
|
|
||||||
build-native:
|
package-native:
|
||||||
runs-on: ubuntu-latest
|
uses: ./.github/workflows/package_amd64.yml
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
- name: Build base
|
|
||||||
id: base
|
|
||||||
uses: ./.github/actions/setup-base
|
|
||||||
|
|
||||||
# We now run integration test before other build steps (to quickly see runtime failures)
|
|
||||||
#- name: Build for native
|
|
||||||
# run: platformio run -e native
|
|
||||||
#- name: Integration test
|
|
||||||
# run: |
|
|
||||||
#.pio/build/native/program
|
|
||||||
#& sleep 20 # 5 seconds was not enough
|
|
||||||
#echo "Simulator started, launching python test..."
|
|
||||||
#python3 -c 'from meshtastic.test import testSimulator; testSimulator()'
|
|
||||||
|
|
||||||
- name: Build Native
|
|
||||||
run: bin/build-native.sh
|
|
||||||
|
|
||||||
- name: Get release version string
|
|
||||||
run: echo "version=$(./bin/buildinfo.py long)" >> $GITHUB_OUTPUT
|
|
||||||
id: version
|
|
||||||
|
|
||||||
- name: Store binaries as an artifact
|
|
||||||
uses: actions/upload-artifact@v4
|
|
||||||
with:
|
|
||||||
name: firmware-native-${{ steps.version.outputs.version }}.zip
|
|
||||||
overwrite: true
|
|
||||||
path: |
|
|
||||||
release/device-*.sh
|
|
||||||
release/device-*.bat
|
|
||||||
|
|
||||||
- name: Docker login
|
|
||||||
if: ${{ github.event_name != 'pull_request_target' && github.event_name != 'pull_request' }}
|
|
||||||
uses: docker/login-action@v3
|
|
||||||
with:
|
|
||||||
username: meshtastic
|
|
||||||
password: ${{ secrets.DOCKER_TOKEN }}
|
|
||||||
- name: Docker setup
|
|
||||||
if: ${{ github.event_name != 'pull_request_target' && github.event_name != 'pull_request' }}
|
|
||||||
uses: docker/setup-buildx-action@v3
|
|
||||||
|
|
||||||
- name: Docker build and push tagged versions
|
|
||||||
if: ${{ github.event_name == 'workflow_dispatch' }}
|
|
||||||
uses: docker/build-push-action@v5
|
|
||||||
with:
|
|
||||||
context: .
|
|
||||||
file: ./Dockerfile
|
|
||||||
push: true
|
|
||||||
tags: meshtastic/device-simulator:${{ steps.version.outputs.version }}
|
|
||||||
|
|
||||||
- name: Docker build and push
|
|
||||||
if: ${{ github.ref == 'refs/heads/master' && github.event_name != 'pull_request_target' && github.event_name != 'pull_request' }}
|
|
||||||
uses: docker/build-push-action@v5
|
|
||||||
with:
|
|
||||||
context: .
|
|
||||||
file: ./Dockerfile
|
|
||||||
push: true
|
|
||||||
tags: meshtastic/device-simulator:latest
|
|
||||||
|
|
||||||
after-checks:
|
after-checks:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
@@ -192,10 +133,10 @@ jobs:
|
|||||||
build-esp32-s3,
|
build-esp32-s3,
|
||||||
build-esp32-c3,
|
build-esp32-c3,
|
||||||
build-nrf52,
|
build-nrf52,
|
||||||
build-native,
|
|
||||||
build-rpi2040,
|
build-rpi2040,
|
||||||
package-raspbian,
|
package-raspbian,
|
||||||
package-raspbian-armv7l,
|
package-raspbian-armv7l,
|
||||||
|
package-native
|
||||||
]
|
]
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
@@ -217,7 +158,7 @@ jobs:
|
|||||||
id: version
|
id: version
|
||||||
|
|
||||||
- name: Move files up
|
- name: Move files up
|
||||||
run: mv -b -t ./ ./release/meshtasticd_linux_aarch64 ./release/meshtasticd_linux_armv7l ./bin/config-dist.yaml
|
run: mv -b -t ./ ./release/meshtasticd_linux_* ./bin/config-dist.yaml ./bin/device-*.sh ./bin/device-*.bat
|
||||||
|
|
||||||
- name: Repackage in single firmware zip
|
- name: Repackage in single firmware zip
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v4
|
||||||
@@ -381,6 +322,16 @@ jobs:
|
|||||||
asset_name: meshtasticd_${{ steps.version.outputs.version }}_armhf.deb
|
asset_name: meshtasticd_${{ steps.version.outputs.version }}_armhf.deb
|
||||||
asset_content_type: application/vnd.debian.binary-package
|
asset_content_type: application/vnd.debian.binary-package
|
||||||
|
|
||||||
|
- name: Add raspbian amd64 .deb
|
||||||
|
uses: actions/upload-release-asset@v1
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ github.token }}
|
||||||
|
with:
|
||||||
|
upload_url: ${{ steps.create_release.outputs.upload_url }}
|
||||||
|
asset_path: ./output/meshtasticd_${{ steps.version.outputs.version }}_amd64.deb
|
||||||
|
asset_name: meshtasticd_${{ steps.version.outputs.version }}_amd64.deb
|
||||||
|
asset_content_type: application/vnd.debian.binary-package
|
||||||
|
|
||||||
- name: Bump version.properties
|
- name: Bump version.properties
|
||||||
run: >-
|
run: >-
|
||||||
bin/bump_version.py
|
bin/bump_version.py
|
||||||
|
|||||||
@@ -0,0 +1,78 @@
|
|||||||
|
name: Package Native
|
||||||
|
|
||||||
|
on:
|
||||||
|
workflow_call:
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: write
|
||||||
|
packages: write
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build-native:
|
||||||
|
uses: ./.github/workflows/build_native.yml
|
||||||
|
|
||||||
|
package-native:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
needs: build-native
|
||||||
|
steps:
|
||||||
|
- name: Checkout code
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
submodules: recursive
|
||||||
|
ref: ${{github.event.pull_request.head.ref}}
|
||||||
|
repository: ${{github.event.pull_request.head.repo.full_name}}
|
||||||
|
|
||||||
|
- name: Pull web ui
|
||||||
|
uses: dsaltares/fetch-gh-release-asset@master
|
||||||
|
with:
|
||||||
|
repo: meshtastic/web
|
||||||
|
file: build.tar
|
||||||
|
target: build.tar
|
||||||
|
token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
|
- name: Get release version string
|
||||||
|
run: echo "version=$(./bin/buildinfo.py long)" >> $GITHUB_OUTPUT
|
||||||
|
id: version
|
||||||
|
|
||||||
|
- name: Download artifacts
|
||||||
|
uses: actions/download-artifact@v4
|
||||||
|
with:
|
||||||
|
name: firmware-native-${{ steps.version.outputs.version }}.zip
|
||||||
|
merge-multiple: true
|
||||||
|
|
||||||
|
- name: Display structure of downloaded files
|
||||||
|
run: ls -R
|
||||||
|
|
||||||
|
- name: build .debpkg
|
||||||
|
run: |
|
||||||
|
mkdir -p .debpkg/DEBIAN
|
||||||
|
mkdir -p .debpkg/usr/share/doc/meshtasticd/web
|
||||||
|
mkdir -p .debpkg/usr/sbin
|
||||||
|
mkdir -p .debpkg/etc/meshtasticd
|
||||||
|
mkdir -p .debpkg/usr/lib/systemd/system/
|
||||||
|
tar -xf build.tar -C .debpkg/usr/share/doc/meshtasticd/web
|
||||||
|
gunzip .debpkg/usr/share/doc/meshtasticd/web/*.gz
|
||||||
|
cp release/meshtasticd_linux_x86_64 .debpkg/usr/sbin/meshtasticd
|
||||||
|
cp bin/config-dist.yaml .debpkg/etc/meshtasticd/config.yaml
|
||||||
|
chmod +x .debpkg/usr/sbin/meshtasticd
|
||||||
|
cp bin/meshtasticd.service .debpkg/usr/lib/systemd/system/meshtasticd.service
|
||||||
|
echo "/etc/meshtasticd/config.yaml" > .debpkg/DEBIAN/conffiles
|
||||||
|
chmod +x .debpkg/DEBIAN/conffiles
|
||||||
|
|
||||||
|
- uses: jiro4989/build-deb-action@v3
|
||||||
|
with:
|
||||||
|
package: meshtasticd
|
||||||
|
package_root: .debpkg
|
||||||
|
maintainer: Jonathan Bennett
|
||||||
|
version: ${{ steps.version.outputs.version }} # refs/tags/v*.*.*
|
||||||
|
arch: amd64
|
||||||
|
depends: libyaml-cpp0.7, openssl, libulfius2.7
|
||||||
|
desc: Native Linux Meshtastic binary.
|
||||||
|
|
||||||
|
- uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: meshtasticd_${{ steps.version.outputs.version }}_amd64.deb
|
||||||
|
overwrite: true
|
||||||
|
path: |
|
||||||
|
./*.deb
|
||||||
@@ -48,6 +48,7 @@ USER mesh
|
|||||||
WORKDIR /home/mesh
|
WORKDIR /home/mesh
|
||||||
COPY --from=builder /tmp/firmware/release/meshtasticd /home/mesh/
|
COPY --from=builder /tmp/firmware/release/meshtasticd /home/mesh/
|
||||||
|
|
||||||
|
RUN mkdir data
|
||||||
VOLUME /home/mesh/data
|
VOLUME /home/mesh/data
|
||||||
|
|
||||||
CMD [ "sh", "-cx", "./meshtasticd -d /home/mesh/data --hwid=${HWID:-$RANDOM}" ]
|
CMD [ "sh", "-cx", "./meshtasticd -d /home/mesh/data --hwid=${HWID:-$RANDOM}" ]
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
; Common settings for ESP targes, mixin with extends = esp32_base
|
; Common settings for ESP targes, mixin with extends = esp32_base
|
||||||
[esp32_base]
|
[esp32_base]
|
||||||
extends = arduino_base
|
extends = arduino_base
|
||||||
|
custom_esp32_kind = esp32
|
||||||
platform = platformio/espressif32@6.7.0
|
platform = platformio/espressif32@6.7.0
|
||||||
|
|
||||||
build_src_filter =
|
build_src_filter =
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
[esp32c3_base]
|
[esp32c3_base]
|
||||||
extends = esp32_base
|
extends = esp32_base
|
||||||
|
custom_esp32_kind = esp32c3
|
||||||
|
|
||||||
monitor_speed = 115200
|
monitor_speed = 115200
|
||||||
monitor_filters = esp32_c3_exception_decoder
|
monitor_filters = esp32_c3_exception_decoder
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
[esp32s2_base]
|
[esp32s2_base]
|
||||||
extends = esp32_base
|
extends = esp32_base
|
||||||
|
custom_esp32_kind = esp32s2
|
||||||
|
|
||||||
build_src_filter =
|
build_src_filter =
|
||||||
${esp32_base.build_src_filter} - <libpax/> -<nimble/> -<mesh/raspihttp>
|
${esp32_base.build_src_filter} - <libpax/> -<nimble/> -<mesh/raspihttp>
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
[esp32s3_base]
|
[esp32s3_base]
|
||||||
extends = esp32_base
|
extends = esp32_base
|
||||||
|
custom_esp32_kind = esp32s3
|
||||||
|
|
||||||
monitor_speed = 115200
|
monitor_speed = 115200
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[nrf52_base]
|
[nrf52_base]
|
||||||
; Instead of the standard nordicnrf52 platform, we use our fork which has our added variant files
|
; Instead of the standard nordicnrf52 platform, we use our fork which has our added variant files
|
||||||
platform = platformio/nordicnrf52@^10.4.0
|
platform = platformio/nordicnrf52@^10.5.0
|
||||||
extends = arduino_base
|
extends = arduino_base
|
||||||
|
|
||||||
build_type = debug
|
build_type = debug
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
; The Portduino based sim environment on top of any host OS, all hardware will be simulated
|
; The Portduino based sim environment on top of any host OS, all hardware will be simulated
|
||||||
[portduino_base]
|
[portduino_base]
|
||||||
platform = https://github.com/meshtastic/platform-native.git#f5ec3c031b0fcd89c0523de9e43eef3a92d59292
|
platform = https://github.com/meshtastic/platform-native.git#ad8112adf82ce1f5b917092cf32be07a077801a0
|
||||||
framework = arduino
|
framework = arduino
|
||||||
|
|
||||||
build_src_filter =
|
build_src_filter =
|
||||||
|
|||||||
@@ -62,7 +62,21 @@ if platform.name == "espressif32":
|
|||||||
import esptool
|
import esptool
|
||||||
|
|
||||||
env.AddPostAction("$BUILD_DIR/${PROGNAME}.bin", esp32_create_combined_bin)
|
env.AddPostAction("$BUILD_DIR/${PROGNAME}.bin", esp32_create_combined_bin)
|
||||||
env.Append(LINKFLAGS=["--specs=nano.specs", "-u", "_printf_float"])
|
|
||||||
|
esp32_kind = env.GetProjectOption("custom_esp32_kind")
|
||||||
|
if esp32_kind == "esp32":
|
||||||
|
# Free up some IRAM by removing auxiliary SPI flash chip drivers.
|
||||||
|
# Wrapped stub symbols are defined in src/platform/esp32/iram-quirk.c.
|
||||||
|
env.Append(
|
||||||
|
LINKFLAGS=[
|
||||||
|
"-Wl,--wrap=esp_flash_chip_gd",
|
||||||
|
"-Wl,--wrap=esp_flash_chip_issi",
|
||||||
|
"-Wl,--wrap=esp_flash_chip_winbond",
|
||||||
|
]
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
# For newer ESP32 targets, using newlib nano works better.
|
||||||
|
env.Append(LINKFLAGS=["--specs=nano.specs", "-u", "_printf_float"])
|
||||||
|
|
||||||
Import("projenv")
|
Import("projenv")
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,58 @@
|
|||||||
|
{
|
||||||
|
"build": {
|
||||||
|
"arduino": {
|
||||||
|
"ldscript": "nrf52840_s140_v6.ld"
|
||||||
|
},
|
||||||
|
"core": "nRF5",
|
||||||
|
"cpu": "cortex-m4",
|
||||||
|
"extra_flags": "-DARDUINO_WIO_WM1110 -DNRF52840_XXAA",
|
||||||
|
"f_cpu": "64000000L",
|
||||||
|
"hwids": [
|
||||||
|
["0x239A", "0x8029"],
|
||||||
|
["0x239A", "0x0029"],
|
||||||
|
["0x239A", "0x002A"],
|
||||||
|
["0x239A", "0x802A"]
|
||||||
|
],
|
||||||
|
"usb_product": "WIO-BOOT",
|
||||||
|
"mcu": "nrf52840",
|
||||||
|
"variant": "Seeed_WIO_WM1110",
|
||||||
|
"bsp": {
|
||||||
|
"name": "adafruit"
|
||||||
|
},
|
||||||
|
"softdevice": {
|
||||||
|
"sd_flags": "-DS140",
|
||||||
|
"sd_name": "s140",
|
||||||
|
"sd_version": "6.1.1",
|
||||||
|
"sd_fwid": "0x00B6"
|
||||||
|
},
|
||||||
|
"bootloader": {
|
||||||
|
"settings_addr": "0xFF000"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"connectivity": ["bluetooth"],
|
||||||
|
"debug": {
|
||||||
|
"jlink_device": "nRF52840_xxAA",
|
||||||
|
"svd_path": "nrf52840.svd"
|
||||||
|
},
|
||||||
|
"frameworks": ["arduino"],
|
||||||
|
"name": "Seeed WIO WM1110",
|
||||||
|
"upload": {
|
||||||
|
"maximum_ram_size": 248832,
|
||||||
|
"maximum_size": 815104,
|
||||||
|
"speed": 115200,
|
||||||
|
"protocol": "nrfutil",
|
||||||
|
"protocols": [
|
||||||
|
"jlink",
|
||||||
|
"nrfjprog",
|
||||||
|
"nrfutil",
|
||||||
|
"stlink",
|
||||||
|
"cmsis-dap",
|
||||||
|
"blackmagic"
|
||||||
|
],
|
||||||
|
"use_1200bps_touch": true,
|
||||||
|
"require_upload_port": true,
|
||||||
|
"wait_for_upload_port": true
|
||||||
|
},
|
||||||
|
"url": "https://www.seeedstudio.com/Wio-WM1110-Dev-Kit-p-5677.html",
|
||||||
|
"vendor": "Seeed Studio"
|
||||||
|
}
|
||||||
@@ -0,0 +1,58 @@
|
|||||||
|
{
|
||||||
|
"build": {
|
||||||
|
"arduino": {
|
||||||
|
"ldscript": "nrf52840_s140_v6.ld"
|
||||||
|
},
|
||||||
|
"core": "nRF5",
|
||||||
|
"cpu": "cortex-m4",
|
||||||
|
"extra_flags": "-DARDUINO_WIO_WM1110 -DNRF52840_XXAA",
|
||||||
|
"f_cpu": "64000000L",
|
||||||
|
"hwids": [
|
||||||
|
["0x239A", "0x8029"],
|
||||||
|
["0x239A", "0x0029"],
|
||||||
|
["0x239A", "0x002A"],
|
||||||
|
["0x239A", "0x802A"]
|
||||||
|
],
|
||||||
|
"usb_product": "WIO-BOOT",
|
||||||
|
"mcu": "nrf52840",
|
||||||
|
"variant": "Seeed_WIO_WM1110",
|
||||||
|
"bsp": {
|
||||||
|
"name": "adafruit"
|
||||||
|
},
|
||||||
|
"softdevice": {
|
||||||
|
"sd_flags": "-DS140",
|
||||||
|
"sd_name": "s140",
|
||||||
|
"sd_version": "6.1.1",
|
||||||
|
"sd_fwid": "0x00B6"
|
||||||
|
},
|
||||||
|
"bootloader": {
|
||||||
|
"settings_addr": "0xFF000"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"connectivity": ["bluetooth"],
|
||||||
|
"debug": {
|
||||||
|
"jlink_device": "nRF52840_xxAA",
|
||||||
|
"svd_path": "nrf52840.svd"
|
||||||
|
},
|
||||||
|
"frameworks": ["arduino"],
|
||||||
|
"name": "Seeed WIO WM1110",
|
||||||
|
"upload": {
|
||||||
|
"maximum_ram_size": 248832,
|
||||||
|
"maximum_size": 815104,
|
||||||
|
"speed": 115200,
|
||||||
|
"protocol": "nrfutil",
|
||||||
|
"protocols": [
|
||||||
|
"jlink",
|
||||||
|
"nrfjprog",
|
||||||
|
"nrfutil",
|
||||||
|
"stlink",
|
||||||
|
"cmsis-dap",
|
||||||
|
"blackmagic"
|
||||||
|
],
|
||||||
|
"use_1200bps_touch": true,
|
||||||
|
"require_upload_port": true,
|
||||||
|
"wait_for_upload_port": true
|
||||||
|
},
|
||||||
|
"url": "https://www.seeedstudio.com/Wio-WM1110-Dev-Kit-p-5677.html",
|
||||||
|
"vendor": "Seeed Studio"
|
||||||
|
}
|
||||||
+23
-7
@@ -31,6 +31,9 @@ default_envs = tbeam
|
|||||||
;default_envs = rak4631
|
;default_envs = rak4631
|
||||||
;default_envs = rak10701
|
;default_envs = rak10701
|
||||||
;default_envs = wio-e5
|
;default_envs = wio-e5
|
||||||
|
;default_envs = radiomaster_900_bandit_nano
|
||||||
|
;default_envs = radiomaster_900_bandit_micro
|
||||||
|
;default_envs = heltec_capsule_sensor_v3
|
||||||
|
|
||||||
extra_configs =
|
extra_configs =
|
||||||
arch/*/*.ini
|
arch/*/*.ini
|
||||||
@@ -70,12 +73,13 @@ build_flags = -Wno-missing-field-initializers
|
|||||||
-DRADIOLIB_EXCLUDE_FSK4
|
-DRADIOLIB_EXCLUDE_FSK4
|
||||||
-DRADIOLIB_EXCLUDE_APRS
|
-DRADIOLIB_EXCLUDE_APRS
|
||||||
-DRADIOLIB_EXCLUDE_LORAWAN
|
-DRADIOLIB_EXCLUDE_LORAWAN
|
||||||
|
-DMESHTASTIC_EXCLUDE_DROPZONE=1
|
||||||
|
|
||||||
monitor_speed = 115200
|
monitor_speed = 115200
|
||||||
|
|
||||||
lib_deps =
|
lib_deps =
|
||||||
jgromes/RadioLib@~6.5.0
|
jgromes/RadioLib@~6.6.0
|
||||||
https://github.com/meshtastic/esp8266-oled-ssd1306.git#ee628ee6c9588d4c56c9e3da35f0fc9448ad54a8 ; ESP8266_SSD1306
|
https://github.com/meshtastic/esp8266-oled-ssd1306.git#69ba98fa30e67b12d4577b121f210f3eb7049d6b ; ESP8266_SSD1306
|
||||||
mathertel/OneButton@^2.5.0 ; OneButton library for non-blocking button debounce
|
mathertel/OneButton@^2.5.0 ; OneButton library for non-blocking button debounce
|
||||||
https://github.com/meshtastic/arduino-fsm.git#7db3702bf0cfe97b783d6c72595e3f38e0b19159
|
https://github.com/meshtastic/arduino-fsm.git#7db3702bf0cfe97b783d6c72595e3f38e0b19159
|
||||||
https://github.com/meshtastic/TinyGPSPlus.git#71a82db35f3b973440044c476d4bcdc673b104f4
|
https://github.com/meshtastic/TinyGPSPlus.git#71a82db35f3b973440044c476d4bcdc673b104f4
|
||||||
@@ -118,10 +122,7 @@ lib_deps =
|
|||||||
adafruit/Adafruit BMP280 Library@^2.6.8
|
adafruit/Adafruit BMP280 Library@^2.6.8
|
||||||
adafruit/Adafruit BMP085 Library@^1.2.4
|
adafruit/Adafruit BMP085 Library@^1.2.4
|
||||||
adafruit/Adafruit BME280 Library@^2.2.2
|
adafruit/Adafruit BME280 Library@^2.2.2
|
||||||
https://github.com/boschsensortec/Bosch-BSEC2-Library#v1.7.2502
|
|
||||||
boschsensortec/BME68x Sensor Library@^1.1.40407
|
|
||||||
adafruit/Adafruit MCP9808 Library@^2.0.0
|
adafruit/Adafruit MCP9808 Library@^2.0.0
|
||||||
https://github.com/KodinLanewave/INA3221@^1.0.0
|
|
||||||
adafruit/Adafruit INA260 Library@^1.5.0
|
adafruit/Adafruit INA260 Library@^1.5.0
|
||||||
adafruit/Adafruit INA219@^1.2.0
|
adafruit/Adafruit INA219@^1.2.0
|
||||||
adafruit/Adafruit SHTC3 Library@^1.0.0
|
adafruit/Adafruit SHTC3 Library@^1.0.0
|
||||||
@@ -130,8 +131,23 @@ lib_deps =
|
|||||||
adafruit/Adafruit PM25 AQI Sensor@^1.0.6
|
adafruit/Adafruit PM25 AQI Sensor@^1.0.6
|
||||||
adafruit/Adafruit MPU6050@^2.2.4
|
adafruit/Adafruit MPU6050@^2.2.4
|
||||||
adafruit/Adafruit LIS3DH@^1.2.4
|
adafruit/Adafruit LIS3DH@^1.2.4
|
||||||
lewisxhe/SensorLib@^0.2.0
|
adafruit/Adafruit AHTX0@^2.0.5
|
||||||
adafruit/Adafruit LSM6DS@^4.7.2
|
adafruit/Adafruit LSM6DS@^4.7.2
|
||||||
mprograms/QMC5883LCompass@^1.2.0
|
|
||||||
adafruit/Adafruit VEML7700 Library@^2.1.6
|
adafruit/Adafruit VEML7700 Library@^2.1.6
|
||||||
adafruit/Adafruit SHT4x Library@^1.0.4
|
adafruit/Adafruit SHT4x Library@^1.0.4
|
||||||
|
adafruit/Adafruit TSL2591 Library@^1.4.5
|
||||||
|
sparkfun/SparkFun Qwiic Scale NAU7802 Arduino Library@^1.0.5
|
||||||
|
ClosedCube OPT3001@^1.1.2
|
||||||
|
emotibit/EmotiBit MLX90632@^1.0.8
|
||||||
|
dfrobot/DFRobot_RTU@^1.0.3
|
||||||
|
|
||||||
|
|
||||||
|
https://github.com/boschsensortec/Bosch-BSEC2-Library#v1.7.2502
|
||||||
|
boschsensortec/BME68x Sensor Library@^1.1.40407
|
||||||
|
https://github.com/KodinLanewave/INA3221@^1.0.0
|
||||||
|
lewisxhe/SensorLib@^0.2.0
|
||||||
|
mprograms/QMC5883LCompass@^1.2.0
|
||||||
|
|
||||||
|
|
||||||
|
https://github.com/meshtastic/DFRobot_LarkWeatherStation#dee914270dc7cb3e43fbf034edd85a63a16a12ee
|
||||||
|
|
||||||
|
|||||||
+1
-1
Submodule protobufs updated: b5dc871a1b...dc066c89f7
@@ -14,6 +14,10 @@
|
|||||||
#include <Arduino.h>
|
#include <Arduino.h>
|
||||||
#include <SensorBMA423.hpp>
|
#include <SensorBMA423.hpp>
|
||||||
#include <Wire.h>
|
#include <Wire.h>
|
||||||
|
#ifdef RAK_4631
|
||||||
|
#include "Fusion/Fusion.h"
|
||||||
|
#include <Rak_BMX160.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
#define ACCELEROMETER_CHECK_INTERVAL_MS 100
|
#define ACCELEROMETER_CHECK_INTERVAL_MS 100
|
||||||
#define ACCELEROMETER_CLICK_THRESHOLD 40
|
#define ACCELEROMETER_CLICK_THRESHOLD 40
|
||||||
@@ -50,12 +54,13 @@ class AccelerometerThread : public concurrency::OSThread
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
acceleremoter_type = type;
|
acceleremoter_type = type;
|
||||||
|
#ifndef RAK_4631
|
||||||
if (!config.display.wake_on_tap_or_motion && !config.device.double_tap_as_button_press) {
|
if (!config.display.wake_on_tap_or_motion && !config.device.double_tap_as_button_press) {
|
||||||
LOG_DEBUG("AccelerometerThread disabling due to no interested configurations\n");
|
LOG_DEBUG("AccelerometerThread disabling due to no interested configurations\n");
|
||||||
disable();
|
disable();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
init();
|
init();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -87,6 +92,72 @@ class AccelerometerThread : public concurrency::OSThread
|
|||||||
wakeScreen();
|
wakeScreen();
|
||||||
return 500;
|
return 500;
|
||||||
}
|
}
|
||||||
|
#ifdef RAK_4631
|
||||||
|
} else if (acceleremoter_type == ScanI2C::DeviceType::BMX160) {
|
||||||
|
sBmx160SensorData_t magAccel;
|
||||||
|
sBmx160SensorData_t gAccel;
|
||||||
|
|
||||||
|
/* Get a new sensor event */
|
||||||
|
bmx160.getAllData(&magAccel, NULL, &gAccel);
|
||||||
|
|
||||||
|
// expirimental calibrate routine. Limited to between 10 and 30 seconds after boot
|
||||||
|
if (millis() > 10 * 1000 && millis() < 30 * 1000) {
|
||||||
|
if (magAccel.x > highestX)
|
||||||
|
highestX = magAccel.x;
|
||||||
|
if (magAccel.x < lowestX)
|
||||||
|
lowestX = magAccel.x;
|
||||||
|
if (magAccel.y > highestY)
|
||||||
|
highestY = magAccel.y;
|
||||||
|
if (magAccel.y < lowestY)
|
||||||
|
lowestY = magAccel.y;
|
||||||
|
if (magAccel.z > highestZ)
|
||||||
|
highestZ = magAccel.z;
|
||||||
|
if (magAccel.z < lowestZ)
|
||||||
|
lowestZ = magAccel.z;
|
||||||
|
}
|
||||||
|
|
||||||
|
int highestRealX = highestX - (highestX + lowestX) / 2;
|
||||||
|
|
||||||
|
magAccel.x -= (highestX + lowestX) / 2;
|
||||||
|
magAccel.y -= (highestY + lowestY) / 2;
|
||||||
|
magAccel.z -= (highestZ + lowestZ) / 2;
|
||||||
|
FusionVector ga, ma;
|
||||||
|
ga.axis.x = -gAccel.x; // default location for the BMX160 is on the rear of the board
|
||||||
|
ga.axis.y = -gAccel.y;
|
||||||
|
ga.axis.z = gAccel.z;
|
||||||
|
ma.axis.x = -magAccel.x;
|
||||||
|
ma.axis.y = -magAccel.y;
|
||||||
|
ma.axis.z = magAccel.z * 3;
|
||||||
|
|
||||||
|
// If we're set to one of the inverted positions
|
||||||
|
if (config.display.compass_orientation > meshtastic_Config_DisplayConfig_CompassOrientation_DEGREES_270) {
|
||||||
|
ma = FusionAxesSwap(ma, FusionAxesAlignmentNXNYPZ);
|
||||||
|
ga = FusionAxesSwap(ga, FusionAxesAlignmentNXNYPZ);
|
||||||
|
}
|
||||||
|
|
||||||
|
float heading = FusionCompassCalculateHeading(FusionConventionNed, ga, ma);
|
||||||
|
|
||||||
|
switch (config.display.compass_orientation) {
|
||||||
|
case meshtastic_Config_DisplayConfig_CompassOrientation_DEGREES_0_INVERTED:
|
||||||
|
case meshtastic_Config_DisplayConfig_CompassOrientation_DEGREES_0:
|
||||||
|
break;
|
||||||
|
case meshtastic_Config_DisplayConfig_CompassOrientation_DEGREES_90:
|
||||||
|
case meshtastic_Config_DisplayConfig_CompassOrientation_DEGREES_90_INVERTED:
|
||||||
|
heading += 90;
|
||||||
|
break;
|
||||||
|
case meshtastic_Config_DisplayConfig_CompassOrientation_DEGREES_180:
|
||||||
|
case meshtastic_Config_DisplayConfig_CompassOrientation_DEGREES_180_INVERTED:
|
||||||
|
heading += 180;
|
||||||
|
break;
|
||||||
|
case meshtastic_Config_DisplayConfig_CompassOrientation_DEGREES_270:
|
||||||
|
case meshtastic_Config_DisplayConfig_CompassOrientation_DEGREES_270_INVERTED:
|
||||||
|
heading += 270;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
screen->setHeading(heading);
|
||||||
|
|
||||||
|
#endif
|
||||||
} else if (acceleremoter_type == ScanI2C::DeviceType::LSM6DS3 && lsm.shake()) {
|
} else if (acceleremoter_type == ScanI2C::DeviceType::LSM6DS3 && lsm.shake()) {
|
||||||
wakeScreen();
|
wakeScreen();
|
||||||
return 500;
|
return 500;
|
||||||
@@ -149,6 +220,11 @@ class AccelerometerThread : public concurrency::OSThread
|
|||||||
bmaSensor.enableTiltIRQ();
|
bmaSensor.enableTiltIRQ();
|
||||||
// It corresponds to isDoubleClick interrupt
|
// It corresponds to isDoubleClick interrupt
|
||||||
bmaSensor.enableWakeupIRQ();
|
bmaSensor.enableWakeupIRQ();
|
||||||
|
#ifdef RAK_4631
|
||||||
|
} else if (acceleremoter_type == ScanI2C::DeviceType::BMX160 && bmx160.begin()) {
|
||||||
|
bmx160.ODR_Config(BMX160_ACCEL_ODR_100HZ, BMX160_GYRO_ODR_100HZ); // set output data rate
|
||||||
|
|
||||||
|
#endif
|
||||||
} else if (acceleremoter_type == ScanI2C::DeviceType::LSM6DS3 && lsm.begin_I2C(accelerometer_found.address)) {
|
} else if (acceleremoter_type == ScanI2C::DeviceType::LSM6DS3 && lsm.begin_I2C(accelerometer_found.address)) {
|
||||||
LOG_DEBUG("LSM6DS3 initializing\n");
|
LOG_DEBUG("LSM6DS3 initializing\n");
|
||||||
// Default threshold of 2G, less sensitive options are 4, 8 or 16G
|
// Default threshold of 2G, less sensitive options are 4, 8 or 16G
|
||||||
@@ -179,6 +255,10 @@ class AccelerometerThread : public concurrency::OSThread
|
|||||||
Adafruit_LIS3DH lis;
|
Adafruit_LIS3DH lis;
|
||||||
Adafruit_LSM6DS3TRC lsm;
|
Adafruit_LSM6DS3TRC lsm;
|
||||||
SensorBMA423 bmaSensor;
|
SensorBMA423 bmaSensor;
|
||||||
|
#ifdef RAK_4631
|
||||||
|
RAK_BMX160 bmx160;
|
||||||
|
float highestX = 0, lowestX = 0, highestY = 0, lowestY = 0, highestZ = 0, lowestZ = 0;
|
||||||
|
#endif
|
||||||
bool BMA_IRQ = false;
|
bool BMA_IRQ = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -41,7 +41,11 @@ ButtonThread::ButtonThread() : OSThread("Button")
|
|||||||
}
|
}
|
||||||
#elif defined(BUTTON_PIN)
|
#elif defined(BUTTON_PIN)
|
||||||
int pin = config.device.button_gpio ? config.device.button_gpio : BUTTON_PIN; // Resolved button pin
|
int pin = config.device.button_gpio ? config.device.button_gpio : BUTTON_PIN; // Resolved button pin
|
||||||
|
#if defined(HELTEC_CAPSULE_SENSOR_V3)
|
||||||
|
this->userButton = OneButton(pin, false, false);
|
||||||
|
#else
|
||||||
this->userButton = OneButton(pin, true, true);
|
this->userButton = OneButton(pin, true, true);
|
||||||
|
#endif
|
||||||
LOG_DEBUG("Using GPIO%02d for button\n", pin);
|
LOG_DEBUG("Using GPIO%02d for button\n", pin);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@@ -232,10 +236,10 @@ void ButtonThread::attachButtonInterrupts()
|
|||||||
attachInterrupt(
|
attachInterrupt(
|
||||||
config.device.button_gpio ? config.device.button_gpio : BUTTON_PIN,
|
config.device.button_gpio ? config.device.button_gpio : BUTTON_PIN,
|
||||||
[]() {
|
[]() {
|
||||||
BaseType_t higherWake = 0;
|
|
||||||
mainDelay.interruptFromISR(&higherWake);
|
|
||||||
ButtonThread::userButton.tick();
|
ButtonThread::userButton.tick();
|
||||||
runASAP = true;
|
runASAP = true;
|
||||||
|
BaseType_t higherWake = 0;
|
||||||
|
mainDelay.interruptFromISR(&higherWake);
|
||||||
},
|
},
|
||||||
CHANGE);
|
CHANGE);
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -0,0 +1,32 @@
|
|||||||
|
/**
|
||||||
|
* @file Fusion.h
|
||||||
|
* @author Seb Madgwick
|
||||||
|
* @brief Main header file for the Fusion library. This is the only file that
|
||||||
|
* needs to be included when using the library.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef FUSION_H
|
||||||
|
#define FUSION_H
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// Includes
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "FusionAhrs.h"
|
||||||
|
#include "FusionAxes.h"
|
||||||
|
#include "FusionCalibration.h"
|
||||||
|
#include "FusionCompass.h"
|
||||||
|
#include "FusionConvention.h"
|
||||||
|
#include "FusionMath.h"
|
||||||
|
#include "FusionOffset.h"
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// End of file
|
||||||
@@ -0,0 +1,542 @@
|
|||||||
|
/**
|
||||||
|
* @file FusionAhrs.c
|
||||||
|
* @author Seb Madgwick
|
||||||
|
* @brief AHRS algorithm to combine gyroscope, accelerometer, and magnetometer
|
||||||
|
* measurements into a single measurement of orientation relative to the Earth.
|
||||||
|
*/
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// Includes
|
||||||
|
|
||||||
|
#include "FusionAhrs.h"
|
||||||
|
#include <float.h> // FLT_MAX
|
||||||
|
#include <math.h> // atan2f, cosf, fabsf, powf, sinf
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// Definitions
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Initial gain used during the initialisation.
|
||||||
|
*/
|
||||||
|
#define INITIAL_GAIN (10.0f)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Initialisation period in seconds.
|
||||||
|
*/
|
||||||
|
#define INITIALISATION_PERIOD (3.0f)
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// Function declarations
|
||||||
|
|
||||||
|
static inline FusionVector HalfGravity(const FusionAhrs *const ahrs);
|
||||||
|
|
||||||
|
static inline FusionVector HalfMagnetic(const FusionAhrs *const ahrs);
|
||||||
|
|
||||||
|
static inline FusionVector Feedback(const FusionVector sensor, const FusionVector reference);
|
||||||
|
|
||||||
|
static inline int Clamp(const int value, const int min, const int max);
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// Functions
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Initialises the AHRS algorithm structure.
|
||||||
|
* @param ahrs AHRS algorithm structure.
|
||||||
|
*/
|
||||||
|
void FusionAhrsInitialise(FusionAhrs *const ahrs)
|
||||||
|
{
|
||||||
|
const FusionAhrsSettings settings = {
|
||||||
|
.convention = FusionConventionNwu,
|
||||||
|
.gain = 0.5f,
|
||||||
|
.gyroscopeRange = 0.0f,
|
||||||
|
.accelerationRejection = 90.0f,
|
||||||
|
.magneticRejection = 90.0f,
|
||||||
|
.recoveryTriggerPeriod = 0,
|
||||||
|
};
|
||||||
|
FusionAhrsSetSettings(ahrs, &settings);
|
||||||
|
FusionAhrsReset(ahrs);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Resets the AHRS algorithm. This is equivalent to reinitialising the
|
||||||
|
* algorithm while maintaining the current settings.
|
||||||
|
* @param ahrs AHRS algorithm structure.
|
||||||
|
*/
|
||||||
|
void FusionAhrsReset(FusionAhrs *const ahrs)
|
||||||
|
{
|
||||||
|
ahrs->quaternion = FUSION_IDENTITY_QUATERNION;
|
||||||
|
ahrs->accelerometer = FUSION_VECTOR_ZERO;
|
||||||
|
ahrs->initialising = true;
|
||||||
|
ahrs->rampedGain = INITIAL_GAIN;
|
||||||
|
ahrs->angularRateRecovery = false;
|
||||||
|
ahrs->halfAccelerometerFeedback = FUSION_VECTOR_ZERO;
|
||||||
|
ahrs->halfMagnetometerFeedback = FUSION_VECTOR_ZERO;
|
||||||
|
ahrs->accelerometerIgnored = false;
|
||||||
|
ahrs->accelerationRecoveryTrigger = 0;
|
||||||
|
ahrs->accelerationRecoveryTimeout = ahrs->settings.recoveryTriggerPeriod;
|
||||||
|
ahrs->magnetometerIgnored = false;
|
||||||
|
ahrs->magneticRecoveryTrigger = 0;
|
||||||
|
ahrs->magneticRecoveryTimeout = ahrs->settings.recoveryTriggerPeriod;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Sets the AHRS algorithm settings.
|
||||||
|
* @param ahrs AHRS algorithm structure.
|
||||||
|
* @param settings Settings.
|
||||||
|
*/
|
||||||
|
void FusionAhrsSetSettings(FusionAhrs *const ahrs, const FusionAhrsSettings *const settings)
|
||||||
|
{
|
||||||
|
ahrs->settings.convention = settings->convention;
|
||||||
|
ahrs->settings.gain = settings->gain;
|
||||||
|
ahrs->settings.gyroscopeRange = settings->gyroscopeRange == 0.0f ? FLT_MAX : 0.98f * settings->gyroscopeRange;
|
||||||
|
ahrs->settings.accelerationRejection = settings->accelerationRejection == 0.0f
|
||||||
|
? FLT_MAX
|
||||||
|
: powf(0.5f * sinf(FusionDegreesToRadians(settings->accelerationRejection)), 2);
|
||||||
|
ahrs->settings.magneticRejection =
|
||||||
|
settings->magneticRejection == 0.0f ? FLT_MAX : powf(0.5f * sinf(FusionDegreesToRadians(settings->magneticRejection)), 2);
|
||||||
|
ahrs->settings.recoveryTriggerPeriod = settings->recoveryTriggerPeriod;
|
||||||
|
ahrs->accelerationRecoveryTimeout = ahrs->settings.recoveryTriggerPeriod;
|
||||||
|
ahrs->magneticRecoveryTimeout = ahrs->settings.recoveryTriggerPeriod;
|
||||||
|
if ((settings->gain == 0.0f) ||
|
||||||
|
(settings->recoveryTriggerPeriod == 0)) { // disable acceleration and magnetic rejection features if gain is zero
|
||||||
|
ahrs->settings.accelerationRejection = FLT_MAX;
|
||||||
|
ahrs->settings.magneticRejection = FLT_MAX;
|
||||||
|
}
|
||||||
|
if (ahrs->initialising == false) {
|
||||||
|
ahrs->rampedGain = ahrs->settings.gain;
|
||||||
|
}
|
||||||
|
ahrs->rampedGainStep = (INITIAL_GAIN - ahrs->settings.gain) / INITIALISATION_PERIOD;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Updates the AHRS algorithm using the gyroscope, accelerometer, and
|
||||||
|
* magnetometer measurements.
|
||||||
|
* @param ahrs AHRS algorithm structure.
|
||||||
|
* @param gyroscope Gyroscope measurement in degrees per second.
|
||||||
|
* @param accelerometer Accelerometer measurement in g.
|
||||||
|
* @param magnetometer Magnetometer measurement in arbitrary units.
|
||||||
|
* @param deltaTime Delta time in seconds.
|
||||||
|
*/
|
||||||
|
void FusionAhrsUpdate(FusionAhrs *const ahrs, const FusionVector gyroscope, const FusionVector accelerometer,
|
||||||
|
const FusionVector magnetometer, const float deltaTime)
|
||||||
|
{
|
||||||
|
#define Q ahrs->quaternion.element
|
||||||
|
|
||||||
|
// Store accelerometer
|
||||||
|
ahrs->accelerometer = accelerometer;
|
||||||
|
|
||||||
|
// Reinitialise if gyroscope range exceeded
|
||||||
|
if ((fabsf(gyroscope.axis.x) > ahrs->settings.gyroscopeRange) || (fabsf(gyroscope.axis.y) > ahrs->settings.gyroscopeRange) ||
|
||||||
|
(fabsf(gyroscope.axis.z) > ahrs->settings.gyroscopeRange)) {
|
||||||
|
const FusionQuaternion quaternion = ahrs->quaternion;
|
||||||
|
FusionAhrsReset(ahrs);
|
||||||
|
ahrs->quaternion = quaternion;
|
||||||
|
ahrs->angularRateRecovery = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ramp down gain during initialisation
|
||||||
|
if (ahrs->initialising) {
|
||||||
|
ahrs->rampedGain -= ahrs->rampedGainStep * deltaTime;
|
||||||
|
if ((ahrs->rampedGain < ahrs->settings.gain) || (ahrs->settings.gain == 0.0f)) {
|
||||||
|
ahrs->rampedGain = ahrs->settings.gain;
|
||||||
|
ahrs->initialising = false;
|
||||||
|
ahrs->angularRateRecovery = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calculate direction of gravity indicated by algorithm
|
||||||
|
const FusionVector halfGravity = HalfGravity(ahrs);
|
||||||
|
|
||||||
|
// Calculate accelerometer feedback
|
||||||
|
FusionVector halfAccelerometerFeedback = FUSION_VECTOR_ZERO;
|
||||||
|
ahrs->accelerometerIgnored = true;
|
||||||
|
if (FusionVectorIsZero(accelerometer) == false) {
|
||||||
|
|
||||||
|
// Calculate accelerometer feedback scaled by 0.5
|
||||||
|
ahrs->halfAccelerometerFeedback = Feedback(FusionVectorNormalise(accelerometer), halfGravity);
|
||||||
|
|
||||||
|
// Don't ignore accelerometer if acceleration error below threshold
|
||||||
|
if (ahrs->initialising ||
|
||||||
|
((FusionVectorMagnitudeSquared(ahrs->halfAccelerometerFeedback) <= ahrs->settings.accelerationRejection))) {
|
||||||
|
ahrs->accelerometerIgnored = false;
|
||||||
|
ahrs->accelerationRecoveryTrigger -= 9;
|
||||||
|
} else {
|
||||||
|
ahrs->accelerationRecoveryTrigger += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Don't ignore accelerometer during acceleration recovery
|
||||||
|
if (ahrs->accelerationRecoveryTrigger > ahrs->accelerationRecoveryTimeout) {
|
||||||
|
ahrs->accelerationRecoveryTimeout = 0;
|
||||||
|
ahrs->accelerometerIgnored = false;
|
||||||
|
} else {
|
||||||
|
ahrs->accelerationRecoveryTimeout = ahrs->settings.recoveryTriggerPeriod;
|
||||||
|
}
|
||||||
|
ahrs->accelerationRecoveryTrigger = Clamp(ahrs->accelerationRecoveryTrigger, 0, ahrs->settings.recoveryTriggerPeriod);
|
||||||
|
|
||||||
|
// Apply accelerometer feedback
|
||||||
|
if (ahrs->accelerometerIgnored == false) {
|
||||||
|
halfAccelerometerFeedback = ahrs->halfAccelerometerFeedback;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calculate magnetometer feedback
|
||||||
|
FusionVector halfMagnetometerFeedback = FUSION_VECTOR_ZERO;
|
||||||
|
ahrs->magnetometerIgnored = true;
|
||||||
|
if (FusionVectorIsZero(magnetometer) == false) {
|
||||||
|
|
||||||
|
// Calculate direction of magnetic field indicated by algorithm
|
||||||
|
const FusionVector halfMagnetic = HalfMagnetic(ahrs);
|
||||||
|
|
||||||
|
// Calculate magnetometer feedback scaled by 0.5
|
||||||
|
ahrs->halfMagnetometerFeedback =
|
||||||
|
Feedback(FusionVectorNormalise(FusionVectorCrossProduct(halfGravity, magnetometer)), halfMagnetic);
|
||||||
|
|
||||||
|
// Don't ignore magnetometer if magnetic error below threshold
|
||||||
|
if (ahrs->initialising ||
|
||||||
|
((FusionVectorMagnitudeSquared(ahrs->halfMagnetometerFeedback) <= ahrs->settings.magneticRejection))) {
|
||||||
|
ahrs->magnetometerIgnored = false;
|
||||||
|
ahrs->magneticRecoveryTrigger -= 9;
|
||||||
|
} else {
|
||||||
|
ahrs->magneticRecoveryTrigger += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Don't ignore magnetometer during magnetic recovery
|
||||||
|
if (ahrs->magneticRecoveryTrigger > ahrs->magneticRecoveryTimeout) {
|
||||||
|
ahrs->magneticRecoveryTimeout = 0;
|
||||||
|
ahrs->magnetometerIgnored = false;
|
||||||
|
} else {
|
||||||
|
ahrs->magneticRecoveryTimeout = ahrs->settings.recoveryTriggerPeriod;
|
||||||
|
}
|
||||||
|
ahrs->magneticRecoveryTrigger = Clamp(ahrs->magneticRecoveryTrigger, 0, ahrs->settings.recoveryTriggerPeriod);
|
||||||
|
|
||||||
|
// Apply magnetometer feedback
|
||||||
|
if (ahrs->magnetometerIgnored == false) {
|
||||||
|
halfMagnetometerFeedback = ahrs->halfMagnetometerFeedback;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert gyroscope to radians per second scaled by 0.5
|
||||||
|
const FusionVector halfGyroscope = FusionVectorMultiplyScalar(gyroscope, FusionDegreesToRadians(0.5f));
|
||||||
|
|
||||||
|
// Apply feedback to gyroscope
|
||||||
|
const FusionVector adjustedHalfGyroscope = FusionVectorAdd(
|
||||||
|
halfGyroscope,
|
||||||
|
FusionVectorMultiplyScalar(FusionVectorAdd(halfAccelerometerFeedback, halfMagnetometerFeedback), ahrs->rampedGain));
|
||||||
|
|
||||||
|
// Integrate rate of change of quaternion
|
||||||
|
ahrs->quaternion = FusionQuaternionAdd(
|
||||||
|
ahrs->quaternion,
|
||||||
|
FusionQuaternionMultiplyVector(ahrs->quaternion, FusionVectorMultiplyScalar(adjustedHalfGyroscope, deltaTime)));
|
||||||
|
|
||||||
|
// Normalise quaternion
|
||||||
|
ahrs->quaternion = FusionQuaternionNormalise(ahrs->quaternion);
|
||||||
|
#undef Q
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Returns the direction of gravity scaled by 0.5.
|
||||||
|
* @param ahrs AHRS algorithm structure.
|
||||||
|
* @return Direction of gravity scaled by 0.5.
|
||||||
|
*/
|
||||||
|
static inline FusionVector HalfGravity(const FusionAhrs *const ahrs)
|
||||||
|
{
|
||||||
|
#define Q ahrs->quaternion.element
|
||||||
|
switch (ahrs->settings.convention) {
|
||||||
|
case FusionConventionNwu:
|
||||||
|
case FusionConventionEnu: {
|
||||||
|
const FusionVector halfGravity = {.axis = {
|
||||||
|
.x = Q.x * Q.z - Q.w * Q.y,
|
||||||
|
.y = Q.y * Q.z + Q.w * Q.x,
|
||||||
|
.z = Q.w * Q.w - 0.5f + Q.z * Q.z,
|
||||||
|
}}; // third column of transposed rotation matrix scaled by 0.5
|
||||||
|
return halfGravity;
|
||||||
|
}
|
||||||
|
case FusionConventionNed: {
|
||||||
|
const FusionVector halfGravity = {.axis = {
|
||||||
|
.x = Q.w * Q.y - Q.x * Q.z,
|
||||||
|
.y = -1.0f * (Q.y * Q.z + Q.w * Q.x),
|
||||||
|
.z = 0.5f - Q.w * Q.w - Q.z * Q.z,
|
||||||
|
}}; // third column of transposed rotation matrix scaled by -0.5
|
||||||
|
return halfGravity;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return FUSION_VECTOR_ZERO; // avoid compiler warning
|
||||||
|
#undef Q
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Returns the direction of the magnetic field scaled by 0.5.
|
||||||
|
* @param ahrs AHRS algorithm structure.
|
||||||
|
* @return Direction of the magnetic field scaled by 0.5.
|
||||||
|
*/
|
||||||
|
static inline FusionVector HalfMagnetic(const FusionAhrs *const ahrs)
|
||||||
|
{
|
||||||
|
#define Q ahrs->quaternion.element
|
||||||
|
switch (ahrs->settings.convention) {
|
||||||
|
case FusionConventionNwu: {
|
||||||
|
const FusionVector halfMagnetic = {.axis = {
|
||||||
|
.x = Q.x * Q.y + Q.w * Q.z,
|
||||||
|
.y = Q.w * Q.w - 0.5f + Q.y * Q.y,
|
||||||
|
.z = Q.y * Q.z - Q.w * Q.x,
|
||||||
|
}}; // second column of transposed rotation matrix scaled by 0.5
|
||||||
|
return halfMagnetic;
|
||||||
|
}
|
||||||
|
case FusionConventionEnu: {
|
||||||
|
const FusionVector halfMagnetic = {.axis = {
|
||||||
|
.x = 0.5f - Q.w * Q.w - Q.x * Q.x,
|
||||||
|
.y = Q.w * Q.z - Q.x * Q.y,
|
||||||
|
.z = -1.0f * (Q.x * Q.z + Q.w * Q.y),
|
||||||
|
}}; // first column of transposed rotation matrix scaled by -0.5
|
||||||
|
return halfMagnetic;
|
||||||
|
}
|
||||||
|
case FusionConventionNed: {
|
||||||
|
const FusionVector halfMagnetic = {.axis = {
|
||||||
|
.x = -1.0f * (Q.x * Q.y + Q.w * Q.z),
|
||||||
|
.y = 0.5f - Q.w * Q.w - Q.y * Q.y,
|
||||||
|
.z = Q.w * Q.x - Q.y * Q.z,
|
||||||
|
}}; // second column of transposed rotation matrix scaled by -0.5
|
||||||
|
return halfMagnetic;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return FUSION_VECTOR_ZERO; // avoid compiler warning
|
||||||
|
#undef Q
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Returns the feedback.
|
||||||
|
* @param sensor Sensor.
|
||||||
|
* @param reference Reference.
|
||||||
|
* @return Feedback.
|
||||||
|
*/
|
||||||
|
static inline FusionVector Feedback(const FusionVector sensor, const FusionVector reference)
|
||||||
|
{
|
||||||
|
if (FusionVectorDotProduct(sensor, reference) < 0.0f) { // if error is >90 degrees
|
||||||
|
return FusionVectorNormalise(FusionVectorCrossProduct(sensor, reference));
|
||||||
|
}
|
||||||
|
return FusionVectorCrossProduct(sensor, reference);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Returns a value limited to maximum and minimum.
|
||||||
|
* @param value Value.
|
||||||
|
* @param min Minimum value.
|
||||||
|
* @param max Maximum value.
|
||||||
|
* @return Value limited to maximum and minimum.
|
||||||
|
*/
|
||||||
|
static inline int Clamp(const int value, const int min, const int max)
|
||||||
|
{
|
||||||
|
if (value < min) {
|
||||||
|
return min;
|
||||||
|
}
|
||||||
|
if (value > max) {
|
||||||
|
return max;
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Updates the AHRS algorithm using the gyroscope and accelerometer
|
||||||
|
* measurements only.
|
||||||
|
* @param ahrs AHRS algorithm structure.
|
||||||
|
* @param gyroscope Gyroscope measurement in degrees per second.
|
||||||
|
* @param accelerometer Accelerometer measurement in g.
|
||||||
|
* @param deltaTime Delta time in seconds.
|
||||||
|
*/
|
||||||
|
void FusionAhrsUpdateNoMagnetometer(FusionAhrs *const ahrs, const FusionVector gyroscope, const FusionVector accelerometer,
|
||||||
|
const float deltaTime)
|
||||||
|
{
|
||||||
|
|
||||||
|
// Update AHRS algorithm
|
||||||
|
FusionAhrsUpdate(ahrs, gyroscope, accelerometer, FUSION_VECTOR_ZERO, deltaTime);
|
||||||
|
|
||||||
|
// Zero heading during initialisation
|
||||||
|
if (ahrs->initialising) {
|
||||||
|
FusionAhrsSetHeading(ahrs, 0.0f);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Updates the AHRS algorithm using the gyroscope, accelerometer, and
|
||||||
|
* heading measurements.
|
||||||
|
* @param ahrs AHRS algorithm structure.
|
||||||
|
* @param gyroscope Gyroscope measurement in degrees per second.
|
||||||
|
* @param accelerometer Accelerometer measurement in g.
|
||||||
|
* @param heading Heading measurement in degrees.
|
||||||
|
* @param deltaTime Delta time in seconds.
|
||||||
|
*/
|
||||||
|
void FusionAhrsUpdateExternalHeading(FusionAhrs *const ahrs, const FusionVector gyroscope, const FusionVector accelerometer,
|
||||||
|
const float heading, const float deltaTime)
|
||||||
|
{
|
||||||
|
#define Q ahrs->quaternion.element
|
||||||
|
|
||||||
|
// Calculate roll
|
||||||
|
const float roll = atan2f(Q.w * Q.x + Q.y * Q.z, 0.5f - Q.y * Q.y - Q.x * Q.x);
|
||||||
|
|
||||||
|
// Calculate magnetometer
|
||||||
|
const float headingRadians = FusionDegreesToRadians(heading);
|
||||||
|
const float sinHeadingRadians = sinf(headingRadians);
|
||||||
|
const FusionVector magnetometer = {.axis = {
|
||||||
|
.x = cosf(headingRadians),
|
||||||
|
.y = -1.0f * cosf(roll) * sinHeadingRadians,
|
||||||
|
.z = sinHeadingRadians * sinf(roll),
|
||||||
|
}};
|
||||||
|
|
||||||
|
// Update AHRS algorithm
|
||||||
|
FusionAhrsUpdate(ahrs, gyroscope, accelerometer, magnetometer, deltaTime);
|
||||||
|
#undef Q
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Returns the quaternion describing the sensor relative to the Earth.
|
||||||
|
* @param ahrs AHRS algorithm structure.
|
||||||
|
* @return Quaternion describing the sensor relative to the Earth.
|
||||||
|
*/
|
||||||
|
FusionQuaternion FusionAhrsGetQuaternion(const FusionAhrs *const ahrs)
|
||||||
|
{
|
||||||
|
return ahrs->quaternion;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Sets the quaternion describing the sensor relative to the Earth.
|
||||||
|
* @param ahrs AHRS algorithm structure.
|
||||||
|
* @param quaternion Quaternion describing the sensor relative to the Earth.
|
||||||
|
*/
|
||||||
|
void FusionAhrsSetQuaternion(FusionAhrs *const ahrs, const FusionQuaternion quaternion)
|
||||||
|
{
|
||||||
|
ahrs->quaternion = quaternion;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Returns the linear acceleration measurement equal to the accelerometer
|
||||||
|
* measurement with the 1 g of gravity removed.
|
||||||
|
* @param ahrs AHRS algorithm structure.
|
||||||
|
* @return Linear acceleration measurement in g.
|
||||||
|
*/
|
||||||
|
FusionVector FusionAhrsGetLinearAcceleration(const FusionAhrs *const ahrs)
|
||||||
|
{
|
||||||
|
#define Q ahrs->quaternion.element
|
||||||
|
|
||||||
|
// Calculate gravity in the sensor coordinate frame
|
||||||
|
const FusionVector gravity = {.axis = {
|
||||||
|
.x = 2.0f * (Q.x * Q.z - Q.w * Q.y),
|
||||||
|
.y = 2.0f * (Q.y * Q.z + Q.w * Q.x),
|
||||||
|
.z = 2.0f * (Q.w * Q.w - 0.5f + Q.z * Q.z),
|
||||||
|
}}; // third column of transposed rotation matrix
|
||||||
|
|
||||||
|
// Remove gravity from accelerometer measurement
|
||||||
|
switch (ahrs->settings.convention) {
|
||||||
|
case FusionConventionNwu:
|
||||||
|
case FusionConventionEnu: {
|
||||||
|
return FusionVectorSubtract(ahrs->accelerometer, gravity);
|
||||||
|
}
|
||||||
|
case FusionConventionNed: {
|
||||||
|
return FusionVectorAdd(ahrs->accelerometer, gravity);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return FUSION_VECTOR_ZERO; // avoid compiler warning
|
||||||
|
#undef Q
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Returns the Earth acceleration measurement equal to accelerometer
|
||||||
|
* measurement in the Earth coordinate frame with the 1 g of gravity removed.
|
||||||
|
* @param ahrs AHRS algorithm structure.
|
||||||
|
* @return Earth acceleration measurement in g.
|
||||||
|
*/
|
||||||
|
FusionVector FusionAhrsGetEarthAcceleration(const FusionAhrs *const ahrs)
|
||||||
|
{
|
||||||
|
#define Q ahrs->quaternion.element
|
||||||
|
#define A ahrs->accelerometer.axis
|
||||||
|
|
||||||
|
// Calculate accelerometer measurement in the Earth coordinate frame
|
||||||
|
const float qwqw = Q.w * Q.w; // calculate common terms to avoid repeated operations
|
||||||
|
const float qwqx = Q.w * Q.x;
|
||||||
|
const float qwqy = Q.w * Q.y;
|
||||||
|
const float qwqz = Q.w * Q.z;
|
||||||
|
const float qxqy = Q.x * Q.y;
|
||||||
|
const float qxqz = Q.x * Q.z;
|
||||||
|
const float qyqz = Q.y * Q.z;
|
||||||
|
FusionVector accelerometer = {.axis = {
|
||||||
|
.x = 2.0f * ((qwqw - 0.5f + Q.x * Q.x) * A.x + (qxqy - qwqz) * A.y + (qxqz + qwqy) * A.z),
|
||||||
|
.y = 2.0f * ((qxqy + qwqz) * A.x + (qwqw - 0.5f + Q.y * Q.y) * A.y + (qyqz - qwqx) * A.z),
|
||||||
|
.z = 2.0f * ((qxqz - qwqy) * A.x + (qyqz + qwqx) * A.y + (qwqw - 0.5f + Q.z * Q.z) * A.z),
|
||||||
|
}}; // rotation matrix multiplied with the accelerometer
|
||||||
|
|
||||||
|
// Remove gravity from accelerometer measurement
|
||||||
|
switch (ahrs->settings.convention) {
|
||||||
|
case FusionConventionNwu:
|
||||||
|
case FusionConventionEnu:
|
||||||
|
accelerometer.axis.z -= 1.0f;
|
||||||
|
break;
|
||||||
|
case FusionConventionNed:
|
||||||
|
accelerometer.axis.z += 1.0f;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return accelerometer;
|
||||||
|
#undef Q
|
||||||
|
#undef A
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Returns the AHRS algorithm internal states.
|
||||||
|
* @param ahrs AHRS algorithm structure.
|
||||||
|
* @return AHRS algorithm internal states.
|
||||||
|
*/
|
||||||
|
FusionAhrsInternalStates FusionAhrsGetInternalStates(const FusionAhrs *const ahrs)
|
||||||
|
{
|
||||||
|
const FusionAhrsInternalStates internalStates = {
|
||||||
|
.accelerationError = FusionRadiansToDegrees(FusionAsin(2.0f * FusionVectorMagnitude(ahrs->halfAccelerometerFeedback))),
|
||||||
|
.accelerometerIgnored = ahrs->accelerometerIgnored,
|
||||||
|
.accelerationRecoveryTrigger =
|
||||||
|
ahrs->settings.recoveryTriggerPeriod == 0
|
||||||
|
? 0.0f
|
||||||
|
: (float)ahrs->accelerationRecoveryTrigger / (float)ahrs->settings.recoveryTriggerPeriod,
|
||||||
|
.magneticError = FusionRadiansToDegrees(FusionAsin(2.0f * FusionVectorMagnitude(ahrs->halfMagnetometerFeedback))),
|
||||||
|
.magnetometerIgnored = ahrs->magnetometerIgnored,
|
||||||
|
.magneticRecoveryTrigger = ahrs->settings.recoveryTriggerPeriod == 0
|
||||||
|
? 0.0f
|
||||||
|
: (float)ahrs->magneticRecoveryTrigger / (float)ahrs->settings.recoveryTriggerPeriod,
|
||||||
|
};
|
||||||
|
return internalStates;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Returns the AHRS algorithm flags.
|
||||||
|
* @param ahrs AHRS algorithm structure.
|
||||||
|
* @return AHRS algorithm flags.
|
||||||
|
*/
|
||||||
|
FusionAhrsFlags FusionAhrsGetFlags(const FusionAhrs *const ahrs)
|
||||||
|
{
|
||||||
|
const FusionAhrsFlags flags = {
|
||||||
|
.initialising = ahrs->initialising,
|
||||||
|
.angularRateRecovery = ahrs->angularRateRecovery,
|
||||||
|
.accelerationRecovery = ahrs->accelerationRecoveryTrigger > ahrs->accelerationRecoveryTimeout,
|
||||||
|
.magneticRecovery = ahrs->magneticRecoveryTrigger > ahrs->magneticRecoveryTimeout,
|
||||||
|
};
|
||||||
|
return flags;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Sets the heading of the orientation measurement provided by the AHRS
|
||||||
|
* algorithm. This function can be used to reset drift in heading when the AHRS
|
||||||
|
* algorithm is being used without a magnetometer.
|
||||||
|
* @param ahrs AHRS algorithm structure.
|
||||||
|
* @param heading Heading angle in degrees.
|
||||||
|
*/
|
||||||
|
void FusionAhrsSetHeading(FusionAhrs *const ahrs, const float heading)
|
||||||
|
{
|
||||||
|
#define Q ahrs->quaternion.element
|
||||||
|
const float yaw = atan2f(Q.w * Q.z + Q.x * Q.y, 0.5f - Q.y * Q.y - Q.z * Q.z);
|
||||||
|
const float halfYawMinusHeading = 0.5f * (yaw - FusionDegreesToRadians(heading));
|
||||||
|
const FusionQuaternion rotation = {.element = {
|
||||||
|
.w = cosf(halfYawMinusHeading),
|
||||||
|
.x = 0.0f,
|
||||||
|
.y = 0.0f,
|
||||||
|
.z = -1.0f * sinf(halfYawMinusHeading),
|
||||||
|
}};
|
||||||
|
ahrs->quaternion = FusionQuaternionMultiply(rotation, ahrs->quaternion);
|
||||||
|
#undef Q
|
||||||
|
}
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// End of file
|
||||||
@@ -0,0 +1,112 @@
|
|||||||
|
/**
|
||||||
|
* @file FusionAhrs.h
|
||||||
|
* @author Seb Madgwick
|
||||||
|
* @brief AHRS algorithm to combine gyroscope, accelerometer, and magnetometer
|
||||||
|
* measurements into a single measurement of orientation relative to the Earth.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef FUSION_AHRS_H
|
||||||
|
#define FUSION_AHRS_H
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// Includes
|
||||||
|
|
||||||
|
#include "FusionConvention.h"
|
||||||
|
#include "FusionMath.h"
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// Definitions
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief AHRS algorithm settings.
|
||||||
|
*/
|
||||||
|
typedef struct {
|
||||||
|
FusionConvention convention;
|
||||||
|
float gain;
|
||||||
|
float gyroscopeRange;
|
||||||
|
float accelerationRejection;
|
||||||
|
float magneticRejection;
|
||||||
|
unsigned int recoveryTriggerPeriod;
|
||||||
|
} FusionAhrsSettings;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief AHRS algorithm structure. Structure members are used internally and
|
||||||
|
* must not be accessed by the application.
|
||||||
|
*/
|
||||||
|
typedef struct {
|
||||||
|
FusionAhrsSettings settings;
|
||||||
|
FusionQuaternion quaternion;
|
||||||
|
FusionVector accelerometer;
|
||||||
|
bool initialising;
|
||||||
|
float rampedGain;
|
||||||
|
float rampedGainStep;
|
||||||
|
bool angularRateRecovery;
|
||||||
|
FusionVector halfAccelerometerFeedback;
|
||||||
|
FusionVector halfMagnetometerFeedback;
|
||||||
|
bool accelerometerIgnored;
|
||||||
|
int accelerationRecoveryTrigger;
|
||||||
|
int accelerationRecoveryTimeout;
|
||||||
|
bool magnetometerIgnored;
|
||||||
|
int magneticRecoveryTrigger;
|
||||||
|
int magneticRecoveryTimeout;
|
||||||
|
} FusionAhrs;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief AHRS algorithm internal states.
|
||||||
|
*/
|
||||||
|
typedef struct {
|
||||||
|
float accelerationError;
|
||||||
|
bool accelerometerIgnored;
|
||||||
|
float accelerationRecoveryTrigger;
|
||||||
|
float magneticError;
|
||||||
|
bool magnetometerIgnored;
|
||||||
|
float magneticRecoveryTrigger;
|
||||||
|
} FusionAhrsInternalStates;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief AHRS algorithm flags.
|
||||||
|
*/
|
||||||
|
typedef struct {
|
||||||
|
bool initialising;
|
||||||
|
bool angularRateRecovery;
|
||||||
|
bool accelerationRecovery;
|
||||||
|
bool magneticRecovery;
|
||||||
|
} FusionAhrsFlags;
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// Function declarations
|
||||||
|
|
||||||
|
void FusionAhrsInitialise(FusionAhrs *const ahrs);
|
||||||
|
|
||||||
|
void FusionAhrsReset(FusionAhrs *const ahrs);
|
||||||
|
|
||||||
|
void FusionAhrsSetSettings(FusionAhrs *const ahrs, const FusionAhrsSettings *const settings);
|
||||||
|
|
||||||
|
void FusionAhrsUpdate(FusionAhrs *const ahrs, const FusionVector gyroscope, const FusionVector accelerometer,
|
||||||
|
const FusionVector magnetometer, const float deltaTime);
|
||||||
|
|
||||||
|
void FusionAhrsUpdateNoMagnetometer(FusionAhrs *const ahrs, const FusionVector gyroscope, const FusionVector accelerometer,
|
||||||
|
const float deltaTime);
|
||||||
|
|
||||||
|
void FusionAhrsUpdateExternalHeading(FusionAhrs *const ahrs, const FusionVector gyroscope, const FusionVector accelerometer,
|
||||||
|
const float heading, const float deltaTime);
|
||||||
|
|
||||||
|
FusionQuaternion FusionAhrsGetQuaternion(const FusionAhrs *const ahrs);
|
||||||
|
|
||||||
|
void FusionAhrsSetQuaternion(FusionAhrs *const ahrs, const FusionQuaternion quaternion);
|
||||||
|
|
||||||
|
FusionVector FusionAhrsGetLinearAcceleration(const FusionAhrs *const ahrs);
|
||||||
|
|
||||||
|
FusionVector FusionAhrsGetEarthAcceleration(const FusionAhrs *const ahrs);
|
||||||
|
|
||||||
|
FusionAhrsInternalStates FusionAhrsGetInternalStates(const FusionAhrs *const ahrs);
|
||||||
|
|
||||||
|
FusionAhrsFlags FusionAhrsGetFlags(const FusionAhrs *const ahrs);
|
||||||
|
|
||||||
|
void FusionAhrsSetHeading(FusionAhrs *const ahrs, const float heading);
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// End of file
|
||||||
@@ -0,0 +1,188 @@
|
|||||||
|
/**
|
||||||
|
* @file FusionAxes.h
|
||||||
|
* @author Seb Madgwick
|
||||||
|
* @brief Swaps sensor axes for alignment with the body axes.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef FUSION_AXES_H
|
||||||
|
#define FUSION_AXES_H
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// Includes
|
||||||
|
|
||||||
|
#include "FusionMath.h"
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// Definitions
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Axes alignment describing the sensor axes relative to the body axes.
|
||||||
|
* For example, if the body X axis is aligned with the sensor Y axis and the
|
||||||
|
* body Y axis is aligned with sensor X axis but pointing the opposite direction
|
||||||
|
* then alignment is +Y-X+Z.
|
||||||
|
*/
|
||||||
|
typedef enum {
|
||||||
|
FusionAxesAlignmentPXPYPZ, /* +X+Y+Z */
|
||||||
|
FusionAxesAlignmentPXNZPY, /* +X-Z+Y */
|
||||||
|
FusionAxesAlignmentPXNYNZ, /* +X-Y-Z */
|
||||||
|
FusionAxesAlignmentPXPZNY, /* +X+Z-Y */
|
||||||
|
FusionAxesAlignmentNXPYNZ, /* -X+Y-Z */
|
||||||
|
FusionAxesAlignmentNXPZPY, /* -X+Z+Y */
|
||||||
|
FusionAxesAlignmentNXNYPZ, /* -X-Y+Z */
|
||||||
|
FusionAxesAlignmentNXNZNY, /* -X-Z-Y */
|
||||||
|
FusionAxesAlignmentPYNXPZ, /* +Y-X+Z */
|
||||||
|
FusionAxesAlignmentPYNZNX, /* +Y-Z-X */
|
||||||
|
FusionAxesAlignmentPYPXNZ, /* +Y+X-Z */
|
||||||
|
FusionAxesAlignmentPYPZPX, /* +Y+Z+X */
|
||||||
|
FusionAxesAlignmentNYPXPZ, /* -Y+X+Z */
|
||||||
|
FusionAxesAlignmentNYNZPX, /* -Y-Z+X */
|
||||||
|
FusionAxesAlignmentNYNXNZ, /* -Y-X-Z */
|
||||||
|
FusionAxesAlignmentNYPZNX, /* -Y+Z-X */
|
||||||
|
FusionAxesAlignmentPZPYNX, /* +Z+Y-X */
|
||||||
|
FusionAxesAlignmentPZPXPY, /* +Z+X+Y */
|
||||||
|
FusionAxesAlignmentPZNYPX, /* +Z-Y+X */
|
||||||
|
FusionAxesAlignmentPZNXNY, /* +Z-X-Y */
|
||||||
|
FusionAxesAlignmentNZPYPX, /* -Z+Y+X */
|
||||||
|
FusionAxesAlignmentNZNXPY, /* -Z-X+Y */
|
||||||
|
FusionAxesAlignmentNZNYNX, /* -Z-Y-X */
|
||||||
|
FusionAxesAlignmentNZPXNY, /* -Z+X-Y */
|
||||||
|
} FusionAxesAlignment;
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// Inline functions
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Swaps sensor axes for alignment with the body axes.
|
||||||
|
* @param sensor Sensor axes.
|
||||||
|
* @param alignment Axes alignment.
|
||||||
|
* @return Sensor axes aligned with the body axes.
|
||||||
|
*/
|
||||||
|
static inline FusionVector FusionAxesSwap(const FusionVector sensor, const FusionAxesAlignment alignment)
|
||||||
|
{
|
||||||
|
FusionVector result;
|
||||||
|
switch (alignment) {
|
||||||
|
case FusionAxesAlignmentPXPYPZ:
|
||||||
|
break;
|
||||||
|
case FusionAxesAlignmentPXNZPY:
|
||||||
|
result.axis.x = +sensor.axis.x;
|
||||||
|
result.axis.y = -sensor.axis.z;
|
||||||
|
result.axis.z = +sensor.axis.y;
|
||||||
|
return result;
|
||||||
|
case FusionAxesAlignmentPXNYNZ:
|
||||||
|
result.axis.x = +sensor.axis.x;
|
||||||
|
result.axis.y = -sensor.axis.y;
|
||||||
|
result.axis.z = -sensor.axis.z;
|
||||||
|
return result;
|
||||||
|
case FusionAxesAlignmentPXPZNY:
|
||||||
|
result.axis.x = +sensor.axis.x;
|
||||||
|
result.axis.y = +sensor.axis.z;
|
||||||
|
result.axis.z = -sensor.axis.y;
|
||||||
|
return result;
|
||||||
|
case FusionAxesAlignmentNXPYNZ:
|
||||||
|
result.axis.x = -sensor.axis.x;
|
||||||
|
result.axis.y = +sensor.axis.y;
|
||||||
|
result.axis.z = -sensor.axis.z;
|
||||||
|
return result;
|
||||||
|
case FusionAxesAlignmentNXPZPY:
|
||||||
|
result.axis.x = -sensor.axis.x;
|
||||||
|
result.axis.y = +sensor.axis.z;
|
||||||
|
result.axis.z = +sensor.axis.y;
|
||||||
|
return result;
|
||||||
|
case FusionAxesAlignmentNXNYPZ:
|
||||||
|
result.axis.x = -sensor.axis.x;
|
||||||
|
result.axis.y = -sensor.axis.y;
|
||||||
|
result.axis.z = +sensor.axis.z;
|
||||||
|
return result;
|
||||||
|
case FusionAxesAlignmentNXNZNY:
|
||||||
|
result.axis.x = -sensor.axis.x;
|
||||||
|
result.axis.y = -sensor.axis.z;
|
||||||
|
result.axis.z = -sensor.axis.y;
|
||||||
|
return result;
|
||||||
|
case FusionAxesAlignmentPYNXPZ:
|
||||||
|
result.axis.x = +sensor.axis.y;
|
||||||
|
result.axis.y = -sensor.axis.x;
|
||||||
|
result.axis.z = +sensor.axis.z;
|
||||||
|
return result;
|
||||||
|
case FusionAxesAlignmentPYNZNX:
|
||||||
|
result.axis.x = +sensor.axis.y;
|
||||||
|
result.axis.y = -sensor.axis.z;
|
||||||
|
result.axis.z = -sensor.axis.x;
|
||||||
|
return result;
|
||||||
|
case FusionAxesAlignmentPYPXNZ:
|
||||||
|
result.axis.x = +sensor.axis.y;
|
||||||
|
result.axis.y = +sensor.axis.x;
|
||||||
|
result.axis.z = -sensor.axis.z;
|
||||||
|
return result;
|
||||||
|
case FusionAxesAlignmentPYPZPX:
|
||||||
|
result.axis.x = +sensor.axis.y;
|
||||||
|
result.axis.y = +sensor.axis.z;
|
||||||
|
result.axis.z = +sensor.axis.x;
|
||||||
|
return result;
|
||||||
|
case FusionAxesAlignmentNYPXPZ:
|
||||||
|
result.axis.x = -sensor.axis.y;
|
||||||
|
result.axis.y = +sensor.axis.x;
|
||||||
|
result.axis.z = +sensor.axis.z;
|
||||||
|
return result;
|
||||||
|
case FusionAxesAlignmentNYNZPX:
|
||||||
|
result.axis.x = -sensor.axis.y;
|
||||||
|
result.axis.y = -sensor.axis.z;
|
||||||
|
result.axis.z = +sensor.axis.x;
|
||||||
|
return result;
|
||||||
|
case FusionAxesAlignmentNYNXNZ:
|
||||||
|
result.axis.x = -sensor.axis.y;
|
||||||
|
result.axis.y = -sensor.axis.x;
|
||||||
|
result.axis.z = -sensor.axis.z;
|
||||||
|
return result;
|
||||||
|
case FusionAxesAlignmentNYPZNX:
|
||||||
|
result.axis.x = -sensor.axis.y;
|
||||||
|
result.axis.y = +sensor.axis.z;
|
||||||
|
result.axis.z = -sensor.axis.x;
|
||||||
|
return result;
|
||||||
|
case FusionAxesAlignmentPZPYNX:
|
||||||
|
result.axis.x = +sensor.axis.z;
|
||||||
|
result.axis.y = +sensor.axis.y;
|
||||||
|
result.axis.z = -sensor.axis.x;
|
||||||
|
return result;
|
||||||
|
case FusionAxesAlignmentPZPXPY:
|
||||||
|
result.axis.x = +sensor.axis.z;
|
||||||
|
result.axis.y = +sensor.axis.x;
|
||||||
|
result.axis.z = +sensor.axis.y;
|
||||||
|
return result;
|
||||||
|
case FusionAxesAlignmentPZNYPX:
|
||||||
|
result.axis.x = +sensor.axis.z;
|
||||||
|
result.axis.y = -sensor.axis.y;
|
||||||
|
result.axis.z = +sensor.axis.x;
|
||||||
|
return result;
|
||||||
|
case FusionAxesAlignmentPZNXNY:
|
||||||
|
result.axis.x = +sensor.axis.z;
|
||||||
|
result.axis.y = -sensor.axis.x;
|
||||||
|
result.axis.z = -sensor.axis.y;
|
||||||
|
return result;
|
||||||
|
case FusionAxesAlignmentNZPYPX:
|
||||||
|
result.axis.x = -sensor.axis.z;
|
||||||
|
result.axis.y = +sensor.axis.y;
|
||||||
|
result.axis.z = +sensor.axis.x;
|
||||||
|
return result;
|
||||||
|
case FusionAxesAlignmentNZNXPY:
|
||||||
|
result.axis.x = -sensor.axis.z;
|
||||||
|
result.axis.y = -sensor.axis.x;
|
||||||
|
result.axis.z = +sensor.axis.y;
|
||||||
|
return result;
|
||||||
|
case FusionAxesAlignmentNZNYNX:
|
||||||
|
result.axis.x = -sensor.axis.z;
|
||||||
|
result.axis.y = -sensor.axis.y;
|
||||||
|
result.axis.z = -sensor.axis.x;
|
||||||
|
return result;
|
||||||
|
case FusionAxesAlignmentNZPXNY:
|
||||||
|
result.axis.x = -sensor.axis.z;
|
||||||
|
result.axis.y = +sensor.axis.x;
|
||||||
|
result.axis.z = -sensor.axis.y;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
return sensor; // avoid compiler warning
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// End of file
|
||||||
@@ -0,0 +1,49 @@
|
|||||||
|
/**
|
||||||
|
* @file FusionCalibration.h
|
||||||
|
* @author Seb Madgwick
|
||||||
|
* @brief Gyroscope, accelerometer, and magnetometer calibration models.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef FUSION_CALIBRATION_H
|
||||||
|
#define FUSION_CALIBRATION_H
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// Includes
|
||||||
|
|
||||||
|
#include "FusionMath.h"
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// Inline functions
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Gyroscope and accelerometer calibration model.
|
||||||
|
* @param uncalibrated Uncalibrated measurement.
|
||||||
|
* @param misalignment Misalignment matrix.
|
||||||
|
* @param sensitivity Sensitivity.
|
||||||
|
* @param offset Offset.
|
||||||
|
* @return Calibrated measurement.
|
||||||
|
*/
|
||||||
|
static inline FusionVector FusionCalibrationInertial(const FusionVector uncalibrated, const FusionMatrix misalignment,
|
||||||
|
const FusionVector sensitivity, const FusionVector offset)
|
||||||
|
{
|
||||||
|
return FusionMatrixMultiplyVector(misalignment,
|
||||||
|
FusionVectorHadamardProduct(FusionVectorSubtract(uncalibrated, offset), sensitivity));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Magnetometer calibration model.
|
||||||
|
* @param uncalibrated Uncalibrated measurement.
|
||||||
|
* @param softIronMatrix Soft-iron matrix.
|
||||||
|
* @param hardIronOffset Hard-iron offset.
|
||||||
|
* @return Calibrated measurement.
|
||||||
|
*/
|
||||||
|
static inline FusionVector FusionCalibrationMagnetic(const FusionVector uncalibrated, const FusionMatrix softIronMatrix,
|
||||||
|
const FusionVector hardIronOffset)
|
||||||
|
{
|
||||||
|
return FusionMatrixMultiplyVector(softIronMatrix, FusionVectorSubtract(uncalibrated, hardIronOffset));
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// End of file
|
||||||
@@ -0,0 +1,51 @@
|
|||||||
|
/**
|
||||||
|
* @file FusionCompass.c
|
||||||
|
* @author Seb Madgwick
|
||||||
|
* @brief Tilt-compensated compass to calculate the magnetic heading using
|
||||||
|
* accelerometer and magnetometer measurements.
|
||||||
|
*/
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// Includes
|
||||||
|
|
||||||
|
#include "FusionCompass.h"
|
||||||
|
#include "FusionAxes.h"
|
||||||
|
#include <math.h> // atan2f
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// Functions
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Calculates the magnetic heading.
|
||||||
|
* @param convention Earth axes convention.
|
||||||
|
* @param accelerometer Accelerometer measurement in any calibrated units.
|
||||||
|
* @param magnetometer Magnetometer measurement in any calibrated units.
|
||||||
|
* @return Heading angle in degrees.
|
||||||
|
*/
|
||||||
|
float FusionCompassCalculateHeading(const FusionConvention convention, const FusionVector accelerometer,
|
||||||
|
const FusionVector magnetometer)
|
||||||
|
{
|
||||||
|
switch (convention) {
|
||||||
|
case FusionConventionNwu: {
|
||||||
|
const FusionVector west = FusionVectorNormalise(FusionVectorCrossProduct(accelerometer, magnetometer));
|
||||||
|
const FusionVector north = FusionVectorNormalise(FusionVectorCrossProduct(west, accelerometer));
|
||||||
|
return FusionRadiansToDegrees(atan2f(west.axis.x, north.axis.x));
|
||||||
|
}
|
||||||
|
case FusionConventionEnu: {
|
||||||
|
const FusionVector west = FusionVectorNormalise(FusionVectorCrossProduct(accelerometer, magnetometer));
|
||||||
|
const FusionVector north = FusionVectorNormalise(FusionVectorCrossProduct(west, accelerometer));
|
||||||
|
const FusionVector east = FusionVectorMultiplyScalar(west, -1.0f);
|
||||||
|
return FusionRadiansToDegrees(atan2f(north.axis.x, east.axis.x));
|
||||||
|
}
|
||||||
|
case FusionConventionNed: {
|
||||||
|
const FusionVector up = FusionVectorMultiplyScalar(accelerometer, -1.0f);
|
||||||
|
const FusionVector west = FusionVectorNormalise(FusionVectorCrossProduct(up, magnetometer));
|
||||||
|
const FusionVector north = FusionVectorNormalise(FusionVectorCrossProduct(west, up));
|
||||||
|
return FusionRadiansToDegrees(atan2f(west.axis.x, north.axis.x));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0; // avoid compiler warning
|
||||||
|
}
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// End of file
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
/**
|
||||||
|
* @file FusionCompass.h
|
||||||
|
* @author Seb Madgwick
|
||||||
|
* @brief Tilt-compensated compass to calculate the magnetic heading using
|
||||||
|
* accelerometer and magnetometer measurements.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef FUSION_COMPASS_H
|
||||||
|
#define FUSION_COMPASS_H
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// Includes
|
||||||
|
|
||||||
|
#include "FusionConvention.h"
|
||||||
|
#include "FusionMath.h"
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// Function declarations
|
||||||
|
|
||||||
|
float FusionCompassCalculateHeading(const FusionConvention convention, const FusionVector accelerometer,
|
||||||
|
const FusionVector magnetometer);
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// End of file
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
/**
|
||||||
|
* @file FusionConvention.h
|
||||||
|
* @author Seb Madgwick
|
||||||
|
* @brief Earth axes convention.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef FUSION_CONVENTION_H
|
||||||
|
#define FUSION_CONVENTION_H
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// Definitions
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Earth axes convention.
|
||||||
|
*/
|
||||||
|
typedef enum {
|
||||||
|
FusionConventionNwu, /* North-West-Up */
|
||||||
|
FusionConventionEnu, /* East-North-Up */
|
||||||
|
FusionConventionNed, /* North-East-Down */
|
||||||
|
} FusionConvention;
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// End of file
|
||||||
@@ -0,0 +1,503 @@
|
|||||||
|
/**
|
||||||
|
* @file FusionMath.h
|
||||||
|
* @author Seb Madgwick
|
||||||
|
* @brief Math library.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef FUSION_MATH_H
|
||||||
|
#define FUSION_MATH_H
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// Includes
|
||||||
|
|
||||||
|
#include <math.h> // M_PI, sqrtf, atan2f, asinf
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// Definitions
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 3D vector.
|
||||||
|
*/
|
||||||
|
typedef union {
|
||||||
|
float array[3];
|
||||||
|
|
||||||
|
struct {
|
||||||
|
float x;
|
||||||
|
float y;
|
||||||
|
float z;
|
||||||
|
} axis;
|
||||||
|
} FusionVector;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Quaternion.
|
||||||
|
*/
|
||||||
|
typedef union {
|
||||||
|
float array[4];
|
||||||
|
|
||||||
|
struct {
|
||||||
|
float w;
|
||||||
|
float x;
|
||||||
|
float y;
|
||||||
|
float z;
|
||||||
|
} element;
|
||||||
|
} FusionQuaternion;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 3x3 matrix in row-major order.
|
||||||
|
* See http://en.wikipedia.org/wiki/Row-major_order
|
||||||
|
*/
|
||||||
|
typedef union {
|
||||||
|
float array[3][3];
|
||||||
|
|
||||||
|
struct {
|
||||||
|
float xx;
|
||||||
|
float xy;
|
||||||
|
float xz;
|
||||||
|
float yx;
|
||||||
|
float yy;
|
||||||
|
float yz;
|
||||||
|
float zx;
|
||||||
|
float zy;
|
||||||
|
float zz;
|
||||||
|
} element;
|
||||||
|
} FusionMatrix;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Euler angles. Roll, pitch, and yaw correspond to rotations around
|
||||||
|
* X, Y, and Z respectively.
|
||||||
|
*/
|
||||||
|
typedef union {
|
||||||
|
float array[3];
|
||||||
|
|
||||||
|
struct {
|
||||||
|
float roll;
|
||||||
|
float pitch;
|
||||||
|
float yaw;
|
||||||
|
} angle;
|
||||||
|
} FusionEuler;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Vector of zeros.
|
||||||
|
*/
|
||||||
|
#define FUSION_VECTOR_ZERO ((FusionVector){.array = {0.0f, 0.0f, 0.0f}})
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Vector of ones.
|
||||||
|
*/
|
||||||
|
#define FUSION_VECTOR_ONES ((FusionVector){.array = {1.0f, 1.0f, 1.0f}})
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Identity quaternion.
|
||||||
|
*/
|
||||||
|
#define FUSION_IDENTITY_QUATERNION ((FusionQuaternion){.array = {1.0f, 0.0f, 0.0f, 0.0f}})
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Identity matrix.
|
||||||
|
*/
|
||||||
|
#define FUSION_IDENTITY_MATRIX ((FusionMatrix){.array = {{1.0f, 0.0f, 0.0f}, {0.0f, 1.0f, 0.0f}, {0.0f, 0.0f, 1.0f}}})
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Euler angles of zero.
|
||||||
|
*/
|
||||||
|
#define FUSION_EULER_ZERO ((FusionEuler){.array = {0.0f, 0.0f, 0.0f}})
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Pi. May not be defined in math.h.
|
||||||
|
*/
|
||||||
|
#ifndef M_PI
|
||||||
|
#define M_PI (3.14159265358979323846)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Include this definition or add as a preprocessor definition to use
|
||||||
|
* normal square root operations.
|
||||||
|
*/
|
||||||
|
// #define FUSION_USE_NORMAL_SQRT
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// Inline functions - Degrees and radians conversion
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Converts degrees to radians.
|
||||||
|
* @param degrees Degrees.
|
||||||
|
* @return Radians.
|
||||||
|
*/
|
||||||
|
static inline float FusionDegreesToRadians(const float degrees)
|
||||||
|
{
|
||||||
|
return degrees * ((float)M_PI / 180.0f);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Converts radians to degrees.
|
||||||
|
* @param radians Radians.
|
||||||
|
* @return Degrees.
|
||||||
|
*/
|
||||||
|
static inline float FusionRadiansToDegrees(const float radians)
|
||||||
|
{
|
||||||
|
return radians * (180.0f / (float)M_PI);
|
||||||
|
}
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// Inline functions - Arc sine
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Returns the arc sine of the value.
|
||||||
|
* @param value Value.
|
||||||
|
* @return Arc sine of the value.
|
||||||
|
*/
|
||||||
|
static inline float FusionAsin(const float value)
|
||||||
|
{
|
||||||
|
if (value <= -1.0f) {
|
||||||
|
return (float)M_PI / -2.0f;
|
||||||
|
}
|
||||||
|
if (value >= 1.0f) {
|
||||||
|
return (float)M_PI / 2.0f;
|
||||||
|
}
|
||||||
|
return asinf(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// Inline functions - Fast inverse square root
|
||||||
|
|
||||||
|
#ifndef FUSION_USE_NORMAL_SQRT
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Calculates the reciprocal of the square root.
|
||||||
|
* See https://pizer.wordpress.com/2008/10/12/fast-inverse-square-root/
|
||||||
|
* @param x Operand.
|
||||||
|
* @return Reciprocal of the square root of x.
|
||||||
|
*/
|
||||||
|
static inline float FusionFastInverseSqrt(const float x)
|
||||||
|
{
|
||||||
|
|
||||||
|
typedef union {
|
||||||
|
float f;
|
||||||
|
int32_t i;
|
||||||
|
} Union32;
|
||||||
|
|
||||||
|
Union32 union32 = {.f = x};
|
||||||
|
union32.i = 0x5F1F1412 - (union32.i >> 1);
|
||||||
|
return union32.f * (1.69000231f - 0.714158168f * x * union32.f * union32.f);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// Inline functions - Vector operations
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Returns true if the vector is zero.
|
||||||
|
* @param vector Vector.
|
||||||
|
* @return True if the vector is zero.
|
||||||
|
*/
|
||||||
|
static inline bool FusionVectorIsZero(const FusionVector vector)
|
||||||
|
{
|
||||||
|
return (vector.axis.x == 0.0f) && (vector.axis.y == 0.0f) && (vector.axis.z == 0.0f);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Returns the sum of two vectors.
|
||||||
|
* @param vectorA Vector A.
|
||||||
|
* @param vectorB Vector B.
|
||||||
|
* @return Sum of two vectors.
|
||||||
|
*/
|
||||||
|
static inline FusionVector FusionVectorAdd(const FusionVector vectorA, const FusionVector vectorB)
|
||||||
|
{
|
||||||
|
const FusionVector result = {.axis = {
|
||||||
|
.x = vectorA.axis.x + vectorB.axis.x,
|
||||||
|
.y = vectorA.axis.y + vectorB.axis.y,
|
||||||
|
.z = vectorA.axis.z + vectorB.axis.z,
|
||||||
|
}};
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Returns vector B subtracted from vector A.
|
||||||
|
* @param vectorA Vector A.
|
||||||
|
* @param vectorB Vector B.
|
||||||
|
* @return Vector B subtracted from vector A.
|
||||||
|
*/
|
||||||
|
static inline FusionVector FusionVectorSubtract(const FusionVector vectorA, const FusionVector vectorB)
|
||||||
|
{
|
||||||
|
const FusionVector result = {.axis = {
|
||||||
|
.x = vectorA.axis.x - vectorB.axis.x,
|
||||||
|
.y = vectorA.axis.y - vectorB.axis.y,
|
||||||
|
.z = vectorA.axis.z - vectorB.axis.z,
|
||||||
|
}};
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Returns the sum of the elements.
|
||||||
|
* @param vector Vector.
|
||||||
|
* @return Sum of the elements.
|
||||||
|
*/
|
||||||
|
static inline float FusionVectorSum(const FusionVector vector)
|
||||||
|
{
|
||||||
|
return vector.axis.x + vector.axis.y + vector.axis.z;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Returns the multiplication of a vector by a scalar.
|
||||||
|
* @param vector Vector.
|
||||||
|
* @param scalar Scalar.
|
||||||
|
* @return Multiplication of a vector by a scalar.
|
||||||
|
*/
|
||||||
|
static inline FusionVector FusionVectorMultiplyScalar(const FusionVector vector, const float scalar)
|
||||||
|
{
|
||||||
|
const FusionVector result = {.axis = {
|
||||||
|
.x = vector.axis.x * scalar,
|
||||||
|
.y = vector.axis.y * scalar,
|
||||||
|
.z = vector.axis.z * scalar,
|
||||||
|
}};
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Calculates the Hadamard product (element-wise multiplication).
|
||||||
|
* @param vectorA Vector A.
|
||||||
|
* @param vectorB Vector B.
|
||||||
|
* @return Hadamard product.
|
||||||
|
*/
|
||||||
|
static inline FusionVector FusionVectorHadamardProduct(const FusionVector vectorA, const FusionVector vectorB)
|
||||||
|
{
|
||||||
|
const FusionVector result = {.axis = {
|
||||||
|
.x = vectorA.axis.x * vectorB.axis.x,
|
||||||
|
.y = vectorA.axis.y * vectorB.axis.y,
|
||||||
|
.z = vectorA.axis.z * vectorB.axis.z,
|
||||||
|
}};
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Returns the cross product.
|
||||||
|
* @param vectorA Vector A.
|
||||||
|
* @param vectorB Vector B.
|
||||||
|
* @return Cross product.
|
||||||
|
*/
|
||||||
|
static inline FusionVector FusionVectorCrossProduct(const FusionVector vectorA, const FusionVector vectorB)
|
||||||
|
{
|
||||||
|
#define A vectorA.axis
|
||||||
|
#define B vectorB.axis
|
||||||
|
const FusionVector result = {.axis = {
|
||||||
|
.x = A.y * B.z - A.z * B.y,
|
||||||
|
.y = A.z * B.x - A.x * B.z,
|
||||||
|
.z = A.x * B.y - A.y * B.x,
|
||||||
|
}};
|
||||||
|
return result;
|
||||||
|
#undef A
|
||||||
|
#undef B
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Returns the dot product.
|
||||||
|
* @param vectorA Vector A.
|
||||||
|
* @param vectorB Vector B.
|
||||||
|
* @return Dot product.
|
||||||
|
*/
|
||||||
|
static inline float FusionVectorDotProduct(const FusionVector vectorA, const FusionVector vectorB)
|
||||||
|
{
|
||||||
|
return FusionVectorSum(FusionVectorHadamardProduct(vectorA, vectorB));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Returns the vector magnitude squared.
|
||||||
|
* @param vector Vector.
|
||||||
|
* @return Vector magnitude squared.
|
||||||
|
*/
|
||||||
|
static inline float FusionVectorMagnitudeSquared(const FusionVector vector)
|
||||||
|
{
|
||||||
|
return FusionVectorSum(FusionVectorHadamardProduct(vector, vector));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Returns the vector magnitude.
|
||||||
|
* @param vector Vector.
|
||||||
|
* @return Vector magnitude.
|
||||||
|
*/
|
||||||
|
static inline float FusionVectorMagnitude(const FusionVector vector)
|
||||||
|
{
|
||||||
|
return sqrtf(FusionVectorMagnitudeSquared(vector));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Returns the normalised vector.
|
||||||
|
* @param vector Vector.
|
||||||
|
* @return Normalised vector.
|
||||||
|
*/
|
||||||
|
static inline FusionVector FusionVectorNormalise(const FusionVector vector)
|
||||||
|
{
|
||||||
|
#ifdef FUSION_USE_NORMAL_SQRT
|
||||||
|
const float magnitudeReciprocal = 1.0f / sqrtf(FusionVectorMagnitudeSquared(vector));
|
||||||
|
#else
|
||||||
|
const float magnitudeReciprocal = FusionFastInverseSqrt(FusionVectorMagnitudeSquared(vector));
|
||||||
|
#endif
|
||||||
|
return FusionVectorMultiplyScalar(vector, magnitudeReciprocal);
|
||||||
|
}
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// Inline functions - Quaternion operations
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Returns the sum of two quaternions.
|
||||||
|
* @param quaternionA Quaternion A.
|
||||||
|
* @param quaternionB Quaternion B.
|
||||||
|
* @return Sum of two quaternions.
|
||||||
|
*/
|
||||||
|
static inline FusionQuaternion FusionQuaternionAdd(const FusionQuaternion quaternionA, const FusionQuaternion quaternionB)
|
||||||
|
{
|
||||||
|
const FusionQuaternion result = {.element = {
|
||||||
|
.w = quaternionA.element.w + quaternionB.element.w,
|
||||||
|
.x = quaternionA.element.x + quaternionB.element.x,
|
||||||
|
.y = quaternionA.element.y + quaternionB.element.y,
|
||||||
|
.z = quaternionA.element.z + quaternionB.element.z,
|
||||||
|
}};
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Returns the multiplication of two quaternions.
|
||||||
|
* @param quaternionA Quaternion A (to be post-multiplied).
|
||||||
|
* @param quaternionB Quaternion B (to be pre-multiplied).
|
||||||
|
* @return Multiplication of two quaternions.
|
||||||
|
*/
|
||||||
|
static inline FusionQuaternion FusionQuaternionMultiply(const FusionQuaternion quaternionA, const FusionQuaternion quaternionB)
|
||||||
|
{
|
||||||
|
#define A quaternionA.element
|
||||||
|
#define B quaternionB.element
|
||||||
|
const FusionQuaternion result = {.element = {
|
||||||
|
.w = A.w * B.w - A.x * B.x - A.y * B.y - A.z * B.z,
|
||||||
|
.x = A.w * B.x + A.x * B.w + A.y * B.z - A.z * B.y,
|
||||||
|
.y = A.w * B.y - A.x * B.z + A.y * B.w + A.z * B.x,
|
||||||
|
.z = A.w * B.z + A.x * B.y - A.y * B.x + A.z * B.w,
|
||||||
|
}};
|
||||||
|
return result;
|
||||||
|
#undef A
|
||||||
|
#undef B
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Returns the multiplication of a quaternion with a vector. This is a
|
||||||
|
* normal quaternion multiplication where the vector is treated a
|
||||||
|
* quaternion with a W element value of zero. The quaternion is post-
|
||||||
|
* multiplied by the vector.
|
||||||
|
* @param quaternion Quaternion.
|
||||||
|
* @param vector Vector.
|
||||||
|
* @return Multiplication of a quaternion with a vector.
|
||||||
|
*/
|
||||||
|
static inline FusionQuaternion FusionQuaternionMultiplyVector(const FusionQuaternion quaternion, const FusionVector vector)
|
||||||
|
{
|
||||||
|
#define Q quaternion.element
|
||||||
|
#define V vector.axis
|
||||||
|
const FusionQuaternion result = {.element = {
|
||||||
|
.w = -Q.x * V.x - Q.y * V.y - Q.z * V.z,
|
||||||
|
.x = Q.w * V.x + Q.y * V.z - Q.z * V.y,
|
||||||
|
.y = Q.w * V.y - Q.x * V.z + Q.z * V.x,
|
||||||
|
.z = Q.w * V.z + Q.x * V.y - Q.y * V.x,
|
||||||
|
}};
|
||||||
|
return result;
|
||||||
|
#undef Q
|
||||||
|
#undef V
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Returns the normalised quaternion.
|
||||||
|
* @param quaternion Quaternion.
|
||||||
|
* @return Normalised quaternion.
|
||||||
|
*/
|
||||||
|
static inline FusionQuaternion FusionQuaternionNormalise(const FusionQuaternion quaternion)
|
||||||
|
{
|
||||||
|
#define Q quaternion.element
|
||||||
|
#ifdef FUSION_USE_NORMAL_SQRT
|
||||||
|
const float magnitudeReciprocal = 1.0f / sqrtf(Q.w * Q.w + Q.x * Q.x + Q.y * Q.y + Q.z * Q.z);
|
||||||
|
#else
|
||||||
|
const float magnitudeReciprocal = FusionFastInverseSqrt(Q.w * Q.w + Q.x * Q.x + Q.y * Q.y + Q.z * Q.z);
|
||||||
|
#endif
|
||||||
|
const FusionQuaternion result = {.element = {
|
||||||
|
.w = Q.w * magnitudeReciprocal,
|
||||||
|
.x = Q.x * magnitudeReciprocal,
|
||||||
|
.y = Q.y * magnitudeReciprocal,
|
||||||
|
.z = Q.z * magnitudeReciprocal,
|
||||||
|
}};
|
||||||
|
return result;
|
||||||
|
#undef Q
|
||||||
|
}
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// Inline functions - Matrix operations
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Returns the multiplication of a matrix with a vector.
|
||||||
|
* @param matrix Matrix.
|
||||||
|
* @param vector Vector.
|
||||||
|
* @return Multiplication of a matrix with a vector.
|
||||||
|
*/
|
||||||
|
static inline FusionVector FusionMatrixMultiplyVector(const FusionMatrix matrix, const FusionVector vector)
|
||||||
|
{
|
||||||
|
#define R matrix.element
|
||||||
|
const FusionVector result = {.axis = {
|
||||||
|
.x = R.xx * vector.axis.x + R.xy * vector.axis.y + R.xz * vector.axis.z,
|
||||||
|
.y = R.yx * vector.axis.x + R.yy * vector.axis.y + R.yz * vector.axis.z,
|
||||||
|
.z = R.zx * vector.axis.x + R.zy * vector.axis.y + R.zz * vector.axis.z,
|
||||||
|
}};
|
||||||
|
return result;
|
||||||
|
#undef R
|
||||||
|
}
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// Inline functions - Conversion operations
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Converts a quaternion to a rotation matrix.
|
||||||
|
* @param quaternion Quaternion.
|
||||||
|
* @return Rotation matrix.
|
||||||
|
*/
|
||||||
|
static inline FusionMatrix FusionQuaternionToMatrix(const FusionQuaternion quaternion)
|
||||||
|
{
|
||||||
|
#define Q quaternion.element
|
||||||
|
const float qwqw = Q.w * Q.w; // calculate common terms to avoid repeated operations
|
||||||
|
const float qwqx = Q.w * Q.x;
|
||||||
|
const float qwqy = Q.w * Q.y;
|
||||||
|
const float qwqz = Q.w * Q.z;
|
||||||
|
const float qxqy = Q.x * Q.y;
|
||||||
|
const float qxqz = Q.x * Q.z;
|
||||||
|
const float qyqz = Q.y * Q.z;
|
||||||
|
const FusionMatrix matrix = {.element = {
|
||||||
|
.xx = 2.0f * (qwqw - 0.5f + Q.x * Q.x),
|
||||||
|
.xy = 2.0f * (qxqy - qwqz),
|
||||||
|
.xz = 2.0f * (qxqz + qwqy),
|
||||||
|
.yx = 2.0f * (qxqy + qwqz),
|
||||||
|
.yy = 2.0f * (qwqw - 0.5f + Q.y * Q.y),
|
||||||
|
.yz = 2.0f * (qyqz - qwqx),
|
||||||
|
.zx = 2.0f * (qxqz - qwqy),
|
||||||
|
.zy = 2.0f * (qyqz + qwqx),
|
||||||
|
.zz = 2.0f * (qwqw - 0.5f + Q.z * Q.z),
|
||||||
|
}};
|
||||||
|
return matrix;
|
||||||
|
#undef Q
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Converts a quaternion to ZYX Euler angles in degrees.
|
||||||
|
* @param quaternion Quaternion.
|
||||||
|
* @return Euler angles in degrees.
|
||||||
|
*/
|
||||||
|
static inline FusionEuler FusionQuaternionToEuler(const FusionQuaternion quaternion)
|
||||||
|
{
|
||||||
|
#define Q quaternion.element
|
||||||
|
const float halfMinusQySquared = 0.5f - Q.y * Q.y; // calculate common terms to avoid repeated operations
|
||||||
|
const FusionEuler euler = {.angle = {
|
||||||
|
.roll = FusionRadiansToDegrees(atan2f(Q.w * Q.x + Q.y * Q.z, halfMinusQySquared - Q.x * Q.x)),
|
||||||
|
.pitch = FusionRadiansToDegrees(FusionAsin(2.0f * (Q.w * Q.y - Q.z * Q.x))),
|
||||||
|
.yaw = FusionRadiansToDegrees(atan2f(Q.w * Q.z + Q.x * Q.y, halfMinusQySquared - Q.z * Q.z)),
|
||||||
|
}};
|
||||||
|
return euler;
|
||||||
|
#undef Q
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// End of file
|
||||||
@@ -0,0 +1,80 @@
|
|||||||
|
/**
|
||||||
|
* @file FusionOffset.c
|
||||||
|
* @author Seb Madgwick
|
||||||
|
* @brief Gyroscope offset correction algorithm for run-time calibration of the
|
||||||
|
* gyroscope offset.
|
||||||
|
*/
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// Includes
|
||||||
|
|
||||||
|
#include "FusionOffset.h"
|
||||||
|
#include <math.h> // fabsf
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// Definitions
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Cutoff frequency in Hz.
|
||||||
|
*/
|
||||||
|
#define CUTOFF_FREQUENCY (0.02f)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Timeout in seconds.
|
||||||
|
*/
|
||||||
|
#define TIMEOUT (5)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Threshold in degrees per second.
|
||||||
|
*/
|
||||||
|
#define THRESHOLD (3.0f)
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// Functions
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Initialises the gyroscope offset algorithm.
|
||||||
|
* @param offset Gyroscope offset algorithm structure.
|
||||||
|
* @param sampleRate Sample rate in Hz.
|
||||||
|
*/
|
||||||
|
void FusionOffsetInitialise(FusionOffset *const offset, const unsigned int sampleRate)
|
||||||
|
{
|
||||||
|
offset->filterCoefficient = 2.0f * (float)M_PI * CUTOFF_FREQUENCY * (1.0f / (float)sampleRate);
|
||||||
|
offset->timeout = TIMEOUT * sampleRate;
|
||||||
|
offset->timer = 0;
|
||||||
|
offset->gyroscopeOffset = FUSION_VECTOR_ZERO;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Updates the gyroscope offset algorithm and returns the corrected
|
||||||
|
* gyroscope measurement.
|
||||||
|
* @param offset Gyroscope offset algorithm structure.
|
||||||
|
* @param gyroscope Gyroscope measurement in degrees per second.
|
||||||
|
* @return Corrected gyroscope measurement in degrees per second.
|
||||||
|
*/
|
||||||
|
FusionVector FusionOffsetUpdate(FusionOffset *const offset, FusionVector gyroscope)
|
||||||
|
{
|
||||||
|
|
||||||
|
// Subtract offset from gyroscope measurement
|
||||||
|
gyroscope = FusionVectorSubtract(gyroscope, offset->gyroscopeOffset);
|
||||||
|
|
||||||
|
// Reset timer if gyroscope not stationary
|
||||||
|
if ((fabsf(gyroscope.axis.x) > THRESHOLD) || (fabsf(gyroscope.axis.y) > THRESHOLD) || (fabsf(gyroscope.axis.z) > THRESHOLD)) {
|
||||||
|
offset->timer = 0;
|
||||||
|
return gyroscope;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Increment timer while gyroscope stationary
|
||||||
|
if (offset->timer < offset->timeout) {
|
||||||
|
offset->timer++;
|
||||||
|
return gyroscope;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Adjust offset if timer has elapsed
|
||||||
|
offset->gyroscopeOffset =
|
||||||
|
FusionVectorAdd(offset->gyroscopeOffset, FusionVectorMultiplyScalar(gyroscope, offset->filterCoefficient));
|
||||||
|
return gyroscope;
|
||||||
|
}
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// End of file
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
/**
|
||||||
|
* @file FusionOffset.h
|
||||||
|
* @author Seb Madgwick
|
||||||
|
* @brief Gyroscope offset correction algorithm for run-time calibration of the
|
||||||
|
* gyroscope offset.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef FUSION_OFFSET_H
|
||||||
|
#define FUSION_OFFSET_H
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// Includes
|
||||||
|
|
||||||
|
#include "FusionMath.h"
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// Definitions
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Gyroscope offset algorithm structure. Structure members are used
|
||||||
|
* internally and must not be accessed by the application.
|
||||||
|
*/
|
||||||
|
typedef struct {
|
||||||
|
float filterCoefficient;
|
||||||
|
unsigned int timeout;
|
||||||
|
unsigned int timer;
|
||||||
|
FusionVector gyroscopeOffset;
|
||||||
|
} FusionOffset;
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// Function declarations
|
||||||
|
|
||||||
|
void FusionOffsetInitialise(FusionOffset *const offset, const unsigned int sampleRate);
|
||||||
|
|
||||||
|
FusionVector FusionOffsetUpdate(FusionOffset *const offset, FusionVector gyroscope);
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// End of file
|
||||||
+18
-7
@@ -335,13 +335,20 @@ class AnalogBatteryLevel : public HasBatteryLevel
|
|||||||
virtual bool isVbusIn() override
|
virtual bool isVbusIn() override
|
||||||
{
|
{
|
||||||
#ifdef EXT_PWR_DETECT
|
#ifdef EXT_PWR_DETECT
|
||||||
// if external powered that pin will be pulled up
|
#ifdef HELTEC_CAPSULE_SENSOR_V3
|
||||||
if (digitalRead(EXT_PWR_DETECT) == HIGH) {
|
// if external powered that pin will be pulled down
|
||||||
return true;
|
if (digitalRead(EXT_PWR_DETECT) == LOW) {
|
||||||
}
|
return true;
|
||||||
// if it's not HIGH - check the battery
|
}
|
||||||
|
// if it's not LOW - check the battery
|
||||||
|
#else
|
||||||
|
// if external powered that pin will be pulled up
|
||||||
|
if (digitalRead(EXT_PWR_DETECT) == HIGH) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
// if it's not HIGH - check the battery
|
||||||
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
return getBattVoltage() > chargingVolt;
|
return getBattVoltage() > chargingVolt;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -421,7 +428,11 @@ Power::Power() : OSThread("Power")
|
|||||||
bool Power::analogInit()
|
bool Power::analogInit()
|
||||||
{
|
{
|
||||||
#ifdef EXT_PWR_DETECT
|
#ifdef EXT_PWR_DETECT
|
||||||
pinMode(EXT_PWR_DETECT, INPUT);
|
#ifdef HELTEC_CAPSULE_SENSOR_V3
|
||||||
|
pinMode(EXT_PWR_DETECT, INPUT_PULLUP);
|
||||||
|
#else
|
||||||
|
pinMode(EXT_PWR_DETECT, INPUT);
|
||||||
|
#endif
|
||||||
#endif
|
#endif
|
||||||
#ifdef EXT_CHRG_DETECT
|
#ifdef EXT_CHRG_DETECT
|
||||||
pinMode(EXT_CHRG_DETECT, ext_chrg_detect_mode);
|
pinMode(EXT_CHRG_DETECT, ext_chrg_detect_mode);
|
||||||
|
|||||||
+10
-1
@@ -59,9 +59,18 @@ class PowerStatus : public Status
|
|||||||
int getBatteryVoltageMv() const { return batteryVoltageMv; }
|
int getBatteryVoltageMv() const { return batteryVoltageMv; }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Note: 0% battery means 'unknown/this board doesn't have a battery installed'
|
* Note: for boards with battery pin or PMU, 0% battery means 'unknown/this board doesn't have a battery installed'
|
||||||
*/
|
*/
|
||||||
|
#if defined(HAS_PMU) || defined(BATTERY_PIN)
|
||||||
uint8_t getBatteryChargePercent() const { return getHasBattery() ? batteryChargePercent : 0; }
|
uint8_t getBatteryChargePercent() const { return getHasBattery() ? batteryChargePercent : 0; }
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Note: for boards without battery pin and PMU, 101% battery means 'the board is using external power'
|
||||||
|
*/
|
||||||
|
#if !defined(HAS_PMU) && !defined(BATTERY_PIN)
|
||||||
|
uint8_t getBatteryChargePercent() const { return getHasBattery() ? batteryChargePercent : 101; }
|
||||||
|
#endif
|
||||||
|
|
||||||
bool matches(const PowerStatus *newStatus) const
|
bool matches(const PowerStatus *newStatus) const
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -128,8 +128,15 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|||||||
#define LPS22HB_ADDR_ALT 0x5D
|
#define LPS22HB_ADDR_ALT 0x5D
|
||||||
#define SHT31_4x_ADDR 0x44
|
#define SHT31_4x_ADDR 0x44
|
||||||
#define PMSA0031_ADDR 0x12
|
#define PMSA0031_ADDR 0x12
|
||||||
|
#define AHT10_ADDR 0x38
|
||||||
#define RCWL9620_ADDR 0x57
|
#define RCWL9620_ADDR 0x57
|
||||||
#define VEML7700_ADDR 0x10
|
#define VEML7700_ADDR 0x10
|
||||||
|
#define TSL25911_ADDR 0x29
|
||||||
|
#define OPT3001_ADDR 0x45
|
||||||
|
#define OPT3001_ADDR_ALT 0x44
|
||||||
|
#define MLX90632_ADDR 0x3A
|
||||||
|
#define DFROBOT_LARK_ADDR 0x42
|
||||||
|
#define NAU7802_ADDR 0x2A
|
||||||
|
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
// ACCELEROMETER
|
// ACCELEROMETER
|
||||||
@@ -138,6 +145,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|||||||
#define LIS3DH_ADR 0x18
|
#define LIS3DH_ADR 0x18
|
||||||
#define BMA423_ADDR 0x19
|
#define BMA423_ADDR 0x19
|
||||||
#define LSM6DS3_ADDR 0x6A
|
#define LSM6DS3_ADDR 0x6A
|
||||||
|
#define BMX160_ADDR 0x69
|
||||||
|
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
// LED
|
// LED
|
||||||
|
|||||||
@@ -1,5 +1,16 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
enum LoRaRadioType { NO_RADIO, STM32WLx_RADIO, SIM_RADIO, RF95_RADIO, SX1262_RADIO, SX1268_RADIO, LLCC68_RADIO, SX1280_RADIO };
|
enum LoRaRadioType {
|
||||||
|
NO_RADIO,
|
||||||
|
STM32WLx_RADIO,
|
||||||
|
SIM_RADIO,
|
||||||
|
RF95_RADIO,
|
||||||
|
SX1262_RADIO,
|
||||||
|
SX1268_RADIO,
|
||||||
|
LLCC68_RADIO,
|
||||||
|
SX1280_RADIO,
|
||||||
|
LR1110_RADIO,
|
||||||
|
LR1120_RADIO
|
||||||
|
};
|
||||||
|
|
||||||
extern LoRaRadioType radioType;
|
extern LoRaRadioType radioType;
|
||||||
@@ -37,8 +37,8 @@ ScanI2C::FoundDevice ScanI2C::firstKeyboard() const
|
|||||||
|
|
||||||
ScanI2C::FoundDevice ScanI2C::firstAccelerometer() const
|
ScanI2C::FoundDevice ScanI2C::firstAccelerometer() const
|
||||||
{
|
{
|
||||||
ScanI2C::DeviceType types[] = {MPU6050, LIS3DH, BMA423, LSM6DS3};
|
ScanI2C::DeviceType types[] = {MPU6050, LIS3DH, BMA423, LSM6DS3, BMX160};
|
||||||
return firstOfOrNONE(4, types);
|
return firstOfOrNONE(5, types);
|
||||||
}
|
}
|
||||||
|
|
||||||
ScanI2C::FoundDevice ScanI2C::find(ScanI2C::DeviceType) const
|
ScanI2C::FoundDevice ScanI2C::find(ScanI2C::DeviceType) const
|
||||||
|
|||||||
@@ -45,6 +45,13 @@ class ScanI2C
|
|||||||
VEML7700,
|
VEML7700,
|
||||||
RCWL9620,
|
RCWL9620,
|
||||||
NCP5623,
|
NCP5623,
|
||||||
|
TSL2591,
|
||||||
|
OPT3001,
|
||||||
|
MLX90632,
|
||||||
|
AHT10,
|
||||||
|
BMX160,
|
||||||
|
DFROBOT_LARK,
|
||||||
|
NAU7802
|
||||||
} DeviceType;
|
} DeviceType;
|
||||||
|
|
||||||
// typedef uint8_t DeviceAddress;
|
// typedef uint8_t DeviceAddress;
|
||||||
|
|||||||
@@ -269,7 +269,12 @@ void ScanI2CTwoWire::scanPort(I2CPort port, uint8_t *address, uint8_t asize)
|
|||||||
type = BMP_280;
|
type = BMP_280;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
#ifndef HAS_NCP5623
|
||||||
|
case AHT10_ADDR:
|
||||||
|
LOG_INFO("AHT10 sensor found at address 0x%x\n", (uint8_t)addr.address);
|
||||||
|
type = AHT10;
|
||||||
|
break;
|
||||||
|
#endif
|
||||||
case INA_ADDR:
|
case INA_ADDR:
|
||||||
case INA_ADDR_ALTERNATE:
|
case INA_ADDR_ALTERNATE:
|
||||||
case INA_ADDR_WAVESHARE_UPS:
|
case INA_ADDR_WAVESHARE_UPS:
|
||||||
@@ -289,8 +294,9 @@ void ScanI2CTwoWire::scanPort(I2CPort port, uint8_t *address, uint8_t asize)
|
|||||||
if (registerValue == 0x5449) {
|
if (registerValue == 0x5449) {
|
||||||
LOG_INFO("INA3221 sensor found at address 0x%x\n", (uint8_t)addr.address);
|
LOG_INFO("INA3221 sensor found at address 0x%x\n", (uint8_t)addr.address);
|
||||||
type = INA3221;
|
type = INA3221;
|
||||||
} else { // Unknown device
|
} else {
|
||||||
LOG_INFO("No INA3221 found at address 0x%x\n", (uint8_t)addr.address);
|
LOG_INFO("DFRobot Lark weather station found at address 0x%x\n", (uint8_t)addr.address);
|
||||||
|
type = DFROBOT_LARK;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case MCP9808_ADDR:
|
case MCP9808_ADDR:
|
||||||
@@ -310,6 +316,9 @@ void ScanI2CTwoWire::scanPort(I2CPort port, uint8_t *address, uint8_t asize)
|
|||||||
if (registerValue == 0x11a2) {
|
if (registerValue == 0x11a2) {
|
||||||
type = SHT4X;
|
type = SHT4X;
|
||||||
LOG_INFO("SHT4X sensor found\n");
|
LOG_INFO("SHT4X sensor found\n");
|
||||||
|
} else if (getRegisterValue(ScanI2CTwoWire::RegisterLocation(addr, 0x7E), 2) == 0x5449) {
|
||||||
|
type = OPT3001;
|
||||||
|
LOG_INFO("OPT3001 light sensor found\n");
|
||||||
} else {
|
} else {
|
||||||
type = SHT31;
|
type = SHT31;
|
||||||
LOG_INFO("SHT31 sensor found\n");
|
LOG_INFO("SHT31 sensor found\n");
|
||||||
@@ -346,10 +355,15 @@ void ScanI2CTwoWire::scanPort(I2CPort port, uint8_t *address, uint8_t asize)
|
|||||||
|
|
||||||
SCAN_SIMPLE_CASE(PMSA0031_ADDR, PMSA0031, "PMSA0031 air quality sensor found\n")
|
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(MPU6050_ADDR, MPU6050, "MPU6050 accelerometer found\n");
|
||||||
|
SCAN_SIMPLE_CASE(BMX160_ADDR, BMX160, "BMX160 accelerometer found\n");
|
||||||
SCAN_SIMPLE_CASE(BMA423_ADDR, BMA423, "BMA423 accelerometer found\n");
|
SCAN_SIMPLE_CASE(BMA423_ADDR, BMA423, "BMA423 accelerometer found\n");
|
||||||
SCAN_SIMPLE_CASE(LSM6DS3_ADDR, LSM6DS3, "LSM6DS3 accelerometer found at address 0x%x\n", (uint8_t)addr.address);
|
SCAN_SIMPLE_CASE(LSM6DS3_ADDR, LSM6DS3, "LSM6DS3 accelerometer found at address 0x%x\n", (uint8_t)addr.address);
|
||||||
SCAN_SIMPLE_CASE(TCA9555_ADDR, TCA9555, "TCA9555 I2C expander found\n");
|
SCAN_SIMPLE_CASE(TCA9555_ADDR, TCA9555, "TCA9555 I2C expander found\n");
|
||||||
SCAN_SIMPLE_CASE(VEML7700_ADDR, VEML7700, "VEML7700 light sensor found\n");
|
SCAN_SIMPLE_CASE(VEML7700_ADDR, VEML7700, "VEML7700 light sensor found\n");
|
||||||
|
SCAN_SIMPLE_CASE(TSL25911_ADDR, TSL2591, "TSL2591 light sensor found\n");
|
||||||
|
SCAN_SIMPLE_CASE(OPT3001_ADDR, OPT3001, "OPT3001 light sensor found\n");
|
||||||
|
SCAN_SIMPLE_CASE(MLX90632_ADDR, MLX90632, "MLX90632 IR temp sensor found\n");
|
||||||
|
SCAN_SIMPLE_CASE(NAU7802_ADDR, NAU7802, "NAU7802 based scale found\n");
|
||||||
|
|
||||||
default:
|
default:
|
||||||
LOG_INFO("Device found at address 0x%x was not able to be enumerated\n", addr.address);
|
LOG_INFO("Device found at address 0x%x was not able to be enumerated\n", addr.address);
|
||||||
|
|||||||
+95
-34
@@ -21,6 +21,19 @@
|
|||||||
#define GPS_RESET_MODE HIGH
|
#define GPS_RESET_MODE HIGH
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
// How many minutes of sleep make it worthwhile to power-off the GPS
|
||||||
|
// Shorter than this, and GPS will only enter standby
|
||||||
|
// Affected by lock-time, and config.position.gps_update_interval
|
||||||
|
#ifndef GPS_STANDBY_THRESHOLD_MINUTES
|
||||||
|
#define GPS_STANDBY_THRESHOLD_MINUTES 15
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// How many seconds of sleep make it worthwhile for the GPS to use powered-on standby
|
||||||
|
// Shorter than this, and we'll just wait instead
|
||||||
|
#ifndef GPS_IDLE_THRESHOLD_SECONDS
|
||||||
|
#define GPS_IDLE_THRESHOLD_SECONDS 10
|
||||||
|
#endif
|
||||||
|
|
||||||
#if defined(NRF52840_XXAA) || defined(NRF52833_XXAA) || defined(ARCH_ESP32) || defined(ARCH_PORTDUINO)
|
#if defined(NRF52840_XXAA) || defined(NRF52833_XXAA) || defined(ARCH_ESP32) || defined(ARCH_PORTDUINO)
|
||||||
HardwareSerial *GPS::_serial_gps = &Serial1;
|
HardwareSerial *GPS::_serial_gps = &Serial1;
|
||||||
#else
|
#else
|
||||||
@@ -767,7 +780,24 @@ GPS::~GPS()
|
|||||||
|
|
||||||
void GPS::setGPSPower(bool on, bool standbyOnly, uint32_t sleepTime)
|
void GPS::setGPSPower(bool on, bool standbyOnly, uint32_t sleepTime)
|
||||||
{
|
{
|
||||||
LOG_INFO("Setting GPS power=%d\n", on);
|
// Record the current powerState
|
||||||
|
if (on)
|
||||||
|
powerState = GPS_ACTIVE;
|
||||||
|
else if (!enabled) // User has disabled with triple press
|
||||||
|
powerState = GPS_OFF;
|
||||||
|
else if (sleepTime <= GPS_IDLE_THRESHOLD_SECONDS * 1000UL)
|
||||||
|
powerState = GPS_IDLE;
|
||||||
|
else if (standbyOnly)
|
||||||
|
powerState = GPS_STANDBY;
|
||||||
|
else
|
||||||
|
powerState = GPS_OFF;
|
||||||
|
|
||||||
|
LOG_DEBUG("GPS::powerState=%d\n", powerState);
|
||||||
|
|
||||||
|
// If the next update is due *really soon*, don't actually power off or enter standby. Just wait it out.
|
||||||
|
if (!on && powerState == GPS_IDLE)
|
||||||
|
return;
|
||||||
|
|
||||||
if (on) {
|
if (on) {
|
||||||
clearBuffer(); // drop any old data waiting in the buffer before re-enabling
|
clearBuffer(); // drop any old data waiting in the buffer before re-enabling
|
||||||
if (en_gpio)
|
if (en_gpio)
|
||||||
@@ -861,45 +891,72 @@ void GPS::setConnected()
|
|||||||
*
|
*
|
||||||
* calls sleep/wake
|
* calls sleep/wake
|
||||||
*/
|
*/
|
||||||
void GPS::setAwake(bool on)
|
void GPS::setAwake(bool wantAwake)
|
||||||
{
|
{
|
||||||
if (isAwake != on) {
|
|
||||||
LOG_DEBUG("WANT GPS=%d\n", on);
|
|
||||||
isAwake = on;
|
|
||||||
if (!enabled) { // short circuit if the user has disabled GPS
|
|
||||||
setGPSPower(false, false, 0);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (on) {
|
// If user has disabled GPS, make sure it is off, not just in standby or idle
|
||||||
|
if (!wantAwake && !enabled && powerState != GPS_OFF) {
|
||||||
|
setGPSPower(false, false, 0);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If GPS power state needs to change
|
||||||
|
if ((wantAwake && powerState != GPS_ACTIVE) || (!wantAwake && powerState == GPS_ACTIVE)) {
|
||||||
|
LOG_DEBUG("WANT GPS=%d\n", wantAwake);
|
||||||
|
|
||||||
|
// Calculate how long it takes to get a GPS lock
|
||||||
|
if (wantAwake) {
|
||||||
|
// Record the time we start looking for a lock
|
||||||
lastWakeStartMsec = millis();
|
lastWakeStartMsec = millis();
|
||||||
} else {
|
} else {
|
||||||
|
// Record by how much we missed our ideal target postion.gps_update_interval (for logging only)
|
||||||
|
// Need to calculate this before we update lastSleepStartMsec, to make the new prediction
|
||||||
|
int32_t lateByMsec = (int32_t)(millis() - lastSleepStartMsec) - (int32_t)getSleepTime();
|
||||||
|
|
||||||
|
// Record the time we finish looking for a lock
|
||||||
lastSleepStartMsec = millis();
|
lastSleepStartMsec = millis();
|
||||||
if (GPSCycles == 1) { // Skipping initial lock time, as it will likely be much longer than average
|
|
||||||
averageLockTime = lastSleepStartMsec - lastWakeStartMsec;
|
// How long did it take to get GPS lock this time?
|
||||||
} else if (GPSCycles > 1) {
|
uint32_t lockTime = lastSleepStartMsec - lastWakeStartMsec;
|
||||||
averageLockTime += ((int32_t)(lastSleepStartMsec - lastWakeStartMsec) - averageLockTime) / (int32_t)GPSCycles;
|
|
||||||
|
// Update the lock-time prediction
|
||||||
|
// Used pre-emptively, attempting to hit target of gps.position_update_interval
|
||||||
|
switch (GPSCycles) {
|
||||||
|
case 0:
|
||||||
|
LOG_DEBUG("Initial GPS lock took %ds\n", lockTime / 1000);
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
predictedLockTime = lockTime; // Avoid slow ramp-up - start with a real value
|
||||||
|
LOG_DEBUG("GPS Lock took %ds\n", lockTime / 1000);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
// Predict lock-time using exponential smoothing: respond slowly to changes
|
||||||
|
predictedLockTime = (lockTime * 0.2) + (predictedLockTime * 0.8); // Latest lock time has 20% weight on prediction
|
||||||
|
LOG_INFO("GPS Lock took %ds. %s by %ds. Next lock predicted to take %ds.\n", lockTime / 1000,
|
||||||
|
(lateByMsec > 0) ? "Late" : "Early", abs(lateByMsec) / 1000, predictedLockTime / 1000);
|
||||||
}
|
}
|
||||||
GPSCycles++;
|
GPSCycles++;
|
||||||
LOG_DEBUG("GPS Lock took %d, average %d\n", (lastSleepStartMsec - lastWakeStartMsec) / 1000, averageLockTime / 1000);
|
|
||||||
}
|
}
|
||||||
if ((int32_t)getSleepTime() - averageLockTime >
|
|
||||||
15 * 60 * 1000) { // 15 minutes is probably long enough to make a complete poweroff worth it.
|
// How long to wait before attempting next GPS update
|
||||||
setGPSPower(on, false, getSleepTime() - averageLockTime);
|
// Aims to hit position.gps_update_interval by using the lock-time prediction
|
||||||
return;
|
uint32_t compensatedSleepTime = (getSleepTime() > predictedLockTime) ? (getSleepTime() - predictedLockTime) : 0;
|
||||||
} else if ((int32_t)getSleepTime() - averageLockTime > 10000) { // 10 seconds is enough for standby
|
|
||||||
|
// If long interval between updates: power off between updates
|
||||||
|
if (compensatedSleepTime > GPS_STANDBY_THRESHOLD_MINUTES * MS_IN_MINUTE) {
|
||||||
|
setGPSPower(wantAwake, false, getSleepTime() - predictedLockTime);
|
||||||
|
}
|
||||||
|
|
||||||
|
// If waking relatively frequently: don't power off. Would use more energy trying to reacquire lock each time
|
||||||
|
// We'll either use a "powered-on" standby, or just wait it out, depending on how soon the next update is due
|
||||||
|
// Will decide which inside setGPSPower method
|
||||||
|
else {
|
||||||
#ifdef GPS_UC6580
|
#ifdef GPS_UC6580
|
||||||
setGPSPower(on, false, getSleepTime() - averageLockTime);
|
setGPSPower(wantAwake, false, compensatedSleepTime);
|
||||||
#else
|
#else
|
||||||
setGPSPower(on, true, getSleepTime() - averageLockTime);
|
setGPSPower(wantAwake, true, compensatedSleepTime);
|
||||||
#endif
|
#endif
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
if (averageLockTime > 20000) {
|
|
||||||
averageLockTime -= 1000; // eventually want to sleep again.
|
|
||||||
}
|
|
||||||
if (on)
|
|
||||||
setGPSPower(true, true, 0); // make sure we don't have a fallthrough where GPS is stuck off
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1005,14 +1062,14 @@ int32_t GPS::runOnce()
|
|||||||
uint32_t timeAsleep = now - lastSleepStartMsec;
|
uint32_t timeAsleep = now - lastSleepStartMsec;
|
||||||
|
|
||||||
auto sleepTime = getSleepTime();
|
auto sleepTime = getSleepTime();
|
||||||
if (!isAwake && (sleepTime != UINT32_MAX) &&
|
if (powerState != GPS_ACTIVE && (sleepTime != UINT32_MAX) &&
|
||||||
((timeAsleep > sleepTime) || (isInPowersave && timeAsleep > (sleepTime - averageLockTime)))) {
|
((timeAsleep > sleepTime) || (isInPowersave && timeAsleep > (sleepTime - predictedLockTime)))) {
|
||||||
// We now want to be awake - so wake up the GPS
|
// We now want to be awake - so wake up the GPS
|
||||||
setAwake(true);
|
setAwake(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
// While we are awake
|
// While we are awake
|
||||||
if (isAwake) {
|
if (powerState == GPS_ACTIVE) {
|
||||||
// LOG_DEBUG("looking for location\n");
|
// LOG_DEBUG("looking for location\n");
|
||||||
// If we've already set time from the GPS, no need to ask the GPS
|
// If we've already set time from the GPS, no need to ask the GPS
|
||||||
bool gotTime = (getRTCQuality() >= RTCQualityGPS);
|
bool gotTime = (getRTCQuality() >= RTCQualityGPS);
|
||||||
@@ -1058,7 +1115,7 @@ int32_t GPS::runOnce()
|
|||||||
|
|
||||||
// 9600bps is approx 1 byte per msec, so considering our buffer size we never need to wake more often than 200ms
|
// 9600bps is approx 1 byte per msec, so considering our buffer size we never need to wake more often than 200ms
|
||||||
// if not awake we can run super infrquently (once every 5 secs?) to see if we need to wake.
|
// if not awake we can run super infrquently (once every 5 secs?) to see if we need to wake.
|
||||||
return isAwake ? GPS_THREAD_INTERVAL : 5000;
|
return (powerState == GPS_ACTIVE) ? GPS_THREAD_INTERVAL : 5000;
|
||||||
}
|
}
|
||||||
|
|
||||||
// clear the GPS rx buffer as quickly as possible
|
// clear the GPS rx buffer as quickly as possible
|
||||||
@@ -1589,9 +1646,9 @@ bool GPS::whileIdle()
|
|||||||
{
|
{
|
||||||
unsigned int charsInBuf = 0;
|
unsigned int charsInBuf = 0;
|
||||||
bool isValid = false;
|
bool isValid = false;
|
||||||
if (!isAwake) {
|
if (powerState != GPS_ACTIVE) {
|
||||||
clearBuffer();
|
clearBuffer();
|
||||||
return isAwake;
|
return (powerState == GPS_ACTIVE);
|
||||||
}
|
}
|
||||||
#ifdef SERIAL_BUFFER_SIZE
|
#ifdef SERIAL_BUFFER_SIZE
|
||||||
if (_serial_gps->available() >= SERIAL_BUFFER_SIZE - 1) {
|
if (_serial_gps->available() >= SERIAL_BUFFER_SIZE - 1) {
|
||||||
@@ -1622,6 +1679,10 @@ bool GPS::whileIdle()
|
|||||||
}
|
}
|
||||||
void GPS::enable()
|
void GPS::enable()
|
||||||
{
|
{
|
||||||
|
// Clear the old lock-time prediction
|
||||||
|
GPSCycles = 0;
|
||||||
|
predictedLockTime = 0;
|
||||||
|
|
||||||
enabled = true;
|
enabled = true;
|
||||||
setInterval(GPS_THREAD_INTERVAL);
|
setInterval(GPS_THREAD_INTERVAL);
|
||||||
setAwake(true);
|
setAwake(true);
|
||||||
|
|||||||
+10
-3
@@ -38,6 +38,13 @@ typedef enum {
|
|||||||
GNSS_RESPONSE_OK,
|
GNSS_RESPONSE_OK,
|
||||||
} GPS_RESPONSE;
|
} GPS_RESPONSE;
|
||||||
|
|
||||||
|
enum GPSPowerState : uint8_t {
|
||||||
|
GPS_OFF = 0, // Physically powered off
|
||||||
|
GPS_ACTIVE = 1, // Awake and want a position
|
||||||
|
GPS_STANDBY = 2, // Physically powered on, but soft-sleeping
|
||||||
|
GPS_IDLE = 3, // Awake, but not wanting another position yet
|
||||||
|
};
|
||||||
|
|
||||||
// Generate a string representation of DOP
|
// Generate a string representation of DOP
|
||||||
const char *getDOPString(uint32_t dop);
|
const char *getDOPString(uint32_t dop);
|
||||||
|
|
||||||
@@ -66,7 +73,7 @@ class GPS : private concurrency::OSThread
|
|||||||
uint32_t rx_gpio = 0;
|
uint32_t rx_gpio = 0;
|
||||||
uint32_t tx_gpio = 0;
|
uint32_t tx_gpio = 0;
|
||||||
uint32_t en_gpio = 0;
|
uint32_t en_gpio = 0;
|
||||||
int32_t averageLockTime = 0;
|
uint32_t predictedLockTime = 0;
|
||||||
uint32_t GPSCycles = 0;
|
uint32_t GPSCycles = 0;
|
||||||
|
|
||||||
int speedSelect = 0;
|
int speedSelect = 0;
|
||||||
@@ -78,8 +85,6 @@ class GPS : private concurrency::OSThread
|
|||||||
*/
|
*/
|
||||||
bool hasValidLocation = false; // default to false, until we complete our first read
|
bool hasValidLocation = false; // default to false, until we complete our first read
|
||||||
|
|
||||||
bool isAwake = false; // true if we want a location right now
|
|
||||||
|
|
||||||
bool isInPowersave = false;
|
bool isInPowersave = false;
|
||||||
|
|
||||||
bool shouldPublish = false; // If we've changed GPS state, this will force a publish the next loop()
|
bool shouldPublish = false; // If we've changed GPS state, this will force a publish the next loop()
|
||||||
@@ -89,6 +94,8 @@ class GPS : private concurrency::OSThread
|
|||||||
bool GPSInitFinished = false; // Init thread finished?
|
bool GPSInitFinished = false; // Init thread finished?
|
||||||
bool GPSInitStarted = false; // Init thread finished?
|
bool GPSInitStarted = false; // Init thread finished?
|
||||||
|
|
||||||
|
GPSPowerState powerState = GPS_OFF; // GPS_ACTIVE if we want a location right now
|
||||||
|
|
||||||
uint8_t numSatellites = 0;
|
uint8_t numSatellites = 0;
|
||||||
|
|
||||||
CallbackObserver<GPS, void *> notifyDeepSleepObserver = CallbackObserver<GPS, void *>(this, &GPS::prepareDeepSleep);
|
CallbackObserver<GPS, void *> notifyDeepSleepObserver = CallbackObserver<GPS, void *>(this, &GPS::prepareDeepSleep);
|
||||||
|
|||||||
@@ -486,3 +486,91 @@ std::shared_ptr<GeoCoord> GeoCoord::pointAtDistance(double bearing, double range
|
|||||||
|
|
||||||
return std::make_shared<GeoCoord>(double(lat), double(lon), this->getAltitude());
|
return std::make_shared<GeoCoord>(double(lat), double(lon), this->getAltitude());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convert bearing to degrees
|
||||||
|
* @param bearing
|
||||||
|
* The bearing in string format
|
||||||
|
* @return Bearing in degrees
|
||||||
|
*/
|
||||||
|
uint GeoCoord::bearingToDegrees(const char *bearing)
|
||||||
|
{
|
||||||
|
if (strcmp(bearing, "N") == 0)
|
||||||
|
return 0;
|
||||||
|
else if (strcmp(bearing, "NNE") == 0)
|
||||||
|
return 22;
|
||||||
|
else if (strcmp(bearing, "NE") == 0)
|
||||||
|
return 45;
|
||||||
|
else if (strcmp(bearing, "ENE") == 0)
|
||||||
|
return 67;
|
||||||
|
else if (strcmp(bearing, "E") == 0)
|
||||||
|
return 90;
|
||||||
|
else if (strcmp(bearing, "ESE") == 0)
|
||||||
|
return 112;
|
||||||
|
else if (strcmp(bearing, "SE") == 0)
|
||||||
|
return 135;
|
||||||
|
else if (strcmp(bearing, "SSE") == 0)
|
||||||
|
return 157;
|
||||||
|
else if (strcmp(bearing, "S") == 0)
|
||||||
|
return 180;
|
||||||
|
else if (strcmp(bearing, "SSW") == 0)
|
||||||
|
return 202;
|
||||||
|
else if (strcmp(bearing, "SW") == 0)
|
||||||
|
return 225;
|
||||||
|
else if (strcmp(bearing, "WSW") == 0)
|
||||||
|
return 247;
|
||||||
|
else if (strcmp(bearing, "W") == 0)
|
||||||
|
return 270;
|
||||||
|
else if (strcmp(bearing, "WNW") == 0)
|
||||||
|
return 292;
|
||||||
|
else if (strcmp(bearing, "NW") == 0)
|
||||||
|
return 315;
|
||||||
|
else if (strcmp(bearing, "NNW") == 0)
|
||||||
|
return 337;
|
||||||
|
else
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convert bearing to string
|
||||||
|
* @param degrees
|
||||||
|
* The bearing in degrees
|
||||||
|
* @return Bearing in string format
|
||||||
|
*/
|
||||||
|
const char *GeoCoord::degreesToBearing(uint degrees)
|
||||||
|
{
|
||||||
|
if (degrees >= 348 || degrees < 11)
|
||||||
|
return "N";
|
||||||
|
else if (degrees >= 11 && degrees < 34)
|
||||||
|
return "NNE";
|
||||||
|
else if (degrees >= 34 && degrees < 56)
|
||||||
|
return "NE";
|
||||||
|
else if (degrees >= 56 && degrees < 79)
|
||||||
|
return "ENE";
|
||||||
|
else if (degrees >= 79 && degrees < 101)
|
||||||
|
return "E";
|
||||||
|
else if (degrees >= 101 && degrees < 124)
|
||||||
|
return "ESE";
|
||||||
|
else if (degrees >= 124 && degrees < 146)
|
||||||
|
return "SE";
|
||||||
|
else if (degrees >= 146 && degrees < 169)
|
||||||
|
return "SSE";
|
||||||
|
else if (degrees >= 169 && degrees < 191)
|
||||||
|
return "S";
|
||||||
|
else if (degrees >= 191 && degrees < 214)
|
||||||
|
return "SSW";
|
||||||
|
else if (degrees >= 214 && degrees < 236)
|
||||||
|
return "SW";
|
||||||
|
else if (degrees >= 236 && degrees < 259)
|
||||||
|
return "WSW";
|
||||||
|
else if (degrees >= 259 && degrees < 281)
|
||||||
|
return "W";
|
||||||
|
else if (degrees >= 281 && degrees < 304)
|
||||||
|
return "WNW";
|
||||||
|
else if (degrees >= 304 && degrees < 326)
|
||||||
|
return "NW";
|
||||||
|
else if (degrees >= 326 && degrees < 348)
|
||||||
|
return "NNW";
|
||||||
|
else
|
||||||
|
return "N";
|
||||||
|
}
|
||||||
|
|||||||
@@ -117,6 +117,8 @@ class GeoCoord
|
|||||||
static float bearing(double lat1, double lon1, double lat2, double lon2);
|
static float bearing(double lat1, double lon1, double lat2, double lon2);
|
||||||
static float rangeRadiansToMeters(double range_radians);
|
static float rangeRadiansToMeters(double range_radians);
|
||||||
static float rangeMetersToRadians(double range_meters);
|
static float rangeMetersToRadians(double range_meters);
|
||||||
|
static uint bearingToDegrees(const char *bearing);
|
||||||
|
static const char *degreesToBearing(uint degrees);
|
||||||
|
|
||||||
// Point to point conversions
|
// Point to point conversions
|
||||||
int32_t distanceTo(const GeoCoord &pointB);
|
int32_t distanceTo(const GeoCoord &pointB);
|
||||||
|
|||||||
+7
-4
@@ -96,13 +96,17 @@ void readFromRTC()
|
|||||||
*
|
*
|
||||||
* If we haven't yet set our RTC this boot, set it from a GPS derived time
|
* If we haven't yet set our RTC this boot, set it from a GPS derived time
|
||||||
*/
|
*/
|
||||||
bool perhapsSetRTC(RTCQuality q, const struct timeval *tv)
|
bool perhapsSetRTC(RTCQuality q, const struct timeval *tv, bool forceUpdate)
|
||||||
{
|
{
|
||||||
static uint32_t lastSetMsec = 0;
|
static uint32_t lastSetMsec = 0;
|
||||||
uint32_t now = millis();
|
uint32_t now = millis();
|
||||||
|
|
||||||
bool shouldSet;
|
bool shouldSet;
|
||||||
if (q > currentQuality) {
|
if (forceUpdate) {
|
||||||
|
shouldSet = true;
|
||||||
|
LOG_DEBUG("Overriding current RTC quality (%s) with incoming time of RTC quality of %s\n", RtcName(currentQuality),
|
||||||
|
RtcName(q));
|
||||||
|
} else if (q > currentQuality) {
|
||||||
shouldSet = true;
|
shouldSet = true;
|
||||||
LOG_DEBUG("Upgrading time to quality %s\n", RtcName(q));
|
LOG_DEBUG("Upgrading time to quality %s\n", RtcName(q));
|
||||||
} else if (q >= RTCQualityNTP && (now - lastSetMsec) > (12 * 60 * 60 * 1000UL)) {
|
} else if (q >= RTCQualityNTP && (now - lastSetMsec) > (12 * 60 * 60 * 1000UL)) {
|
||||||
@@ -218,9 +222,8 @@ bool perhapsSetRTC(RTCQuality q, struct tm &t)
|
|||||||
*/
|
*/
|
||||||
int32_t getTZOffset()
|
int32_t getTZOffset()
|
||||||
{
|
{
|
||||||
time_t now;
|
time_t now = getTime(false);
|
||||||
struct tm *gmt;
|
struct tm *gmt;
|
||||||
now = time(NULL);
|
|
||||||
gmt = gmtime(&now);
|
gmt = gmtime(&now);
|
||||||
gmt->tm_isdst = -1;
|
gmt->tm_isdst = -1;
|
||||||
return (int32_t)difftime(now, mktime(gmt));
|
return (int32_t)difftime(now, mktime(gmt));
|
||||||
|
|||||||
+1
-1
@@ -25,7 +25,7 @@ enum RTCQuality {
|
|||||||
RTCQuality getRTCQuality();
|
RTCQuality getRTCQuality();
|
||||||
|
|
||||||
/// If we haven't yet set our RTC this boot, set it from a GPS derived time
|
/// If we haven't yet set our RTC this boot, set it from a GPS derived time
|
||||||
bool perhapsSetRTC(RTCQuality q, const struct timeval *tv);
|
bool perhapsSetRTC(RTCQuality q, const struct timeval *tv, bool forceUpdate = false);
|
||||||
bool perhapsSetRTC(RTCQuality q, struct tm &t);
|
bool perhapsSetRTC(RTCQuality q, struct tm &t);
|
||||||
|
|
||||||
/// Return a string name for the quality
|
/// Return a string name for the quality
|
||||||
|
|||||||
+32
-28
@@ -319,6 +319,8 @@ const uint8_t GPS::_message_SAVE[] = {
|
|||||||
// As the M10 has no flash, the best we can do to preserve the config is to set it in RAM and BBR.
|
// As the M10 has no flash, the best we can do to preserve the config is to set it in RAM and BBR.
|
||||||
// BBR will survive a restart, and power off for a while, but modules with small backup
|
// BBR will survive a restart, and power off for a while, but modules with small backup
|
||||||
// batteries or super caps will not retain the config for a long power off time.
|
// batteries or super caps will not retain the config for a long power off time.
|
||||||
|
// for all configurations using sleep / low power modes, V_BCKP needs to be hooked to permanent power for fast aquisition after
|
||||||
|
// sleep
|
||||||
|
|
||||||
// VALSET Commands for M10
|
// VALSET Commands for M10
|
||||||
// Please refer to the M10 Protocol Specification:
|
// Please refer to the M10 Protocol Specification:
|
||||||
@@ -327,40 +329,42 @@ const uint8_t GPS::_message_SAVE[] = {
|
|||||||
// and:
|
// and:
|
||||||
// https://content.u-blox.com/sites/default/files/u-blox-M10-ROM-5.10_ReleaseNotes_UBX-22001426.pdf
|
// https://content.u-blox.com/sites/default/files/u-blox-M10-ROM-5.10_ReleaseNotes_UBX-22001426.pdf
|
||||||
// for interesting insights.
|
// for interesting insights.
|
||||||
|
//
|
||||||
|
// Integration manual:
|
||||||
|
// https://content.u-blox.com/sites/default/files/documents/SAM-M10Q_IntegrationManual_UBX-22020019.pdf
|
||||||
|
// has details on low-power modes
|
||||||
|
|
||||||
/*
|
/*
|
||||||
CFG-PM2 has been replaced by many CFG-PM commands
|
CFG-PM2 has been replaced by many CFG-PM commands
|
||||||
OPERATEMODE E1 2 (0 | 1 | 2)
|
CFG-PMS has been removed
|
||||||
POSUPDATEPERIOD U4 1000ms for M10 must be >= 5s try 5
|
|
||||||
ACQPERIOD U4 10 seems ok for M10 def ok
|
CFG-PM-OPERATEMODE E1 (0 | 1 | 2) -> 1 (PSMOO), because sporadic position updates are required instead of continous tracking <10s
|
||||||
GRIDOFFSET U4 0 seems ok for M10 def ok
|
(PSMCT) CFG-PM-POSUPDATEPERIOD U4 -> 0ms, no self-timed wakup because receiver power mode is controlled via "software standby
|
||||||
ONTIME U2 1 will try 1
|
mode" by legacy UBX-RXM-PMREQ request CFG-PM-ACQPERIOD U4 -> 0ms, because receiver power mode is controlled via "software standby
|
||||||
MINACQTIME U1 0 will try 0 def ok
|
mode" by legacy UBX-RXM-PMREQ request CFG-PM-ONTIME U4 -> 0ms, optional I guess CFG-PM-EXTINTBACKUP L -> 1, force receiver into
|
||||||
MAXACQTIME U1 stick with default of 0 def ok
|
BACKUP mode when EXTINT (should be connected to GPS_EN_PIN) pin is "low"
|
||||||
DONOTENTEROFF L 1 stay at 1
|
|
||||||
WAITTIMEFIX L 1 stay with 1
|
This is required because the receiver never enters low power mode if microcontroller is in deep-sleep.
|
||||||
UPDATEEPH L 1 changed to 1 for gps rework default is 1
|
Maybe the changing UART_RX levels trigger a wakeup but even with UBX-RXM-PMREQ[12] = 0x00 (all external wakeup sources disabled)
|
||||||
EXTINTWAKE L 0 no ext ints
|
the receivcer remains in aquisition state -> potentially a bug
|
||||||
EXTINTBACKUP L 0 no ext ints
|
|
||||||
EXTINTINACTIVE L 0 no ext ints
|
Workaround: Control the EXTINT pin by the GPS_EN_PIN signal
|
||||||
EXTINTACTIVITY U4 0 no ext ints
|
|
||||||
LIMITPEAKCURRENT L 1 stay with 1
|
As mentioned in the M10 operational issues down below, power save won't allow the use of BDS B1C.
|
||||||
*/
|
CFG-SIGNAL-BDS_B1C_ENA L -> 0
|
||||||
// CFG-PMS has been removed
|
|
||||||
|
|
||||||
// Ram layer config message:
|
// Ram layer config message:
|
||||||
// b5 62 06 8a 26 00 00 01 00 00 01 00 d0 20 02 02 00 d0 40 05 00 00 00 05 00 d0 30 01 00 08 00 d0 10 01 09 00 d0 10 01 10 00 d0
|
// 01 01 00 00 01 00 D0 20 01 02 00 D0 40 00 00 00 00 03 00 D0 40 00 00 00 00 05 00 D0 30 00 00 0D 00 D0 10 01
|
||||||
// 10 01 8b de
|
|
||||||
|
|
||||||
// BBR layer config message:
|
// BBR layer config message:
|
||||||
// b5 62 06 8a 26 00 00 02 00 00 01 00 d0 20 02 02 00 d0 40 05 00 00 00 05 00 d0 30 01 00 08 00 d0 10 01 09 00 d0 10 01 10 00 d0
|
// 01 02 00 00 01 00 D0 20 01 02 00 D0 40 00 00 00 00 03 00 D0 40 00 00 00 00 05 00 D0 30 00 00 0D 00 D0 10 01
|
||||||
// 10 01 8c 03
|
*/
|
||||||
|
const uint8_t GPS::_message_VALSET_PM_RAM[] = {0x01, 0x01, 0x00, 0x00, 0x0F, 0x00, 0x31, 0x10, 0x00, 0x01, 0x00, 0xD0, 0x20, 0x01,
|
||||||
const uint8_t GPS::_message_VALSET_PM_RAM[] = {0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0xd0, 0x20, 0x02, 0x02, 0x00, 0xd0, 0x40,
|
0x02, 0x00, 0xD0, 0x40, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0xD0, 0x40, 0x00, 0x00,
|
||||||
0x05, 0x00, 0x00, 0x00, 0x05, 0x00, 0xd0, 0x30, 0x01, 0x00, 0x08, 0x00, 0xd0,
|
0x00, 0x00, 0x05, 0x00, 0xD0, 0x30, 0x00, 0x00, 0x0D, 0x00, 0xD0, 0x10, 0x01};
|
||||||
0x10, 0x01, 0x09, 0x00, 0xd0, 0x10, 0x01, 0x10, 0x00, 0xd0, 0x10, 0x01};
|
const uint8_t GPS::_message_VALSET_PM_BBR[] = {0x01, 0x02, 0x00, 0x00, 0x0F, 0x00, 0x31, 0x10, 0x00, 0x01, 0x00, 0xD0, 0x20, 0x01,
|
||||||
const uint8_t GPS::_message_VALSET_PM_BBR[] = {0x00, 0x02, 0x00, 0x00, 0x01, 0x00, 0xd0, 0x20, 0x02, 0x02, 0x00, 0xd0, 0x40,
|
0x02, 0x00, 0xD0, 0x40, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0xD0, 0x40, 0x00, 0x00,
|
||||||
0x05, 0x00, 0x00, 0x00, 0x05, 0x00, 0xd0, 0x30, 0x01, 0x00, 0x08, 0x00, 0xd0,
|
0x00, 0x00, 0x05, 0x00, 0xD0, 0x30, 0x00, 0x00, 0x0D, 0x00, 0xD0, 0x10, 0x01};
|
||||||
0x10, 0x01, 0x09, 0x00, 0xd0, 0x10, 0x01, 0x10, 0x00, 0xd0, 0x10, 0x01};
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
CFG-ITFM replaced by 5 valset messages which can be combined into one for RAM and one for BBR
|
CFG-ITFM replaced by 5 valset messages which can be combined into one for RAM and one for BBR
|
||||||
|
|||||||
+50
-22
@@ -277,6 +277,30 @@ static void drawFunctionOverlay(OLEDDisplay *display, OLEDDisplayUiState *state)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Check if the display can render a string (detect special chars; emoji)
|
||||||
|
static bool haveGlyphs(const char *str)
|
||||||
|
{
|
||||||
|
#if defined(OLED_UA) || defined(OLED_RU)
|
||||||
|
// Don't want to make any assumptions about custom language support
|
||||||
|
return true;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Check each character with the lookup function for the OLED library
|
||||||
|
// We're not really meant to use this directly..
|
||||||
|
bool have = true;
|
||||||
|
for (uint16_t i = 0; i < strlen(str); i++) {
|
||||||
|
uint8_t result = Screen::customFontTableLookup((uint8_t)str[i]);
|
||||||
|
// If font doesn't support a character, it is substituted for ¿
|
||||||
|
if (result == 191 && (uint8_t)str[i] != 191) {
|
||||||
|
have = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
LOG_DEBUG("haveGlyphs=%d\n", have);
|
||||||
|
return have;
|
||||||
|
}
|
||||||
|
|
||||||
#ifdef USE_EINK
|
#ifdef USE_EINK
|
||||||
/// Used on eink displays while in deep sleep
|
/// Used on eink displays while in deep sleep
|
||||||
static void drawDeepSleepScreen(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y)
|
static void drawDeepSleepScreen(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y)
|
||||||
@@ -301,14 +325,15 @@ static void drawScreensaverOverlay(OLEDDisplay *display, OLEDDisplayUiState *sta
|
|||||||
display->setTextAlignment(TEXT_ALIGN_LEFT);
|
display->setTextAlignment(TEXT_ALIGN_LEFT);
|
||||||
const char *pauseText = "Screen Paused";
|
const char *pauseText = "Screen Paused";
|
||||||
const char *idText = owner.short_name;
|
const char *idText = owner.short_name;
|
||||||
|
const bool useId = haveGlyphs(idText); // This bool is used to hide the idText box if we can't render the short name
|
||||||
constexpr uint16_t padding = 5;
|
constexpr uint16_t padding = 5;
|
||||||
constexpr uint8_t dividerGap = 1;
|
constexpr uint8_t dividerGap = 1;
|
||||||
constexpr uint8_t imprecision = 5; // How far the box origins can drift from center. Combat burn-in.
|
constexpr uint8_t imprecision = 5; // How far the box origins can drift from center. Combat burn-in.
|
||||||
|
|
||||||
// Dimensions
|
// Dimensions
|
||||||
const uint16_t idTextWidth = display->getStringWidth(idText, strlen(idText));
|
const uint16_t idTextWidth = display->getStringWidth(idText, strlen(idText), true); // "true": handle utf8 chars
|
||||||
const uint16_t pauseTextWidth = display->getStringWidth(pauseText, strlen(pauseText));
|
const uint16_t pauseTextWidth = display->getStringWidth(pauseText, strlen(pauseText));
|
||||||
const uint16_t boxWidth = padding + idTextWidth + padding + padding + pauseTextWidth + padding;
|
const uint16_t boxWidth = padding + (useId ? idTextWidth + padding + padding : 0) + pauseTextWidth + padding;
|
||||||
const uint16_t boxHeight = padding + FONT_HEIGHT_SMALL + padding;
|
const uint16_t boxHeight = padding + FONT_HEIGHT_SMALL + padding;
|
||||||
|
|
||||||
// Position
|
// Position
|
||||||
@@ -318,7 +343,7 @@ static void drawScreensaverOverlay(OLEDDisplay *display, OLEDDisplayUiState *sta
|
|||||||
const int16_t boxBottom = boxTop + boxHeight - 1;
|
const int16_t boxBottom = boxTop + boxHeight - 1;
|
||||||
const int16_t idTextLeft = boxLeft + padding;
|
const int16_t idTextLeft = boxLeft + padding;
|
||||||
const int16_t idTextTop = boxTop + padding;
|
const int16_t idTextTop = boxTop + padding;
|
||||||
const int16_t pauseTextLeft = boxLeft + padding + idTextWidth + padding + padding;
|
const int16_t pauseTextLeft = boxLeft + (useId ? padding + idTextWidth + padding : 0) + padding;
|
||||||
const int16_t pauseTextTop = boxTop + padding;
|
const int16_t pauseTextTop = boxTop + padding;
|
||||||
const int16_t dividerX = boxLeft + padding + idTextWidth + padding;
|
const int16_t dividerX = boxLeft + padding + idTextWidth + padding;
|
||||||
const int16_t dividerTop = boxTop + 1 + dividerGap;
|
const int16_t dividerTop = boxTop + 1 + dividerGap;
|
||||||
@@ -331,12 +356,14 @@ static void drawScreensaverOverlay(OLEDDisplay *display, OLEDDisplayUiState *sta
|
|||||||
display->drawRect(boxLeft, boxTop, boxWidth, boxHeight);
|
display->drawRect(boxLeft, boxTop, boxWidth, boxHeight);
|
||||||
|
|
||||||
// Draw: Text
|
// Draw: Text
|
||||||
display->drawString(idTextLeft, idTextTop, idText);
|
if (useId)
|
||||||
|
display->drawString(idTextLeft, idTextTop, idText);
|
||||||
display->drawString(pauseTextLeft, pauseTextTop, pauseText);
|
display->drawString(pauseTextLeft, pauseTextTop, pauseText);
|
||||||
display->drawString(pauseTextLeft + 1, pauseTextTop, pauseText); // Faux bold
|
display->drawString(pauseTextLeft + 1, pauseTextTop, pauseText); // Faux bold
|
||||||
|
|
||||||
// Draw: divider
|
// Draw: divider
|
||||||
display->drawLine(dividerX, dividerTop, dividerX, dividerBottom);
|
if (useId)
|
||||||
|
display->drawLine(dividerX, dividerTop, dividerX, dividerBottom);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@@ -1460,18 +1487,13 @@ static void drawNodeInfo(OLEDDisplay *display, OLEDDisplayUiState *state, int16_
|
|||||||
|
|
||||||
static char signalStr[20];
|
static char signalStr[20];
|
||||||
|
|
||||||
//section here to choose whether to display hops away rather than signal strength if more than 0 hops away.
|
// section here to choose whether to display hops away rather than signal strength if more than 0 hops away.
|
||||||
if(node->hops_away>0)
|
if (node->hops_away > 0) {
|
||||||
{
|
|
||||||
snprintf(signalStr, sizeof(signalStr), "Hops Away: %d", node->hops_away);
|
snprintf(signalStr, sizeof(signalStr), "Hops Away: %d", node->hops_away);
|
||||||
}
|
} else {
|
||||||
else
|
|
||||||
{
|
|
||||||
snprintf(signalStr, sizeof(signalStr), "Signal: %d%%", clamp((int)((node->snr + 10) * 5), 0, 100));
|
snprintf(signalStr, sizeof(signalStr), "Signal: %d%%", clamp((int)((node->snr + 10) * 5), 0, 100));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
uint32_t agoSecs = sinceLastSeen(node);
|
uint32_t agoSecs = sinceLastSeen(node);
|
||||||
static char lastStr[20];
|
static char lastStr[20];
|
||||||
|
|
||||||
@@ -1521,9 +1543,13 @@ static void drawNodeInfo(OLEDDisplay *display, OLEDDisplayUiState *state, int16_
|
|||||||
}
|
}
|
||||||
bool hasNodeHeading = false;
|
bool hasNodeHeading = false;
|
||||||
|
|
||||||
if (ourNode && hasValidPosition(ourNode)) {
|
if (ourNode && (hasValidPosition(ourNode) || screen->hasHeading())) {
|
||||||
const meshtastic_PositionLite &op = ourNode->position;
|
const meshtastic_PositionLite &op = ourNode->position;
|
||||||
float myHeading = estimatedHeading(DegD(op.latitude_i), DegD(op.longitude_i));
|
float myHeading;
|
||||||
|
if (screen->hasHeading())
|
||||||
|
myHeading = (screen->getHeading()) * PI / 180; // gotta convert compass degrees to Radians
|
||||||
|
else
|
||||||
|
myHeading = estimatedHeading(DegD(op.latitude_i), DegD(op.longitude_i));
|
||||||
drawCompassNorth(display, compassX, compassY, myHeading);
|
drawCompassNorth(display, compassX, compassY, myHeading);
|
||||||
|
|
||||||
if (hasValidPosition(node)) {
|
if (hasValidPosition(node)) {
|
||||||
@@ -2076,7 +2102,7 @@ void Screen::setFrames()
|
|||||||
LOG_DEBUG("Total frame count: %d\n", totalFrameCount);
|
LOG_DEBUG("Total frame count: %d\n", totalFrameCount);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// We don't show the node info our our node (if we have it yet - we should)
|
// We don't show the node info of our node (if we have it yet - we should)
|
||||||
size_t numMeshNodes = nodeDB->getNumMeshNodes();
|
size_t numMeshNodes = nodeDB->getNumMeshNodes();
|
||||||
if (numMeshNodes > 0)
|
if (numMeshNodes > 0)
|
||||||
numMeshNodes--;
|
numMeshNodes--;
|
||||||
@@ -2099,6 +2125,10 @@ void Screen::setFrames()
|
|||||||
if (error_code)
|
if (error_code)
|
||||||
normalFrames[numframes++] = drawCriticalFaultFrame;
|
normalFrames[numframes++] = drawCriticalFaultFrame;
|
||||||
|
|
||||||
|
#ifdef T_WATCH_S3
|
||||||
|
normalFrames[numframes++] = screen->digitalWatchFace ? &Screen::drawDigitalClockFrame : &Screen::drawAnalogClockFrame;
|
||||||
|
#endif
|
||||||
|
|
||||||
// If we have a text message - show it next, unless it's a phone message and we aren't using any special modules
|
// If we have a text message - show it next, unless it's a phone message and we aren't using any special modules
|
||||||
if (devicestate.has_rx_text_message && shouldDrawMessage(&devicestate.rx_text_message)) {
|
if (devicestate.has_rx_text_message && shouldDrawMessage(&devicestate.rx_text_message)) {
|
||||||
normalFrames[numframes++] = drawTextMessageFrame;
|
normalFrames[numframes++] = drawTextMessageFrame;
|
||||||
@@ -2108,10 +2138,6 @@ void Screen::setFrames()
|
|||||||
normalFrames[numframes++] = drawWaypointFrame;
|
normalFrames[numframes++] = drawWaypointFrame;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef T_WATCH_S3
|
|
||||||
normalFrames[numframes++] = screen->digitalWatchFace ? &Screen::drawDigitalClockFrame : &Screen::drawAnalogClockFrame;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// then all the nodes
|
// then all the nodes
|
||||||
// We only show a few nodes in our scrolling list - because meshes with many nodes would have too many screens
|
// We only show a few nodes in our scrolling list - because meshes with many nodes would have too many screens
|
||||||
size_t numToShow = min(numMeshNodes, 4U);
|
size_t numToShow = min(numMeshNodes, 4U);
|
||||||
@@ -2686,8 +2712,10 @@ int Screen::handleInputEvent(const InputEvent *event)
|
|||||||
|
|
||||||
#ifdef T_WATCH_S3
|
#ifdef T_WATCH_S3
|
||||||
// For the T-Watch, intercept touches to the 'toggle digital/analog watch face' button
|
// For the T-Watch, intercept touches to the 'toggle digital/analog watch face' button
|
||||||
if (this->ui->getUiState()->currentFrame == 0 && event->touchX >= 204 && event->touchX <= 240 && event->touchY >= 204 &&
|
uint8_t watchFaceFrame = error_code ? 1 : 0;
|
||||||
event->touchY <= 240) {
|
|
||||||
|
if (this->ui->getUiState()->currentFrame == watchFaceFrame && event->touchX >= 204 && event->touchX <= 240 &&
|
||||||
|
event->touchY >= 204 && event->touchY <= 240) {
|
||||||
screen->digitalWatchFace = !screen->digitalWatchFace;
|
screen->digitalWatchFace = !screen->digitalWatchFace;
|
||||||
|
|
||||||
setFrames();
|
setFrames();
|
||||||
|
|||||||
+14
-1
@@ -204,6 +204,17 @@ class Screen : public concurrency::OSThread
|
|||||||
enqueueCmd(cmd);
|
enqueueCmd(cmd);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Function to allow the AccelerometerThread to set the heading if a sensor provides it
|
||||||
|
// Mutex needed?
|
||||||
|
void setHeading(long _heading)
|
||||||
|
{
|
||||||
|
hasCompass = true;
|
||||||
|
compassHeading = _heading;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool hasHeading() { return hasCompass; }
|
||||||
|
|
||||||
|
long getHeading() { return compassHeading; }
|
||||||
// functions for display brightness
|
// functions for display brightness
|
||||||
void increaseBrightness();
|
void increaseBrightness();
|
||||||
void decreaseBrightness();
|
void decreaseBrightness();
|
||||||
@@ -360,7 +371,7 @@ class Screen : public concurrency::OSThread
|
|||||||
bool enqueueCmd(const ScreenCmd &cmd)
|
bool enqueueCmd(const ScreenCmd &cmd)
|
||||||
{
|
{
|
||||||
if (!useDisplay)
|
if (!useDisplay)
|
||||||
return true; // claim success if our display is not in use
|
return false; // not enqueued if our display is not in use
|
||||||
else {
|
else {
|
||||||
bool success = cmdQueue.enqueue(cmd, 0);
|
bool success = cmdQueue.enqueue(cmd, 0);
|
||||||
enabled = true; // handle ASAP (we are the registered reader for cmdQueue, but might have been disabled)
|
enabled = true; // handle ASAP (we are the registered reader for cmdQueue, but might have been disabled)
|
||||||
@@ -428,6 +439,8 @@ class Screen : public concurrency::OSThread
|
|||||||
// Implementation to Adjust Brightness
|
// Implementation to Adjust Brightness
|
||||||
uint8_t brightness = BRIGHTNESS_DEFAULT; // H = 254, MH = 192, ML = 130 L = 103
|
uint8_t brightness = BRIGHTNESS_DEFAULT; // H = 254, MH = 192, ML = 130 L = 103
|
||||||
|
|
||||||
|
bool hasCompass = false;
|
||||||
|
float compassHeading;
|
||||||
/// Holds state for debug information
|
/// Holds state for debug information
|
||||||
DebugInfo debugInfo;
|
DebugInfo debugInfo;
|
||||||
|
|
||||||
|
|||||||
@@ -117,8 +117,16 @@ class LGFX : public lgfx::LGFX_Device
|
|||||||
static LGFX *tft = nullptr;
|
static LGFX *tft = nullptr;
|
||||||
|
|
||||||
#elif defined(RAK14014)
|
#elif defined(RAK14014)
|
||||||
|
#include <RAK14014_FT6336U.h>
|
||||||
#include <TFT_eSPI.h>
|
#include <TFT_eSPI.h>
|
||||||
TFT_eSPI *tft = nullptr;
|
TFT_eSPI *tft = nullptr;
|
||||||
|
FT6336U ft6336u;
|
||||||
|
|
||||||
|
static uint8_t _rak14014_touch_int = false; // TP interrupt generation flag.
|
||||||
|
static void rak14014_tpIntHandle(void)
|
||||||
|
{
|
||||||
|
_rak14014_touch_int = true;
|
||||||
|
}
|
||||||
|
|
||||||
#elif defined(ST7789_CS)
|
#elif defined(ST7789_CS)
|
||||||
#include <LovyanGFX.hpp> // Graphics and font library for ST7735 driver chip
|
#include <LovyanGFX.hpp> // Graphics and font library for ST7735 driver chip
|
||||||
@@ -642,8 +650,12 @@ void TFTDisplay::sendCommand(uint8_t com)
|
|||||||
|
|
||||||
void TFTDisplay::setDisplayBrightness(uint8_t _brightness)
|
void TFTDisplay::setDisplayBrightness(uint8_t _brightness)
|
||||||
{
|
{
|
||||||
|
#ifdef RAK14014
|
||||||
|
// todo
|
||||||
|
#else
|
||||||
tft->setBrightness(_brightness);
|
tft->setBrightness(_brightness);
|
||||||
LOG_DEBUG("Brightness is set to value: %i \n", _brightness);
|
LOG_DEBUG("Brightness is set to value: %i \n", _brightness);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void TFTDisplay::flipScreenVertically()
|
void TFTDisplay::flipScreenVertically()
|
||||||
@@ -657,6 +669,7 @@ void TFTDisplay::flipScreenVertically()
|
|||||||
bool TFTDisplay::hasTouch(void)
|
bool TFTDisplay::hasTouch(void)
|
||||||
{
|
{
|
||||||
#ifdef RAK14014
|
#ifdef RAK14014
|
||||||
|
return true;
|
||||||
#elif !defined(M5STACK)
|
#elif !defined(M5STACK)
|
||||||
return tft->touch() != nullptr;
|
return tft->touch() != nullptr;
|
||||||
#else
|
#else
|
||||||
@@ -667,6 +680,15 @@ bool TFTDisplay::hasTouch(void)
|
|||||||
bool TFTDisplay::getTouch(int16_t *x, int16_t *y)
|
bool TFTDisplay::getTouch(int16_t *x, int16_t *y)
|
||||||
{
|
{
|
||||||
#ifdef RAK14014
|
#ifdef RAK14014
|
||||||
|
if (_rak14014_touch_int) {
|
||||||
|
_rak14014_touch_int = false;
|
||||||
|
/* The X and Y axes have to be switched */
|
||||||
|
*y = ft6336u.read_touch1_x();
|
||||||
|
*x = TFT_HEIGHT - ft6336u.read_touch1_y();
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
#elif !defined(M5STACK)
|
#elif !defined(M5STACK)
|
||||||
return tft->getTouch(x, y);
|
return tft->getTouch(x, y);
|
||||||
#else
|
#else
|
||||||
@@ -716,7 +738,10 @@ bool TFTDisplay::connect()
|
|||||||
#elif defined(RAK14014)
|
#elif defined(RAK14014)
|
||||||
tft->setRotation(1);
|
tft->setRotation(1);
|
||||||
tft->setSwapBytes(true);
|
tft->setSwapBytes(true);
|
||||||
// tft->fillScreen(TFT_BLACK);
|
// tft->fillScreen(TFT_BLACK);
|
||||||
|
ft6336u.begin();
|
||||||
|
pinMode(SCREEN_TOUCH_INT, INPUT_PULLUP);
|
||||||
|
attachInterrupt(digitalPinToInterrupt(SCREEN_TOUCH_INT), rak14014_tpIntHandle, FALLING);
|
||||||
#elif defined(T_DECK) || defined(PICOMPUTER_S3) || defined(CHATTER_2)
|
#elif defined(T_DECK) || defined(PICOMPUTER_S3) || defined(CHATTER_2)
|
||||||
tft->setRotation(1); // T-Deck has the TFT in landscape
|
tft->setRotation(1); // T-Deck has the TFT in landscape
|
||||||
#elif defined(T_WATCH_S3)
|
#elif defined(T_WATCH_S3)
|
||||||
|
|||||||
@@ -65,6 +65,8 @@ NRF52Bluetooth *nrf52Bluetooth;
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include "LLCC68Interface.h"
|
#include "LLCC68Interface.h"
|
||||||
|
#include "LR1110Interface.h"
|
||||||
|
#include "LR1120Interface.h"
|
||||||
#include "RF95Interface.h"
|
#include "RF95Interface.h"
|
||||||
#include "SX1262Interface.h"
|
#include "SX1262Interface.h"
|
||||||
#include "SX1268Interface.h"
|
#include "SX1268Interface.h"
|
||||||
@@ -533,7 +535,12 @@ void setup()
|
|||||||
SCANNER_TO_SENSORS_MAP(ScanI2C::DeviceType::PMSA0031, meshtastic_TelemetrySensorType_PMSA003I)
|
SCANNER_TO_SENSORS_MAP(ScanI2C::DeviceType::PMSA0031, meshtastic_TelemetrySensorType_PMSA003I)
|
||||||
SCANNER_TO_SENSORS_MAP(ScanI2C::DeviceType::RCWL9620, meshtastic_TelemetrySensorType_RCWL9620)
|
SCANNER_TO_SENSORS_MAP(ScanI2C::DeviceType::RCWL9620, meshtastic_TelemetrySensorType_RCWL9620)
|
||||||
SCANNER_TO_SENSORS_MAP(ScanI2C::DeviceType::VEML7700, meshtastic_TelemetrySensorType_VEML7700)
|
SCANNER_TO_SENSORS_MAP(ScanI2C::DeviceType::VEML7700, meshtastic_TelemetrySensorType_VEML7700)
|
||||||
|
SCANNER_TO_SENSORS_MAP(ScanI2C::DeviceType::TSL2591, meshtastic_TelemetrySensorType_TSL25911FN)
|
||||||
|
SCANNER_TO_SENSORS_MAP(ScanI2C::DeviceType::OPT3001, meshtastic_TelemetrySensorType_OPT3001)
|
||||||
|
SCANNER_TO_SENSORS_MAP(ScanI2C::DeviceType::MLX90632, meshtastic_TelemetrySensorType_MLX90632)
|
||||||
SCANNER_TO_SENSORS_MAP(ScanI2C::DeviceType::SHT4X, meshtastic_TelemetrySensorType_SHT4X)
|
SCANNER_TO_SENSORS_MAP(ScanI2C::DeviceType::SHT4X, meshtastic_TelemetrySensorType_SHT4X)
|
||||||
|
SCANNER_TO_SENSORS_MAP(ScanI2C::DeviceType::AHT10, meshtastic_TelemetrySensorType_AHT10)
|
||||||
|
SCANNER_TO_SENSORS_MAP(ScanI2C::DeviceType::DFROBOT_LARK, meshtastic_TelemetrySensorType_DFROBOT_LARK)
|
||||||
|
|
||||||
i2cScanner.reset();
|
i2cScanner.reset();
|
||||||
|
|
||||||
@@ -878,6 +885,32 @@ void setup()
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#if defined(USE_LR1110)
|
||||||
|
if (!rIf) {
|
||||||
|
rIf = new LR1110Interface(RadioLibHAL, LR1110_SPI_NSS_PIN, LR1110_IRQ_PIN, LR1110_NRESER_PIN, LR1110_BUSY_PIN);
|
||||||
|
if (!rIf->init()) {
|
||||||
|
LOG_WARN("Failed to find LR1110 radio\n");
|
||||||
|
delete rIf;
|
||||||
|
rIf = NULL;
|
||||||
|
} else {
|
||||||
|
LOG_INFO("LR1110 Radio init succeeded, using LR1110 radio\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(USE_LR1120)
|
||||||
|
if (!rIf) {
|
||||||
|
rIf = new LR1120Interface(RadioLibHAL, LR1120_SPI_NSS_PIN, LR1120_IRQ_PIN, LR1120_NRESER_PIN, LR1120_BUSY_PIN);
|
||||||
|
if (!rIf->init()) {
|
||||||
|
LOG_WARN("Failed to find LR1120 radio\n");
|
||||||
|
delete rIf;
|
||||||
|
rIf = NULL;
|
||||||
|
} else {
|
||||||
|
LOG_INFO("LR1120 Radio init succeeded, using LR1120 radio\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
#if defined(USE_SX1280)
|
#if defined(USE_SX1280)
|
||||||
if (!rIf) {
|
if (!rIf) {
|
||||||
rIf = new SX1280Interface(RadioLibHAL, SX128X_CS, SX128X_DIO1, SX128X_RESET, SX128X_BUSY);
|
rIf = new SX1280Interface(RadioLibHAL, SX128X_CS, SX128X_DIO1, SX128X_RESET, SX128X_BUSY);
|
||||||
|
|||||||
+3
-3
@@ -53,6 +53,9 @@ extern Adafruit_DRV2605 drv;
|
|||||||
extern AudioThread *audioThread;
|
extern AudioThread *audioThread;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
// Global Screen singleton.
|
||||||
|
extern graphics::Screen *screen;
|
||||||
|
|
||||||
#if !defined(ARCH_PORTDUINO) && !defined(ARCH_STM32WL) && !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR
|
#if !defined(ARCH_PORTDUINO) && !defined(ARCH_STM32WL) && !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR
|
||||||
#include "AccelerometerThread.h"
|
#include "AccelerometerThread.h"
|
||||||
extern AccelerometerThread *accelerometerThread;
|
extern AccelerometerThread *accelerometerThread;
|
||||||
@@ -62,9 +65,6 @@ extern bool isVibrating;
|
|||||||
|
|
||||||
extern int TCPPort; // set by Portduino
|
extern int TCPPort; // set by Portduino
|
||||||
|
|
||||||
// Global Screen singleton.
|
|
||||||
extern graphics::Screen *screen;
|
|
||||||
|
|
||||||
// extern Observable<meshtastic::PowerStatus> newPowerStatus; //TODO: move this to main-esp32.cpp somehow or a helper class
|
// extern Observable<meshtastic::PowerStatus> newPowerStatus; //TODO: move this to main-esp32.cpp somehow or a helper class
|
||||||
|
|
||||||
// extern meshtastic::PowerStatus *powerStatus;
|
// extern meshtastic::PowerStatus *powerStatus;
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
#include "LR11x0Interface.cpp"
|
||||||
|
#include "LR11x0Interface.h"
|
||||||
#include "SX126xInterface.cpp"
|
#include "SX126xInterface.cpp"
|
||||||
#include "SX126xInterface.h"
|
#include "SX126xInterface.h"
|
||||||
#include "SX128xInterface.cpp"
|
#include "SX128xInterface.cpp"
|
||||||
@@ -10,6 +12,8 @@ template class SX126xInterface<SX1262>;
|
|||||||
template class SX126xInterface<SX1268>;
|
template class SX126xInterface<SX1268>;
|
||||||
template class SX126xInterface<LLCC68>;
|
template class SX126xInterface<LLCC68>;
|
||||||
template class SX128xInterface<SX1280>;
|
template class SX128xInterface<SX1280>;
|
||||||
|
template class LR11x0Interface<LR1110>;
|
||||||
|
template class LR11x0Interface<LR1120>;
|
||||||
#ifdef ARCH_STM32WL
|
#ifdef ARCH_STM32WL
|
||||||
template class SX126xInterface<STM32WLx>;
|
template class SX126xInterface<STM32WLx>;
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -0,0 +1,9 @@
|
|||||||
|
#include "LR1110Interface.h"
|
||||||
|
#include "configuration.h"
|
||||||
|
#include "error.h"
|
||||||
|
|
||||||
|
LR1110Interface::LR1110Interface(LockingArduinoHal *hal, RADIOLIB_PIN_TYPE cs, RADIOLIB_PIN_TYPE irq, RADIOLIB_PIN_TYPE rst,
|
||||||
|
RADIOLIB_PIN_TYPE busy)
|
||||||
|
: LR11x0Interface(hal, cs, irq, rst, busy)
|
||||||
|
{
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "LR11x0Interface.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Our adapter for LR1110 radios
|
||||||
|
*/
|
||||||
|
class LR1110Interface : public LR11x0Interface<LR1110>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
LR1110Interface(LockingArduinoHal *hal, RADIOLIB_PIN_TYPE cs, RADIOLIB_PIN_TYPE irq, RADIOLIB_PIN_TYPE rst,
|
||||||
|
RADIOLIB_PIN_TYPE busy);
|
||||||
|
};
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
#include "LR1120Interface.h"
|
||||||
|
#include "configuration.h"
|
||||||
|
#include "error.h"
|
||||||
|
|
||||||
|
LR1120Interface::LR1120Interface(LockingArduinoHal *hal, RADIOLIB_PIN_TYPE cs, RADIOLIB_PIN_TYPE irq, RADIOLIB_PIN_TYPE rst,
|
||||||
|
RADIOLIB_PIN_TYPE busy)
|
||||||
|
: LR11x0Interface(hal, cs, irq, rst, busy)
|
||||||
|
{
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "LR11x0Interface.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Our adapter for LR1120 wideband radios
|
||||||
|
*/
|
||||||
|
class LR1120Interface : public LR11x0Interface<LR1120>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
LR1120Interface(LockingArduinoHal *hal, RADIOLIB_PIN_TYPE cs, RADIOLIB_PIN_TYPE irq, RADIOLIB_PIN_TYPE rst,
|
||||||
|
RADIOLIB_PIN_TYPE busy);
|
||||||
|
};
|
||||||
@@ -0,0 +1,299 @@
|
|||||||
|
#include "LR11x0Interface.h"
|
||||||
|
#include "configuration.h"
|
||||||
|
#include "error.h"
|
||||||
|
#include "mesh/NodeDB.h"
|
||||||
|
#ifdef ARCH_PORTDUINO
|
||||||
|
#include "PortduinoGlue.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Particular boards might define a different max power based on what their hardware can do, default to max power output if not
|
||||||
|
// specified (may be dangerous if using external PA and LR11x0 power config forgotten)
|
||||||
|
#ifndef LR11X0_MAX_POWER
|
||||||
|
#define LR11X0_MAX_POWER 22
|
||||||
|
#endif
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
LR11x0Interface<T>::LR11x0Interface(LockingArduinoHal *hal, RADIOLIB_PIN_TYPE cs, RADIOLIB_PIN_TYPE irq, RADIOLIB_PIN_TYPE rst,
|
||||||
|
RADIOLIB_PIN_TYPE busy)
|
||||||
|
: RadioLibInterface(hal, cs, irq, rst, busy, &lora), lora(&module)
|
||||||
|
{
|
||||||
|
LOG_WARN("LR11x0Interface(cs=%d, irq=%d, rst=%d, busy=%d)\n", cs, irq, rst, busy);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Initialise the Driver transport hardware and software.
|
||||||
|
/// Make sure the Driver is properly configured before calling init().
|
||||||
|
/// \return true if initialisation succeeded.
|
||||||
|
template <typename T> bool LR11x0Interface<T>::init()
|
||||||
|
{
|
||||||
|
#ifdef LR11X0_POWER_EN
|
||||||
|
pinMode(LR11X0_POWER_EN, OUTPUT);
|
||||||
|
digitalWrite(LR11X0_POWER_EN, HIGH);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// FIXME: correct logic to default to not using TCXO if no voltage is specified for LR11x0_DIO3_TCXO_VOLTAGE
|
||||||
|
#if !defined(LR11X0_DIO3_TCXO_VOLTAGE)
|
||||||
|
float tcxoVoltage =
|
||||||
|
0; // "TCXO reference voltage to be set on DIO3. Defaults to 1.6 V, set to 0 to skip." per
|
||||||
|
// https://github.com/jgromes/RadioLib/blob/690a050ebb46e6097c5d00c371e961c1caa3b52e/src/modules/LR11x0/LR11x0.h#L471C26-L471C104
|
||||||
|
// (DIO3 is free to be used as an IRQ)
|
||||||
|
LOG_DEBUG("LR11X0_DIO3_TCXO_VOLTAGE not defined, not using DIO3 as TCXO reference voltage\n");
|
||||||
|
#else
|
||||||
|
float tcxoVoltage = LR11X0_DIO3_TCXO_VOLTAGE;
|
||||||
|
LOG_DEBUG("LR11X0_DIO3_TCXO_VOLTAGE defined, using DIO3 as TCXO reference voltage at %f V\n", LR11X0_DIO3_TCXO_VOLTAGE);
|
||||||
|
// (DIO3 is not free to be used as an IRQ)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
RadioLibInterface::init();
|
||||||
|
|
||||||
|
if (power > LR11X0_MAX_POWER) // Clamp power to maximum defined level
|
||||||
|
power = LR11X0_MAX_POWER;
|
||||||
|
|
||||||
|
limitPower();
|
||||||
|
|
||||||
|
// set RF switch configuration for Wio WM1110
|
||||||
|
// Wio WM1110 uses DIO5 and DIO6 for RF switching
|
||||||
|
// NOTE: other boards may be different. If you are
|
||||||
|
// using a different board, you may need to wrap
|
||||||
|
// this in a conditional.
|
||||||
|
|
||||||
|
static const uint32_t rfswitch_dio_pins[] = {RADIOLIB_LR11X0_DIO5, RADIOLIB_LR11X0_DIO6, RADIOLIB_NC, RADIOLIB_NC,
|
||||||
|
RADIOLIB_NC};
|
||||||
|
|
||||||
|
static const Module::RfSwitchMode_t rfswitch_table[] = {
|
||||||
|
// mode DIO5 DIO6
|
||||||
|
{LR11x0::MODE_STBY, {LOW, LOW}}, {LR11x0::MODE_RX, {HIGH, LOW}},
|
||||||
|
{LR11x0::MODE_TX, {HIGH, HIGH}}, {LR11x0::MODE_TX_HP, {LOW, HIGH}},
|
||||||
|
{LR11x0::MODE_TX_HF, {LOW, LOW}}, {LR11x0::MODE_GNSS, {LOW, LOW}},
|
||||||
|
{LR11x0::MODE_WIFI, {LOW, LOW}}, END_OF_MODE_TABLE,
|
||||||
|
};
|
||||||
|
|
||||||
|
// We need to do this before begin() call
|
||||||
|
#ifdef LR11X0_DIO_AS_RF_SWITCH
|
||||||
|
LOG_DEBUG("Setting DIO RF switch\n");
|
||||||
|
bool dioAsRfSwitch = true;
|
||||||
|
#elif defined(ARCH_PORTDUINO)
|
||||||
|
bool dioAsRfSwitch = false;
|
||||||
|
if (settingsMap[dio2_as_rf_switch]) {
|
||||||
|
LOG_DEBUG("Setting DIO RF switch\n");
|
||||||
|
dioAsRfSwitch = true;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
bool dioAsRfSwitch = false;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (dioAsRfSwitch)
|
||||||
|
lora.setRfSwitchTable(rfswitch_dio_pins, rfswitch_table);
|
||||||
|
|
||||||
|
int res = lora.begin(getFreq(), bw, sf, cr, syncWord, power, preambleLength, tcxoVoltage);
|
||||||
|
// \todo Display actual typename of the adapter, not just `LR11x0`
|
||||||
|
LOG_INFO("LR11x0 init result %d\n", res);
|
||||||
|
if (res == RADIOLIB_ERR_CHIP_NOT_FOUND)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
LOG_INFO("Frequency set to %f\n", getFreq());
|
||||||
|
LOG_INFO("Bandwidth set to %f\n", bw);
|
||||||
|
LOG_INFO("Power output set to %d\n", power);
|
||||||
|
|
||||||
|
if (res == RADIOLIB_ERR_NONE)
|
||||||
|
res = lora.setCRC(2);
|
||||||
|
|
||||||
|
// FIXME: May want to set depending on a definition, currently all LR1110 variant files use the DC-DC regulator option
|
||||||
|
if (res == RADIOLIB_ERR_NONE)
|
||||||
|
res = lora.setRegulatorDCDC();
|
||||||
|
|
||||||
|
if (res == RADIOLIB_ERR_NONE) {
|
||||||
|
if (config.lora.sx126x_rx_boosted_gain) { // the name is unfortunate but historically accurate
|
||||||
|
res = lora.setRxBoostedGainMode(true);
|
||||||
|
LOG_INFO("Set RX gain to boosted mode; result: %d\n", res);
|
||||||
|
} else {
|
||||||
|
res = lora.setRxBoostedGainMode(false);
|
||||||
|
LOG_INFO("Set RX gain to power saving mode (boosted mode off); result: %d\n", res);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (res == RADIOLIB_ERR_NONE)
|
||||||
|
startReceive(); // start receiving
|
||||||
|
|
||||||
|
return res == RADIOLIB_ERR_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T> bool LR11x0Interface<T>::reconfigure()
|
||||||
|
{
|
||||||
|
RadioLibInterface::reconfigure();
|
||||||
|
|
||||||
|
// set mode to standby
|
||||||
|
setStandby();
|
||||||
|
|
||||||
|
// configure publicly accessible settings
|
||||||
|
int err = lora.setSpreadingFactor(sf);
|
||||||
|
if (err != RADIOLIB_ERR_NONE)
|
||||||
|
RECORD_CRITICALERROR(meshtastic_CriticalErrorCode_INVALID_RADIO_SETTING);
|
||||||
|
|
||||||
|
err = lora.setBandwidth(bw);
|
||||||
|
if (err != RADIOLIB_ERR_NONE)
|
||||||
|
RECORD_CRITICALERROR(meshtastic_CriticalErrorCode_INVALID_RADIO_SETTING);
|
||||||
|
|
||||||
|
err = lora.setCodingRate(cr);
|
||||||
|
if (err != RADIOLIB_ERR_NONE)
|
||||||
|
RECORD_CRITICALERROR(meshtastic_CriticalErrorCode_INVALID_RADIO_SETTING);
|
||||||
|
|
||||||
|
// Hmm - seems to lower SNR when the signal levels are high. Leaving off for now...
|
||||||
|
// TODO: Confirm gain registers are okay now
|
||||||
|
// err = lora.setRxGain(true);
|
||||||
|
// assert(err == RADIOLIB_ERR_NONE);
|
||||||
|
|
||||||
|
err = lora.setSyncWord(syncWord);
|
||||||
|
assert(err == RADIOLIB_ERR_NONE);
|
||||||
|
|
||||||
|
err = lora.setPreambleLength(preambleLength);
|
||||||
|
assert(err == RADIOLIB_ERR_NONE);
|
||||||
|
|
||||||
|
err = lora.setFrequency(getFreq());
|
||||||
|
if (err != RADIOLIB_ERR_NONE)
|
||||||
|
RECORD_CRITICALERROR(meshtastic_CriticalErrorCode_INVALID_RADIO_SETTING);
|
||||||
|
|
||||||
|
if (power > LR11X0_MAX_POWER) // This chip has lower power limits than some
|
||||||
|
power = LR11X0_MAX_POWER;
|
||||||
|
|
||||||
|
err = lora.setOutputPower(power);
|
||||||
|
assert(err == RADIOLIB_ERR_NONE);
|
||||||
|
|
||||||
|
startReceive(); // restart receiving
|
||||||
|
|
||||||
|
return RADIOLIB_ERR_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T> void INTERRUPT_ATTR LR11x0Interface<T>::disableInterrupt()
|
||||||
|
{
|
||||||
|
lora.clearIrqAction();
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T> void LR11x0Interface<T>::setStandby()
|
||||||
|
{
|
||||||
|
checkNotification(); // handle any pending interrupts before we force standby
|
||||||
|
|
||||||
|
int err = lora.standby();
|
||||||
|
|
||||||
|
if (err != RADIOLIB_ERR_NONE) {
|
||||||
|
LOG_DEBUG("LR11x0 standby failed with error %d\n", err);
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(err == RADIOLIB_ERR_NONE);
|
||||||
|
|
||||||
|
isReceiving = false; // If we were receiving, not any more
|
||||||
|
activeReceiveStart = 0;
|
||||||
|
disableInterrupt();
|
||||||
|
completeSending(); // If we were sending, not anymore
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add SNR data to received messages
|
||||||
|
*/
|
||||||
|
template <typename T> void LR11x0Interface<T>::addReceiveMetadata(meshtastic_MeshPacket *mp)
|
||||||
|
{
|
||||||
|
// LOG_DEBUG("PacketStatus %x\n", lora.getPacketStatus());
|
||||||
|
mp->rx_snr = lora.getSNR();
|
||||||
|
mp->rx_rssi = lround(lora.getRSSI());
|
||||||
|
}
|
||||||
|
|
||||||
|
/** We override to turn on transmitter power as needed.
|
||||||
|
*/
|
||||||
|
template <typename T> void LR11x0Interface<T>::configHardwareForSend()
|
||||||
|
{
|
||||||
|
RadioLibInterface::configHardwareForSend();
|
||||||
|
}
|
||||||
|
|
||||||
|
// For power draw measurements, helpful to force radio to stay sleeping
|
||||||
|
// #define SLEEP_ONLY
|
||||||
|
|
||||||
|
template <typename T> void LR11x0Interface<T>::startReceive()
|
||||||
|
{
|
||||||
|
#ifdef SLEEP_ONLY
|
||||||
|
sleep();
|
||||||
|
#else
|
||||||
|
|
||||||
|
setStandby();
|
||||||
|
|
||||||
|
lora.setPreambleLength(preambleLength); // Solve RX ack fail after direct message sent. Not sure why this is needed.
|
||||||
|
|
||||||
|
// We use a 16 bit preamble so this should save some power by letting radio sit in standby mostly.
|
||||||
|
// Furthermore, we need the PREAMBLE_DETECTED and HEADER_VALID IRQ flag to detect whether we are actively receiving
|
||||||
|
int err = lora.startReceive(
|
||||||
|
RADIOLIB_LR11X0_RX_TIMEOUT_INF, RADIOLIB_LR11X0_IRQ_RX_DONE,
|
||||||
|
0); // only RX_DONE IRQ is needed, we'll check for PREAMBLE_DETECTED and HEADER_VALID in isActivelyReceiving
|
||||||
|
assert(err == RADIOLIB_ERR_NONE);
|
||||||
|
|
||||||
|
isReceiving = true;
|
||||||
|
|
||||||
|
// Must be done AFTER, starting transmit, because startTransmit clears (possibly stale) interrupt pending register bits
|
||||||
|
enableInterrupt(isrRxLevel0);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Is the channel currently active? */
|
||||||
|
template <typename T> bool LR11x0Interface<T>::isChannelActive()
|
||||||
|
{
|
||||||
|
// check if we can detect a LoRa preamble on the current channel
|
||||||
|
int16_t result;
|
||||||
|
|
||||||
|
setStandby();
|
||||||
|
result = lora.scanChannel();
|
||||||
|
if (result == RADIOLIB_LORA_DETECTED)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
assert(result != RADIOLIB_ERR_WRONG_MODEM);
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Could we send right now (i.e. either not actively receiving or transmitting)? */
|
||||||
|
template <typename T> bool LR11x0Interface<T>::isActivelyReceiving()
|
||||||
|
{
|
||||||
|
// The IRQ status will be cleared when we start our read operation. Check if we've started a header, but haven't yet
|
||||||
|
// received and handled the interrupt for reading the packet/handling errors.
|
||||||
|
|
||||||
|
uint16_t irq = lora.getIrqStatus();
|
||||||
|
bool detected = (irq & (RADIOLIB_LR11X0_IRQ_SYNC_WORD_HEADER_VALID | RADIOLIB_LR11X0_IRQ_PREAMBLE_DETECTED));
|
||||||
|
// Handle false detections
|
||||||
|
if (detected) {
|
||||||
|
uint32_t now = millis();
|
||||||
|
if (!activeReceiveStart) {
|
||||||
|
activeReceiveStart = now;
|
||||||
|
} else if ((now - activeReceiveStart > 2 * preambleTimeMsec) && !(irq & RADIOLIB_LR11X0_IRQ_SYNC_WORD_HEADER_VALID)) {
|
||||||
|
// The HEADER_VALID flag should be set by now if it was really a packet, so ignore PREAMBLE_DETECTED flag
|
||||||
|
activeReceiveStart = 0;
|
||||||
|
LOG_DEBUG("Ignore false preamble detection.\n");
|
||||||
|
return false;
|
||||||
|
} else if (now - activeReceiveStart > maxPacketTimeMsec) {
|
||||||
|
// We should have gotten an RX_DONE IRQ by now if it was really a packet, so ignore HEADER_VALID flag
|
||||||
|
activeReceiveStart = 0;
|
||||||
|
LOG_DEBUG("Ignore false header detection.\n");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// if (detected) LOG_DEBUG("rx detected\n");
|
||||||
|
return detected;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T> bool LR11x0Interface<T>::sleep()
|
||||||
|
{
|
||||||
|
// Not keeping config is busted - next time nrf52 board boots lora sending fails tcxo related? - see datasheet
|
||||||
|
// \todo Display actual typename of the adapter, not just `LR11x0`
|
||||||
|
LOG_DEBUG("LR11x0 entering sleep mode (FIXME, don't keep config)\n");
|
||||||
|
setStandby(); // Stop any pending operations
|
||||||
|
|
||||||
|
// turn off TCXO if it was powered
|
||||||
|
// FIXME - this isn't correct
|
||||||
|
// lora.setTCXO(0);
|
||||||
|
|
||||||
|
// put chipset into sleep mode (we've already disabled interrupts by now)
|
||||||
|
bool keepConfig = true;
|
||||||
|
lora.sleep(keepConfig, 0); // Note: we do not keep the config, full reinit will be needed
|
||||||
|
|
||||||
|
#ifdef LR11X0_POWER_EN
|
||||||
|
digitalWrite(LR11X0_POWER_EN, LOW);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
@@ -0,0 +1,71 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "RadioLibInterface.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Adapter for LR11x0 radio family. Implements common logic for child classes.
|
||||||
|
* \tparam T RadioLib module type for LR11x0: SX1262, SX1268.
|
||||||
|
*/
|
||||||
|
template <class T> class LR11x0Interface : public RadioLibInterface
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
LR11x0Interface(LockingArduinoHal *hal, RADIOLIB_PIN_TYPE cs, RADIOLIB_PIN_TYPE irq, RADIOLIB_PIN_TYPE rst,
|
||||||
|
RADIOLIB_PIN_TYPE busy);
|
||||||
|
|
||||||
|
/// Initialise the Driver transport hardware and software.
|
||||||
|
/// Make sure the Driver is properly configured before calling init().
|
||||||
|
/// \return true if initialisation succeeded.
|
||||||
|
virtual bool init() override;
|
||||||
|
|
||||||
|
/// Apply any radio provisioning changes
|
||||||
|
/// Make sure the Driver is properly configured before calling init().
|
||||||
|
/// \return true if initialisation succeeded.
|
||||||
|
virtual bool reconfigure() override;
|
||||||
|
|
||||||
|
/// Prepare hardware for sleep. Call this _only_ for deep sleep, not needed for light sleep.
|
||||||
|
virtual bool sleep() override;
|
||||||
|
|
||||||
|
bool isIRQPending() override { return lora.getIrqStatus() != 0; }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
/**
|
||||||
|
* Specific module instance
|
||||||
|
*/
|
||||||
|
T lora;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Glue functions called from ISR land
|
||||||
|
*/
|
||||||
|
virtual void disableInterrupt() override;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enable a particular ISR callback glue function
|
||||||
|
*/
|
||||||
|
virtual void enableInterrupt(void (*callback)()) { lora.setIrqAction(callback); }
|
||||||
|
|
||||||
|
/** can we detect a LoRa preamble on the current channel? */
|
||||||
|
virtual bool isChannelActive() override;
|
||||||
|
|
||||||
|
/** are we actively receiving a packet (only called during receiving state) */
|
||||||
|
virtual bool isActivelyReceiving() override;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Start waiting to receive a message
|
||||||
|
*/
|
||||||
|
virtual void startReceive() override;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* We override to turn on transmitter power as needed.
|
||||||
|
*/
|
||||||
|
virtual void configHardwareForSend() override;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add SNR data to received messages
|
||||||
|
*/
|
||||||
|
virtual void addReceiveMetadata(meshtastic_MeshPacket *mp) override;
|
||||||
|
|
||||||
|
virtual void setStandby() override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
uint32_t activeReceiveStart = 0;
|
||||||
|
};
|
||||||
+10
-6
@@ -62,7 +62,10 @@ meshtastic_MeshPacket *MeshModule::allocAckNak(meshtastic_Routing_Error err, Nod
|
|||||||
|
|
||||||
meshtastic_MeshPacket *MeshModule::allocErrorResponse(meshtastic_Routing_Error err, const meshtastic_MeshPacket *p)
|
meshtastic_MeshPacket *MeshModule::allocErrorResponse(meshtastic_Routing_Error err, const meshtastic_MeshPacket *p)
|
||||||
{
|
{
|
||||||
auto r = allocAckNak(err, getFrom(p), p->id, p->channel);
|
// If the original packet couldn't be decoded, use the primary channel
|
||||||
|
uint8_t channelIndex =
|
||||||
|
p->which_payload_variant == meshtastic_MeshPacket_decoded_tag ? p->channel : channels.getPrimaryIndex();
|
||||||
|
auto r = allocAckNak(err, getFrom(p), p->id, channelIndex);
|
||||||
|
|
||||||
setReplyTo(r, *p);
|
setReplyTo(r, *p);
|
||||||
|
|
||||||
@@ -114,13 +117,13 @@ void MeshModule::callModules(meshtastic_MeshPacket &mp, RxSource src)
|
|||||||
/// Also: if a packet comes in on the local PC interface, we don't check for bound channels, because it is TRUSTED and
|
/// Also: if a packet comes in on the local PC interface, we don't check for bound channels, because it is TRUSTED and
|
||||||
/// it needs to to be able to fetch the initial admin packets without yet knowing any channels.
|
/// it needs to to be able to fetch the initial admin packets without yet knowing any channels.
|
||||||
|
|
||||||
bool rxChannelOk = !pi.boundChannel || (mp.from == 0) || (strcasecmp(ch->settings.name, pi.boundChannel) == 0);
|
bool rxChannelOk = !pi.boundChannel || (mp.from == 0) || (ch && strcasecmp(ch->settings.name, pi.boundChannel) == 0);
|
||||||
|
|
||||||
if (!rxChannelOk) {
|
if (!rxChannelOk) {
|
||||||
// no one should have already replied!
|
// no one should have already replied!
|
||||||
assert(!currentReply);
|
assert(!currentReply);
|
||||||
|
|
||||||
if (mp.decoded.want_response) {
|
if (isDecoded && mp.decoded.want_response) {
|
||||||
printPacket("packet on wrong channel, returning error", &mp);
|
printPacket("packet on wrong channel, returning error", &mp);
|
||||||
currentReply = pi.allocErrorResponse(meshtastic_Routing_Error_NOT_AUTHORIZED, &mp);
|
currentReply = pi.allocErrorResponse(meshtastic_Routing_Error_NOT_AUTHORIZED, &mp);
|
||||||
} else
|
} else
|
||||||
@@ -138,7 +141,8 @@ void MeshModule::callModules(meshtastic_MeshPacket &mp, RxSource src)
|
|||||||
// because currently when the phone sends things, it sends things using the local node ID as the from address. A
|
// because currently when the phone sends things, it sends things using the local node ID as the from address. A
|
||||||
// better solution (FIXME) would be to let phones have their own distinct addresses and we 'route' to them like
|
// better solution (FIXME) would be to let phones have their own distinct addresses and we 'route' to them like
|
||||||
// any other node.
|
// any other node.
|
||||||
if (mp.decoded.want_response && toUs && (getFrom(&mp) != ourNodeNum || mp.to == ourNodeNum) && !currentReply) {
|
if (isDecoded && mp.decoded.want_response && toUs && (getFrom(&mp) != ourNodeNum || mp.to == ourNodeNum) &&
|
||||||
|
!currentReply) {
|
||||||
pi.sendResponse(mp);
|
pi.sendResponse(mp);
|
||||||
ignoreRequest = ignoreRequest || pi.ignoreRequest; // If at least one module asks it, we may ignore a request
|
ignoreRequest = ignoreRequest || pi.ignoreRequest; // If at least one module asks it, we may ignore a request
|
||||||
LOG_INFO("Asked module '%s' to send a response\n", pi.name);
|
LOG_INFO("Asked module '%s' to send a response\n", pi.name);
|
||||||
@@ -163,7 +167,7 @@ void MeshModule::callModules(meshtastic_MeshPacket &mp, RxSource src)
|
|||||||
pi.currentRequest = NULL;
|
pi.currentRequest = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mp.decoded.want_response && toUs) {
|
if (isDecoded && mp.decoded.want_response && toUs) {
|
||||||
if (currentReply) {
|
if (currentReply) {
|
||||||
printPacket("Sending response", currentReply);
|
printPacket("Sending response", currentReply);
|
||||||
service.sendToMesh(currentReply);
|
service.sendToMesh(currentReply);
|
||||||
@@ -183,7 +187,7 @@ void MeshModule::callModules(meshtastic_MeshPacket &mp, RxSource src)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!moduleFound) {
|
if (!moduleFound && isDecoded) {
|
||||||
LOG_DEBUG("No modules interested in portnum=%d, src=%s\n", mp.decoded.portnum,
|
LOG_DEBUG("No modules interested in portnum=%d, src=%s\n", mp.decoded.portnum,
|
||||||
(src == RX_SRC_LOCAL) ? "LOCAL" : "REMOTE");
|
(src == RX_SRC_LOCAL) ? "LOCAL" : "REMOTE");
|
||||||
}
|
}
|
||||||
|
|||||||
+4
-1
@@ -292,6 +292,9 @@ void NodeDB::installDefaultConfig()
|
|||||||
meshtastic_Config_PositionConfig_PositionFlags_SPEED | meshtastic_Config_PositionConfig_PositionFlags_HEADING |
|
meshtastic_Config_PositionConfig_PositionFlags_SPEED | meshtastic_Config_PositionConfig_PositionFlags_HEADING |
|
||||||
meshtastic_Config_PositionConfig_PositionFlags_DOP | meshtastic_Config_PositionConfig_PositionFlags_SATINVIEW);
|
meshtastic_Config_PositionConfig_PositionFlags_DOP | meshtastic_Config_PositionConfig_PositionFlags_SATINVIEW);
|
||||||
|
|
||||||
|
#ifdef RADIOMASTER_900_BANDIT_NANO
|
||||||
|
config.display.flip_screen = true;
|
||||||
|
#endif
|
||||||
#ifdef T_WATCH_S3
|
#ifdef T_WATCH_S3
|
||||||
config.display.screen_on_secs = 30;
|
config.display.screen_on_secs = 30;
|
||||||
config.display.wake_on_tap_or_motion = true;
|
config.display.wake_on_tap_or_motion = true;
|
||||||
@@ -441,9 +444,9 @@ void NodeDB::installDefaultChannels()
|
|||||||
|
|
||||||
void NodeDB::resetNodes()
|
void NodeDB::resetNodes()
|
||||||
{
|
{
|
||||||
|
clearLocalPosition();
|
||||||
numMeshNodes = 1;
|
numMeshNodes = 1;
|
||||||
std::fill(devicestate.node_db_lite.begin() + 1, devicestate.node_db_lite.end(), meshtastic_NodeInfoLite());
|
std::fill(devicestate.node_db_lite.begin() + 1, devicestate.node_db_lite.end(), meshtastic_NodeInfoLite());
|
||||||
clearLocalPosition();
|
|
||||||
saveDeviceStateToDisk();
|
saveDeviceStateToDisk();
|
||||||
if (neighborInfoModule && moduleConfig.neighbor_info.enabled)
|
if (neighborInfoModule && moduleConfig.neighbor_info.enabled)
|
||||||
neighborInfoModule->resetNeighbors();
|
neighborInfoModule->resetNeighbors();
|
||||||
|
|||||||
+49
-32
@@ -140,16 +140,18 @@ bool PhoneAPI::handleToRadio(const uint8_t *buf, size_t bufLength)
|
|||||||
*
|
*
|
||||||
* We assume buf is at least FromRadio_size bytes long.
|
* We assume buf is at least FromRadio_size bytes long.
|
||||||
*
|
*
|
||||||
* Our sending states progress in the following sequence (the client app ASSUMES THIS SEQUENCE, DO NOT CHANGE IT):
|
* Our sending states progress in the following sequence (the client apps ASSUME THIS SEQUENCE, DO NOT CHANGE IT):
|
||||||
* STATE_SEND_MY_INFO, // send our my info record
|
STATE_SEND_MY_INFO, // send our my info record
|
||||||
* STATE_SEND_CHANNELS
|
STATE_SEND_OWN_NODEINFO,
|
||||||
* STATE_SEND_NODEINFO, // states progress in this order as the device sends to the client
|
STATE_SEND_METADATA,
|
||||||
STATE_SEND_CONFIG,
|
STATE_SEND_CHANNELS
|
||||||
STATE_SEND_MODULE_CONFIG,
|
STATE_SEND_CONFIG,
|
||||||
STATE_SEND_METADATA,
|
STATE_SEND_MODULE_CONFIG,
|
||||||
STATE_SEND_COMPLETE_ID,
|
STATE_SEND_OTHER_NODEINFOS, // states progress in this order as the device sends to the client
|
||||||
STATE_SEND_PACKETS // send packets or debug strings
|
STATE_SEND_COMPLETE_ID,
|
||||||
|
STATE_SEND_PACKETS // send packets or debug strings
|
||||||
*/
|
*/
|
||||||
|
|
||||||
size_t PhoneAPI::getFromRadio(uint8_t *buf)
|
size_t PhoneAPI::getFromRadio(uint8_t *buf)
|
||||||
{
|
{
|
||||||
if (!available()) {
|
if (!available()) {
|
||||||
@@ -171,37 +173,32 @@ size_t PhoneAPI::getFromRadio(uint8_t *buf)
|
|||||||
// app not to send locations on our behalf.
|
// app not to send locations on our behalf.
|
||||||
fromRadioScratch.which_payload_variant = meshtastic_FromRadio_my_info_tag;
|
fromRadioScratch.which_payload_variant = meshtastic_FromRadio_my_info_tag;
|
||||||
fromRadioScratch.my_info = myNodeInfo;
|
fromRadioScratch.my_info = myNodeInfo;
|
||||||
state = STATE_SEND_METADATA;
|
state = STATE_SEND_OWN_NODEINFO;
|
||||||
|
|
||||||
service.refreshLocalMeshNode(); // Update my NodeInfo because the client will be asking for it soon.
|
service.refreshLocalMeshNode(); // Update my NodeInfo because the client will be asking for it soon.
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case STATE_SEND_OWN_NODEINFO: {
|
||||||
|
LOG_INFO("getFromRadio=STATE_SEND_OWN_NODEINFO\n");
|
||||||
|
auto us = nodeDB->readNextMeshNode(readIndex);
|
||||||
|
if (us) {
|
||||||
|
nodeInfoForPhone = TypeConversions::ConvertToNodeInfo(us);
|
||||||
|
fromRadioScratch.which_payload_variant = meshtastic_FromRadio_node_info_tag;
|
||||||
|
fromRadioScratch.node_info = nodeInfoForPhone;
|
||||||
|
// Should allow us to resume sending NodeInfo in STATE_SEND_OTHER_NODEINFOS
|
||||||
|
nodeInfoForPhone.num = 0;
|
||||||
|
}
|
||||||
|
state = STATE_SEND_METADATA;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
case STATE_SEND_METADATA:
|
case STATE_SEND_METADATA:
|
||||||
LOG_INFO("getFromRadio=STATE_SEND_METADATA\n");
|
LOG_INFO("getFromRadio=STATE_SEND_METADATA\n");
|
||||||
fromRadioScratch.which_payload_variant = meshtastic_FromRadio_metadata_tag;
|
fromRadioScratch.which_payload_variant = meshtastic_FromRadio_metadata_tag;
|
||||||
fromRadioScratch.metadata = getDeviceMetadata();
|
fromRadioScratch.metadata = getDeviceMetadata();
|
||||||
state = STATE_SEND_NODEINFO;
|
state = STATE_SEND_CHANNELS;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case STATE_SEND_NODEINFO: {
|
|
||||||
LOG_INFO("getFromRadio=STATE_SEND_NODEINFO\n");
|
|
||||||
|
|
||||||
if (nodeInfoForPhone.num != 0) {
|
|
||||||
LOG_INFO("nodeinfo: num=0x%x, lastseen=%u, id=%s, name=%s\n", nodeInfoForPhone.num, nodeInfoForPhone.last_heard,
|
|
||||||
nodeInfoForPhone.user.id, nodeInfoForPhone.user.long_name);
|
|
||||||
fromRadioScratch.which_payload_variant = meshtastic_FromRadio_node_info_tag;
|
|
||||||
fromRadioScratch.node_info = nodeInfoForPhone;
|
|
||||||
// Stay in current state until done sending nodeinfos
|
|
||||||
nodeInfoForPhone.num = 0; // We just consumed a nodeinfo, will need a new one next time
|
|
||||||
} else {
|
|
||||||
LOG_INFO("Done sending nodeinfos\n");
|
|
||||||
state = STATE_SEND_CHANNELS;
|
|
||||||
// Go ahead and send that ID right now
|
|
||||||
return getFromRadio(buf);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case STATE_SEND_CHANNELS:
|
case STATE_SEND_CHANNELS:
|
||||||
LOG_INFO("getFromRadio=STATE_SEND_CHANNELS\n");
|
LOG_INFO("getFromRadio=STATE_SEND_CHANNELS\n");
|
||||||
fromRadioScratch.which_payload_variant = meshtastic_FromRadio_channel_tag;
|
fromRadioScratch.which_payload_variant = meshtastic_FromRadio_channel_tag;
|
||||||
@@ -325,11 +322,30 @@ size_t PhoneAPI::getFromRadio(uint8_t *buf)
|
|||||||
config_state++;
|
config_state++;
|
||||||
// Advance when we have sent all of our ModuleConfig objects
|
// Advance when we have sent all of our ModuleConfig objects
|
||||||
if (config_state > (_meshtastic_AdminMessage_ModuleConfigType_MAX + 1)) {
|
if (config_state > (_meshtastic_AdminMessage_ModuleConfigType_MAX + 1)) {
|
||||||
state = STATE_SEND_COMPLETE_ID;
|
// Clients sending special nonce don't want to see other nodeinfos
|
||||||
|
state = config_nonce == SPECIAL_NONCE ? STATE_SEND_COMPLETE_ID : STATE_SEND_OTHER_NODEINFOS;
|
||||||
config_state = 0;
|
config_state = 0;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case STATE_SEND_OTHER_NODEINFOS: {
|
||||||
|
LOG_INFO("getFromRadio=STATE_SEND_OTHER_NODEINFOS\n");
|
||||||
|
if (nodeInfoForPhone.num != 0) {
|
||||||
|
LOG_INFO("nodeinfo: num=0x%x, lastseen=%u, id=%s, name=%s\n", nodeInfoForPhone.num, nodeInfoForPhone.last_heard,
|
||||||
|
nodeInfoForPhone.user.id, nodeInfoForPhone.user.long_name);
|
||||||
|
fromRadioScratch.which_payload_variant = meshtastic_FromRadio_node_info_tag;
|
||||||
|
fromRadioScratch.node_info = nodeInfoForPhone;
|
||||||
|
// Stay in current state until done sending nodeinfos
|
||||||
|
nodeInfoForPhone.num = 0; // We just consumed a nodeinfo, will need a new one next time
|
||||||
|
} else {
|
||||||
|
LOG_INFO("Done sending nodeinfos\n");
|
||||||
|
state = STATE_SEND_COMPLETE_ID;
|
||||||
|
// Go ahead and send that ID right now
|
||||||
|
return getFromRadio(buf);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
case STATE_SEND_COMPLETE_ID:
|
case STATE_SEND_COMPLETE_ID:
|
||||||
LOG_INFO("getFromRadio=STATE_SEND_COMPLETE_ID\n");
|
LOG_INFO("getFromRadio=STATE_SEND_COMPLETE_ID\n");
|
||||||
fromRadioScratch.which_payload_variant = meshtastic_FromRadio_config_complete_id_tag;
|
fromRadioScratch.which_payload_variant = meshtastic_FromRadio_config_complete_id_tag;
|
||||||
@@ -422,10 +438,11 @@ bool PhoneAPI::available()
|
|||||||
case STATE_SEND_CONFIG:
|
case STATE_SEND_CONFIG:
|
||||||
case STATE_SEND_MODULECONFIG:
|
case STATE_SEND_MODULECONFIG:
|
||||||
case STATE_SEND_METADATA:
|
case STATE_SEND_METADATA:
|
||||||
|
case STATE_SEND_OWN_NODEINFO:
|
||||||
case STATE_SEND_COMPLETE_ID:
|
case STATE_SEND_COMPLETE_ID:
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
case STATE_SEND_NODEINFO:
|
case STATE_SEND_OTHER_NODEINFOS:
|
||||||
if (nodeInfoForPhone.num == 0) {
|
if (nodeInfoForPhone.num == 0) {
|
||||||
auto nextNode = nodeDB->readNextMeshNode(readIndex);
|
auto nextNode = nodeDB->readNextMeshNode(readIndex);
|
||||||
if (nextNode) {
|
if (nextNode) {
|
||||||
|
|||||||
+8
-6
@@ -6,6 +6,7 @@
|
|||||||
|
|
||||||
// Make sure that we never let our packets grow too large for one BLE packet
|
// Make sure that we never let our packets grow too large for one BLE packet
|
||||||
#define MAX_TO_FROM_RADIO_SIZE 512
|
#define MAX_TO_FROM_RADIO_SIZE 512
|
||||||
|
#define SPECIAL_NONCE 69420
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Provides our protobuf based API which phone/PC clients can use to talk to our device
|
* Provides our protobuf based API which phone/PC clients can use to talk to our device
|
||||||
@@ -20,13 +21,14 @@ class PhoneAPI
|
|||||||
: public Observer<uint32_t> // FIXME, we shouldn't be inheriting from Observer, instead use CallbackObserver as a member
|
: public Observer<uint32_t> // FIXME, we shouldn't be inheriting from Observer, instead use CallbackObserver as a member
|
||||||
{
|
{
|
||||||
enum State {
|
enum State {
|
||||||
STATE_SEND_NOTHING, // Initial state, don't send anything until the client starts asking for config
|
STATE_SEND_NOTHING, // Initial state, don't send anything until the client starts asking for config
|
||||||
STATE_SEND_MY_INFO, // send our my info record
|
STATE_SEND_MY_INFO, // send our my info record
|
||||||
STATE_SEND_NODEINFO, // states progress in this order as the device sends to to the client
|
STATE_SEND_OWN_NODEINFO,
|
||||||
STATE_SEND_CHANNELS, // Send all channels
|
|
||||||
STATE_SEND_CONFIG, // Replacement for the old Radioconfig
|
|
||||||
STATE_SEND_MODULECONFIG, // Send Module specific config
|
|
||||||
STATE_SEND_METADATA,
|
STATE_SEND_METADATA,
|
||||||
|
STATE_SEND_CHANNELS, // Send all channels
|
||||||
|
STATE_SEND_CONFIG, // Replacement for the old Radioconfig
|
||||||
|
STATE_SEND_MODULECONFIG, // Send Module specific config
|
||||||
|
STATE_SEND_OTHER_NODEINFOS, // states progress in this order as the device sends to to the client
|
||||||
STATE_SEND_COMPLETE_ID,
|
STATE_SEND_COMPLETE_ID,
|
||||||
STATE_SEND_PACKETS // send packets or debug strings
|
STATE_SEND_PACKETS // send packets or debug strings
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -8,12 +8,57 @@
|
|||||||
#include "PortduinoGlue.h"
|
#include "PortduinoGlue.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#define MAX_POWER 20
|
#ifndef RF95_MAX_POWER
|
||||||
|
#define RF95_MAX_POWER 20
|
||||||
|
#endif
|
||||||
|
|
||||||
// if we use 20 we are limited to 1% duty cycle or hw might overheat. For continuous operation set a limit of 17
|
// if we use 20 we are limited to 1% duty cycle or hw might overheat. For continuous operation set a limit of 17
|
||||||
// In theory up to 27 dBm is possible, but the modules installed in most radios can cope with a max of 20. So BIG WARNING
|
// In theory up to 27 dBm is possible, but the modules installed in most radios can cope with a max of 20. So BIG WARNING
|
||||||
// if you set power to something higher than 17 or 20 you might fry your board.
|
// if you set power to something higher than 17 or 20 you might fry your board.
|
||||||
|
|
||||||
#define POWER_DEFAULT 17 // How much power to use if the user hasn't set a power level
|
#define POWER_DEFAULT 17 // How much power to use if the user hasn't set a power level
|
||||||
|
#ifdef RADIOMASTER_900_BANDIT_NANO
|
||||||
|
// Structure to hold DAC and DB values
|
||||||
|
typedef struct {
|
||||||
|
uint8_t dac;
|
||||||
|
uint8_t db;
|
||||||
|
} DACDB;
|
||||||
|
|
||||||
|
// Interpolation function
|
||||||
|
DACDB interpolate(uint8_t dbm, uint8_t dbm1, uint8_t dbm2, DACDB val1, DACDB val2) {
|
||||||
|
DACDB result;
|
||||||
|
double fraction = (double)(dbm - dbm1) / (dbm2 - dbm1);
|
||||||
|
result.dac = (uint8_t)(val1.dac + fraction * (val2.dac - val1.dac));
|
||||||
|
result.db = (uint8_t)(val1.db + fraction * (val2.db - val1.db));
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Function to find the correct DAC and DB values based on dBm using interpolation
|
||||||
|
DACDB getDACandDB(uint8_t dbm) {
|
||||||
|
// Predefined values
|
||||||
|
static const struct {
|
||||||
|
uint8_t dbm;
|
||||||
|
DACDB values;
|
||||||
|
} dbmToDACDB[] = {
|
||||||
|
{20, {168, 2}}, // 100mW
|
||||||
|
{24, {148, 6}}, // 250mW
|
||||||
|
{27, {128, 9}}, // 500mW
|
||||||
|
{30, {90, 12}} // 1000mW
|
||||||
|
};
|
||||||
|
const int numValues = sizeof(dbmToDACDB) / sizeof(dbmToDACDB[0]);
|
||||||
|
|
||||||
|
// Find the interval dbm falls within and interpolate
|
||||||
|
for (int i = 0; i < numValues - 1; i++) {
|
||||||
|
if (dbm >= dbmToDACDB[i].dbm && dbm <= dbmToDACDB[i + 1].dbm) {
|
||||||
|
return interpolate(dbm, dbmToDACDB[i].dbm, dbmToDACDB[i + 1].dbm, dbmToDACDB[i].values, dbmToDACDB[i + 1].values);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return a default value if no match is found and default to 100mW
|
||||||
|
DACDB defaultValue = {168, 2};
|
||||||
|
return defaultValue;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
RF95Interface::RF95Interface(LockingArduinoHal *hal, RADIOLIB_PIN_TYPE cs, RADIOLIB_PIN_TYPE irq, RADIOLIB_PIN_TYPE rst,
|
RF95Interface::RF95Interface(LockingArduinoHal *hal, RADIOLIB_PIN_TYPE cs, RADIOLIB_PIN_TYPE irq, RADIOLIB_PIN_TYPE rst,
|
||||||
RADIOLIB_PIN_TYPE busy)
|
RADIOLIB_PIN_TYPE busy)
|
||||||
@@ -49,8 +94,15 @@ bool RF95Interface::init()
|
|||||||
{
|
{
|
||||||
RadioLibInterface::init();
|
RadioLibInterface::init();
|
||||||
|
|
||||||
if (power > MAX_POWER) // This chip has lower power limits than some
|
#ifdef RADIOMASTER_900_BANDIT_NANO
|
||||||
power = MAX_POWER;
|
// DAC and DB values based on dBm using interpolation
|
||||||
|
DACDB dacDbValues = getDACandDB(power);
|
||||||
|
int8_t powerDAC = dacDbValues.dac;
|
||||||
|
power = dacDbValues.db;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (power > RF95_MAX_POWER) // This chip has lower power limits than some
|
||||||
|
power = RF95_MAX_POWER;
|
||||||
|
|
||||||
limitPower();
|
limitPower();
|
||||||
|
|
||||||
@@ -61,6 +113,19 @@ bool RF95Interface::init()
|
|||||||
digitalWrite(RF95_TCXO, 1);
|
digitalWrite(RF95_TCXO, 1);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
// enable PA
|
||||||
|
#ifdef RF95_PA_EN
|
||||||
|
#if defined(RF95_PA_DAC_EN)
|
||||||
|
#ifdef RADIOMASTER_900_BANDIT_NANO
|
||||||
|
// Use calculated DAC value
|
||||||
|
dacWrite(RF95_PA_EN, powerDAC);
|
||||||
|
#else
|
||||||
|
// Use Value set in /*/variant.h
|
||||||
|
dacWrite(RF95_PA_EN, RF95_PA_LEVEL);
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
/*
|
/*
|
||||||
#define RF95_TXEN (22) // If defined, this pin should be set high prior to transmit (controls an external analog switch)
|
#define RF95_TXEN (22) // If defined, this pin should be set high prior to transmit (controls an external analog switch)
|
||||||
#define RF95_RXEN (23) // If defined, this pin should be set high prior to receive (controls an external analog switch)
|
#define RF95_RXEN (23) // If defined, this pin should be set high prior to receive (controls an external analog switch)
|
||||||
@@ -71,6 +136,11 @@ bool RF95Interface::init()
|
|||||||
digitalWrite(RF95_TXEN, 0);
|
digitalWrite(RF95_TXEN, 0);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef RF95_FAN_EN
|
||||||
|
pinMode(RF95_FAN_EN, OUTPUT);
|
||||||
|
digitalWrite(RF95_FAN_EN, 1);
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifdef RF95_RXEN
|
#ifdef RF95_RXEN
|
||||||
pinMode(RF95_RXEN, OUTPUT);
|
pinMode(RF95_RXEN, OUTPUT);
|
||||||
digitalWrite(RF95_RXEN, 1);
|
digitalWrite(RF95_RXEN, 1);
|
||||||
@@ -92,6 +162,9 @@ bool RF95Interface::init()
|
|||||||
LOG_INFO("Frequency set to %f\n", getFreq());
|
LOG_INFO("Frequency set to %f\n", getFreq());
|
||||||
LOG_INFO("Bandwidth set to %f\n", bw);
|
LOG_INFO("Bandwidth set to %f\n", bw);
|
||||||
LOG_INFO("Power output set to %d\n", power);
|
LOG_INFO("Power output set to %d\n", power);
|
||||||
|
#ifdef RADIOMASTER_900_BANDIT_NANO
|
||||||
|
LOG_INFO("DAC output set to %d\n", powerDAC);
|
||||||
|
#endif
|
||||||
|
|
||||||
if (res == RADIOLIB_ERR_NONE)
|
if (res == RADIOLIB_ERR_NONE)
|
||||||
res = lora->setCRC(RADIOLIB_SX126X_LORA_CRC_ON);
|
res = lora->setCRC(RADIOLIB_SX126X_LORA_CRC_ON);
|
||||||
@@ -146,10 +219,14 @@ bool RF95Interface::reconfigure()
|
|||||||
if (err != RADIOLIB_ERR_NONE)
|
if (err != RADIOLIB_ERR_NONE)
|
||||||
RECORD_CRITICALERROR(meshtastic_CriticalErrorCode_INVALID_RADIO_SETTING);
|
RECORD_CRITICALERROR(meshtastic_CriticalErrorCode_INVALID_RADIO_SETTING);
|
||||||
|
|
||||||
if (power > MAX_POWER) // This chip has lower power limits than some
|
if (power > RF95_MAX_POWER) // This chip has lower power limits than some
|
||||||
power = MAX_POWER;
|
power = RF95_MAX_POWER;
|
||||||
|
|
||||||
|
#ifdef USE_RF95_RFO
|
||||||
|
err = lora->setOutputPower(power, true);
|
||||||
|
#else
|
||||||
err = lora->setOutputPower(power);
|
err = lora->setOutputPower(power);
|
||||||
|
#endif
|
||||||
if (err != RADIOLIB_ERR_NONE)
|
if (err != RADIOLIB_ERR_NONE)
|
||||||
RECORD_CRITICALERROR(meshtastic_CriticalErrorCode_INVALID_RADIO_SETTING);
|
RECORD_CRITICALERROR(meshtastic_CriticalErrorCode_INVALID_RADIO_SETTING);
|
||||||
|
|
||||||
@@ -235,5 +312,9 @@ bool RF95Interface::sleep()
|
|||||||
setStandby(); // First cancel any active receiving/sending
|
setStandby(); // First cancel any active receiving/sending
|
||||||
lora->sleep();
|
lora->sleep();
|
||||||
|
|
||||||
|
#ifdef RF95_FAN_EN
|
||||||
|
digitalWrite(RF95_FAN_EN, 0);
|
||||||
|
#endif
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
+10
-10
@@ -31,18 +31,18 @@ const RegionInfo regions[] = {
|
|||||||
RDEF(EU_433, 433.0f, 434.0f, 10, 0, 12, true, false, false),
|
RDEF(EU_433, 433.0f, 434.0f, 10, 0, 12, true, false, false),
|
||||||
|
|
||||||
/*
|
/*
|
||||||
https://www.thethingsnetwork.org/docs/lorawan/duty-cycle/
|
https://www.thethingsnetwork.org/docs/lorawan/duty-cycle/
|
||||||
https://www.thethingsnetwork.org/docs/lorawan/regional-parameters/
|
https://www.thethingsnetwork.org/docs/lorawan/regional-parameters/
|
||||||
https://www.legislation.gov.uk/uksi/1999/930/schedule/6/part/III/made/data.xht?view=snippet&wrap=true
|
https://www.legislation.gov.uk/uksi/1999/930/schedule/6/part/III/made/data.xht?view=snippet&wrap=true
|
||||||
|
|
||||||
audio_permitted = false per regulation
|
audio_permitted = false per regulation
|
||||||
|
|
||||||
Special Note:
|
Special Note:
|
||||||
The link above describes LoRaWAN's band plan, stating a power limit of 16 dBm. This is their own suggested specification,
|
The link above describes LoRaWAN's band plan, stating a power limit of 16 dBm. This is their own suggested specification,
|
||||||
we do not need to follow it. The European Union regulations clearly state that the power limit for this frequency range is
|
we do not need to follow it. The European Union regulations clearly state that the power limit for this frequency range is
|
||||||
500 mW, or 27 dBm. It also states that we can use interference avoidance and spectrum access techniques to avoid a duty
|
500 mW, or 27 dBm. It also states that we can use interference avoidance and spectrum access techniques (such as LBT +
|
||||||
cycle. (Please refer to section 4.21 in the following document)
|
AFA) to avoid a duty cycle. (Please refer to line P page 22 of this document.)
|
||||||
https://ec.europa.eu/growth/tools-databases/tris/index.cfm/ro/search/?trisaction=search.detail&year=2021&num=528&dLang=EN
|
https://www.etsi.org/deliver/etsi_en/300200_300299/30022002/03.01.01_60/en_30022002v030101p.pdf
|
||||||
*/
|
*/
|
||||||
RDEF(EU_868, 869.4f, 869.65f, 10, 0, 27, false, false, false),
|
RDEF(EU_868, 869.4f, 869.65f, 10, 0, 27, false, false, false),
|
||||||
|
|
||||||
|
|||||||
@@ -42,7 +42,11 @@ int16_t RadioLibRF95::begin(float freq, float bw, uint8_t sf, uint8_t cr, uint8_
|
|||||||
state = setCodingRate(cr);
|
state = setCodingRate(cr);
|
||||||
RADIOLIB_ASSERT(state);
|
RADIOLIB_ASSERT(state);
|
||||||
|
|
||||||
|
#ifdef USE_RF95_RFO
|
||||||
|
state = setOutputPower(power, true);
|
||||||
|
#else
|
||||||
state = setOutputPower(power);
|
state = setOutputPower(power);
|
||||||
|
#endif
|
||||||
RADIOLIB_ASSERT(state);
|
RADIOLIB_ASSERT(state);
|
||||||
|
|
||||||
state = setGain(gain);
|
state = setGain(gain);
|
||||||
|
|||||||
@@ -135,6 +135,8 @@ typedef struct _meshtastic_AdminMessage {
|
|||||||
bool enter_dfu_mode_request;
|
bool enter_dfu_mode_request;
|
||||||
/* Delete the file by the specified path from the device */
|
/* Delete the file by the specified path from the device */
|
||||||
char delete_file_request[201];
|
char delete_file_request[201];
|
||||||
|
/* Set zero and offset for scale chips */
|
||||||
|
uint32_t set_scale;
|
||||||
/* Set the owner for this node */
|
/* Set the owner for this node */
|
||||||
meshtastic_User set_owner;
|
meshtastic_User set_owner;
|
||||||
/* Set channels (using the new API).
|
/* Set channels (using the new API).
|
||||||
@@ -238,6 +240,7 @@ extern "C" {
|
|||||||
#define meshtastic_AdminMessage_get_node_remote_hardware_pins_response_tag 20
|
#define meshtastic_AdminMessage_get_node_remote_hardware_pins_response_tag 20
|
||||||
#define meshtastic_AdminMessage_enter_dfu_mode_request_tag 21
|
#define meshtastic_AdminMessage_enter_dfu_mode_request_tag 21
|
||||||
#define meshtastic_AdminMessage_delete_file_request_tag 22
|
#define meshtastic_AdminMessage_delete_file_request_tag 22
|
||||||
|
#define meshtastic_AdminMessage_set_scale_tag 23
|
||||||
#define meshtastic_AdminMessage_set_owner_tag 32
|
#define meshtastic_AdminMessage_set_owner_tag 32
|
||||||
#define meshtastic_AdminMessage_set_channel_tag 33
|
#define meshtastic_AdminMessage_set_channel_tag 33
|
||||||
#define meshtastic_AdminMessage_set_config_tag 34
|
#define meshtastic_AdminMessage_set_config_tag 34
|
||||||
@@ -281,6 +284,7 @@ X(a, STATIC, ONEOF, BOOL, (payload_variant,get_node_remote_hardware_pin
|
|||||||
X(a, STATIC, ONEOF, MESSAGE, (payload_variant,get_node_remote_hardware_pins_response,get_node_remote_hardware_pins_response), 20) \
|
X(a, STATIC, ONEOF, MESSAGE, (payload_variant,get_node_remote_hardware_pins_response,get_node_remote_hardware_pins_response), 20) \
|
||||||
X(a, STATIC, ONEOF, BOOL, (payload_variant,enter_dfu_mode_request,enter_dfu_mode_request), 21) \
|
X(a, STATIC, ONEOF, BOOL, (payload_variant,enter_dfu_mode_request,enter_dfu_mode_request), 21) \
|
||||||
X(a, STATIC, ONEOF, STRING, (payload_variant,delete_file_request,delete_file_request), 22) \
|
X(a, STATIC, ONEOF, STRING, (payload_variant,delete_file_request,delete_file_request), 22) \
|
||||||
|
X(a, STATIC, ONEOF, UINT32, (payload_variant,set_scale,set_scale), 23) \
|
||||||
X(a, STATIC, ONEOF, MESSAGE, (payload_variant,set_owner,set_owner), 32) \
|
X(a, STATIC, ONEOF, MESSAGE, (payload_variant,set_owner,set_owner), 32) \
|
||||||
X(a, STATIC, ONEOF, MESSAGE, (payload_variant,set_channel,set_channel), 33) \
|
X(a, STATIC, ONEOF, MESSAGE, (payload_variant,set_channel,set_channel), 33) \
|
||||||
X(a, STATIC, ONEOF, MESSAGE, (payload_variant,set_config,set_config), 34) \
|
X(a, STATIC, ONEOF, MESSAGE, (payload_variant,set_config,set_config), 34) \
|
||||||
|
|||||||
@@ -73,6 +73,9 @@ typedef struct _meshtastic_GeoChat {
|
|||||||
/* Uid recipient of the message */
|
/* Uid recipient of the message */
|
||||||
bool has_to;
|
bool has_to;
|
||||||
char to[120];
|
char to[120];
|
||||||
|
/* Callsign of the recipient for the message */
|
||||||
|
bool has_to_callsign;
|
||||||
|
char to_callsign[120];
|
||||||
} meshtastic_GeoChat;
|
} meshtastic_GeoChat;
|
||||||
|
|
||||||
/* ATAK Group
|
/* ATAK Group
|
||||||
@@ -164,13 +167,13 @@ extern "C" {
|
|||||||
|
|
||||||
/* Initializer values for message structs */
|
/* Initializer values for message structs */
|
||||||
#define meshtastic_TAKPacket_init_default {0, false, meshtastic_Contact_init_default, false, meshtastic_Group_init_default, false, meshtastic_Status_init_default, 0, {meshtastic_PLI_init_default}}
|
#define meshtastic_TAKPacket_init_default {0, false, meshtastic_Contact_init_default, false, meshtastic_Group_init_default, false, meshtastic_Status_init_default, 0, {meshtastic_PLI_init_default}}
|
||||||
#define meshtastic_GeoChat_init_default {"", false, ""}
|
#define meshtastic_GeoChat_init_default {"", false, "", false, ""}
|
||||||
#define meshtastic_Group_init_default {_meshtastic_MemberRole_MIN, _meshtastic_Team_MIN}
|
#define meshtastic_Group_init_default {_meshtastic_MemberRole_MIN, _meshtastic_Team_MIN}
|
||||||
#define meshtastic_Status_init_default {0}
|
#define meshtastic_Status_init_default {0}
|
||||||
#define meshtastic_Contact_init_default {"", ""}
|
#define meshtastic_Contact_init_default {"", ""}
|
||||||
#define meshtastic_PLI_init_default {0, 0, 0, 0, 0}
|
#define meshtastic_PLI_init_default {0, 0, 0, 0, 0}
|
||||||
#define meshtastic_TAKPacket_init_zero {0, false, meshtastic_Contact_init_zero, false, meshtastic_Group_init_zero, false, meshtastic_Status_init_zero, 0, {meshtastic_PLI_init_zero}}
|
#define meshtastic_TAKPacket_init_zero {0, false, meshtastic_Contact_init_zero, false, meshtastic_Group_init_zero, false, meshtastic_Status_init_zero, 0, {meshtastic_PLI_init_zero}}
|
||||||
#define meshtastic_GeoChat_init_zero {"", false, ""}
|
#define meshtastic_GeoChat_init_zero {"", false, "", false, ""}
|
||||||
#define meshtastic_Group_init_zero {_meshtastic_MemberRole_MIN, _meshtastic_Team_MIN}
|
#define meshtastic_Group_init_zero {_meshtastic_MemberRole_MIN, _meshtastic_Team_MIN}
|
||||||
#define meshtastic_Status_init_zero {0}
|
#define meshtastic_Status_init_zero {0}
|
||||||
#define meshtastic_Contact_init_zero {"", ""}
|
#define meshtastic_Contact_init_zero {"", ""}
|
||||||
@@ -179,6 +182,7 @@ extern "C" {
|
|||||||
/* Field tags (for use in manual encoding/decoding) */
|
/* Field tags (for use in manual encoding/decoding) */
|
||||||
#define meshtastic_GeoChat_message_tag 1
|
#define meshtastic_GeoChat_message_tag 1
|
||||||
#define meshtastic_GeoChat_to_tag 2
|
#define meshtastic_GeoChat_to_tag 2
|
||||||
|
#define meshtastic_GeoChat_to_callsign_tag 3
|
||||||
#define meshtastic_Group_role_tag 1
|
#define meshtastic_Group_role_tag 1
|
||||||
#define meshtastic_Group_team_tag 2
|
#define meshtastic_Group_team_tag 2
|
||||||
#define meshtastic_Status_battery_tag 1
|
#define meshtastic_Status_battery_tag 1
|
||||||
@@ -214,7 +218,8 @@ X(a, STATIC, ONEOF, MESSAGE, (payload_variant,chat,payload_variant.chat),
|
|||||||
|
|
||||||
#define meshtastic_GeoChat_FIELDLIST(X, a) \
|
#define meshtastic_GeoChat_FIELDLIST(X, a) \
|
||||||
X(a, STATIC, SINGULAR, STRING, message, 1) \
|
X(a, STATIC, SINGULAR, STRING, message, 1) \
|
||||||
X(a, STATIC, OPTIONAL, STRING, to, 2)
|
X(a, STATIC, OPTIONAL, STRING, to, 2) \
|
||||||
|
X(a, STATIC, OPTIONAL, STRING, to_callsign, 3)
|
||||||
#define meshtastic_GeoChat_CALLBACK NULL
|
#define meshtastic_GeoChat_CALLBACK NULL
|
||||||
#define meshtastic_GeoChat_DEFAULT NULL
|
#define meshtastic_GeoChat_DEFAULT NULL
|
||||||
|
|
||||||
@@ -262,11 +267,11 @@ extern const pb_msgdesc_t meshtastic_PLI_msg;
|
|||||||
/* Maximum encoded size of messages (where known) */
|
/* Maximum encoded size of messages (where known) */
|
||||||
#define MESHTASTIC_MESHTASTIC_ATAK_PB_H_MAX_SIZE meshtastic_TAKPacket_size
|
#define MESHTASTIC_MESHTASTIC_ATAK_PB_H_MAX_SIZE meshtastic_TAKPacket_size
|
||||||
#define meshtastic_Contact_size 242
|
#define meshtastic_Contact_size 242
|
||||||
#define meshtastic_GeoChat_size 323
|
#define meshtastic_GeoChat_size 444
|
||||||
#define meshtastic_Group_size 4
|
#define meshtastic_Group_size 4
|
||||||
#define meshtastic_PLI_size 31
|
#define meshtastic_PLI_size 31
|
||||||
#define meshtastic_Status_size 3
|
#define meshtastic_Status_size 3
|
||||||
#define meshtastic_TAKPacket_size 584
|
#define meshtastic_TAKPacket_size 705
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
} /* extern "C" */
|
} /* extern "C" */
|
||||||
|
|||||||
@@ -46,3 +46,4 @@ PB_BIND(meshtastic_Config_BluetoothConfig, meshtastic_Config_BluetoothConfig, AU
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -182,6 +182,25 @@ typedef enum _meshtastic_Config_DisplayConfig_DisplayMode {
|
|||||||
meshtastic_Config_DisplayConfig_DisplayMode_COLOR = 3
|
meshtastic_Config_DisplayConfig_DisplayMode_COLOR = 3
|
||||||
} meshtastic_Config_DisplayConfig_DisplayMode;
|
} meshtastic_Config_DisplayConfig_DisplayMode;
|
||||||
|
|
||||||
|
typedef enum _meshtastic_Config_DisplayConfig_CompassOrientation {
|
||||||
|
/* The compass and the display are in the same orientation. */
|
||||||
|
meshtastic_Config_DisplayConfig_CompassOrientation_DEGREES_0 = 0,
|
||||||
|
/* Rotate the compass by 90 degrees. */
|
||||||
|
meshtastic_Config_DisplayConfig_CompassOrientation_DEGREES_90 = 1,
|
||||||
|
/* Rotate the compass by 180 degrees. */
|
||||||
|
meshtastic_Config_DisplayConfig_CompassOrientation_DEGREES_180 = 2,
|
||||||
|
/* Rotate the compass by 270 degrees. */
|
||||||
|
meshtastic_Config_DisplayConfig_CompassOrientation_DEGREES_270 = 3,
|
||||||
|
/* Don't rotate the compass, but invert the result. */
|
||||||
|
meshtastic_Config_DisplayConfig_CompassOrientation_DEGREES_0_INVERTED = 4,
|
||||||
|
/* Rotate the compass by 90 degrees and invert. */
|
||||||
|
meshtastic_Config_DisplayConfig_CompassOrientation_DEGREES_90_INVERTED = 5,
|
||||||
|
/* Rotate the compass by 180 degrees and invert. */
|
||||||
|
meshtastic_Config_DisplayConfig_CompassOrientation_DEGREES_180_INVERTED = 6,
|
||||||
|
/* Rotate the compass by 270 degrees and invert. */
|
||||||
|
meshtastic_Config_DisplayConfig_CompassOrientation_DEGREES_270_INVERTED = 7
|
||||||
|
} meshtastic_Config_DisplayConfig_CompassOrientation;
|
||||||
|
|
||||||
typedef enum _meshtastic_Config_LoRaConfig_RegionCode {
|
typedef enum _meshtastic_Config_LoRaConfig_RegionCode {
|
||||||
/* Region is not set */
|
/* Region is not set */
|
||||||
meshtastic_Config_LoRaConfig_RegionCode_UNSET = 0,
|
meshtastic_Config_LoRaConfig_RegionCode_UNSET = 0,
|
||||||
@@ -413,6 +432,8 @@ typedef struct _meshtastic_Config_DisplayConfig {
|
|||||||
bool heading_bold;
|
bool heading_bold;
|
||||||
/* Should we wake the screen up on accelerometer detected motion or tap */
|
/* Should we wake the screen up on accelerometer detected motion or tap */
|
||||||
bool wake_on_tap_or_motion;
|
bool wake_on_tap_or_motion;
|
||||||
|
/* Indicates how to rotate or invert the compass output to accurate display on the display. */
|
||||||
|
meshtastic_Config_DisplayConfig_CompassOrientation compass_orientation;
|
||||||
} meshtastic_Config_DisplayConfig;
|
} meshtastic_Config_DisplayConfig;
|
||||||
|
|
||||||
/* Lora Config */
|
/* Lora Config */
|
||||||
@@ -547,6 +568,10 @@ extern "C" {
|
|||||||
#define _meshtastic_Config_DisplayConfig_DisplayMode_MAX meshtastic_Config_DisplayConfig_DisplayMode_COLOR
|
#define _meshtastic_Config_DisplayConfig_DisplayMode_MAX meshtastic_Config_DisplayConfig_DisplayMode_COLOR
|
||||||
#define _meshtastic_Config_DisplayConfig_DisplayMode_ARRAYSIZE ((meshtastic_Config_DisplayConfig_DisplayMode)(meshtastic_Config_DisplayConfig_DisplayMode_COLOR+1))
|
#define _meshtastic_Config_DisplayConfig_DisplayMode_ARRAYSIZE ((meshtastic_Config_DisplayConfig_DisplayMode)(meshtastic_Config_DisplayConfig_DisplayMode_COLOR+1))
|
||||||
|
|
||||||
|
#define _meshtastic_Config_DisplayConfig_CompassOrientation_MIN meshtastic_Config_DisplayConfig_CompassOrientation_DEGREES_0
|
||||||
|
#define _meshtastic_Config_DisplayConfig_CompassOrientation_MAX meshtastic_Config_DisplayConfig_CompassOrientation_DEGREES_270_INVERTED
|
||||||
|
#define _meshtastic_Config_DisplayConfig_CompassOrientation_ARRAYSIZE ((meshtastic_Config_DisplayConfig_CompassOrientation)(meshtastic_Config_DisplayConfig_CompassOrientation_DEGREES_270_INVERTED+1))
|
||||||
|
|
||||||
#define _meshtastic_Config_LoRaConfig_RegionCode_MIN meshtastic_Config_LoRaConfig_RegionCode_UNSET
|
#define _meshtastic_Config_LoRaConfig_RegionCode_MIN meshtastic_Config_LoRaConfig_RegionCode_UNSET
|
||||||
#define _meshtastic_Config_LoRaConfig_RegionCode_MAX meshtastic_Config_LoRaConfig_RegionCode_SG_923
|
#define _meshtastic_Config_LoRaConfig_RegionCode_MAX meshtastic_Config_LoRaConfig_RegionCode_SG_923
|
||||||
#define _meshtastic_Config_LoRaConfig_RegionCode_ARRAYSIZE ((meshtastic_Config_LoRaConfig_RegionCode)(meshtastic_Config_LoRaConfig_RegionCode_SG_923+1))
|
#define _meshtastic_Config_LoRaConfig_RegionCode_ARRAYSIZE ((meshtastic_Config_LoRaConfig_RegionCode)(meshtastic_Config_LoRaConfig_RegionCode_SG_923+1))
|
||||||
@@ -573,6 +598,7 @@ extern "C" {
|
|||||||
#define meshtastic_Config_DisplayConfig_units_ENUMTYPE meshtastic_Config_DisplayConfig_DisplayUnits
|
#define meshtastic_Config_DisplayConfig_units_ENUMTYPE meshtastic_Config_DisplayConfig_DisplayUnits
|
||||||
#define meshtastic_Config_DisplayConfig_oled_ENUMTYPE meshtastic_Config_DisplayConfig_OledType
|
#define meshtastic_Config_DisplayConfig_oled_ENUMTYPE meshtastic_Config_DisplayConfig_OledType
|
||||||
#define meshtastic_Config_DisplayConfig_displaymode_ENUMTYPE meshtastic_Config_DisplayConfig_DisplayMode
|
#define meshtastic_Config_DisplayConfig_displaymode_ENUMTYPE meshtastic_Config_DisplayConfig_DisplayMode
|
||||||
|
#define meshtastic_Config_DisplayConfig_compass_orientation_ENUMTYPE meshtastic_Config_DisplayConfig_CompassOrientation
|
||||||
|
|
||||||
#define meshtastic_Config_LoRaConfig_modem_preset_ENUMTYPE meshtastic_Config_LoRaConfig_ModemPreset
|
#define meshtastic_Config_LoRaConfig_modem_preset_ENUMTYPE meshtastic_Config_LoRaConfig_ModemPreset
|
||||||
#define meshtastic_Config_LoRaConfig_region_ENUMTYPE meshtastic_Config_LoRaConfig_RegionCode
|
#define meshtastic_Config_LoRaConfig_region_ENUMTYPE meshtastic_Config_LoRaConfig_RegionCode
|
||||||
@@ -587,7 +613,7 @@ extern "C" {
|
|||||||
#define meshtastic_Config_PowerConfig_init_default {0, 0, 0, 0, 0, 0, 0, 0}
|
#define meshtastic_Config_PowerConfig_init_default {0, 0, 0, 0, 0, 0, 0, 0}
|
||||||
#define meshtastic_Config_NetworkConfig_init_default {0, "", "", "", 0, _meshtastic_Config_NetworkConfig_AddressMode_MIN, false, meshtastic_Config_NetworkConfig_IpV4Config_init_default, ""}
|
#define meshtastic_Config_NetworkConfig_init_default {0, "", "", "", 0, _meshtastic_Config_NetworkConfig_AddressMode_MIN, false, meshtastic_Config_NetworkConfig_IpV4Config_init_default, ""}
|
||||||
#define meshtastic_Config_NetworkConfig_IpV4Config_init_default {0, 0, 0, 0}
|
#define meshtastic_Config_NetworkConfig_IpV4Config_init_default {0, 0, 0, 0}
|
||||||
#define meshtastic_Config_DisplayConfig_init_default {0, _meshtastic_Config_DisplayConfig_GpsCoordinateFormat_MIN, 0, 0, 0, _meshtastic_Config_DisplayConfig_DisplayUnits_MIN, _meshtastic_Config_DisplayConfig_OledType_MIN, _meshtastic_Config_DisplayConfig_DisplayMode_MIN, 0, 0}
|
#define meshtastic_Config_DisplayConfig_init_default {0, _meshtastic_Config_DisplayConfig_GpsCoordinateFormat_MIN, 0, 0, 0, _meshtastic_Config_DisplayConfig_DisplayUnits_MIN, _meshtastic_Config_DisplayConfig_OledType_MIN, _meshtastic_Config_DisplayConfig_DisplayMode_MIN, 0, 0, _meshtastic_Config_DisplayConfig_CompassOrientation_MIN}
|
||||||
#define meshtastic_Config_LoRaConfig_init_default {0, _meshtastic_Config_LoRaConfig_ModemPreset_MIN, 0, 0, 0, 0, _meshtastic_Config_LoRaConfig_RegionCode_MIN, 0, 0, 0, 0, 0, 0, 0, 0, {0, 0, 0}, 0}
|
#define meshtastic_Config_LoRaConfig_init_default {0, _meshtastic_Config_LoRaConfig_ModemPreset_MIN, 0, 0, 0, 0, _meshtastic_Config_LoRaConfig_RegionCode_MIN, 0, 0, 0, 0, 0, 0, 0, 0, {0, 0, 0}, 0}
|
||||||
#define meshtastic_Config_BluetoothConfig_init_default {0, _meshtastic_Config_BluetoothConfig_PairingMode_MIN, 0}
|
#define meshtastic_Config_BluetoothConfig_init_default {0, _meshtastic_Config_BluetoothConfig_PairingMode_MIN, 0}
|
||||||
#define meshtastic_Config_init_zero {0, {meshtastic_Config_DeviceConfig_init_zero}}
|
#define meshtastic_Config_init_zero {0, {meshtastic_Config_DeviceConfig_init_zero}}
|
||||||
@@ -596,7 +622,7 @@ extern "C" {
|
|||||||
#define meshtastic_Config_PowerConfig_init_zero {0, 0, 0, 0, 0, 0, 0, 0}
|
#define meshtastic_Config_PowerConfig_init_zero {0, 0, 0, 0, 0, 0, 0, 0}
|
||||||
#define meshtastic_Config_NetworkConfig_init_zero {0, "", "", "", 0, _meshtastic_Config_NetworkConfig_AddressMode_MIN, false, meshtastic_Config_NetworkConfig_IpV4Config_init_zero, ""}
|
#define meshtastic_Config_NetworkConfig_init_zero {0, "", "", "", 0, _meshtastic_Config_NetworkConfig_AddressMode_MIN, false, meshtastic_Config_NetworkConfig_IpV4Config_init_zero, ""}
|
||||||
#define meshtastic_Config_NetworkConfig_IpV4Config_init_zero {0, 0, 0, 0}
|
#define meshtastic_Config_NetworkConfig_IpV4Config_init_zero {0, 0, 0, 0}
|
||||||
#define meshtastic_Config_DisplayConfig_init_zero {0, _meshtastic_Config_DisplayConfig_GpsCoordinateFormat_MIN, 0, 0, 0, _meshtastic_Config_DisplayConfig_DisplayUnits_MIN, _meshtastic_Config_DisplayConfig_OledType_MIN, _meshtastic_Config_DisplayConfig_DisplayMode_MIN, 0, 0}
|
#define meshtastic_Config_DisplayConfig_init_zero {0, _meshtastic_Config_DisplayConfig_GpsCoordinateFormat_MIN, 0, 0, 0, _meshtastic_Config_DisplayConfig_DisplayUnits_MIN, _meshtastic_Config_DisplayConfig_OledType_MIN, _meshtastic_Config_DisplayConfig_DisplayMode_MIN, 0, 0, _meshtastic_Config_DisplayConfig_CompassOrientation_MIN}
|
||||||
#define meshtastic_Config_LoRaConfig_init_zero {0, _meshtastic_Config_LoRaConfig_ModemPreset_MIN, 0, 0, 0, 0, _meshtastic_Config_LoRaConfig_RegionCode_MIN, 0, 0, 0, 0, 0, 0, 0, 0, {0, 0, 0}, 0}
|
#define meshtastic_Config_LoRaConfig_init_zero {0, _meshtastic_Config_LoRaConfig_ModemPreset_MIN, 0, 0, 0, 0, _meshtastic_Config_LoRaConfig_RegionCode_MIN, 0, 0, 0, 0, 0, 0, 0, 0, {0, 0, 0}, 0}
|
||||||
#define meshtastic_Config_BluetoothConfig_init_zero {0, _meshtastic_Config_BluetoothConfig_PairingMode_MIN, 0}
|
#define meshtastic_Config_BluetoothConfig_init_zero {0, _meshtastic_Config_BluetoothConfig_PairingMode_MIN, 0}
|
||||||
|
|
||||||
@@ -656,6 +682,7 @@ extern "C" {
|
|||||||
#define meshtastic_Config_DisplayConfig_displaymode_tag 8
|
#define meshtastic_Config_DisplayConfig_displaymode_tag 8
|
||||||
#define meshtastic_Config_DisplayConfig_heading_bold_tag 9
|
#define meshtastic_Config_DisplayConfig_heading_bold_tag 9
|
||||||
#define meshtastic_Config_DisplayConfig_wake_on_tap_or_motion_tag 10
|
#define meshtastic_Config_DisplayConfig_wake_on_tap_or_motion_tag 10
|
||||||
|
#define meshtastic_Config_DisplayConfig_compass_orientation_tag 11
|
||||||
#define meshtastic_Config_LoRaConfig_use_preset_tag 1
|
#define meshtastic_Config_LoRaConfig_use_preset_tag 1
|
||||||
#define meshtastic_Config_LoRaConfig_modem_preset_tag 2
|
#define meshtastic_Config_LoRaConfig_modem_preset_tag 2
|
||||||
#define meshtastic_Config_LoRaConfig_bandwidth_tag 3
|
#define meshtastic_Config_LoRaConfig_bandwidth_tag 3
|
||||||
@@ -778,7 +805,8 @@ X(a, STATIC, SINGULAR, UENUM, units, 6) \
|
|||||||
X(a, STATIC, SINGULAR, UENUM, oled, 7) \
|
X(a, STATIC, SINGULAR, UENUM, oled, 7) \
|
||||||
X(a, STATIC, SINGULAR, UENUM, displaymode, 8) \
|
X(a, STATIC, SINGULAR, UENUM, displaymode, 8) \
|
||||||
X(a, STATIC, SINGULAR, BOOL, heading_bold, 9) \
|
X(a, STATIC, SINGULAR, BOOL, heading_bold, 9) \
|
||||||
X(a, STATIC, SINGULAR, BOOL, wake_on_tap_or_motion, 10)
|
X(a, STATIC, SINGULAR, BOOL, wake_on_tap_or_motion, 10) \
|
||||||
|
X(a, STATIC, SINGULAR, UENUM, compass_orientation, 11)
|
||||||
#define meshtastic_Config_DisplayConfig_CALLBACK NULL
|
#define meshtastic_Config_DisplayConfig_CALLBACK NULL
|
||||||
#define meshtastic_Config_DisplayConfig_DEFAULT NULL
|
#define meshtastic_Config_DisplayConfig_DEFAULT NULL
|
||||||
|
|
||||||
@@ -834,7 +862,7 @@ extern const pb_msgdesc_t meshtastic_Config_BluetoothConfig_msg;
|
|||||||
#define MESHTASTIC_MESHTASTIC_CONFIG_PB_H_MAX_SIZE meshtastic_Config_size
|
#define MESHTASTIC_MESHTASTIC_CONFIG_PB_H_MAX_SIZE meshtastic_Config_size
|
||||||
#define meshtastic_Config_BluetoothConfig_size 10
|
#define meshtastic_Config_BluetoothConfig_size 10
|
||||||
#define meshtastic_Config_DeviceConfig_size 100
|
#define meshtastic_Config_DeviceConfig_size 100
|
||||||
#define meshtastic_Config_DisplayConfig_size 28
|
#define meshtastic_Config_DisplayConfig_size 30
|
||||||
#define meshtastic_Config_LoRaConfig_size 80
|
#define meshtastic_Config_LoRaConfig_size 80
|
||||||
#define meshtastic_Config_NetworkConfig_IpV4Config_size 20
|
#define meshtastic_Config_NetworkConfig_IpV4Config_size 20
|
||||||
#define meshtastic_Config_NetworkConfig_size 196
|
#define meshtastic_Config_NetworkConfig_size 196
|
||||||
|
|||||||
@@ -308,7 +308,7 @@ extern const pb_msgdesc_t meshtastic_OEMStore_msg;
|
|||||||
#define MESHTASTIC_MESHTASTIC_DEVICEONLY_PB_H_MAX_SIZE meshtastic_OEMStore_size
|
#define MESHTASTIC_MESHTASTIC_DEVICEONLY_PB_H_MAX_SIZE meshtastic_OEMStore_size
|
||||||
#define meshtastic_ChannelFile_size 718
|
#define meshtastic_ChannelFile_size 718
|
||||||
#define meshtastic_NodeInfoLite_size 166
|
#define meshtastic_NodeInfoLite_size 166
|
||||||
#define meshtastic_OEMStore_size 3368
|
#define meshtastic_OEMStore_size 3370
|
||||||
#define meshtastic_PositionLite_size 28
|
#define meshtastic_PositionLite_size 28
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
|
|||||||
@@ -181,7 +181,7 @@ extern const pb_msgdesc_t meshtastic_LocalModuleConfig_msg;
|
|||||||
|
|
||||||
/* Maximum encoded size of messages (where known) */
|
/* Maximum encoded size of messages (where known) */
|
||||||
#define MESHTASTIC_MESHTASTIC_LOCALONLY_PB_H_MAX_SIZE meshtastic_LocalModuleConfig_size
|
#define MESHTASTIC_MESHTASTIC_LOCALONLY_PB_H_MAX_SIZE meshtastic_LocalModuleConfig_size
|
||||||
#define meshtastic_LocalConfig_size 537
|
#define meshtastic_LocalConfig_size 539
|
||||||
#define meshtastic_LocalModuleConfig_size 685
|
#define meshtastic_LocalModuleConfig_size 685
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
|
|||||||
@@ -66,6 +66,15 @@ PB_BIND(meshtastic_Heartbeat, meshtastic_Heartbeat, AUTO)
|
|||||||
PB_BIND(meshtastic_NodeRemoteHardwarePin, meshtastic_NodeRemoteHardwarePin, AUTO)
|
PB_BIND(meshtastic_NodeRemoteHardwarePin, meshtastic_NodeRemoteHardwarePin, AUTO)
|
||||||
|
|
||||||
|
|
||||||
|
PB_BIND(meshtastic_ChunkedPayload, meshtastic_ChunkedPayload, AUTO)
|
||||||
|
|
||||||
|
|
||||||
|
PB_BIND(meshtastic_resend_chunks, meshtastic_resend_chunks, AUTO)
|
||||||
|
|
||||||
|
|
||||||
|
PB_BIND(meshtastic_ChunkedPayloadResponse, meshtastic_ChunkedPayloadResponse, AUTO)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -65,6 +65,8 @@ typedef enum _meshtastic_HardwareModel {
|
|||||||
meshtastic_HardwareModel_LORA_TYPE = 19,
|
meshtastic_HardwareModel_LORA_TYPE = 19,
|
||||||
/* wiphone https://www.wiphone.io/ */
|
/* wiphone https://www.wiphone.io/ */
|
||||||
meshtastic_HardwareModel_WIPHONE = 20,
|
meshtastic_HardwareModel_WIPHONE = 20,
|
||||||
|
/* WIO Tracker WM1110 family from Seeed Studio. Includes wio-1110-tracker and wio-1110-sdk */
|
||||||
|
meshtastic_HardwareModel_WIO_WM1110 = 21,
|
||||||
/* B&Q Consulting Station Edition G1: https://uniteng.com/wiki/doku.php?id=meshtastic:station */
|
/* B&Q Consulting Station Edition G1: https://uniteng.com/wiki/doku.php?id=meshtastic:station */
|
||||||
meshtastic_HardwareModel_STATION_G1 = 25,
|
meshtastic_HardwareModel_STATION_G1 = 25,
|
||||||
/* RAK11310 (RP2040 + SX1262) */
|
/* RAK11310 (RP2040 + SX1262) */
|
||||||
@@ -156,6 +158,11 @@ typedef enum _meshtastic_HardwareModel {
|
|||||||
/* NRF52_PROMICRO_DIY
|
/* NRF52_PROMICRO_DIY
|
||||||
Promicro NRF52840 with SX1262/LLCC68, SSD1306 OLED and NEO6M GPS */
|
Promicro NRF52840 with SX1262/LLCC68, SSD1306 OLED and NEO6M GPS */
|
||||||
meshtastic_HardwareModel_NRF52_PROMICRO_DIY = 63,
|
meshtastic_HardwareModel_NRF52_PROMICRO_DIY = 63,
|
||||||
|
/* RadioMaster 900 Bandit Nano, https://www.radiomasterrc.com/products/bandit-nano-expresslrs-rf-module
|
||||||
|
ESP32-D0WDQ6 With SX1276/SKY66122, SSD1306 OLED and No GPS */
|
||||||
|
meshtastic_HardwareModel_RADIOMASTER_900_BANDIT_NANO = 64,
|
||||||
|
/* Heltec Capsule Sensor V3 with ESP32-S3 CPU, Portable LoRa device that can replace GNSS modules or sensors */
|
||||||
|
meshtastic_HardwareModel_HELTEC_CAPSULE_SENSOR_V3 = 65,
|
||||||
/* ------------------------------------------------------------------------------------------------------------------------------------------
|
/* ------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
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.
|
Reserved ID For developing private Ports. These will show up in live traffic sparsely, so we can use a high number. Keep it within 8 bits.
|
||||||
------------------------------------------------------------------------------------------------------------------------------------------ */
|
------------------------------------------------------------------------------------------------------------------------------------------ */
|
||||||
@@ -850,6 +857,38 @@ typedef struct _meshtastic_NodeRemoteHardwarePin {
|
|||||||
meshtastic_RemoteHardwarePin pin;
|
meshtastic_RemoteHardwarePin pin;
|
||||||
} meshtastic_NodeRemoteHardwarePin;
|
} meshtastic_NodeRemoteHardwarePin;
|
||||||
|
|
||||||
|
typedef PB_BYTES_ARRAY_T(228) meshtastic_ChunkedPayload_payload_chunk_t;
|
||||||
|
typedef struct _meshtastic_ChunkedPayload {
|
||||||
|
/* The ID of the entire payload */
|
||||||
|
uint32_t payload_id;
|
||||||
|
/* The total number of chunks in the payload */
|
||||||
|
uint16_t chunk_count;
|
||||||
|
/* The current chunk index in the total */
|
||||||
|
uint16_t chunk_index;
|
||||||
|
/* The binary data of the current chunk */
|
||||||
|
meshtastic_ChunkedPayload_payload_chunk_t payload_chunk;
|
||||||
|
} meshtastic_ChunkedPayload;
|
||||||
|
|
||||||
|
/* Wrapper message for broken repeated oneof support */
|
||||||
|
typedef struct _meshtastic_resend_chunks {
|
||||||
|
pb_callback_t chunks;
|
||||||
|
} meshtastic_resend_chunks;
|
||||||
|
|
||||||
|
/* Responses to a ChunkedPayload request */
|
||||||
|
typedef struct _meshtastic_ChunkedPayloadResponse {
|
||||||
|
/* The ID of the entire payload */
|
||||||
|
uint32_t payload_id;
|
||||||
|
pb_size_t which_payload_variant;
|
||||||
|
union {
|
||||||
|
/* Request to transfer chunked payload */
|
||||||
|
bool request_transfer;
|
||||||
|
/* Accept the transfer chunked payload */
|
||||||
|
bool accept_transfer;
|
||||||
|
/* Request missing indexes in the chunked payload */
|
||||||
|
meshtastic_resend_chunks resend_chunks;
|
||||||
|
} payload_variant;
|
||||||
|
} meshtastic_ChunkedPayloadResponse;
|
||||||
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
extern "C" {
|
extern "C" {
|
||||||
@@ -925,6 +964,9 @@ extern "C" {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* Initializer values for message structs */
|
/* Initializer values for message structs */
|
||||||
#define meshtastic_Position_init_default {0, 0, 0, 0, _meshtastic_Position_LocSource_MIN, _meshtastic_Position_AltSource_MIN, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
|
#define meshtastic_Position_init_default {0, 0, 0, 0, _meshtastic_Position_LocSource_MIN, _meshtastic_Position_AltSource_MIN, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
|
||||||
#define meshtastic_User_init_default {"", "", "", {0}, _meshtastic_HardwareModel_MIN, 0, _meshtastic_Config_DeviceConfig_Role_MIN}
|
#define meshtastic_User_init_default {"", "", "", {0}, _meshtastic_HardwareModel_MIN, 0, _meshtastic_Config_DeviceConfig_Role_MIN}
|
||||||
@@ -946,6 +988,9 @@ extern "C" {
|
|||||||
#define meshtastic_DeviceMetadata_init_default {"", 0, 0, 0, 0, 0, _meshtastic_Config_DeviceConfig_Role_MIN, 0, _meshtastic_HardwareModel_MIN, 0}
|
#define meshtastic_DeviceMetadata_init_default {"", 0, 0, 0, 0, 0, _meshtastic_Config_DeviceConfig_Role_MIN, 0, _meshtastic_HardwareModel_MIN, 0}
|
||||||
#define meshtastic_Heartbeat_init_default {0}
|
#define meshtastic_Heartbeat_init_default {0}
|
||||||
#define meshtastic_NodeRemoteHardwarePin_init_default {0, false, meshtastic_RemoteHardwarePin_init_default}
|
#define meshtastic_NodeRemoteHardwarePin_init_default {0, false, meshtastic_RemoteHardwarePin_init_default}
|
||||||
|
#define meshtastic_ChunkedPayload_init_default {0, 0, 0, {0, {0}}}
|
||||||
|
#define meshtastic_resend_chunks_init_default {{{NULL}, NULL}}
|
||||||
|
#define meshtastic_ChunkedPayloadResponse_init_default {0, 0, {0}}
|
||||||
#define meshtastic_Position_init_zero {0, 0, 0, 0, _meshtastic_Position_LocSource_MIN, _meshtastic_Position_AltSource_MIN, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
|
#define meshtastic_Position_init_zero {0, 0, 0, 0, _meshtastic_Position_LocSource_MIN, _meshtastic_Position_AltSource_MIN, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
|
||||||
#define meshtastic_User_init_zero {"", "", "", {0}, _meshtastic_HardwareModel_MIN, 0, _meshtastic_Config_DeviceConfig_Role_MIN}
|
#define meshtastic_User_init_zero {"", "", "", {0}, _meshtastic_HardwareModel_MIN, 0, _meshtastic_Config_DeviceConfig_Role_MIN}
|
||||||
#define meshtastic_RouteDiscovery_init_zero {0, {0, 0, 0, 0, 0, 0, 0, 0}}
|
#define meshtastic_RouteDiscovery_init_zero {0, {0, 0, 0, 0, 0, 0, 0, 0}}
|
||||||
@@ -966,6 +1011,9 @@ extern "C" {
|
|||||||
#define meshtastic_DeviceMetadata_init_zero {"", 0, 0, 0, 0, 0, _meshtastic_Config_DeviceConfig_Role_MIN, 0, _meshtastic_HardwareModel_MIN, 0}
|
#define meshtastic_DeviceMetadata_init_zero {"", 0, 0, 0, 0, 0, _meshtastic_Config_DeviceConfig_Role_MIN, 0, _meshtastic_HardwareModel_MIN, 0}
|
||||||
#define meshtastic_Heartbeat_init_zero {0}
|
#define meshtastic_Heartbeat_init_zero {0}
|
||||||
#define meshtastic_NodeRemoteHardwarePin_init_zero {0, false, meshtastic_RemoteHardwarePin_init_zero}
|
#define meshtastic_NodeRemoteHardwarePin_init_zero {0, false, meshtastic_RemoteHardwarePin_init_zero}
|
||||||
|
#define meshtastic_ChunkedPayload_init_zero {0, 0, 0, {0, {0}}}
|
||||||
|
#define meshtastic_resend_chunks_init_zero {{{NULL}, NULL}}
|
||||||
|
#define meshtastic_ChunkedPayloadResponse_init_zero {0, 0, {0}}
|
||||||
|
|
||||||
/* Field tags (for use in manual encoding/decoding) */
|
/* Field tags (for use in manual encoding/decoding) */
|
||||||
#define meshtastic_Position_latitude_i_tag 1
|
#define meshtastic_Position_latitude_i_tag 1
|
||||||
@@ -1100,6 +1148,15 @@ extern "C" {
|
|||||||
#define meshtastic_ToRadio_heartbeat_tag 7
|
#define meshtastic_ToRadio_heartbeat_tag 7
|
||||||
#define meshtastic_NodeRemoteHardwarePin_node_num_tag 1
|
#define meshtastic_NodeRemoteHardwarePin_node_num_tag 1
|
||||||
#define meshtastic_NodeRemoteHardwarePin_pin_tag 2
|
#define meshtastic_NodeRemoteHardwarePin_pin_tag 2
|
||||||
|
#define meshtastic_ChunkedPayload_payload_id_tag 1
|
||||||
|
#define meshtastic_ChunkedPayload_chunk_count_tag 2
|
||||||
|
#define meshtastic_ChunkedPayload_chunk_index_tag 3
|
||||||
|
#define meshtastic_ChunkedPayload_payload_chunk_tag 4
|
||||||
|
#define meshtastic_resend_chunks_chunks_tag 1
|
||||||
|
#define meshtastic_ChunkedPayloadResponse_payload_id_tag 1
|
||||||
|
#define meshtastic_ChunkedPayloadResponse_request_transfer_tag 2
|
||||||
|
#define meshtastic_ChunkedPayloadResponse_accept_transfer_tag 3
|
||||||
|
#define meshtastic_ChunkedPayloadResponse_resend_chunks_tag 4
|
||||||
|
|
||||||
/* Struct field encoding specification for nanopb */
|
/* Struct field encoding specification for nanopb */
|
||||||
#define meshtastic_Position_FIELDLIST(X, a) \
|
#define meshtastic_Position_FIELDLIST(X, a) \
|
||||||
@@ -1338,6 +1395,28 @@ X(a, STATIC, OPTIONAL, MESSAGE, pin, 2)
|
|||||||
#define meshtastic_NodeRemoteHardwarePin_DEFAULT NULL
|
#define meshtastic_NodeRemoteHardwarePin_DEFAULT NULL
|
||||||
#define meshtastic_NodeRemoteHardwarePin_pin_MSGTYPE meshtastic_RemoteHardwarePin
|
#define meshtastic_NodeRemoteHardwarePin_pin_MSGTYPE meshtastic_RemoteHardwarePin
|
||||||
|
|
||||||
|
#define meshtastic_ChunkedPayload_FIELDLIST(X, a) \
|
||||||
|
X(a, STATIC, SINGULAR, UINT32, payload_id, 1) \
|
||||||
|
X(a, STATIC, SINGULAR, UINT32, chunk_count, 2) \
|
||||||
|
X(a, STATIC, SINGULAR, UINT32, chunk_index, 3) \
|
||||||
|
X(a, STATIC, SINGULAR, BYTES, payload_chunk, 4)
|
||||||
|
#define meshtastic_ChunkedPayload_CALLBACK NULL
|
||||||
|
#define meshtastic_ChunkedPayload_DEFAULT NULL
|
||||||
|
|
||||||
|
#define meshtastic_resend_chunks_FIELDLIST(X, a) \
|
||||||
|
X(a, CALLBACK, REPEATED, UINT32, chunks, 1)
|
||||||
|
#define meshtastic_resend_chunks_CALLBACK pb_default_field_callback
|
||||||
|
#define meshtastic_resend_chunks_DEFAULT NULL
|
||||||
|
|
||||||
|
#define meshtastic_ChunkedPayloadResponse_FIELDLIST(X, a) \
|
||||||
|
X(a, STATIC, SINGULAR, UINT32, payload_id, 1) \
|
||||||
|
X(a, STATIC, ONEOF, BOOL, (payload_variant,request_transfer,payload_variant.request_transfer), 2) \
|
||||||
|
X(a, STATIC, ONEOF, BOOL, (payload_variant,accept_transfer,payload_variant.accept_transfer), 3) \
|
||||||
|
X(a, STATIC, ONEOF, MESSAGE, (payload_variant,resend_chunks,payload_variant.resend_chunks), 4)
|
||||||
|
#define meshtastic_ChunkedPayloadResponse_CALLBACK NULL
|
||||||
|
#define meshtastic_ChunkedPayloadResponse_DEFAULT NULL
|
||||||
|
#define meshtastic_ChunkedPayloadResponse_payload_variant_resend_chunks_MSGTYPE meshtastic_resend_chunks
|
||||||
|
|
||||||
extern const pb_msgdesc_t meshtastic_Position_msg;
|
extern const pb_msgdesc_t meshtastic_Position_msg;
|
||||||
extern const pb_msgdesc_t meshtastic_User_msg;
|
extern const pb_msgdesc_t meshtastic_User_msg;
|
||||||
extern const pb_msgdesc_t meshtastic_RouteDiscovery_msg;
|
extern const pb_msgdesc_t meshtastic_RouteDiscovery_msg;
|
||||||
@@ -1358,6 +1437,9 @@ extern const pb_msgdesc_t meshtastic_Neighbor_msg;
|
|||||||
extern const pb_msgdesc_t meshtastic_DeviceMetadata_msg;
|
extern const pb_msgdesc_t meshtastic_DeviceMetadata_msg;
|
||||||
extern const pb_msgdesc_t meshtastic_Heartbeat_msg;
|
extern const pb_msgdesc_t meshtastic_Heartbeat_msg;
|
||||||
extern const pb_msgdesc_t meshtastic_NodeRemoteHardwarePin_msg;
|
extern const pb_msgdesc_t meshtastic_NodeRemoteHardwarePin_msg;
|
||||||
|
extern const pb_msgdesc_t meshtastic_ChunkedPayload_msg;
|
||||||
|
extern const pb_msgdesc_t meshtastic_resend_chunks_msg;
|
||||||
|
extern const pb_msgdesc_t meshtastic_ChunkedPayloadResponse_msg;
|
||||||
|
|
||||||
/* Defines for backwards compatibility with code written before nanopb-0.4.0 */
|
/* Defines for backwards compatibility with code written before nanopb-0.4.0 */
|
||||||
#define meshtastic_Position_fields &meshtastic_Position_msg
|
#define meshtastic_Position_fields &meshtastic_Position_msg
|
||||||
@@ -1380,9 +1462,15 @@ extern const pb_msgdesc_t meshtastic_NodeRemoteHardwarePin_msg;
|
|||||||
#define meshtastic_DeviceMetadata_fields &meshtastic_DeviceMetadata_msg
|
#define meshtastic_DeviceMetadata_fields &meshtastic_DeviceMetadata_msg
|
||||||
#define meshtastic_Heartbeat_fields &meshtastic_Heartbeat_msg
|
#define meshtastic_Heartbeat_fields &meshtastic_Heartbeat_msg
|
||||||
#define meshtastic_NodeRemoteHardwarePin_fields &meshtastic_NodeRemoteHardwarePin_msg
|
#define meshtastic_NodeRemoteHardwarePin_fields &meshtastic_NodeRemoteHardwarePin_msg
|
||||||
|
#define meshtastic_ChunkedPayload_fields &meshtastic_ChunkedPayload_msg
|
||||||
|
#define meshtastic_resend_chunks_fields &meshtastic_resend_chunks_msg
|
||||||
|
#define meshtastic_ChunkedPayloadResponse_fields &meshtastic_ChunkedPayloadResponse_msg
|
||||||
|
|
||||||
/* Maximum encoded size of messages (where known) */
|
/* Maximum encoded size of messages (where known) */
|
||||||
|
/* meshtastic_resend_chunks_size depends on runtime parameters */
|
||||||
|
/* meshtastic_ChunkedPayloadResponse_size depends on runtime parameters */
|
||||||
#define MESHTASTIC_MESHTASTIC_MESH_PB_H_MAX_SIZE meshtastic_FromRadio_size
|
#define MESHTASTIC_MESHTASTIC_MESH_PB_H_MAX_SIZE meshtastic_FromRadio_size
|
||||||
|
#define meshtastic_ChunkedPayload_size 245
|
||||||
#define meshtastic_Compressed_size 243
|
#define meshtastic_Compressed_size 243
|
||||||
#define meshtastic_Data_size 270
|
#define meshtastic_Data_size 270
|
||||||
#define meshtastic_DeviceMetadata_size 46
|
#define meshtastic_DeviceMetadata_size 46
|
||||||
|
|||||||
@@ -21,5 +21,8 @@ PB_BIND(meshtastic_AirQualityMetrics, meshtastic_AirQualityMetrics, AUTO)
|
|||||||
PB_BIND(meshtastic_Telemetry, meshtastic_Telemetry, AUTO)
|
PB_BIND(meshtastic_Telemetry, meshtastic_Telemetry, AUTO)
|
||||||
|
|
||||||
|
|
||||||
|
PB_BIND(meshtastic_Nau7802Config, meshtastic_Nau7802Config, AUTO)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -57,7 +57,13 @@ typedef enum _meshtastic_TelemetrySensorType {
|
|||||||
/* Lite On LTR-390UV-01 UV Light Sensor */
|
/* Lite On LTR-390UV-01 UV Light Sensor */
|
||||||
meshtastic_TelemetrySensorType_LTR390UV = 21,
|
meshtastic_TelemetrySensorType_LTR390UV = 21,
|
||||||
/* AMS TSL25911FN RGB Light Sensor */
|
/* AMS TSL25911FN RGB Light Sensor */
|
||||||
meshtastic_TelemetrySensorType_TSL25911FN = 22
|
meshtastic_TelemetrySensorType_TSL25911FN = 22,
|
||||||
|
/* AHT10 Integrated temperature and humidity sensor */
|
||||||
|
meshtastic_TelemetrySensorType_AHT10 = 23,
|
||||||
|
/* DFRobot Lark Weather station (temperature, humidity, pressure, wind speed and direction) */
|
||||||
|
meshtastic_TelemetrySensorType_DFROBOT_LARK = 24,
|
||||||
|
/* NAU7802 Scale Chip or compatible */
|
||||||
|
meshtastic_TelemetrySensorType_NAU7802 = 25
|
||||||
} meshtastic_TelemetrySensorType;
|
} meshtastic_TelemetrySensorType;
|
||||||
|
|
||||||
/* Struct definitions */
|
/* Struct definitions */
|
||||||
@@ -98,6 +104,17 @@ typedef struct _meshtastic_EnvironmentMetrics {
|
|||||||
float lux;
|
float lux;
|
||||||
/* VEML7700 high accuracy white light(irradiance) not calibrated digital 16-bit resolution sensor. */
|
/* VEML7700 high accuracy white light(irradiance) not calibrated digital 16-bit resolution sensor. */
|
||||||
float white_lux;
|
float white_lux;
|
||||||
|
/* Infrared lux */
|
||||||
|
float ir_lux;
|
||||||
|
/* Ultraviolet lux */
|
||||||
|
float uv_lux;
|
||||||
|
/* Wind direction in degrees
|
||||||
|
0 degrees = North, 90 = East, etc... */
|
||||||
|
uint16_t wind_direction;
|
||||||
|
/* Wind speed in m/s */
|
||||||
|
float wind_speed;
|
||||||
|
/* Weight in KG */
|
||||||
|
float weight;
|
||||||
} meshtastic_EnvironmentMetrics;
|
} meshtastic_EnvironmentMetrics;
|
||||||
|
|
||||||
/* Power Metrics (voltage / current / etc) */
|
/* Power Metrics (voltage / current / etc) */
|
||||||
@@ -161,6 +178,14 @@ typedef struct _meshtastic_Telemetry {
|
|||||||
} variant;
|
} variant;
|
||||||
} meshtastic_Telemetry;
|
} meshtastic_Telemetry;
|
||||||
|
|
||||||
|
/* NAU7802 Telemetry configuration, for saving to flash */
|
||||||
|
typedef struct _meshtastic_Nau7802Config {
|
||||||
|
/* The offset setting for the NAU7802 */
|
||||||
|
int32_t zeroOffset;
|
||||||
|
/* The calibration factor for the NAU7802 */
|
||||||
|
float calibrationFactor;
|
||||||
|
} meshtastic_Nau7802Config;
|
||||||
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
extern "C" {
|
extern "C" {
|
||||||
@@ -168,8 +193,9 @@ extern "C" {
|
|||||||
|
|
||||||
/* Helper constants for enums */
|
/* Helper constants for enums */
|
||||||
#define _meshtastic_TelemetrySensorType_MIN meshtastic_TelemetrySensorType_SENSOR_UNSET
|
#define _meshtastic_TelemetrySensorType_MIN meshtastic_TelemetrySensorType_SENSOR_UNSET
|
||||||
#define _meshtastic_TelemetrySensorType_MAX meshtastic_TelemetrySensorType_TSL25911FN
|
#define _meshtastic_TelemetrySensorType_MAX meshtastic_TelemetrySensorType_NAU7802
|
||||||
#define _meshtastic_TelemetrySensorType_ARRAYSIZE ((meshtastic_TelemetrySensorType)(meshtastic_TelemetrySensorType_TSL25911FN+1))
|
#define _meshtastic_TelemetrySensorType_ARRAYSIZE ((meshtastic_TelemetrySensorType)(meshtastic_TelemetrySensorType_NAU7802+1))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -179,15 +205,17 @@ extern "C" {
|
|||||||
|
|
||||||
/* Initializer values for message structs */
|
/* Initializer values for message structs */
|
||||||
#define meshtastic_DeviceMetrics_init_default {0, 0, 0, 0, 0}
|
#define meshtastic_DeviceMetrics_init_default {0, 0, 0, 0, 0}
|
||||||
#define meshtastic_EnvironmentMetrics_init_default {0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
|
#define meshtastic_EnvironmentMetrics_init_default {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
|
||||||
#define meshtastic_PowerMetrics_init_default {0, 0, 0, 0, 0, 0}
|
#define meshtastic_PowerMetrics_init_default {0, 0, 0, 0, 0, 0}
|
||||||
#define meshtastic_AirQualityMetrics_init_default {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
|
#define meshtastic_AirQualityMetrics_init_default {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
|
||||||
#define meshtastic_Telemetry_init_default {0, 0, {meshtastic_DeviceMetrics_init_default}}
|
#define meshtastic_Telemetry_init_default {0, 0, {meshtastic_DeviceMetrics_init_default}}
|
||||||
|
#define meshtastic_Nau7802Config_init_default {0, 0}
|
||||||
#define meshtastic_DeviceMetrics_init_zero {0, 0, 0, 0, 0}
|
#define meshtastic_DeviceMetrics_init_zero {0, 0, 0, 0, 0}
|
||||||
#define meshtastic_EnvironmentMetrics_init_zero {0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
|
#define meshtastic_EnvironmentMetrics_init_zero {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
|
||||||
#define meshtastic_PowerMetrics_init_zero {0, 0, 0, 0, 0, 0}
|
#define meshtastic_PowerMetrics_init_zero {0, 0, 0, 0, 0, 0}
|
||||||
#define meshtastic_AirQualityMetrics_init_zero {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
|
#define meshtastic_AirQualityMetrics_init_zero {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
|
||||||
#define meshtastic_Telemetry_init_zero {0, 0, {meshtastic_DeviceMetrics_init_zero}}
|
#define meshtastic_Telemetry_init_zero {0, 0, {meshtastic_DeviceMetrics_init_zero}}
|
||||||
|
#define meshtastic_Nau7802Config_init_zero {0, 0}
|
||||||
|
|
||||||
/* Field tags (for use in manual encoding/decoding) */
|
/* Field tags (for use in manual encoding/decoding) */
|
||||||
#define meshtastic_DeviceMetrics_battery_level_tag 1
|
#define meshtastic_DeviceMetrics_battery_level_tag 1
|
||||||
@@ -205,6 +233,11 @@ extern "C" {
|
|||||||
#define meshtastic_EnvironmentMetrics_distance_tag 8
|
#define meshtastic_EnvironmentMetrics_distance_tag 8
|
||||||
#define meshtastic_EnvironmentMetrics_lux_tag 9
|
#define meshtastic_EnvironmentMetrics_lux_tag 9
|
||||||
#define meshtastic_EnvironmentMetrics_white_lux_tag 10
|
#define meshtastic_EnvironmentMetrics_white_lux_tag 10
|
||||||
|
#define meshtastic_EnvironmentMetrics_ir_lux_tag 11
|
||||||
|
#define meshtastic_EnvironmentMetrics_uv_lux_tag 12
|
||||||
|
#define meshtastic_EnvironmentMetrics_wind_direction_tag 13
|
||||||
|
#define meshtastic_EnvironmentMetrics_wind_speed_tag 14
|
||||||
|
#define meshtastic_EnvironmentMetrics_weight_tag 15
|
||||||
#define meshtastic_PowerMetrics_ch1_voltage_tag 1
|
#define meshtastic_PowerMetrics_ch1_voltage_tag 1
|
||||||
#define meshtastic_PowerMetrics_ch1_current_tag 2
|
#define meshtastic_PowerMetrics_ch1_current_tag 2
|
||||||
#define meshtastic_PowerMetrics_ch2_voltage_tag 3
|
#define meshtastic_PowerMetrics_ch2_voltage_tag 3
|
||||||
@@ -228,6 +261,8 @@ extern "C" {
|
|||||||
#define meshtastic_Telemetry_environment_metrics_tag 3
|
#define meshtastic_Telemetry_environment_metrics_tag 3
|
||||||
#define meshtastic_Telemetry_air_quality_metrics_tag 4
|
#define meshtastic_Telemetry_air_quality_metrics_tag 4
|
||||||
#define meshtastic_Telemetry_power_metrics_tag 5
|
#define meshtastic_Telemetry_power_metrics_tag 5
|
||||||
|
#define meshtastic_Nau7802Config_zeroOffset_tag 1
|
||||||
|
#define meshtastic_Nau7802Config_calibrationFactor_tag 2
|
||||||
|
|
||||||
/* Struct field encoding specification for nanopb */
|
/* Struct field encoding specification for nanopb */
|
||||||
#define meshtastic_DeviceMetrics_FIELDLIST(X, a) \
|
#define meshtastic_DeviceMetrics_FIELDLIST(X, a) \
|
||||||
@@ -249,7 +284,12 @@ X(a, STATIC, SINGULAR, FLOAT, current, 6) \
|
|||||||
X(a, STATIC, SINGULAR, UINT32, iaq, 7) \
|
X(a, STATIC, SINGULAR, UINT32, iaq, 7) \
|
||||||
X(a, STATIC, SINGULAR, FLOAT, distance, 8) \
|
X(a, STATIC, SINGULAR, FLOAT, distance, 8) \
|
||||||
X(a, STATIC, SINGULAR, FLOAT, lux, 9) \
|
X(a, STATIC, SINGULAR, FLOAT, lux, 9) \
|
||||||
X(a, STATIC, SINGULAR, FLOAT, white_lux, 10)
|
X(a, STATIC, SINGULAR, FLOAT, white_lux, 10) \
|
||||||
|
X(a, STATIC, SINGULAR, FLOAT, ir_lux, 11) \
|
||||||
|
X(a, STATIC, SINGULAR, FLOAT, uv_lux, 12) \
|
||||||
|
X(a, STATIC, SINGULAR, UINT32, wind_direction, 13) \
|
||||||
|
X(a, STATIC, SINGULAR, FLOAT, wind_speed, 14) \
|
||||||
|
X(a, STATIC, SINGULAR, FLOAT, weight, 15)
|
||||||
#define meshtastic_EnvironmentMetrics_CALLBACK NULL
|
#define meshtastic_EnvironmentMetrics_CALLBACK NULL
|
||||||
#define meshtastic_EnvironmentMetrics_DEFAULT NULL
|
#define meshtastic_EnvironmentMetrics_DEFAULT NULL
|
||||||
|
|
||||||
@@ -292,11 +332,18 @@ X(a, STATIC, ONEOF, MESSAGE, (variant,power_metrics,variant.power_metrics)
|
|||||||
#define meshtastic_Telemetry_variant_air_quality_metrics_MSGTYPE meshtastic_AirQualityMetrics
|
#define meshtastic_Telemetry_variant_air_quality_metrics_MSGTYPE meshtastic_AirQualityMetrics
|
||||||
#define meshtastic_Telemetry_variant_power_metrics_MSGTYPE meshtastic_PowerMetrics
|
#define meshtastic_Telemetry_variant_power_metrics_MSGTYPE meshtastic_PowerMetrics
|
||||||
|
|
||||||
|
#define meshtastic_Nau7802Config_FIELDLIST(X, a) \
|
||||||
|
X(a, STATIC, SINGULAR, INT32, zeroOffset, 1) \
|
||||||
|
X(a, STATIC, SINGULAR, FLOAT, calibrationFactor, 2)
|
||||||
|
#define meshtastic_Nau7802Config_CALLBACK NULL
|
||||||
|
#define meshtastic_Nau7802Config_DEFAULT NULL
|
||||||
|
|
||||||
extern const pb_msgdesc_t meshtastic_DeviceMetrics_msg;
|
extern const pb_msgdesc_t meshtastic_DeviceMetrics_msg;
|
||||||
extern const pb_msgdesc_t meshtastic_EnvironmentMetrics_msg;
|
extern const pb_msgdesc_t meshtastic_EnvironmentMetrics_msg;
|
||||||
extern const pb_msgdesc_t meshtastic_PowerMetrics_msg;
|
extern const pb_msgdesc_t meshtastic_PowerMetrics_msg;
|
||||||
extern const pb_msgdesc_t meshtastic_AirQualityMetrics_msg;
|
extern const pb_msgdesc_t meshtastic_AirQualityMetrics_msg;
|
||||||
extern const pb_msgdesc_t meshtastic_Telemetry_msg;
|
extern const pb_msgdesc_t meshtastic_Telemetry_msg;
|
||||||
|
extern const pb_msgdesc_t meshtastic_Nau7802Config_msg;
|
||||||
|
|
||||||
/* Defines for backwards compatibility with code written before nanopb-0.4.0 */
|
/* Defines for backwards compatibility with code written before nanopb-0.4.0 */
|
||||||
#define meshtastic_DeviceMetrics_fields &meshtastic_DeviceMetrics_msg
|
#define meshtastic_DeviceMetrics_fields &meshtastic_DeviceMetrics_msg
|
||||||
@@ -304,14 +351,16 @@ extern const pb_msgdesc_t meshtastic_Telemetry_msg;
|
|||||||
#define meshtastic_PowerMetrics_fields &meshtastic_PowerMetrics_msg
|
#define meshtastic_PowerMetrics_fields &meshtastic_PowerMetrics_msg
|
||||||
#define meshtastic_AirQualityMetrics_fields &meshtastic_AirQualityMetrics_msg
|
#define meshtastic_AirQualityMetrics_fields &meshtastic_AirQualityMetrics_msg
|
||||||
#define meshtastic_Telemetry_fields &meshtastic_Telemetry_msg
|
#define meshtastic_Telemetry_fields &meshtastic_Telemetry_msg
|
||||||
|
#define meshtastic_Nau7802Config_fields &meshtastic_Nau7802Config_msg
|
||||||
|
|
||||||
/* Maximum encoded size of messages (where known) */
|
/* Maximum encoded size of messages (where known) */
|
||||||
#define MESHTASTIC_MESHTASTIC_TELEMETRY_PB_H_MAX_SIZE meshtastic_Telemetry_size
|
#define MESHTASTIC_MESHTASTIC_TELEMETRY_PB_H_MAX_SIZE meshtastic_Telemetry_size
|
||||||
#define meshtastic_AirQualityMetrics_size 72
|
#define meshtastic_AirQualityMetrics_size 72
|
||||||
#define meshtastic_DeviceMetrics_size 27
|
#define meshtastic_DeviceMetrics_size 27
|
||||||
#define meshtastic_EnvironmentMetrics_size 49
|
#define meshtastic_EnvironmentMetrics_size 73
|
||||||
|
#define meshtastic_Nau7802Config_size 16
|
||||||
#define meshtastic_PowerMetrics_size 30
|
#define meshtastic_PowerMetrics_size 30
|
||||||
#define meshtastic_Telemetry_size 79
|
#define meshtastic_Telemetry_size 80
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
} /* extern "C" */
|
} /* extern "C" */
|
||||||
|
|||||||
@@ -108,8 +108,10 @@ static void onNetworkConnected()
|
|||||||
}
|
}
|
||||||
|
|
||||||
// FIXME this is kinda yucky, instead we should just have an observable for 'wifireconnected'
|
// FIXME this is kinda yucky, instead we should just have an observable for 'wifireconnected'
|
||||||
|
#ifndef MESHTASTIC_EXCLUDE_MQTT
|
||||||
if (mqtt)
|
if (mqtt)
|
||||||
mqtt->reconnect();
|
mqtt->reconnect();
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
static int32_t reconnectWiFi()
|
static int32_t reconnectWiFi()
|
||||||
|
|||||||
@@ -26,6 +26,11 @@
|
|||||||
#if !MESHTASTIC_EXCLUDE_GPS
|
#if !MESHTASTIC_EXCLUDE_GPS
|
||||||
#include "GPS.h"
|
#include "GPS.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#if MESHTASTIC_EXCLUDE_GPS
|
||||||
|
#include "modules/PositionModule.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
#if !defined(ARCH_PORTDUINO) && !defined(ARCH_STM32WL) && !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR
|
#if !defined(ARCH_PORTDUINO) && !defined(ARCH_STM32WL) && !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR
|
||||||
#include "AccelerometerThread.h"
|
#include "AccelerometerThread.h"
|
||||||
#endif
|
#endif
|
||||||
@@ -751,7 +756,9 @@ void AdminModule::handleGetDeviceConnectionStatus(const meshtastic_MeshPacket &r
|
|||||||
if (conn.wifi.status.is_connected) {
|
if (conn.wifi.status.is_connected) {
|
||||||
conn.wifi.rssi = WiFi.RSSI();
|
conn.wifi.rssi = WiFi.RSSI();
|
||||||
conn.wifi.status.ip_address = WiFi.localIP();
|
conn.wifi.status.ip_address = WiFi.localIP();
|
||||||
|
#ifndef MESHTASTIC_EXCLUDE_MQTT
|
||||||
conn.wifi.status.is_mqtt_connected = mqtt && mqtt->isConnectedDirectly();
|
conn.wifi.status.is_mqtt_connected = mqtt && mqtt->isConnectedDirectly();
|
||||||
|
#endif
|
||||||
conn.wifi.status.is_syslog_connected = false; // FIXME wire this up
|
conn.wifi.status.is_syslog_connected = false; // FIXME wire this up
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -87,6 +87,14 @@ void AtakPluginModule::alterReceivedProtobuf(meshtastic_MeshPacket &mp, meshtast
|
|||||||
compressed.payload_variant.chat.to);
|
compressed.payload_variant.chat.to);
|
||||||
LOG_DEBUG("Compressed chat to: %d bytes\n", length);
|
LOG_DEBUG("Compressed chat to: %d bytes\n", length);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (t->payload_variant.chat.has_to_callsign) {
|
||||||
|
compressed.payload_variant.chat.has_to_callsign = true;
|
||||||
|
length =
|
||||||
|
unishox2_compress_simple(t->payload_variant.chat.to_callsign, strlen(t->payload_variant.chat.to_callsign),
|
||||||
|
compressed.payload_variant.chat.to_callsign);
|
||||||
|
LOG_DEBUG("Compressed chat to_callsign: %d bytes\n", length);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
mp.decoded.payload.size = pb_encode_to_bytes(mp.decoded.payload.bytes, sizeof(mp.decoded.payload.bytes),
|
mp.decoded.payload.size = pb_encode_to_bytes(mp.decoded.payload.bytes, sizeof(mp.decoded.payload.bytes),
|
||||||
meshtastic_TAKPacket_fields, &compressed);
|
meshtastic_TAKPacket_fields, &compressed);
|
||||||
@@ -124,6 +132,14 @@ void AtakPluginModule::alterReceivedProtobuf(meshtastic_MeshPacket &mp, meshtast
|
|||||||
uncompressed.payload_variant.chat.to);
|
uncompressed.payload_variant.chat.to);
|
||||||
LOG_DEBUG("Decompressed chat to: %d bytes\n", length);
|
LOG_DEBUG("Decompressed chat to: %d bytes\n", length);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (t->payload_variant.chat.has_to_callsign) {
|
||||||
|
uncompressed.payload_variant.chat.has_to_callsign = true;
|
||||||
|
length =
|
||||||
|
unishox2_decompress_simple(t->payload_variant.chat.to_callsign, strlen(t->payload_variant.chat.to_callsign),
|
||||||
|
uncompressed.payload_variant.chat.to_callsign);
|
||||||
|
LOG_DEBUG("Decompressed chat to_callsign: %d bytes\n", length);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
decompressedCopy->decoded.payload.size =
|
decompressedCopy->decoded.payload.size =
|
||||||
pb_encode_to_bytes(decompressedCopy->decoded.payload.bytes, sizeof(decompressedCopy->decoded.payload),
|
pb_encode_to_bytes(decompressedCopy->decoded.payload.bytes, sizeof(decompressedCopy->decoded.payload),
|
||||||
|
|||||||
@@ -49,7 +49,7 @@ CannedMessageModule::CannedMessageModule()
|
|||||||
LOG_INFO("CannedMessageModule is enabled\n");
|
LOG_INFO("CannedMessageModule is enabled\n");
|
||||||
|
|
||||||
// T-Watch interface currently has no way to select destination type, so default to 'node'
|
// T-Watch interface currently has no way to select destination type, so default to 'node'
|
||||||
#ifdef T_WATCH_S3
|
#if defined(T_WATCH_S3) || defined(RAK14014)
|
||||||
this->destSelect = CANNED_MESSAGE_DESTINATION_TYPE_NODE;
|
this->destSelect = CANNED_MESSAGE_DESTINATION_TYPE_NODE;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@@ -75,7 +75,7 @@ int CannedMessageModule::splitConfiguredMessages()
|
|||||||
|
|
||||||
String messages = cannedMessageModuleConfig.messages;
|
String messages = cannedMessageModuleConfig.messages;
|
||||||
|
|
||||||
#ifdef T_WATCH_S3
|
#if defined(T_WATCH_S3) || defined(RAK14014)
|
||||||
String separator = messages.length() ? "|" : "";
|
String separator = messages.length() ? "|" : "";
|
||||||
|
|
||||||
messages = "[---- Free Text ----]" + separator + messages;
|
messages = "[---- Free Text ----]" + separator + messages;
|
||||||
@@ -144,7 +144,7 @@ int CannedMessageModule::handleInputEvent(const InputEvent *event)
|
|||||||
}
|
}
|
||||||
if (event->inputEvent == static_cast<char>(meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_SELECT)) {
|
if (event->inputEvent == static_cast<char>(meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_SELECT)) {
|
||||||
|
|
||||||
#ifdef T_WATCH_S3
|
#if defined(T_WATCH_S3) || defined(RAK14014)
|
||||||
if (this->currentMessageIndex == 0) {
|
if (this->currentMessageIndex == 0) {
|
||||||
this->runState = CANNED_MESSAGE_RUN_STATE_FREETEXT;
|
this->runState = CANNED_MESSAGE_RUN_STATE_FREETEXT;
|
||||||
|
|
||||||
@@ -170,7 +170,7 @@ int CannedMessageModule::handleInputEvent(const InputEvent *event)
|
|||||||
e.frameChanged = true;
|
e.frameChanged = true;
|
||||||
this->currentMessageIndex = -1;
|
this->currentMessageIndex = -1;
|
||||||
|
|
||||||
#ifndef T_WATCH_S3
|
#if !defined(T_WATCH_S3) && !defined(RAK14014)
|
||||||
this->freetext = ""; // clear freetext
|
this->freetext = ""; // clear freetext
|
||||||
this->cursor = 0;
|
this->cursor = 0;
|
||||||
this->destSelect = CANNED_MESSAGE_DESTINATION_TYPE_NONE;
|
this->destSelect = CANNED_MESSAGE_DESTINATION_TYPE_NONE;
|
||||||
@@ -183,7 +183,7 @@ int CannedMessageModule::handleInputEvent(const InputEvent *event)
|
|||||||
(event->inputEvent == static_cast<char>(meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_LEFT)) ||
|
(event->inputEvent == static_cast<char>(meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_LEFT)) ||
|
||||||
(event->inputEvent == static_cast<char>(meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_RIGHT))) {
|
(event->inputEvent == static_cast<char>(meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_RIGHT))) {
|
||||||
|
|
||||||
#ifdef T_WATCH_S3
|
#if defined(T_WATCH_S3) || defined(RAK14014)
|
||||||
if (event->inputEvent == static_cast<char>(meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_LEFT)) {
|
if (event->inputEvent == static_cast<char>(meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_LEFT)) {
|
||||||
this->payload = 0xb4;
|
this->payload = 0xb4;
|
||||||
} else if (event->inputEvent == static_cast<char>(meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_RIGHT)) {
|
} else if (event->inputEvent == static_cast<char>(meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_RIGHT)) {
|
||||||
@@ -283,7 +283,7 @@ int CannedMessageModule::handleInputEvent(const InputEvent *event)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef T_WATCH_S3
|
#if defined(T_WATCH_S3) || defined(RAK14014)
|
||||||
if (this->runState == CANNED_MESSAGE_RUN_STATE_FREETEXT) {
|
if (this->runState == CANNED_MESSAGE_RUN_STATE_FREETEXT) {
|
||||||
String keyTapped = keyForCoordinates(event->touchX, event->touchY);
|
String keyTapped = keyForCoordinates(event->touchX, event->touchY);
|
||||||
|
|
||||||
@@ -404,7 +404,7 @@ int32_t CannedMessageModule::runOnce()
|
|||||||
this->freetext = ""; // clear freetext
|
this->freetext = ""; // clear freetext
|
||||||
this->cursor = 0;
|
this->cursor = 0;
|
||||||
|
|
||||||
#ifndef T_WATCH_S3
|
#if !defined(T_WATCH_S3) && !defined(RAK14014)
|
||||||
this->destSelect = CANNED_MESSAGE_DESTINATION_TYPE_NONE;
|
this->destSelect = CANNED_MESSAGE_DESTINATION_TYPE_NONE;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@@ -417,7 +417,7 @@ int32_t CannedMessageModule::runOnce()
|
|||||||
this->freetext = ""; // clear freetext
|
this->freetext = ""; // clear freetext
|
||||||
this->cursor = 0;
|
this->cursor = 0;
|
||||||
|
|
||||||
#ifndef T_WATCH_S3
|
#if !defined(T_WATCH_S3) && !defined(RAK14014)
|
||||||
this->destSelect = CANNED_MESSAGE_DESTINATION_TYPE_NONE;
|
this->destSelect = CANNED_MESSAGE_DESTINATION_TYPE_NONE;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@@ -437,7 +437,7 @@ int32_t CannedMessageModule::runOnce()
|
|||||||
powerFSM.trigger(EVENT_PRESS);
|
powerFSM.trigger(EVENT_PRESS);
|
||||||
return INT32_MAX;
|
return INT32_MAX;
|
||||||
} else {
|
} else {
|
||||||
#ifdef T_WATCH_S3
|
#if defined(T_WATCH_S3) || defined(RAK14014)
|
||||||
sendText(this->dest, indexChannels[this->channel], this->messages[this->currentMessageIndex], true);
|
sendText(this->dest, indexChannels[this->channel], this->messages[this->currentMessageIndex], true);
|
||||||
#else
|
#else
|
||||||
sendText(NODENUM_BROADCAST, channels.getPrimaryIndex(), this->messages[this->currentMessageIndex], true);
|
sendText(NODENUM_BROADCAST, channels.getPrimaryIndex(), this->messages[this->currentMessageIndex], true);
|
||||||
@@ -454,7 +454,7 @@ int32_t CannedMessageModule::runOnce()
|
|||||||
this->freetext = ""; // clear freetext
|
this->freetext = ""; // clear freetext
|
||||||
this->cursor = 0;
|
this->cursor = 0;
|
||||||
|
|
||||||
#ifndef T_WATCH_S3
|
#if !defined(T_WATCH_S3) && !defined(RAK14014)
|
||||||
this->destSelect = CANNED_MESSAGE_DESTINATION_TYPE_NONE;
|
this->destSelect = CANNED_MESSAGE_DESTINATION_TYPE_NONE;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@@ -471,7 +471,7 @@ int32_t CannedMessageModule::runOnce()
|
|||||||
this->freetext = ""; // clear freetext
|
this->freetext = ""; // clear freetext
|
||||||
this->cursor = 0;
|
this->cursor = 0;
|
||||||
|
|
||||||
#ifndef T_WATCH_S3
|
#if !defined(T_WATCH_S3) && !defined(RAK14014)
|
||||||
this->destSelect = CANNED_MESSAGE_DESTINATION_TYPE_NONE;
|
this->destSelect = CANNED_MESSAGE_DESTINATION_TYPE_NONE;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@@ -484,7 +484,7 @@ int32_t CannedMessageModule::runOnce()
|
|||||||
this->freetext = ""; // clear freetext
|
this->freetext = ""; // clear freetext
|
||||||
this->cursor = 0;
|
this->cursor = 0;
|
||||||
|
|
||||||
#ifndef T_WATCH_S3
|
#if !defined(T_WATCH_S3) && !defined(RAK14014)
|
||||||
this->destSelect = CANNED_MESSAGE_DESTINATION_TYPE_NONE;
|
this->destSelect = CANNED_MESSAGE_DESTINATION_TYPE_NONE;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@@ -714,7 +714,7 @@ void CannedMessageModule::showTemporaryMessage(const String &message)
|
|||||||
setIntervalFromNow(2000);
|
setIntervalFromNow(2000);
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef T_WATCH_S3
|
#if defined(T_WATCH_S3) || defined(RAK14014)
|
||||||
|
|
||||||
String CannedMessageModule::keyForCoordinates(uint x, uint y)
|
String CannedMessageModule::keyForCoordinates(uint x, uint y)
|
||||||
{
|
{
|
||||||
@@ -949,7 +949,7 @@ void CannedMessageModule::drawFrame(OLEDDisplay *display, OLEDDisplayUiState *st
|
|||||||
display->drawString(10 + x, 0 + y + FONT_HEIGHT_SMALL, "Canned Message\nModule disabled.");
|
display->drawString(10 + x, 0 + y + FONT_HEIGHT_SMALL, "Canned Message\nModule disabled.");
|
||||||
} else if (cannedMessageModule->runState == CANNED_MESSAGE_RUN_STATE_FREETEXT) {
|
} else if (cannedMessageModule->runState == CANNED_MESSAGE_RUN_STATE_FREETEXT) {
|
||||||
|
|
||||||
#ifdef T_WATCH_S3
|
#if defined(T_WATCH_S3) || defined(RAK14014)
|
||||||
drawKeyboard(display, state, 0, 0);
|
drawKeyboard(display, state, 0, 0);
|
||||||
#else
|
#else
|
||||||
|
|
||||||
|
|||||||
@@ -98,7 +98,7 @@ class CannedMessageModule : public SinglePortModule, public Observable<const UIF
|
|||||||
int getNextIndex();
|
int getNextIndex();
|
||||||
int getPrevIndex();
|
int getPrevIndex();
|
||||||
|
|
||||||
#ifdef T_WATCH_S3
|
#if defined(T_WATCH_S3) || defined(RAK14014)
|
||||||
void drawKeyboard(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y);
|
void drawKeyboard(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y);
|
||||||
String keyForCoordinates(uint x, uint y);
|
String keyForCoordinates(uint x, uint y);
|
||||||
bool shift = false;
|
bool shift = false;
|
||||||
@@ -150,7 +150,7 @@ class CannedMessageModule : public SinglePortModule, public Observable<const UIF
|
|||||||
unsigned long lastTouchMillis = 0;
|
unsigned long lastTouchMillis = 0;
|
||||||
String temporaryMessage;
|
String temporaryMessage;
|
||||||
|
|
||||||
#ifdef T_WATCH_S3
|
#if defined(T_WATCH_S3) || defined(RAK14014)
|
||||||
Letter keyboard[2][4][10] = {{{{"Q", 20, 0, 0, 0, 0},
|
Letter keyboard[2][4][10] = {{{{"Q", 20, 0, 0, 0, 0},
|
||||||
{"W", 22, 0, 0, 0, 0},
|
{"W", 22, 0, 0, 0, 0},
|
||||||
{"E", 17, 0, 0, 0, 0},
|
{"E", 17, 0, 0, 0, 0},
|
||||||
|
|||||||
@@ -0,0 +1,95 @@
|
|||||||
|
#if !MESHTASTIC_EXCLUDE_DROPZONE
|
||||||
|
|
||||||
|
#include "DropzoneModule.h"
|
||||||
|
#include "MeshService.h"
|
||||||
|
#include "configuration.h"
|
||||||
|
#include "gps/GeoCoord.h"
|
||||||
|
#include "gps/RTC.h"
|
||||||
|
#include "main.h"
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
|
|
||||||
|
#include "modules/Telemetry/Sensor/DFRobotLarkSensor.h"
|
||||||
|
#include "modules/Telemetry/UnitConversions.h"
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
DropzoneModule *dropzoneModule;
|
||||||
|
|
||||||
|
int32_t DropzoneModule::runOnce()
|
||||||
|
{
|
||||||
|
// Send on a 5 second delay from receiving the matching request
|
||||||
|
if (startSendConditions != 0 && (startSendConditions + 5000U) < millis()) {
|
||||||
|
service.sendToMesh(sendConditions(), RX_SRC_LOCAL);
|
||||||
|
startSendConditions = 0;
|
||||||
|
}
|
||||||
|
// Run every second to check if we need to send conditions
|
||||||
|
return 1000;
|
||||||
|
}
|
||||||
|
|
||||||
|
ProcessMessage DropzoneModule::handleReceived(const meshtastic_MeshPacket &mp)
|
||||||
|
{
|
||||||
|
auto &p = mp.decoded;
|
||||||
|
char matchCompare[54];
|
||||||
|
auto incomingMessage = reinterpret_cast<const char *>(p.payload.bytes);
|
||||||
|
sprintf(matchCompare, "%s conditions", owner.short_name);
|
||||||
|
if (strncasecmp(incomingMessage, matchCompare, strlen(matchCompare)) == 0) {
|
||||||
|
LOG_DEBUG("Received dropzone conditions request\n");
|
||||||
|
startSendConditions = millis();
|
||||||
|
}
|
||||||
|
|
||||||
|
sprintf(matchCompare, "%s conditions", owner.long_name);
|
||||||
|
if (strncasecmp(incomingMessage, matchCompare, strlen(matchCompare)) == 0) {
|
||||||
|
LOG_DEBUG("Received dropzone conditions request\n");
|
||||||
|
startSendConditions = millis();
|
||||||
|
}
|
||||||
|
return ProcessMessage::CONTINUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
meshtastic_MeshPacket *DropzoneModule::sendConditions()
|
||||||
|
{
|
||||||
|
char replyStr[200];
|
||||||
|
/*
|
||||||
|
CLOSED @ {HH:MM:SS}z
|
||||||
|
Wind 2 kts @ 125°
|
||||||
|
29.25 inHg 72°C
|
||||||
|
*/
|
||||||
|
uint32_t rtc_sec = getValidTime(RTCQuality::RTCQualityDevice, true);
|
||||||
|
int hour = 0, min = 0, sec = 0;
|
||||||
|
if (rtc_sec > 0) {
|
||||||
|
long hms = rtc_sec % SEC_PER_DAY;
|
||||||
|
hms = (hms + SEC_PER_DAY) % SEC_PER_DAY;
|
||||||
|
|
||||||
|
hour = hms / SEC_PER_HOUR;
|
||||||
|
min = (hms % SEC_PER_HOUR) / SEC_PER_MIN;
|
||||||
|
sec = (hms % SEC_PER_HOUR) % SEC_PER_MIN;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if the dropzone is open or closed by reading the analog pin
|
||||||
|
// If pin is connected to GND (below 100 should be lower than floating voltage),
|
||||||
|
// the dropzone is open
|
||||||
|
auto dropzoneStatus = analogRead(A1) < 100 ? "OPEN" : "CLOSED";
|
||||||
|
auto reply = allocDataPacket();
|
||||||
|
|
||||||
|
auto node = nodeDB->getMeshNode(nodeDB->getNodeNum());
|
||||||
|
if (sensor.hasSensor()) {
|
||||||
|
meshtastic_Telemetry telemetry = meshtastic_Telemetry_init_zero;
|
||||||
|
sensor.getMetrics(&telemetry);
|
||||||
|
auto windSpeed = UnitConversions::MetersPerSecondToKnots(telemetry.variant.environment_metrics.wind_speed);
|
||||||
|
auto windDirection = telemetry.variant.environment_metrics.wind_direction;
|
||||||
|
auto temp = telemetry.variant.environment_metrics.temperature;
|
||||||
|
auto baro = UnitConversions::HectoPascalToInchesOfMercury(telemetry.variant.environment_metrics.barometric_pressure);
|
||||||
|
sprintf(replyStr, "%s @ %02d:%02d:%02dz\nWind %.2f kts @ %d°\nBaro %.2f inHg %.2f°C", dropzoneStatus, hour, min, sec,
|
||||||
|
windSpeed, windDirection, baro, temp);
|
||||||
|
} else {
|
||||||
|
LOG_ERROR("No sensor found\n");
|
||||||
|
sprintf(replyStr, "%s @ %02d:%02d:%02d\nNo sensor found", dropzoneStatus, hour, min, sec);
|
||||||
|
}
|
||||||
|
LOG_DEBUG("Conditions reply: %s\n", replyStr);
|
||||||
|
reply->decoded.payload.size = strlen(replyStr); // You must specify how many bytes are in the reply
|
||||||
|
memcpy(reply->decoded.payload.bytes, replyStr, reply->decoded.payload.size);
|
||||||
|
|
||||||
|
return reply;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
#pragma once
|
||||||
|
#if !MESHTASTIC_EXCLUDE_DROPZONE
|
||||||
|
#include "SinglePortModule.h"
|
||||||
|
#include "modules/Telemetry/Sensor/DFRobotLarkSensor.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An example module that replies to a message with the current conditions
|
||||||
|
* and status at the dropzone when it receives a text message mentioning it's name followed by "conditions"
|
||||||
|
*/
|
||||||
|
class DropzoneModule : public SinglePortModule, private concurrency::OSThread
|
||||||
|
{
|
||||||
|
DFRobotLarkSensor sensor;
|
||||||
|
|
||||||
|
public:
|
||||||
|
/** Constructor
|
||||||
|
* name is for debugging output
|
||||||
|
*/
|
||||||
|
DropzoneModule() : SinglePortModule("dropzone", meshtastic_PortNum_TEXT_MESSAGE_APP), concurrency::OSThread("DropzoneModule")
|
||||||
|
{
|
||||||
|
// Set up the analog pin for reading the dropzone status
|
||||||
|
pinMode(PIN_A1, INPUT);
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual int32_t runOnce() override;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
/** Called to handle a particular incoming message
|
||||||
|
*/
|
||||||
|
virtual ProcessMessage handleReceived(const meshtastic_MeshPacket &mp) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
meshtastic_MeshPacket *sendConditions();
|
||||||
|
uint32_t startSendConditions = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
extern DropzoneModule *dropzoneModule;
|
||||||
|
#endif
|
||||||
@@ -70,6 +70,11 @@
|
|||||||
#include "modules/SerialModule.h"
|
#include "modules/SerialModule.h"
|
||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#if !MESHTASTIC_EXCLUDE_DROPZONE
|
||||||
|
#include "modules/DropzoneModule.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create module instances here. If you are adding a new module, you must 'new' it here (or somewhere else)
|
* Create module instances here. If you are adding a new module, you must 'new' it here (or somewhere else)
|
||||||
*/
|
*/
|
||||||
@@ -100,6 +105,10 @@ void setupModules()
|
|||||||
#if !MESHTASTIC_EXCLUDE_ATAK
|
#if !MESHTASTIC_EXCLUDE_ATAK
|
||||||
atakPluginModule = new AtakPluginModule();
|
atakPluginModule = new AtakPluginModule();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#if !MESHTASTIC_EXCLUDE_DROPZONE
|
||||||
|
dropzoneModule = new DropzoneModule();
|
||||||
|
#endif
|
||||||
// Note: if the rest of meshtastic doesn't need to explicitly use your module, you do not need to assign the instance
|
// Note: if the rest of meshtastic doesn't need to explicitly use your module, you do not need to assign the instance
|
||||||
// to a global variable.
|
// to a global variable.
|
||||||
|
|
||||||
|
|||||||
@@ -58,10 +58,15 @@ void NodeInfoModule::sendOurNodeInfo(NodeNum dest, bool wantReplies, uint8_t cha
|
|||||||
|
|
||||||
meshtastic_MeshPacket *NodeInfoModule::allocReply()
|
meshtastic_MeshPacket *NodeInfoModule::allocReply()
|
||||||
{
|
{
|
||||||
|
if (!airTime->isTxAllowedChannelUtil(false)) {
|
||||||
|
ignoreRequest = true; // Mark it as ignored for MeshModule
|
||||||
|
LOG_DEBUG("Skip sending NodeInfo due to > 40 percent channel util.\n");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
uint32_t now = millis();
|
uint32_t now = millis();
|
||||||
// If we sent our NodeInfo less than 5 min. ago, don't send it again as it may be still underway.
|
// If we sent our NodeInfo less than 5 min. ago, don't send it again as it may be still underway.
|
||||||
if (lastSentToMesh && (now - lastSentToMesh) < (5 * 60 * 1000)) {
|
if (lastSentToMesh && (now - lastSentToMesh) < (5 * 60 * 1000)) {
|
||||||
LOG_DEBUG("Sending NodeInfo will be ignored since we just sent it.\n");
|
LOG_DEBUG("Skip sending NodeInfo since we just sent it less than 5 minutes ago.\n");
|
||||||
ignoreRequest = true; // Mark it as ignored for MeshModule
|
ignoreRequest = true; // Mark it as ignored for MeshModule
|
||||||
return NULL;
|
return NULL;
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -55,6 +55,15 @@ bool PositionModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp, mes
|
|||||||
isLocal = true;
|
isLocal = true;
|
||||||
if (config.position.fixed_position) {
|
if (config.position.fixed_position) {
|
||||||
LOG_DEBUG("Ignore incoming position update from myself except for time, because position.fixed_position is true\n");
|
LOG_DEBUG("Ignore incoming position update from myself except for time, because position.fixed_position is true\n");
|
||||||
|
|
||||||
|
#ifdef T_WATCH_S3
|
||||||
|
// Since we return early if position.fixed_position is true, set the T-Watch's RTC to the time received from the
|
||||||
|
// client device here
|
||||||
|
if (p.time && channels.getByIndex(mp.channel).role == meshtastic_Channel_Role_PRIMARY) {
|
||||||
|
trySetRtc(p, isLocal, true);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
nodeDB->setLocalPosition(p, true);
|
nodeDB->setLocalPosition(p, true);
|
||||||
return false;
|
return false;
|
||||||
} else {
|
} else {
|
||||||
@@ -71,8 +80,17 @@ bool PositionModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp, mes
|
|||||||
p.time);
|
p.time);
|
||||||
|
|
||||||
if (p.time && channels.getByIndex(mp.channel).role == meshtastic_Channel_Role_PRIMARY) {
|
if (p.time && channels.getByIndex(mp.channel).role == meshtastic_Channel_Role_PRIMARY) {
|
||||||
|
bool force = false;
|
||||||
|
|
||||||
|
#ifdef T_WATCH_S3
|
||||||
|
// The T-Watch appears to "pause" its RTC when shut down, such that the time it reads upon powering on is the same as when
|
||||||
|
// it was shut down. So we need to force the update here, since otherwise RTC::perhapsSetRTC will ignore it because it
|
||||||
|
// will always be an equivalent or lesser RTCQuality (RTCQualityNTP or RTCQualityNet).
|
||||||
|
force = true;
|
||||||
|
#endif
|
||||||
|
|
||||||
// Set from phone RTC Quality to RTCQualityNTP since it should be approximately so
|
// Set from phone RTC Quality to RTCQualityNTP since it should be approximately so
|
||||||
trySetRtc(p, isLocal);
|
trySetRtc(p, isLocal, force);
|
||||||
}
|
}
|
||||||
|
|
||||||
nodeDB->updatePosition(getFrom(&mp), p);
|
nodeDB->updatePosition(getFrom(&mp), p);
|
||||||
@@ -104,14 +122,14 @@ void PositionModule::alterReceivedProtobuf(meshtastic_MeshPacket &mp, meshtastic
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void PositionModule::trySetRtc(meshtastic_Position p, bool isLocal)
|
void PositionModule::trySetRtc(meshtastic_Position p, bool isLocal, bool forceUpdate)
|
||||||
{
|
{
|
||||||
struct timeval tv;
|
struct timeval tv;
|
||||||
uint32_t secs = p.time;
|
uint32_t secs = p.time;
|
||||||
|
|
||||||
tv.tv_sec = secs;
|
tv.tv_sec = secs;
|
||||||
tv.tv_usec = 0;
|
tv.tv_usec = 0;
|
||||||
perhapsSetRTC(isLocal ? RTCQualityNTP : RTCQualityFromNet, &tv);
|
perhapsSetRTC(isLocal ? RTCQualityNTP : RTCQualityFromNet, &tv, forceUpdate);
|
||||||
}
|
}
|
||||||
|
|
||||||
meshtastic_MeshPacket *PositionModule::allocReply()
|
meshtastic_MeshPacket *PositionModule::allocReply()
|
||||||
@@ -191,13 +209,13 @@ meshtastic_MeshPacket *PositionModule::allocReply()
|
|||||||
p.ground_speed = localPosition.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
|
// 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
|
// nodes shouldn't trust it anyways) Note: we allow a device with a local GPS or NTP to include the time, so that devices
|
||||||
// devices can get time.
|
// without can get time.
|
||||||
if (getRTCQuality() < RTCQualityDevice) {
|
if (getRTCQuality() < RTCQualityNTP) {
|
||||||
LOG_INFO("Stripping time %u from position send\n", p.time);
|
LOG_INFO("Stripping time %u from position send\n", p.time);
|
||||||
p.time = 0;
|
p.time = 0;
|
||||||
} else {
|
} else {
|
||||||
p.time = getValidTime(RTCQualityDevice);
|
p.time = getValidTime(RTCQualityNTP);
|
||||||
LOG_INFO("Providing time to mesh %u\n", p.time);
|
LOG_INFO("Providing time to mesh %u\n", p.time);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -54,7 +54,7 @@ class PositionModule : public ProtobufModule<meshtastic_Position>, private concu
|
|||||||
private:
|
private:
|
||||||
struct SmartPosition getDistanceTraveledSinceLastSend(meshtastic_PositionLite currentPosition);
|
struct SmartPosition getDistanceTraveledSinceLastSend(meshtastic_PositionLite currentPosition);
|
||||||
meshtastic_MeshPacket *allocAtakPli();
|
meshtastic_MeshPacket *allocAtakPli();
|
||||||
void trySetRtc(meshtastic_Position p, bool isLocal);
|
void trySetRtc(meshtastic_Position p, bool isLocal, bool forceUpdate = false);
|
||||||
uint32_t precision;
|
uint32_t precision;
|
||||||
void sendLostAndFoundText();
|
void sendLostAndFoundText();
|
||||||
|
|
||||||
|
|||||||
@@ -92,7 +92,7 @@ bool AirQualityTelemetryModule::sendTelemetry(NodeNum dest, bool phoneOnly)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
meshtastic_Telemetry m;
|
meshtastic_Telemetry m = meshtastic_Telemetry_init_zero;
|
||||||
m.time = getTime();
|
m.time = getTime();
|
||||||
m.which_variant = meshtastic_Telemetry_air_quality_metrics_tag;
|
m.which_variant = meshtastic_Telemetry_air_quality_metrics_tag;
|
||||||
m.variant.air_quality_metrics.pm10_standard = data.pm10_standard;
|
m.variant.air_quality_metrics.pm10_standard = data.pm10_standard;
|
||||||
|
|||||||
@@ -64,7 +64,7 @@ meshtastic_MeshPacket *DeviceTelemetryModule::allocReply()
|
|||||||
|
|
||||||
meshtastic_Telemetry DeviceTelemetryModule::getDeviceTelemetry()
|
meshtastic_Telemetry DeviceTelemetryModule::getDeviceTelemetry()
|
||||||
{
|
{
|
||||||
meshtastic_Telemetry t;
|
meshtastic_Telemetry t = meshtastic_Telemetry_init_zero;
|
||||||
|
|
||||||
t.time = getTime();
|
t.time = getTime();
|
||||||
t.which_variant = meshtastic_Telemetry_device_metrics_tag;
|
t.which_variant = meshtastic_Telemetry_device_metrics_tag;
|
||||||
|
|||||||
@@ -18,16 +18,22 @@
|
|||||||
#include <OLEDDisplayUi.h>
|
#include <OLEDDisplayUi.h>
|
||||||
|
|
||||||
// Sensors
|
// Sensors
|
||||||
|
#include "Sensor/AHT10.h"
|
||||||
#include "Sensor/BME280Sensor.h"
|
#include "Sensor/BME280Sensor.h"
|
||||||
#include "Sensor/BME680Sensor.h"
|
#include "Sensor/BME680Sensor.h"
|
||||||
#include "Sensor/BMP085Sensor.h"
|
#include "Sensor/BMP085Sensor.h"
|
||||||
#include "Sensor/BMP280Sensor.h"
|
#include "Sensor/BMP280Sensor.h"
|
||||||
|
#include "Sensor/DFRobotLarkSensor.h"
|
||||||
#include "Sensor/LPS22HBSensor.h"
|
#include "Sensor/LPS22HBSensor.h"
|
||||||
#include "Sensor/MCP9808Sensor.h"
|
#include "Sensor/MCP9808Sensor.h"
|
||||||
|
#include "Sensor/MLX90632Sensor.h"
|
||||||
|
#include "Sensor/NAU7802Sensor.h"
|
||||||
|
#include "Sensor/OPT3001Sensor.h"
|
||||||
#include "Sensor/RCWL9620Sensor.h"
|
#include "Sensor/RCWL9620Sensor.h"
|
||||||
#include "Sensor/SHT31Sensor.h"
|
#include "Sensor/SHT31Sensor.h"
|
||||||
#include "Sensor/SHT4XSensor.h"
|
#include "Sensor/SHT4XSensor.h"
|
||||||
#include "Sensor/SHTC3Sensor.h"
|
#include "Sensor/SHTC3Sensor.h"
|
||||||
|
#include "Sensor/TSL2591Sensor.h"
|
||||||
#include "Sensor/VEML7700Sensor.h"
|
#include "Sensor/VEML7700Sensor.h"
|
||||||
|
|
||||||
BMP085Sensor bmp085Sensor;
|
BMP085Sensor bmp085Sensor;
|
||||||
@@ -39,8 +45,14 @@ SHTC3Sensor shtc3Sensor;
|
|||||||
LPS22HBSensor lps22hbSensor;
|
LPS22HBSensor lps22hbSensor;
|
||||||
SHT31Sensor sht31Sensor;
|
SHT31Sensor sht31Sensor;
|
||||||
VEML7700Sensor veml7700Sensor;
|
VEML7700Sensor veml7700Sensor;
|
||||||
|
TSL2591Sensor tsl2591Sensor;
|
||||||
|
OPT3001Sensor opt3001Sensor;
|
||||||
SHT4XSensor sht4xSensor;
|
SHT4XSensor sht4xSensor;
|
||||||
RCWL9620Sensor rcwl9620Sensor;
|
RCWL9620Sensor rcwl9620Sensor;
|
||||||
|
AHT10Sensor aht10Sensor;
|
||||||
|
MLX90632Sensor mlx90632Sensor;
|
||||||
|
DFRobotLarkSensor dfRobotLarkSensor;
|
||||||
|
NAU7802Sensor nau7802Sensor;
|
||||||
|
|
||||||
#define FAILED_STATE_SENSOR_READ_MULTIPLIER 10
|
#define FAILED_STATE_SENSOR_READ_MULTIPLIER 10
|
||||||
#define DISPLAY_RECEIVEID_MEASUREMENTS_ON_SCREEN true
|
#define DISPLAY_RECEIVEID_MEASUREMENTS_ON_SCREEN true
|
||||||
@@ -63,8 +75,8 @@ int32_t EnvironmentTelemetryModule::runOnce()
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
// moduleConfig.telemetry.environment_measurement_enabled = 1;
|
// moduleConfig.telemetry.environment_measurement_enabled = 1;
|
||||||
// moduleConfig.telemetry.environment_screen_enabled = 1;
|
// moduleConfig.telemetry.environment_screen_enabled = 1;
|
||||||
// moduleConfig.telemetry.environment_update_interval = 45;
|
// moduleConfig.telemetry.environment_update_interval = 15;
|
||||||
|
|
||||||
if (!(moduleConfig.telemetry.environment_measurement_enabled || moduleConfig.telemetry.environment_screen_enabled)) {
|
if (!(moduleConfig.telemetry.environment_measurement_enabled || moduleConfig.telemetry.environment_screen_enabled)) {
|
||||||
// If this module is not enabled, and the user doesn't want the display screen don't waste any OSThread time on it
|
// If this module is not enabled, and the user doesn't want the display screen don't waste any OSThread time on it
|
||||||
@@ -79,6 +91,8 @@ int32_t EnvironmentTelemetryModule::runOnce()
|
|||||||
LOG_INFO("Environment Telemetry: Initializing\n");
|
LOG_INFO("Environment Telemetry: Initializing\n");
|
||||||
// it's possible to have this module enabled, only for displaying values on the screen.
|
// it's possible to have this module enabled, only for displaying values on the screen.
|
||||||
// therefore, we should only enable the sensor loop if measurement is also enabled
|
// therefore, we should only enable the sensor loop if measurement is also enabled
|
||||||
|
if (dfRobotLarkSensor.hasSensor())
|
||||||
|
result = dfRobotLarkSensor.runOnce();
|
||||||
if (bmp085Sensor.hasSensor())
|
if (bmp085Sensor.hasSensor())
|
||||||
result = bmp085Sensor.runOnce();
|
result = bmp085Sensor.runOnce();
|
||||||
if (bmp280Sensor.hasSensor())
|
if (bmp280Sensor.hasSensor())
|
||||||
@@ -103,8 +117,18 @@ int32_t EnvironmentTelemetryModule::runOnce()
|
|||||||
result = ina260Sensor.runOnce();
|
result = ina260Sensor.runOnce();
|
||||||
if (veml7700Sensor.hasSensor())
|
if (veml7700Sensor.hasSensor())
|
||||||
result = veml7700Sensor.runOnce();
|
result = veml7700Sensor.runOnce();
|
||||||
|
if (tsl2591Sensor.hasSensor())
|
||||||
|
result = tsl2591Sensor.runOnce();
|
||||||
|
if (opt3001Sensor.hasSensor())
|
||||||
|
result = opt3001Sensor.runOnce();
|
||||||
if (rcwl9620Sensor.hasSensor())
|
if (rcwl9620Sensor.hasSensor())
|
||||||
result = rcwl9620Sensor.runOnce();
|
result = rcwl9620Sensor.runOnce();
|
||||||
|
if (aht10Sensor.hasSensor())
|
||||||
|
result = aht10Sensor.runOnce();
|
||||||
|
if (mlx90632Sensor.hasSensor())
|
||||||
|
result = mlx90632Sensor.runOnce();
|
||||||
|
if (nau7802Sensor.hasSensor())
|
||||||
|
result = nau7802Sensor.runOnce();
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
} else {
|
} else {
|
||||||
@@ -203,12 +227,18 @@ void EnvironmentTelemetryModule::drawFrame(OLEDDisplay *display, OLEDDisplayUiSt
|
|||||||
"Volt/Cur: " + String(lastMeasurement.variant.environment_metrics.voltage, 0) + "V / " +
|
"Volt/Cur: " + String(lastMeasurement.variant.environment_metrics.voltage, 0) + "V / " +
|
||||||
String(lastMeasurement.variant.environment_metrics.current, 0) + "mA");
|
String(lastMeasurement.variant.environment_metrics.current, 0) + "mA");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (lastMeasurement.variant.environment_metrics.iaq != 0) {
|
if (lastMeasurement.variant.environment_metrics.iaq != 0) {
|
||||||
display->drawString(x, y += fontHeight(FONT_SMALL), "IAQ: " + String(lastMeasurement.variant.environment_metrics.iaq));
|
display->drawString(x, y += fontHeight(FONT_SMALL), "IAQ: " + String(lastMeasurement.variant.environment_metrics.iaq));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (lastMeasurement.variant.environment_metrics.distance != 0)
|
if (lastMeasurement.variant.environment_metrics.distance != 0)
|
||||||
display->drawString(x, y += fontHeight(FONT_SMALL),
|
display->drawString(x, y += fontHeight(FONT_SMALL),
|
||||||
"Water Level: " + String(lastMeasurement.variant.environment_metrics.distance, 0) + "mm");
|
"Water Level: " + String(lastMeasurement.variant.environment_metrics.distance, 0) + "mm");
|
||||||
|
|
||||||
|
if (lastMeasurement.variant.environment_metrics.weight != 0)
|
||||||
|
display->drawString(x, y += fontHeight(FONT_SMALL),
|
||||||
|
"Weight: " + String(lastMeasurement.variant.environment_metrics.weight, 0) + "kg");
|
||||||
}
|
}
|
||||||
|
|
||||||
bool EnvironmentTelemetryModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp, meshtastic_Telemetry *t)
|
bool EnvironmentTelemetryModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp, meshtastic_Telemetry *t)
|
||||||
@@ -224,6 +254,11 @@ bool EnvironmentTelemetryModule::handleReceivedProtobuf(const meshtastic_MeshPac
|
|||||||
t->variant.environment_metrics.temperature);
|
t->variant.environment_metrics.temperature);
|
||||||
LOG_INFO("(Received from %s): voltage=%f, IAQ=%d, distance=%f, lux=%f\n", sender, t->variant.environment_metrics.voltage,
|
LOG_INFO("(Received from %s): voltage=%f, IAQ=%d, distance=%f, lux=%f\n", sender, t->variant.environment_metrics.voltage,
|
||||||
t->variant.environment_metrics.iaq, t->variant.environment_metrics.distance, t->variant.environment_metrics.lux);
|
t->variant.environment_metrics.iaq, t->variant.environment_metrics.distance, t->variant.environment_metrics.lux);
|
||||||
|
|
||||||
|
LOG_INFO("(Received from %s): wind speed=%fm/s, direction=%d degrees, weight=%fkg\n", sender,
|
||||||
|
t->variant.environment_metrics.wind_speed, t->variant.environment_metrics.wind_direction,
|
||||||
|
t->variant.environment_metrics.weight);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
// release previous packet before occupying a new spot
|
// release previous packet before occupying a new spot
|
||||||
if (lastMeasurementPacket != nullptr)
|
if (lastMeasurementPacket != nullptr)
|
||||||
@@ -237,12 +272,16 @@ bool EnvironmentTelemetryModule::handleReceivedProtobuf(const meshtastic_MeshPac
|
|||||||
|
|
||||||
bool EnvironmentTelemetryModule::sendTelemetry(NodeNum dest, bool phoneOnly)
|
bool EnvironmentTelemetryModule::sendTelemetry(NodeNum dest, bool phoneOnly)
|
||||||
{
|
{
|
||||||
meshtastic_Telemetry m;
|
meshtastic_Telemetry m = meshtastic_Telemetry_init_zero;
|
||||||
bool valid = true;
|
bool valid = true;
|
||||||
bool hasSensor = false;
|
bool hasSensor = false;
|
||||||
m.time = getTime();
|
m.time = getTime();
|
||||||
m.which_variant = meshtastic_Telemetry_environment_metrics_tag;
|
m.which_variant = meshtastic_Telemetry_environment_metrics_tag;
|
||||||
|
|
||||||
|
if (dfRobotLarkSensor.hasSensor()) {
|
||||||
|
valid = valid && dfRobotLarkSensor.getMetrics(&m);
|
||||||
|
hasSensor = true;
|
||||||
|
}
|
||||||
if (sht31Sensor.hasSensor()) {
|
if (sht31Sensor.hasSensor()) {
|
||||||
valid = valid && sht31Sensor.getMetrics(&m);
|
valid = valid && sht31Sensor.getMetrics(&m);
|
||||||
hasSensor = true;
|
hasSensor = true;
|
||||||
@@ -287,10 +326,39 @@ bool EnvironmentTelemetryModule::sendTelemetry(NodeNum dest, bool phoneOnly)
|
|||||||
valid = valid && veml7700Sensor.getMetrics(&m);
|
valid = valid && veml7700Sensor.getMetrics(&m);
|
||||||
hasSensor = true;
|
hasSensor = true;
|
||||||
}
|
}
|
||||||
|
if (tsl2591Sensor.hasSensor()) {
|
||||||
|
valid = valid && tsl2591Sensor.getMetrics(&m);
|
||||||
|
hasSensor = true;
|
||||||
|
}
|
||||||
|
if (opt3001Sensor.hasSensor()) {
|
||||||
|
valid = valid && opt3001Sensor.getMetrics(&m);
|
||||||
|
hasSensor = true;
|
||||||
|
}
|
||||||
|
if (mlx90632Sensor.hasSensor()) {
|
||||||
|
valid = valid && mlx90632Sensor.getMetrics(&m);
|
||||||
|
hasSensor = true;
|
||||||
|
}
|
||||||
if (rcwl9620Sensor.hasSensor()) {
|
if (rcwl9620Sensor.hasSensor()) {
|
||||||
valid = valid && rcwl9620Sensor.getMetrics(&m);
|
valid = valid && rcwl9620Sensor.getMetrics(&m);
|
||||||
hasSensor = true;
|
hasSensor = true;
|
||||||
}
|
}
|
||||||
|
if (nau7802Sensor.hasSensor()) {
|
||||||
|
valid = valid && nau7802Sensor.getMetrics(&m);
|
||||||
|
hasSensor = true;
|
||||||
|
}
|
||||||
|
if (aht10Sensor.hasSensor()) {
|
||||||
|
if (!bmp280Sensor.hasSensor()) {
|
||||||
|
valid = valid && aht10Sensor.getMetrics(&m);
|
||||||
|
hasSensor = true;
|
||||||
|
} else {
|
||||||
|
// prefer bmp280 temp if both sensors are present, fetch only humidity
|
||||||
|
meshtastic_Telemetry m_ahtx = meshtastic_Telemetry_init_zero;
|
||||||
|
LOG_INFO("AHTX0+BMP280 module detected: using temp from BMP280 and humy from AHTX0\n");
|
||||||
|
aht10Sensor.getMetrics(&m_ahtx);
|
||||||
|
m.variant.environment_metrics.relative_humidity = m_ahtx.variant.environment_metrics.relative_humidity;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
valid = valid && hasSensor;
|
valid = valid && hasSensor;
|
||||||
|
|
||||||
if (valid) {
|
if (valid) {
|
||||||
@@ -301,6 +369,9 @@ bool EnvironmentTelemetryModule::sendTelemetry(NodeNum dest, bool phoneOnly)
|
|||||||
LOG_INFO("(Sending): voltage=%f, IAQ=%d, distance=%f, lux=%f\n", m.variant.environment_metrics.voltage,
|
LOG_INFO("(Sending): voltage=%f, IAQ=%d, distance=%f, lux=%f\n", m.variant.environment_metrics.voltage,
|
||||||
m.variant.environment_metrics.iaq, m.variant.environment_metrics.distance, m.variant.environment_metrics.lux);
|
m.variant.environment_metrics.iaq, m.variant.environment_metrics.distance, m.variant.environment_metrics.lux);
|
||||||
|
|
||||||
|
LOG_INFO("(Sending): wind speed=%fm/s, direction=%d degrees, weight=%fkg\n", m.variant.environment_metrics.wind_speed,
|
||||||
|
m.variant.environment_metrics.wind_direction, m.variant.environment_metrics.weight);
|
||||||
|
|
||||||
sensor_read_error_count = 0;
|
sensor_read_error_count = 0;
|
||||||
|
|
||||||
meshtastic_MeshPacket *p = allocDataProtobuf(m);
|
meshtastic_MeshPacket *p = allocDataProtobuf(m);
|
||||||
@@ -332,4 +403,102 @@ bool EnvironmentTelemetryModule::sendTelemetry(NodeNum dest, bool phoneOnly)
|
|||||||
return valid;
|
return valid;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
AdminMessageHandleResult EnvironmentTelemetryModule::handleAdminMessageForModule(const meshtastic_MeshPacket &mp,
|
||||||
|
meshtastic_AdminMessage *request,
|
||||||
|
meshtastic_AdminMessage *response)
|
||||||
|
{
|
||||||
|
AdminMessageHandleResult result = AdminMessageHandleResult::NOT_HANDLED;
|
||||||
|
if (dfRobotLarkSensor.hasSensor()) {
|
||||||
|
result = dfRobotLarkSensor.handleAdminMessage(mp, request, response);
|
||||||
|
if (result != AdminMessageHandleResult::NOT_HANDLED)
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
if (sht31Sensor.hasSensor()) {
|
||||||
|
result = sht31Sensor.handleAdminMessage(mp, request, response);
|
||||||
|
if (result != AdminMessageHandleResult::NOT_HANDLED)
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
if (lps22hbSensor.hasSensor()) {
|
||||||
|
result = lps22hbSensor.handleAdminMessage(mp, request, response);
|
||||||
|
if (result != AdminMessageHandleResult::NOT_HANDLED)
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
if (shtc3Sensor.hasSensor()) {
|
||||||
|
result = shtc3Sensor.handleAdminMessage(mp, request, response);
|
||||||
|
if (result != AdminMessageHandleResult::NOT_HANDLED)
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
if (bmp085Sensor.hasSensor()) {
|
||||||
|
result = bmp085Sensor.handleAdminMessage(mp, request, response);
|
||||||
|
if (result != AdminMessageHandleResult::NOT_HANDLED)
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
if (bmp280Sensor.hasSensor()) {
|
||||||
|
result = bmp280Sensor.handleAdminMessage(mp, request, response);
|
||||||
|
if (result != AdminMessageHandleResult::NOT_HANDLED)
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
if (bme280Sensor.hasSensor()) {
|
||||||
|
result = bme280Sensor.handleAdminMessage(mp, request, response);
|
||||||
|
if (result != AdminMessageHandleResult::NOT_HANDLED)
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
if (bme680Sensor.hasSensor()) {
|
||||||
|
result = bme680Sensor.handleAdminMessage(mp, request, response);
|
||||||
|
if (result != AdminMessageHandleResult::NOT_HANDLED)
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
if (mcp9808Sensor.hasSensor()) {
|
||||||
|
result = mcp9808Sensor.handleAdminMessage(mp, request, response);
|
||||||
|
if (result != AdminMessageHandleResult::NOT_HANDLED)
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
if (ina219Sensor.hasSensor()) {
|
||||||
|
result = ina219Sensor.handleAdminMessage(mp, request, response);
|
||||||
|
if (result != AdminMessageHandleResult::NOT_HANDLED)
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
if (ina260Sensor.hasSensor()) {
|
||||||
|
result = ina260Sensor.handleAdminMessage(mp, request, response);
|
||||||
|
if (result != AdminMessageHandleResult::NOT_HANDLED)
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
if (veml7700Sensor.hasSensor()) {
|
||||||
|
result = veml7700Sensor.handleAdminMessage(mp, request, response);
|
||||||
|
if (result != AdminMessageHandleResult::NOT_HANDLED)
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
if (tsl2591Sensor.hasSensor()) {
|
||||||
|
result = tsl2591Sensor.handleAdminMessage(mp, request, response);
|
||||||
|
if (result != AdminMessageHandleResult::NOT_HANDLED)
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
if (opt3001Sensor.hasSensor()) {
|
||||||
|
result = opt3001Sensor.handleAdminMessage(mp, request, response);
|
||||||
|
if (result != AdminMessageHandleResult::NOT_HANDLED)
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
if (mlx90632Sensor.hasSensor()) {
|
||||||
|
result = mlx90632Sensor.handleAdminMessage(mp, request, response);
|
||||||
|
if (result != AdminMessageHandleResult::NOT_HANDLED)
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
if (rcwl9620Sensor.hasSensor()) {
|
||||||
|
result = rcwl9620Sensor.handleAdminMessage(mp, request, response);
|
||||||
|
if (result != AdminMessageHandleResult::NOT_HANDLED)
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
if (nau7802Sensor.hasSensor()) {
|
||||||
|
result = nau7802Sensor.handleAdminMessage(mp, request, response);
|
||||||
|
if (result != AdminMessageHandleResult::NOT_HANDLED)
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
if (aht10Sensor.hasSensor()) {
|
||||||
|
result = aht10Sensor.handleAdminMessage(mp, request, response);
|
||||||
|
if (result != AdminMessageHandleResult::NOT_HANDLED)
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
@@ -37,6 +37,10 @@ class EnvironmentTelemetryModule : private concurrency::OSThread, public Protobu
|
|||||||
*/
|
*/
|
||||||
bool sendTelemetry(NodeNum dest = NODENUM_BROADCAST, bool wantReplies = false);
|
bool sendTelemetry(NodeNum dest = NODENUM_BROADCAST, bool wantReplies = false);
|
||||||
|
|
||||||
|
virtual AdminMessageHandleResult handleAdminMessageForModule(const meshtastic_MeshPacket &mp,
|
||||||
|
meshtastic_AdminMessage *request,
|
||||||
|
meshtastic_AdminMessage *response) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
float CelsiusToFahrenheit(float c);
|
float CelsiusToFahrenheit(float c);
|
||||||
bool firstTime = 1;
|
bool firstTime = 1;
|
||||||
|
|||||||
@@ -165,7 +165,7 @@ bool PowerTelemetryModule::handleReceivedProtobuf(const meshtastic_MeshPacket &m
|
|||||||
|
|
||||||
bool PowerTelemetryModule::sendTelemetry(NodeNum dest, bool phoneOnly)
|
bool PowerTelemetryModule::sendTelemetry(NodeNum dest, bool phoneOnly)
|
||||||
{
|
{
|
||||||
meshtastic_Telemetry m;
|
meshtastic_Telemetry m = meshtastic_Telemetry_init_zero;
|
||||||
bool valid = false;
|
bool valid = false;
|
||||||
m.time = getTime();
|
m.time = getTime();
|
||||||
m.which_variant = meshtastic_Telemetry_power_metrics_tag;
|
m.which_variant = meshtastic_Telemetry_power_metrics_tag;
|
||||||
|
|||||||
@@ -0,0 +1,41 @@
|
|||||||
|
#include "configuration.h"
|
||||||
|
|
||||||
|
#if !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR
|
||||||
|
|
||||||
|
#include "../mesh/generated/meshtastic/telemetry.pb.h"
|
||||||
|
#include "AHT10.h"
|
||||||
|
#include "TelemetrySensor.h"
|
||||||
|
|
||||||
|
#include <Adafruit_AHTX0.h>
|
||||||
|
#include <typeinfo>
|
||||||
|
|
||||||
|
AHT10Sensor::AHT10Sensor() : TelemetrySensor(meshtastic_TelemetrySensorType_AHT10, "AHT10") {}
|
||||||
|
|
||||||
|
int32_t AHT10Sensor::runOnce()
|
||||||
|
{
|
||||||
|
LOG_INFO("Init sensor: %s\n", sensorName);
|
||||||
|
if (!hasSensor()) {
|
||||||
|
return DEFAULT_SENSOR_MINIMUM_WAIT_TIME_BETWEEN_READS;
|
||||||
|
}
|
||||||
|
aht10 = Adafruit_AHTX0();
|
||||||
|
status = aht10.begin(nodeTelemetrySensorsMap[sensorType].second, 0, nodeTelemetrySensorsMap[sensorType].first);
|
||||||
|
|
||||||
|
return initI2CSensor();
|
||||||
|
}
|
||||||
|
|
||||||
|
void AHT10Sensor::setup() {}
|
||||||
|
|
||||||
|
bool AHT10Sensor::getMetrics(meshtastic_Telemetry *measurement)
|
||||||
|
{
|
||||||
|
LOG_DEBUG("AHT10Sensor::getMetrics\n");
|
||||||
|
|
||||||
|
sensors_event_t humidity, temp;
|
||||||
|
aht10.getEvent(&humidity, &temp);
|
||||||
|
|
||||||
|
measurement->variant.environment_metrics.temperature = temp.temperature;
|
||||||
|
measurement->variant.environment_metrics.relative_humidity = humidity.relative_humidity;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
#include "configuration.h"
|
||||||
|
|
||||||
|
#if !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR
|
||||||
|
|
||||||
|
#include "../mesh/generated/meshtastic/telemetry.pb.h"
|
||||||
|
#include "TelemetrySensor.h"
|
||||||
|
#include <Adafruit_AHTX0.h>
|
||||||
|
|
||||||
|
class AHT10Sensor : public TelemetrySensor
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
Adafruit_AHTX0 aht10;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual void setup() override;
|
||||||
|
|
||||||
|
public:
|
||||||
|
AHT10Sensor();
|
||||||
|
virtual int32_t runOnce() override;
|
||||||
|
virtual bool getMetrics(meshtastic_Telemetry *measurement) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -28,7 +28,7 @@ int32_t BME680Sensor::runOnce()
|
|||||||
|
|
||||||
if (bme680.status == BSEC_OK) {
|
if (bme680.status == BSEC_OK) {
|
||||||
status = 1;
|
status = 1;
|
||||||
if (!bme680.setConfig(bsec_config_iaq)) {
|
if (!bme680.setConfig(bsec_config)) {
|
||||||
checkStatus("setConfig");
|
checkStatus("setConfig");
|
||||||
status = 0;
|
status = 0;
|
||||||
}
|
}
|
||||||
@@ -57,7 +57,7 @@ bool BME680Sensor::getMetrics(meshtastic_Telemetry *measurement)
|
|||||||
measurement->variant.environment_metrics.temperature = bme680.getData(BSEC_OUTPUT_SENSOR_HEAT_COMPENSATED_TEMPERATURE).signal;
|
measurement->variant.environment_metrics.temperature = bme680.getData(BSEC_OUTPUT_SENSOR_HEAT_COMPENSATED_TEMPERATURE).signal;
|
||||||
measurement->variant.environment_metrics.relative_humidity =
|
measurement->variant.environment_metrics.relative_humidity =
|
||||||
bme680.getData(BSEC_OUTPUT_SENSOR_HEAT_COMPENSATED_HUMIDITY).signal;
|
bme680.getData(BSEC_OUTPUT_SENSOR_HEAT_COMPENSATED_HUMIDITY).signal;
|
||||||
measurement->variant.environment_metrics.barometric_pressure = bme680.getData(BSEC_OUTPUT_RAW_PRESSURE).signal / 100.0F;
|
measurement->variant.environment_metrics.barometric_pressure = bme680.getData(BSEC_OUTPUT_RAW_PRESSURE).signal;
|
||||||
measurement->variant.environment_metrics.gas_resistance = bme680.getData(BSEC_OUTPUT_RAW_GAS).signal / 1000.0;
|
measurement->variant.environment_metrics.gas_resistance = bme680.getData(BSEC_OUTPUT_RAW_GAS).signal / 1000.0;
|
||||||
// Check if we need to save state to filesystem (every STATE_SAVE_PERIOD ms)
|
// Check if we need to save state to filesystem (every STATE_SAVE_PERIOD ms)
|
||||||
measurement->variant.environment_metrics.iaq = bme680.getData(BSEC_OUTPUT_IAQ).signal;
|
measurement->variant.environment_metrics.iaq = bme680.getData(BSEC_OUTPUT_IAQ).signal;
|
||||||
|
|||||||
@@ -8,7 +8,9 @@
|
|||||||
|
|
||||||
#define STATE_SAVE_PERIOD UINT32_C(360 * 60 * 1000) // That's 6 hours worth of millis()
|
#define STATE_SAVE_PERIOD UINT32_C(360 * 60 * 1000) // That's 6 hours worth of millis()
|
||||||
|
|
||||||
#include "bme680_iaq_33v_3s_4d/bsec_iaq.h"
|
const uint8_t bsec_config[] = {
|
||||||
|
#include "config/bme680/bme680_iaq_33v_3s_4d/bsec_iaq.txt"
|
||||||
|
};
|
||||||
|
|
||||||
class BME680Sensor : public TelemetrySensor
|
class BME680Sensor : public TelemetrySensor
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -0,0 +1,53 @@
|
|||||||
|
#include "configuration.h"
|
||||||
|
|
||||||
|
#if !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR
|
||||||
|
|
||||||
|
#include "../mesh/generated/meshtastic/telemetry.pb.h"
|
||||||
|
#include "DFRobotLarkSensor.h"
|
||||||
|
#include "TelemetrySensor.h"
|
||||||
|
#include "gps/GeoCoord.h"
|
||||||
|
#include <DFRobot_LarkWeatherStation.h>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
DFRobotLarkSensor::DFRobotLarkSensor() : TelemetrySensor(meshtastic_TelemetrySensorType_DFROBOT_LARK, "DFROBOT_LARK") {}
|
||||||
|
|
||||||
|
int32_t DFRobotLarkSensor::runOnce()
|
||||||
|
{
|
||||||
|
LOG_INFO("Init sensor: %s\n", sensorName);
|
||||||
|
if (!hasSensor()) {
|
||||||
|
return DEFAULT_SENSOR_MINIMUM_WAIT_TIME_BETWEEN_READS;
|
||||||
|
}
|
||||||
|
|
||||||
|
lark = DFRobot_LarkWeatherStation_I2C(nodeTelemetrySensorsMap[sensorType].first, nodeTelemetrySensorsMap[sensorType].second);
|
||||||
|
|
||||||
|
if (lark.begin() == 0) // DFRobotLarkSensor init
|
||||||
|
{
|
||||||
|
LOG_DEBUG("DFRobotLarkSensor Init Succeed\n");
|
||||||
|
status = true;
|
||||||
|
} else {
|
||||||
|
LOG_ERROR("DFRobotLarkSensor Init Failed\n");
|
||||||
|
status = false;
|
||||||
|
}
|
||||||
|
return initI2CSensor();
|
||||||
|
}
|
||||||
|
|
||||||
|
void DFRobotLarkSensor::setup() {}
|
||||||
|
|
||||||
|
bool DFRobotLarkSensor::getMetrics(meshtastic_Telemetry *measurement)
|
||||||
|
{
|
||||||
|
measurement->variant.environment_metrics.temperature = lark.getValue("Temp").toFloat();
|
||||||
|
measurement->variant.environment_metrics.relative_humidity = lark.getValue("Humi").toFloat();
|
||||||
|
measurement->variant.environment_metrics.wind_speed = lark.getValue("Speed").toFloat();
|
||||||
|
measurement->variant.environment_metrics.wind_direction = GeoCoord::bearingToDegrees(lark.getValue("Dir").c_str());
|
||||||
|
measurement->variant.environment_metrics.barometric_pressure = lark.getValue("Pressure").toFloat();
|
||||||
|
|
||||||
|
LOG_INFO("Temperature: %f\n", measurement->variant.environment_metrics.temperature);
|
||||||
|
LOG_INFO("Humidity: %f\n", measurement->variant.environment_metrics.relative_humidity);
|
||||||
|
LOG_INFO("Wind Speed: %f\n", measurement->variant.environment_metrics.wind_speed);
|
||||||
|
LOG_INFO("Wind Direction: %d\n", measurement->variant.environment_metrics.wind_direction);
|
||||||
|
LOG_INFO("Barometric Pressure: %f\n", measurement->variant.environment_metrics.barometric_pressure);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#ifndef _MT_DFROBOTLARKSENSOR_H
|
||||||
|
#define _MT_DFROBOTLARKSENSOR_H
|
||||||
|
#include "configuration.h"
|
||||||
|
|
||||||
|
#if !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR
|
||||||
|
|
||||||
|
#include "../mesh/generated/meshtastic/telemetry.pb.h"
|
||||||
|
#include "TelemetrySensor.h"
|
||||||
|
#include <DFRobot_LarkWeatherStation.h>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
class DFRobotLarkSensor : public TelemetrySensor
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
DFRobot_LarkWeatherStation_I2C lark = DFRobot_LarkWeatherStation_I2C();
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual void setup() override;
|
||||||
|
|
||||||
|
public:
|
||||||
|
DFRobotLarkSensor();
|
||||||
|
virtual int32_t runOnce() override;
|
||||||
|
virtual bool getMetrics(meshtastic_Telemetry *measurement) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
#include "configuration.h"
|
||||||
|
|
||||||
|
#if !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR
|
||||||
|
|
||||||
|
#include "../mesh/generated/meshtastic/telemetry.pb.h"
|
||||||
|
#include "MLX90632Sensor.h"
|
||||||
|
#include "TelemetrySensor.h"
|
||||||
|
|
||||||
|
MLX90632Sensor::MLX90632Sensor() : TelemetrySensor(meshtastic_TelemetrySensorType_MLX90632, "MLX90632") {}
|
||||||
|
|
||||||
|
int32_t MLX90632Sensor::runOnce()
|
||||||
|
{
|
||||||
|
LOG_INFO("Init sensor: %s\n", sensorName);
|
||||||
|
if (!hasSensor()) {
|
||||||
|
return DEFAULT_SENSOR_MINIMUM_WAIT_TIME_BETWEEN_READS;
|
||||||
|
}
|
||||||
|
|
||||||
|
MLX90632::status returnError;
|
||||||
|
if (mlx.begin(nodeTelemetrySensorsMap[sensorType].first, *nodeTelemetrySensorsMap[sensorType].second, returnError) ==
|
||||||
|
true) // MLX90632 init
|
||||||
|
{
|
||||||
|
LOG_DEBUG("MLX90632 Init Succeed\n");
|
||||||
|
status = true;
|
||||||
|
} else {
|
||||||
|
LOG_ERROR("MLX90632 Init Failed\n");
|
||||||
|
status = false;
|
||||||
|
}
|
||||||
|
return initI2CSensor();
|
||||||
|
}
|
||||||
|
|
||||||
|
void MLX90632Sensor::setup() {}
|
||||||
|
|
||||||
|
bool MLX90632Sensor::getMetrics(meshtastic_Telemetry *measurement)
|
||||||
|
{
|
||||||
|
measurement->variant.environment_metrics.temperature = mlx.getObjectTemp(); // Get the object temperature in Fahrenheit
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
#include "configuration.h"
|
||||||
|
|
||||||
|
#if !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR
|
||||||
|
|
||||||
|
#include "../mesh/generated/meshtastic/telemetry.pb.h"
|
||||||
|
#include "TelemetrySensor.h"
|
||||||
|
#include <SparkFun_MLX90632_Arduino_Library.h>
|
||||||
|
|
||||||
|
class MLX90632Sensor : public TelemetrySensor
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
MLX90632 mlx = MLX90632();
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual void setup() override;
|
||||||
|
|
||||||
|
public:
|
||||||
|
MLX90632Sensor();
|
||||||
|
virtual int32_t runOnce() override;
|
||||||
|
virtual bool getMetrics(meshtastic_Telemetry *measurement) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -0,0 +1,143 @@
|
|||||||
|
#include "configuration.h"
|
||||||
|
|
||||||
|
#if !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR
|
||||||
|
|
||||||
|
#include "../mesh/generated/meshtastic/telemetry.pb.h"
|
||||||
|
#include "FSCommon.h"
|
||||||
|
#include "NAU7802Sensor.h"
|
||||||
|
#include "TelemetrySensor.h"
|
||||||
|
#include <pb_decode.h>
|
||||||
|
#include <pb_encode.h>
|
||||||
|
|
||||||
|
meshtastic_Nau7802Config nau7802config = meshtastic_Nau7802Config_init_zero;
|
||||||
|
|
||||||
|
NAU7802Sensor::NAU7802Sensor() : TelemetrySensor(meshtastic_TelemetrySensorType_NAU7802, "NAU7802") {}
|
||||||
|
|
||||||
|
int32_t NAU7802Sensor::runOnce()
|
||||||
|
{
|
||||||
|
LOG_INFO("Init sensor: %s\n", sensorName);
|
||||||
|
if (!hasSensor()) {
|
||||||
|
return DEFAULT_SENSOR_MINIMUM_WAIT_TIME_BETWEEN_READS;
|
||||||
|
}
|
||||||
|
status = nau7802.begin(*nodeTelemetrySensorsMap[sensorType].second);
|
||||||
|
nau7802.setSampleRate(NAU7802_SPS_320);
|
||||||
|
if (!loadCalibrationData()) {
|
||||||
|
LOG_ERROR("Failed to load calibration data\n");
|
||||||
|
}
|
||||||
|
nau7802.calibrateAFE();
|
||||||
|
LOG_INFO("Offset: %d, Calibration factor: %.2f\n", nau7802.getZeroOffset(), nau7802.getCalibrationFactor());
|
||||||
|
return initI2CSensor();
|
||||||
|
}
|
||||||
|
|
||||||
|
void NAU7802Sensor::setup() {}
|
||||||
|
|
||||||
|
bool NAU7802Sensor::getMetrics(meshtastic_Telemetry *measurement)
|
||||||
|
{
|
||||||
|
LOG_DEBUG("NAU7802Sensor::getMetrics\n");
|
||||||
|
nau7802.powerUp();
|
||||||
|
// Wait for the sensor to become ready for one second max
|
||||||
|
uint32_t start = millis();
|
||||||
|
while (!nau7802.available()) {
|
||||||
|
delay(100);
|
||||||
|
if (millis() - start > 1000) {
|
||||||
|
nau7802.powerDown();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Check if we have correct calibration values after powerup
|
||||||
|
LOG_DEBUG("Offset: %d, Calibration factor: %.2f\n", nau7802.getZeroOffset(), nau7802.getCalibrationFactor());
|
||||||
|
measurement->variant.environment_metrics.weight = nau7802.getWeight() / 1000; // sample is in kg
|
||||||
|
nau7802.powerDown();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void NAU7802Sensor::calibrate(float weight)
|
||||||
|
{
|
||||||
|
nau7802.calculateCalibrationFactor(weight * 1000, 64); // internal sample is in grams
|
||||||
|
if (!saveCalibrationData()) {
|
||||||
|
LOG_WARN("Failed to save calibration data\n");
|
||||||
|
}
|
||||||
|
LOG_INFO("Offset: %d, Calibration factor: %.2f\n", nau7802.getZeroOffset(), nau7802.getCalibrationFactor());
|
||||||
|
}
|
||||||
|
|
||||||
|
AdminMessageHandleResult NAU7802Sensor::handleAdminMessage(const meshtastic_MeshPacket &mp, meshtastic_AdminMessage *request,
|
||||||
|
meshtastic_AdminMessage *response)
|
||||||
|
{
|
||||||
|
AdminMessageHandleResult result;
|
||||||
|
|
||||||
|
switch (request->which_payload_variant) {
|
||||||
|
case meshtastic_AdminMessage_set_scale_tag:
|
||||||
|
if (request->set_scale == 0) {
|
||||||
|
this->tare();
|
||||||
|
LOG_DEBUG("Client requested to tare scale\n");
|
||||||
|
} else {
|
||||||
|
this->calibrate(request->set_scale);
|
||||||
|
LOG_DEBUG("Client requested to calibrate to %d kg\n", request->set_scale);
|
||||||
|
}
|
||||||
|
result = AdminMessageHandleResult::HANDLED;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
result = AdminMessageHandleResult::NOT_HANDLED;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
void NAU7802Sensor::tare()
|
||||||
|
{
|
||||||
|
nau7802.calculateZeroOffset(64);
|
||||||
|
if (!saveCalibrationData()) {
|
||||||
|
LOG_WARN("Failed to save calibration data\n");
|
||||||
|
}
|
||||||
|
LOG_INFO("Offset: %d, Calibration factor: %.2f\n", nau7802.getZeroOffset(), nau7802.getCalibrationFactor());
|
||||||
|
}
|
||||||
|
|
||||||
|
bool NAU7802Sensor::saveCalibrationData()
|
||||||
|
{
|
||||||
|
if (FSCom.exists(nau7802ConfigFileName) && !FSCom.remove(nau7802ConfigFileName)) {
|
||||||
|
LOG_WARN("Can't remove old state file\n");
|
||||||
|
}
|
||||||
|
auto file = FSCom.open(nau7802ConfigFileName, FILE_O_WRITE);
|
||||||
|
nau7802config.zeroOffset = nau7802.getZeroOffset();
|
||||||
|
nau7802config.calibrationFactor = nau7802.getCalibrationFactor();
|
||||||
|
bool okay = false;
|
||||||
|
if (file) {
|
||||||
|
LOG_INFO("%s state write to %s.\n", sensorName, nau7802ConfigFileName);
|
||||||
|
pb_ostream_t stream = {&writecb, &file, meshtastic_Nau7802Config_size};
|
||||||
|
|
||||||
|
if (!pb_encode(&stream, &meshtastic_Nau7802Config_msg, &nau7802config)) {
|
||||||
|
LOG_ERROR("Error: can't encode protobuf %s\n", PB_GET_ERROR(&stream));
|
||||||
|
} else {
|
||||||
|
okay = true;
|
||||||
|
}
|
||||||
|
file.flush();
|
||||||
|
file.close();
|
||||||
|
} else {
|
||||||
|
LOG_INFO("Can't write %s state (File: %s).\n", sensorName, nau7802ConfigFileName);
|
||||||
|
}
|
||||||
|
return okay;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool NAU7802Sensor::loadCalibrationData()
|
||||||
|
{
|
||||||
|
auto file = FSCom.open(nau7802ConfigFileName, FILE_O_READ);
|
||||||
|
bool okay = false;
|
||||||
|
if (file) {
|
||||||
|
LOG_INFO("%s state read from %s.\n", sensorName, nau7802ConfigFileName);
|
||||||
|
pb_istream_t stream = {&readcb, &file, meshtastic_Nau7802Config_size};
|
||||||
|
if (!pb_decode(&stream, &meshtastic_Nau7802Config_msg, &nau7802config)) {
|
||||||
|
LOG_ERROR("Error: can't decode protobuf %s\n", PB_GET_ERROR(&stream));
|
||||||
|
} else {
|
||||||
|
nau7802.setZeroOffset(nau7802config.zeroOffset);
|
||||||
|
nau7802.setCalibrationFactor(nau7802config.calibrationFactor);
|
||||||
|
okay = true;
|
||||||
|
}
|
||||||
|
file.close();
|
||||||
|
} else {
|
||||||
|
LOG_INFO("No %s state found (File: %s).\n", sensorName, nau7802ConfigFileName);
|
||||||
|
}
|
||||||
|
return okay;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
#include "MeshModule.h"
|
||||||
|
#include "configuration.h"
|
||||||
|
|
||||||
|
#if !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR
|
||||||
|
|
||||||
|
#include "../mesh/generated/meshtastic/telemetry.pb.h"
|
||||||
|
#include "TelemetrySensor.h"
|
||||||
|
#include <SparkFun_Qwiic_Scale_NAU7802_Arduino_Library.h>
|
||||||
|
|
||||||
|
class NAU7802Sensor : public TelemetrySensor
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
NAU7802 nau7802;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual void setup() override;
|
||||||
|
const char *nau7802ConfigFileName = "/prefs/nau7802.dat";
|
||||||
|
bool saveCalibrationData();
|
||||||
|
bool loadCalibrationData();
|
||||||
|
|
||||||
|
public:
|
||||||
|
NAU7802Sensor();
|
||||||
|
virtual int32_t runOnce() override;
|
||||||
|
virtual bool getMetrics(meshtastic_Telemetry *measurement) override;
|
||||||
|
void tare();
|
||||||
|
void calibrate(float weight);
|
||||||
|
AdminMessageHandleResult handleAdminMessage(const meshtastic_MeshPacket &mp, meshtastic_AdminMessage *request,
|
||||||
|
meshtastic_AdminMessage *response) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
#include "OPT3001Sensor.h"
|
||||||
|
#include "../mesh/generated/meshtastic/telemetry.pb.h"
|
||||||
|
#include "TelemetrySensor.h"
|
||||||
|
#include "configuration.h"
|
||||||
|
#include <ClosedCube_OPT3001.h>
|
||||||
|
|
||||||
|
OPT3001Sensor::OPT3001Sensor() : TelemetrySensor(meshtastic_TelemetrySensorType_OPT3001, "OPT3001") {}
|
||||||
|
|
||||||
|
int32_t OPT3001Sensor::runOnce()
|
||||||
|
{
|
||||||
|
LOG_INFO("Init sensor: %s\n", sensorName);
|
||||||
|
if (!hasSensor()) {
|
||||||
|
return DEFAULT_SENSOR_MINIMUM_WAIT_TIME_BETWEEN_READS;
|
||||||
|
}
|
||||||
|
auto errorCode = opt3001.begin(nodeTelemetrySensorsMap[sensorType].first);
|
||||||
|
status = errorCode == NO_ERROR;
|
||||||
|
|
||||||
|
return initI2CSensor();
|
||||||
|
}
|
||||||
|
|
||||||
|
void OPT3001Sensor::setup()
|
||||||
|
{
|
||||||
|
OPT3001_Config newConfig;
|
||||||
|
|
||||||
|
newConfig.RangeNumber = B1100;
|
||||||
|
newConfig.ConvertionTime = B0;
|
||||||
|
newConfig.Latch = B1;
|
||||||
|
newConfig.ModeOfConversionOperation = B11;
|
||||||
|
|
||||||
|
OPT3001_ErrorCode errorConfig = opt3001.writeConfig(newConfig);
|
||||||
|
if (errorConfig != NO_ERROR) {
|
||||||
|
LOG_ERROR("OPT3001 configuration error #%d", errorConfig);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool OPT3001Sensor::getMetrics(meshtastic_Telemetry *measurement)
|
||||||
|
{
|
||||||
|
OPT3001 result = opt3001.readResult();
|
||||||
|
|
||||||
|
measurement->variant.environment_metrics.lux = result.lux;
|
||||||
|
LOG_INFO("Lux: %f\n", measurement->variant.environment_metrics.lux);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
#include "../mesh/generated/meshtastic/telemetry.pb.h"
|
||||||
|
#include "TelemetrySensor.h"
|
||||||
|
#include <ClosedCube_OPT3001.h>
|
||||||
|
|
||||||
|
class OPT3001Sensor : public TelemetrySensor
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
ClosedCube_OPT3001 opt3001;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual void setup() override;
|
||||||
|
|
||||||
|
public:
|
||||||
|
OPT3001Sensor();
|
||||||
|
virtual int32_t runOnce() override;
|
||||||
|
virtual bool getMetrics(meshtastic_Telemetry *measurement) override;
|
||||||
|
};
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user