mirror of
https://github.com/alexhopeoconnor/firmware.git
synced 2026-10-14 15:31:31 +10:00
Compare commits
371
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e470619e3d | ||
|
|
bf34329033 | ||
|
|
8e88b9e819 | ||
|
|
56a2e40681 | ||
|
|
ba28ffb65a | ||
|
|
9651b5a0ec | ||
|
|
2f2ddae12a | ||
|
|
c77b89d85c | ||
|
|
5c2fe4a2c0 | ||
|
|
b8cee51e84 | ||
|
|
fd1ebdf363 | ||
|
|
35b47467c7 | ||
|
|
8f35a42f4f | ||
|
|
35a565cd84 | ||
|
|
ee68e727ed | ||
|
|
962d9ff220 | ||
|
|
8e0a342f06 | ||
|
|
55292f8a84 | ||
|
|
d72a836e07 | ||
|
|
26a3841a93 | ||
|
|
8e519d09b4 | ||
|
|
ae41a7cc06 | ||
|
|
011e640e95 | ||
|
|
e4e1ea971f | ||
|
|
bcdc36c07c | ||
|
|
972a5d5779 | ||
|
|
7c6454f171 | ||
|
|
9e55e6befb | ||
|
|
1d3d44061b | ||
|
|
4d57c99ad1 | ||
|
|
22e23997c1 | ||
|
|
8d29ce939d | ||
|
|
bb9ddcf2b5 | ||
|
|
ff40a3f120 | ||
|
|
8b1d7825b9 | ||
|
|
6fc4a1754b | ||
|
|
d83f8edd54 | ||
|
|
543e7f3342 | ||
|
|
f9e513f4fd | ||
|
|
7dad2286e2 | ||
|
|
190c7ecdd4 | ||
|
|
8598645931 | ||
|
|
3bb1cb8f1d | ||
|
|
cdea602181 | ||
|
|
b526a3ad53 | ||
|
|
2f0c19ebea | ||
|
|
06e27bb6c2 | ||
|
|
0588d69694 | ||
|
|
367d787d74 | ||
|
|
c8bf43de93 | ||
|
|
e543b61dd8 | ||
|
|
1fc6cc2d6c | ||
|
|
719faf4f97 | ||
|
|
3bf20dc3a6 | ||
|
|
d93425fde1 | ||
|
|
234e652a07 | ||
|
|
cd16b7b00a | ||
|
|
24501c30c0 | ||
|
|
7d2f3a3425 | ||
|
|
56223710b5 | ||
|
|
e45a358de0 | ||
|
|
b71e12c5e5 | ||
|
|
eb1f80f520 | ||
|
|
6c0911038a | ||
|
|
644e213b13 | ||
|
|
33eb073535 | ||
|
|
8729cdb699 | ||
|
|
7475cc301e | ||
|
|
eb071ec80d | ||
|
|
8144dcbc25 | ||
|
|
2b0113ae82 | ||
|
|
6a24566efb | ||
|
|
dd933e6bab | ||
|
|
79925406d6 | ||
|
|
5bc17a9911 | ||
|
|
a1d0af6636 | ||
|
|
22454c95c7 | ||
|
|
b99fd93247 | ||
|
|
50631f96fc | ||
|
|
a6cc7041d3 | ||
|
|
171512d2f6 | ||
|
|
db870dc17d | ||
|
|
28d0cef427 | ||
|
|
6d2011c172 | ||
|
|
47e1580a62 | ||
|
|
fc1e60ac58 | ||
|
|
c02bbad9f3 | ||
|
|
f1f66cf54a | ||
|
|
92eae39a1b | ||
|
|
9e20b0e9b9 | ||
|
|
dc9f6e1360 | ||
|
|
a1bf0d8519 | ||
|
|
f5633bf0c5 | ||
|
|
ad931799c9 | ||
|
|
3ad0af5ce8 | ||
|
|
545d32fcec | ||
|
|
94c3bb4a56 | ||
|
|
72c82c1c08 | ||
|
|
cc93df27a5 | ||
|
|
e3ce3a3a4f | ||
|
|
3c4d964334 | ||
|
|
574124aee5 | ||
|
|
1fe80e0f30 | ||
|
|
927a35ef51 | ||
|
|
c11a66030f | ||
|
|
059d5582d1 | ||
|
|
17b2a83b44 | ||
|
|
eddb72705f | ||
|
|
9631a1be38 | ||
|
|
8a9cc727a8 | ||
|
|
cdafa87cef | ||
|
|
5ce5b7b08b | ||
|
|
b285aa5bd6 | ||
|
|
9de0b7cfac | ||
|
|
fe9a80a4e0 | ||
|
|
de41a054b0 | ||
|
|
9b2ef971c2 | ||
|
|
710fdbd4e5 | ||
|
|
06175737cc | ||
|
|
4a2a00a227 | ||
|
|
50f06840d7 | ||
|
|
6660aec79a | ||
|
|
a34170654c | ||
|
|
d611a38ce2 | ||
|
|
5b3579af52 | ||
|
|
f27281d3fa | ||
|
|
59ecea507f | ||
|
|
f7f21ecefd | ||
|
|
d1e64c74de | ||
|
|
b8609ff130 | ||
|
|
ef9ecec341 | ||
|
|
0ee9d375b3 | ||
|
|
50d778d281 | ||
|
|
ab62590aa9 | ||
|
|
ada61ae178 | ||
|
|
b9a8683a4b | ||
|
|
5824a8f4c1 | ||
|
|
777ae2b99c | ||
|
|
d0fd17134e | ||
|
|
d6dac1737a | ||
|
|
23844389ac | ||
|
|
8847945734 | ||
|
|
5514aab007 | ||
|
|
aa54335e21 | ||
|
|
2a279c7f3d | ||
|
|
7abc194ef5 | ||
|
|
f99b81acf7 | ||
|
|
0850ad6c8d | ||
|
|
00ea9182a4 | ||
|
|
601ae29fe9 | ||
|
|
ff500bc5a9 | ||
|
|
e6163a59cd | ||
|
|
5c5cbb23cf | ||
|
|
f77c5f6a5b | ||
|
|
2a7cf9d387 | ||
|
|
db6e591c07 | ||
|
|
02c34e6214 | ||
|
|
5570b6bbc6 | ||
|
|
2dda640d27 | ||
|
|
5ccb6df142 | ||
|
|
3ae8aadaf0 | ||
|
|
7fb9b094d5 | ||
|
|
734f36589d | ||
|
|
1e655052fc | ||
|
|
d017fc7a5d | ||
|
|
6ddee795d6 | ||
|
|
d556ae762c | ||
|
|
48e0fd7ed0 | ||
|
|
b19c1a52cb | ||
|
|
ba771ae507 | ||
|
|
9014058935 | ||
|
|
ab7de7f6a0 | ||
|
|
6ee30043c3 | ||
|
|
314009a10f | ||
|
|
2d9126f873 | ||
|
|
d404a49336 | ||
|
|
ee9e46ec92 | ||
|
|
929b3e4f88 | ||
|
|
2043ad3bd0 | ||
|
|
2472c7cdc7 | ||
|
|
058e9769d6 | ||
|
|
3b2c37c47f | ||
|
|
ef5279e85e | ||
|
|
bd21a0455b | ||
|
|
9b4ad68f43 | ||
|
|
e20d57f3ad | ||
|
|
33b12126e0 | ||
|
|
9d323a3832 | ||
|
|
2a664e01b0 | ||
|
|
c1569b0f70 | ||
|
|
48dc222b75 | ||
|
|
ab9268cba9 | ||
|
|
6de3ca4301 | ||
|
|
e65e79c6c9 | ||
|
|
14146d6ff5 | ||
|
|
273beef148 | ||
|
|
94d5ee9fe6 | ||
|
|
7b64c4a5bf | ||
|
|
ecb4fb72db | ||
|
|
f439081674 | ||
|
|
bfbc4bf93a | ||
|
|
22e129e716 | ||
|
|
a85df199a5 | ||
|
|
7a65c8838d | ||
|
|
e3e36e23f9 | ||
|
|
7129cee944 | ||
|
|
23e3e6db92 | ||
|
|
a8999d7759 | ||
|
|
c7c620ac69 | ||
|
|
1bbc273ba6 | ||
|
|
33ced7e87a | ||
|
|
578ac6711b | ||
|
|
e37acae405 | ||
|
|
daddaf7146 | ||
|
|
5ff1078c8c | ||
|
|
0b010b4fd8 | ||
|
|
f86dde3c40 | ||
|
|
fdaaf71366 | ||
|
|
a577dd4142 | ||
|
|
9dad62e3c4 | ||
|
|
e0b4a8e31e | ||
|
|
7cbae56e6c | ||
|
|
6eabbaf432 | ||
|
|
cec8233cd1 | ||
|
|
0ebdc7ab0c | ||
|
|
e61bd84116 | ||
|
|
b0c1b7b7b5 | ||
|
|
eefe9efa9f | ||
|
|
390de724ba | ||
|
|
85176756ec | ||
|
|
6f1dae1b1b | ||
|
|
ef56fae976 | ||
|
|
d398419aef | ||
|
|
96cf78aadd | ||
|
|
ced87596cb | ||
|
|
1be635a797 | ||
|
|
36f1a62b0b | ||
|
|
8ef72a5c08 | ||
|
|
efc27f2051 | ||
|
|
837c4e9e7b | ||
|
|
181325103a | ||
|
|
207b9b49a5 | ||
|
|
8ce1c07c4e | ||
|
|
2661fc694f | ||
|
|
b528290fde | ||
|
|
ff89dca5b3 | ||
|
|
80fd121d87 | ||
|
|
f3fa8daedf | ||
|
|
bcd77c4523 | ||
|
|
308c0a6bb8 | ||
|
|
754db3f2bc | ||
|
|
c16f20de21 | ||
|
|
b4cbea1b3d | ||
|
|
0e7253d309 | ||
|
|
b91d66b436 | ||
|
|
7537b55586 | ||
|
|
2d18130235 | ||
|
|
67ddae2851 | ||
|
|
884bc529f0 | ||
|
|
8f3614d66c | ||
|
|
e7dfabc20f | ||
|
|
185eb318ad | ||
|
|
c86a3200f0 | ||
|
|
c3aa56ef30 | ||
|
|
192af05a25 | ||
|
|
26d0b2b477 | ||
|
|
b726792efd | ||
|
|
c451db3a3f | ||
|
|
95682c9095 | ||
|
|
da53b8152d | ||
|
|
8d1a34a4bf | ||
|
|
464f270b12 | ||
|
|
7740b4bccd | ||
|
|
e85a2e827b | ||
|
|
62a0321c7d | ||
|
|
6e8300287b | ||
|
|
f97ae52263 | ||
|
|
9bd293a941 | ||
|
|
bc69621c3e | ||
|
|
2ee53d1500 | ||
|
|
bee959150b | ||
|
|
c74bce9360 | ||
|
|
48eee747da | ||
|
|
a28f10e0c2 | ||
|
|
6cd1882aaa | ||
|
|
0bd17e6da6 | ||
|
|
9bc2224164 | ||
|
|
e1b4b226c9 | ||
|
|
cf392a4c20 | ||
|
|
54a2e14e35 | ||
|
|
1cfd5d12d2 | ||
|
|
b573e0eacc | ||
|
|
8ca884bafd | ||
|
|
864b793ce0 | ||
|
|
74afd13171 | ||
|
|
8daebf80dd | ||
|
|
a767997cea | ||
|
|
861f0b6769 | ||
|
|
2012a0ae1c | ||
|
|
3513d88794 | ||
|
|
debf4b934f | ||
|
|
3878e025e4 | ||
|
|
3ab4bebdcb | ||
|
|
e38aca3cba | ||
|
|
d8bdb92efe | ||
|
|
c6a9edf8c7 | ||
|
|
a7da3537e2 | ||
|
|
5b4530325f | ||
|
|
b498c0bfbf | ||
|
|
02ae24b6fa | ||
|
|
5111bd703a | ||
|
|
789e8f02bf | ||
|
|
92526fca23 | ||
|
|
9ec7dbd695 | ||
|
|
66c41e683d | ||
|
|
c1870f91fc | ||
|
|
06eaf2ba5d | ||
|
|
4a79a690db | ||
|
|
1f458d6397 | ||
|
|
02231fd487 | ||
|
|
66a4632f34 | ||
|
|
e509a91019 | ||
|
|
d8f3c3324c | ||
|
|
9ddfc6de4c | ||
|
|
1a38c4e51d | ||
|
|
40d6b99911 | ||
|
|
7d00e1cef9 | ||
|
|
5bbafdfd31 | ||
|
|
5453c495e2 | ||
|
|
d1ff160256 | ||
|
|
dd552a99e1 | ||
|
|
09ea198205 | ||
|
|
703da1d8c7 | ||
|
|
48c0635188 | ||
|
|
8db6039264 | ||
|
|
4b4c1669a9 | ||
|
|
d2ea430a3e | ||
|
|
4c1c5b070e | ||
|
|
bcdda4de8a | ||
|
|
24ecfa1a45 | ||
|
|
106a50bce2 | ||
|
|
103ab0c242 | ||
|
|
848b9773b9 | ||
|
|
ce1eb149ac | ||
|
|
29fe6e7448 | ||
|
|
a111f54b61 | ||
|
|
9f5f630dca | ||
|
|
1f9dacf486 | ||
|
|
5dde738a31 | ||
|
|
1951569b1a | ||
|
|
93ba19d1e1 | ||
|
|
a1c998e7e0 | ||
|
|
302caa854a | ||
|
|
59cc57fc29 | ||
|
|
6813b8e4e9 | ||
|
|
4aa6f60e95 | ||
|
|
2ffc93324d | ||
|
|
c501cc501d | ||
|
|
8c0ff89972 | ||
|
|
cf22b7ff04 | ||
|
|
811a9ae261 | ||
|
|
8b0208d1c6 | ||
|
|
1a1d545c38 | ||
|
|
32bc2f1137 | ||
|
|
1b249c32bf | ||
|
|
bca9fbe7e4 | ||
|
|
e70435ebd7 | ||
|
|
f583837b4e | ||
|
|
6f235232f0 | ||
|
|
8641777bac | ||
|
|
4ee15d8128 |
@@ -0,0 +1,25 @@
|
||||
FROM mcr.microsoft.com/devcontainers/cpp:1-debian-12
|
||||
|
||||
# [Optional] Uncomment this section to install additional packages.
|
||||
RUN apt-get update && export DEBIAN_FRONTEND=noninteractive \
|
||||
&& apt-get -y install --no-install-recommends \
|
||||
ca-certificates \
|
||||
g++ \
|
||||
git \
|
||||
libbluetooth-dev \
|
||||
libgpiod-dev \
|
||||
liborcania-dev \
|
||||
libssl-dev \
|
||||
libulfius-dev \
|
||||
libyaml-cpp-dev \
|
||||
pipx \
|
||||
pkg-config \
|
||||
python3 \
|
||||
python3-pip \
|
||||
python3-venv \
|
||||
python3-wheel \
|
||||
wget \
|
||||
zip \
|
||||
&& apt-get clean && rm -rf /var/lib/apt/lists/*
|
||||
|
||||
RUN pipx install platformio==6.1.15
|
||||
@@ -0,0 +1,28 @@
|
||||
// For format details, see https://aka.ms/devcontainer.json. For config options, see the
|
||||
// README at: https://github.com/devcontainers/templates/tree/main/src/cpp
|
||||
{
|
||||
"name": "Meshtastic Firmware Dev",
|
||||
"build": {
|
||||
"dockerfile": "Dockerfile"
|
||||
},
|
||||
"features": {
|
||||
"ghcr.io/devcontainers/features/python:1": {
|
||||
"installTools": true,
|
||||
"version": "latest"
|
||||
}
|
||||
},
|
||||
"customizations": {
|
||||
"vscode": {
|
||||
"extensions": [
|
||||
"ms-vscode.cpptools",
|
||||
"platformio.platformio-ide",
|
||||
]
|
||||
}
|
||||
},
|
||||
|
||||
// Use 'forwardPorts' to make a list of ports inside the container available locally.
|
||||
"forwardPorts": [ 4403 ],
|
||||
|
||||
// Run commands to prepare the container for use
|
||||
"postCreateCommand": ".devcontainer/setup.sh",
|
||||
}
|
||||
Executable
+3
@@ -0,0 +1,3 @@
|
||||
#!/usr/bin/env sh
|
||||
|
||||
git submodule update --init
|
||||
@@ -11,7 +11,7 @@ runs:
|
||||
ref: ${{github.event.pull_request.head.ref}}
|
||||
repository: ${{github.event.pull_request.head.repo.full_name}}
|
||||
|
||||
- name: Install dependencies
|
||||
- name: Install dependencies
|
||||
shell: bash
|
||||
run: |
|
||||
sudo apt-get -y update --fix-missing
|
||||
@@ -22,19 +22,21 @@ runs:
|
||||
with:
|
||||
python-version: 3.x
|
||||
|
||||
- name: Cache python libs
|
||||
uses: actions/cache@v4
|
||||
id: cache-pip # needed in if test
|
||||
with:
|
||||
path: ~/.cache/pip
|
||||
key: ${{ runner.os }}-pip
|
||||
# - name: Cache python libs
|
||||
# uses: actions/cache@v4
|
||||
# id: cache-pip # needed in if test
|
||||
# with:
|
||||
# path: ~/.cache/pip
|
||||
# key: ${{ runner.os }}-pip
|
||||
|
||||
- name: Upgrade python tools
|
||||
shell: bash
|
||||
run: |
|
||||
python -m pip install --upgrade pip
|
||||
pip install -U platformio adafruit-nrfutil
|
||||
pip install -U meshtastic --pre
|
||||
pip install -U --no-build-isolation --no-cache-dir "setuptools<72"
|
||||
pip install -U platformio adafruit-nrfutil --no-build-isolation
|
||||
pip install -U poetry --no-build-isolation
|
||||
pip install -U meshtastic --pre --no-build-isolation
|
||||
|
||||
- name: Upgrade platformio
|
||||
shell: bash
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
### ❌ (Please delete all these tips and replace them with your text) ❌
|
||||
|
||||
## Thank you for sending in a pull request, here's some tips to get started!
|
||||
|
||||
(Please delete all these tips and replace with your text)
|
||||
|
||||
- Before starting on some new big chunk of code, it it is optional but highly recommended to open an issue first
|
||||
to say "hey, I think this idea X should be implemented and I'm starting work on it. My general plan is Y, any feedback
|
||||
to say "Hey, I think this idea X should be implemented and I'm starting work on it. My general plan is Y, any feedback
|
||||
is appreciated." This will allow other devs to potentially save you time by not accidentially duplicating work etc...
|
||||
- Please do not check in files that don't have real changes
|
||||
- Please do not reformat lines that you didn't have to change the code on
|
||||
@@ -12,3 +12,4 @@
|
||||
- If your PR fixes a bug, mention "fixes #bugnum" somewhere in your pull request description.
|
||||
- If your other co-developers have comments on your PR please tweak as needed.
|
||||
- Please also enable "Allow edits by maintainers".
|
||||
- If your PR gets accepted you can request a "Contributor" role in the Meshtastic Discord
|
||||
|
||||
@@ -10,7 +10,7 @@ jobs:
|
||||
build-native:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Install libbluetooth
|
||||
- name: Install libs needed for native build
|
||||
shell: bash
|
||||
run: |
|
||||
sudo apt-get update --fix-missing
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
name: Build STM32
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
board:
|
||||
required: true
|
||||
type: string
|
||||
|
||||
jobs:
|
||||
build-stm32:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Build base
|
||||
id: base
|
||||
uses: ./.github/actions/setup-base
|
||||
|
||||
- name: Build STM32
|
||||
run: bin/build-stm32.sh ${{ inputs.board }}
|
||||
|
||||
- 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-${{ inputs.board }}-${{ steps.version.outputs.version }}.zip
|
||||
overwrite: true
|
||||
path: |
|
||||
release/*.hex
|
||||
release/*.bin
|
||||
@@ -24,7 +24,7 @@ jobs:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
arch: [esp32, esp32s3, esp32c3, nrf52840, rp2040, check]
|
||||
arch: [esp32, esp32s3, esp32c3, nrf52840, rp2040, stm32, check]
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- id: checkout
|
||||
@@ -41,6 +41,7 @@ jobs:
|
||||
esp32c3: ${{ steps.jsonStep.outputs.esp32c3 }}
|
||||
nrf52840: ${{ steps.jsonStep.outputs.nrf52840 }}
|
||||
rp2040: ${{ steps.jsonStep.outputs.rp2040 }}
|
||||
stm32: ${{ steps.jsonStep.outputs.stm32 }}
|
||||
check: ${{ steps.jsonStep.outputs.check }}
|
||||
|
||||
check:
|
||||
@@ -103,6 +104,15 @@ jobs:
|
||||
with:
|
||||
board: ${{ matrix.board }}
|
||||
|
||||
build-stm32:
|
||||
needs: setup
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix: ${{ fromJson(needs.setup.outputs.stm32) }}
|
||||
uses: ./.github/workflows/build_stm32.yml
|
||||
with:
|
||||
board: ${{ matrix.board }}
|
||||
|
||||
package-raspbian:
|
||||
uses: ./.github/workflows/package_raspbian.yml
|
||||
|
||||
@@ -134,6 +144,7 @@ jobs:
|
||||
build-esp32-c3,
|
||||
build-nrf52,
|
||||
build-rpi2040,
|
||||
build-stm32,
|
||||
package-raspbian,
|
||||
package-raspbian-armv7l,
|
||||
package-native,
|
||||
|
||||
@@ -0,0 +1,91 @@
|
||||
name: End to end tests
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: "0 0 * * *" # Run every day at midnight
|
||||
workflow_dispatch: {}
|
||||
|
||||
jobs:
|
||||
test-simulator:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Install libbluetooth
|
||||
shell: bash
|
||||
run: |
|
||||
sudo apt-get update --fix-missing
|
||||
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
|
||||
|
||||
- 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
|
||||
|
||||
# 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 10 # 5 seconds was not enough
|
||||
echo "Simulator started, launching python test..."
|
||||
python3 -c 'from meshtastic.test import testSimulator; testSimulator()'
|
||||
|
||||
- name: PlatformIO Tests
|
||||
run: platformio test -e native --junit-output-path testreport.xml
|
||||
|
||||
- name: Test Report
|
||||
uses: dorny/test-reporter@v1.9.1
|
||||
if: success() || failure() # run this step even if previous step failed
|
||||
with:
|
||||
name: PlatformIO Tests
|
||||
path: testreport.xml
|
||||
reporter: java-junit
|
||||
|
||||
hardware-tests:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Upgrade python tools
|
||||
shell: bash
|
||||
run: |
|
||||
python -m pip install --upgrade pip
|
||||
pip install -U --no-build-isolation --no-cache-dir "setuptools<72"
|
||||
pip install -U platformio adafruit-nrfutil --no-build-isolation
|
||||
pip install -U poetry --no-build-isolation
|
||||
pip install -U meshtastic --pre --no-build-isolation
|
||||
|
||||
- name: Upgrade platformio
|
||||
shell: bash
|
||||
run: |
|
||||
pio upgrade
|
||||
|
||||
- name: Setup pnpm
|
||||
uses: pnpm/action-setup@v2
|
||||
with:
|
||||
version: latest
|
||||
|
||||
- name: Install Dependencies
|
||||
run: pnpm install
|
||||
|
||||
- name: Setup devices
|
||||
run: pnpm run setup
|
||||
|
||||
- name: Execute end to end tests on connected hardware
|
||||
run: pnpm run test
|
||||
@@ -1,3 +1,6 @@
|
||||
[submodule "protobufs"]
|
||||
path = protobufs
|
||||
url = https://github.com/meshtastic/protobufs.git
|
||||
[submodule "meshtestic"]
|
||||
path = meshtestic
|
||||
url = https://github.com/meshtastic/meshTestic
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
tasks:
|
||||
- init: pip install platformio && pip install --upgrade pip
|
||||
+15
-15
@@ -1,36 +1,36 @@
|
||||
version: 0.1
|
||||
cli:
|
||||
version: 1.22.2
|
||||
version: 1.22.3
|
||||
plugins:
|
||||
sources:
|
||||
- id: trunk
|
||||
ref: v1.5.0
|
||||
ref: v1.6.2
|
||||
uri: https://github.com/trunk-io/plugins
|
||||
lint:
|
||||
enabled:
|
||||
- trufflehog@3.76.3
|
||||
- trufflehog@3.81.9
|
||||
- yamllint@1.35.1
|
||||
- bandit@1.7.8
|
||||
- checkov@3.2.95
|
||||
- bandit@1.7.9
|
||||
- checkov@3.2.238
|
||||
- terrascan@1.19.1
|
||||
- trivy@0.51.1
|
||||
- trivy@0.54.1
|
||||
#- trufflehog@3.63.2-rc0
|
||||
- taplo@0.8.1
|
||||
- ruff@0.4.4
|
||||
- taplo@0.9.3
|
||||
- ruff@0.6.2
|
||||
- isort@5.13.2
|
||||
- markdownlint@0.40.0
|
||||
- oxipng@9.1.1
|
||||
- markdownlint@0.41.0
|
||||
- oxipng@9.1.2
|
||||
- svgo@3.3.2
|
||||
- actionlint@1.7.0
|
||||
- flake8@7.0.0
|
||||
- actionlint@1.7.1
|
||||
- flake8@7.1.1
|
||||
- hadolint@2.12.0
|
||||
- shfmt@3.6.0
|
||||
- shellcheck@0.10.0
|
||||
- black@24.4.2
|
||||
- black@24.8.0
|
||||
- git-diff-check
|
||||
- gitleaks@8.18.2
|
||||
- gitleaks@8.18.4
|
||||
- clang-format@16.0.3
|
||||
- prettier@3.2.5
|
||||
- prettier@3.3.3
|
||||
ignore:
|
||||
- linters: [ALL]
|
||||
paths:
|
||||
|
||||
Vendored
+4
-3
@@ -2,8 +2,9 @@
|
||||
// See http://go.microsoft.com/fwlink/?LinkId=827846
|
||||
// for the documentation about the extensions.json format
|
||||
"recommendations": [
|
||||
"ms-vscode.cpptools",
|
||||
"platformio.platformio-ide",
|
||||
"trunk.io"
|
||||
"platformio.platformio-ide"
|
||||
],
|
||||
"unwantedRecommendations": [
|
||||
"ms-vscode.cpptools-extension-pack"
|
||||
]
|
||||
}
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
# Contributing to Meshtastic Firmware
|
||||
|
||||
We're excited that you're interested in contributing to the Meshtastic firmware! This document provides a high-level overview of how you can get involved.
|
||||
|
||||
## Important First Steps
|
||||
|
||||
Before you begin, please:
|
||||
|
||||
1. **Read our documentation**: Our [official documentation](https://meshtastic.org/docs/) is a crucial resource. It contains essential information about the project.
|
||||
|
||||
2. **Check out the firmware build guide**: For specific instructions on setting up your development environment and building the firmware, refer to our [Firmware Build Guide](https://meshtastic.org/docs/development/firmware/build/).
|
||||
|
||||
3. Read our [Code of Conduct](https://meshtastic.org/docs/legal/conduct/)
|
||||
|
||||
4. Join our [Discord community](https://discord.com/invite/ktMAKGBnBs) to connect with developers and other contributors to get help.
|
||||
|
||||
## Getting Help and Discussing Ideas
|
||||
|
||||
We encourage open communication and discussion before diving into code changes:
|
||||
|
||||
1. **Use GitHub Discussions**: For new ideas, questions, or to discuss potential changes, start a conversation in our [GitHub Discussions](https://github.com/meshtastic/firmware/discussions) first. This helps us collaborate and avoid duplicate work.
|
||||
|
||||
2. **Join our Discord**: For real-time chat and quick questions, join our [Discord server](https://discord.com/invite/ktMAKGBnBs). It's a great place to get help and connect with other developers and the community.
|
||||
|
||||
3. **Reporting Issues**: If you've identified a bug, please use our bug report template when creating a new issue in the [issue tracker](https://github.com/meshtastic/firmware/issues). Ensure you've searched existing issues to avoid duplicates.
|
||||
|
||||
## Making Contributions
|
||||
|
||||
> [!IMPORTANT]
|
||||
> Before making any contributions, you must sign our Contributor License Agreement (CLA). You can do this by visiting https://cla-assistant.io/meshtastic/firmware. Be sure to use the GitHub account you will use to submit your contributions when signing.
|
||||
|
||||
1. Fork the repository
|
||||
2. Create a new branch for your feature or bug fix
|
||||
3. Make your changes
|
||||
4. Test your changes thoroughly
|
||||
5. Create a pull request with a clear description, using the provided template, of your changes. Be sure to enable "Allow edits from maintainers".
|
||||
|
||||
## Coding Standards
|
||||
|
||||
To ensure consistent code formatting across the project:
|
||||
|
||||
1. Install the [Trunk](https://marketplace.visualstudio.com/items?itemName=Trunk.io) extension for Visual Studio Code.
|
||||
2. Before submitting your changes, run `trunk fmt` to automatically format your code according to our standards.
|
||||
|
||||
Adhering to these formatting guidelines helps maintain code consistency and makes the review process smoother.
|
||||
|
||||
Thank you for contributing to Meshtastic!
|
||||
@@ -48,6 +48,7 @@ lib_deps =
|
||||
https://github.com/dbSuS/libpax.git#7bcd3fcab75037505be9b122ab2b24cc5176b587
|
||||
https://github.com/lewisxhe/XPowersLib.git#84b7373faea3118b6c37954d52f98b8a337148d6
|
||||
https://github.com/meshtastic/ESP32_Codec2.git#633326c78ac251c059ab3a8c430fcdf25b41672f
|
||||
rweather/Crypto@^0.4.0
|
||||
|
||||
lib_ignore =
|
||||
segger_rtt
|
||||
|
||||
@@ -0,0 +1,208 @@
|
||||
/*
|
||||
* lfs utility functions
|
||||
*
|
||||
* Copyright (c) 2017, Arm Limited. All rights reserved.
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
// MESHTASTIC/@geeksville note: This file is copied from the Adafruit nrf52 arduino lib. And we use a special -include in
|
||||
// nrf52.ini to load it before EVERY file we do this hack because the default definitions for LFS_ASSERT are quite poor and we
|
||||
// don't want to fork the adafruit lib (again) and send in a PR that they probably won't merge anyways. This file might break if
|
||||
// they ever update lfs.util on their side, in which case we'll need to update this file to match their new version. The version
|
||||
// this is a copy from is almost exactly
|
||||
// https://github.com/adafruit/Adafruit_nRF52_Arduino/blob/c25d93268a3b9c23e9a1ccfcaf9b208beca624ca/libraries/Adafruit_LittleFS/src/littlefs/lfs_util.h
|
||||
|
||||
#ifndef LFS_UTIL_H
|
||||
#define LFS_UTIL_H
|
||||
|
||||
// Users can override lfs_util.h with their own configuration by defining
|
||||
// LFS_CONFIG as a header file to include (-DLFS_CONFIG=lfs_config.h).
|
||||
//
|
||||
// If LFS_CONFIG is used, none of the default utils will be emitted and must be
|
||||
// provided by the config file. To start I would suggest copying lfs_util.h and
|
||||
// modifying as needed.
|
||||
#ifdef LFS_CONFIG
|
||||
#define LFS_STRINGIZE(x) LFS_STRINGIZE2(x)
|
||||
#define LFS_STRINGIZE2(x) #x
|
||||
#include LFS_STRINGIZE(LFS_CONFIG)
|
||||
#else
|
||||
|
||||
// System includes
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
#ifndef LFS_NO_MALLOC
|
||||
#include <stdlib.h>
|
||||
#endif
|
||||
#ifndef LFS_NO_ASSERT
|
||||
#include <assert.h>
|
||||
#endif
|
||||
|
||||
#if !defined(LFS_NO_DEBUG) || !defined(LFS_NO_WARN) || !defined(LFS_NO_ERROR)
|
||||
#include <stdio.h>
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
// Macros, may be replaced by system specific wrappers. Arguments to these
|
||||
// macros must not have side-effects as the macros can be removed for a smaller
|
||||
// code footprint
|
||||
|
||||
// Logging functions
|
||||
#ifndef LFS_NO_DEBUG
|
||||
|
||||
void logLegacy(const char *level, const char *fmt, ...);
|
||||
#define LFS_DEBUG(fmt, ...) logLegacy("DEBUG", "lfs debug:%d: " fmt "\n", __LINE__, __VA_ARGS__)
|
||||
#else
|
||||
#define LFS_DEBUG(fmt, ...)
|
||||
#endif
|
||||
|
||||
#ifndef LFS_NO_WARN
|
||||
#define LFS_WARN(fmt, ...) logLegacy("WARN", "lfs warn:%d: " fmt "\n", __LINE__, __VA_ARGS__)
|
||||
#else
|
||||
#define LFS_WARN(fmt, ...)
|
||||
#endif
|
||||
|
||||
#ifndef LFS_NO_ERROR
|
||||
#define LFS_ERROR(fmt, ...) logLegacy("ERROR", "lfs error:%d: " fmt "\n", __LINE__, __VA_ARGS__)
|
||||
#else
|
||||
#define LFS_ERROR(fmt, ...)
|
||||
#endif
|
||||
|
||||
// Runtime assertions
|
||||
#ifndef LFS_NO_ASSERT
|
||||
#define LFS_ASSERT(test) assert(test)
|
||||
#else
|
||||
extern void lfs_assert(const char *reason);
|
||||
#define LFS_ASSERT(test) \
|
||||
if (!(test)) \
|
||||
lfs_assert(#test)
|
||||
#endif
|
||||
|
||||
// Builtin functions, these may be replaced by more efficient
|
||||
// toolchain-specific implementations. LFS_NO_INTRINSICS falls back to a more
|
||||
// expensive basic C implementation for debugging purposes
|
||||
|
||||
// Min/max functions for unsigned 32-bit numbers
|
||||
static inline uint32_t lfs_max(uint32_t a, uint32_t b)
|
||||
{
|
||||
return (a > b) ? a : b;
|
||||
}
|
||||
|
||||
static inline uint32_t lfs_min(uint32_t a, uint32_t b)
|
||||
{
|
||||
return (a < b) ? a : b;
|
||||
}
|
||||
|
||||
// Find the next smallest power of 2 less than or equal to a
|
||||
static inline uint32_t lfs_npw2(uint32_t a)
|
||||
{
|
||||
#if !defined(LFS_NO_INTRINSICS) && (defined(__GNUC__) || defined(__CC_ARM))
|
||||
return 32 - __builtin_clz(a - 1);
|
||||
#else
|
||||
uint32_t r = 0;
|
||||
uint32_t s;
|
||||
a -= 1;
|
||||
s = (a > 0xffff) << 4;
|
||||
a >>= s;
|
||||
r |= s;
|
||||
s = (a > 0xff) << 3;
|
||||
a >>= s;
|
||||
r |= s;
|
||||
s = (a > 0xf) << 2;
|
||||
a >>= s;
|
||||
r |= s;
|
||||
s = (a > 0x3) << 1;
|
||||
a >>= s;
|
||||
r |= s;
|
||||
return (r | (a >> 1)) + 1;
|
||||
#endif
|
||||
}
|
||||
|
||||
// Count the number of trailing binary zeros in a
|
||||
// lfs_ctz(0) may be undefined
|
||||
static inline uint32_t lfs_ctz(uint32_t a)
|
||||
{
|
||||
#if !defined(LFS_NO_INTRINSICS) && defined(__GNUC__)
|
||||
return __builtin_ctz(a);
|
||||
#else
|
||||
return lfs_npw2((a & -a) + 1) - 1;
|
||||
#endif
|
||||
}
|
||||
|
||||
// Count the number of binary ones in a
|
||||
static inline uint32_t lfs_popc(uint32_t a)
|
||||
{
|
||||
#if !defined(LFS_NO_INTRINSICS) && (defined(__GNUC__) || defined(__CC_ARM))
|
||||
return __builtin_popcount(a);
|
||||
#else
|
||||
a = a - ((a >> 1) & 0x55555555);
|
||||
a = (a & 0x33333333) + ((a >> 2) & 0x33333333);
|
||||
return (((a + (a >> 4)) & 0xf0f0f0f) * 0x1010101) >> 24;
|
||||
#endif
|
||||
}
|
||||
|
||||
// Find the sequence comparison of a and b, this is the distance
|
||||
// between a and b ignoring overflow
|
||||
static inline int lfs_scmp(uint32_t a, uint32_t b)
|
||||
{
|
||||
return (int)(unsigned)(a - b);
|
||||
}
|
||||
|
||||
// Convert from 32-bit little-endian to native order
|
||||
static inline uint32_t lfs_fromle32(uint32_t a)
|
||||
{
|
||||
#if !defined(LFS_NO_INTRINSICS) && ((defined(BYTE_ORDER) && BYTE_ORDER == ORDER_LITTLE_ENDIAN) || \
|
||||
(defined(__BYTE_ORDER) && __BYTE_ORDER == __ORDER_LITTLE_ENDIAN) || \
|
||||
(defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__))
|
||||
return a;
|
||||
#elif !defined(LFS_NO_INTRINSICS) && \
|
||||
((defined(BYTE_ORDER) && BYTE_ORDER == ORDER_BIG_ENDIAN) || (defined(__BYTE_ORDER) && __BYTE_ORDER == __ORDER_BIG_ENDIAN) || \
|
||||
(defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__))
|
||||
return __builtin_bswap32(a);
|
||||
#else
|
||||
return (((uint8_t *)&a)[0] << 0) | (((uint8_t *)&a)[1] << 8) | (((uint8_t *)&a)[2] << 16) | (((uint8_t *)&a)[3] << 24);
|
||||
#endif
|
||||
}
|
||||
|
||||
// Convert to 32-bit little-endian from native order
|
||||
static inline uint32_t lfs_tole32(uint32_t a)
|
||||
{
|
||||
return lfs_fromle32(a);
|
||||
}
|
||||
|
||||
// Calculate CRC-32 with polynomial = 0x04c11db7
|
||||
void lfs_crc(uint32_t *crc, const void *buffer, size_t size);
|
||||
|
||||
// Allocate memory, only used if buffers are not provided to littlefs
|
||||
static inline void *lfs_malloc(size_t size)
|
||||
{
|
||||
#ifndef LFS_NO_MALLOC
|
||||
extern void *pvPortMalloc(size_t xWantedSize);
|
||||
return pvPortMalloc(size);
|
||||
#else
|
||||
(void)size;
|
||||
return NULL;
|
||||
#endif
|
||||
}
|
||||
|
||||
// Deallocate memory, only used if buffers are not provided to littlefs
|
||||
static inline void lfs_free(void *p)
|
||||
{
|
||||
#ifndef LFS_NO_MALLOC
|
||||
extern void vPortFree(void *pv);
|
||||
vPortFree(p);
|
||||
#else
|
||||
(void)p;
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /* extern "C" */
|
||||
#endif
|
||||
|
||||
#endif
|
||||
#endif
|
||||
@@ -2,9 +2,13 @@
|
||||
; Instead of the standard nordicnrf52 platform, we use our fork which has our added variant files
|
||||
platform = platformio/nordicnrf52@^10.5.0
|
||||
extends = arduino_base
|
||||
platform_packages =
|
||||
; our custom Git version until they merge our PR
|
||||
framework-arduinoadafruitnrf52 @ https://github.com/geeksville/Adafruit_nRF52_Arduino.git
|
||||
|
||||
build_type = debug
|
||||
build_flags =
|
||||
build_flags =
|
||||
-include arch/nrf52/cpp_overrides/lfs_util.h
|
||||
${arduino_base.build_flags}
|
||||
-DSERIAL_BUFFER_SIZE=1024
|
||||
-Wno-unused-variable
|
||||
@@ -16,6 +20,7 @@ build_src_filter =
|
||||
|
||||
lib_deps=
|
||||
${arduino_base.lib_deps}
|
||||
rweather/Crypto@^0.4.0
|
||||
|
||||
lib_ignore =
|
||||
BluetoothOTA
|
||||
@@ -7,3 +7,72 @@ lib_deps =
|
||||
${nrf52_base.lib_deps}
|
||||
${environmental_base.lib_deps}
|
||||
https://github.com/Kongduino/Adafruit_nRFCrypto.git#e31a8825ea3300b163a0a3c1ddd5de34e10e1371
|
||||
|
||||
; Common NRF52 debugging settings follow. See the Meshtastic developer docs for how to connect SWD debugging probes to your board.
|
||||
|
||||
; We want the initial breakpoint at setup() instead of main(). Also we want to enable semihosting at that point so instead of
|
||||
debug_init_break = tbreak setup
|
||||
; we just turn off the platformio tbreak and do it in .gdbinit (where we have more flexibility for scripting)
|
||||
; also we use a permanent breakpoint so it gets reused each time we restart the debugging session?
|
||||
; debug_init_break = tbreak main
|
||||
|
||||
; Note: add "monitor arm semihosting_redirect tcp 4444 all" if you want the stdout from the device to go to that port number instead
|
||||
; (for use by meshtastic command line)
|
||||
; monitor arm semihosting disable
|
||||
; monitor debug_level 3
|
||||
;
|
||||
; IMPORTANT: fileio must be disabled before using port 5555 - openocd ver 0.12 has a bug where if enabled it never properly parses the special :tt name
|
||||
; for stdio access.
|
||||
; monitor arm semihosting_redirect tcp 5555 stdio
|
||||
|
||||
; Also note: it is _impossible_ to do non blocking reads on the semihost console port (an oversight when ARM specified the semihost API).
|
||||
; So we'll neve be able to general purpose bi-directional communication with the device over semihosting.
|
||||
debug_extra_cmds =
|
||||
echo Running .gdbinit script
|
||||
;monitor arm semihosting enable
|
||||
;monitor arm semihosting_fileio enable
|
||||
;monitor arm semihosting_redirect disable
|
||||
commands 1
|
||||
; echo Breakpoint at setup() has semihosting console, connect to it with "telnet localhost 5555"
|
||||
; set wantSemihost = 1
|
||||
set useSoftDevice = 0
|
||||
end
|
||||
|
||||
; Only reprogram the board if the code has changed
|
||||
debug_load_mode = modified
|
||||
;debug_load_mode = manual
|
||||
; We default to the stlink adapter because it is very cheap and works well, though others (such as jlink) are also supported.
|
||||
;debug_tool = jlink
|
||||
debug_tool = stlink
|
||||
debug_speed = 4000
|
||||
;debug_tool = custom
|
||||
; debug_server =
|
||||
; openocd
|
||||
; -f
|
||||
; /usr/local/share/openocd/scripts/interface/stlink.cfg
|
||||
; -f
|
||||
; /usr/local/share/openocd/scripts/target/nrf52.cfg
|
||||
; $PLATFORMIO_CORE_DIR/packages/tool-openocd/openocd/scripts/interface/cmsis-dap.cfg
|
||||
|
||||
; Allows programming and debug via the RAK NanoDAP as the default debugger tool for the RAK4631 (it is only $10!)
|
||||
; programming time is about the same as the bootloader version.
|
||||
; For information on this see the meshtastic developers documentation for "Development on the NRF52"
|
||||
; We manually pass in the elf file so that pyocd can reverse engineer FreeRTOS data (running threads, etc...)
|
||||
;debug_server =
|
||||
; pyocd
|
||||
; gdbserver
|
||||
; -j
|
||||
; ${platformio.workspace_dir}/..
|
||||
; -t
|
||||
; nrf52840
|
||||
; --semihosting
|
||||
; --elf
|
||||
; ${platformio.build_dir}/${this.__env__}/firmware.elf
|
||||
|
||||
; If you want to debug the semihosting support you can turn on extra logging in pyocd with
|
||||
; -L
|
||||
; pyocd.debug.semihost.trace=debug
|
||||
|
||||
; The following is not needed because it automatically tries do this
|
||||
;debug_server_ready_pattern = -.*GDB server started on port \d+.*
|
||||
;debug_port = localhost:3333
|
||||
@@ -12,6 +12,7 @@ build_flags =
|
||||
-flto
|
||||
-Isrc/platform/stm32wl -g
|
||||
-DMESHTASTIC_MINIMIZE_BUILD
|
||||
-DMESHTASTIC_EXCLUDE_GPS
|
||||
-DDEBUG_MUTE
|
||||
; -DVECT_TAB_OFFSET=0x08000000
|
||||
-DconfigUSE_CMSIS_RTOS_V2=1
|
||||
@@ -21,7 +22,7 @@ build_flags =
|
||||
-fdata-sections
|
||||
|
||||
build_src_filter =
|
||||
${arduino_base.build_src_filter} -<platform/esp32/> -<nimble/> -<mesh/api/> -<mesh/wifi/> -<mesh/http/> -<modules/esp32> -<mesh/eth/> -<input> -<buzz> -<modules/Telemetry> -<platform/nrf52> -<platform/portduino> -<platform/rp2040> -<mesh/raspihttp>
|
||||
${arduino_base.build_src_filter} -<platform/esp32/> -<nimble/> -<mesh/api/> -<mesh/wifi/> -<mesh/http/> -<modules/esp32> -<mesh/eth/> -<input> -<buzz> -<modules/RemoteHardwareModule.cpp> -<platform/nrf52> -<platform/portduino> -<platform/rp2040> -<mesh/raspihttp>
|
||||
|
||||
board_upload.offset_address = 0x08000000
|
||||
upload_protocol = stlink
|
||||
@@ -32,5 +33,5 @@ lib_deps =
|
||||
https://github.com/caveman99/Crypto.git#f61ae26a53f7a2d0ba5511625b8bf8eff3a35d5e
|
||||
|
||||
lib_ignore =
|
||||
mathertel/OneButton
|
||||
https://github.com/mathertel/OneButton@~2.6.1
|
||||
Wire
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
+1
-1
@@ -11,7 +11,7 @@ rm -f $OUTDIR/firmware*
|
||||
rm -r $OUTDIR/* || true
|
||||
|
||||
# Important to pull latest version of libs into all device flavors, otherwise some devices might be stale
|
||||
platformio pkg update
|
||||
platformio pkg update -e $1
|
||||
|
||||
echo "Building for $1 with $PLATFORMIO_BUILD_FLAGS"
|
||||
rm -f .pio/build/$1/firmware.*
|
||||
|
||||
+6
-1
@@ -11,7 +11,7 @@ rm -f $OUTDIR/firmware*
|
||||
rm -r $OUTDIR/* || true
|
||||
|
||||
# Important to pull latest version of libs into all device flavors, otherwise some devices might be stale
|
||||
platformio pkg update
|
||||
platformio pkg update -e $1
|
||||
|
||||
echo "Building for $1 with $PLATFORMIO_BUILD_FLAGS"
|
||||
rm -f .pio/build/$1/firmware.*
|
||||
@@ -46,3 +46,8 @@ else
|
||||
cp bin/device-update.* $OUTDIR
|
||||
cp bin/*.uf2 $OUTDIR
|
||||
fi
|
||||
|
||||
if (echo $1 | grep -q "rak4631"); then
|
||||
echo "Copying hex file"
|
||||
cp .pio/build/$1/firmware.hex $OUTDIR/$basename.hex
|
||||
fi
|
||||
@@ -11,7 +11,7 @@ rm -f $OUTDIR/firmware*
|
||||
rm -r $OUTDIR/* || true
|
||||
|
||||
# Important to pull latest version of libs into all device flavors, otherwise some devices might be stale
|
||||
platformio pkg update
|
||||
platformio pkg update -e $1
|
||||
|
||||
echo "Building for $1 with $PLATFORMIO_BUILD_FLAGS"
|
||||
rm -f .pio/build/$1/firmware.*
|
||||
|
||||
Executable
+29
@@ -0,0 +1,29 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -e
|
||||
|
||||
VERSION=$(bin/buildinfo.py long)
|
||||
SHORT_VERSION=$(bin/buildinfo.py short)
|
||||
|
||||
OUTDIR=release/
|
||||
|
||||
rm -f $OUTDIR/firmware*
|
||||
rm -r $OUTDIR/* || true
|
||||
|
||||
# Important to pull latest version of libs into all device flavors, otherwise some devices might be stale
|
||||
platformio pkg update -e $1
|
||||
|
||||
echo "Building for $1 with $PLATFORMIO_BUILD_FLAGS"
|
||||
rm -f .pio/build/$1/firmware.*
|
||||
|
||||
# The shell vars the build tool expects to find
|
||||
export APP_VERSION=$VERSION
|
||||
|
||||
basename=firmware-$1-$VERSION
|
||||
|
||||
pio run --environment $1 # -v
|
||||
SRCELF=.pio/build/$1/firmware.elf
|
||||
cp $SRCELF $OUTDIR/$basename.elf
|
||||
|
||||
SRCBIN=.pio/build/$1/firmware.bin
|
||||
cp $SRCBIN $OUTDIR/$basename.bin
|
||||
+13
-1
@@ -54,6 +54,8 @@ Lora:
|
||||
|
||||
# ch341_quirk: true # Uncomment this to use the chunked SPI transfer that seems to fix the ch341
|
||||
|
||||
# spiSpeed: 2000000
|
||||
|
||||
### Set gpio chip to use in /dev/. Defaults to 0.
|
||||
### Notably the Raspberry Pi 5 puts the GPIO header on gpiochip4
|
||||
# gpiochip: 4
|
||||
@@ -111,6 +113,9 @@ Display:
|
||||
# Height: 320
|
||||
# Rotate: true
|
||||
|
||||
### You can also specify the spi device for the display to use
|
||||
# spidev: spidev0.0
|
||||
|
||||
Touchscreen:
|
||||
### Note, at least for now, the touchscreen must have a CS pin defined, even if you let Linux manage the CS switching.
|
||||
|
||||
@@ -126,15 +131,21 @@ Touchscreen:
|
||||
# CS: 7
|
||||
# IRQ: 17
|
||||
|
||||
### Configure device for direct keyboard input
|
||||
### You can also specify the spi device for the touchscreen to use
|
||||
# spidev: spidev0.0
|
||||
|
||||
|
||||
Input:
|
||||
### Configure device for direct keyboard input
|
||||
|
||||
# KeyboardDevice: /dev/input/by-id/usb-_Raspberry_Pi_Internal_Keyboard-event-kbd
|
||||
|
||||
###
|
||||
|
||||
Logging:
|
||||
LogLevel: info # debug, info, warn, error
|
||||
# TraceFile: /var/log/meshtasticd.json
|
||||
# AsciiLogs: true # default if not specified is !isatty() on stdout
|
||||
|
||||
Webserver:
|
||||
# Port: 443 # Port for Webserver & Webservices
|
||||
@@ -142,3 +153,4 @@ Webserver:
|
||||
|
||||
General:
|
||||
MaxNodes: 200
|
||||
MaxMessageQueue: 100
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
Binary file not shown.
File diff suppressed because it is too large
Load Diff
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -1,3 +1,4 @@
|
||||
#!/usr/bin/env python3
|
||||
# trunk-ignore-all(ruff/F821)
|
||||
# trunk-ignore-all(flake8/F821): For SConstruct imports
|
||||
import sys
|
||||
@@ -78,6 +79,11 @@ if platform.name == "espressif32":
|
||||
# For newer ESP32 targets, using newlib nano works better.
|
||||
env.Append(LINKFLAGS=["--specs=nano.specs", "-u", "_printf_float"])
|
||||
|
||||
if platform.name == "nordicnrf52":
|
||||
env.AddPostAction("$BUILD_DIR/${PROGNAME}.hex",
|
||||
env.VerboseAction(f"{sys.executable} ./bin/uf2conv.py $BUILD_DIR/firmware.hex -c -f 0xADA52840 -o $BUILD_DIR/firmware.uf2",
|
||||
"Generating UF2 file"))
|
||||
|
||||
Import("projenv")
|
||||
|
||||
prefsLoc = projenv["PROJECT_DIR"] + "/version.properties"
|
||||
|
||||
+20
-15
@@ -1,9 +1,5 @@
|
||||
|
||||
|
||||
import subprocess
|
||||
import configparser
|
||||
import traceback
|
||||
import sys
|
||||
import subprocess
|
||||
|
||||
|
||||
def readProps(prefsLoc):
|
||||
@@ -11,27 +7,36 @@ def readProps(prefsLoc):
|
||||
|
||||
config = configparser.RawConfigParser()
|
||||
config.read(prefsLoc)
|
||||
version = dict(config.items('VERSION'))
|
||||
verObj = dict(short = "{}.{}.{}".format(version["major"], version["minor"], version["build"]),
|
||||
long = "unset")
|
||||
version = dict(config.items("VERSION"))
|
||||
verObj = dict(
|
||||
short="{}.{}.{}".format(version["major"], version["minor"], version["build"]),
|
||||
long="unset",
|
||||
)
|
||||
|
||||
# Try to find current build SHA if if the workspace is clean. This could fail if git is not installed
|
||||
try:
|
||||
sha = subprocess.check_output(
|
||||
['git', 'rev-parse', '--short', 'HEAD']).decode("utf-8").strip()
|
||||
isDirty = subprocess.check_output(
|
||||
['git', 'diff', 'HEAD']).decode("utf-8").strip()
|
||||
sha = (
|
||||
subprocess.check_output(["git", "rev-parse", "--short", "HEAD"])
|
||||
.decode("utf-8")
|
||||
.strip()
|
||||
)
|
||||
isDirty = (
|
||||
subprocess.check_output(["git", "diff", "HEAD"]).decode("utf-8").strip()
|
||||
)
|
||||
suffix = sha
|
||||
# if isDirty:
|
||||
# # short for 'dirty', we want to keep our verstrings source for protobuf reasons
|
||||
# suffix = sha + "-d"
|
||||
verObj['long'] = "{}.{}.{}.{}".format(
|
||||
version["major"], version["minor"], version["build"], suffix)
|
||||
verObj["long"] = "{}.{}.{}.{}".format(
|
||||
version["major"], version["minor"], version["build"], suffix
|
||||
)
|
||||
except:
|
||||
# print("Unexpected error:", sys.exc_info()[0])
|
||||
# traceback.print_exc()
|
||||
verObj['long'] = verObj['short']
|
||||
verObj["long"] = verObj["short"]
|
||||
|
||||
# print("firmware version " + verStr)
|
||||
return verObj
|
||||
|
||||
|
||||
# print("path is" + ','.join(sys.path))
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
# tips from mark on how to replace the (broken) bootloader on the red wio_tracker_1110 boards.
|
||||
|
||||
~/.platformio/penv/bin/adafruit-nrfutil --verbose dfu serial --package wio_tracker_1110_bootloader-0.9.1_s140_7.3.0.zip -p /dev/ttyACM1 -b 115200 --singlebank --touch 1200
|
||||
|
||||
exit
|
||||
|
||||
# Output should look like
|
||||
|
||||
Upgrading target on /dev/ttyACM1 with DFU package /home/kevinh/development/meshtastic/WioWM1110/wio_tracker_1110_bootloader-0.9.1_s140_7.3.0.zip. Flow control is disabled, Single bank, Touch 1200
|
||||
Touched serial port /dev/ttyACM1
|
||||
Opened serial port /dev/ttyACM1
|
||||
Starting DFU upgrade of type 3, SoftDevice size: 152728, bootloader size: 39000, application size: 0
|
||||
Sending DFU start packet
|
||||
Sending DFU init packet
|
||||
Sending firmware file
|
||||
########################################
|
||||
########################################
|
||||
########################################
|
||||
########################################
|
||||
########################################
|
||||
########################################
|
||||
########################################
|
||||
########################################
|
||||
########################################
|
||||
###############
|
||||
Activating new firmware
|
||||
|
||||
DFU upgrade took 20.242434978485107s
|
||||
Device programmed.
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
"mcu": "esp32s3",
|
||||
"variant": "CDEBYTE_EoRa-S3"
|
||||
},
|
||||
"connectivity": ["wifi"],
|
||||
"connectivity": ["wifi", "bluetooth"],
|
||||
"debug": {
|
||||
"openocd_target": "esp32s3.cfg"
|
||||
},
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
"mcu": "esp32s3",
|
||||
"variant": "ESP32-S3-WROOM-1-N4"
|
||||
},
|
||||
"connectivity": ["wifi"],
|
||||
"connectivity": ["wifi", "bluetooth"],
|
||||
"debug": {
|
||||
"default_tool": "esp-builtin",
|
||||
"onboard_tools": ["esp-builtin"],
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
{
|
||||
"build": {
|
||||
"arduino": {
|
||||
"ldscript": "esp32s3_out.ld",
|
||||
"partitions": "default_8MB.csv"
|
||||
},
|
||||
"core": "esp32",
|
||||
"extra_flags": [
|
||||
"-DBOARD_HAS_PSRAM",
|
||||
"-DARDUINO_USB_CDC_ON_BOOT=1",
|
||||
"-DARDUINO_USB_MODE=0",
|
||||
"-DARDUINO_RUNNING_CORE=1",
|
||||
"-DARDUINO_EVENT_RUNNING_CORE=1"
|
||||
],
|
||||
"f_cpu": "240000000L",
|
||||
"f_flash": "80000000L",
|
||||
"flash_mode": "qio",
|
||||
"hwids": [
|
||||
["0x303A", "0x1001"],
|
||||
["0x303A", "0x0002"]
|
||||
],
|
||||
"mcu": "esp32s3",
|
||||
"variant": "heltec_vision_master_e290"
|
||||
},
|
||||
"connectivity": ["wifi", "bluetooth", "lora"],
|
||||
"debug": {
|
||||
"openocd_target": "esp32s3.cfg"
|
||||
},
|
||||
"frameworks": ["arduino", "espidf"],
|
||||
"name": "Heltec Vision Master E290",
|
||||
"upload": {
|
||||
"flash_size": "8MB",
|
||||
"maximum_ram_size": 327680,
|
||||
"maximum_size": 8388608,
|
||||
"use_1200bps_touch": true,
|
||||
"wait_for_upload_port": true,
|
||||
"require_upload_port": true,
|
||||
"speed": 921600
|
||||
},
|
||||
"url": "https://heltec.org/project/vision-master-e290/",
|
||||
"vendor": "Heltec"
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
{
|
||||
"build": {
|
||||
"arduino": {
|
||||
"ldscript": "esp32s3_out.ld",
|
||||
"partitions": "default_8MB.csv"
|
||||
},
|
||||
"core": "esp32",
|
||||
"extra_flags": [
|
||||
"-DBOARD_HAS_PSRAM",
|
||||
"-DARDUINO_USB_CDC_ON_BOOT=1",
|
||||
"-DARDUINO_USB_MODE=0",
|
||||
"-DARDUINO_RUNNING_CORE=1",
|
||||
"-DARDUINO_EVENT_RUNNING_CORE=1"
|
||||
],
|
||||
"f_cpu": "240000000L",
|
||||
"f_flash": "80000000L",
|
||||
"flash_mode": "qio",
|
||||
"hwids": [
|
||||
["0x303A", "0x1001"],
|
||||
["0x303A", "0x0002"]
|
||||
],
|
||||
"mcu": "esp32s3",
|
||||
"variant": "heltec_vision_master_t190"
|
||||
},
|
||||
"connectivity": ["wifi", "bluetooth", "lora"],
|
||||
"debug": {
|
||||
"openocd_target": "esp32s3.cfg"
|
||||
},
|
||||
"frameworks": ["arduino", "espidf"],
|
||||
"name": "Heltec Vision Master t190",
|
||||
"upload": {
|
||||
"flash_size": "8MB",
|
||||
"maximum_ram_size": 327680,
|
||||
"maximum_size": 8388608,
|
||||
"use_1200bps_touch": true,
|
||||
"wait_for_upload_port": true,
|
||||
"require_upload_port": true,
|
||||
"speed": 921600
|
||||
},
|
||||
"url": "https://heltec.org/project/vision-master-t190/",
|
||||
"vendor": "Heltec"
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
{
|
||||
"build": {
|
||||
"arduino": {
|
||||
"ldscript": "nrf52840_s140_v7.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": "ME25LS01-BOOT",
|
||||
"mcu": "nrf52840",
|
||||
"variant": "MINEWSEMI_ME25LS01",
|
||||
"bsp": {
|
||||
"name": "adafruit"
|
||||
},
|
||||
"softdevice": {
|
||||
"sd_flags": "-DS140",
|
||||
"sd_name": "s140",
|
||||
"sd_version": "7.3.0",
|
||||
"sd_fwid": "0x0123"
|
||||
},
|
||||
"bootloader": {
|
||||
"settings_addr": "0xFF000"
|
||||
}
|
||||
},
|
||||
"connectivity": ["bluetooth"],
|
||||
"debug": {
|
||||
"jlink_device": "nRF52840_xxAA",
|
||||
"svd_path": "nrf52840.svd"
|
||||
},
|
||||
"frameworks": ["arduino"],
|
||||
"name": "Minesemi ME25LS01",
|
||||
"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://en.minewsemi.com/lora-module/lr1110-nrf52840-me25LS01l",
|
||||
"vendor": "Minesemi"
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
{
|
||||
"build": {
|
||||
"arduino": {
|
||||
"ldscript": "nrf52840_s140_v7.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": "MS24SF1-BOOT",
|
||||
"mcu": "nrf52840",
|
||||
"variant": "MINEWSEMI_MS24SF1_SX1262",
|
||||
"bsp": {
|
||||
"name": "adafruit"
|
||||
},
|
||||
"softdevice": {
|
||||
"sd_flags": "-DS140",
|
||||
"sd_name": "s140",
|
||||
"sd_version": "7.3.0",
|
||||
"sd_fwid": "0x0123"
|
||||
},
|
||||
"bootloader": {
|
||||
"settings_addr": "0xFF000"
|
||||
}
|
||||
},
|
||||
"connectivity": ["bluetooth"],
|
||||
"debug": {
|
||||
"jlink_device": "nRF52840_xxAA",
|
||||
"svd_path": "nrf52840.svd"
|
||||
},
|
||||
"frameworks": ["arduino"],
|
||||
"name": "MINEWSEMI_MS24SF1_SX1262",
|
||||
"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://en.minewsemi.com/lora-module/nrf52840-sx1262-ms24sf1",
|
||||
"vendor": "Minesemi"
|
||||
}
|
||||
@@ -19,7 +19,7 @@
|
||||
"mcu": "esp32s3",
|
||||
"variant": "tlora-t3s3-v1"
|
||||
},
|
||||
"connectivity": ["wifi"],
|
||||
"connectivity": ["wifi", "bluetooth"],
|
||||
"debug": {
|
||||
"openocd_target": "esp32s3.cfg"
|
||||
},
|
||||
|
||||
@@ -5,16 +5,24 @@ Import("env")
|
||||
|
||||
# NOTE: This is not currently used, but can serve as an example on how to write extra_scripts
|
||||
|
||||
print("Current CLI targets", COMMAND_LINE_TARGETS)
|
||||
print("Current Build targets", BUILD_TARGETS)
|
||||
print("CPP defs", env.get("CPPDEFINES"))
|
||||
# print("Current CLI targets", COMMAND_LINE_TARGETS)
|
||||
# print("Current Build targets", BUILD_TARGETS)
|
||||
# print("CPP defs", env.get("CPPDEFINES"))
|
||||
# print(env.Dump())
|
||||
|
||||
# Adafruit.py in the platformio build tree is a bit naive and always enables their USB stack for building. We don't want this.
|
||||
# So come in after that python script has run and disable it. This hack avoids us having to fork that big project and send in a PR
|
||||
# which might not be accepted. -@geeksville
|
||||
|
||||
env["CPPDEFINES"].remove("USBCON")
|
||||
env["CPPDEFINES"].remove("USE_TINYUSB")
|
||||
lib_builders = env.get("__PIO_LIB_BUILDERS", None)
|
||||
if lib_builders is not None:
|
||||
print("Disabling Adafruit USB stack")
|
||||
for k in lib_builders:
|
||||
if k.name == "Adafruit TinyUSB Library":
|
||||
libenv = k.env
|
||||
# print(f"{k.name }: { libenv.Dump() } ")
|
||||
# libenv["CPPDEFINES"].remove("USBCON")
|
||||
libenv["CPPDEFINES"].remove("USE_TINYUSB")
|
||||
|
||||
# Custom actions when building program/firmware
|
||||
# env.AddPreAction("buildprog", callback...)
|
||||
|
||||
Submodule
+1
Submodule meshtestic added at 31ee3d90c8
@@ -18,10 +18,7 @@ import subprocess
|
||||
import sys
|
||||
|
||||
from platformio.project.exception import PlatformioException
|
||||
from platformio.public import (
|
||||
DeviceMonitorFilterBase,
|
||||
load_build_metadata,
|
||||
)
|
||||
from platformio.public import DeviceMonitorFilterBase, load_build_metadata
|
||||
|
||||
# By design, __init__ is called inside miniterm and we can't pass context to it.
|
||||
# pylint: disable=attribute-defined-outside-init
|
||||
@@ -32,7 +29,7 @@ IS_WINDOWS = sys.platform.startswith("win")
|
||||
class Esp32C3ExceptionDecoder(DeviceMonitorFilterBase):
|
||||
NAME = "esp32_c3_exception_decoder"
|
||||
|
||||
PCADDR_PATTERN = re.compile(r'0x4[0-9a-f]{7}', re.IGNORECASE)
|
||||
PCADDR_PATTERN = re.compile(r"0x4[0-9a-f]{7}", re.IGNORECASE)
|
||||
|
||||
def __call__(self):
|
||||
self.buffer = ""
|
||||
@@ -75,14 +72,14 @@ See https://docs.platformio.org/page/projectconf/build_configurations.html
|
||||
% self.__class__.__name__
|
||||
)
|
||||
return False
|
||||
|
||||
|
||||
if not os.path.isfile(self.addr2line_path):
|
||||
sys.stderr.write(
|
||||
"%s: disabling, addr2line at %s does not exist\n"
|
||||
% (self.__class__.__name__, self.addr2line_path)
|
||||
)
|
||||
return False
|
||||
|
||||
|
||||
return True
|
||||
except PlatformioException as e:
|
||||
sys.stderr.write(
|
||||
@@ -117,7 +114,7 @@ See https://docs.platformio.org/page/projectconf/build_configurations.html
|
||||
|
||||
trace = self.get_backtrace(m)
|
||||
if len(trace) != "":
|
||||
text = text[: last] + trace + text[last :]
|
||||
text = text[:last] + trace + text[last:]
|
||||
last += len(trace)
|
||||
|
||||
return text
|
||||
@@ -125,14 +122,10 @@ See https://docs.platformio.org/page/projectconf/build_configurations.html
|
||||
def get_backtrace(self, match):
|
||||
trace = "\n"
|
||||
enc = "mbcs" if IS_WINDOWS else "utf-8"
|
||||
args = [self.addr2line_path, u"-fipC", u"-e", self.firmware_path]
|
||||
args = [self.addr2line_path, "-fipC", "-e", self.firmware_path]
|
||||
try:
|
||||
addr = match.group()
|
||||
output = (
|
||||
subprocess.check_output(args + [addr])
|
||||
.decode(enc)
|
||||
.strip()
|
||||
)
|
||||
output = subprocess.check_output(args + [addr]).decode(enc).strip()
|
||||
output = output.replace(
|
||||
"\n", "\n "
|
||||
) # newlines happen with inlined methods
|
||||
|
||||
+7
-2
@@ -34,6 +34,7 @@ default_envs = tbeam
|
||||
;default_envs = wio-e5
|
||||
;default_envs = radiomaster_900_bandit_nano
|
||||
;default_envs = radiomaster_900_bandit_micro
|
||||
;default_envs = radiomaster_900_bandit
|
||||
;default_envs = heltec_capsule_sensor_v3
|
||||
;default_envs = heltec_vision_master_t190
|
||||
;default_envs = heltec_vision_master_e213
|
||||
@@ -45,6 +46,7 @@ extra_configs =
|
||||
variants/*/platformio.ini
|
||||
|
||||
[env]
|
||||
test_build_src = true
|
||||
extra_scripts = bin/platformio-custom.py
|
||||
|
||||
; note: we add src to our include search path so that lmic_project_config can override
|
||||
@@ -79,14 +81,16 @@ build_flags = -Wno-missing-field-initializers
|
||||
-DRADIOLIB_EXCLUDE_APRS
|
||||
-DRADIOLIB_EXCLUDE_LORAWAN
|
||||
-DMESHTASTIC_EXCLUDE_DROPZONE=1
|
||||
;-D OLED_PL
|
||||
|
||||
monitor_speed = 115200
|
||||
monitor_filters = direct
|
||||
|
||||
lib_deps =
|
||||
jgromes/RadioLib@~6.6.0
|
||||
; jgromes/RadioLib@~6.6.0
|
||||
https://github.com/jgromes/RadioLib.git#3115fc2d6700a9aee05888791ac930a910f2628f
|
||||
https://github.com/meshtastic/esp8266-oled-ssd1306.git#e16cee124fe26490cb14880c679321ad8ac89c95 ; ESP8266_SSD1306
|
||||
mathertel/OneButton@^2.5.0 ; OneButton library for non-blocking button debounce
|
||||
https://github.com/mathertel/OneButton@~2.6.1 ; OneButton library for non-blocking button debounce
|
||||
https://github.com/meshtastic/arduino-fsm.git#7db3702bf0cfe97b783d6c72595e3f38e0b19159
|
||||
https://github.com/meshtastic/TinyGPSPlus.git#71a82db35f3b973440044c476d4bcdc673b104f4
|
||||
https://github.com/meshtastic/ArduinoThread.git#1ae8778c85d0a2a729f989e0b1e7d7c4dc84eef0
|
||||
@@ -128,6 +132,7 @@ lib_deps =
|
||||
adafruit/Adafruit BMP280 Library@^2.6.8
|
||||
adafruit/Adafruit BMP085 Library@^1.2.4
|
||||
adafruit/Adafruit BME280 Library@^2.2.2
|
||||
adafruit/Adafruit BMP3XX Library@^2.1.5
|
||||
adafruit/Adafruit MCP9808 Library@^2.0.0
|
||||
adafruit/Adafruit INA260 Library@^1.5.0
|
||||
adafruit/Adafruit INA219@^1.2.0
|
||||
|
||||
+1
-1
Submodule protobufs updated: b1a79d5db0...0acaec6eff
@@ -1,3 +1,4 @@
|
||||
#include "Observer.h"
|
||||
#include "configuration.h"
|
||||
|
||||
#ifdef HAS_NCP5623
|
||||
@@ -22,10 +23,18 @@ class AmbientLightingThread : public concurrency::OSThread
|
||||
public:
|
||||
explicit AmbientLightingThread(ScanI2C::DeviceType type) : OSThread("AmbientLightingThread")
|
||||
{
|
||||
notifyDeepSleepObserver.observe(¬ifyDeepSleep); // Let us know when shutdown() is issued.
|
||||
|
||||
// Enables Ambient Lighting by default if conditions are meet.
|
||||
#if defined(HAS_NCP5623) || defined(RGBLED_RED) || defined(HAS_NEOPIXEL) || defined(UNPHONE)
|
||||
#ifdef ENABLE_AMBIENTLIGHTING
|
||||
moduleConfig.ambient_lighting.led_state = true;
|
||||
#endif
|
||||
#endif
|
||||
// Uncomment to test module
|
||||
// moduleConfig.ambient_lighting.led_state = true;
|
||||
// moduleConfig.ambient_lighting.current = 10;
|
||||
// // Default to a color based on our node number
|
||||
// Default to a color based on our node number
|
||||
// moduleConfig.ambient_lighting.red = (myNodeInfo.my_node_num & 0xFF0000) >> 16;
|
||||
// moduleConfig.ambient_lighting.green = (myNodeInfo.my_node_num & 0x00FF00) >> 8;
|
||||
// moduleConfig.ambient_lighting.blue = myNodeInfo.my_node_num & 0x0000FF;
|
||||
@@ -82,9 +91,46 @@ class AmbientLightingThread : public concurrency::OSThread
|
||||
return disable();
|
||||
}
|
||||
|
||||
// When shutdown() is issued, setLightingOff will be called.
|
||||
CallbackObserver<AmbientLightingThread, void *> notifyDeepSleepObserver =
|
||||
CallbackObserver<AmbientLightingThread, void *>(this, &AmbientLightingThread::setLightingOff);
|
||||
|
||||
private:
|
||||
ScanI2C::DeviceType _type = ScanI2C::DeviceType::NONE;
|
||||
|
||||
// Turn RGB lighting off, is used in junction to shutdown()
|
||||
int setLightingOff(void *unused)
|
||||
{
|
||||
#ifdef HAS_NCP5623
|
||||
rgb.setCurrent(0);
|
||||
rgb.setRed(0);
|
||||
rgb.setGreen(0);
|
||||
rgb.setBlue(0);
|
||||
LOG_INFO("Turn Off NCP5623 Ambient lighting.\n");
|
||||
#endif
|
||||
#ifdef HAS_NEOPIXEL
|
||||
pixels.clear();
|
||||
pixels.show();
|
||||
LOG_INFO("Turn Off NeoPixel Ambient lighting.\n");
|
||||
#endif
|
||||
#ifdef RGBLED_CA
|
||||
analogWrite(RGBLED_RED, 255 - 0);
|
||||
analogWrite(RGBLED_GREEN, 255 - 0);
|
||||
analogWrite(RGBLED_BLUE, 255 - 0);
|
||||
LOG_INFO("Turn Off Ambient lighting RGB Common Anode.\n");
|
||||
#elif defined(RGBLED_RED)
|
||||
analogWrite(RGBLED_RED, 0);
|
||||
analogWrite(RGBLED_GREEN, 0);
|
||||
analogWrite(RGBLED_BLUE, 0);
|
||||
LOG_INFO("Turn Off Ambient lighting RGB Common Cathode.\n");
|
||||
#endif
|
||||
#ifdef UNPHONE
|
||||
unphone.rgb(0, 0, 0);
|
||||
LOG_INFO("Turn Off unPhone Ambient lighting.\n");
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
void setLighting()
|
||||
{
|
||||
#ifdef HAS_NCP5623
|
||||
@@ -100,6 +146,17 @@ class AmbientLightingThread : public concurrency::OSThread
|
||||
pixels.fill(pixels.Color(moduleConfig.ambient_lighting.red, moduleConfig.ambient_lighting.green,
|
||||
moduleConfig.ambient_lighting.blue),
|
||||
0, NEOPIXEL_COUNT);
|
||||
|
||||
// RadioMaster Bandit has addressable LED at the two buttons
|
||||
// this allow us to set different lighting for them in variant.h file.
|
||||
#ifdef RADIOMASTER_900_BANDIT
|
||||
#if defined(BUTTON1_COLOR) && defined(BUTTON1_COLOR_INDEX)
|
||||
pixels.fill(BUTTON1_COLOR, BUTTON1_COLOR_INDEX, 1);
|
||||
#endif
|
||||
#if defined(BUTTON2_COLOR) && defined(BUTTON2_COLOR_INDEX)
|
||||
pixels.fill(BUTTON2_COLOR, BUTTON1_COLOR_INDEX, 1);
|
||||
#endif
|
||||
#endif
|
||||
pixels.show();
|
||||
LOG_DEBUG("Initializing NeoPixel Ambient lighting w/ brightness(current)=%d, red=%d, green=%d, blue=%d\n",
|
||||
moduleConfig.ambient_lighting.current, moduleConfig.ambient_lighting.red, moduleConfig.ambient_lighting.green,
|
||||
|
||||
@@ -144,8 +144,8 @@ int32_t ButtonThread::runOnce()
|
||||
|
||||
case BUTTON_EVENT_DOUBLE_PRESSED: {
|
||||
LOG_BUTTON("Double press!\n");
|
||||
service.refreshLocalMeshNode();
|
||||
auto sentPosition = service.trySendPosition(NODENUM_BROADCAST, true);
|
||||
service->refreshLocalMeshNode();
|
||||
auto sentPosition = service->trySendPosition(NODENUM_BROADCAST, true);
|
||||
if (screen) {
|
||||
if (sentPosition)
|
||||
screen->print("Sent ad-hoc position\n");
|
||||
@@ -224,7 +224,6 @@ int32_t ButtonThread::runOnce()
|
||||
btnEvent = BUTTON_EVENT_NONE;
|
||||
}
|
||||
|
||||
runASAP = false;
|
||||
return 50;
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -13,7 +13,7 @@
|
||||
#endif
|
||||
|
||||
#ifndef BUTTON_TOUCH_MS
|
||||
#define BUTTON_TOCH_MS 400
|
||||
#define BUTTON_TOUCH_MS 400
|
||||
#endif
|
||||
|
||||
class ButtonThread : public concurrency::OSThread
|
||||
|
||||
@@ -26,6 +26,20 @@ SOFTWARE.*/
|
||||
|
||||
#include "DebugConfiguration.h"
|
||||
|
||||
#ifdef ARCH_PORTDUINO
|
||||
#include "platform/portduino/PortduinoGlue.h"
|
||||
#endif
|
||||
|
||||
/// A C wrapper for LOG_DEBUG that can be used from arduino C libs that don't know about C++ or meshtastic
|
||||
extern "C" void logLegacy(const char *level, const char *fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
if (console)
|
||||
console->vprintf(level, fmt, args);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
#if HAS_NETWORKING
|
||||
|
||||
Syslog::Syslog(UDP &client)
|
||||
@@ -83,12 +97,14 @@ Syslog &Syslog::logMask(uint8_t priMask)
|
||||
|
||||
void Syslog::enable()
|
||||
{
|
||||
this->_client->begin(this->_port);
|
||||
this->_enabled = true;
|
||||
}
|
||||
|
||||
void Syslog::disable()
|
||||
{
|
||||
this->_enabled = false;
|
||||
this->_client->stop();
|
||||
}
|
||||
|
||||
bool Syslog::isEnabled()
|
||||
@@ -129,6 +145,11 @@ bool Syslog::vlogf(uint16_t pri, const char *appName, const char *fmt, va_list a
|
||||
inline bool Syslog::_sendLog(uint16_t pri, const char *appName, const char *message)
|
||||
{
|
||||
int result;
|
||||
#ifdef ARCH_PORTDUINO
|
||||
bool utf = !settingsMap[ascii_logs];
|
||||
#else
|
||||
bool utf = true;
|
||||
#endif
|
||||
|
||||
if (!this->_enabled)
|
||||
return false;
|
||||
@@ -159,7 +180,12 @@ inline bool Syslog::_sendLog(uint16_t pri, const char *appName, const char *mess
|
||||
this->_client->print(this->_deviceHostname);
|
||||
this->_client->print(' ');
|
||||
this->_client->print(appName);
|
||||
this->_client->print(F(" - - - \xEF\xBB\xBF"));
|
||||
this->_client->print(F(" - - - "));
|
||||
if (utf) {
|
||||
this->_client->print(F("\xEF\xBB\xBF"));
|
||||
} else {
|
||||
this->_client->print(F(" "));
|
||||
}
|
||||
this->_client->print(F("["));
|
||||
this->_client->print(int(millis() / 1000));
|
||||
this->_client->print(F("]: "));
|
||||
|
||||
@@ -3,8 +3,8 @@
|
||||
#include "configuration.h"
|
||||
|
||||
// DEBUG LED
|
||||
#ifndef LED_INVERTED
|
||||
#define LED_INVERTED 0 // define as 1 if LED is active low (on)
|
||||
#ifndef LED_STATE_ON
|
||||
#define LED_STATE_ON 1
|
||||
#endif
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
@@ -45,7 +45,7 @@
|
||||
#define LOG_CRIT(...) SEGGER_RTT_printf(0, __VA_ARGS__)
|
||||
#define LOG_TRACE(...) SEGGER_RTT_printf(0, __VA_ARGS__)
|
||||
#else
|
||||
#if defined(DEBUG_PORT) && !defined(DEBUG_MUTE)
|
||||
#if defined(DEBUG_PORT) && !defined(DEBUG_MUTE) && !defined(PIO_UNIT_TESTING)
|
||||
#define LOG_DEBUG(...) DEBUG_PORT.log(MESHTASTIC_LOG_LEVEL_DEBUG, __VA_ARGS__)
|
||||
#define LOG_INFO(...) DEBUG_PORT.log(MESHTASTIC_LOG_LEVEL_INFO, __VA_ARGS__)
|
||||
#define LOG_WARN(...) DEBUG_PORT.log(MESHTASTIC_LOG_LEVEL_WARN, __VA_ARGS__)
|
||||
@@ -62,6 +62,9 @@
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/// A C wrapper for LOG_DEBUG that can be used from arduino C libs that don't know about C++ or meshtastic
|
||||
extern "C" void logLegacy(const char *level, const char *fmt, ...);
|
||||
|
||||
#define SYSLOG_NILVALUE "-"
|
||||
|
||||
#define SYSLOG_CRIT 2 /* critical conditions */
|
||||
|
||||
@@ -3,6 +3,9 @@
|
||||
const char *DisplayFormatters::getModemPresetDisplayName(meshtastic_Config_LoRaConfig_ModemPreset preset, bool useShortName)
|
||||
{
|
||||
switch (preset) {
|
||||
case meshtastic_Config_LoRaConfig_ModemPreset_SHORT_TURBO:
|
||||
return useShortName ? "ShortT" : "ShortTurbo";
|
||||
break;
|
||||
case meshtastic_Config_LoRaConfig_ModemPreset_SHORT_SLOW:
|
||||
return useShortName ? "ShortS" : "ShortSlow";
|
||||
break;
|
||||
|
||||
+15
-88
@@ -48,6 +48,15 @@ void OSFS::writeNBytes(uint16_t address, unsigned int num, const byte *input)
|
||||
}
|
||||
#endif
|
||||
|
||||
bool lfs_assert_failed =
|
||||
false; // Note: we use this global on all platforms, though it can only be set true on nrf52 (in our modified lfs_util.h)
|
||||
|
||||
extern "C" void lfs_assert(const char *reason)
|
||||
{
|
||||
LOG_ERROR("LFS assert: %s\n", reason);
|
||||
lfs_assert_failed = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Copies a file from one location to another.
|
||||
*
|
||||
@@ -199,7 +208,7 @@ std::vector<meshtastic_FileInfo> getFiles(const char *dirname, uint8_t levels)
|
||||
* @param levels The number of levels of subdirectories to list.
|
||||
* @param del Whether or not to delete the contents of the directory after listing.
|
||||
*/
|
||||
void listDir(const char *dirname, uint8_t levels, bool del = false)
|
||||
void listDir(const char *dirname, uint8_t levels, bool del)
|
||||
{
|
||||
#ifdef FSCom
|
||||
#if (defined(ARCH_ESP32) || defined(ARCH_RP2040) || defined(ARCH_PORTDUINO))
|
||||
@@ -214,7 +223,9 @@ void listDir(const char *dirname, uint8_t levels, bool del = false)
|
||||
}
|
||||
|
||||
File file = root.openNextFile();
|
||||
while (file) {
|
||||
while (
|
||||
file &&
|
||||
file.name()[0]) { // This file.name() check is a workaround for a bug in the Adafruit LittleFS nrf52 glue (see issue 4395)
|
||||
if (file.isDirectory() && !String(file.name()).endsWith(".")) {
|
||||
if (levels) {
|
||||
#ifdef ARCH_ESP32
|
||||
@@ -238,6 +249,7 @@ void listDir(const char *dirname, uint8_t levels, bool del = false)
|
||||
file.close();
|
||||
}
|
||||
#else
|
||||
LOG_DEBUG(" %s (directory)\n", file.name());
|
||||
listDir(file.name(), levels - 1, del);
|
||||
file.close();
|
||||
#endif
|
||||
@@ -264,7 +276,7 @@ void listDir(const char *dirname, uint8_t levels, bool del = false)
|
||||
file.close();
|
||||
}
|
||||
#else
|
||||
LOG_DEBUG(" %s (%i Bytes)\n", file.name(), file.size());
|
||||
LOG_DEBUG(" %s (%i Bytes)\n", file.name(), file.size());
|
||||
file.close();
|
||||
#endif
|
||||
}
|
||||
@@ -313,62 +325,6 @@ void rmDir(const char *dirname)
|
||||
#endif
|
||||
}
|
||||
|
||||
bool fsCheck()
|
||||
{
|
||||
#if defined(ARCH_NRF52)
|
||||
size_t write_size = 0;
|
||||
size_t read_size = 0;
|
||||
char buf[32] = {0};
|
||||
|
||||
Adafruit_LittleFS_Namespace::File file(FSCom);
|
||||
const char *text = "meshtastic fs test";
|
||||
size_t text_length = strlen(text);
|
||||
const char *filename = "/meshtastic.txt";
|
||||
|
||||
LOG_DEBUG("Try create file .\n");
|
||||
if (file.open(filename, FILE_O_WRITE)) {
|
||||
write_size = file.write(text);
|
||||
} else {
|
||||
LOG_DEBUG("Open file failed .\n");
|
||||
goto FORMAT_FS;
|
||||
}
|
||||
|
||||
if (write_size != text_length) {
|
||||
LOG_DEBUG("Text bytes do not match .\n");
|
||||
file.close();
|
||||
goto FORMAT_FS;
|
||||
}
|
||||
|
||||
file.close();
|
||||
|
||||
if (!file.open(filename, FILE_O_READ)) {
|
||||
LOG_DEBUG("Open file failed .\n");
|
||||
goto FORMAT_FS;
|
||||
}
|
||||
|
||||
read_size = file.readBytes(buf, text_length);
|
||||
if (read_size != text_length) {
|
||||
LOG_DEBUG("Text bytes do not match .\n");
|
||||
file.close();
|
||||
goto FORMAT_FS;
|
||||
}
|
||||
|
||||
if (memcmp(buf, text, text_length) != 0) {
|
||||
LOG_DEBUG("The written bytes do not match the read bytes .\n");
|
||||
file.close();
|
||||
goto FORMAT_FS;
|
||||
}
|
||||
return true;
|
||||
FORMAT_FS:
|
||||
LOG_DEBUG("Format FS ....\n");
|
||||
FSCom.format();
|
||||
FSCom.begin();
|
||||
return false;
|
||||
#else
|
||||
return true;
|
||||
#endif
|
||||
}
|
||||
|
||||
void fsInit()
|
||||
{
|
||||
#ifdef FSCom
|
||||
@@ -378,35 +334,6 @@ void fsInit()
|
||||
}
|
||||
#if defined(ARCH_ESP32)
|
||||
LOG_DEBUG("Filesystem files (%d/%d Bytes):\n", FSCom.usedBytes(), FSCom.totalBytes());
|
||||
#elif defined(ARCH_NRF52)
|
||||
/*
|
||||
* nRF52840 has a certain chance of automatic formatting failure.
|
||||
* Try to create a file after initializing the file system. If the creation fails,
|
||||
* it means that the file system is not working properly. Please format it manually again.
|
||||
* To check the normality of the file system, you need to disable the LFS_NO_ASSERT assertion.
|
||||
* Otherwise, the assertion will be entered at the moment of reading or opening, and the FS will not be formatted.
|
||||
* */
|
||||
bool ret = false;
|
||||
uint8_t retry = 3;
|
||||
|
||||
while (retry--) {
|
||||
ret = fsCheck();
|
||||
if (ret) {
|
||||
LOG_DEBUG("File system check is OK.\n");
|
||||
break;
|
||||
}
|
||||
delay(10);
|
||||
}
|
||||
|
||||
// It may not be possible to reach this step.
|
||||
// Add a loop here to prevent unpredictable situations from happening.
|
||||
// Can add a screen to display error status later.
|
||||
if (!ret) {
|
||||
while (1) {
|
||||
LOG_ERROR("The file system is damaged and cannot proceed to the next step.\n");
|
||||
delay(1000);
|
||||
}
|
||||
}
|
||||
#else
|
||||
LOG_DEBUG("Filesystem files:\n");
|
||||
#endif
|
||||
|
||||
+6
-2
@@ -51,9 +51,13 @@ using namespace Adafruit_LittleFS_Namespace;
|
||||
#endif
|
||||
|
||||
void fsInit();
|
||||
void fsListFiles();
|
||||
bool copyFile(const char *from, const char *to);
|
||||
bool renameFile(const char *pathFrom, const char *pathTo);
|
||||
std::vector<meshtastic_FileInfo> getFiles(const char *dirname, uint8_t levels);
|
||||
void listDir(const char *dirname, uint8_t levels, bool del);
|
||||
void listDir(const char *dirname, uint8_t levels, bool del = false);
|
||||
void rmDir(const char *dirname);
|
||||
void setupSDCard();
|
||||
void setupSDCard();
|
||||
|
||||
extern bool lfs_assert_failed; // Note: we use this global on all platforms, though it can only be set true on nrf52 (in our
|
||||
// modified lfs_util.h)
|
||||
|
||||
@@ -0,0 +1,104 @@
|
||||
#include "GpioLogic.h"
|
||||
#include <assert.h>
|
||||
|
||||
void GpioVirtPin::set(bool value)
|
||||
{
|
||||
if (value != this->value) {
|
||||
this->value = value ? PinState::On : PinState::Off;
|
||||
if (dependentPin)
|
||||
dependentPin->update();
|
||||
}
|
||||
}
|
||||
|
||||
void GpioHwPin::set(bool value)
|
||||
{
|
||||
// if (num == 3) LOG_DEBUG("Setting pin %d to %d\n", num, value);
|
||||
pinMode(num, OUTPUT);
|
||||
digitalWrite(num, value);
|
||||
}
|
||||
|
||||
GpioTransformer::GpioTransformer(GpioPin *outPin) : outPin(outPin) {}
|
||||
|
||||
void GpioTransformer::set(bool value)
|
||||
{
|
||||
outPin->set(value);
|
||||
}
|
||||
|
||||
GpioUnaryTransformer::GpioUnaryTransformer(GpioVirtPin *inPin, GpioPin *outPin) : GpioTransformer(outPin), inPin(inPin)
|
||||
{
|
||||
assert(!inPin->dependentPin); // We only allow one dependent pin
|
||||
inPin->dependentPin = this;
|
||||
|
||||
// Don't update at construction time, because various GpioPins might be global constructor based not yet initied because
|
||||
// order of operations for global constructors is not defined.
|
||||
// update();
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the output pin based on the current state of the input pin.
|
||||
*/
|
||||
void GpioUnaryTransformer::update()
|
||||
{
|
||||
auto p = inPin->get();
|
||||
if (p == GpioVirtPin::PinState::Unset)
|
||||
return; // Not yet fully initialized
|
||||
|
||||
set(p);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the output pin based on the current state of the input pin.
|
||||
*/
|
||||
void GpioNotTransformer::update()
|
||||
{
|
||||
auto p = inPin->get();
|
||||
if (p == GpioVirtPin::PinState::Unset)
|
||||
return; // Not yet fully initialized
|
||||
|
||||
set(!p);
|
||||
}
|
||||
|
||||
GpioBinaryTransformer::GpioBinaryTransformer(GpioVirtPin *inPin1, GpioVirtPin *inPin2, GpioPin *outPin, Operation operation)
|
||||
: GpioTransformer(outPin), inPin1(inPin1), inPin2(inPin2), operation(operation)
|
||||
{
|
||||
assert(!inPin1->dependentPin); // We only allow one dependent pin
|
||||
inPin1->dependentPin = this;
|
||||
assert(!inPin2->dependentPin); // We only allow one dependent pin
|
||||
inPin2->dependentPin = this;
|
||||
|
||||
// Don't update at construction time, because various GpioPins might be global constructor based not yet initied because
|
||||
// order of operations for global constructors is not defined.
|
||||
// update();
|
||||
}
|
||||
|
||||
void GpioBinaryTransformer::update()
|
||||
{
|
||||
auto p1 = inPin1->get(), p2 = inPin2->get();
|
||||
GpioVirtPin::PinState newValue = GpioVirtPin::PinState::Unset;
|
||||
|
||||
if (p1 == GpioVirtPin::PinState::Unset)
|
||||
newValue = p2; // Not yet fully initialized
|
||||
else if (p2 == GpioVirtPin::PinState::Unset)
|
||||
newValue = p1; // Not yet fully initialized
|
||||
|
||||
// If we've already found our value just use it, otherwise need to do the operation
|
||||
if (newValue == GpioVirtPin::PinState::Unset) {
|
||||
switch (operation) {
|
||||
case And:
|
||||
newValue = (GpioVirtPin::PinState)(p1 && p2);
|
||||
break;
|
||||
case Or:
|
||||
// LOG_DEBUG("Doing GPIO OR\n");
|
||||
newValue = (GpioVirtPin::PinState)(p1 || p2);
|
||||
break;
|
||||
case Xor:
|
||||
newValue = (GpioVirtPin::PinState)(p1 != p2);
|
||||
break;
|
||||
default:
|
||||
assert(false);
|
||||
}
|
||||
}
|
||||
set(newValue);
|
||||
}
|
||||
|
||||
GpioSplitter::GpioSplitter(GpioPin *outPin1, GpioPin *outPin2) : outPin1(outPin1), outPin2(outPin2) {}
|
||||
+160
@@ -0,0 +1,160 @@
|
||||
#pragma once
|
||||
|
||||
#include "configuration.h"
|
||||
|
||||
/**This is a set of classes to mediate access to GPIOs in a structured way. Most usage of GPIOs do not
|
||||
require these classes! But if your hardware has a GPIO that is 'shared' between multiple devices (i.e. a shared power enable)
|
||||
then using these classes might be able to let you cleanly turn on that enable when either dependent device is needed.
|
||||
|
||||
Note: these classes are intended to be 99% inline for the common case so should have minimal impact on flash or RAM
|
||||
requirements.
|
||||
*/
|
||||
|
||||
/**
|
||||
* A logical GPIO pin (not necessary raw hardware).
|
||||
*/
|
||||
class GpioPin
|
||||
{
|
||||
public:
|
||||
virtual void set(bool value) = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* A physical GPIO hw pin.
|
||||
*/
|
||||
class GpioHwPin : public GpioPin
|
||||
{
|
||||
uint32_t num;
|
||||
|
||||
public:
|
||||
explicit GpioHwPin(uint32_t num) : num(num) {}
|
||||
|
||||
void set(bool value);
|
||||
};
|
||||
|
||||
class GpioTransformer;
|
||||
class GpioNotTransformer;
|
||||
class GpioBinaryTransformer;
|
||||
|
||||
/**
|
||||
* A virtual GPIO pin.
|
||||
*/
|
||||
class GpioVirtPin : public GpioPin
|
||||
{
|
||||
friend class GpioBinaryTransformer;
|
||||
friend class GpioUnaryTransformer;
|
||||
|
||||
public:
|
||||
enum PinState { On = true, Off = false, Unset = 2 };
|
||||
|
||||
void set(bool value);
|
||||
PinState get() const { return value; }
|
||||
|
||||
private:
|
||||
PinState value = PinState::Unset;
|
||||
GpioTransformer *dependentPin = NULL;
|
||||
};
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
/**
|
||||
* A 'smart' trigger that can depend in a fake GPIO and if that GPIO changes, drive some other downstream GPIO to change.
|
||||
* notably: the set method is not public (because it always is calculated by a subclass)
|
||||
*/
|
||||
class GpioTransformer
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Update the output pin based on the current state of the input pin.
|
||||
*/
|
||||
virtual void update() = 0;
|
||||
|
||||
protected:
|
||||
GpioTransformer(GpioPin *outPin);
|
||||
|
||||
void set(bool value);
|
||||
|
||||
private:
|
||||
GpioPin *outPin;
|
||||
};
|
||||
|
||||
/**
|
||||
* A transformer that just drives a hw pin based on a virtual pin.
|
||||
*/
|
||||
class GpioUnaryTransformer : public GpioTransformer
|
||||
{
|
||||
public:
|
||||
GpioUnaryTransformer(GpioVirtPin *inPin, GpioPin *outPin);
|
||||
|
||||
protected:
|
||||
friend class GpioVirtPin;
|
||||
|
||||
/**
|
||||
* Update the output pin based on the current state of the input pin.
|
||||
*/
|
||||
virtual void update();
|
||||
|
||||
GpioVirtPin *inPin;
|
||||
};
|
||||
|
||||
/**
|
||||
* A transformer that performs a unary NOT operation from an input.
|
||||
*/
|
||||
class GpioNotTransformer : public GpioUnaryTransformer
|
||||
{
|
||||
public:
|
||||
GpioNotTransformer(GpioVirtPin *inPin, GpioPin *outPin) : GpioUnaryTransformer(inPin, outPin) {}
|
||||
|
||||
protected:
|
||||
friend class GpioVirtPin;
|
||||
|
||||
/**
|
||||
* Update the output pin based on the current state of the input pin.
|
||||
*/
|
||||
void update();
|
||||
};
|
||||
|
||||
/**
|
||||
* A transformer that combines multiple virtual pins to drive an output pin
|
||||
*/
|
||||
class GpioBinaryTransformer : public GpioTransformer
|
||||
{
|
||||
|
||||
public:
|
||||
enum Operation { And, Or, Xor };
|
||||
|
||||
GpioBinaryTransformer(GpioVirtPin *inPin1, GpioVirtPin *inPin2, GpioPin *outPin, Operation operation);
|
||||
|
||||
protected:
|
||||
friend class GpioVirtPin;
|
||||
|
||||
/**
|
||||
* Update the output pin based on the current state of the input pins.
|
||||
*/
|
||||
void update();
|
||||
|
||||
private:
|
||||
GpioVirtPin *inPin1;
|
||||
GpioVirtPin *inPin2;
|
||||
Operation operation;
|
||||
};
|
||||
|
||||
/**
|
||||
* Sometimes a single output GPIO single needs to drive multiple physical GPIOs. This class provides that.
|
||||
*/
|
||||
class GpioSplitter : public GpioPin
|
||||
{
|
||||
|
||||
public:
|
||||
GpioSplitter(GpioPin *outPin1, GpioPin *outPin2);
|
||||
|
||||
void set(bool value)
|
||||
{
|
||||
outPin1->set(value);
|
||||
outPin2->set(value);
|
||||
}
|
||||
|
||||
private:
|
||||
GpioPin *outPin1;
|
||||
GpioPin *outPin2;
|
||||
};
|
||||
+66
@@ -0,0 +1,66 @@
|
||||
#include "Led.h"
|
||||
#include "PowerMon.h"
|
||||
#include "main.h"
|
||||
#include "power.h"
|
||||
|
||||
GpioVirtPin ledForceOn, ledBlink;
|
||||
|
||||
#if defined(LED_PIN)
|
||||
// Most boards have a GPIO for LED control
|
||||
static GpioHwPin ledRawHwPin(LED_PIN);
|
||||
#else
|
||||
static GpioVirtPin ledRawHwPin; // Dummy pin for no hardware
|
||||
#endif
|
||||
|
||||
#if LED_STATE_ON == 0
|
||||
static GpioVirtPin ledHwPin;
|
||||
static GpioNotTransformer ledInverter(&ledHwPin, &ledRawHwPin);
|
||||
#else
|
||||
static GpioPin &ledHwPin = ledRawHwPin;
|
||||
#endif
|
||||
|
||||
#if defined(HAS_PMU)
|
||||
/**
|
||||
* A GPIO controlled by the PMU
|
||||
*/
|
||||
class GpioPmuPin : public GpioPin
|
||||
{
|
||||
public:
|
||||
void set(bool value)
|
||||
{
|
||||
if (pmu_found && PMU) {
|
||||
// blink the axp led
|
||||
PMU->setChargingLedMode(value ? XPOWERS_CHG_LED_ON : XPOWERS_CHG_LED_OFF);
|
||||
}
|
||||
}
|
||||
} ledPmuHwPin;
|
||||
|
||||
// In some cases we need to drive a PMU LED and a normal LED
|
||||
static GpioSplitter ledFinalPin(&ledHwPin, &ledPmuHwPin);
|
||||
#else
|
||||
static GpioPin &ledFinalPin = ledHwPin;
|
||||
#endif
|
||||
|
||||
#ifdef USE_POWERMON
|
||||
/**
|
||||
* We monitor changes to the LED drive output because we use that as a sanity test in our power monitor stuff.
|
||||
*/
|
||||
class MonitoredLedPin : public GpioPin
|
||||
{
|
||||
public:
|
||||
void set(bool value)
|
||||
{
|
||||
if (powerMon) {
|
||||
if (value)
|
||||
powerMon->setState(meshtastic_PowerMon_State_LED_On);
|
||||
else
|
||||
powerMon->clearState(meshtastic_PowerMon_State_LED_On);
|
||||
}
|
||||
ledFinalPin.set(value);
|
||||
}
|
||||
} monitoredLedPin;
|
||||
#else
|
||||
static GpioPin &monitoredLedPin = ledFinalPin;
|
||||
#endif
|
||||
|
||||
static GpioBinaryTransformer ledForcer(&ledForceOn, &ledBlink, &monitoredLedPin, GpioBinaryTransformer::Or);
|
||||
@@ -0,0 +1,7 @@
|
||||
#include "GpioLogic.h"
|
||||
#include "configuration.h"
|
||||
|
||||
/**
|
||||
* ledForceOn and ledForceOff both override the normal ledBlinker behavior (which is controlled by main)
|
||||
*/
|
||||
extern GpioVirtPin ledForceOn, ledBlink;
|
||||
@@ -1,60 +0,0 @@
|
||||
#include "OSTimer.h"
|
||||
#include "configuration.h"
|
||||
|
||||
/**
|
||||
* Schedule a callback to run. The callback must _not_ block, though it is called from regular thread level (not ISR)
|
||||
*
|
||||
* NOTE! xTimerPend... seems to ignore the time passed in on ESP32 and on NRF52
|
||||
* The reason this didn't work is because xTimerPednFunctCall really isn't a timer function at all - it just means run the
|
||||
callback
|
||||
* from the timer thread the next time you have spare cycles.
|
||||
*
|
||||
* @return true if successful, false if the timer fifo is too full.
|
||||
|
||||
bool scheduleOSCallback(PendableFunction callback, void *param1, uint32_t param2, uint32_t delayMsec)
|
||||
{
|
||||
return xTimerPendFunctionCall(callback, param1, param2, pdMS_TO_TICKS(delayMsec));
|
||||
} */
|
||||
|
||||
#ifdef ARCH_ESP32
|
||||
|
||||
// Super skanky quick hack to use hardware timers of the ESP32
|
||||
static hw_timer_t *timer;
|
||||
static PendableFunction tCallback;
|
||||
static void *tParam1;
|
||||
static uint32_t tParam2;
|
||||
|
||||
static void IRAM_ATTR onTimer()
|
||||
{
|
||||
(*tCallback)(tParam1, tParam2);
|
||||
}
|
||||
|
||||
/**
|
||||
* Schedules a hardware callback function to be executed after a specified delay.
|
||||
*
|
||||
* @param callback The function to be executed.
|
||||
* @param param1 The first parameter to be passed to the function.
|
||||
* @param param2 The second parameter to be passed to the function.
|
||||
* @param delayMsec The delay time in milliseconds before the function is executed.
|
||||
*
|
||||
* @return True if the function was successfully scheduled, false otherwise.
|
||||
*/
|
||||
bool scheduleHWCallback(PendableFunction callback, void *param1, uint32_t param2, uint32_t delayMsec)
|
||||
{
|
||||
if (!timer) {
|
||||
timer = timerBegin(0, 80, true); // one usec per tick (main clock is 80MhZ on ESP32)
|
||||
assert(timer);
|
||||
timerAttachInterrupt(timer, &onTimer, true);
|
||||
}
|
||||
|
||||
tCallback = callback;
|
||||
tParam1 = param1;
|
||||
tParam2 = param2;
|
||||
|
||||
timerAlarmWrite(timer, delayMsec * 1000UL, false); // Do not reload, we want it to be a single shot timer
|
||||
timerRestart(timer);
|
||||
timerAlarmEnable(timer);
|
||||
return true;
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -1,8 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <Arduino.h>
|
||||
|
||||
typedef void (*PendableFunction)(void *pvParameter1, uint32_t ulParameter2);
|
||||
|
||||
/// Uses a hardware timer, but calls the handler in _interrupt_ context
|
||||
bool scheduleHWCallback(PendableFunction callback, void *param1, uint32_t param2, uint32_t delayMsec);
|
||||
+106
-115
@@ -80,9 +80,6 @@ RAK9154Sensor rak9154Sensor;
|
||||
#endif
|
||||
|
||||
#ifdef HAS_PMU
|
||||
#include "XPowersAXP192.tpp"
|
||||
#include "XPowersAXP2101.tpp"
|
||||
#include "XPowersLibInterface.hpp"
|
||||
XPowersLibInterface *PMU = NULL;
|
||||
#else
|
||||
|
||||
@@ -139,6 +136,30 @@ using namespace meshtastic;
|
||||
*/
|
||||
static HasBatteryLevel *batteryLevel; // Default to NULL for no battery level sensor
|
||||
|
||||
static void adcEnable()
|
||||
{
|
||||
#ifdef ADC_CTRL // enable adc voltage divider when we need to read
|
||||
#ifdef ADC_USE_PULLUP
|
||||
pinMode(ADC_CTRL, INPUT_PULLUP);
|
||||
#else
|
||||
pinMode(ADC_CTRL, OUTPUT);
|
||||
digitalWrite(ADC_CTRL, ADC_CTRL_ENABLED);
|
||||
#endif
|
||||
delay(10);
|
||||
#endif
|
||||
}
|
||||
|
||||
static void adcDisable()
|
||||
{
|
||||
#ifdef ADC_CTRL // disable adc voltage divider when we need to read
|
||||
#ifdef ADC_USE_PULLUP
|
||||
pinMode(ADC_CTRL, INPUT_PULLDOWN);
|
||||
#else
|
||||
digitalWrite(ADC_CTRL, !ADC_CTRL_ENABLED);
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* A simple battery level sensor that assumes the battery voltage is attached via a voltage-divider to an analog input
|
||||
*/
|
||||
@@ -229,25 +250,19 @@ class AnalogBatteryLevel : public HasBatteryLevel
|
||||
uint32_t raw = 0;
|
||||
float scaled = 0;
|
||||
|
||||
adcEnable();
|
||||
#ifdef ARCH_ESP32 // ADC block for espressif platforms
|
||||
raw = espAdcRead();
|
||||
scaled = esp_adc_cal_raw_to_voltage(raw, adc_characs);
|
||||
scaled *= operativeAdcMultiplier;
|
||||
#else // block for all other platforms
|
||||
#ifdef ADC_CTRL // enable adc voltage divider when we need to read
|
||||
pinMode(ADC_CTRL, OUTPUT);
|
||||
digitalWrite(ADC_CTRL, ADC_CTRL_ENABLED);
|
||||
delay(10);
|
||||
#endif
|
||||
#else // block for all other platforms
|
||||
for (uint32_t i = 0; i < BATTERY_SENSE_SAMPLES; i++) {
|
||||
raw += analogRead(BATTERY_PIN);
|
||||
}
|
||||
raw = raw / BATTERY_SENSE_SAMPLES;
|
||||
scaled = operativeAdcMultiplier * ((1000 * AREF_VOLTAGE) / pow(2, BATTERY_SENSE_RESOLUTION_BITS)) * raw;
|
||||
#ifdef ADC_CTRL // disable adc voltage divider when we need to read
|
||||
digitalWrite(ADC_CTRL, !ADC_CTRL_ENABLED);
|
||||
#endif
|
||||
#endif
|
||||
adcDisable();
|
||||
|
||||
if (!initial_read_done) {
|
||||
// Flush the smoothing filter with an ADC reading, if the reading is plausibly correct
|
||||
@@ -278,11 +293,6 @@ class AnalogBatteryLevel : public HasBatteryLevel
|
||||
uint8_t raw_c = 0; // raw reading counter
|
||||
|
||||
#ifndef BAT_MEASURE_ADC_UNIT // ADC1
|
||||
#ifdef ADC_CTRL // enable adc voltage divider when we need to read
|
||||
pinMode(ADC_CTRL, OUTPUT);
|
||||
digitalWrite(ADC_CTRL, ADC_CTRL_ENABLED);
|
||||
delay(10);
|
||||
#endif
|
||||
for (int i = 0; i < BATTERY_SENSE_SAMPLES; i++) {
|
||||
int val_ = adc1_get_raw(adc_channel);
|
||||
if (val_ >= 0) { // save only valid readings
|
||||
@@ -291,18 +301,7 @@ class AnalogBatteryLevel : public HasBatteryLevel
|
||||
}
|
||||
// delayMicroseconds(100);
|
||||
}
|
||||
#ifdef ADC_CTRL // disable adc voltage divider when we need to read
|
||||
digitalWrite(ADC_CTRL, !ADC_CTRL_ENABLED);
|
||||
#endif
|
||||
#else // ADC2
|
||||
#ifdef ADC_CTRL
|
||||
#if defined(HELTEC_WIRELESS_PAPER) || defined(HELTEC_WIRELESS_PAPER_V1_0)
|
||||
pinMode(ADC_CTRL, OUTPUT);
|
||||
digitalWrite(ADC_CTRL, LOW); // ACTIVE LOW
|
||||
delay(10);
|
||||
#endif
|
||||
#endif // End ADC_CTRL
|
||||
|
||||
#else // ADC2
|
||||
#ifdef CONFIG_IDF_TARGET_ESP32S3 // ESP32S3
|
||||
// ADC2 wifi bug workaround not required, breaks compile
|
||||
// On ESP32S3, ADC2 can take turns with Wifi (?)
|
||||
@@ -337,12 +336,6 @@ class AnalogBatteryLevel : public HasBatteryLevel
|
||||
}
|
||||
#endif // BAT_MEASURE_ADC_UNIT
|
||||
|
||||
#ifdef ADC_CTRL
|
||||
#if defined(HELTEC_WIRELESS_PAPER) || defined(HELTEC_WIRELESS_PAPER_V1_0)
|
||||
digitalWrite(ADC_CTRL, HIGH);
|
||||
#endif
|
||||
#endif // End ADC_CTRL
|
||||
|
||||
#endif // End BAT_MEASURE_ADC_UNIT
|
||||
return (raw / (raw_c < 1 ? 1 : raw_c));
|
||||
}
|
||||
@@ -461,7 +454,7 @@ class AnalogBatteryLevel : public HasBatteryLevel
|
||||
#endif
|
||||
};
|
||||
|
||||
AnalogBatteryLevel analogLevel;
|
||||
static AnalogBatteryLevel analogLevel;
|
||||
|
||||
Power::Power() : OSThread("Power")
|
||||
{
|
||||
@@ -560,6 +553,10 @@ bool Power::setup()
|
||||
{
|
||||
bool found = axpChipInit() || analogInit();
|
||||
|
||||
#ifdef NRF_APM
|
||||
found = true;
|
||||
#endif
|
||||
|
||||
enabled = found;
|
||||
low_voltage_counter = 0;
|
||||
|
||||
@@ -589,10 +586,16 @@ void Power::shutdown()
|
||||
// TODO(girts): move this and other axp stuff to power.h/power.cpp.
|
||||
void Power::readPowerStatus()
|
||||
{
|
||||
int32_t batteryVoltageMv = -1; // Assume unknown
|
||||
int8_t batteryChargePercent = -1;
|
||||
OptionalBool usbPowered = OptUnknown;
|
||||
OptionalBool hasBattery = OptUnknown; // These must be static because NRF_APM code doesn't run every time
|
||||
OptionalBool isCharging = OptUnknown;
|
||||
|
||||
if (batteryLevel) {
|
||||
bool hasBattery = batteryLevel->isBatteryConnect();
|
||||
uint32_t batteryVoltageMv = 0;
|
||||
int8_t batteryChargePercent = 0;
|
||||
hasBattery = batteryLevel->isBatteryConnect() ? OptTrue : OptFalse;
|
||||
usbPowered = batteryLevel->isVbusIn() ? OptTrue : OptFalse;
|
||||
isCharging = batteryLevel->isCharging() ? OptTrue : OptFalse;
|
||||
if (hasBattery) {
|
||||
batteryVoltageMv = batteryLevel->getBattVoltage();
|
||||
// If the AXP192 returns a valid battery percentage, use it
|
||||
@@ -607,102 +610,90 @@ void Power::readPowerStatus()
|
||||
0, 100);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
OptionalBool NRF_USB = OptFalse;
|
||||
|
||||
// FIXME: IMO we shouldn't be littering our code with all these ifdefs. Way better instead to make a Nrf52IsUsbPowered subclass
|
||||
// (which shares a superclass with the BatteryLevel stuff)
|
||||
// that just provides a few methods. But in the interest of fixing this bug I'm going to follow current
|
||||
// practice.
|
||||
#ifdef NRF_APM // Section of code detects USB power on the RAK4631 and updates the power states. Takes 20 seconds or so to detect
|
||||
// changes.
|
||||
|
||||
static nrfx_power_usb_state_t prev_nrf_usb_state = (nrfx_power_usb_state_t)-1; // -1 so that state detected at boot
|
||||
nrfx_power_usb_state_t nrf_usb_state = nrfx_power_usbstatus_get();
|
||||
nrfx_power_usb_state_t nrf_usb_state = nrfx_power_usbstatus_get();
|
||||
// LOG_DEBUG("NRF Power %d\n", nrf_usb_state);
|
||||
|
||||
// If state changed
|
||||
if (nrf_usb_state != prev_nrf_usb_state) {
|
||||
// If changed to DISCONNECTED
|
||||
if (nrf_usb_state == NRFX_POWER_USB_STATE_DISCONNECTED) {
|
||||
powerFSM.trigger(EVENT_POWER_DISCONNECTED);
|
||||
NRF_USB = OptFalse;
|
||||
}
|
||||
// If changed to CONNECTED / READY
|
||||
else {
|
||||
powerFSM.trigger(EVENT_POWER_CONNECTED);
|
||||
NRF_USB = OptTrue;
|
||||
}
|
||||
// If changed to DISCONNECTED
|
||||
if (nrf_usb_state == NRFX_POWER_USB_STATE_DISCONNECTED)
|
||||
isCharging = usbPowered = OptFalse;
|
||||
// If changed to CONNECTED / READY
|
||||
else
|
||||
isCharging = usbPowered = OptTrue;
|
||||
|
||||
// Cache the current state
|
||||
prev_nrf_usb_state = nrf_usb_state;
|
||||
}
|
||||
#endif
|
||||
// Notify any status instances that are observing us
|
||||
const PowerStatus powerStatus2 = PowerStatus(
|
||||
hasBattery ? OptTrue : OptFalse, batteryLevel->isVbusIn() || NRF_USB == OptTrue ? OptTrue : OptFalse,
|
||||
batteryLevel->isCharging() || NRF_USB == OptTrue ? OptTrue : OptFalse, batteryVoltageMv, batteryChargePercent);
|
||||
LOG_DEBUG("Battery: usbPower=%d, isCharging=%d, batMv=%d, batPct=%d\n", powerStatus2.getHasUSB(),
|
||||
powerStatus2.getIsCharging(), powerStatus2.getBatteryVoltageMv(), powerStatus2.getBatteryChargePercent());
|
||||
newStatus.notifyObservers(&powerStatus2);
|
||||
|
||||
// Notify any status instances that are observing us
|
||||
const PowerStatus powerStatus2 = PowerStatus(hasBattery, usbPowered, isCharging, batteryVoltageMv, batteryChargePercent);
|
||||
LOG_DEBUG("Battery: usbPower=%d, isCharging=%d, batMv=%d, batPct=%d\n", powerStatus2.getHasUSB(),
|
||||
powerStatus2.getIsCharging(), powerStatus2.getBatteryVoltageMv(), powerStatus2.getBatteryChargePercent());
|
||||
newStatus.notifyObservers(&powerStatus2);
|
||||
#ifdef DEBUG_HEAP
|
||||
if (lastheap != memGet.getFreeHeap()) {
|
||||
LOG_DEBUG("Threads running:");
|
||||
int running = 0;
|
||||
for (int i = 0; i < MAX_THREADS; i++) {
|
||||
auto thread = concurrency::mainController.get(i);
|
||||
if ((thread != nullptr) && (thread->enabled)) {
|
||||
LOG_DEBUG(" %s", thread->ThreadName.c_str());
|
||||
running++;
|
||||
}
|
||||
if (lastheap != memGet.getFreeHeap()) {
|
||||
LOG_DEBUG("Threads running:");
|
||||
int running = 0;
|
||||
for (int i = 0; i < MAX_THREADS; i++) {
|
||||
auto thread = concurrency::mainController.get(i);
|
||||
if ((thread != nullptr) && (thread->enabled)) {
|
||||
LOG_DEBUG(" %s", thread->ThreadName.c_str());
|
||||
running++;
|
||||
}
|
||||
LOG_DEBUG("\n");
|
||||
LOG_DEBUG("Heap status: %d/%d bytes free (%d), running %d/%d threads\n", memGet.getFreeHeap(), memGet.getHeapSize(),
|
||||
memGet.getFreeHeap() - lastheap, running, concurrency::mainController.size(false));
|
||||
lastheap = memGet.getFreeHeap();
|
||||
}
|
||||
LOG_DEBUG("\n");
|
||||
LOG_DEBUG("Heap status: %d/%d bytes free (%d), running %d/%d threads\n", memGet.getFreeHeap(), memGet.getHeapSize(),
|
||||
memGet.getFreeHeap() - lastheap, running, concurrency::mainController.size(false));
|
||||
lastheap = memGet.getFreeHeap();
|
||||
}
|
||||
#ifdef DEBUG_HEAP_MQTT
|
||||
if (mqtt) {
|
||||
// send MQTT-Packet with Heap-Size
|
||||
uint8_t dmac[6];
|
||||
getMacAddr(dmac); // Get our hardware ID
|
||||
char mac[18];
|
||||
sprintf(mac, "!%02x%02x%02x%02x", dmac[2], dmac[3], dmac[4], dmac[5]);
|
||||
if (mqtt) {
|
||||
// send MQTT-Packet with Heap-Size
|
||||
uint8_t dmac[6];
|
||||
getMacAddr(dmac); // Get our hardware ID
|
||||
char mac[18];
|
||||
sprintf(mac, "!%02x%02x%02x%02x", dmac[2], dmac[3], dmac[4], dmac[5]);
|
||||
|
||||
auto newHeap = memGet.getFreeHeap();
|
||||
std::string heapTopic =
|
||||
(*moduleConfig.mqtt.root ? moduleConfig.mqtt.root : "msh") + std::string("/2/heap/") + std::string(mac);
|
||||
std::string heapString = std::to_string(newHeap);
|
||||
mqtt->pubSub.publish(heapTopic.c_str(), heapString.c_str(), false);
|
||||
auto wifiRSSI = WiFi.RSSI();
|
||||
std::string wifiTopic =
|
||||
(*moduleConfig.mqtt.root ? moduleConfig.mqtt.root : "msh") + std::string("/2/wifi/") + std::string(mac);
|
||||
std::string wifiString = std::to_string(wifiRSSI);
|
||||
mqtt->pubSub.publish(wifiTopic.c_str(), wifiString.c_str(), false);
|
||||
}
|
||||
auto newHeap = memGet.getFreeHeap();
|
||||
std::string heapTopic =
|
||||
(*moduleConfig.mqtt.root ? moduleConfig.mqtt.root : "msh") + std::string("/2/heap/") + std::string(mac);
|
||||
std::string heapString = std::to_string(newHeap);
|
||||
mqtt->pubSub.publish(heapTopic.c_str(), heapString.c_str(), false);
|
||||
auto wifiRSSI = WiFi.RSSI();
|
||||
std::string wifiTopic =
|
||||
(*moduleConfig.mqtt.root ? moduleConfig.mqtt.root : "msh") + std::string("/2/wifi/") + std::string(mac);
|
||||
std::string wifiString = std::to_string(wifiRSSI);
|
||||
mqtt->pubSub.publish(wifiTopic.c_str(), wifiString.c_str(), false);
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
// If we have a battery at all and it is less than 0%, force deep sleep if we have more than 10 low readings in
|
||||
// a row. NOTE: min LiIon/LiPo voltage is 2.0 to 2.5V, current OCV min is set to 3100 that is large enough.
|
||||
//
|
||||
if (powerStatus2.getHasBattery() && !powerStatus2.getHasUSB()) {
|
||||
if (batteryLevel->getBattVoltage() < OCV[NUM_OCV_POINTS - 1]) {
|
||||
low_voltage_counter++;
|
||||
LOG_DEBUG("Low voltage counter: %d/10\n", low_voltage_counter);
|
||||
if (low_voltage_counter > 10) {
|
||||
// If we have a battery at all and it is less than 0%, force deep sleep if we have more than 10 low readings in
|
||||
// a row. NOTE: min LiIon/LiPo voltage is 2.0 to 2.5V, current OCV min is set to 3100 that is large enough.
|
||||
//
|
||||
if (batteryLevel && powerStatus2.getHasBattery() && !powerStatus2.getHasUSB()) {
|
||||
if (batteryLevel->getBattVoltage() < OCV[NUM_OCV_POINTS - 1]) {
|
||||
low_voltage_counter++;
|
||||
LOG_DEBUG("Low voltage counter: %d/10\n", low_voltage_counter);
|
||||
if (low_voltage_counter > 10) {
|
||||
#ifdef ARCH_NRF52
|
||||
// We can't trigger deep sleep on NRF52, it's freezing the board
|
||||
LOG_DEBUG("Low voltage detected, but not triggering deep sleep\n");
|
||||
// We can't trigger deep sleep on NRF52, it's freezing the board
|
||||
LOG_DEBUG("Low voltage detected, but not triggering deep sleep\n");
|
||||
#else
|
||||
LOG_INFO("Low voltage detected, triggering deep sleep\n");
|
||||
powerFSM.trigger(EVENT_LOW_BATTERY);
|
||||
LOG_INFO("Low voltage detected, triggering deep sleep\n");
|
||||
powerFSM.trigger(EVENT_LOW_BATTERY);
|
||||
#endif
|
||||
}
|
||||
} else {
|
||||
low_voltage_counter = 0;
|
||||
}
|
||||
} else {
|
||||
low_voltage_counter = 0;
|
||||
}
|
||||
} else {
|
||||
// No power sensing on this board - tell everyone else we have no idea what is happening
|
||||
const PowerStatus powerStatus3 = PowerStatus(OptUnknown, OptUnknown, OptUnknown, -1, -1);
|
||||
newStatus.notifyObservers(&powerStatus3);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1051,4 +1042,4 @@ bool Power::axpChipInit()
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
+11
-20
@@ -9,6 +9,7 @@
|
||||
*/
|
||||
#include "PowerFSM.h"
|
||||
#include "Default.h"
|
||||
#include "Led.h"
|
||||
#include "MeshService.h"
|
||||
#include "NodeDB.h"
|
||||
#include "PowerMon.h"
|
||||
@@ -21,12 +22,15 @@
|
||||
#ifndef SLEEP_TIME
|
||||
#define SLEEP_TIME 30
|
||||
#endif
|
||||
|
||||
#if EXCLUDE_POWER_FSM
|
||||
FakeFsm powerFSM;
|
||||
void PowerFSM_setup(){};
|
||||
#else
|
||||
/// Should we behave as if we have AC power now?
|
||||
static bool isPowered()
|
||||
{
|
||||
// Circumvent the battery sensing logic and assumes constant power if no battery pin or power mgmt IC
|
||||
#if !defined(BATTERY_PIN) && !defined(HAS_AXP192) && !defined(HAS_AXP2101)
|
||||
#if !defined(BATTERY_PIN) && !defined(HAS_AXP192) && !defined(HAS_AXP2101) && !defined(NRF_APM)
|
||||
return true;
|
||||
#endif
|
||||
|
||||
@@ -50,7 +54,6 @@ static bool isPowered()
|
||||
static void sdsEnter()
|
||||
{
|
||||
LOG_DEBUG("Enter state: SDS\n");
|
||||
powerMon->setState(meshtastic_PowerMon_State_CPU_DeepSleep);
|
||||
// FIXME - make sure GPS and LORA radio are off first - because we want close to zero current draw
|
||||
doDeepSleep(Default::getConfiguredOrDefaultMs(config.power.sds_secs), false);
|
||||
}
|
||||
@@ -70,7 +73,6 @@ static uint32_t secsSlept;
|
||||
static void lsEnter()
|
||||
{
|
||||
LOG_INFO("lsEnter begin, ls_secs=%u\n", config.power.ls_secs);
|
||||
powerMon->clearState(meshtastic_PowerMon_State_Screen_On);
|
||||
screen->setOn(false);
|
||||
secsSlept = 0; // How long have we been sleeping this time
|
||||
|
||||
@@ -91,7 +93,7 @@ static void lsIdle()
|
||||
uint32_t sleepTime = SLEEP_TIME;
|
||||
|
||||
powerMon->setState(meshtastic_PowerMon_State_CPU_LightSleep);
|
||||
setLed(false); // Never leave led on while in light sleep
|
||||
ledBlink.set(false); // Never leave led on while in light sleep
|
||||
esp_sleep_source_t wakeCause2 = doLightSleep(sleepTime * 1000LL);
|
||||
powerMon->clearState(meshtastic_PowerMon_State_CPU_LightSleep);
|
||||
|
||||
@@ -99,7 +101,7 @@ static void lsIdle()
|
||||
case ESP_SLEEP_WAKEUP_TIMER:
|
||||
// Normal case: timer expired, we should just go back to sleep ASAP
|
||||
|
||||
setLed(true); // briefly turn on led
|
||||
ledBlink.set(true); // briefly turn on led
|
||||
wakeCause2 = doLightSleep(100); // leave led on for 1ms
|
||||
|
||||
secsSlept += sleepTime;
|
||||
@@ -134,7 +136,7 @@ static void lsIdle()
|
||||
}
|
||||
} else {
|
||||
// Time to stop sleeping!
|
||||
setLed(false);
|
||||
ledBlink.set(false);
|
||||
LOG_INFO("Reached ls_secs, servicing loop()\n");
|
||||
powerFSM.trigger(EVENT_WAKE_TIMER);
|
||||
}
|
||||
@@ -149,7 +151,6 @@ static void lsExit()
|
||||
static void nbEnter()
|
||||
{
|
||||
LOG_DEBUG("Enter state: NB\n");
|
||||
powerMon->clearState(meshtastic_PowerMon_State_BT_On);
|
||||
screen->setOn(false);
|
||||
#ifdef ARCH_ESP32
|
||||
// Only ESP32 should turn off bluetooth
|
||||
@@ -161,8 +162,6 @@ static void nbEnter()
|
||||
|
||||
static void darkEnter()
|
||||
{
|
||||
powerMon->clearState(meshtastic_PowerMon_State_BT_On);
|
||||
powerMon->clearState(meshtastic_PowerMon_State_Screen_On);
|
||||
setBluetoothEnable(true);
|
||||
screen->setOn(false);
|
||||
}
|
||||
@@ -170,8 +169,6 @@ static void darkEnter()
|
||||
static void serialEnter()
|
||||
{
|
||||
LOG_DEBUG("Enter state: SERIAL\n");
|
||||
powerMon->clearState(meshtastic_PowerMon_State_BT_On);
|
||||
powerMon->setState(meshtastic_PowerMon_State_Screen_On);
|
||||
setBluetoothEnable(false);
|
||||
screen->setOn(true);
|
||||
screen->print("Serial connected\n");
|
||||
@@ -180,7 +177,6 @@ static void serialEnter()
|
||||
static void serialExit()
|
||||
{
|
||||
// Turn bluetooth back on when we leave serial stream API
|
||||
powerMon->setState(meshtastic_PowerMon_State_BT_On);
|
||||
setBluetoothEnable(true);
|
||||
screen->print("Serial disconnected\n");
|
||||
}
|
||||
@@ -193,8 +189,6 @@ static void powerEnter()
|
||||
LOG_INFO("Loss of power in Powered\n");
|
||||
powerFSM.trigger(EVENT_POWER_DISCONNECTED);
|
||||
} else {
|
||||
powerMon->setState(meshtastic_PowerMon_State_BT_On);
|
||||
powerMon->setState(meshtastic_PowerMon_State_Screen_On);
|
||||
screen->setOn(true);
|
||||
setBluetoothEnable(true);
|
||||
// within enter() the function getState() returns the state we came from
|
||||
@@ -218,8 +212,6 @@ static void powerIdle()
|
||||
|
||||
static void powerExit()
|
||||
{
|
||||
powerMon->setState(meshtastic_PowerMon_State_BT_On);
|
||||
powerMon->setState(meshtastic_PowerMon_State_Screen_On);
|
||||
screen->setOn(true);
|
||||
setBluetoothEnable(true);
|
||||
|
||||
@@ -231,8 +223,6 @@ static void powerExit()
|
||||
static void onEnter()
|
||||
{
|
||||
LOG_DEBUG("Enter state: ON\n");
|
||||
powerMon->setState(meshtastic_PowerMon_State_BT_On);
|
||||
powerMon->setState(meshtastic_PowerMon_State_Screen_On);
|
||||
screen->setOn(true);
|
||||
setBluetoothEnable(true);
|
||||
}
|
||||
@@ -408,4 +398,5 @@ void PowerFSM_setup()
|
||||
#endif
|
||||
|
||||
powerFSM.run_machine(); // run one iteration of the state machine, so we run our on enter tasks for the initial DARK state
|
||||
}
|
||||
}
|
||||
#endif
|
||||
+24
-1
@@ -1,6 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include <Fsm.h>
|
||||
#include "configuration.h"
|
||||
|
||||
// See sw-design.md for documentation
|
||||
|
||||
@@ -22,7 +22,30 @@
|
||||
#define EVENT_SHUTDOWN 16 // force a full shutdown now (not just sleep)
|
||||
#define EVENT_INPUT 17 // input broker wants something, we need to wake up and enable screen
|
||||
|
||||
#if EXCLUDE_POWER_FSM
|
||||
class FakeFsm
|
||||
{
|
||||
public:
|
||||
void trigger(int event)
|
||||
{
|
||||
if (event == EVENT_SERIAL_CONNECTED) {
|
||||
serialConnected = true;
|
||||
} else if (event == EVENT_SERIAL_DISCONNECTED) {
|
||||
serialConnected = false;
|
||||
}
|
||||
};
|
||||
bool getState() { return serialConnected; };
|
||||
|
||||
private:
|
||||
bool serialConnected = false;
|
||||
};
|
||||
extern FakeFsm powerFSM;
|
||||
void PowerFSM_setup();
|
||||
|
||||
#else
|
||||
#include <Fsm.h>
|
||||
extern Fsm powerFSM;
|
||||
extern State stateON, statePOWER, stateSERIAL, stateDARK;
|
||||
|
||||
void PowerFSM_setup();
|
||||
#endif
|
||||
@@ -18,6 +18,7 @@ class PowerFSMThread : public OSThread
|
||||
protected:
|
||||
int32_t runOnce() override
|
||||
{
|
||||
#if !EXCLUDE_POWER_FSM
|
||||
powerFSM.run_machine();
|
||||
|
||||
/// If we are in power state we force the CPU to wake every 10ms to check for serial characters (we don't yet wake
|
||||
@@ -35,6 +36,9 @@ class PowerFSMThread : public OSThread
|
||||
}
|
||||
|
||||
return 100;
|
||||
#else
|
||||
return INT32_MAX;
|
||||
#endif
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
+4
-2
@@ -2,9 +2,11 @@
|
||||
#include "NodeDB.h"
|
||||
|
||||
// Use the 'live' config flag to figure out if we should be showing this message
|
||||
static bool is_power_enabled(uint64_t m)
|
||||
bool PowerMon::is_power_enabled(uint64_t m)
|
||||
{
|
||||
return (m & config.power.powermon_enables) ? true : false;
|
||||
// FIXME: VERY STRANGE BUG: if I or in "force_enabled || " the flashed image on a rak4631 is not accepted by the bootloader as
|
||||
// valid!!! Possibly a linker/gcc/bootloader bug somewhere?
|
||||
return ((m & config.power.powermon_enables) ? true : false);
|
||||
}
|
||||
|
||||
void PowerMon::setState(_meshtastic_PowerMon_State state, const char *reason)
|
||||
|
||||
@@ -17,6 +17,13 @@ class PowerMon
|
||||
{
|
||||
uint64_t states = 0UL;
|
||||
|
||||
friend class PowerStressModule;
|
||||
|
||||
/**
|
||||
* If stress testing we always want all events logged
|
||||
*/
|
||||
bool force_enabled = false;
|
||||
|
||||
public:
|
||||
PowerMon() {}
|
||||
|
||||
@@ -27,6 +34,9 @@ class PowerMon
|
||||
private:
|
||||
// Emit the coded log message
|
||||
void emitLog(const char *reason);
|
||||
|
||||
// Use the 'live' config flag to figure out if we should be showing this message
|
||||
bool is_power_enabled(uint64_t m);
|
||||
};
|
||||
|
||||
extern PowerMon *powerMon;
|
||||
|
||||
@@ -3,5 +3,4 @@
|
||||
#define RF95_RESET LORA_RESET
|
||||
#define RF95_IRQ LORA_DIO0 // on SX1262 version this is a no connect DIO0
|
||||
#define RF95_DIO1 LORA_DIO1 // Note: not really used for RF95, but used for pure SX127x
|
||||
#define RF95_DIO2 LORA_DIO2 // Note: not really used for RF95
|
||||
#endif
|
||||
#endif
|
||||
|
||||
+74
-21
@@ -38,8 +38,9 @@ size_t RedirectablePrint::write(uint8_t c)
|
||||
#ifdef USE_SEGGER
|
||||
SEGGER_RTT_PutChar(SEGGER_STDOUT_CH, c);
|
||||
#endif
|
||||
|
||||
if (!config.has_lora || config.device.serial_enabled)
|
||||
// Account for legacy config transition
|
||||
bool serialEnabled = config.has_security ? config.security.serial_enabled : config.device.serial_enabled;
|
||||
if (!config.has_lora || serialEnabled)
|
||||
dest->write(c);
|
||||
|
||||
return 1; // We always claim one was written, rather than trusting what the
|
||||
@@ -49,7 +50,17 @@ size_t RedirectablePrint::write(uint8_t c)
|
||||
size_t RedirectablePrint::vprintf(const char *logLevel, const char *format, va_list arg)
|
||||
{
|
||||
va_list copy;
|
||||
#if ENABLE_JSON_LOGGING || ARCH_PORTDUINO
|
||||
static char printBuf[512];
|
||||
#else
|
||||
static char printBuf[160];
|
||||
#endif
|
||||
|
||||
#ifdef ARCH_PORTDUINO
|
||||
bool color = !settingsMap[ascii_logs];
|
||||
#else
|
||||
bool color = true;
|
||||
#endif
|
||||
|
||||
va_copy(copy, arg);
|
||||
size_t len = vsnprintf(printBuf, sizeof(printBuf), format, copy);
|
||||
@@ -66,7 +77,7 @@ size_t RedirectablePrint::vprintf(const char *logLevel, const char *format, va_l
|
||||
if (!std::isprint(static_cast<unsigned char>(printBuf[f])) && printBuf[f] != '\n')
|
||||
printBuf[f] = '#';
|
||||
}
|
||||
if (logLevel != nullptr) {
|
||||
if (color && logLevel != nullptr) {
|
||||
if (strcmp(logLevel, MESHTASTIC_LOG_LEVEL_DEBUG) == 0)
|
||||
Print::write("\u001b[34m", 6);
|
||||
if (strcmp(logLevel, MESHTASTIC_LOG_LEVEL_INFO) == 0)
|
||||
@@ -77,7 +88,9 @@ size_t RedirectablePrint::vprintf(const char *logLevel, const char *format, va_l
|
||||
Print::write("\u001b[31m", 6);
|
||||
}
|
||||
len = Print::write(printBuf, len);
|
||||
Print::write("\u001b[0m", 5);
|
||||
if (color && logLevel != nullptr) {
|
||||
Print::write("\u001b[0m", 5);
|
||||
}
|
||||
return len;
|
||||
}
|
||||
|
||||
@@ -87,17 +100,27 @@ void RedirectablePrint::log_to_serial(const char *logLevel, const char *format,
|
||||
|
||||
// Cope with 0 len format strings, but look for new line terminator
|
||||
bool hasNewline = *format && format[strlen(format) - 1] == '\n';
|
||||
#ifdef ARCH_PORTDUINO
|
||||
bool color = !settingsMap[ascii_logs];
|
||||
#else
|
||||
bool color = true;
|
||||
#endif
|
||||
|
||||
// If we are the first message on a report, include the header
|
||||
if (!isContinuationMessage) {
|
||||
if (strcmp(logLevel, MESHTASTIC_LOG_LEVEL_DEBUG) == 0)
|
||||
Print::write("\u001b[34m", 6);
|
||||
if (strcmp(logLevel, MESHTASTIC_LOG_LEVEL_INFO) == 0)
|
||||
Print::write("\u001b[32m", 6);
|
||||
if (strcmp(logLevel, MESHTASTIC_LOG_LEVEL_WARN) == 0)
|
||||
Print::write("\u001b[33m", 6);
|
||||
if (strcmp(logLevel, MESHTASTIC_LOG_LEVEL_ERROR) == 0)
|
||||
Print::write("\u001b[31m", 6);
|
||||
if (color) {
|
||||
if (strcmp(logLevel, MESHTASTIC_LOG_LEVEL_DEBUG) == 0)
|
||||
Print::write("\u001b[34m", 6);
|
||||
if (strcmp(logLevel, MESHTASTIC_LOG_LEVEL_INFO) == 0)
|
||||
Print::write("\u001b[32m", 6);
|
||||
if (strcmp(logLevel, MESHTASTIC_LOG_LEVEL_WARN) == 0)
|
||||
Print::write("\u001b[33m", 6);
|
||||
if (strcmp(logLevel, MESHTASTIC_LOG_LEVEL_ERROR) == 0)
|
||||
Print::write("\u001b[31m", 6);
|
||||
if (strcmp(logLevel, MESHTASTIC_LOG_LEVEL_TRACE) == 0)
|
||||
Print::write("\u001b[35m", 6);
|
||||
}
|
||||
|
||||
uint32_t rtc_sec = getValidTime(RTCQuality::RTCQualityDevice, true); // display local time on logfile
|
||||
if (rtc_sec > 0) {
|
||||
long hms = rtc_sec % SEC_PER_DAY;
|
||||
@@ -111,17 +134,33 @@ void RedirectablePrint::log_to_serial(const char *logLevel, const char *format,
|
||||
int min = (hms % SEC_PER_HOUR) / SEC_PER_MIN;
|
||||
int sec = (hms % SEC_PER_HOUR) % SEC_PER_MIN; // or hms % SEC_PER_MIN
|
||||
#ifdef ARCH_PORTDUINO
|
||||
::printf("%s \u001b[0m| %02d:%02d:%02d %u ", logLevel, hour, min, sec, millis() / 1000);
|
||||
::printf("%s ", logLevel);
|
||||
if (color) {
|
||||
::printf("\u001b[0m");
|
||||
}
|
||||
::printf("| %02d:%02d:%02d %u ", hour, min, sec, millis() / 1000);
|
||||
#else
|
||||
printf("%s \u001b[0m| %02d:%02d:%02d %u ", logLevel, hour, min, sec, millis() / 1000);
|
||||
printf("%s ", logLevel);
|
||||
if (color) {
|
||||
printf("\u001b[0m");
|
||||
}
|
||||
printf("| %02d:%02d:%02d %u ", hour, min, sec, millis() / 1000);
|
||||
#endif
|
||||
} else
|
||||
} else {
|
||||
#ifdef ARCH_PORTDUINO
|
||||
::printf("%s \u001b[0m| ??:??:?? %u ", logLevel, millis() / 1000);
|
||||
::printf("%s ", logLevel);
|
||||
if (color) {
|
||||
::printf("\u001b[0m");
|
||||
}
|
||||
::printf("| ??:??:?? %u ", millis() / 1000);
|
||||
#else
|
||||
printf("%s \u001b[0m| ??:??:?? %u ", logLevel, millis() / 1000);
|
||||
printf("%s ", logLevel);
|
||||
if (color) {
|
||||
printf("\u001b[0m");
|
||||
}
|
||||
printf("| ??:??:?? %u ", millis() / 1000);
|
||||
#endif
|
||||
|
||||
}
|
||||
auto thread = concurrency::OSThread::currentThread;
|
||||
if (thread) {
|
||||
print("[");
|
||||
@@ -174,7 +213,7 @@ void RedirectablePrint::log_to_syslog(const char *logLevel, const char *format,
|
||||
void RedirectablePrint::log_to_ble(const char *logLevel, const char *format, va_list arg)
|
||||
{
|
||||
#if !MESHTASTIC_EXCLUDE_BLUETOOTH
|
||||
if (config.bluetooth.device_logging_enabled && !pauseBluetoothLogging) {
|
||||
if (config.security.debug_log_api_enabled && !pauseBluetoothLogging) {
|
||||
bool isBleConnected = false;
|
||||
#ifdef ARCH_ESP32
|
||||
isBleConnected = nimbleBluetooth && nimbleBluetooth->isActive() && nimbleBluetooth->isConnected();
|
||||
@@ -244,7 +283,21 @@ meshtastic_LogRecord_Level RedirectablePrint::getLogLevel(const char *logLevel)
|
||||
|
||||
void RedirectablePrint::log(const char *logLevel, const char *format, ...)
|
||||
{
|
||||
#ifdef ARCH_PORTDUINO
|
||||
#if ARCH_PORTDUINO
|
||||
// level trace is special, two possible ways to handle it.
|
||||
if (strcmp(logLevel, MESHTASTIC_LOG_LEVEL_TRACE) == 0) {
|
||||
if (settingsStrings[traceFilename] != "") {
|
||||
va_list arg;
|
||||
va_start(arg, format);
|
||||
try {
|
||||
traceFile << va_arg(arg, char *) << std::endl;
|
||||
} catch (const std::ios_base::failure &e) {
|
||||
}
|
||||
va_end(arg);
|
||||
}
|
||||
if (settingsMap[logoutputlevel] < level_trace && strcmp(logLevel, MESHTASTIC_LOG_LEVEL_TRACE) == 0)
|
||||
return;
|
||||
}
|
||||
if (settingsMap[logoutputlevel] < level_debug && strcmp(logLevel, MESHTASTIC_LOG_LEVEL_DEBUG) == 0)
|
||||
return;
|
||||
else if (settingsMap[logoutputlevel] < level_info && strcmp(logLevel, MESHTASTIC_LOG_LEVEL_INFO) == 0)
|
||||
@@ -330,4 +383,4 @@ std::string RedirectablePrint::mt_sprintf(const std::string fmt_str, ...)
|
||||
break;
|
||||
}
|
||||
return std::string(formatted.get());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,105 @@
|
||||
#include "SafeFile.h"
|
||||
|
||||
#ifdef FSCom
|
||||
|
||||
// Only way to work on both esp32 and nrf52
|
||||
static File openFile(const char *filename, bool fullAtomic)
|
||||
{
|
||||
if (!fullAtomic)
|
||||
FSCom.remove(filename); // Nuke the old file to make space (ignore if it !exists)
|
||||
|
||||
String filenameTmp = filename;
|
||||
filenameTmp += ".tmp";
|
||||
|
||||
// clear any previous LFS errors
|
||||
lfs_assert_failed = false;
|
||||
|
||||
return FSCom.open(filenameTmp.c_str(), FILE_O_WRITE);
|
||||
}
|
||||
|
||||
SafeFile::SafeFile(const char *_filename, bool fullAtomic)
|
||||
: filename(_filename), f(openFile(_filename, fullAtomic)), fullAtomic(fullAtomic)
|
||||
{
|
||||
}
|
||||
|
||||
size_t SafeFile::write(uint8_t ch)
|
||||
{
|
||||
if (!f)
|
||||
return 0;
|
||||
|
||||
hash ^= ch;
|
||||
return f.write(ch);
|
||||
}
|
||||
|
||||
size_t SafeFile::write(const uint8_t *buffer, size_t size)
|
||||
{
|
||||
if (!f)
|
||||
return 0;
|
||||
|
||||
for (size_t i = 0; i < size; i++) {
|
||||
hash ^= buffer[i];
|
||||
}
|
||||
return f.write((uint8_t const *)buffer, size); // This nasty cast is _IMPORTANT_ otherwise the correct adafruit method does
|
||||
// not get used (they made a mistake in their typing)
|
||||
}
|
||||
|
||||
/**
|
||||
* Atomically close the file (deleting any old versions) and readback the contents to confirm the hash matches
|
||||
*
|
||||
* @return false for failure
|
||||
*/
|
||||
bool SafeFile::close()
|
||||
{
|
||||
if (!f)
|
||||
return false;
|
||||
|
||||
f.close();
|
||||
if (!testReadback())
|
||||
return false;
|
||||
|
||||
// brief window of risk here ;-)
|
||||
if (fullAtomic && FSCom.exists(filename.c_str()) && !FSCom.remove(filename.c_str())) {
|
||||
LOG_ERROR("Can't remove old pref file\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
String filenameTmp = filename;
|
||||
filenameTmp += ".tmp";
|
||||
if (!renameFile(filenameTmp.c_str(), filename.c_str())) {
|
||||
LOG_ERROR("Error: can't rename new pref file\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// Read our (closed) tempfile back in and compare the hash
|
||||
bool SafeFile::testReadback()
|
||||
{
|
||||
bool lfs_failed = lfs_assert_failed;
|
||||
lfs_assert_failed = false;
|
||||
|
||||
String filenameTmp = filename;
|
||||
filenameTmp += ".tmp";
|
||||
auto f2 = FSCom.open(filenameTmp.c_str(), FILE_O_READ);
|
||||
if (!f2) {
|
||||
LOG_ERROR("Can't open tmp file for readback\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
int c = 0;
|
||||
uint8_t test_hash = 0;
|
||||
while ((c = f2.read()) >= 0) {
|
||||
test_hash ^= (uint8_t)c;
|
||||
}
|
||||
f2.close();
|
||||
|
||||
if (test_hash != hash) {
|
||||
LOG_ERROR("Readback failed hash mismatch\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
return !lfs_failed;
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,49 @@
|
||||
#pragma once
|
||||
|
||||
#include "FSCommon.h"
|
||||
#include "configuration.h"
|
||||
|
||||
#ifdef FSCom
|
||||
|
||||
/**
|
||||
* This class provides 'safe'/paranoid file writing.
|
||||
*
|
||||
* Some of our filesystems (in particular the nrf52) may have bugs beneath our layer. Therefore we want to
|
||||
* be very careful about how we write files. This class provides a restricted (Stream only) writing API for writing to files.
|
||||
*
|
||||
* Notably:
|
||||
* - we keep a simple xor hash of all characters that were written.
|
||||
* - We do not allow seeking (because we want to maintain our hash)
|
||||
* - we provide an close() method which is similar to close but returns false if we were unable to successfully write the
|
||||
* file. Also this method
|
||||
* - atomically replaces any old version of the file on the disk with our new file (after first rereading the file from the disk
|
||||
* to confirm the hash matches)
|
||||
* - Some files are super huge so we can't do the full atomic rename/copy (because of filesystem size limits). If !fullAtomic
|
||||
* then we still do the readback to verify file is valid so higher level code can handle failures.
|
||||
*/
|
||||
class SafeFile : public Print
|
||||
{
|
||||
public:
|
||||
SafeFile(char const *filepath, bool fullAtomic = false);
|
||||
|
||||
virtual size_t write(uint8_t);
|
||||
virtual size_t write(const uint8_t *buffer, size_t size);
|
||||
|
||||
/**
|
||||
* Atomically close the file (deleting any old versions) and readback the contents to confirm the hash matches
|
||||
*
|
||||
* @return false for failure
|
||||
*/
|
||||
bool close();
|
||||
|
||||
private:
|
||||
/// Read our (closed) tempfile back in and compare the hash
|
||||
bool testReadback();
|
||||
|
||||
String filename;
|
||||
File f;
|
||||
bool fullAtomic;
|
||||
uint8_t hash = 0;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -83,7 +83,7 @@ bool SerialConsole::checkIsConnected()
|
||||
bool SerialConsole::handleToRadio(const uint8_t *buf, size_t len)
|
||||
{
|
||||
// only talk to the API once the configuration has been loaded and we're sure the serial port is not disabled.
|
||||
if (config.has_lora && config.device.serial_enabled) {
|
||||
if (config.has_lora && config.security.serial_enabled) {
|
||||
// Switch to protobufs for log messages
|
||||
usingProtobufs = true;
|
||||
canWrite = true;
|
||||
@@ -96,7 +96,7 @@ bool SerialConsole::handleToRadio(const uint8_t *buf, size_t len)
|
||||
|
||||
void SerialConsole::log_to_serial(const char *logLevel, const char *format, va_list arg)
|
||||
{
|
||||
if (usingProtobufs) {
|
||||
if (usingProtobufs && config.security.debug_log_api_enabled) {
|
||||
meshtastic_LogRecord_Level ll = meshtastic_LogRecord_Level_UNSET; // default to unset
|
||||
switch (logLevel[0]) {
|
||||
case 'D':
|
||||
@@ -120,4 +120,4 @@ void SerialConsole::log_to_serial(const char *logLevel, const char *format, va_l
|
||||
emitLogRecord(ll, thread ? thread->ThreadName.c_str() : "", format, arg);
|
||||
} else
|
||||
RedirectablePrint::log_to_serial(logLevel, format, arg);
|
||||
}
|
||||
}
|
||||
|
||||
+15
-5
@@ -52,10 +52,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
// Configuration
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
// If we are using the JTAG port for debugging, some pins must be left free for that (and things like GPS have to be disabled)
|
||||
// we don't support jtag on the ttgo - access to gpio 12 is a PITA
|
||||
#define REQUIRE_RADIO true // If true, we will fail to start if the radio is not found
|
||||
|
||||
/// Convert a preprocessor name into a quoted string
|
||||
#define xstr(s) ystr(s)
|
||||
#define ystr(s) #s
|
||||
@@ -181,6 +177,11 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
/* Step #1: offer chance for variant-specific defines */
|
||||
#include "variant.h"
|
||||
|
||||
#if defined(VEXT_ENABLE) && !defined(VEXT_ON_VALUE)
|
||||
// Older variant.h files might not be defining this value, so stay with the old default
|
||||
#define VEXT_ON_VALUE LOW
|
||||
#endif
|
||||
|
||||
#ifndef GPS_BAUDRATE
|
||||
#define GPS_BAUDRATE 9600
|
||||
#endif
|
||||
@@ -197,6 +198,10 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#define DEFAULT_SHUTDOWN_SECONDS 2
|
||||
#endif
|
||||
|
||||
#ifndef MINIMUM_SAFE_FREE_HEAP
|
||||
#define MINIMUM_SAFE_FREE_HEAP 1500
|
||||
#endif
|
||||
|
||||
/* Step #3: mop up with disabled values for HAS_ options not handled by the above two */
|
||||
|
||||
#ifndef HAS_WIFI
|
||||
@@ -260,6 +265,9 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#define MESHTASTIC_EXCLUDE_MQTT 1
|
||||
#define MESHTASTIC_EXCLUDE_POWERMON 1
|
||||
#define MESHTASTIC_EXCLUDE_I2C 1
|
||||
#define MESHTASTIC_EXCLUDE_PKI 1
|
||||
#define MESHTASTIC_EXCLUDE_POWER_FSM 1
|
||||
#define MESHTASTIC_EXCLUDE_TZ 1
|
||||
#endif
|
||||
|
||||
// Turn off all optional modules
|
||||
@@ -273,6 +281,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#define MESHTASTIC_EXCLUDE_RANGETEST 1
|
||||
#define MESHTASTIC_EXCLUDE_REMOTEHARDWARE 1
|
||||
#define MESHTASTIC_EXCLUDE_STOREFORWARD 1
|
||||
#define MESHTASTIC_EXCLUDE_TEXTMESSAGE 1
|
||||
#define MESHTASTIC_EXCLUDE_ATAK 1
|
||||
#define MESHTASTIC_EXCLUDE_CANNEDMESSAGES 1
|
||||
#define MESHTASTIC_EXCLUDE_NEIGHBORINFO 1
|
||||
@@ -281,6 +290,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#define MESHTASTIC_EXCLUDE_INPUTBROKER 1
|
||||
#define MESHTASTIC_EXCLUDE_SERIAL 1
|
||||
#define MESHTASTIC_EXCLUDE_POWERSTRESS 1
|
||||
#define MESHTASTIC_EXCLUDE_ADMIN 1
|
||||
#endif
|
||||
|
||||
// // Turn off wifi even if HW supports wifi (webserver relies on wifi and is also disabled)
|
||||
@@ -314,4 +324,4 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#endif
|
||||
|
||||
#include "DebugConfiguration.h"
|
||||
#include "RF95Configuration.h"
|
||||
#include "RF95Configuration.h"
|
||||
@@ -24,6 +24,7 @@ class ScanI2C
|
||||
BME_280,
|
||||
BMP_280,
|
||||
BMP_085,
|
||||
BMP_3XX,
|
||||
INA260,
|
||||
INA219,
|
||||
INA3221,
|
||||
|
||||
@@ -267,8 +267,19 @@ void ScanI2CTwoWire::scanPort(I2CPort port, uint8_t *address, uint8_t asize)
|
||||
type = BMP_085;
|
||||
break;
|
||||
default:
|
||||
LOG_INFO("BMP-280 sensor found at address 0x%x\n", (uint8_t)addr.address);
|
||||
type = BMP_280;
|
||||
registerValue = getRegisterValue(ScanI2CTwoWire::RegisterLocation(addr, 0x00), 1); // GET_ID
|
||||
switch (registerValue) {
|
||||
case 0x50: // BMP-388 should be 0x50
|
||||
LOG_INFO("BMP-388 sensor found at address 0x%x\n", (uint8_t)addr.address);
|
||||
type = BMP_3XX;
|
||||
break;
|
||||
case 0x58: // BMP-280 should be 0x58
|
||||
default:
|
||||
LOG_INFO("BMP-280 sensor found at address 0x%x\n", (uint8_t)addr.address);
|
||||
type = BMP_280;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
#ifndef HAS_NCP5623
|
||||
@@ -315,7 +326,7 @@ void ScanI2CTwoWire::scanPort(I2CPort port, uint8_t *address, uint8_t asize)
|
||||
|
||||
case SHT31_4x_ADDR:
|
||||
registerValue = getRegisterValue(ScanI2CTwoWire::RegisterLocation(addr, 0x89), 2);
|
||||
if (registerValue == 0x11a2 || registerValue == 0x11da) {
|
||||
if (registerValue == 0x11a2 || registerValue == 0x11da || registerValue == 0xe9c) {
|
||||
type = SHT4X;
|
||||
LOG_INFO("SHT4X sensor found\n");
|
||||
} else if (getRegisterValue(ScanI2CTwoWire::RegisterLocation(addr, 0x7E), 2) == 0x5449) {
|
||||
@@ -404,4 +415,4 @@ size_t ScanI2CTwoWire::countDevices() const
|
||||
{
|
||||
return foundDevices.size();
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
+63
-61
@@ -2,6 +2,7 @@
|
||||
#if !MESHTASTIC_EXCLUDE_GPS
|
||||
#include "Default.h"
|
||||
#include "GPS.h"
|
||||
#include "GpioLogic.h"
|
||||
#include "NodeDB.h"
|
||||
#include "PowerMon.h"
|
||||
#include "RTC.h"
|
||||
@@ -400,14 +401,6 @@ bool GPS::setup()
|
||||
int msglen = 0;
|
||||
|
||||
if (!didSerialInit) {
|
||||
#ifdef GNSS_AIROHA
|
||||
if (tx_gpio && gnssModel == GNSS_MODEL_UNKNOWN) {
|
||||
probe(GPS_BAUDRATE);
|
||||
LOG_INFO("GPS setting to %d.\n", GPS_BAUDRATE);
|
||||
}
|
||||
#else
|
||||
#if !defined(GPS_UC6580)
|
||||
|
||||
if (tx_gpio && gnssModel == GNSS_MODEL_UNKNOWN) {
|
||||
|
||||
// if GPS_BAUDRATE is specified in variant (i.e. not 9600), skip to the specified rate.
|
||||
@@ -430,9 +423,6 @@ bool GPS::setup()
|
||||
} else {
|
||||
gnssModel = GNSS_MODEL_UNKNOWN;
|
||||
}
|
||||
#else
|
||||
gnssModel = GNSS_MODEL_UC6580;
|
||||
#endif
|
||||
|
||||
if (gnssModel == GNSS_MODEL_MTK) {
|
||||
/*
|
||||
@@ -515,6 +505,22 @@ bool GPS::setup()
|
||||
delay(250);
|
||||
_serial_gps->write("$CFGMSG,6,1,0\r\n");
|
||||
delay(250);
|
||||
} else if (gnssModel == GNSS_MODEL_AG3335) {
|
||||
|
||||
_serial_gps->write("$PAIR066,1,0,1,0,0,1*3B\r\n"); // Enable GPS+GALILEO+NAVIC
|
||||
|
||||
// Configure NMEA (sentences will output once per fix)
|
||||
_serial_gps->write("$PAIR062,0,0*3F\r\n"); // GGA ON
|
||||
_serial_gps->write("$PAIR062,1,0*3F\r\n"); // GLL OFF
|
||||
_serial_gps->write("$PAIR062,2,1*3D\r\n"); // GSA ON
|
||||
_serial_gps->write("$PAIR062,3,0*3D\r\n"); // GSV OFF
|
||||
_serial_gps->write("$PAIR062,4,0*3B\r\n"); // RMC ON
|
||||
_serial_gps->write("$PAIR062,5,0*3B\r\n"); // VTG OFF
|
||||
_serial_gps->write("$PAIR062,6,1*39\r\n"); // ZDA ON
|
||||
|
||||
delay(250);
|
||||
_serial_gps->write("$PAIR513*3D\r\n"); // save configuration
|
||||
|
||||
} else if (gnssModel == GNSS_MODEL_UBLOX) {
|
||||
// Configure GNSS system to GPS+SBAS+GLONASS (Module may restart after this command)
|
||||
// We need set it because by default it is GPS only, and we want to use GLONASS too
|
||||
@@ -784,7 +790,6 @@ bool GPS::setup()
|
||||
LOG_INFO("GNSS module configuration saved!\n");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
didSerialInit = true;
|
||||
}
|
||||
|
||||
@@ -806,6 +811,13 @@ void GPS::setPowerState(GPSPowerState newState, uint32_t sleepTime)
|
||||
powerState = newState;
|
||||
LOG_INFO("GPS power state moving from %s to %s\n", getGPSPowerStateString(oldState), getGPSPowerStateString(newState));
|
||||
|
||||
#ifdef HELTEC_MESH_NODE_T114
|
||||
if ((oldState == GPS_OFF || oldState == GPS_HARDSLEEP) && (newState != GPS_OFF && newState != GPS_HARDSLEEP)) {
|
||||
_serial_gps->begin(serialSpeeds[speedSelect]);
|
||||
} else if ((newState == GPS_OFF || newState == GPS_HARDSLEEP) && (oldState != GPS_OFF && oldState != GPS_HARDSLEEP)) {
|
||||
_serial_gps->end();
|
||||
}
|
||||
#endif
|
||||
switch (newState) {
|
||||
case GPS_ACTIVE:
|
||||
case GPS_IDLE:
|
||||
@@ -864,16 +876,8 @@ void GPS::writePinEN(bool on)
|
||||
if (HW_VENDOR == meshtastic_HardwareModel_RAK4631 && (rotaryEncoderInterruptImpl1 || upDownInterruptImpl1))
|
||||
return;
|
||||
|
||||
// Abort: if pin unset
|
||||
if (!en_gpio)
|
||||
return;
|
||||
|
||||
// Determine new value for the pin
|
||||
bool val = GPS_EN_ACTIVE ? on : !on;
|
||||
|
||||
// Write and log
|
||||
pinMode(en_gpio, OUTPUT);
|
||||
digitalWrite(en_gpio, val);
|
||||
enablePin->set(on);
|
||||
#ifdef GPS_EXTRAVERBOSE
|
||||
LOG_DEBUG("Pin EN %s\n", val == HIGH ? "HIGH" : "LOW");
|
||||
#endif
|
||||
@@ -1102,7 +1106,7 @@ int32_t GPS::runOnce()
|
||||
if (devicestate.did_gps_reset && scheduling.elapsedSearchMs() > 60 * 1000UL && !hasFlow()) {
|
||||
LOG_DEBUG("GPS is not communicating, trying factory reset on next bootup.\n");
|
||||
devicestate.did_gps_reset = false;
|
||||
nodeDB->saveDeviceStateToDisk();
|
||||
nodeDB->saveToDisk(SEGMENT_DEVICESTATE);
|
||||
return disable(); // Stop the GPS thread as it can do nothing useful until next reboot.
|
||||
}
|
||||
}
|
||||
@@ -1180,6 +1184,15 @@ int GPS::prepareDeepSleep(void *unused)
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define PROBE_SIMPLE(CHIP, TOWRITE, RESPONSE, DRIVER, TIMEOUT, ...) \
|
||||
LOG_DEBUG("Trying " TOWRITE " (" CHIP ") ...\n"); \
|
||||
clearBuffer(); \
|
||||
_serial_gps->write(TOWRITE "\r\n"); \
|
||||
if (getACK(RESPONSE, TIMEOUT) == GNSS_RESPONSE_OK) { \
|
||||
LOG_INFO(CHIP " detected, using " #DRIVER " Module\n"); \
|
||||
return DRIVER; \
|
||||
}
|
||||
|
||||
GnssModel_t GPS::probe(int serialSpeed)
|
||||
{
|
||||
#if defined(ARCH_NRF52) || defined(ARCH_PORTDUINO) || defined(ARCH_RP2040) || defined(ARCH_STM32WL)
|
||||
@@ -1192,14 +1205,9 @@ GnssModel_t GPS::probe(int serialSpeed)
|
||||
}
|
||||
#endif
|
||||
#ifdef GNSS_AIROHA
|
||||
|
||||
return GNSS_MODEL_UNKNOWN;
|
||||
#else
|
||||
#ifdef GPS_DEBUG
|
||||
for (int i = 0; i < 20; i++) {
|
||||
getACK("$GP", 200);
|
||||
}
|
||||
return GNSS_MODEL_AG3335;
|
||||
#endif
|
||||
|
||||
memset(&info, 0, sizeof(struct uBloxGnssModelInfo));
|
||||
uint8_t buffer[768] = {0};
|
||||
delay(100);
|
||||
@@ -1208,42 +1216,24 @@ GnssModel_t GPS::probe(int serialSpeed)
|
||||
_serial_gps->write("$PCAS03,0,0,0,0,0,0,0,0,0,0,,,0,0*02\r\n");
|
||||
delay(20);
|
||||
|
||||
// get version information from Unicore UFirebirdII Series
|
||||
// Works for: UC6580, UM620, UM621, UM670A, UM680A, or UM681A
|
||||
_serial_gps->write("$PDTINFO\r\n");
|
||||
delay(750);
|
||||
if (getACK("UC6580", 500) == GNSS_RESPONSE_OK) {
|
||||
LOG_INFO("UC6580 detected, using UC6580 Module\n");
|
||||
return GNSS_MODEL_UC6580;
|
||||
}
|
||||
// Unicore UFirebirdII Series: UC6580, UM620, UM621, UM670A, UM680A, or UM681A
|
||||
PROBE_SIMPLE("UC6580", "$PDTINFO", "UC6580", GNSS_MODEL_UC6580, 500);
|
||||
PROBE_SIMPLE("UM600", "$PDTINFO", "UM600", GNSS_MODEL_UC6580, 500);
|
||||
PROBE_SIMPLE("ATGM336H", "$PCAS06,1*1A", "$GPTXT,01,01,02,HW=ATGM336H", GNSS_MODEL_ATGM336H, 500);
|
||||
/* ATGM332D series (-11(GPS), -21(BDS), -31(GPS+BDS), -51(GPS+GLONASS), -71-0(GPS+BDS+GLONASS))
|
||||
based on AT6558 */
|
||||
PROBE_SIMPLE("ATGM332D", "$PCAS06,1*1A", "$GPTXT,01,01,02,HW=ATGM332D", GNSS_MODEL_ATGM336H, 500);
|
||||
|
||||
// Get version information
|
||||
clearBuffer();
|
||||
_serial_gps->write("$PCAS06,1*1A\r\n");
|
||||
if (getACK("$GPTXT,01,01,02,HW=ATGM336H", 500) == GNSS_RESPONSE_OK) {
|
||||
LOG_INFO("ATGM336H GNSS init succeeded, using ATGM336H Module\n");
|
||||
return GNSS_MODEL_ATGM336H;
|
||||
}
|
||||
/* Airoha (Mediatek) AG3335A/M/S, A3352Q, Quectel L89 2.0, SimCom SIM65M */
|
||||
PROBE_SIMPLE("AG3335", "PAIR020*38", "$PAIR020,AG3335", GNSS_MODEL_AG3335, 500);
|
||||
|
||||
// Get version information
|
||||
clearBuffer();
|
||||
_serial_gps->write("$PCAS06,0*1B\r\n");
|
||||
if (getACK("$GPTXT,01,01,02,SW=", 500) == GNSS_RESPONSE_OK) {
|
||||
LOG_INFO("L76K GNSS init succeeded, using L76K GNSS Module\n");
|
||||
return GNSS_MODEL_MTK;
|
||||
}
|
||||
PROBE_SIMPLE("L76K", "$PCAS06,0*1B", "$GPTXT,01,01,02,SW=", GNSS_MODEL_MTK, 500);
|
||||
|
||||
// Close all NMEA sentences, valid for L76B MTK platform (Waveshare Pico GPS)
|
||||
_serial_gps->write("$PMTK514,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0*2E\r\n");
|
||||
delay(20);
|
||||
|
||||
// Get version information
|
||||
clearBuffer();
|
||||
_serial_gps->write("$PMTK605*31\r\n");
|
||||
if (getACK("Quectel-L76B", 500) == GNSS_RESPONSE_OK) {
|
||||
LOG_INFO("L76B GNSS init succeeded, using L76B GNSS Module\n");
|
||||
return GNSS_MODEL_MTK_L76B;
|
||||
}
|
||||
PROBE_SIMPLE("L76B", "$PMTK605*31", "Quectel-L76B", GNSS_MODEL_MTK_L76B, 500);
|
||||
|
||||
uint8_t cfg_rate[] = {0xB5, 0x62, 0x06, 0x08, 0x00, 0x00, 0x00, 0x00};
|
||||
UBXChecksum(cfg_rate, sizeof(cfg_rate));
|
||||
@@ -1350,7 +1340,6 @@ GnssModel_t GPS::probe(int serialSpeed)
|
||||
}
|
||||
|
||||
return GNSS_MODEL_UBLOX;
|
||||
#endif // !GNSS_Airoha
|
||||
}
|
||||
|
||||
GPS *GPS::createGps()
|
||||
@@ -1384,7 +1373,20 @@ GPS *GPS::createGps()
|
||||
GPS *new_gps = new GPS;
|
||||
new_gps->rx_gpio = _rx_gpio;
|
||||
new_gps->tx_gpio = _tx_gpio;
|
||||
new_gps->en_gpio = _en_gpio;
|
||||
|
||||
GpioVirtPin *virtPin = new GpioVirtPin();
|
||||
new_gps->enablePin = virtPin; // Always at least populate a virtual pin
|
||||
if (_en_gpio) {
|
||||
GpioPin *p = new GpioHwPin(_en_gpio);
|
||||
|
||||
if (!GPS_EN_ACTIVE) { // Need to invert the pin before hardware
|
||||
new GpioNotTransformer(
|
||||
virtPin, p); // We just leave this created object on the heap so it can stay watching virtPin and driving en_gpio
|
||||
} else {
|
||||
new GpioUnaryTransformer(
|
||||
virtPin, p); // We just leave this created object on the heap so it can stay watching virtPin and driving en_gpio
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef PIN_GPS_PPS
|
||||
// pulse per second
|
||||
@@ -1800,4 +1802,4 @@ void GPS::toggleGpsMode()
|
||||
enable();
|
||||
}
|
||||
}
|
||||
#endif // Exclude GPS
|
||||
#endif // Exclude GPS
|
||||
+12
-5
@@ -3,6 +3,7 @@
|
||||
#if !MESHTASTIC_EXCLUDE_GPS
|
||||
|
||||
#include "GPSStatus.h"
|
||||
#include "GpioLogic.h"
|
||||
#include "Observer.h"
|
||||
#include "TinyGPS++.h"
|
||||
#include "concurrency/OSThread.h"
|
||||
@@ -28,7 +29,8 @@ typedef enum {
|
||||
GNSS_MODEL_UBLOX,
|
||||
GNSS_MODEL_UC6580,
|
||||
GNSS_MODEL_UNKNOWN,
|
||||
GNSS_MODEL_MTK_L76B
|
||||
GNSS_MODEL_MTK_L76B,
|
||||
GNSS_MODEL_AG3335
|
||||
} GnssModel_t;
|
||||
|
||||
typedef enum {
|
||||
@@ -50,7 +52,7 @@ enum GPSPowerState : uint8_t {
|
||||
const char *getDOPString(uint32_t dop);
|
||||
|
||||
/**
|
||||
* A gps class that only reads from the GPS periodically (and FIXME - eventually keeps the gps powered down except when reading)
|
||||
* A gps class that only reads from the GPS periodically and keeps the gps powered down except when reading
|
||||
*
|
||||
* When new data is available it will notify observers.
|
||||
*/
|
||||
@@ -72,7 +74,6 @@ class GPS : private concurrency::OSThread
|
||||
uint32_t lastWakeStartMsec = 0, lastSleepStartMsec = 0, lastFixStartMsec = 0;
|
||||
uint32_t rx_gpio = 0;
|
||||
uint32_t tx_gpio = 0;
|
||||
uint32_t en_gpio = 0;
|
||||
|
||||
int speedSelect = 0;
|
||||
int probeTries = 2;
|
||||
@@ -151,6 +152,13 @@ class GPS : private concurrency::OSThread
|
||||
|
||||
meshtastic_Position p = meshtastic_Position_init_default;
|
||||
|
||||
/** This is normally bound to config.position.gps_en_gpio but some rare boards (like heltec tracker) need more advanced
|
||||
* implementations. Those boards will set this public variable to a custom implementation.
|
||||
*
|
||||
* Normally set by GPS::createGPS()
|
||||
*/
|
||||
GpioVirtPin *enablePin = NULL;
|
||||
|
||||
GPS() : concurrency::OSThread("GPS") {}
|
||||
|
||||
virtual ~GPS();
|
||||
@@ -289,7 +297,6 @@ class GPS : private concurrency::OSThread
|
||||
virtual int32_t runOnce() override;
|
||||
|
||||
// Get GNSS model
|
||||
String getNMEA();
|
||||
GnssModel_t probe(int serialSpeed);
|
||||
|
||||
// delay counter to allow more sats before fixed position stops GPS thread
|
||||
@@ -302,4 +309,4 @@ class GPS : private concurrency::OSThread
|
||||
};
|
||||
|
||||
extern GPS *gps;
|
||||
#endif // Exclude GPS
|
||||
#endif // Exclude GPS
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#include <time.h>
|
||||
|
||||
static RTCQuality currentQuality = RTCQualityNone;
|
||||
uint32_t lastSetFromPhoneNtpOrGps = 0;
|
||||
|
||||
RTCQuality getRTCQuality()
|
||||
{
|
||||
@@ -121,6 +122,9 @@ bool perhapsSetRTC(RTCQuality q, const struct timeval *tv, bool forceUpdate)
|
||||
if (shouldSet) {
|
||||
currentQuality = q;
|
||||
lastSetMsec = now;
|
||||
if (currentQuality >= RTCQualityNTP) {
|
||||
lastSetFromPhoneNtpOrGps = now;
|
||||
}
|
||||
|
||||
// This delta value works on all platforms
|
||||
timeStartMsec = now;
|
||||
@@ -256,6 +260,7 @@ uint32_t getValidTime(RTCQuality minQuality, bool local)
|
||||
|
||||
time_t gm_mktime(struct tm *tm)
|
||||
{
|
||||
#if !MESHTASTIC_EXCLUDE_TZ
|
||||
setenv("TZ", "GMT0", 1);
|
||||
time_t res = mktime(tm);
|
||||
if (*config.device.tzdef) {
|
||||
@@ -264,4 +269,7 @@ time_t gm_mktime(struct tm *tm)
|
||||
setenv("TZ", "UTC0", 1);
|
||||
}
|
||||
return res;
|
||||
#else
|
||||
return mktime(tm);
|
||||
#endif
|
||||
}
|
||||
+3
-1
@@ -24,6 +24,8 @@ enum RTCQuality {
|
||||
|
||||
RTCQuality getRTCQuality();
|
||||
|
||||
extern uint32_t lastSetFromPhoneNtpOrGps;
|
||||
|
||||
/// 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 forceUpdate = false);
|
||||
bool perhapsSetRTC(RTCQuality q, struct tm &t);
|
||||
@@ -43,4 +45,4 @@ time_t gm_mktime(struct tm *tm);
|
||||
|
||||
#define SEC_PER_DAY 86400
|
||||
#define SEC_PER_HOUR 3600
|
||||
#define SEC_PER_MIN 60
|
||||
#define SEC_PER_MIN 60
|
||||
|
||||
@@ -157,7 +157,7 @@ bool EInkDisplay::connect()
|
||||
}
|
||||
|
||||
#elif defined(HELTEC_WIRELESS_PAPER_V1_0) || defined(HELTEC_WIRELESS_PAPER) || defined(HELTEC_VISION_MASTER_E213) || \
|
||||
defined(HELTEC_VISION_MASTER_E290)
|
||||
defined(HELTEC_VISION_MASTER_E290) || defined(TLORA_T3S3_EPAPER)
|
||||
{
|
||||
// Start HSPI
|
||||
hspi = new SPIClass(HSPI);
|
||||
@@ -174,13 +174,13 @@ bool EInkDisplay::connect()
|
||||
adafruitDisplay->init();
|
||||
adafruitDisplay->setRotation(3);
|
||||
}
|
||||
#elif defined(PCA10059)
|
||||
#elif defined(PCA10059) || defined(ME25LS01)
|
||||
{
|
||||
auto lowLevel = new EINK_DISPLAY_MODEL(PIN_EINK_CS, PIN_EINK_DC, PIN_EINK_RES, PIN_EINK_BUSY);
|
||||
adafruitDisplay = new GxEPD2_BW<EINK_DISPLAY_MODEL, EINK_DISPLAY_MODEL::HEIGHT>(*lowLevel);
|
||||
adafruitDisplay->init(115200, true, 10, false, SPI1, SPISettings(4000000, MSBFIRST, SPI_MODE0));
|
||||
adafruitDisplay->setRotation(3);
|
||||
adafruitDisplay->setPartialWindow(0, 0, displayWidth, displayHeight);
|
||||
adafruitDisplay->init(115200, true, 40, false, SPI1, SPISettings(4000000, MSBFIRST, SPI_MODE0));
|
||||
adafruitDisplay->setRotation(0);
|
||||
adafruitDisplay->setPartialWindow(0, 0, EINK_WIDTH, EINK_HEIGHT);
|
||||
}
|
||||
#elif defined(M5_COREINK)
|
||||
auto lowLevel = new EINK_DISPLAY_MODEL(PIN_EINK_CS, PIN_EINK_DC, PIN_EINK_RES, PIN_EINK_BUSY);
|
||||
|
||||
@@ -68,7 +68,7 @@ class EInkDisplay : public OLEDDisplay
|
||||
|
||||
// If display uses HSPI
|
||||
#if defined(HELTEC_WIRELESS_PAPER) || defined(HELTEC_WIRELESS_PAPER_V1_0) || defined(HELTEC_VISION_MASTER_E213) || \
|
||||
defined(HELTEC_VISION_MASTER_E290)
|
||||
defined(HELTEC_VISION_MASTER_E290) || defined(TLORA_T3S3_EPAPER)
|
||||
SPIClass *hspi = NULL;
|
||||
#endif
|
||||
|
||||
|
||||
@@ -375,7 +375,7 @@ void EInkDynamicDisplay::hashImage()
|
||||
|
||||
// Sum all bytes of the image buffer together
|
||||
for (uint16_t b = 0; b < (displayWidth / 8) * displayHeight; b++) {
|
||||
imageHash += buffer[b];
|
||||
imageHash ^= buffer[b] << b;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+52
-9
@@ -20,6 +20,8 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
*/
|
||||
#include "Screen.h"
|
||||
#include "../userPrefs.h"
|
||||
#include "PowerMon.h"
|
||||
#include "configuration.h"
|
||||
#if HAS_SCREEN
|
||||
#include <OLEDDisplay.h>
|
||||
@@ -35,6 +37,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#include "gps/RTC.h"
|
||||
#include "graphics/ScreenFonts.h"
|
||||
#include "graphics/images.h"
|
||||
#include "input/ScanAndSelect.h"
|
||||
#include "input/TouchScreenImpl1.h"
|
||||
#include "main.h"
|
||||
#include "mesh-pb-constants.h"
|
||||
@@ -120,7 +123,7 @@ static bool heartbeat = false;
|
||||
/// 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)
|
||||
#if defined(OLED_PL) || defined(OLED_UA) || defined(OLED_RU)
|
||||
// Don't want to make any assumptions about custom language support
|
||||
return true;
|
||||
#endif
|
||||
@@ -156,7 +159,11 @@ static void drawIconScreen(const char *upperMsg, OLEDDisplay *display, OLEDDispl
|
||||
|
||||
display->setFont(FONT_MEDIUM);
|
||||
display->setTextAlignment(TEXT_ALIGN_LEFT);
|
||||
#ifdef SPLASH_TITLE_USERPREFS
|
||||
const char *title = SPLASH_TITLE_USERPREFS;
|
||||
#else
|
||||
const char *title = "meshtastic.org";
|
||||
#endif
|
||||
display->drawString(x + getStringCenteredX(title), y + SCREEN_HEIGHT - FONT_HEIGHT_MEDIUM, title);
|
||||
display->setFont(FONT_SMALL);
|
||||
|
||||
@@ -1086,7 +1093,8 @@ static void drawNodes(OLEDDisplay *display, int16_t x, int16_t y, const NodeStat
|
||||
{
|
||||
char usersString[20];
|
||||
snprintf(usersString, sizeof(usersString), "%d/%d", nodeStatus->getNumOnline(), nodeStatus->getNumTotal());
|
||||
#if (defined(USE_EINK) || defined(ILI9341_DRIVER) || defined(ST7735_CS) || defined(ST7789_CS) || defined(HX8357_CS)) && \
|
||||
#if (defined(USE_EINK) || defined(ILI9341_DRIVER) || defined(ST7735_CS) || defined(ST7789_CS) || defined(USE_ST7789) || \
|
||||
defined(HX8357_CS)) && \
|
||||
!defined(DISPLAY_FORCE_SMALL_FONTS)
|
||||
display->drawFastImage(x, y + 3, 8, 8, imgUser);
|
||||
#else
|
||||
@@ -1569,6 +1577,7 @@ void Screen::handleSetOn(bool on, FrameCallback einkScreensaver)
|
||||
if (on != screenOn) {
|
||||
if (on) {
|
||||
LOG_INFO("Turning on screen\n");
|
||||
powerMon->setState(meshtastic_PowerMon_State_Screen_On);
|
||||
#ifdef T_WATCH_S3
|
||||
PMU->enablePowerOutput(XPOWERS_ALDO2);
|
||||
#endif
|
||||
@@ -1583,6 +1592,9 @@ void Screen::handleSetOn(bool on, FrameCallback einkScreensaver)
|
||||
|
||||
dispdev->displayOn();
|
||||
#ifdef USE_ST7789
|
||||
pinMode(VTFT_CTRL, OUTPUT);
|
||||
digitalWrite(VTFT_CTRL, LOW);
|
||||
ui->init();
|
||||
#ifdef ESP_PLATFORM
|
||||
analogWrite(VTFT_LEDA, BRIGHTNESS_DEFAULT);
|
||||
#else
|
||||
@@ -1594,16 +1606,28 @@ void Screen::handleSetOn(bool on, FrameCallback einkScreensaver)
|
||||
setInterval(0); // Draw ASAP
|
||||
runASAP = true;
|
||||
} else {
|
||||
powerMon->clearState(meshtastic_PowerMon_State_Screen_On);
|
||||
#ifdef USE_EINK
|
||||
// eInkScreensaver parameter is usually NULL (default argument), default frame used instead
|
||||
setScreensaverFrames(einkScreensaver);
|
||||
#endif
|
||||
LOG_INFO("Turning off screen\n");
|
||||
dispdev->displayOff();
|
||||
|
||||
#ifdef USE_ST7789
|
||||
pinMode(VTFT_LEDA, OUTPUT);
|
||||
digitalWrite(VTFT_LEDA, !TFT_BACKLIGHT_ON);
|
||||
SPI1.end();
|
||||
#if defined(ARCH_ESP32)
|
||||
pinMode(VTFT_LEDA, ANALOG);
|
||||
pinMode(VTFT_CTRL, ANALOG);
|
||||
pinMode(ST7789_RESET, ANALOG);
|
||||
pinMode(ST7789_RS, ANALOG);
|
||||
pinMode(ST7789_NSS, ANALOG);
|
||||
#else
|
||||
nrf_gpio_cfg_default(VTFT_LEDA);
|
||||
nrf_gpio_cfg_default(VTFT_CTRL);
|
||||
nrf_gpio_cfg_default(ST7789_RESET);
|
||||
nrf_gpio_cfg_default(ST7789_RS);
|
||||
nrf_gpio_cfg_default(ST7789_NSS);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef T_WATCH_S3
|
||||
@@ -1742,6 +1766,11 @@ void Screen::forceDisplay(bool forceUiUpdate)
|
||||
#ifdef USE_EINK
|
||||
// If requested, make sure queued commands are run, and UI has rendered a new frame
|
||||
if (forceUiUpdate) {
|
||||
// Force a display refresh, in addition to the UI update
|
||||
// Changing the GPS status bar icon apparently doesn't register as a change in image
|
||||
// (False negative of the image hashing algorithm used to skip identical frames)
|
||||
EINK_ADD_FRAMEFLAG(dispdev, DEMAND_FAST);
|
||||
|
||||
// No delay between UI frame rendering
|
||||
setFastFramerate();
|
||||
|
||||
@@ -1897,6 +1926,13 @@ int32_t Screen::runOnce()
|
||||
// standard screen loop handling here
|
||||
if (config.display.auto_screen_carousel_secs > 0 &&
|
||||
(millis() - lastScreenTransition) > (config.display.auto_screen_carousel_secs * 1000)) {
|
||||
|
||||
// If an E-Ink display struggles with fast refresh, force carousel to use full refresh instead
|
||||
// Carousel is potentially a major source of E-Ink display wear
|
||||
#if !defined(EINK_BACKGROUND_USES_FAST)
|
||||
EINK_ADD_FRAMEFLAG(dispdev, COSMETIC);
|
||||
#endif
|
||||
|
||||
LOG_DEBUG("LastScreenTransition exceeded %ums transitioning to next frame\n", (millis() - lastScreenTransition));
|
||||
handleOnPress();
|
||||
}
|
||||
@@ -2276,6 +2312,11 @@ void Screen::handlePrint(const char *text)
|
||||
|
||||
void Screen::handleOnPress()
|
||||
{
|
||||
// If Canned Messages is using the "Scan and Select" input, dismiss the canned message frame when user button is pressed
|
||||
// Minimize impact as a courtesy, as "scan and select" may be used as default config for some boards
|
||||
if (scanAndSelectInput != nullptr && scanAndSelectInput->dismissCannedMessageFrame())
|
||||
return;
|
||||
|
||||
// If screen was off, just wake it, otherwise advance to next frame
|
||||
// If we are in a transition, the press must have bounced, drop it.
|
||||
if (ui->getUiState()->frameState == FIXED) {
|
||||
@@ -2379,7 +2420,8 @@ void DebugInfo::drawFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16
|
||||
#ifdef ARCH_ESP32
|
||||
if (millis() - storeForwardModule->lastHeartbeat >
|
||||
(storeForwardModule->heartbeatInterval * 1200)) { // no heartbeat, overlap a bit
|
||||
#if (defined(USE_EINK) || defined(ILI9341_DRIVER) || defined(ST7735_CS) || defined(ST7789_CS) || defined(HX8357_CS)) && \
|
||||
#if (defined(USE_EINK) || defined(ILI9341_DRIVER) || defined(ST7735_CS) || defined(ST7789_CS) || defined(USE_ST7789) || \
|
||||
defined(HX8357_CS)) && \
|
||||
!defined(DISPLAY_FORCE_SMALL_FONTS)
|
||||
display->drawFastImage(x + SCREEN_WIDTH - 14 - display->getStringWidth(ourId), y + 3 + FONT_HEIGHT_SMALL, 12, 8,
|
||||
imgQuestionL1);
|
||||
@@ -2390,7 +2432,8 @@ void DebugInfo::drawFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16
|
||||
imgQuestion);
|
||||
#endif
|
||||
} else {
|
||||
#if (defined(USE_EINK) || defined(ILI9341_DRIVER) || defined(ST7735_CS) || defined(ST7789_CS) || defined(HX8357_CS)) && \
|
||||
#if (defined(USE_EINK) || defined(ILI9341_DRIVER) || defined(ST7735_CS) || defined(ST7789_CS) || defined(USE_ST7789) || \
|
||||
defined(HX8357_CS)) && \
|
||||
!defined(DISPLAY_FORCE_SMALL_FONTS)
|
||||
display->drawFastImage(x + SCREEN_WIDTH - 18 - display->getStringWidth(ourId), y + 3 + FONT_HEIGHT_SMALL, 16, 8,
|
||||
imgSFL1);
|
||||
@@ -2404,8 +2447,8 @@ void DebugInfo::drawFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16
|
||||
#endif
|
||||
} else {
|
||||
// TODO: Raspberry Pi supports more than just the one screen size
|
||||
#if (defined(USE_EINK) || defined(ILI9341_DRIVER) || defined(ST7735_CS) || defined(ST7789_CS) || defined(HX8357_CS) || \
|
||||
ARCH_PORTDUINO) && \
|
||||
#if (defined(USE_EINK) || defined(ILI9341_DRIVER) || defined(ST7735_CS) || defined(ST7789_CS) || defined(USE_ST7789) || \
|
||||
defined(HX8357_CS) || ARCH_PORTDUINO) && \
|
||||
!defined(DISPLAY_FORCE_SMALL_FONTS)
|
||||
display->drawFastImage(x + SCREEN_WIDTH - 14 - display->getStringWidth(ourId), y + 3 + FONT_HEIGHT_SMALL, 12, 8,
|
||||
imgInfoL1);
|
||||
|
||||
+50
-2
@@ -309,7 +309,7 @@ class Screen : public concurrency::OSThread
|
||||
static char customFontTableLookup(const uint8_t ch)
|
||||
{
|
||||
// UTF-8 to font table index converter
|
||||
// Code form http://playground.arduino.cc/Main/Utf8ascii
|
||||
// Code from http://playground.arduino.cc/Main/Utf8ascii
|
||||
static uint8_t LASTCHAR;
|
||||
static bool SKIPREST; // Only display a single unconvertable-character symbol per sequence of unconvertable characters
|
||||
|
||||
@@ -322,11 +322,57 @@ class Screen : public concurrency::OSThread
|
||||
uint8_t last = LASTCHAR; // get last char
|
||||
LASTCHAR = ch;
|
||||
|
||||
switch (last) { // conversion depending on first UTF8-character
|
||||
switch (last) {
|
||||
case 0xC2: {
|
||||
SKIPREST = false;
|
||||
return (uint8_t)ch;
|
||||
}
|
||||
}
|
||||
|
||||
// We want to strip out prefix chars for two-byte char formats
|
||||
if (ch == 0xC2)
|
||||
return (uint8_t)0;
|
||||
|
||||
#if defined(OLED_PL)
|
||||
|
||||
switch (last) {
|
||||
case 0xC3: {
|
||||
|
||||
if (ch == 147)
|
||||
return (uint8_t)(ch); // Ó
|
||||
else if (ch == 179)
|
||||
return (uint8_t)(148); // ó
|
||||
else
|
||||
return (uint8_t)(ch | 0xC0);
|
||||
break;
|
||||
}
|
||||
|
||||
case 0xC4: {
|
||||
SKIPREST = false;
|
||||
return (uint8_t)(ch);
|
||||
}
|
||||
|
||||
case 0xC5: {
|
||||
SKIPREST = false;
|
||||
if (ch == 132)
|
||||
return (uint8_t)(136); // ń
|
||||
else if (ch == 186)
|
||||
return (uint8_t)(137); // ź
|
||||
else
|
||||
return (uint8_t)(ch);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// We want to strip out prefix chars for two-byte char formats
|
||||
if (ch == 0xC2 || ch == 0xC3 || ch == 0xC4 || ch == 0xC5)
|
||||
return (uint8_t)0;
|
||||
|
||||
#endif
|
||||
|
||||
#if defined(OLED_UA) || defined(OLED_RU)
|
||||
|
||||
switch (last) {
|
||||
case 0xC3: {
|
||||
SKIPREST = false;
|
||||
return (uint8_t)(ch | 0xC0);
|
||||
@@ -376,6 +422,8 @@ class Screen : public concurrency::OSThread
|
||||
if (ch == 0xC2 || ch == 0xC3 || ch == 0x82 || ch == 0xD0 || ch == 0xD1)
|
||||
return (uint8_t)0;
|
||||
|
||||
#endif
|
||||
|
||||
// If we already returned an unconvertable-character symbol for this unconvertable-character sequence, return NULs for the
|
||||
// rest of it
|
||||
if (SKIPREST)
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
#pragma once
|
||||
|
||||
#ifdef OLED_PL
|
||||
#include "graphics/fonts/OLEDDisplayFontsPL.h"
|
||||
#endif
|
||||
|
||||
#ifdef OLED_RU
|
||||
#include "graphics/fonts/OLEDDisplayFontsRU.h"
|
||||
#endif
|
||||
@@ -8,13 +12,17 @@
|
||||
#include "graphics/fonts/OLEDDisplayFontsUA.h"
|
||||
#endif
|
||||
|
||||
#if (defined(USE_EINK) || defined(ILI9341_DRIVER) || defined(ST7735_CS) || defined(ST7789_CS) || defined(HX8357_CS)) && \
|
||||
#if (defined(USE_EINK) || defined(ILI9341_DRIVER) || defined(ST7735_CS) || defined(ST7789_CS) || defined(USE_ST7789) || \
|
||||
defined(HX8357_CS)) && \
|
||||
!defined(DISPLAY_FORCE_SMALL_FONTS)
|
||||
// The screen is bigger so use bigger fonts
|
||||
#define FONT_SMALL ArialMT_Plain_16 // Height: 19
|
||||
#define FONT_MEDIUM ArialMT_Plain_24 // Height: 28
|
||||
#define FONT_LARGE ArialMT_Plain_24 // Height: 28
|
||||
#else
|
||||
#ifdef OLED_PL
|
||||
#define FONT_SMALL ArialMT_Plain_10_PL
|
||||
#else
|
||||
#ifdef OLED_RU
|
||||
#define FONT_SMALL ArialMT_Plain_10_RU
|
||||
#else
|
||||
@@ -24,6 +32,7 @@
|
||||
#define FONT_SMALL ArialMT_Plain_10 // Height: 13
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
#define FONT_MEDIUM ArialMT_Plain_16 // Height: 19
|
||||
#define FONT_LARGE ArialMT_Plain_24 // Height: 28
|
||||
#endif
|
||||
|
||||
+30
-49
@@ -21,10 +21,6 @@ extern SX1509 gpioExtender;
|
||||
#if defined(ST7735S)
|
||||
#include <LovyanGFX.hpp> // Graphics and font library for ST7735 driver chip
|
||||
|
||||
#if defined(ST7735_BACKLIGHT_EN) && !defined(TFT_BL)
|
||||
#define TFT_BL ST7735_BACKLIGHT_EN
|
||||
#endif
|
||||
|
||||
#ifndef TFT_INVERT
|
||||
#define TFT_INVERT true
|
||||
#endif
|
||||
@@ -91,24 +87,20 @@ class LGFX : public lgfx::LGFX_Device
|
||||
_panel_instance.config(cfg);
|
||||
}
|
||||
|
||||
#ifdef TFT_BL
|
||||
// Set the backlight control
|
||||
{
|
||||
auto cfg = _light_instance.config(); // Gets a structure for backlight settings.
|
||||
|
||||
#ifdef ST7735_BL_V03
|
||||
cfg.pin_bl = ST7735_BL_V03;
|
||||
#elif defined(ST7735_BL_V05)
|
||||
cfg.pin_bl = ST7735_BL_V05;
|
||||
#else
|
||||
cfg.pin_bl = ST7735_BL; // Pin number to which the backlight is connected
|
||||
#endif
|
||||
cfg.invert = true; // true to invert the brightness of the backlight
|
||||
cfg.pin_bl = TFT_BL; // Pin number to which the backlight is connected
|
||||
cfg.invert = true; // true to invert the brightness of the backlight
|
||||
// cfg.freq = 44100; // PWM frequency of backlight
|
||||
// cfg.pwm_channel = 1; // PWM channel number to use
|
||||
|
||||
_light_instance.config(cfg);
|
||||
_panel_instance.setLight(&_light_instance); // Set the backlight on the panel.
|
||||
}
|
||||
#endif
|
||||
|
||||
setPanel(&_panel_instance);
|
||||
}
|
||||
@@ -131,10 +123,6 @@ static void rak14014_tpIntHandle(void)
|
||||
#elif defined(ST7789_CS)
|
||||
#include <LovyanGFX.hpp> // Graphics and font library for ST7735 driver chip
|
||||
|
||||
#if defined(ST7789_BACKLIGHT_EN) && !defined(TFT_BL)
|
||||
#define TFT_BL ST7789_BACKLIGHT_EN
|
||||
#endif
|
||||
|
||||
class LGFX : public lgfx::LGFX_Device
|
||||
{
|
||||
lgfx::Panel_ST7789 _panel_instance;
|
||||
@@ -204,6 +192,7 @@ class LGFX : public lgfx::LGFX_Device
|
||||
_panel_instance.config(cfg);
|
||||
}
|
||||
|
||||
#ifdef ST7789_BL
|
||||
// Set the backlight control. (delete if not necessary)
|
||||
{
|
||||
auto cfg = _light_instance.config(); // Gets a structure for backlight settings.
|
||||
@@ -215,6 +204,7 @@ class LGFX : public lgfx::LGFX_Device
|
||||
_light_instance.config(cfg);
|
||||
_panel_instance.setLight(&_light_instance); // Set the backlight on the panel.
|
||||
}
|
||||
#endif
|
||||
|
||||
#if HAS_TOUCHSCREEN
|
||||
// Configure settings for touch screen control.
|
||||
@@ -324,6 +314,7 @@ class LGFX : public lgfx::LGFX_Device
|
||||
_panel_instance.config(cfg);
|
||||
}
|
||||
|
||||
#ifdef TFT_BL
|
||||
// Set the backlight control
|
||||
{
|
||||
auto cfg = _light_instance.config(); // Gets a structure for backlight settings.
|
||||
@@ -336,6 +327,7 @@ class LGFX : public lgfx::LGFX_Device
|
||||
_light_instance.config(cfg);
|
||||
_panel_instance.setLight(&_light_instance); // Set the backlight on the panel.
|
||||
}
|
||||
#endif
|
||||
|
||||
setPanel(&_panel_instance);
|
||||
}
|
||||
@@ -521,9 +513,26 @@ static LGFX *tft = nullptr;
|
||||
extern unPhone unphone;
|
||||
#endif
|
||||
|
||||
GpioPin *TFTDisplay::backlightEnable = NULL;
|
||||
|
||||
TFTDisplay::TFTDisplay(uint8_t address, int sda, int scl, OLEDDISPLAY_GEOMETRY geometry, HW_I2C i2cBus)
|
||||
{
|
||||
LOG_DEBUG("TFTDisplay!\n");
|
||||
|
||||
#ifdef TFT_BL
|
||||
GpioPin *p = new GpioHwPin(TFT_BL);
|
||||
|
||||
if (!TFT_BACKLIGHT_ON) { // Need to invert the pin before hardware
|
||||
auto virtPin = new GpioVirtPin();
|
||||
new GpioNotTransformer(
|
||||
virtPin, p); // We just leave this created object on the heap so it can stay watching virtPin and driving en_gpio
|
||||
p = virtPin;
|
||||
}
|
||||
#else
|
||||
GpioPin *p = new GpioVirtPin(); // Just simulate a pin
|
||||
#endif
|
||||
backlightEnable = p;
|
||||
|
||||
#if ARCH_PORTDUINO
|
||||
if (settingsMap[displayRotate]) {
|
||||
setGeometry(GEOMETRY_RAWMODE, settingsMap[configNames::displayHeight], settingsMap[configNames::displayWidth]);
|
||||
@@ -577,24 +586,15 @@ void TFTDisplay::sendCommand(uint8_t com)
|
||||
// handle display on/off directly
|
||||
switch (com) {
|
||||
case DISPLAYON: {
|
||||
// LOG_DEBUG("Display on\n");
|
||||
backlightEnable->set(true);
|
||||
#if ARCH_PORTDUINO
|
||||
display(true);
|
||||
if (settingsMap[displayBacklight] > 0)
|
||||
digitalWrite(settingsMap[displayBacklight], TFT_BACKLIGHT_ON);
|
||||
#elif defined(ST7735_BL_V03)
|
||||
digitalWrite(ST7735_BL_V03, TFT_BACKLIGHT_ON);
|
||||
#elif defined(ST7735_BL_V05)
|
||||
pinMode(ST7735_BL_V05, OUTPUT);
|
||||
digitalWrite(ST7735_BL_V05, TFT_BACKLIGHT_ON);
|
||||
#elif !defined(RAK14014) && !defined(M5STACK) && !defined(UNPHONE)
|
||||
tft->wakeup();
|
||||
tft->powerSaveOff();
|
||||
#elif defined(TFT_BL) && defined(TFT_BACKLIGHT_ON)
|
||||
digitalWrite(TFT_BL, TFT_BACKLIGHT_ON);
|
||||
#endif
|
||||
|
||||
#ifdef VTFT_CTRL_V03
|
||||
digitalWrite(VTFT_CTRL_V03, LOW);
|
||||
#endif
|
||||
|
||||
#ifdef VTFT_CTRL
|
||||
@@ -610,25 +610,17 @@ void TFTDisplay::sendCommand(uint8_t com)
|
||||
break;
|
||||
}
|
||||
case DISPLAYOFF: {
|
||||
// LOG_DEBUG("Display off\n");
|
||||
backlightEnable->set(false);
|
||||
#if ARCH_PORTDUINO
|
||||
tft->clear();
|
||||
if (settingsMap[displayBacklight] > 0)
|
||||
digitalWrite(settingsMap[displayBacklight], !TFT_BACKLIGHT_ON);
|
||||
#elif defined(ST7735_BL_V03)
|
||||
digitalWrite(ST7735_BL_V03, !TFT_BACKLIGHT_ON);
|
||||
#elif defined(ST7735_BL_V05)
|
||||
pinMode(ST7735_BL_V05, OUTPUT);
|
||||
digitalWrite(ST7735_BL_V05, !TFT_BACKLIGHT_ON);
|
||||
#elif !defined(RAK14014) && !defined(M5STACK) && !defined(UNPHONE)
|
||||
tft->sleep();
|
||||
tft->powerSaveOn();
|
||||
#elif defined(TFT_BL) && defined(TFT_BACKLIGHT_ON)
|
||||
digitalWrite(TFT_BL, !TFT_BACKLIGHT_ON);
|
||||
#endif
|
||||
|
||||
#ifdef VTFT_CTRL_V03
|
||||
digitalWrite(VTFT_CTRL_V03, HIGH);
|
||||
#endif
|
||||
#ifdef VTFT_CTRL
|
||||
digitalWrite(VTFT_CTRL, HIGH);
|
||||
#endif
|
||||
@@ -712,20 +704,9 @@ bool TFTDisplay::connect()
|
||||
tft = new LGFX;
|
||||
#endif
|
||||
|
||||
#ifdef TFT_BL
|
||||
pinMode(TFT_BL, OUTPUT);
|
||||
digitalWrite(TFT_BL, TFT_BACKLIGHT_ON);
|
||||
// pinMode(PIN_3V3_EN, OUTPUT);
|
||||
// digitalWrite(PIN_3V3_EN, HIGH);
|
||||
backlightEnable->set(true);
|
||||
LOG_INFO("Power to TFT Backlight\n");
|
||||
#endif
|
||||
|
||||
#ifdef ST7735_BL_V03
|
||||
digitalWrite(ST7735_BL_V03, TFT_BACKLIGHT_ON);
|
||||
#elif defined(ST7735_BL_V05)
|
||||
pinMode(ST7735_BL_V05, OUTPUT);
|
||||
digitalWrite(ST7735_BL_V05, TFT_BACKLIGHT_ON);
|
||||
#endif
|
||||
#ifdef UNPHONE
|
||||
unphone.backlight(true); // using unPhone library
|
||||
LOG_INFO("Power to TFT Backlight\n");
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include <GpioLogic.h>
|
||||
#include <OLEDDisplay.h>
|
||||
|
||||
/**
|
||||
@@ -39,6 +40,14 @@ class TFTDisplay : public OLEDDisplay
|
||||
*/
|
||||
void setDetected(uint8_t detected);
|
||||
|
||||
/**
|
||||
* This is normally managed entirely by TFTDisplay, but some rare applications (heltec tracker) might need to replace the
|
||||
* default GPIO behavior with something a bit more complex.
|
||||
*
|
||||
* We (cruftily) make it static so that variant.cpp can access it without needing a ptr to the TFTDisplay instance.
|
||||
*/
|
||||
static GpioPin *backlightEnable;
|
||||
|
||||
protected:
|
||||
// the header size of the buffer used, e.g. for the SPI command header
|
||||
virtual int getBufferOffset(void) override { return 0; }
|
||||
|
||||
@@ -0,0 +1,440 @@
|
||||
#include "OLEDDisplayFontsPL.h"
|
||||
|
||||
// Font generated or edited with the glyphEditor
|
||||
const uint8_t ArialMT_Plain_10_PL[] PROGMEM = {
|
||||
0x0A, // Width: 10
|
||||
0x0D, // Height: 13
|
||||
0x20, // First char: 32
|
||||
0xE0, // Number of chars: 224
|
||||
|
||||
// Jump Table:
|
||||
0xFF, 0xFF, 0x00, 0x03, // 32:65535
|
||||
0x00, 0x00, 0x04, 0x03, // 33
|
||||
0x00, 0x04, 0x05, 0x04, // 34
|
||||
0x00, 0x09, 0x09, 0x06, // 35
|
||||
0x00, 0x12, 0x0A, 0x06, // 36
|
||||
0x00, 0x1C, 0x10, 0x09, // 37
|
||||
0x00, 0x2C, 0x0E, 0x08, // 38
|
||||
0x00, 0x3A, 0x01, 0x02, // 39
|
||||
0x00, 0x3B, 0x06, 0x04, // 40
|
||||
0x00, 0x41, 0x06, 0x04, // 41
|
||||
0x00, 0x47, 0x05, 0x04, // 42
|
||||
0x00, 0x4C, 0x09, 0x06, // 43
|
||||
0x00, 0x55, 0x04, 0x03, // 44
|
||||
0x00, 0x59, 0x03, 0x03, // 45
|
||||
0x00, 0x5C, 0x04, 0x03, // 46
|
||||
0x00, 0x60, 0x05, 0x04, // 47
|
||||
0x00, 0x65, 0x0A, 0x06, // 48
|
||||
0x00, 0x6F, 0x08, 0x05, // 49
|
||||
0x00, 0x77, 0x0A, 0x06, // 50
|
||||
0x00, 0x81, 0x0A, 0x06, // 51
|
||||
0x00, 0x8B, 0x0B, 0x07, // 52
|
||||
0x00, 0x96, 0x0A, 0x06, // 53
|
||||
0x00, 0xA0, 0x0A, 0x06, // 54
|
||||
0x00, 0xAA, 0x09, 0x06, // 55
|
||||
0x00, 0xB3, 0x0A, 0x06, // 56
|
||||
0x00, 0xBD, 0x0A, 0x06, // 57
|
||||
0x00, 0xC7, 0x04, 0x03, // 58
|
||||
0x00, 0xCB, 0x04, 0x03, // 59
|
||||
0x00, 0xCF, 0x0A, 0x06, // 60
|
||||
0x00, 0xD9, 0x09, 0x06, // 61
|
||||
0x00, 0xE2, 0x09, 0x06, // 62
|
||||
0x00, 0xEB, 0x0B, 0x07, // 63
|
||||
0x00, 0xF6, 0x14, 0x0B, // 64
|
||||
0x01, 0x0A, 0x0E, 0x08, // 65
|
||||
0x01, 0x18, 0x0C, 0x07, // 66
|
||||
0x01, 0x24, 0x0C, 0x07, // 67
|
||||
0x01, 0x30, 0x0B, 0x07, // 68
|
||||
0x01, 0x3B, 0x0C, 0x07, // 69
|
||||
0x01, 0x47, 0x09, 0x06, // 70
|
||||
0x01, 0x50, 0x0D, 0x08, // 71
|
||||
0x01, 0x5D, 0x0C, 0x07, // 72
|
||||
0x01, 0x69, 0x04, 0x03, // 73
|
||||
0x01, 0x6D, 0x08, 0x05, // 74
|
||||
0x01, 0x75, 0x0E, 0x08, // 75
|
||||
0x01, 0x83, 0x0C, 0x07, // 76
|
||||
0x01, 0x8F, 0x10, 0x09, // 77
|
||||
0x01, 0x9F, 0x0C, 0x07, // 78
|
||||
0x01, 0xAB, 0x0E, 0x08, // 79
|
||||
0x01, 0xB9, 0x0B, 0x07, // 80
|
||||
0x01, 0xC4, 0x0E, 0x08, // 81
|
||||
0x01, 0xD2, 0x0C, 0x07, // 82
|
||||
0x01, 0xDE, 0x0C, 0x07, // 83
|
||||
0x01, 0xEA, 0x0B, 0x07, // 84
|
||||
0x01, 0xF5, 0x0C, 0x07, // 85
|
||||
0x02, 0x01, 0x0D, 0x08, // 86
|
||||
0x02, 0x0E, 0x11, 0x0A, // 87
|
||||
0x02, 0x1F, 0x0E, 0x08, // 88
|
||||
0x02, 0x2D, 0x0D, 0x08, // 89
|
||||
0x02, 0x3A, 0x0C, 0x07, // 90
|
||||
0x02, 0x46, 0x06, 0x04, // 91
|
||||
0x02, 0x4C, 0x06, 0x04, // 92
|
||||
0x02, 0x52, 0x04, 0x03, // 93
|
||||
0x02, 0x56, 0x09, 0x06, // 94
|
||||
0x02, 0x5F, 0x0C, 0x07, // 95
|
||||
0x02, 0x6B, 0x03, 0x03, // 96
|
||||
0x02, 0x6E, 0x0A, 0x06, // 97
|
||||
0x02, 0x78, 0x0A, 0x06, // 98
|
||||
0x02, 0x82, 0x0A, 0x06, // 99
|
||||
0x02, 0x8C, 0x0A, 0x06, // 100
|
||||
0x02, 0x96, 0x0A, 0x06, // 101
|
||||
0x02, 0xA0, 0x05, 0x04, // 102
|
||||
0x02, 0xA5, 0x0A, 0x06, // 103
|
||||
0x02, 0xAF, 0x0A, 0x06, // 104
|
||||
0x02, 0xB9, 0x04, 0x03, // 105
|
||||
0x02, 0xBD, 0x04, 0x03, // 106
|
||||
0x02, 0xC1, 0x08, 0x05, // 107
|
||||
0x02, 0xC9, 0x04, 0x03, // 108
|
||||
0x02, 0xCD, 0x10, 0x09, // 109
|
||||
0x02, 0xDD, 0x0A, 0x06, // 110
|
||||
0x02, 0xE7, 0x0A, 0x06, // 111
|
||||
0x02, 0xF1, 0x0A, 0x06, // 112
|
||||
0x02, 0xFB, 0x0A, 0x06, // 113
|
||||
0x03, 0x05, 0x05, 0x04, // 114
|
||||
0x03, 0x0A, 0x08, 0x05, // 115
|
||||
0x03, 0x12, 0x06, 0x04, // 116
|
||||
0x03, 0x18, 0x0A, 0x06, // 117
|
||||
0x03, 0x22, 0x09, 0x06, // 118
|
||||
0x03, 0x2B, 0x0E, 0x08, // 119
|
||||
0x03, 0x39, 0x0A, 0x06, // 120
|
||||
0x03, 0x43, 0x09, 0x06, // 121
|
||||
0x03, 0x4C, 0x0A, 0x06, // 122
|
||||
0x03, 0x56, 0x06, 0x04, // 123
|
||||
0x03, 0x5C, 0x04, 0x03, // 124
|
||||
0x03, 0x60, 0x05, 0x04, // 125
|
||||
0x03, 0x65, 0x09, 0x06, // 126
|
||||
0xFF, 0xFF, 0x00, 0x0A, // 127
|
||||
0xFF, 0xFF, 0x00, 0x0A, // 128
|
||||
0x03, 0x6E, 0x0C, 0x07, // 129
|
||||
0x03, 0x7A, 0x05, 0x04, // 130
|
||||
0x03, 0x7F, 0x0C, 0x07, // 131
|
||||
0x03, 0x8B, 0x0E, 0x08, // 132
|
||||
0x03, 0x99, 0x0C, 0x07, // 133
|
||||
0x03, 0xA5, 0x0C, 0x07, // 134
|
||||
0x03, 0xB1, 0x0A, 0x06, // 135
|
||||
0x03, 0xBB, 0x0A, 0x06, // 136
|
||||
0x03, 0xC5, 0x0A, 0x06, // 137
|
||||
0xFF, 0xFF, 0x00, 0x0A, // 138
|
||||
0xFF, 0xFF, 0x00, 0x0A, // 139
|
||||
0xFF, 0xFF, 0x00, 0x0A, // 140
|
||||
0xFF, 0xFF, 0x00, 0x0A, // 141
|
||||
0xFF, 0xFF, 0x00, 0x0A, // 142
|
||||
0xFF, 0xFF, 0x00, 0x0A, // 143
|
||||
0xFF, 0xFF, 0x00, 0x0A, // 144
|
||||
0xFF, 0xFF, 0x00, 0x0A, // 145
|
||||
0xFF, 0xFF, 0x00, 0x0A, // 146
|
||||
0x03, 0xCF, 0x0E, 0x08, // 147
|
||||
0x03, 0xDD, 0x0A, 0x06, // 148
|
||||
0xFF, 0xFF, 0x00, 0x0A, // 149
|
||||
0xFF, 0xFF, 0x00, 0x0A, // 150
|
||||
0xFF, 0xFF, 0x00, 0x0A, // 151
|
||||
0x03, 0xE7, 0x0C, 0x07, // 152
|
||||
0x03, 0xF3, 0x0C, 0x07, // 153
|
||||
0x03, 0xFF, 0x0C, 0x07, // 154
|
||||
0x04, 0x0B, 0x08, 0x05, // 155
|
||||
0xFF, 0xFF, 0x00, 0x0A, // 156
|
||||
0xFF, 0xFF, 0x00, 0x0A, // 157
|
||||
0xFF, 0xFF, 0x00, 0x0A, // 158
|
||||
0xFF, 0xFF, 0x00, 0x0A, // 159
|
||||
0xFF, 0xFF, 0x00, 0x0A, // 160
|
||||
0x04, 0x13, 0x04, 0x03, // 161
|
||||
0x04, 0x17, 0x0A, 0x06, // 162
|
||||
0x04, 0x21, 0x0C, 0x07, // 163
|
||||
0x04, 0x2D, 0x0A, 0x06, // 164
|
||||
0x04, 0x37, 0x0A, 0x06, // 165
|
||||
0x04, 0x41, 0x04, 0x03, // 166
|
||||
0x04, 0x45, 0x0A, 0x06, // 167
|
||||
0x04, 0x4F, 0x05, 0x04, // 168
|
||||
0x04, 0x54, 0x0D, 0x08, // 169
|
||||
0x04, 0x61, 0x07, 0x05, // 170
|
||||
0x04, 0x68, 0x0A, 0x06, // 171
|
||||
0x04, 0x72, 0x09, 0x06, // 172
|
||||
0x04, 0x7B, 0x03, 0x03, // 173
|
||||
0x04, 0x7E, 0x0D, 0x08, // 174
|
||||
0x04, 0x8B, 0x0B, 0x07, // 175
|
||||
0x04, 0x96, 0x07, 0x05, // 176
|
||||
0x04, 0x9D, 0x0A, 0x06, // 177
|
||||
0x04, 0xA7, 0x05, 0x04, // 178
|
||||
0x04, 0xAC, 0x05, 0x04, // 179
|
||||
0x04, 0xB1, 0x05, 0x04, // 180
|
||||
0x04, 0xB6, 0x0A, 0x06, // 181
|
||||
0x04, 0xC0, 0x09, 0x06, // 182
|
||||
0x04, 0xC9, 0x03, 0x03, // 183
|
||||
0x04, 0xCC, 0x06, 0x04, // 184
|
||||
0x04, 0xD2, 0x0C, 0x07, // 185
|
||||
0x04, 0xDE, 0x07, 0x05, // 186
|
||||
0x04, 0xE5, 0x0C, 0x07, // 187
|
||||
0x04, 0xF1, 0x0A, 0x06, // 188
|
||||
0x04, 0xFB, 0x10, 0x09, // 189
|
||||
0x05, 0x0B, 0x10, 0x09, // 190
|
||||
0x05, 0x1B, 0x0A, 0x06, // 191
|
||||
0x05, 0x25, 0x0E, 0x08, // 192
|
||||
0x05, 0x33, 0x0E, 0x08, // 193
|
||||
0x05, 0x41, 0x0E, 0x08, // 194
|
||||
0x05, 0x4F, 0x0E, 0x08, // 195
|
||||
0x05, 0x5D, 0x0E, 0x08, // 196
|
||||
0x05, 0x6B, 0x0E, 0x08, // 197
|
||||
0x05, 0x79, 0x12, 0x0A, // 198
|
||||
0x05, 0x8B, 0x0C, 0x07, // 199
|
||||
0x05, 0x97, 0x0C, 0x07, // 200
|
||||
0x05, 0xA3, 0x0C, 0x07, // 201
|
||||
0x05, 0xAF, 0x0C, 0x07, // 202
|
||||
0x05, 0xBB, 0x0C, 0x07, // 203
|
||||
0x05, 0xC7, 0x05, 0x04, // 204
|
||||
0x05, 0xCC, 0x04, 0x03, // 205
|
||||
0x05, 0xD0, 0x04, 0x03, // 206
|
||||
0x05, 0xD4, 0x05, 0x04, // 207
|
||||
0x05, 0xD9, 0x0B, 0x07, // 208
|
||||
0x05, 0xE4, 0x0C, 0x07, // 209
|
||||
0x05, 0xF0, 0x0E, 0x08, // 210
|
||||
0x05, 0xFE, 0x0E, 0x08, // 211
|
||||
0x06, 0x0C, 0x0E, 0x08, // 212
|
||||
0x06, 0x1A, 0x0E, 0x08, // 213
|
||||
0x06, 0x28, 0x0E, 0x08, // 214
|
||||
0x06, 0x36, 0x0A, 0x06, // 215
|
||||
0x06, 0x40, 0x0D, 0x08, // 216
|
||||
0x06, 0x4D, 0x0C, 0x07, // 217
|
||||
0x06, 0x59, 0x0C, 0x07, // 218
|
||||
0x06, 0x65, 0x0C, 0x07, // 219
|
||||
0x06, 0x71, 0x0C, 0x07, // 220
|
||||
0x06, 0x7D, 0x0D, 0x08, // 221
|
||||
0x06, 0x8A, 0x0B, 0x07, // 222
|
||||
0x06, 0x95, 0x0C, 0x07, // 223
|
||||
0x06, 0xA1, 0x0A, 0x06, // 224
|
||||
0x06, 0xAB, 0x0A, 0x06, // 225
|
||||
0x06, 0xB5, 0x0A, 0x06, // 226
|
||||
0x06, 0xBF, 0x0A, 0x06, // 227
|
||||
0x06, 0xC9, 0x0A, 0x06, // 228
|
||||
0x06, 0xD3, 0x0A, 0x06, // 229
|
||||
0x06, 0xDD, 0x10, 0x09, // 230
|
||||
0x06, 0xED, 0x0A, 0x06, // 231
|
||||
0x06, 0xF7, 0x0A, 0x06, // 232
|
||||
0x07, 0x01, 0x0A, 0x06, // 233
|
||||
0x07, 0x0B, 0x0A, 0x06, // 234
|
||||
0x07, 0x15, 0x0A, 0x06, // 235
|
||||
0x07, 0x1F, 0x05, 0x04, // 236
|
||||
0x07, 0x24, 0x04, 0x03, // 237
|
||||
0x07, 0x28, 0x05, 0x04, // 238
|
||||
0x07, 0x2D, 0x05, 0x04, // 239
|
||||
0x07, 0x32, 0x0A, 0x06, // 240
|
||||
0x07, 0x3C, 0x0A, 0x06, // 241
|
||||
0x07, 0x46, 0x0A, 0x06, // 242
|
||||
0x07, 0x50, 0x0A, 0x06, // 243
|
||||
0x07, 0x5A, 0x0A, 0x06, // 244
|
||||
0x07, 0x64, 0x0A, 0x06, // 245
|
||||
0x07, 0x6E, 0x0A, 0x06, // 246
|
||||
0x07, 0x78, 0x09, 0x06, // 247
|
||||
0x07, 0x81, 0x0A, 0x06, // 248
|
||||
0x07, 0x8B, 0x0A, 0x06, // 249
|
||||
0x07, 0x95, 0x0A, 0x06, // 250
|
||||
0x07, 0x9F, 0x0A, 0x06, // 251
|
||||
0x07, 0xA9, 0x0A, 0x06, // 252
|
||||
0x07, 0xB3, 0x09, 0x06, // 253
|
||||
0x07, 0xBC, 0x0A, 0x06, // 254
|
||||
0x07, 0xC6, 0x09, 0x06, // 255
|
||||
// Font Data:
|
||||
0x00, 0x00, 0xF8, 0x02, // 33
|
||||
0x38, 0x00, 0x00, 0x00, 0x38, // 34
|
||||
0xA0, 0x03, 0xE0, 0x00, 0xB8, 0x03, 0xE0, 0x00, 0xB8, // 35
|
||||
0x30, 0x01, 0x28, 0x02, 0xF8, 0x07, 0x48, 0x02, 0x90, 0x01, // 36
|
||||
0x00, 0x00, 0x30, 0x00, 0x48, 0x00, 0x30, 0x03, 0xC0, 0x00, 0xB0, 0x01, 0x48, 0x02, 0x80, 0x01, // 37
|
||||
0x80, 0x01, 0x50, 0x02, 0x68, 0x02, 0xA8, 0x02, 0x18, 0x01, 0x80, 0x03, 0x80, 0x02, // 38
|
||||
0x38, // 39
|
||||
0xE0, 0x03, 0x10, 0x04, 0x08, 0x08, // 40
|
||||
0x08, 0x08, 0x10, 0x04, 0xE0, 0x03, // 41
|
||||
0x28, 0x00, 0x18, 0x00, 0x28, // 42
|
||||
0x40, 0x00, 0x40, 0x00, 0xF0, 0x01, 0x40, 0x00, 0x40, // 43
|
||||
0x00, 0x00, 0x00, 0x06, // 44
|
||||
0x80, 0x00, 0x80, // 45
|
||||
0x00, 0x00, 0x00, 0x02, // 46
|
||||
0x00, 0x03, 0xE0, 0x00, 0x18, // 47
|
||||
0xF0, 0x01, 0x08, 0x02, 0x08, 0x02, 0x08, 0x02, 0xF0, 0x01, // 48
|
||||
0x00, 0x00, 0x20, 0x00, 0x10, 0x00, 0xF8, 0x03, // 49
|
||||
0x10, 0x02, 0x08, 0x03, 0x88, 0x02, 0x48, 0x02, 0x30, 0x02, // 50
|
||||
0x10, 0x01, 0x08, 0x02, 0x48, 0x02, 0x48, 0x02, 0xB0, 0x01, // 51
|
||||
0xC0, 0x00, 0xA0, 0x00, 0x90, 0x00, 0x88, 0x00, 0xF8, 0x03, 0x80, // 52
|
||||
0x60, 0x01, 0x38, 0x02, 0x28, 0x02, 0x28, 0x02, 0xC8, 0x01, // 53
|
||||
0xF0, 0x01, 0x28, 0x02, 0x28, 0x02, 0x28, 0x02, 0xD0, 0x01, // 54
|
||||
0x08, 0x00, 0x08, 0x03, 0xC8, 0x00, 0x38, 0x00, 0x08, // 55
|
||||
0xB0, 0x01, 0x48, 0x02, 0x48, 0x02, 0x48, 0x02, 0xB0, 0x01, // 56
|
||||
0x70, 0x01, 0x88, 0x02, 0x88, 0x02, 0x88, 0x02, 0xF0, 0x01, // 57
|
||||
0x00, 0x00, 0x20, 0x02, // 58
|
||||
0x00, 0x00, 0x20, 0x06, // 59
|
||||
0x00, 0x00, 0x40, 0x00, 0xA0, 0x00, 0xA0, 0x00, 0x10, 0x01, // 60
|
||||
0xA0, 0x00, 0xA0, 0x00, 0xA0, 0x00, 0xA0, 0x00, 0xA0, // 61
|
||||
0x00, 0x00, 0x10, 0x01, 0xA0, 0x00, 0xA0, 0x00, 0x40, // 62
|
||||
0x10, 0x00, 0x08, 0x00, 0x08, 0x00, 0xC8, 0x02, 0x48, 0x00, 0x30, // 63
|
||||
0x00, 0x00, 0xC0, 0x03, 0x30, 0x04, 0xD0, 0x09, 0x28, 0x0A, 0x28, 0x0A, 0xC8, 0x0B, 0x68, 0x0A, 0x10, 0x05, 0xE0, 0x04, // 64
|
||||
0x00, 0x02, 0xC0, 0x01, 0xB0, 0x00, 0x88, 0x00, 0xB0, 0x00, 0xC0, 0x01, 0x00, 0x02, // 65
|
||||
0x00, 0x00, 0xF8, 0x03, 0x48, 0x02, 0x48, 0x02, 0x48, 0x02, 0xF0, 0x01, // 66
|
||||
0x00, 0x00, 0xF0, 0x01, 0x08, 0x02, 0x08, 0x02, 0x08, 0x02, 0x10, 0x01, // 67
|
||||
0x00, 0x00, 0xF8, 0x03, 0x08, 0x02, 0x08, 0x02, 0x10, 0x01, 0xE0, // 68
|
||||
0x00, 0x00, 0xF8, 0x03, 0x48, 0x02, 0x48, 0x02, 0x48, 0x02, 0x48, 0x02, // 69
|
||||
0x00, 0x00, 0xF8, 0x03, 0x48, 0x00, 0x48, 0x00, 0x08, // 70
|
||||
0x00, 0x00, 0xE0, 0x00, 0x10, 0x01, 0x08, 0x02, 0x48, 0x02, 0x50, 0x01, 0xC0, // 71
|
||||
0x00, 0x00, 0xF8, 0x03, 0x40, 0x00, 0x40, 0x00, 0x40, 0x00, 0xF8, 0x03, // 72
|
||||
0x00, 0x00, 0xF8, 0x03, // 73
|
||||
0x00, 0x03, 0x00, 0x02, 0x00, 0x02, 0xF8, 0x01, // 74
|
||||
0x00, 0x00, 0xF8, 0x03, 0x80, 0x00, 0x60, 0x00, 0x90, 0x00, 0x08, 0x01, 0x00, 0x02, // 75
|
||||
0x00, 0x00, 0xF8, 0x03, 0x00, 0x02, 0x00, 0x02, 0x00, 0x02, 0x00, 0x02, // 76
|
||||
0x00, 0x00, 0xF8, 0x03, 0x30, 0x00, 0xC0, 0x01, 0x00, 0x02, 0xC0, 0x01, 0x30, 0x00, 0xF8, 0x03, // 77
|
||||
0x00, 0x00, 0xF8, 0x03, 0x30, 0x00, 0x40, 0x00, 0x80, 0x01, 0xF8, 0x03, // 78
|
||||
0x00, 0x00, 0xF0, 0x01, 0x08, 0x02, 0x08, 0x02, 0x08, 0x02, 0x08, 0x02, 0xF0, 0x01, // 79
|
||||
0x00, 0x00, 0xF8, 0x03, 0x48, 0x00, 0x48, 0x00, 0x48, 0x00, 0x30, // 80
|
||||
0x00, 0x00, 0xF0, 0x01, 0x08, 0x02, 0x08, 0x02, 0x08, 0x03, 0x08, 0x03, 0xF0, 0x02, // 81
|
||||
0x00, 0x00, 0xF8, 0x03, 0x48, 0x00, 0x48, 0x00, 0xC8, 0x00, 0x30, 0x03, // 82
|
||||
0x00, 0x00, 0x30, 0x01, 0x48, 0x02, 0x48, 0x02, 0x48, 0x02, 0x90, 0x01, // 83
|
||||
0x00, 0x00, 0x08, 0x00, 0x08, 0x00, 0xF8, 0x03, 0x08, 0x00, 0x08, // 84
|
||||
0x00, 0x00, 0xF8, 0x01, 0x00, 0x02, 0x00, 0x02, 0x00, 0x02, 0xF8, 0x01, // 85
|
||||
0x08, 0x00, 0x70, 0x00, 0x80, 0x01, 0x00, 0x02, 0x80, 0x01, 0x70, 0x00, 0x08, // 86
|
||||
0x18, 0x00, 0xE0, 0x01, 0x00, 0x02, 0xF0, 0x01, 0x08, 0x00, 0xF0, 0x01, 0x00, 0x02, 0xE0, 0x01, 0x18, // 87
|
||||
0x00, 0x02, 0x08, 0x01, 0x90, 0x00, 0x60, 0x00, 0x90, 0x00, 0x08, 0x01, 0x00, 0x02, // 88
|
||||
0x08, 0x00, 0x10, 0x00, 0x20, 0x00, 0xC0, 0x03, 0x20, 0x00, 0x10, 0x00, 0x08, // 89
|
||||
0x08, 0x03, 0x88, 0x02, 0xC8, 0x02, 0x68, 0x02, 0x38, 0x02, 0x18, 0x02, // 90
|
||||
0x00, 0x00, 0xF8, 0x0F, 0x08, 0x08, // 91
|
||||
0x18, 0x00, 0xE0, 0x00, 0x00, 0x03, // 92
|
||||
0x08, 0x08, 0xF8, 0x0F, // 93
|
||||
0x40, 0x00, 0x30, 0x00, 0x08, 0x00, 0x30, 0x00, 0x40, // 94
|
||||
0x00, 0x08, 0x00, 0x08, 0x00, 0x08, 0x00, 0x08, 0x00, 0x08, 0x00, 0x08, // 95
|
||||
0x08, 0x00, 0x10, // 96
|
||||
0x00, 0x00, 0x00, 0x03, 0xA0, 0x02, 0xA0, 0x02, 0xE0, 0x03, // 97
|
||||
0x00, 0x00, 0xF8, 0x03, 0x20, 0x02, 0x20, 0x02, 0xC0, 0x01, // 98
|
||||
0x00, 0x00, 0xC0, 0x01, 0x20, 0x02, 0x20, 0x02, 0x40, 0x01, // 99
|
||||
0x00, 0x00, 0xC0, 0x01, 0x20, 0x02, 0x20, 0x02, 0xF8, 0x03, // 100
|
||||
0x00, 0x00, 0xC0, 0x01, 0xA0, 0x02, 0xA0, 0x02, 0xC0, 0x02, // 101
|
||||
0x20, 0x00, 0xF0, 0x03, 0x28, // 102
|
||||
0x00, 0x00, 0xC0, 0x05, 0x20, 0x0A, 0x20, 0x0A, 0xE0, 0x07, // 103
|
||||
0x00, 0x00, 0xF8, 0x03, 0x20, 0x00, 0x20, 0x00, 0xC0, 0x03, // 104
|
||||
0x00, 0x00, 0xE8, 0x03, // 105
|
||||
0x00, 0x08, 0xE8, 0x07, // 106
|
||||
0xF8, 0x03, 0x80, 0x00, 0xC0, 0x01, 0x20, 0x02, // 107
|
||||
0x00, 0x00, 0xF8, 0x03, // 108
|
||||
0x00, 0x00, 0xE0, 0x03, 0x20, 0x00, 0x20, 0x00, 0xE0, 0x03, 0x20, 0x00, 0x20, 0x00, 0xC0, 0x03, // 109
|
||||
0x00, 0x00, 0xE0, 0x03, 0x20, 0x00, 0x20, 0x00, 0xC0, 0x03, // 110
|
||||
0x00, 0x00, 0xC0, 0x01, 0x20, 0x02, 0x20, 0x02, 0xC0, 0x01, // 111
|
||||
0x00, 0x00, 0xE0, 0x0F, 0x20, 0x02, 0x20, 0x02, 0xC0, 0x01, // 112
|
||||
0x00, 0x00, 0xC0, 0x01, 0x20, 0x02, 0x20, 0x02, 0xE0, 0x0F, // 113
|
||||
0x00, 0x00, 0xE0, 0x03, 0x20, // 114
|
||||
0x40, 0x02, 0xA0, 0x02, 0xA0, 0x02, 0x20, 0x01, // 115
|
||||
0x20, 0x00, 0xF8, 0x03, 0x20, 0x02, // 116
|
||||
0x00, 0x00, 0xE0, 0x01, 0x00, 0x02, 0x00, 0x02, 0xE0, 0x03, // 117
|
||||
0x20, 0x00, 0xC0, 0x01, 0x00, 0x02, 0xC0, 0x01, 0x20, // 118
|
||||
0xE0, 0x01, 0x00, 0x02, 0xC0, 0x01, 0x20, 0x00, 0xC0, 0x01, 0x00, 0x02, 0xE0, 0x01, // 119
|
||||
0x20, 0x02, 0x40, 0x01, 0x80, 0x00, 0x40, 0x01, 0x20, 0x02, // 120
|
||||
0x20, 0x00, 0xC0, 0x09, 0x00, 0x06, 0xC0, 0x01, 0x20, // 121
|
||||
0x20, 0x02, 0x20, 0x03, 0xA0, 0x02, 0x60, 0x02, 0x20, 0x02, // 122
|
||||
0x80, 0x00, 0x78, 0x0F, 0x08, 0x08, // 123
|
||||
0x00, 0x00, 0xF8, 0x0F, // 124
|
||||
0x08, 0x08, 0x78, 0x0F, 0x80, // 125
|
||||
0xC0, 0x00, 0x40, 0x00, 0xC0, 0x00, 0x80, 0x00, 0xC0, // 126
|
||||
0x00, 0x00, 0xF8, 0x03, 0x40, 0x02, 0x20, 0x02, 0x00, 0x02, 0x00, 0x02, // 129
|
||||
0x40, 0x00, 0xF8, 0x03, 0x20, // 130
|
||||
0x00, 0x00, 0xF8, 0x03, 0x30, 0x00, 0x44, 0x00, 0x82, 0x01, 0xF8, 0x03, // 131
|
||||
0x00, 0x02, 0xC0, 0x01, 0xB0, 0x00, 0x88, 0x00, 0xB0, 0x00, 0xC0, 0x05, 0x00, 0x0A, // 132
|
||||
0x00, 0x00, 0x00, 0x03, 0xA0, 0x02, 0xA0, 0x02, 0xE0, 0x07, 0x00, 0x08, // 133
|
||||
0x00, 0x00, 0xF0, 0x01, 0x08, 0x02, 0x0A, 0x02, 0x09, 0x02, 0x10, 0x01, // 134
|
||||
0x00, 0x00, 0xC0, 0x01, 0x20, 0x02, 0x28, 0x02, 0x44, 0x01, // 135
|
||||
0x00, 0x00, 0xE0, 0x03, 0x20, 0x00, 0x28, 0x00, 0xC4, 0x03, // 136
|
||||
0x20, 0x02, 0x20, 0x03, 0xA8, 0x02, 0x64, 0x02, 0x20, 0x02, // 137
|
||||
0x00, 0x00, 0xF0, 0x01, 0x08, 0x02, 0x08, 0x02, 0x0A, 0x02, 0x09, 0x02, 0xF0, 0x01, // 147
|
||||
0x00, 0x00, 0xC0, 0x01, 0x20, 0x02, 0x28, 0x02, 0xC4, 0x01, // 148
|
||||
0x00, 0x00, 0xF8, 0x03, 0x48, 0x02, 0x48, 0x02, 0x48, 0x06, 0x48, 0x0A, // 152
|
||||
0x00, 0x00, 0xC0, 0x01, 0xA0, 0x02, 0xA0, 0x02, 0xC0, 0x06, 0x00, 0x08, // 153
|
||||
0x00, 0x00, 0x30, 0x01, 0x48, 0x02, 0x4A, 0x02, 0x49, 0x02, 0x90, 0x01, // 154
|
||||
0x40, 0x02, 0xA0, 0x02, 0xA8, 0x02, 0x24, 0x01, // 155
|
||||
0x00, 0x00, 0xA0, 0x0F, // 161
|
||||
0x00, 0x00, 0xC0, 0x01, 0xA0, 0x0F, 0x78, 0x02, 0x40, 0x01, // 162
|
||||
0x40, 0x02, 0x70, 0x03, 0xC8, 0x02, 0x48, 0x02, 0x08, 0x02, 0x10, 0x02, // 163
|
||||
0x00, 0x00, 0xE0, 0x01, 0x20, 0x01, 0x20, 0x01, 0xE0, 0x01, // 164
|
||||
0x48, 0x01, 0x70, 0x01, 0xC0, 0x03, 0x70, 0x01, 0x48, 0x01, // 165
|
||||
0x00, 0x00, 0x38, 0x0F, // 166
|
||||
0xD0, 0x04, 0x28, 0x09, 0x48, 0x09, 0x48, 0x0A, 0x90, 0x05, // 167
|
||||
0x08, 0x00, 0x00, 0x00, 0x08, // 168
|
||||
0xE0, 0x00, 0x10, 0x01, 0x48, 0x02, 0xA8, 0x02, 0xA8, 0x02, 0x10, 0x01, 0xE0, // 169
|
||||
0x68, 0x00, 0x68, 0x00, 0x68, 0x00, 0x78, // 170
|
||||
0x00, 0x00, 0x80, 0x01, 0x40, 0x02, 0x80, 0x01, 0x40, 0x02, // 171
|
||||
0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0xE0, // 172
|
||||
0x80, 0x00, 0x80, // 173
|
||||
0xE0, 0x00, 0x10, 0x01, 0xE8, 0x02, 0x68, 0x02, 0xC8, 0x02, 0x10, 0x01, 0xE0, // 174
|
||||
0x02, 0x00, 0x02, 0x00, 0x02, 0x00, 0x02, 0x00, 0x02, 0x00, 0x02, // 175
|
||||
0x00, 0x00, 0x38, 0x00, 0x28, 0x00, 0x38, // 176
|
||||
0x40, 0x02, 0x40, 0x02, 0xF0, 0x03, 0x40, 0x02, 0x40, 0x02, // 177
|
||||
0x48, 0x00, 0x68, 0x00, 0x58, // 178
|
||||
0x48, 0x00, 0x58, 0x00, 0x68, // 179
|
||||
0x00, 0x00, 0x10, 0x00, 0x08, // 180
|
||||
0x00, 0x00, 0xE0, 0x0F, 0x00, 0x02, 0x00, 0x02, 0xE0, 0x03, // 181
|
||||
0x70, 0x00, 0xF8, 0x0F, 0x08, 0x00, 0xF8, 0x0F, 0x08, // 182
|
||||
0x00, 0x00, 0x40, // 183
|
||||
0x00, 0x00, 0x00, 0x14, 0x00, 0x18, // 184
|
||||
0x08, 0x03, 0x88, 0x02, 0xCA, 0x02, 0x69, 0x02, 0x38, 0x02, 0x18, 0x02, // 185
|
||||
0x30, 0x00, 0x48, 0x00, 0x48, 0x00, 0x30, // 186
|
||||
0x08, 0x03, 0x88, 0x02, 0xC8, 0x02, 0x6A, 0x02, 0x38, 0x02, 0x18, 0x02, // 187
|
||||
0x20, 0x02, 0x20, 0x03, 0xA8, 0x02, 0x60, 0x02, 0x20, 0x02, // 188
|
||||
0x00, 0x00, 0x10, 0x02, 0x78, 0x01, 0x80, 0x00, 0x60, 0x00, 0x50, 0x02, 0x48, 0x03, 0xC0, 0x02, // 189
|
||||
0x48, 0x00, 0x58, 0x00, 0x68, 0x03, 0x80, 0x00, 0x60, 0x01, 0x90, 0x01, 0xC8, 0x03, 0x00, 0x01, // 190
|
||||
0x00, 0x00, 0x00, 0x06, 0x00, 0x09, 0xA0, 0x09, 0x00, 0x04, // 191
|
||||
0x00, 0x02, 0xC0, 0x01, 0xB0, 0x00, 0x89, 0x00, 0xB2, 0x00, 0xC0, 0x01, 0x00, 0x02, // 192
|
||||
0x00, 0x02, 0xC0, 0x01, 0xB0, 0x00, 0x8A, 0x00, 0xB1, 0x00, 0xC0, 0x01, 0x00, 0x02, // 193
|
||||
0x00, 0x02, 0xC0, 0x01, 0xB2, 0x00, 0x89, 0x00, 0xB2, 0x00, 0xC0, 0x01, 0x00, 0x02, // 194
|
||||
0x00, 0x02, 0xC2, 0x01, 0xB1, 0x00, 0x8A, 0x00, 0xB1, 0x00, 0xC0, 0x01, 0x00, 0x02, // 195
|
||||
0x00, 0x02, 0xC0, 0x01, 0xB2, 0x00, 0x88, 0x00, 0xB2, 0x00, 0xC0, 0x01, 0x00, 0x02, // 196
|
||||
0x00, 0x02, 0xC0, 0x01, 0xBE, 0x00, 0x8A, 0x00, 0xBE, 0x00, 0xC0, 0x01, 0x00, 0x02, // 197
|
||||
0x00, 0x03, 0xC0, 0x00, 0xE0, 0x00, 0x98, 0x00, 0x88, 0x00, 0xF8, 0x03, 0x48, 0x02, 0x48, 0x02, 0x48, 0x02, // 198
|
||||
0x00, 0x00, 0xF0, 0x01, 0x08, 0x02, 0x08, 0x16, 0x08, 0x1A, 0x10, 0x01, // 199
|
||||
0x00, 0x00, 0xF8, 0x03, 0x49, 0x02, 0x4A, 0x02, 0x48, 0x02, 0x48, 0x02, // 200
|
||||
0x00, 0x00, 0xF8, 0x03, 0x48, 0x02, 0x4A, 0x02, 0x49, 0x02, 0x48, 0x02, // 201
|
||||
0x00, 0x00, 0xFA, 0x03, 0x49, 0x02, 0x4A, 0x02, 0x48, 0x02, 0x48, 0x02, // 202
|
||||
0x00, 0x00, 0xF8, 0x03, 0x4A, 0x02, 0x48, 0x02, 0x4A, 0x02, 0x48, 0x02, // 203
|
||||
0x00, 0x00, 0xF9, 0x03, 0x02, // 204
|
||||
0x02, 0x00, 0xF9, 0x03, // 205
|
||||
0x01, 0x00, 0xFA, 0x03, // 206
|
||||
0x02, 0x00, 0xF8, 0x03, 0x02, // 207
|
||||
0x40, 0x00, 0xF8, 0x03, 0x48, 0x02, 0x48, 0x02, 0x10, 0x01, 0xE0, // 208
|
||||
0x00, 0x00, 0xFA, 0x03, 0x31, 0x00, 0x42, 0x00, 0x81, 0x01, 0xF8, 0x03, // 209
|
||||
0x00, 0x00, 0xF0, 0x01, 0x08, 0x02, 0x09, 0x02, 0x0A, 0x02, 0x08, 0x02, 0xF0, 0x01, // 210
|
||||
0x00, 0x00, 0xF0, 0x01, 0x08, 0x02, 0x0A, 0x02, 0x09, 0x02, 0x08, 0x02, 0xF0, 0x01, // 211
|
||||
0x00, 0x00, 0xF0, 0x01, 0x08, 0x02, 0x0A, 0x02, 0x09, 0x02, 0x0A, 0x02, 0xF0, 0x01, // 212
|
||||
0x00, 0x00, 0xF0, 0x01, 0x0A, 0x02, 0x09, 0x02, 0x0A, 0x02, 0x09, 0x02, 0xF0, 0x01, // 213
|
||||
0x00, 0x00, 0xF0, 0x01, 0x0A, 0x02, 0x08, 0x02, 0x0A, 0x02, 0x08, 0x02, 0xF0, 0x01, // 214
|
||||
0x10, 0x01, 0xA0, 0x00, 0xE0, 0x00, 0xA0, 0x00, 0x10, 0x01, // 215
|
||||
0x00, 0x00, 0xF0, 0x02, 0x08, 0x03, 0xC8, 0x02, 0x28, 0x02, 0x18, 0x03, 0xE8, // 216
|
||||
0x00, 0x00, 0xF8, 0x01, 0x01, 0x02, 0x02, 0x02, 0x00, 0x02, 0xF8, 0x01, // 217
|
||||
0x00, 0x00, 0xF8, 0x01, 0x02, 0x02, 0x01, 0x02, 0x00, 0x02, 0xF8, 0x01, // 218
|
||||
0x00, 0x00, 0xF8, 0x01, 0x02, 0x02, 0x01, 0x02, 0x02, 0x02, 0xF8, 0x01, // 219
|
||||
0x00, 0x00, 0xF8, 0x01, 0x02, 0x02, 0x00, 0x02, 0x02, 0x02, 0xF8, 0x01, // 220
|
||||
0x08, 0x00, 0x10, 0x00, 0x20, 0x00, 0xC2, 0x03, 0x21, 0x00, 0x10, 0x00, 0x08, // 221
|
||||
0x00, 0x00, 0xF8, 0x03, 0x10, 0x01, 0x10, 0x01, 0x10, 0x01, 0xE0, // 222
|
||||
0x00, 0x00, 0xF0, 0x03, 0x08, 0x01, 0x48, 0x02, 0xB0, 0x02, 0x80, 0x01, // 223
|
||||
0x00, 0x00, 0x00, 0x03, 0xA4, 0x02, 0xA8, 0x02, 0xE0, 0x03, // 224
|
||||
0x00, 0x00, 0x00, 0x03, 0xA8, 0x02, 0xA4, 0x02, 0xE0, 0x03, // 225
|
||||
0x00, 0x00, 0x00, 0x03, 0xA8, 0x02, 0xA4, 0x02, 0xE8, 0x03, // 226
|
||||
0x00, 0x00, 0x08, 0x03, 0xA4, 0x02, 0xA8, 0x02, 0xE4, 0x03, // 227
|
||||
0x00, 0x00, 0x00, 0x03, 0xA8, 0x02, 0xA0, 0x02, 0xE8, 0x03, // 228
|
||||
0x00, 0x00, 0x00, 0x03, 0xAE, 0x02, 0xAA, 0x02, 0xEE, 0x03, // 229
|
||||
0x00, 0x00, 0x40, 0x03, 0xA0, 0x02, 0xA0, 0x02, 0xC0, 0x01, 0xA0, 0x02, 0xA0, 0x02, 0xC0, 0x02, // 230
|
||||
0x00, 0x00, 0xC0, 0x01, 0x20, 0x16, 0x20, 0x1A, 0x40, 0x01, // 231
|
||||
0x00, 0x00, 0xC0, 0x01, 0xA4, 0x02, 0xA8, 0x02, 0xC0, 0x02, // 232
|
||||
0x00, 0x00, 0xC0, 0x01, 0xA8, 0x02, 0xA4, 0x02, 0xC0, 0x02, // 233
|
||||
0x00, 0x00, 0xC0, 0x01, 0xA8, 0x02, 0xA4, 0x02, 0xC8, 0x02, // 234
|
||||
0x00, 0x00, 0xC0, 0x01, 0xA8, 0x02, 0xA0, 0x02, 0xC8, 0x02, // 235
|
||||
0x00, 0x00, 0xE4, 0x03, 0x08, // 236
|
||||
0x08, 0x00, 0xE4, 0x03, // 237
|
||||
0x08, 0x00, 0xE4, 0x03, 0x08, // 238
|
||||
0x08, 0x00, 0xE0, 0x03, 0x08, // 239
|
||||
0x00, 0x00, 0xC0, 0x01, 0x28, 0x02, 0x38, 0x02, 0xE0, 0x01, // 240
|
||||
0x00, 0x00, 0xE8, 0x03, 0x24, 0x00, 0x28, 0x00, 0xC4, 0x03, // 241
|
||||
0x00, 0x00, 0xC0, 0x01, 0x24, 0x02, 0x28, 0x02, 0xC0, 0x01, // 242
|
||||
0x00, 0x00, 0xC0, 0x01, 0x28, 0x02, 0x24, 0x02, 0xC0, 0x01, // 243
|
||||
0x00, 0x00, 0xC0, 0x01, 0x28, 0x02, 0x24, 0x02, 0xC8, 0x01, // 244
|
||||
0x00, 0x00, 0xC8, 0x01, 0x24, 0x02, 0x28, 0x02, 0xC4, 0x01, // 245
|
||||
0x00, 0x00, 0xC0, 0x01, 0x28, 0x02, 0x20, 0x02, 0xC8, 0x01, // 246
|
||||
0x40, 0x00, 0x40, 0x00, 0x50, 0x01, 0x40, 0x00, 0x40, // 247
|
||||
0x00, 0x00, 0xC0, 0x02, 0xA0, 0x03, 0x60, 0x02, 0xA0, 0x01, // 248
|
||||
0x00, 0x00, 0xE0, 0x01, 0x04, 0x02, 0x08, 0x02, 0xE0, 0x03, // 249
|
||||
0x00, 0x00, 0xE0, 0x01, 0x08, 0x02, 0x04, 0x02, 0xE0, 0x03, // 250
|
||||
0x00, 0x00, 0xE8, 0x01, 0x04, 0x02, 0x08, 0x02, 0xE0, 0x03, // 251
|
||||
0x00, 0x00, 0xE0, 0x01, 0x08, 0x02, 0x00, 0x02, 0xE8, 0x03, // 252
|
||||
0x20, 0x00, 0xC0, 0x09, 0x08, 0x06, 0xC4, 0x01, 0x20, // 253
|
||||
0x00, 0x00, 0xF8, 0x0F, 0x20, 0x02, 0x20, 0x02, 0xC0, 0x01, // 254
|
||||
0x20, 0x00, 0xC8, 0x09, 0x00, 0x06, 0xC8, 0x01, 0x20, // 255
|
||||
};
|
||||
@@ -0,0 +1,11 @@
|
||||
#ifndef OLEDDISPLAYFONTSPL_h
|
||||
#define OLEDDISPLAYFONTSPL_h
|
||||
|
||||
#ifdef ARDUINO
|
||||
#include <Arduino.h>
|
||||
#elif __MBED__
|
||||
#define PROGMEM
|
||||
#endif
|
||||
|
||||
extern const uint8_t ArialMT_Plain_10_PL[] PROGMEM;
|
||||
#endif
|
||||
@@ -20,8 +20,8 @@ const uint8_t bluetoothConnectedIcon[36] PROGMEM = {0xfe, 0x01, 0xff, 0x03, 0x03
|
||||
0xfe, 0x31, 0x00, 0x30, 0x30, 0x30, 0x30, 0x30, 0xf0, 0x3f, 0xe0, 0x1f};
|
||||
#endif
|
||||
|
||||
#if (defined(USE_EINK) || defined(ILI9341_DRIVER) || defined(ST7735_CS) || defined(ST7789_CS) || defined(HX8357_CS) || \
|
||||
ARCH_PORTDUINO) && \
|
||||
#if (defined(USE_EINK) || defined(ILI9341_DRIVER) || defined(ST7735_CS) || defined(ST7789_CS) || defined(USE_ST7789) || \
|
||||
defined(HX8357_CS) || ARCH_PORTDUINO) && \
|
||||
!defined(DISPLAY_FORCE_SMALL_FONTS)
|
||||
const uint8_t imgQuestionL1[] PROGMEM = {0xff, 0x01, 0x01, 0x32, 0x7b, 0x49, 0x49, 0x6f, 0x26, 0x01, 0x01, 0xff};
|
||||
const uint8_t imgQuestionL2[] PROGMEM = {0x0f, 0x08, 0x08, 0x08, 0x06, 0x0f, 0x0f, 0x06, 0x08, 0x08, 0x08, 0x0f};
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
#ifndef HAS_USERPREFS_SPLASH
|
||||
#define icon_width 50
|
||||
#define icon_height 28
|
||||
static uint8_t icon_bits[] = {
|
||||
@@ -17,4 +18,5 @@ static uint8_t icon_bits[] = {
|
||||
0xFE, 0x00, 0x00, 0xFC, 0x01, 0x7E, 0x00, 0x7F, 0x00, 0x00, 0xF8, 0x01,
|
||||
0x7E, 0x00, 0x3E, 0x00, 0x00, 0xF8, 0x01, 0x38, 0x00, 0x3C, 0x00, 0x00,
|
||||
0x70, 0x00, 0x10, 0x00, 0x10, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, };
|
||||
0x00, 0x00, 0x00, 0x00, };
|
||||
#endif
|
||||
@@ -0,0 +1,260 @@
|
||||
|
||||
#include "ExpressLRSFiveWay.h"
|
||||
|
||||
#ifdef INPUTBROKER_EXPRESSLRSFIVEWAY_TYPE
|
||||
|
||||
static const char inputSourceName[] = "ExpressLRS5Way"; // should match "allow input source" string
|
||||
|
||||
/**
|
||||
* @brief Calculate fuzz: half the distance to the next nearest neighbor for each joystick position.
|
||||
*
|
||||
* The goal is to avoid collisions between joystick positions while still maintaining
|
||||
* the widest tolerance for the analog value.
|
||||
*
|
||||
* Example: {10,50,800,1000,300,1600}
|
||||
* If we just choose the minimum difference for this array the value would
|
||||
* be 40/2 = 20.
|
||||
*
|
||||
* 20 does not leave enough room for the joystick position using 1600 which
|
||||
* could have a +-100 offset.
|
||||
*
|
||||
* Example Fuzz values: {20, 20, 100, 100, 125, 300} now the fuzz for the 1600
|
||||
* position is 300 instead of 20
|
||||
*/
|
||||
void ExpressLRSFiveWay::calcFuzzValues()
|
||||
{
|
||||
for (unsigned int i = 0; i < N_JOY_ADC_VALUES; i++) {
|
||||
uint16_t closestDist = 0xffff;
|
||||
uint16_t ival = joyAdcValues[i];
|
||||
// Find the closest value to ival
|
||||
for (unsigned int j = 0; j < N_JOY_ADC_VALUES; j++) {
|
||||
// Don't compare value with itself
|
||||
if (j == i)
|
||||
continue;
|
||||
uint16_t jval = joyAdcValues[j];
|
||||
if (jval < ival && (ival - jval < closestDist))
|
||||
closestDist = ival - jval;
|
||||
if (jval > ival && (jval - ival < closestDist))
|
||||
closestDist = jval - ival;
|
||||
} // for j
|
||||
|
||||
// And the fuzz is half the distance to the closest value
|
||||
fuzzValues[i] = closestDist / 2;
|
||||
// DBG("joy%u=%u f=%u, ", i, ival, fuzzValues[i]);
|
||||
} // for i
|
||||
}
|
||||
|
||||
int ExpressLRSFiveWay::readKey()
|
||||
{
|
||||
uint16_t value = analogRead(PIN_JOYSTICK);
|
||||
|
||||
constexpr uint8_t IDX_TO_INPUT[N_JOY_ADC_VALUES - 1] = {UP, DOWN, LEFT, RIGHT, OK};
|
||||
for (unsigned int i = 0; i < N_JOY_ADC_VALUES - 1; ++i) {
|
||||
if (value < (joyAdcValues[i] + fuzzValues[i]) && value > (joyAdcValues[i] - fuzzValues[i]))
|
||||
return IDX_TO_INPUT[i];
|
||||
}
|
||||
return NO_PRESS;
|
||||
}
|
||||
|
||||
ExpressLRSFiveWay::ExpressLRSFiveWay() : concurrency::OSThread(inputSourceName)
|
||||
{
|
||||
// ExpressLRS: init values
|
||||
isLongPressed = false;
|
||||
keyInProcess = NO_PRESS;
|
||||
keyDownStart = 0;
|
||||
|
||||
// Express LRS: calculate the threshold for interpreting ADC values as various buttons
|
||||
calcFuzzValues();
|
||||
|
||||
// Meshtastic: register with canned messages
|
||||
inputBroker->registerSource(this);
|
||||
}
|
||||
|
||||
// ExpressLRS: interpret reading as key events
|
||||
void ExpressLRSFiveWay::update(int *keyValue, bool *keyLongPressed)
|
||||
{
|
||||
*keyValue = NO_PRESS;
|
||||
|
||||
int newKey = readKey();
|
||||
uint32_t now = millis();
|
||||
if (keyInProcess == NO_PRESS) {
|
||||
// New key down
|
||||
if (newKey != NO_PRESS) {
|
||||
keyDownStart = now;
|
||||
// DBGLN("down=%u", newKey);
|
||||
}
|
||||
} else {
|
||||
// if key released
|
||||
if (newKey == NO_PRESS) {
|
||||
// DBGLN("up=%u", keyInProcess);
|
||||
if (!isLongPressed) {
|
||||
if ((now - keyDownStart) > KEY_DEBOUNCE_MS) {
|
||||
*keyValue = keyInProcess;
|
||||
*keyLongPressed = false;
|
||||
}
|
||||
}
|
||||
isLongPressed = false;
|
||||
}
|
||||
// else if the key has changed while down, reset state for next go-around
|
||||
else if (newKey != keyInProcess) {
|
||||
newKey = NO_PRESS;
|
||||
}
|
||||
// else still pressing, waiting for long if not already signaled
|
||||
else if (!isLongPressed) {
|
||||
if ((now - keyDownStart) > KEY_LONG_PRESS_MS) {
|
||||
*keyValue = keyInProcess;
|
||||
*keyLongPressed = true;
|
||||
isLongPressed = true;
|
||||
}
|
||||
}
|
||||
} // if keyInProcess != NO_PRESS
|
||||
|
||||
keyInProcess = newKey;
|
||||
}
|
||||
|
||||
// Meshtastic: runs at regular intervals
|
||||
int32_t ExpressLRSFiveWay::runOnce()
|
||||
{
|
||||
uint32_t now = millis();
|
||||
|
||||
// Dismiss any alert frames after 2 seconds
|
||||
// Feedback for GPS toggle / adhoc ping
|
||||
if (alerting && now > alertingSinceMs + 2000) {
|
||||
alerting = false;
|
||||
screen->endAlert();
|
||||
}
|
||||
|
||||
// Get key events from ExpressLRS code
|
||||
int keyValue;
|
||||
bool longPressed;
|
||||
update(&keyValue, &longPressed);
|
||||
|
||||
// Do something about this key press
|
||||
determineAction((KeyType)keyValue, longPressed ? LONG : SHORT);
|
||||
|
||||
// If there has been recent key activity, poll the joystick slightly more frequently
|
||||
if (now < keyDownStart + (20 * 1000UL)) // Within last 20 seconds
|
||||
return 100;
|
||||
|
||||
// Otherwise, poll slightly less often
|
||||
// Too many missed pressed if much slower than 250ms
|
||||
return 250;
|
||||
}
|
||||
|
||||
// Determine what action to take when a button press is detected
|
||||
// Written verbose for easier remapping by user
|
||||
void ExpressLRSFiveWay::determineAction(KeyType key, PressLength length)
|
||||
{
|
||||
switch (key) {
|
||||
case LEFT:
|
||||
if (inCannedMessageMenu()) // If in canned message menu
|
||||
sendKey(CANCEL); // exit the menu (press imaginary cancel key)
|
||||
else
|
||||
sendKey(LEFT);
|
||||
break;
|
||||
|
||||
case RIGHT:
|
||||
if (inCannedMessageMenu()) // If in canned message menu:
|
||||
sendKey(CANCEL); // exit the menu (press imaginary cancel key)
|
||||
else
|
||||
sendKey(RIGHT);
|
||||
break;
|
||||
|
||||
case UP:
|
||||
if (length == LONG)
|
||||
toggleGPS();
|
||||
else
|
||||
sendKey(UP);
|
||||
break;
|
||||
|
||||
case DOWN:
|
||||
if (length == LONG)
|
||||
sendAdhocPing();
|
||||
else
|
||||
sendKey(DOWN);
|
||||
break;
|
||||
|
||||
case OK:
|
||||
if (length == LONG)
|
||||
shutdown();
|
||||
else
|
||||
click(); // Use instead of sendKey(OK). Works better when canned message module disabled
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Feed input to the canned messages module
|
||||
void ExpressLRSFiveWay::sendKey(KeyType key)
|
||||
{
|
||||
InputEvent e;
|
||||
e.source = inputSourceName;
|
||||
e.inputEvent = key;
|
||||
notifyObservers(&e);
|
||||
}
|
||||
|
||||
// Enable or Disable a connected GPS
|
||||
// Contained as one method for easier remapping of buttons by user
|
||||
void ExpressLRSFiveWay::toggleGPS()
|
||||
{
|
||||
#if HAS_GPS && !MESHTASTIC_EXCLUDE_GPS
|
||||
if (!config.device.disable_triple_click && (gps != nullptr)) {
|
||||
gps->toggleGpsMode();
|
||||
screen->startAlert("GPS Toggled");
|
||||
alerting = true;
|
||||
alertingSinceMs = millis();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
// Send either node-info or position, on demand
|
||||
// Contained as one method for easier remapping of buttons by user
|
||||
void ExpressLRSFiveWay::sendAdhocPing()
|
||||
{
|
||||
service->refreshLocalMeshNode();
|
||||
bool sentPosition = service->trySendPosition(NODENUM_BROADCAST, true);
|
||||
|
||||
// Show custom alert frame, with multi-line centering
|
||||
screen->startAlert([sentPosition](OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y) -> void {
|
||||
uint16_t x_offset = display->width() / 2;
|
||||
uint16_t y_offset = 26; // Same constant as the default startAlert frame
|
||||
display->setTextAlignment(TEXT_ALIGN_CENTER);
|
||||
display->setFont(FONT_MEDIUM);
|
||||
display->drawString(x_offset + x, y_offset + y, "Sent ad-hoc");
|
||||
display->drawString(x_offset + x, y_offset + FONT_HEIGHT_MEDIUM + y, sentPosition ? "position" : "nodeinfo");
|
||||
});
|
||||
|
||||
alerting = true;
|
||||
alertingSinceMs = millis();
|
||||
}
|
||||
|
||||
// Shutdown the node (enter deep-sleep)
|
||||
// Contained as one method for easier remapping of buttons by user
|
||||
void ExpressLRSFiveWay::shutdown()
|
||||
{
|
||||
LOG_INFO("Shutdown from long press\n");
|
||||
powerFSM.trigger(EVENT_PRESS);
|
||||
screen->startAlert("Shutting down...");
|
||||
// Don't set alerting = true. We don't want to auto-dismiss this alert.
|
||||
|
||||
playShutdownMelody(); // In case user adds a buzzer
|
||||
|
||||
shutdownAtMsec = millis() + 3000;
|
||||
}
|
||||
|
||||
// Emulate user button, or canned message SELECT
|
||||
// This is necessary as canned message module doesn't translate SELECT to user button presses if the module is disabled
|
||||
// Contained as one method for easier remapping of buttons by user
|
||||
void ExpressLRSFiveWay::click()
|
||||
{
|
||||
if (!moduleConfig.canned_message.enabled)
|
||||
powerFSM.trigger(EVENT_PRESS);
|
||||
else
|
||||
sendKey(OK);
|
||||
}
|
||||
|
||||
ExpressLRSFiveWay *expressLRSFiveWayInput = nullptr;
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,85 @@
|
||||
/*
|
||||
Input source for Radio Master Bandit Nano, and similar hardware.
|
||||
Devices have a 5-button "resistor ladder" style joystick, read by ADC.
|
||||
These devices do not use the ADC to monitor input voltage.
|
||||
|
||||
Much of this code taken directly from ExpressLRS FiveWayButton class:
|
||||
https://github.com/ExpressLRS/ExpressLRS/tree/d9f56f8bd6f9f7144d5f01caaca766383e1e0950/src/lib/SCREEN/FiveWayButton
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "configuration.h"
|
||||
|
||||
#ifdef INPUTBROKER_EXPRESSLRSFIVEWAY_TYPE
|
||||
|
||||
#include <esp_adc_cal.h>
|
||||
#include <soc/adc_channel.h>
|
||||
|
||||
#include "InputBroker.h"
|
||||
#include "MeshService.h" // For adhoc ping action
|
||||
#include "buzz.h"
|
||||
#include "concurrency/OSThread.h"
|
||||
#include "graphics/Screen.h" // Feedback for adhoc ping / toggle GPS
|
||||
#include "main.h"
|
||||
#include "modules/CannedMessageModule.h"
|
||||
|
||||
#if HAS_GPS && !MESHTASTIC_EXCLUDE_GPS
|
||||
#include "GPS.h" // For toggle GPS action
|
||||
#endif
|
||||
|
||||
class ExpressLRSFiveWay : public Observable<const InputEvent *>, public concurrency::OSThread
|
||||
{
|
||||
private:
|
||||
// Number of values in JOY_ADC_VALUES, if defined
|
||||
// These must be ADC readings for {UP, DOWN, LEFT, RIGHT, ENTER, IDLE}
|
||||
static constexpr size_t N_JOY_ADC_VALUES = 6;
|
||||
static constexpr uint32_t KEY_DEBOUNCE_MS = 25;
|
||||
static constexpr uint32_t KEY_LONG_PRESS_MS = 3000; // How many milliseconds to hold key for a long press
|
||||
|
||||
// This merged an enum used by the ExpressLRS code, with meshtastic canned message values
|
||||
// Key names are kept simple, to allow user customizaton
|
||||
typedef enum {
|
||||
UP = meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_UP,
|
||||
DOWN = meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_DOWN,
|
||||
LEFT = meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_LEFT,
|
||||
RIGHT = meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_RIGHT,
|
||||
OK = meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_SELECT,
|
||||
CANCEL = meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_CANCEL,
|
||||
NO_PRESS = meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_NONE
|
||||
} KeyType;
|
||||
|
||||
typedef enum { SHORT, LONG } PressLength;
|
||||
|
||||
// From ExpressLRS
|
||||
int keyInProcess;
|
||||
uint32_t keyDownStart;
|
||||
bool isLongPressed;
|
||||
const uint16_t joyAdcValues[N_JOY_ADC_VALUES] = {JOYSTICK_ADC_VALS};
|
||||
uint16_t fuzzValues[N_JOY_ADC_VALUES];
|
||||
void calcFuzzValues();
|
||||
int readKey();
|
||||
void update(int *keyValue, bool *keyLongPressed);
|
||||
|
||||
// Meshtastic code
|
||||
void determineAction(KeyType key, PressLength length);
|
||||
void sendKey(KeyType key);
|
||||
inline bool inCannedMessageMenu() { return cannedMessageModule->shouldDraw(); }
|
||||
int32_t runOnce() override;
|
||||
|
||||
// Simplified Meshtastic actions, for easier remapping by user
|
||||
void toggleGPS();
|
||||
void sendAdhocPing();
|
||||
void shutdown();
|
||||
void click();
|
||||
|
||||
bool alerting = false; // Is the screen showing an alert frame? Feedback for GPS toggle / adhoc ping actions
|
||||
uint32_t alertingSinceMs = 0; // When did screen begin showing an alert frame? Used to auto-dismiss
|
||||
|
||||
public:
|
||||
ExpressLRSFiveWay();
|
||||
};
|
||||
|
||||
extern ExpressLRSFiveWay *expressLRSFiveWayInput;
|
||||
|
||||
#endif
|
||||
@@ -4,6 +4,20 @@
|
||||
#define ANYKEY 0xFF
|
||||
#define MATRIXKEY 0xFE
|
||||
|
||||
#define INPUT_BROKER_MSG_BRIGHTNESS_UP 0x11
|
||||
#define INPUT_BROKER_MSG_BRIGHTNESS_DOWN 0x12
|
||||
#define INPUT_BROKER_MSG_REBOOT 0x90
|
||||
#define INPUT_BROKER_MSG_SHUTDOWN 0x9b
|
||||
#define INPUT_BROKER_MSG_GPS_TOGGLE 0x9e
|
||||
#define INPUT_BROKER_MSG_MUTE_TOGGLE 0xac
|
||||
#define INPUT_BROKER_MSG_SEND_PING 0xaf
|
||||
#define INPUT_BROKER_MSG_LEFT 0xb4
|
||||
#define INPUT_BROKER_MSG_UP 0xb5
|
||||
#define INPUT_BROKER_MSG_DOWN 0xb6
|
||||
#define INPUT_BROKER_MSG_RIGHT 0xb7
|
||||
#define INPUT_BROKER_MSG_FN_SYMBOL_ON 0xf1
|
||||
#define INPUT_BROKER_MSG_FN_SYMBOL_OFF 0xf2
|
||||
|
||||
typedef struct _InputEvent {
|
||||
const char *source;
|
||||
char inputEvent;
|
||||
|
||||
@@ -147,11 +147,11 @@ int32_t LinuxInput::runOnce()
|
||||
case KEY_LEFT: // Left
|
||||
e.inputEvent = meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_LEFT;
|
||||
break;
|
||||
e.kbchar = 0xb4;
|
||||
e.kbchar = INPUT_BROKER_MSG_LEFT;
|
||||
case KEY_RIGHT: // Right
|
||||
e.inputEvent = meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_RIGHT;
|
||||
break;
|
||||
e.kbchar = 0xb7;
|
||||
e.kbchar = INPUT_BROKER_MSG_RIGHT;
|
||||
case KEY_ENTER: // Enter
|
||||
e.inputEvent = meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_SELECT;
|
||||
break;
|
||||
|
||||
@@ -0,0 +1,204 @@
|
||||
#include "configuration.h"
|
||||
|
||||
// Normally these input methods are protected by guarding in setupModules
|
||||
// In order to have the user button dismiss the canned message frame, this class lightly interacts with the Screen class
|
||||
#if HAS_SCREEN
|
||||
|
||||
#include "ScanAndSelect.h"
|
||||
#include "modules/CannedMessageModule.h"
|
||||
|
||||
// Config
|
||||
static const char name[] = "scanAndSelect"; // should match "allow input source" string
|
||||
static constexpr uint32_t durationShortMs = 50;
|
||||
static constexpr uint32_t durationLongMs = 1500;
|
||||
static constexpr uint32_t durationAlertMs = 2000;
|
||||
|
||||
// Constructor: init base class
|
||||
ScanAndSelectInput::ScanAndSelectInput() : concurrency::OSThread(name) {}
|
||||
|
||||
// Attempt to setup class; true if success.
|
||||
// Called by setupModules method. Instance deleted if setup fails.
|
||||
bool ScanAndSelectInput::init()
|
||||
{
|
||||
// Short circuit: Canned messages enabled?
|
||||
if (!moduleConfig.canned_message.enabled)
|
||||
return false;
|
||||
|
||||
// Short circuit: Using correct "input source"?
|
||||
// Todo: protobuf enum instead of string?
|
||||
if (strcasecmp(moduleConfig.canned_message.allow_input_source, name) != 0)
|
||||
return false;
|
||||
|
||||
// Use any available inputbroker pin as the button
|
||||
if (moduleConfig.canned_message.inputbroker_pin_press)
|
||||
pin = moduleConfig.canned_message.inputbroker_pin_press;
|
||||
else if (moduleConfig.canned_message.inputbroker_pin_a)
|
||||
pin = moduleConfig.canned_message.inputbroker_pin_a;
|
||||
else if (moduleConfig.canned_message.inputbroker_pin_b)
|
||||
pin = moduleConfig.canned_message.inputbroker_pin_b;
|
||||
else
|
||||
return false; // Short circuit: no button found
|
||||
|
||||
// Set-up the button
|
||||
pinMode(pin, INPUT_PULLUP);
|
||||
attachInterrupt(pin, handleChangeInterrupt, CHANGE);
|
||||
|
||||
// Connect our class to the canned message module
|
||||
inputBroker->registerSource(this);
|
||||
|
||||
LOG_INFO("Initialized 'Scan and Select' input for Canned Messages, using pin %d\n", pin);
|
||||
return true; // Init succeded
|
||||
}
|
||||
|
||||
// Runs periodically, unless sleeping between presses
|
||||
int32_t ScanAndSelectInput::runOnce()
|
||||
{
|
||||
uint32_t now = millis();
|
||||
|
||||
// If: "no messages added" alert screen currently shown
|
||||
if (alertingNoMessage) {
|
||||
// Dismiss the alert screen several seconds after it appears
|
||||
if (now > alertingSinceMs + durationAlertMs) {
|
||||
alertingNoMessage = false;
|
||||
screen->endAlert();
|
||||
}
|
||||
}
|
||||
|
||||
// If: Button is pressed
|
||||
if (digitalRead(pin) == LOW) {
|
||||
// New press
|
||||
if (!held) {
|
||||
downSinceMs = now;
|
||||
}
|
||||
|
||||
// Existing press
|
||||
else {
|
||||
// Duration enough for long press
|
||||
// Long press not yet fired (prevent repeat firing while held)
|
||||
if (!longPressFired && now - downSinceMs > durationLongMs) {
|
||||
longPressFired = true;
|
||||
longPress();
|
||||
}
|
||||
}
|
||||
|
||||
// Record the change of state: button is down
|
||||
held = true;
|
||||
}
|
||||
|
||||
// If: Button is not pressed
|
||||
else {
|
||||
// Button newly released
|
||||
// Long press event didn't already fire
|
||||
if (held && !longPressFired) {
|
||||
// Duration enough for short press
|
||||
if (now - downSinceMs > durationShortMs) {
|
||||
shortPress();
|
||||
}
|
||||
}
|
||||
|
||||
// Record the change of state: button is up
|
||||
held = false;
|
||||
longPressFired = false; // Re-Arm: allow another long press
|
||||
}
|
||||
|
||||
// If thread's job is done, let it sleep
|
||||
if (!held && !alertingNoMessage) {
|
||||
Thread::canSleep = true;
|
||||
return OSThread::disable();
|
||||
}
|
||||
|
||||
// Run this method again is a few ms
|
||||
return durationShortMs;
|
||||
}
|
||||
|
||||
void ScanAndSelectInput::longPress()
|
||||
{
|
||||
// (If canned messages set)
|
||||
if (cannedMessageModule->hasMessages()) {
|
||||
// If module frame displayed already, send the current message
|
||||
if (cannedMessageModule->shouldDraw())
|
||||
raiseEvent(meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_SELECT);
|
||||
|
||||
// Otherwise, initial long press opens the module frame
|
||||
else
|
||||
raiseEvent(meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_DOWN);
|
||||
}
|
||||
|
||||
// (If canned messages not set) tell the user
|
||||
else
|
||||
alertNoMessage();
|
||||
}
|
||||
|
||||
void ScanAndSelectInput::shortPress()
|
||||
{
|
||||
// (If canned messages set) scroll to next message
|
||||
if (cannedMessageModule->hasMessages())
|
||||
raiseEvent(meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_DOWN);
|
||||
|
||||
// (If canned messages not yet set) tell the user
|
||||
else
|
||||
alertNoMessage();
|
||||
}
|
||||
|
||||
// Begin running runOnce at regular intervals
|
||||
// Called from pin change interrupt
|
||||
void ScanAndSelectInput::enableThread()
|
||||
{
|
||||
Thread::canSleep = false;
|
||||
OSThread::enabled = true;
|
||||
OSThread::setIntervalFromNow(0);
|
||||
}
|
||||
|
||||
// Inform user (screen) that no canned messages have been added
|
||||
// Automatically dismissed after several seconds
|
||||
void ScanAndSelectInput::alertNoMessage()
|
||||
{
|
||||
alertingNoMessage = true;
|
||||
alertingSinceMs = millis();
|
||||
|
||||
// Graphics code: the alert frame to show on screen
|
||||
screen->startAlert([](OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y) -> void {
|
||||
display->setTextAlignment(TEXT_ALIGN_CENTER_BOTH);
|
||||
display->setFont(FONT_SMALL);
|
||||
int16_t textX = display->getWidth() / 2;
|
||||
int16_t textY = display->getHeight() / 2;
|
||||
display->drawString(textX + x, textY + y, "No Canned Messages");
|
||||
});
|
||||
}
|
||||
|
||||
// Remove the canned message frame from screen
|
||||
// Used to dismiss the module frame when user button pressed
|
||||
// Returns true if the frame was previously displayed, and has now been closed
|
||||
// Return value consumed by Screen class when determining how to handle user button
|
||||
bool ScanAndSelectInput::dismissCannedMessageFrame()
|
||||
{
|
||||
if (cannedMessageModule->shouldDraw()) {
|
||||
raiseEvent(meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_CANCEL);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Feed input to the canned messages module
|
||||
void ScanAndSelectInput::raiseEvent(_meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar key)
|
||||
{
|
||||
InputEvent e;
|
||||
e.source = name;
|
||||
e.inputEvent = key;
|
||||
notifyObservers(&e);
|
||||
}
|
||||
|
||||
// Pin change interrupt
|
||||
void ScanAndSelectInput::handleChangeInterrupt()
|
||||
{
|
||||
// Because we need to detect both press and release (rising and falling edge), the interrupt itself can't determine the
|
||||
// action. Instead, we start up the thread and get it to read the button for us
|
||||
|
||||
// The instance we're referring to here is created in setupModules()
|
||||
scanAndSelectInput->enableThread();
|
||||
}
|
||||
|
||||
ScanAndSelectInput *scanAndSelectInput = nullptr; // Instantiated in setupModules method. Deleted if unused, or init() fails
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
A "single button" input method for Canned Messages
|
||||
|
||||
- Short press to cycle through messages
|
||||
- Long Press to send
|
||||
|
||||
To use:
|
||||
- set "allow input source" to "scanAndSelect"
|
||||
- set the single button's GPIO as either pin A, pin B, or pin Press
|
||||
|
||||
Originally designed to make use of "extra" built-in button on some boards.
|
||||
Non-intrusive; suitable for use as a default module config.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include "concurrency/OSThread.h"
|
||||
#include "main.h"
|
||||
|
||||
// Normally these input methods are protected by guarding in setupModules
|
||||
// In order to have the user button dismiss the canned message frame, this class lightly interacts with the Screen class
|
||||
#if HAS_SCREEN
|
||||
|
||||
class ScanAndSelectInput : public Observable<const InputEvent *>, public concurrency::OSThread
|
||||
{
|
||||
public:
|
||||
ScanAndSelectInput(); // No-op constructor, only initializes OSThread base class
|
||||
bool init(); // Attempt to setup class; true if success. Instance deleted if setup fails
|
||||
bool dismissCannedMessageFrame(); // Remove the canned message frame from screen. True if frame was open, and now closed.
|
||||
void alertNoMessage(); // Inform user (screen) that no canned messages have been added
|
||||
|
||||
protected:
|
||||
int32_t runOnce() override; // Runs at regular intervals, when enabled
|
||||
void enableThread(); // Begin running runOnce at regular intervals
|
||||
static void handleChangeInterrupt(); // Calls enableThread from pin change interrupt
|
||||
void shortPress(); // Code to run when short press fires
|
||||
void longPress(); // Code to run when long press fires
|
||||
void raiseEvent(_meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar key); // Feed input to canned message module
|
||||
|
||||
bool held = false; // Have we handled a change in button state?
|
||||
bool longPressFired = false; // Long press fires while button still held. This bool ensures the release is no-op
|
||||
uint32_t downSinceMs = 0; // Debouncing for short press, timing for long press
|
||||
uint8_t pin = -1; // Read from cannned message config during init
|
||||
|
||||
bool alertingNoMessage = false; // Is the "no canned messages" alert shown on screen?
|
||||
uint32_t alertingSinceMs = 0; // Used to dismiss the "no canned message" alert several seconds
|
||||
};
|
||||
|
||||
extern ScanAndSelectInput *scanAndSelectInput; // Instantiated in setupModules method. Deleted if unused, or init() fails
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,170 @@
|
||||
#include "SerialKeyboard.h"
|
||||
#include "configuration.h"
|
||||
|
||||
#ifdef INPUTBROKER_SERIAL_TYPE
|
||||
#define CANNED_MESSAGE_MODULE_ENABLE 1 // in case it's not set in the variant file
|
||||
|
||||
#if INPUTBROKER_SERIAL_TYPE == 1 // It's a Chatter
|
||||
// 3 SHIFT level (lower case, upper case, numbers), up to 4 repeated presses, button number
|
||||
unsigned char KeyMap[3][4][10] = {{{'.', 'a', 'd', 'g', 'j', 'm', 'p', 't', 'w', ' '},
|
||||
{',', 'b', 'e', 'h', 'k', 'n', 'q', 'u', 'x', ' '},
|
||||
{'?', 'c', 'f', 'i', 'l', 'o', 'r', 'v', 'y', ' '},
|
||||
{'1', '2', '3', '4', '5', '6', 's', '8', 'z', ' '}}, // low case
|
||||
{{'!', 'A', 'D', 'G', 'J', 'M', 'P', 'T', 'W', ' '},
|
||||
{'+', 'B', 'E', 'H', 'K', 'N', 'Q', 'U', 'X', ' '},
|
||||
{'-', 'C', 'F', 'I', 'L', 'O', 'R', 'V', 'Y', ' '},
|
||||
{'1', '2', '3', '4', '5', '6', 'S', '8', 'Z', ' '}}, // upper case
|
||||
{{'1', '2', '3', '4', '5', '6', '7', '8', '9', '0'},
|
||||
{'1', '2', '3', '4', '5', '6', '7', '8', '9', '0'},
|
||||
{'1', '2', '3', '4', '5', '6', '7', '8', '9', '0'},
|
||||
{'1', '2', '3', '4', '5', '6', '7', '8', '9', '0'}}}; // numbers
|
||||
|
||||
#endif
|
||||
|
||||
SerialKeyboard::SerialKeyboard(const char *name) : concurrency::OSThread(name)
|
||||
{
|
||||
this->_originName = name;
|
||||
}
|
||||
|
||||
void SerialKeyboard::erase()
|
||||
{
|
||||
InputEvent e;
|
||||
e.inputEvent = meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_BACK;
|
||||
e.kbchar = 0x08;
|
||||
e.source = this->_originName;
|
||||
this->notifyObservers(&e);
|
||||
}
|
||||
|
||||
int32_t SerialKeyboard::runOnce()
|
||||
{
|
||||
if (!INPUTBROKER_SERIAL_TYPE) {
|
||||
// Input device is not requested.
|
||||
return disable();
|
||||
}
|
||||
|
||||
if (firstTime) {
|
||||
// This is the first time the OSThread library has called this function, so do port setup
|
||||
firstTime = 0;
|
||||
pinMode(KB_LOAD, OUTPUT);
|
||||
pinMode(KB_CLK, OUTPUT);
|
||||
pinMode(KB_DATA, INPUT);
|
||||
digitalWrite(KB_LOAD, HIGH);
|
||||
digitalWrite(KB_CLK, LOW);
|
||||
prevKeys = 0b1111111111111111;
|
||||
LOG_DEBUG("Serial Keyboard setup\n");
|
||||
}
|
||||
|
||||
if (INPUTBROKER_SERIAL_TYPE == 1) { // Chatter V1.0 & V2.0 keypads
|
||||
// scan for keypresses
|
||||
// Write pulse to load pin
|
||||
digitalWrite(KB_LOAD, LOW);
|
||||
delayMicroseconds(5);
|
||||
digitalWrite(KB_LOAD, HIGH);
|
||||
delayMicroseconds(5);
|
||||
|
||||
// Get data from 74HC165
|
||||
byte shiftRegister1 = shiftIn(KB_DATA, KB_CLK, LSBFIRST);
|
||||
byte shiftRegister2 = shiftIn(KB_DATA, KB_CLK, LSBFIRST);
|
||||
|
||||
keys = (shiftRegister1 << 8) + shiftRegister2;
|
||||
|
||||
// Print to serial monitor
|
||||
// Serial.print (shiftRegister1, BIN);
|
||||
// Serial.print ("X");
|
||||
// Serial.println (shiftRegister2, BIN);
|
||||
|
||||
if (millis() - lastPressTime > 500) {
|
||||
quickPress = 0;
|
||||
}
|
||||
|
||||
if (keys < prevKeys) { // a new key has been pressed (and not released), doesn't works for multiple presses at once but
|
||||
// shouldn't be a limitation
|
||||
InputEvent e;
|
||||
e.inputEvent = meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_NONE;
|
||||
e.source = this->_originName;
|
||||
// SELECT OR SEND OR CANCEL EVENT
|
||||
if (!(shiftRegister2 & (1 << 3))) {
|
||||
e.inputEvent = meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_UP;
|
||||
} else if (!(shiftRegister2 & (1 << 2))) {
|
||||
e.inputEvent = meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_RIGHT;
|
||||
e.kbchar = INPUT_BROKER_MSG_RIGHT;
|
||||
} else if (!(shiftRegister2 & (1 << 1))) {
|
||||
e.inputEvent = meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_SELECT;
|
||||
} else if (!(shiftRegister2 & (1 << 0))) {
|
||||
e.inputEvent = meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_CANCEL;
|
||||
}
|
||||
|
||||
// TEXT INPUT EVENT
|
||||
else if (!(shiftRegister1 & (1 << 4))) {
|
||||
keyPressed = 0;
|
||||
} else if (!(shiftRegister1 & (1 << 3))) {
|
||||
keyPressed = 1;
|
||||
} else if (!(shiftRegister2 & (1 << 4))) {
|
||||
keyPressed = 2;
|
||||
} else if (!(shiftRegister1 & (1 << 5))) {
|
||||
keyPressed = 3;
|
||||
} else if (!(shiftRegister1 & (1 << 2))) {
|
||||
keyPressed = 4;
|
||||
} else if (!(shiftRegister2 & (1 << 5))) {
|
||||
keyPressed = 5;
|
||||
} else if (!(shiftRegister1 & (1 << 6))) {
|
||||
keyPressed = 6;
|
||||
} else if (!(shiftRegister1 & (1 << 1))) {
|
||||
keyPressed = 7;
|
||||
} else if (!(shiftRegister2 & (1 << 6))) {
|
||||
keyPressed = 8;
|
||||
} else if (!(shiftRegister1 & (1 << 0))) {
|
||||
keyPressed = 9;
|
||||
}
|
||||
// BACKSPACE or TAB
|
||||
else if (!(shiftRegister1 & (1 << 7))) {
|
||||
if (shift == 0 || shift == 2) { // BACKSPACE
|
||||
e.inputEvent = meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_BACK;
|
||||
e.kbchar = 0x08;
|
||||
} else { // shift = 1 => TAB
|
||||
e.inputEvent = ANYKEY;
|
||||
e.kbchar = 0x09;
|
||||
}
|
||||
}
|
||||
// SHIFT
|
||||
else if (!(shiftRegister2 & (1 << 7))) {
|
||||
keyPressed = 10;
|
||||
}
|
||||
|
||||
if (keyPressed < 11) {
|
||||
if (keyPressed == lastKeyPressed && millis() - lastPressTime < 500) {
|
||||
quickPress += 1;
|
||||
if (quickPress > 3) {
|
||||
quickPress = 0;
|
||||
}
|
||||
}
|
||||
if (keyPressed != lastKeyPressed) {
|
||||
quickPress = 0;
|
||||
}
|
||||
if (keyPressed < 10) { // if it's a letter
|
||||
if (keyPressed == lastKeyPressed && millis() - lastPressTime < 500) {
|
||||
erase();
|
||||
}
|
||||
e.inputEvent = ANYKEY;
|
||||
e.kbchar = char(KeyMap[shift][quickPress][keyPressed]);
|
||||
} else { // then it's shift
|
||||
shift += 1;
|
||||
if (shift > 2) {
|
||||
shift = 0;
|
||||
}
|
||||
}
|
||||
lastPressTime = millis();
|
||||
lastKeyPressed = keyPressed;
|
||||
keyPressed = 13;
|
||||
}
|
||||
|
||||
if (e.inputEvent != meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_NONE) {
|
||||
this->notifyObservers(&e);
|
||||
}
|
||||
}
|
||||
prevKeys = keys;
|
||||
}
|
||||
return 50;
|
||||
}
|
||||
|
||||
#endif // INPUTBROKER_SERIAL_TYPE
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user