mirror of
https://github.com/meshtastic/firmware.git
synced 2025-12-15 15:22:34 +00:00
Compare commits
265 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3d9cc8a056 | ||
|
|
ff885ef215 | ||
|
|
eb4286b560 | ||
|
|
9c90de0f6f | ||
|
|
d7a1cef046 | ||
|
|
6a359e2124 | ||
|
|
ca75dcd64d | ||
|
|
aba05ba5ce | ||
|
|
99882a675b | ||
|
|
9c9347df23 | ||
|
|
b66856c53f | ||
|
|
285069703c | ||
|
|
d91ab5480f | ||
|
|
e3b74ece74 | ||
|
|
66557241f3 | ||
|
|
3c09c3e520 | ||
|
|
781077e799 | ||
|
|
22946b5e51 | ||
|
|
c0307cbcb0 | ||
|
|
6b568ab2fb | ||
|
|
67bad9a689 | ||
|
|
559a790286 | ||
|
|
08e5bd728b | ||
|
|
0cfeeba2e2 | ||
|
|
5007624ba5 | ||
|
|
bba4677915 | ||
|
|
ac969cdb26 | ||
|
|
1c3eff0ee5 | ||
|
|
cba9546a4d | ||
|
|
ceae60cf13 | ||
|
|
3de1607cea | ||
|
|
029b2f3139 | ||
|
|
ab6c97bfef | ||
|
|
a61b15e861 | ||
|
|
8c7aa07c70 | ||
|
|
6a402b13fa | ||
|
|
c30b570e16 | ||
|
|
9b25818a50 | ||
|
|
5311e44660 | ||
|
|
55dafcbecb | ||
|
|
178958c165 | ||
|
|
d7cf7e2eb4 | ||
|
|
fce8c16d52 | ||
|
|
b690868bb1 | ||
|
|
dec88a368b | ||
|
|
17394d8c1c | ||
|
|
a7da7cd32e | ||
|
|
f37dc9c776 | ||
|
|
d6658dbb2e | ||
|
|
05531b2684 | ||
|
|
8b1fb39ce1 | ||
|
|
da46d4ca0e | ||
|
|
047141eb34 | ||
|
|
cb1053850d | ||
|
|
7652331e8c | ||
|
|
12bf3795ea | ||
|
|
7f45184d90 | ||
|
|
829763af2c | ||
|
|
75806ee666 | ||
|
|
91ec29db03 | ||
|
|
d191b12801 | ||
|
|
e0d6456618 | ||
|
|
70eda2ee06 | ||
|
|
7c4eb3eddd | ||
|
|
661a75d796 | ||
|
|
b617010a46 | ||
|
|
5a70c45a3e | ||
|
|
cb2b36811a | ||
|
|
92edcb97ed | ||
|
|
7f1ec15cab | ||
|
|
1aa7451866 | ||
|
|
d5c46dc114 | ||
|
|
5bab16636d | ||
|
|
204f2c1a68 | ||
|
|
a5b7501a4e | ||
|
|
b0e2c81666 | ||
|
|
00ca351169 | ||
|
|
0415a3c369 | ||
|
|
4eb27b637d | ||
|
|
107b56a346 | ||
|
|
abdc4dfae8 | ||
|
|
014eea2f56 | ||
|
|
9b4ca95660 | ||
|
|
78ff9a8116 | ||
|
|
66b147fb31 | ||
|
|
c5df1bc885 | ||
|
|
00cf3a768e | ||
|
|
b6a3deb341 | ||
|
|
531f488fe8 | ||
|
|
d674aaaa29 | ||
|
|
7f6dc104f0 | ||
|
|
102085808f | ||
|
|
2645730329 | ||
|
|
6aa28f55dd | ||
|
|
1e86365167 | ||
|
|
62c20f8ab9 | ||
|
|
7706f65921 | ||
|
|
68490336b8 | ||
|
|
20ac8d71fd | ||
|
|
ec082b7c9a | ||
|
|
ece75d1d7f | ||
|
|
9e10ce487c | ||
|
|
d248c6be4b | ||
|
|
39b0a89821 | ||
|
|
d9f43d3e2f | ||
|
|
94f03bee01 | ||
|
|
08c77caaa9 | ||
|
|
cfedc97cd0 | ||
|
|
cfad226b2b | ||
|
|
859642d2e4 | ||
|
|
8316419a01 | ||
|
|
96f5069742 | ||
|
|
e433249bb1 | ||
|
|
2a6df797ca | ||
|
|
28aa48c8d2 | ||
|
|
582f77e4ec | ||
|
|
5700cf96d5 | ||
|
|
769a98f1f4 | ||
|
|
4ee35a0612 | ||
|
|
64f6741f82 | ||
|
|
6ad5abcab2 | ||
|
|
5480f10184 | ||
|
|
41dfbdd331 | ||
|
|
903268c52a | ||
|
|
b60c630922 | ||
|
|
95588b420c | ||
|
|
b141ec2e35 | ||
|
|
3d0c611896 | ||
|
|
6cb92143ec | ||
|
|
f919eb6a64 | ||
|
|
4147786b12 | ||
|
|
3400bcde85 | ||
|
|
e5fac4b78d | ||
|
|
4d20865c67 | ||
|
|
8b4cf91f1f | ||
|
|
6a6a10fb9b | ||
|
|
44749470a4 | ||
|
|
8fe714d8b1 | ||
|
|
22137ff1bd | ||
|
|
da3b6d1958 | ||
|
|
c7213fb710 | ||
|
|
637960edde | ||
|
|
be7e4fea6a | ||
|
|
d9209ffaea | ||
|
|
9fb94796c8 | ||
|
|
f060f7faad | ||
|
|
55673fcd66 | ||
|
|
2ff94cb11d | ||
|
|
e46bebc06f | ||
|
|
80e8b4adcc | ||
|
|
e9be03b76c | ||
|
|
98dfecdb79 | ||
|
|
8ba8278fb5 | ||
|
|
51267379ab | ||
|
|
e2cf2ba4f2 | ||
|
|
4550cce639 | ||
|
|
f2f17c81d4 | ||
|
|
7c0d13f00a | ||
|
|
f78f3232e2 | ||
|
|
a687aa8e75 | ||
|
|
ed6b89b3b1 | ||
|
|
9d3ca0d0f9 | ||
|
|
05a0405709 | ||
|
|
efd8b70089 | ||
|
|
e12c057c31 | ||
|
|
0b5b18653a | ||
|
|
dc54e7331f | ||
|
|
6fbf6b2986 | ||
|
|
feb9992d7d | ||
|
|
fb4ac82d45 | ||
|
|
acbe8c159b | ||
|
|
63474dd952 | ||
|
|
cc35ed7782 | ||
|
|
d9fd227862 | ||
|
|
1415f2bed7 | ||
|
|
5b07d454b1 | ||
|
|
0c04ba4776 | ||
|
|
b00a936f41 | ||
|
|
5b11c1ca86 | ||
|
|
7802d00031 | ||
|
|
40a15248e8 | ||
|
|
9ba9e82706 | ||
|
|
ea6c33f3d2 | ||
|
|
82e5e1858a | ||
|
|
80b14c0a6f | ||
|
|
6954d0d5f4 | ||
|
|
7e53731fe6 | ||
|
|
c7290e6ccc | ||
|
|
284317cb25 | ||
|
|
311d1a56b4 | ||
|
|
ed589727d6 | ||
|
|
62c9bad183 | ||
|
|
616da8228e | ||
|
|
3a62453b8b | ||
|
|
c3f7829255 | ||
|
|
37d9fb2dad | ||
|
|
4388e72dec | ||
|
|
9803141fe7 | ||
|
|
1f0e9cc1c3 | ||
|
|
92b30ebec6 | ||
|
|
ccadb6a43d | ||
|
|
6f7f540c79 | ||
|
|
d5b8038457 | ||
|
|
0a6059ba13 | ||
|
|
aba5b01fa0 | ||
|
|
09f4943869 | ||
|
|
29c8543f87 | ||
|
|
7bd4940ed8 | ||
|
|
d5116935b5 | ||
|
|
0d320fe29b | ||
|
|
4159461a62 | ||
|
|
f4bd39e3fa | ||
|
|
fbc36a2cfd | ||
|
|
e93ba73adb | ||
|
|
03301f093d | ||
|
|
55a5fa6fb5 | ||
|
|
4d04d10135 | ||
|
|
cda423acab | ||
|
|
0f92678c3b | ||
|
|
8d122f36e3 | ||
|
|
439cdfbb32 | ||
|
|
0a6ab31e10 | ||
|
|
0b6486256d | ||
|
|
da12b93f82 | ||
|
|
6dec6af5dc | ||
|
|
11444621ae | ||
|
|
98f1b3296c | ||
|
|
26c43e7091 | ||
|
|
9b9447858a | ||
|
|
3151cfb064 | ||
|
|
c327fee986 | ||
|
|
a4f53270e8 | ||
|
|
a7456a1126 | ||
|
|
8381512ce4 | ||
|
|
57d968cdcd | ||
|
|
20a669029b | ||
|
|
f2e6c6de58 | ||
|
|
8fa44c3590 | ||
|
|
f5b7c33d4e | ||
|
|
be8e663d39 | ||
|
|
0d4a9748e3 | ||
|
|
bd477f0fb2 | ||
|
|
5317895a5e | ||
|
|
542b8b26ce | ||
|
|
aaca854620 | ||
|
|
64da384fc1 | ||
|
|
a595fc4642 | ||
|
|
ac135be8cd | ||
|
|
ac2d3e2ae0 | ||
|
|
33946af39f | ||
|
|
5ea59a1c4d | ||
|
|
8bafd87b76 | ||
|
|
6b40e9a5e0 | ||
|
|
a2f6fd9298 | ||
|
|
bc604fc9ba | ||
|
|
9baaa13897 | ||
|
|
65e53be8b0 | ||
|
|
7f5283e95d | ||
|
|
ae4ab48ddc | ||
|
|
afccf1da02 | ||
|
|
fc07c7c01f | ||
|
|
aeb906414f | ||
|
|
a6c6b45576 | ||
|
|
91b4cadb1b | ||
|
|
a23f327461 |
38
.github/ISSUE_TEMPLATE/bug-report-or-feature-proposal.md
vendored
Normal file
38
.github/ISSUE_TEMPLATE/bug-report-or-feature-proposal.md
vendored
Normal file
@@ -0,0 +1,38 @@
|
||||
---
|
||||
name: Bug report or feature proposal
|
||||
about: Create a report to help us improve
|
||||
title: ''
|
||||
labels: ''
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
Please - if you just have a question (i.e. not a bug report or a feature proposal), post in our [forum](https://meshtastic.discourse.group/) instead.
|
||||
|
||||
**Describe the bug**
|
||||
A clear and concise description of what the bug is.
|
||||
|
||||
**To Reproduce**
|
||||
Steps to reproduce the behavior:
|
||||
1. Go to '...'
|
||||
2. Click on '....'
|
||||
3. Scroll down to '....'
|
||||
4. See error
|
||||
|
||||
**Expected behavior**
|
||||
A clear and concise description of what you expected to happen.
|
||||
|
||||
**Screenshots**
|
||||
If applicable, add screenshots to help explain your problem.
|
||||
|
||||
**Device info:**
|
||||
- Device model: [e.g. TBEAM]
|
||||
- Software Version [e.g. 0.7.8]
|
||||
|
||||
**Smartphone information (if relevant):**
|
||||
- Device: [e.g. iPhone6]
|
||||
- OS: [e.g. iOS8.1]
|
||||
- App Version [e.g. 0.7.2]
|
||||
|
||||
**Additional context**
|
||||
Add any other context about the problem here.
|
||||
13
.github/pull_request_template.md
vendored
Normal file
13
.github/pull_request_template.md
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
## 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
|
||||
is appreciated." This will allow other devs to potentially save you time by not accidentially duplicating work etc...
|
||||
- Please do not check in files that don't have real changes
|
||||
- Please do not reformat lines that you didn't have to change the code on
|
||||
- We recommend using the [Visual Studio Code](https://platformio.org/install/ide?install=vscode) editor,
|
||||
because automatically follows our indentation rules and it's auto reformatting will not cause spurious changes to lines.
|
||||
- If your PR fixes a bug, mention "fixes #bugnum" somewhere in your pull request description.
|
||||
- If your other co-developers have comments on your PR please tweak as needed.
|
||||
7
.github/workflows/main.yml
vendored
7
.github/workflows/main.yml
vendored
@@ -9,6 +9,8 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@master
|
||||
- name: Checkout submodules
|
||||
uses: textbook/git-checkout-submodule-action@master
|
||||
- name: Setup Python
|
||||
uses: actions/setup-python@master
|
||||
with:
|
||||
@@ -17,5 +19,8 @@ jobs:
|
||||
run: |
|
||||
python -m pip install --upgrade pip
|
||||
pip install -U platformio
|
||||
- name: Install extra python tools
|
||||
run: |
|
||||
pip install -U adafruit-nrfutil
|
||||
- name: Build
|
||||
run: platformio run
|
||||
run: platformio run -e tbeam -e heltec -e lora-relay-v1
|
||||
|
||||
13
.gitignore
vendored
13
.gitignore
vendored
@@ -7,4 +7,15 @@ main/credentials.h
|
||||
!.vscode/settings.json
|
||||
!.vscode/tasks.json
|
||||
!.vscode/extensions.json
|
||||
*.code-workspace
|
||||
*.code-workspace
|
||||
|
||||
.DS_Store
|
||||
Thumbs.db
|
||||
.autotools
|
||||
.built
|
||||
.context
|
||||
.cproject
|
||||
.idea/*
|
||||
.vagrant
|
||||
|
||||
flash.uf2
|
||||
|
||||
6
.vscode/settings.json
vendored
6
.vscode/settings.json
vendored
@@ -47,7 +47,8 @@
|
||||
"memory_resource": "cpp",
|
||||
"optional": "cpp",
|
||||
"string_view": "cpp",
|
||||
"cassert": "cpp"
|
||||
"cassert": "cpp",
|
||||
"iterator": "cpp"
|
||||
},
|
||||
"cSpell.words": [
|
||||
"Blox",
|
||||
@@ -61,5 +62,6 @@
|
||||
"ocrypto",
|
||||
"protobufs",
|
||||
"wifi"
|
||||
]
|
||||
],
|
||||
"C_Cpp.dimInactiveRegions": true
|
||||
}
|
||||
17
.vscode/tasks.json
vendored
Normal file
17
.vscode/tasks.json
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"version": "2.0.0",
|
||||
"tasks": [
|
||||
{
|
||||
"type": "PlatformIO",
|
||||
"task": "Build",
|
||||
"problemMatcher": [
|
||||
"$platformio"
|
||||
],
|
||||
"group": {
|
||||
"kind": "build",
|
||||
"isDefault": true
|
||||
},
|
||||
"label": "PlatformIO: Build"
|
||||
}
|
||||
]
|
||||
}
|
||||
17
README.md
17
README.md
@@ -22,16 +22,20 @@ This software is 100% open source and developed by a group of hobbyist experimen
|
||||
|
||||
We currently support three models of radios.
|
||||
|
||||
- TTGO T-Beam
|
||||
|
||||
- [T-Beam V1.0 w/ NEO-6M - special Meshtastic version](https://www.aliexpress.com/item/4001178678568.html) (Includes built-in OLED display and they have **preinstalled** the meshtastic software)
|
||||
- [T-Beam V1.0 w/ NEO-M8N](https://www.aliexpress.com/item/33047631119.html) (slightly better GPS)
|
||||
- TTGO T-Beam (usually the recommended choice)
|
||||
- [T-Beam V1.1 w/ NEO-6M - special Meshtastic version](https://www.aliexpress.com/item/4001178678568.html) (Includes built-in OLED display and they have **preinstalled** the meshtastic software)
|
||||
- [T-Beam V1.1 w/ NEO-M8N](https://www.aliexpress.com/item/33047631119.html) (slightly better GPS)
|
||||
- [T-Beam V0.7 w/ NEO-6M](https://www.aliexpress.com/item/4000574335430.html) (will work but **you must use the tbeam0.7 firmware ** - but the T-Beam V1.0 or later are better!)
|
||||
- board labels "TTGO T22_V07 20180711"
|
||||
- 3D printable cases
|
||||
- [T-Beam V0](https://www.thingiverse.com/thing:3773717)
|
||||
- [T-Beam V1](https://www.thingiverse.com/thing:3830711)
|
||||
- Laser-cut cases
|
||||
- [T-Beam V1](https://www.thingiverse.com/thing:4552771)
|
||||
|
||||
- [TTGO LORA32](https://www.aliexpress.com/item/4000211331316.html) - No GPS
|
||||
|
||||
- version 2.1
|
||||
- board labels "TTGO T3_V1.6 20180606"
|
||||
- 3D printable case
|
||||
- [TTGO LORA32 v1](https://www.thingiverse.com/thing:3385109)
|
||||
|
||||
@@ -40,9 +44,10 @@ We currently support three models of radios.
|
||||
|
||||
**Make sure to get the frequency for your country**
|
||||
|
||||
- US/JP/AU/NZ - 915MHz
|
||||
- US/JP/AU/NZ/CA - 915MHz
|
||||
- CN - 470MHz
|
||||
- EU - 868MHz, 433MHz
|
||||
- full list of LoRa frequencies per region is available [here](https://www.thethingsnetwork.org/docs/lorawan/frequencies-by-country.html)
|
||||
|
||||
Getting a version that includes a screen is optional, but highly recommended.
|
||||
|
||||
|
||||
@@ -8,7 +8,11 @@ COUNTRIES="US EU433 EU865 CN JP"
|
||||
#COUNTRIES=US
|
||||
#COUNTRIES=CN
|
||||
|
||||
BOARDS="ttgo-lora32-v2 ttgo-lora32-v1 tbeam heltec"
|
||||
BOARDS_ESP32="tlora-v2 tlora-v1 tlora-v2-1-1.6 tbeam heltec tbeam0.7"
|
||||
|
||||
# FIXME note nrf52840dk build is for some reason only generating a BIN file but not a HEX file nrf52840dk-geeksville is fine
|
||||
BOARDS_NRF52="lora-relay-v1"
|
||||
BOARDS="$BOARDS_ESP32 $BOARDS_NRF52"
|
||||
#BOARDS=tbeam
|
||||
|
||||
OUTDIR=release/latest
|
||||
@@ -23,20 +27,17 @@ rm -f $OUTDIR/bins/*
|
||||
|
||||
# build the named environment and copy the bins to the release directory
|
||||
function do_build {
|
||||
ENV_NAME=$1
|
||||
echo "Building for $ENV_NAME with $PLATFORMIO_BUILD_FLAGS"
|
||||
SRCBIN=.pio/build/$ENV_NAME/firmware.bin
|
||||
SRCELF=.pio/build/$ENV_NAME/firmware.elf
|
||||
rm -f $SRCBIN
|
||||
echo "Building for $BOARD with $PLATFORMIO_BUILD_FLAGS"
|
||||
rm -f .pio/build/$BOARD/firmware.*
|
||||
|
||||
# The shell vars the build tool expects to find
|
||||
export HW_VERSION="1.0-$COUNTRY"
|
||||
export APP_VERSION=$VERSION
|
||||
export COUNTRY
|
||||
|
||||
pio run --jobs 4 --environment $ENV_NAME # -v
|
||||
cp $SRCBIN $OUTDIR/bins/firmware-$ENV_NAME-$COUNTRY-$VERSION.bin
|
||||
cp $SRCELF $OUTDIR/elfs/firmware-$ENV_NAME-$COUNTRY-$VERSION.elf
|
||||
pio run --jobs 4 --environment $BOARD # -v
|
||||
SRCELF=.pio/build/$BOARD/firmware.elf
|
||||
cp $SRCELF $OUTDIR/elfs/firmware-$BOARD-$COUNTRY-$VERSION.elf
|
||||
}
|
||||
|
||||
# Make sure our submodules are current
|
||||
@@ -49,6 +50,18 @@ for COUNTRY in $COUNTRIES; do
|
||||
for BOARD in $BOARDS; do
|
||||
do_build $BOARD
|
||||
done
|
||||
|
||||
echo "Copying ESP32 bin files"
|
||||
for BOARD in $BOARDS_ESP32; do
|
||||
SRCBIN=.pio/build/$BOARD/firmware.bin
|
||||
cp $SRCBIN $OUTDIR/bins/firmware-$BOARD-$COUNTRY-$VERSION.bin
|
||||
done
|
||||
|
||||
echo "Generating NRF52 uf2 files"
|
||||
for BOARD in $BOARDS_NRF52; do
|
||||
SRCHEX=.pio/build/$BOARD/firmware.hex
|
||||
bin/uf2conv.py $SRCHEX -c -o $OUTDIR/bins/firmware-$BOARD-$COUNTRY-$VERSION.uf2 -f 0xADA52840
|
||||
done
|
||||
done
|
||||
|
||||
# keep the bins in archive also
|
||||
|
||||
@@ -1,11 +1,45 @@
|
||||
#!/bin/bash
|
||||
#!/bin/sh
|
||||
|
||||
set -e
|
||||
# Usage info
|
||||
show_help() {
|
||||
cat << EOF
|
||||
Usage: ${0##*/} [-h] [-p ESPTOOL_PORT] [-f FILENAME]
|
||||
Flash image file to device, but first erasing and writing system information"
|
||||
|
||||
FILENAME=$1
|
||||
-h Display this help and exit
|
||||
-p ESPTOOL_PORT Set the environment variable for ESPTOOL_PORT. If not set, ESPTOOL iterates all ports (Dangerrous).
|
||||
-f FILENAME The .bin file to flash. Custom to your device type and region.
|
||||
EOF
|
||||
}
|
||||
|
||||
echo "Trying to flash $FILENAME, but first erasing and writing system information"
|
||||
esptool.py --baud 921600 erase_flash
|
||||
esptool.py --baud 921600 write_flash 0x1000 system-info.bin
|
||||
esptool.py --baud 921600 write_flash 0x10000 $FILENAME
|
||||
|
||||
while getopts ":h:p:f:" opt; do
|
||||
case "${opt}" in
|
||||
h)
|
||||
show_help
|
||||
exit 0
|
||||
;;
|
||||
p) export ESPTOOL_PORT=${OPTARG}
|
||||
;;
|
||||
f) FILENAME=${OPTARG}
|
||||
;;
|
||||
*)
|
||||
echo "Invalid flag."
|
||||
show_help >&2
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
done
|
||||
shift "$((OPTIND-1))"
|
||||
|
||||
if [ -f "${FILENAME}" ]; then
|
||||
echo "Trying to flash ${FILENAME}, but first erasing and writing system information"
|
||||
esptool.py --baud 921600 erase_flash
|
||||
esptool.py --baud 921600 write_flash 0x1000 system-info.bin
|
||||
esptool.py --baud 921600 write_flash 0x10000 ${FILENAME}
|
||||
else
|
||||
echo "Invalid file: ${FILENAME}"
|
||||
show_help
|
||||
fi
|
||||
|
||||
exit 0
|
||||
|
||||
@@ -1,8 +1,43 @@
|
||||
#!/bin/bash
|
||||
#!/bin/sh
|
||||
|
||||
set -e
|
||||
# Usage info
|
||||
show_help() {
|
||||
cat << EOF
|
||||
Usage: ${0##*/} [-h] [-p ESPTOOL_PORT] -f FILENAME
|
||||
Flash image file to device, leave existing system intact."
|
||||
|
||||
FILENAME=$1
|
||||
-h Display this help and exit
|
||||
-p ESPTOOL_PORT Set the environment variable for ESPTOOL_PORT. If not set, ESPTOOL iterates all ports (Dangerrous).
|
||||
-f FILENAME The .bin file to flash. Custom to your device type and region.
|
||||
EOF
|
||||
}
|
||||
|
||||
echo "Trying to update $FILENAME"
|
||||
esptool.py --baud 921600 write_flash 0x10000 $FILENAME
|
||||
|
||||
while getopts ":h:p:f:" opt; do
|
||||
case "${opt}" in
|
||||
h)
|
||||
show_help
|
||||
exit 0
|
||||
;;
|
||||
p) export ESPTOOL_PORT=${OPTARG}
|
||||
;;
|
||||
f) FILENAME=${OPTARG}
|
||||
;;
|
||||
*)
|
||||
echo "Invalid flag."
|
||||
show_help >&2
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
done
|
||||
shift "$((OPTIND-1))"
|
||||
|
||||
if [ -f "${FILENAME}" ]; then
|
||||
echo "Trying to flash update ${FILENAME}."
|
||||
esptool.py --baud 921600 write_flash 0x10000 ${FILENAME}
|
||||
else
|
||||
echo "Invalid file: ${FILENAME}"
|
||||
show_help
|
||||
fi
|
||||
|
||||
exit 0
|
||||
|
||||
3
bin/nrf52840-gdbserver.sh
Executable file
3
bin/nrf52840-gdbserver.sh
Executable file
@@ -0,0 +1,3 @@
|
||||
|
||||
|
||||
JLinkGDBServerCLExe -if SWD -select USB -port 2331 -device NRF52832_XXAA
|
||||
314
bin/uf2conv.py
Executable file
314
bin/uf2conv.py
Executable file
@@ -0,0 +1,314 @@
|
||||
#!/usr/bin/env python3
|
||||
import sys
|
||||
import struct
|
||||
import subprocess
|
||||
import re
|
||||
import os
|
||||
import os.path
|
||||
import argparse
|
||||
|
||||
|
||||
UF2_MAGIC_START0 = 0x0A324655 # "UF2\n"
|
||||
UF2_MAGIC_START1 = 0x9E5D5157 # Randomly selected
|
||||
UF2_MAGIC_END = 0x0AB16F30 # Ditto
|
||||
|
||||
families = {
|
||||
'SAMD21': 0x68ed2b88,
|
||||
'SAML21': 0x1851780a,
|
||||
'SAMD51': 0x55114460,
|
||||
'NRF52': 0x1b57745f,
|
||||
'STM32F0': 0x647824b6,
|
||||
'STM32F1': 0x5ee21072,
|
||||
'STM32F2': 0x5d1a0a2e,
|
||||
'STM32F3': 0x6b846188,
|
||||
'STM32F4': 0x57755a57,
|
||||
'STM32F7': 0x53b80f00,
|
||||
'STM32G0': 0x300f5633,
|
||||
'STM32G4': 0x4c71240a,
|
||||
'STM32H7': 0x6db66082,
|
||||
'STM32L0': 0x202e3a91,
|
||||
'STM32L1': 0x1e1f432d,
|
||||
'STM32L4': 0x00ff6919,
|
||||
'STM32L5': 0x04240bdf,
|
||||
'STM32WB': 0x70d16653,
|
||||
'STM32WL': 0x21460ff0,
|
||||
'ATMEGA32': 0x16573617,
|
||||
'MIMXRT10XX': 0x4FB2D5BD
|
||||
}
|
||||
|
||||
INFO_FILE = "/INFO_UF2.TXT"
|
||||
|
||||
appstartaddr = 0x2000
|
||||
familyid = 0x0
|
||||
|
||||
|
||||
def is_uf2(buf):
|
||||
w = struct.unpack("<II", buf[0:8])
|
||||
return w[0] == UF2_MAGIC_START0 and w[1] == UF2_MAGIC_START1
|
||||
|
||||
def is_hex(buf):
|
||||
try:
|
||||
w = buf[0:30].decode("utf-8")
|
||||
except UnicodeDecodeError:
|
||||
return False
|
||||
if w[0] == ':' and re.match(b"^[:0-9a-fA-F\r\n]+$", buf):
|
||||
return True
|
||||
return False
|
||||
|
||||
def convert_from_uf2(buf):
|
||||
global appstartaddr
|
||||
numblocks = len(buf) // 512
|
||||
curraddr = None
|
||||
outp = b""
|
||||
for blockno in range(numblocks):
|
||||
ptr = blockno * 512
|
||||
block = buf[ptr:ptr + 512]
|
||||
hd = struct.unpack(b"<IIIIIIII", block[0:32])
|
||||
if hd[0] != UF2_MAGIC_START0 or hd[1] != UF2_MAGIC_START1:
|
||||
print("Skipping block at " + ptr + "; bad magic")
|
||||
continue
|
||||
if hd[2] & 1:
|
||||
# NO-flash flag set; skip block
|
||||
continue
|
||||
datalen = hd[4]
|
||||
if datalen > 476:
|
||||
assert False, "Invalid UF2 data size at " + ptr
|
||||
newaddr = hd[3]
|
||||
if curraddr == None:
|
||||
appstartaddr = newaddr
|
||||
curraddr = newaddr
|
||||
padding = newaddr - curraddr
|
||||
if padding < 0:
|
||||
assert False, "Block out of order at " + ptr
|
||||
if padding > 10*1024*1024:
|
||||
assert False, "More than 10M of padding needed at " + ptr
|
||||
if padding % 4 != 0:
|
||||
assert False, "Non-word padding size at " + ptr
|
||||
while padding > 0:
|
||||
padding -= 4
|
||||
outp += b"\x00\x00\x00\x00"
|
||||
outp += block[32 : 32 + datalen]
|
||||
curraddr = newaddr + datalen
|
||||
return outp
|
||||
|
||||
def convert_to_carray(file_content):
|
||||
outp = "const unsigned char bindata[] __attribute__((aligned(16))) = {"
|
||||
for i in range(len(file_content)):
|
||||
if i % 16 == 0:
|
||||
outp += "\n"
|
||||
outp += "0x%02x, " % ord(file_content[i])
|
||||
outp += "\n};\n"
|
||||
return outp
|
||||
|
||||
def convert_to_uf2(file_content):
|
||||
global familyid
|
||||
datapadding = b""
|
||||
while len(datapadding) < 512 - 256 - 32 - 4:
|
||||
datapadding += b"\x00\x00\x00\x00"
|
||||
numblocks = (len(file_content) + 255) // 256
|
||||
outp = b""
|
||||
for blockno in range(numblocks):
|
||||
ptr = 256 * blockno
|
||||
chunk = file_content[ptr:ptr + 256]
|
||||
flags = 0x0
|
||||
if familyid:
|
||||
flags |= 0x2000
|
||||
hd = struct.pack(b"<IIIIIIII",
|
||||
UF2_MAGIC_START0, UF2_MAGIC_START1,
|
||||
flags, ptr + appstartaddr, 256, blockno, numblocks, familyid)
|
||||
while len(chunk) < 256:
|
||||
chunk += b"\x00"
|
||||
block = hd + chunk + datapadding + struct.pack(b"<I", UF2_MAGIC_END)
|
||||
assert len(block) == 512
|
||||
outp += block
|
||||
return outp
|
||||
|
||||
class Block:
|
||||
def __init__(self, addr):
|
||||
self.addr = addr
|
||||
self.bytes = bytearray(256)
|
||||
|
||||
def encode(self, blockno, numblocks):
|
||||
global familyid
|
||||
flags = 0x0
|
||||
if familyid:
|
||||
flags |= 0x2000
|
||||
hd = struct.pack("<IIIIIIII",
|
||||
UF2_MAGIC_START0, UF2_MAGIC_START1,
|
||||
flags, self.addr, 256, blockno, numblocks, familyid)
|
||||
hd += self.bytes[0:256]
|
||||
while len(hd) < 512 - 4:
|
||||
hd += b"\x00"
|
||||
hd += struct.pack("<I", UF2_MAGIC_END)
|
||||
return hd
|
||||
|
||||
def convert_from_hex_to_uf2(buf):
|
||||
global appstartaddr
|
||||
appstartaddr = None
|
||||
upper = 0
|
||||
currblock = None
|
||||
blocks = []
|
||||
for line in buf.split('\n'):
|
||||
if line[0] != ":":
|
||||
continue
|
||||
i = 1
|
||||
rec = []
|
||||
while i < len(line) - 1:
|
||||
rec.append(int(line[i:i+2], 16))
|
||||
i += 2
|
||||
tp = rec[3]
|
||||
if tp == 4:
|
||||
upper = ((rec[4] << 8) | rec[5]) << 16
|
||||
elif tp == 2:
|
||||
upper = ((rec[4] << 8) | rec[5]) << 4
|
||||
assert (upper & 0xffff) == 0
|
||||
elif tp == 1:
|
||||
break
|
||||
elif tp == 0:
|
||||
addr = upper | (rec[1] << 8) | rec[2]
|
||||
if appstartaddr == None:
|
||||
appstartaddr = addr
|
||||
i = 4
|
||||
while i < len(rec) - 1:
|
||||
if not currblock or currblock.addr & ~0xff != addr & ~0xff:
|
||||
currblock = Block(addr & ~0xff)
|
||||
blocks.append(currblock)
|
||||
currblock.bytes[addr & 0xff] = rec[i]
|
||||
addr += 1
|
||||
i += 1
|
||||
numblocks = len(blocks)
|
||||
resfile = b""
|
||||
for i in range(0, numblocks):
|
||||
resfile += blocks[i].encode(i, numblocks)
|
||||
return resfile
|
||||
|
||||
def to_str(b):
|
||||
return b.decode("utf-8")
|
||||
|
||||
def get_drives():
|
||||
drives = []
|
||||
if sys.platform == "win32":
|
||||
r = subprocess.check_output(["wmic", "PATH", "Win32_LogicalDisk",
|
||||
"get", "DeviceID,", "VolumeName,",
|
||||
"FileSystem,", "DriveType"])
|
||||
for line in to_str(r).split('\n'):
|
||||
words = re.split('\s+', line)
|
||||
if len(words) >= 3 and words[1] == "2" and words[2] == "FAT":
|
||||
drives.append(words[0])
|
||||
else:
|
||||
rootpath = "/media"
|
||||
if sys.platform == "darwin":
|
||||
rootpath = "/Volumes"
|
||||
elif sys.platform == "linux":
|
||||
tmp = rootpath + "/" + os.environ["USER"]
|
||||
if os.path.isdir(tmp):
|
||||
rootpath = tmp
|
||||
for d in os.listdir(rootpath):
|
||||
drives.append(os.path.join(rootpath, d))
|
||||
|
||||
|
||||
def has_info(d):
|
||||
try:
|
||||
return os.path.isfile(d + INFO_FILE)
|
||||
except:
|
||||
return False
|
||||
|
||||
return list(filter(has_info, drives))
|
||||
|
||||
|
||||
def board_id(path):
|
||||
with open(path + INFO_FILE, mode='r') as file:
|
||||
file_content = file.read()
|
||||
return re.search("Board-ID: ([^\r\n]*)", file_content).group(1)
|
||||
|
||||
|
||||
def list_drives():
|
||||
for d in get_drives():
|
||||
print(d, board_id(d))
|
||||
|
||||
|
||||
def write_file(name, buf):
|
||||
with open(name, "wb") as f:
|
||||
f.write(buf)
|
||||
print("Wrote %d bytes to %s" % (len(buf), name))
|
||||
|
||||
|
||||
def main():
|
||||
global appstartaddr, familyid
|
||||
def error(msg):
|
||||
print(msg)
|
||||
sys.exit(1)
|
||||
parser = argparse.ArgumentParser(description='Convert to UF2 or flash directly.')
|
||||
parser.add_argument('input', metavar='INPUT', type=str, nargs='?',
|
||||
help='input file (HEX, BIN or UF2)')
|
||||
parser.add_argument('-b' , '--base', dest='base', type=str,
|
||||
default="0x2000",
|
||||
help='set base address of application for BIN format (default: 0x2000)')
|
||||
parser.add_argument('-o' , '--output', metavar="FILE", dest='output', type=str,
|
||||
help='write output to named file; defaults to "flash.uf2" or "flash.bin" where sensible')
|
||||
parser.add_argument('-d' , '--device', dest="device_path",
|
||||
help='select a device path to flash')
|
||||
parser.add_argument('-l' , '--list', action='store_true',
|
||||
help='list connected devices')
|
||||
parser.add_argument('-c' , '--convert', action='store_true',
|
||||
help='do not flash, just convert')
|
||||
parser.add_argument('-D' , '--deploy', action='store_true',
|
||||
help='just flash, do not convert')
|
||||
parser.add_argument('-f' , '--family', dest='family', type=str,
|
||||
default="0x0",
|
||||
help='specify familyID - number or name (default: 0x0)')
|
||||
parser.add_argument('-C' , '--carray', action='store_true',
|
||||
help='convert binary file to a C array, not UF2')
|
||||
args = parser.parse_args()
|
||||
appstartaddr = int(args.base, 0)
|
||||
|
||||
if args.family.upper() in families:
|
||||
familyid = families[args.family.upper()]
|
||||
else:
|
||||
try:
|
||||
familyid = int(args.family, 0)
|
||||
except ValueError:
|
||||
error("Family ID needs to be a number or one of: " + ", ".join(families.keys()))
|
||||
|
||||
if args.list:
|
||||
list_drives()
|
||||
else:
|
||||
if not args.input:
|
||||
error("Need input file")
|
||||
with open(args.input, mode='rb') as f:
|
||||
inpbuf = f.read()
|
||||
from_uf2 = is_uf2(inpbuf)
|
||||
ext = "uf2"
|
||||
if args.deploy:
|
||||
outbuf = inpbuf
|
||||
elif from_uf2:
|
||||
outbuf = convert_from_uf2(inpbuf)
|
||||
ext = "bin"
|
||||
elif is_hex(inpbuf):
|
||||
outbuf = convert_from_hex_to_uf2(inpbuf.decode("utf-8"))
|
||||
elif args.carray:
|
||||
outbuf = convert_to_carray(inpbuf)
|
||||
ext = "h"
|
||||
else:
|
||||
outbuf = convert_to_uf2(inpbuf)
|
||||
print("Converting to %s, output size: %d, start address: 0x%x" %
|
||||
(ext, len(outbuf), appstartaddr))
|
||||
if args.convert or ext != "uf2":
|
||||
drives = []
|
||||
if args.output == None:
|
||||
args.output = "flash." + ext
|
||||
else:
|
||||
drives = get_drives()
|
||||
|
||||
if args.output:
|
||||
write_file(args.output, outbuf)
|
||||
else:
|
||||
if len(drives) == 0:
|
||||
error("No drive to deploy.")
|
||||
for d in drives:
|
||||
print("Flashing %s (%s)" % (d, board_id(d)))
|
||||
write_file(d + "/NEW.UF2", outbuf)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
4
bin/upload-to-bootloader.sh
Executable file
4
bin/upload-to-bootloader.sh
Executable file
@@ -0,0 +1,4 @@
|
||||
|
||||
echo "Converting to uf2 for NRF52 Adafruit bootloader"
|
||||
bin/uf2conv.py .pio/build/lora-relay-v1/firmware.hex -f 0xADA52840
|
||||
# cp flash.uf2 /media/kevinh/FTH*BOOT/
|
||||
@@ -1,3 +1,3 @@
|
||||
|
||||
|
||||
export VERSION=0.7.9
|
||||
export VERSION=0.9.2
|
||||
46
boards/lora-relay-v1.json
Normal file
46
boards/lora-relay-v1.json
Normal file
@@ -0,0 +1,46 @@
|
||||
{
|
||||
"build": {
|
||||
"arduino": {
|
||||
"ldscript": "nrf52840_s140_v6.ld"
|
||||
},
|
||||
"core": "nRF5",
|
||||
"cpu": "cortex-m4",
|
||||
"extra_flags": "-DARDUINO_NRF52840_LORA_RELAY_V1 -DNRF52840_XXAA",
|
||||
"f_cpu": "64000000L",
|
||||
"hwids": [["0x239A", "0x4404"]],
|
||||
"usb_product": "LORA_RELAY",
|
||||
"mcu": "nrf52840",
|
||||
"variant": "lora_relay_v1",
|
||||
"variants_dir": "variants",
|
||||
"bsp": {
|
||||
"name": "adafruit"
|
||||
},
|
||||
"softdevice": {
|
||||
"sd_flags": "-DS140",
|
||||
"sd_name": "s140",
|
||||
"sd_version": "6.1.1",
|
||||
"sd_fwid": "0x00B6"
|
||||
},
|
||||
"bootloader": {
|
||||
"settings_addr": "0xFF000"
|
||||
}
|
||||
},
|
||||
"connectivity": ["bluetooth"],
|
||||
"debug": {
|
||||
"jlink_device": "nRF52840_xxAA",
|
||||
"onboard_tools": ["jlink"],
|
||||
"svd_path": "nrf52840.svd"
|
||||
},
|
||||
"frameworks": ["arduino"],
|
||||
"name": "Meshtastic Lora Relay V1 (Adafruit BSP)",
|
||||
"upload": {
|
||||
"maximum_ram_size": 248832,
|
||||
"maximum_size": 815104,
|
||||
"require_upload_port": true,
|
||||
"speed": 115200,
|
||||
"protocol": "jlink",
|
||||
"protocols": ["jlink", "nrfjprog", "stlink"]
|
||||
},
|
||||
"url": "https://github.com/BigCorvus/SX1262-LoRa-BLE-Relay",
|
||||
"vendor": "BigCorvus"
|
||||
}
|
||||
47
boards/nrf52840_dk.json
Normal file
47
boards/nrf52840_dk.json
Normal file
@@ -0,0 +1,47 @@
|
||||
{
|
||||
"build": {
|
||||
"arduino": {
|
||||
"ldscript": "nrf52840_s140_v6.ld"
|
||||
},
|
||||
"core": "nRF5",
|
||||
"cpu": "cortex-m4",
|
||||
"extra_flags": "-DARDUINO_NRF52840_PCA10056 -DNRF52840_XXAA",
|
||||
"f_cpu": "64000000L",
|
||||
"hwids": [["0x239A", "0x4404"]],
|
||||
"usb_product": "nrf52840dk",
|
||||
"mcu": "nrf52840",
|
||||
"variant": "pca10056",
|
||||
"variants_dir": "variants",
|
||||
"bsp": {
|
||||
"name": "adafruit"
|
||||
},
|
||||
"softdevice": {
|
||||
"sd_flags": "-DS140",
|
||||
"sd_name": "s140",
|
||||
"sd_version": "6.1.1",
|
||||
"sd_fwid": "0x00B6"
|
||||
},
|
||||
"bootloader": {
|
||||
"settings_addr": "0xFF000"
|
||||
}
|
||||
},
|
||||
"connectivity": ["bluetooth"],
|
||||
"debug": {
|
||||
"jlink_device": "nRF52840_xxAA",
|
||||
"onboard_tools": ["jlink"],
|
||||
"svd_path": "nrf52840.svd"
|
||||
},
|
||||
"frameworks": ["arduino"],
|
||||
"name": "A modified NRF52840-DK devboard (Adafruit BSP)",
|
||||
"upload": {
|
||||
"maximum_ram_size": 248832,
|
||||
"maximum_size": 815104,
|
||||
"require_upload_port": true,
|
||||
"speed": 115200,
|
||||
"protocol": "jlink",
|
||||
"protocols": ["jlink", "nrfjprog", "stlink"]
|
||||
},
|
||||
"url": "https://meshtastic.org/",
|
||||
"vendor": "Nordic Semi"
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
"extra_flags": "-DARDUINO_NRF52840_PCA10056 -DNRF52840_XXAA",
|
||||
"f_cpu": "64000000L",
|
||||
"hwids": [["0x239A", "0x4404"]],
|
||||
"usb_product": "SimPPR",
|
||||
"usb_product": "nrf52840dk",
|
||||
"mcu": "nrf52840",
|
||||
"variant": "pca10056-rc-clock",
|
||||
"variants_dir": "variants",
|
||||
|
||||
55
boards/rak815.json
Normal file
55
boards/rak815.json
Normal file
@@ -0,0 +1,55 @@
|
||||
{
|
||||
"build": {
|
||||
"arduino":{
|
||||
"ldscript": "nrf52832_s132_v6.ld"
|
||||
},
|
||||
"core": "nRF5",
|
||||
"cpu": "cortex-m4",
|
||||
"extra_flags": "-DNRF52832_XXAA -DNRF52",
|
||||
"f_cpu": "64000000L",
|
||||
"hwids": [
|
||||
[
|
||||
"0x10c4",
|
||||
"0xea60"
|
||||
]
|
||||
],
|
||||
"usb_product": "RAK815",
|
||||
"mcu": "nrf52832",
|
||||
"variant": "rak815",
|
||||
"bsp": {
|
||||
"name": "adafruit"
|
||||
},
|
||||
"softdevice": {
|
||||
"sd_flags": "-DS132",
|
||||
"sd_name": "s132",
|
||||
"sd_version": "6.1.1",
|
||||
"sd_fwid": "0x00B7"
|
||||
}
|
||||
},
|
||||
"connectivity": [
|
||||
"bluetooth"
|
||||
],
|
||||
"debug": {
|
||||
"jlink_device": "nRF52832_xxAA",
|
||||
"svd_path": "nrf52.svd"
|
||||
},
|
||||
"frameworks": [
|
||||
"arduino"
|
||||
],
|
||||
"name": "RAK RAK815",
|
||||
"upload": {
|
||||
"maximum_ram_size": 65536,
|
||||
"maximum_size": 524288,
|
||||
"require_upload_port": true,
|
||||
"speed": 115200,
|
||||
"protocol": "nrfutil",
|
||||
"protocols": [
|
||||
"jlink",
|
||||
"nrfjprog",
|
||||
"nrfutil",
|
||||
"stlink"
|
||||
]
|
||||
},
|
||||
"url": "https://store.rakwireless.com/products/rak815-hybrid-location-tracker",
|
||||
"vendor": "RAK"
|
||||
}
|
||||
@@ -35,6 +35,10 @@ This project is currently in beta testing but it is fairly stable and feature co
|
||||
|
||||
This software is 100% open source and developed by a group of hobbyist experimenters. No warranty is provided, if you'd like to improve it - we'd love your help. Please post in the [forum](https://meshtastic.discourse.group/).
|
||||
|
||||
### Beginner's Guide
|
||||
|
||||
For an detailed walk-through aimed at beginners, we recommend [meshtastic.letstalkthis.com](https://meshtastic.letstalkthis.com/).
|
||||
|
||||
# Updates
|
||||
|
||||
Note: Updates are happening almost daily, only major updates are listed below. For more details see our forum.
|
||||
@@ -62,6 +66,8 @@ The link above will return older more stable releases. We would prefer if you jo
|
||||
|
||||
If you'd like to help with development, the source code is [on github](https://github.com/meshtastic/Meshtastic-Android).
|
||||
|
||||
The app is also distributed for Amazon Fire devices via the Amazon appstore: [](https://www.amazon.com/Geeksville-Industries-Meshtastic/dp/B08CY9394Q)
|
||||
|
||||
## Supported hardware
|
||||
|
||||
We currently support two brands of radios. The [TTGO T-Beam](https://www.aliexpress.com/item/4001178678568.html) and the [Heltec LoRa 32](https://heltec.org/project/wifi-lora-32/). Most people should buy the T-Beam and a 18650 battery (total cost less than \$35). Also, the version of the T-Beam we link to is shipped with Meshtastic **preinstalled** by TTGO, so you don't have to install it yourself.
|
||||
|
||||
8
docs/SupportedHardware.md
Normal file
8
docs/SupportedHardware.md
Normal file
@@ -0,0 +1,8 @@
|
||||
| Vendor | Product line | Version | Board labels | Notes | URL |
|
||||
|---|---|---|---|---|---|
|
||||
| TTGO | T-Beam | 0.7 | T22_V07 20180711 | LoRa 433/470MHz *OR* LoRa 868/915MHz , <br/>GPS ublox NEO-6M , <br/>battery holder for Li-Ion 18650 | [buy](https://www.aliexpress.com/item/4000574335430.html) |
|
||||
| TTGO | T-Beam | 1.0 | | | [buy](https://www.aliexpress.com/item/4001178678568.html) |
|
||||
| TTGO | T-Beam | 1.1 | T22_V11 20191212 | LoRa 433/470MHz *OR* LoRa 868/915MHz *OR* LoRa 923MHz , <br/>GPS ublox NEO-M8N , <br/>battery holder for Li-Ion 18650 | [buy](https://www.aliexpress.com/item/4001178678568.html) |
|
||||
| TTGO | Lora32 | 2.0 | *missing* | LoRa 433/470MHz *OR* LoRa 868/915MHz , <br/>OLED SSD1306 , <br/>SD card holder | [buy](https://www.aliexpress.com/item/4000211331316.html) |
|
||||
| TTGO | Lora32 | 2.1 | T3_V1.6 20180606 | LoRa 32 (V2) , <br/>SD card holder | [buy](https://www.aliexpress.com/item/4000119208093.html) |
|
||||
| Heltec | Lora 32 | V2 | V2 | LoRa 433/470MHz *OR* LoRa 868/915MHz | [buy](https://heltec.org/project/wifi-lora-32/) |
|
||||
35
docs/hardware/corvus.md
Normal file
35
docs/hardware/corvus.md
Normal file
@@ -0,0 +1,35 @@
|
||||
# Notes on @BigCorvus boards
|
||||
|
||||
## Board version 1.1
|
||||
|
||||
variant name lora_relay_v1
|
||||
|
||||
### Remaining TODOs
|
||||
|
||||
- power hold for the ST7735
|
||||
- look at example sketch
|
||||
- turn on xmit boost
|
||||
|
||||
## Recommendations for future boards
|
||||
|
||||
@BigCorvus your board is **really** nice. Here's some ideas for the future:
|
||||
|
||||
- make the SWDIO header more standard (the small ARM 2x5 micro footprint?) or at least througholes so it is easy to solder a header
|
||||
|
||||
## How to program bootloader
|
||||
|
||||
Download from here: https://github.com/adafruit/Adafruit_nRF52_Bootloader/releases
|
||||
|
||||
```
|
||||
nrfjprog -f nrf52 --eraseall
|
||||
Erasing user available code and UICR flash areas.
|
||||
Applying system reset.
|
||||
|
||||
nrfjprog -f nrf52 --program feather_nrf52840_express_bootloader-0.3.2_s140_6.1.1.hex
|
||||
Parsing hex file.
|
||||
Reading flash area to program to guarantee it is erased.
|
||||
Checking that the area to write is not protected.
|
||||
Programming device.
|
||||
```
|
||||
|
||||
Then reboot the board, if all went well it now shows up as a mountable filesystem on your USB bus.
|
||||
BIN
docs/hardware/rak815/PE4259.pdf
Normal file
BIN
docs/hardware/rak815/PE4259.pdf
Normal file
Binary file not shown.
BIN
docs/hardware/rak815/RAK813 Module Datasheet V1.0.pdf
Normal file
BIN
docs/hardware/rak815/RAK813 Module Datasheet V1.0.pdf
Normal file
Binary file not shown.
3692
docs/hardware/rak815/RAK813_BLE_LoRa_Schematic_20180322.pdf
Normal file
3692
docs/hardware/rak815/RAK813_BLE_LoRa_Schematic_20180322.pdf
Normal file
File diff suppressed because one or more lines are too long
Binary file not shown.
File diff suppressed because one or more lines are too long
8249
docs/hardware/rak815/ublox max7 datasheet.pdf
Normal file
8249
docs/hardware/rak815/ublox max7 datasheet.pdf
Normal file
File diff suppressed because one or more lines are too long
@@ -2,18 +2,20 @@
|
||||
|
||||
You probably don't care about this section - skip to the next one.
|
||||
|
||||
- do device release (after testing BLE fixes) https://github.com/meshtastic/Meshtastic-device/pull/218#event-3475395948
|
||||
- some channel names don't work - check for illegal symbols per forum report
|
||||
- auto invalidate old BLE handles
|
||||
- disable software update button after update finishes
|
||||
- mention translations in main readme
|
||||
- check BLE handle stability across sleep - stress test sleep/wake - btu_init_core calls gatt_init - which assigns handles global
|
||||
- @feh123 Sony Xperia Z1 C6903 running Android 5.1.1
|
||||
- first message sent is still doubled for some people
|
||||
- Android frontend should refetch the android messages from backend service on Resume
|
||||
- let users set arbitrary params in android
|
||||
- implement first cut of router mode: preferentially handle flooding, and change sleep and GPS behaviors
|
||||
- NRF52 BLE support
|
||||
Nimble tasks:
|
||||
|
||||
- readerror.txt stress test bug
|
||||
- started RPA long test, jul 22 6pm
|
||||
- implement nimble software update api
|
||||
- update to latest bins, test OTA again (measure times) and then checkin bins
|
||||
- do alpha release
|
||||
|
||||
* update protocol description per cyclomies email thread
|
||||
* update faq with antennas https://meshtastic.discourse.group/t/range-test-ideas-requested/738/2
|
||||
* update faq on recommended android version and phones
|
||||
* add help link inside the app, reference a page on the wiki
|
||||
* turn on amazon reviews support
|
||||
* add a tablet layout (with map next to messages) in the android app
|
||||
|
||||
# Medium priority
|
||||
|
||||
@@ -32,6 +34,10 @@ Items to complete before 1.0.
|
||||
|
||||
Items after the first final candidate release.
|
||||
|
||||
- implement nimble battery level service
|
||||
- Nimble implement device info service remaining fields (hw version etc)
|
||||
- Turn on RPA addresses for the device side in Nimble
|
||||
- Try to teardown less of the Nimble protocol stack across sleep
|
||||
- dynamic frequency scaling could save a lot of power on ESP32, but it seems to corrupt uart (even with ref_tick set correctly)
|
||||
- Change back to using a fixed sized MemoryPool rather than MemoryDynamic (see bug #149)
|
||||
- scan to find channels with low background noise? (Use CAD mode of the RF95 to automatically find low noise channels)
|
||||
@@ -58,7 +64,6 @@ Items after the first final candidate release.
|
||||
- split out the software update utility so other projects can use it. Have the appload specify the URL for downloads.
|
||||
- read the PMU battery fault indicators and blink/led/warn user on screen
|
||||
- discard very old nodedb records (> 1wk)
|
||||
- add a watchdog timer
|
||||
- handle millis() rollover in GPS.getTime - otherwise we will break after 50 days
|
||||
- report esp32 device code bugs back to the mothership via android
|
||||
- change BLE bonding to something more secure. see comment by pSecurity->setAuthenticationMode(ESP_LE_AUTH_BOND)
|
||||
|
||||
14
docs/software/ant.md
Normal file
14
docs/software/ant.md
Normal file
@@ -0,0 +1,14 @@
|
||||
# ANT protocol notes
|
||||
|
||||
SD340 terms are reasonable for NRF52
|
||||
https://www.thisisant.com/developer/components/nrf52832#tab_protocol_stacks_tab
|
||||
|
||||
Profiles to implement:
|
||||
|
||||
tracker
|
||||
https://www.thisisant.com/developer/ant-plus/device-profiles/#4365_tab
|
||||
|
||||
ebike
|
||||
https://www.thisisant.com/developer/ant-plus/device-profiles/#527_tab
|
||||
|
||||
no profile for messaging?
|
||||
@@ -1,15 +1,30 @@
|
||||
# Build instructions
|
||||
|
||||
This project uses the simple PlatformIO build system. You can use the IDE, but for brevity
|
||||
in these instructions I describe use of their command line tool.
|
||||
This project uses the simple PlatformIO build system. PlatformIO is an extension to Microsoft VSCode.
|
||||
|
||||
1. Purchase a suitable radio (see above)
|
||||
## GUI
|
||||
1. Purchase a suitable [radio](https://github.com/meshtastic/Meshtastic-device/wiki/Hardware-Information).
|
||||
2. Install [PlatformIO](https://platformio.org/platformio-ide).
|
||||
3. Click the PlatformIO icon on the side bar. 
|
||||
4. Under `Quick Access, Miscellaneous, Clone Git Project` enter the URL of the Meshtastic repo found [here](https://github.com/meshtastic/Meshtastic-device). 
|
||||
5. Select a file location to save the repo.
|
||||
6. Once loaded, open the `platformio.ini` file.
|
||||
7. At the line `default_envs` you can change it to the board type you are building for ie. `tlora-v2, tlora-v1, tlora-v2-1-1.6, tbeam, heltec, tbeam0.7` (boards are listed further down in the file).
|
||||
8. Click the PlatformIO icon on the side bar. Under `Project Tasks` you can now build or upload.
|
||||
|
||||
## Command Line
|
||||
1. Purchase a suitable [radio](https://github.com/meshtastic/Meshtastic-device/wiki/Hardware-Information).
|
||||
2. Install [PlatformIO](https://platformio.org/platformio-ide)
|
||||
3. Download this git repo and cd into it
|
||||
3. Download this git repo and cd into it:
|
||||
|
||||
```
|
||||
git clone https://github.com/meshtastic/Meshtastic-device.git
|
||||
cd Meshtastic-device
|
||||
```
|
||||
4. Run `git submodule update --init --recursive` to pull in dependencies this project needs.
|
||||
5. If you are outside the USA, run "export COUNTRY=EU865" (or whatever) to set the correct frequency range for your country. Options are provided for `EU433`, `EU865`, `CN`, `JP` and `US` (default). Pull-requests eagerly accepted for other countries.
|
||||
6. Plug the radio into your USB port
|
||||
7. Type `pio run --environment XXX -t upload` (This command will fetch dependencies, build the project and install it on the board via USB). For XXX, use the board type you have (either `tbeam`, `heltec`, `ttgo-lora32-v1`, `ttgo-lora32-v2`).
|
||||
7. Type `pio run --environment XXX -t upload` (This command will fetch dependencies, build the project and install it on the board via USB). For XXX, use the board type you have (either `tlora-v2, tlora-v1, tlora-v2-1-1.6, tbeam, heltec, tbeam0.7`).
|
||||
8. Platform IO also installs a very nice VisualStudio Code based IDE, see their [tutorial](https://docs.platformio.org/en/latest/tutorials/espressif32/arduino_debugging_unit_testing.html) if you'd like to use it.
|
||||
|
||||
## Decoding stack traces
|
||||
|
||||
@@ -1,12 +1,26 @@
|
||||
# Bluetooth API
|
||||
# Device API
|
||||
|
||||
The Bluetooth API is design to have only a few characteristics and most polymorphism comes from the flexible set of Google Protocol Buffers which are sent over the wire. We use protocol buffers extensively both for the bluetooth API and for packets inside the mesh or when providing packets to other applications on the phone.
|
||||
The Device API is design to have only a simple stream of ToRadio and FromRadio packets and all polymorphism comes from the flexible set of Google Protocol Buffers which are sent over the wire. We use protocol buffers extensively both for the bluetooth API and for packets inside the mesh or when providing packets to other applications on the phone.
|
||||
|
||||
## A note on MTU sizes
|
||||
## Streaming version
|
||||
|
||||
This device will work with any MTU size, but it is highly recommended that you call your phone's "setMTU function to increase MTU to 512 bytes" as soon as you connect to a service. This will dramatically improve performance when reading/writing packets.
|
||||
This protocol is **almost** identical when it is deployed over BLE, Serial/USB or TCP (our three currently supported transports for connecting to phone/PC). Most of this document is in terms of the original BLE version, but this section describes the small changes when this API is exposed over a Streaming (non datagram) transport. The streaming version has the following changes:
|
||||
|
||||
## MeshBluetoothService
|
||||
- We assume the stream is reliable (though the protocol will resynchronize if bytes are lost or corrupted). i.e. we do not include CRCs or error correction codes.
|
||||
- Packets always have a four byte header (described below) prefixed before each packet. This header provides framing characters and length.
|
||||
- The stream going towards the radio is only a series of ToRadio packets (with the extra 4 byte headers)
|
||||
- The stream going towards the PC is a stream of FromRadio packets (with the 4 byte headers), or if the receiver state machine does not see valid header bytes it can (optionally) print those bytes as the debug console from the radio. This allows the device to emit regular serial debugging messages (which can be understood by a terminal program) but also switch to a more structured set of protobufs once it sees that the PC client has sent a protobuf towards it.
|
||||
|
||||
The 4 byte header is constructed to both provide framing and to not look line 'normal' 7 bit ASCII.
|
||||
|
||||
- Byte 0: START1 (0x94)
|
||||
- Byte 1: START2 (0xc3)
|
||||
- Byte 2: MSB of protobuf length
|
||||
- Byte 3: LSB of protobuf length
|
||||
|
||||
The receiver will validate length and if >512 it will assume the packet is corrupted and return to looking for START1. While looking for START1 any other characters are printed as "debug output". For small example implementation of this reader see the meshtastic-python implementation.
|
||||
|
||||
## MeshBluetoothService (the BLE API)
|
||||
|
||||
This is the main bluetooth service for the device and provides the API your app should use to get information about the mesh, send packets or provision the radio.
|
||||
|
||||
@@ -71,16 +85,20 @@ Not all messages are kept in the fromradio queue (filtered based on SubPacket):
|
||||
- No WantNodeNum / DenyNodeNum messages are kept
|
||||
A variable keepAllPackets, if set to true will suppress this behavior and instead keep everything for forwarding to the phone (for debugging)
|
||||
|
||||
## Protobuf API
|
||||
### A note on MTU sizes
|
||||
|
||||
This device will work with any MTU size, but it is highly recommended that you call your phone's "setMTU function to increase MTU to 512 bytes" as soon as you connect to a service. This will dramatically improve performance when reading/writing packets.
|
||||
|
||||
### Protobuf API
|
||||
|
||||
On connect, you should send a want_config_id protobuf to the device. This will cause the device to send its node DB and radio config via the fromradio endpoint. After sending the full DB, the radio will send a want_config_id to indicate it is done sending the configuration.
|
||||
|
||||
## Other bluetooth services
|
||||
### Other bluetooth services
|
||||
|
||||
This document focuses on the core mesh service, but it is worth noting that the following other Bluetooth services are also
|
||||
This document focuses on the core device protocol, but it is worth noting that the following other Bluetooth services are also
|
||||
provided by the device.
|
||||
|
||||
### BluetoothSoftwareUpdate
|
||||
#### BluetoothSoftwareUpdate
|
||||
|
||||
The software update service. For a sample function that performs a software update using this API see [startUpdate](https://github.com/meshtastic/Meshtastic-Android/blob/master/app/src/main/java/com/geeksville/mesh/service/SoftwareUpdateService.kt).
|
||||
|
||||
@@ -98,10 +116,10 @@ Characteristics
|
||||
| GATT_UUID_MANU_NAME/0x2a29 | read | |
|
||||
| GATT_UUID_HW_VERSION_STR/0x2a27 | read | |
|
||||
|
||||
### DeviceInformationService
|
||||
#### DeviceInformationService
|
||||
|
||||
Implements the standard BLE contract for this service (has software version, hardware model, serial number, etc...)
|
||||
|
||||
### BatteryLevelService
|
||||
#### BatteryLevelService
|
||||
|
||||
Implements the standard BLE contract service, provides battery level in a way that most client devices should automatically understand (i.e. it should show in the bluetooth devices screen automatically)
|
||||
@@ -12,5 +12,12 @@ you'll automatically get our fixed libraries.
|
||||
https://docs.espressif.com/projects/esp-idf/en/release-v3.3/get-started/linux-setup.html
|
||||
kevinh@kevin-server:~/development/meshtastic/esp32-arduino-lib-builder\$ python /home/kevinh/development/meshtastic/esp32-arduino-lib-builder/esp-idf/components/esptool*py/esptool/esptool.py --chip esp32 --port /dev/ttyUSB0 --baud 921600 --before default_reset --after hard_reset write_flash -z --flash_mode dout --flash_freq 40m --flash_size detect 0x1000 /home/kevinh/development/meshtastic/esp32-arduino-lib-builder/build/bootloader/bootloader.bin
|
||||
cp -a out/tools/sdk/* components/arduino/tools/sdk
|
||||
cp -ar components/arduino/* ~/.platformio/packages/framework-arduinoespressif32@src-fba9d33740f719f712e9f8b07da6ea13/
|
||||
cp -ar components/arduino/* ~/.platformio/packages/framework-arduinoespressif32
|
||||
|
||||
/// @src-fba9d33740f719f712e9f8b07da6ea13/
|
||||
|
||||
or
|
||||
|
||||
cp -ar out/tools/sdk/* ~/.platformio/packages/framework-arduinoespressif32/tools/sdk
|
||||
|
||||
```
|
||||
|
||||
@@ -1,5 +1,75 @@
|
||||
# Mesh broadcast algorithm
|
||||
|
||||
## Current algorithm
|
||||
|
||||
The routing protocol for Meshtastic is really quite simple (and suboptimal). It is heavily influenced by the mesh routing algorithm used in [Radiohead](https://www.airspayce.com/mikem/arduino/RadioHead/) (which was used in very early versions of this project). It has four conceptual layers.
|
||||
|
||||
### A note about protocol buffers
|
||||
|
||||
Because we want our devices to work across various vendors and implementations, we use [Protocol Buffers](https://github.com/meshtastic/Meshtastic-protobufs) pervasively. For information on how the protocol buffers are used wrt API clients see [sw-design](sw-design.md), for purposes of this document you mostly only
|
||||
need to consider the MeshPacket and Subpacket message types.
|
||||
|
||||
### Layer 1: Non reliable zero hop messaging
|
||||
|
||||
This layer is conventional non-reliable lora packet transmission. The transmitted packet has the following representation on the ether:
|
||||
|
||||
- A 32 bit LORA preamble (to allow receiving radios to synchronize clocks and start framing). We use a longer than minimum (8 bit) preamble to maximize the amount of time the LORA receivers can stay asleep, which dramatically lowers power consumption.
|
||||
|
||||
After the preamble the 16 byte packet header is transmitted. This header is described directly by the PacketHeader class in the C++ source code. But indirectly it matches the first portion of the "MeshPacket" protobuf definition. But notably: this portion of the packet is sent directly as the following 16 bytes (rather than using the protobuf encoding). We do this to both save airtime and to allow receiving radio hardware the option of filtering packets before even waking the main CPU.
|
||||
|
||||
- to (4 bytes): the unique NodeId of the destination (or 0xffffffff for NodeNum_BROADCAST)
|
||||
- from (4 bytes): the unique NodeId of the sender)
|
||||
- id (4 bytes): the unique (wrt the sending node only) packet ID number for this packet. We use a large (32 bit) packet ID to ensure there is enough unique state to protect any encrypted payload from attack.
|
||||
- flags (4 bytes): Only a few bits are are currently used - 3 bits for for the "HopLimit" (see below) and 1 bit for "WantAck"
|
||||
|
||||
After the packet header the actual packet is placed onto the the wire. These bytes are merely the encrypted packed protobuf encoding of the SubPacket protobuf. A full description of our encryption is available in [crypto](crypto.md). It is worth noting that only this SubPacket is encrypted, headers are not. Which leaves open the option of eventually allowing nodes to route packets without knowing the keys used to encrypt.
|
||||
|
||||
NodeIds are constructed from the bottom four bytes of the macaddr of the bluetooth address. Because the OUI is assigned by the IEEE and we currently only support a few CPU manufacturers, the upper byte is defacto guaranteed unique for each vendor. The bottom 3 bytes are guaranteed unique by that vendor.
|
||||
|
||||
To prevent collisions all transmitters will listen before attempting to send. If they hear some other node transmitting, they will reattempt transmission in x milliseconds. This retransmission delay is random between FIXME and FIXME (these two numbers are currently hardwired, but really should be scaled based on expected packet transmission time at current channel settings).
|
||||
|
||||
### Layer 2: Reliable zero hop messaging
|
||||
|
||||
This layer adds reliable messaging between the node and its immediate neighbors (only).
|
||||
|
||||
The default messaging provided by layer-1 is extended by setting the "want-ack" flag in the MeshPacket protobuf. If want-ack is set the following documentation from mesh.proto applies:
|
||||
|
||||
"""This packet is being sent as a reliable message, we would prefer it to arrive
|
||||
at the destination. We would like to receive a ack packet in response.
|
||||
|
||||
Broadcasts messages treat this flag specially: Since acks for broadcasts would
|
||||
rapidly flood the channel, the normal ack behavior is suppressed. Instead,
|
||||
the original sender listens to see if at least one node is rebroadcasting this
|
||||
packet (because naive flooding algorithm). If it hears that the odds (given
|
||||
typical LoRa topologies) the odds are very high that every node should
|
||||
eventually receive the message. So FloodingRouter.cpp generates an implicit
|
||||
ack which is delivered to the original sender. If after some time we don't
|
||||
hear anyone rebroadcast our packet, we will timeout and retransmit, using the
|
||||
regular resend logic."""
|
||||
|
||||
If a transmitting node does not receive an ACK (or a NAK) packet within FIXME milliseconds, it will use layer-1 to attempt a retransmission of the sent packet. A reliable packet (at this 'zero hop' level) will be resent a maximum of three times. If no ack or nak has been received by then the local node will internally generate a nak (either for local consumption or use by higher layers of the protocol).
|
||||
|
||||
### Layer 3: (Naive) flooding for multi-hop messaging
|
||||
|
||||
Given our use-case for the initial release, most of our protocol is built around [flooding](<https://en.wikipedia.org/wiki/Flooding_(computer_networking)>). The implementation is currently 'naive' - i.e. it doesn't try to optimize flooding other than abandoning retransmission once we've seen a nearby receiver has acked the packet. Therefore, for each source packet up to N retransmissions might occur (if there are N nodes in the mesh).
|
||||
|
||||
Each node in the mesh, if it sees a packet on the ether with HopLimit set to a value other than zero, it will decrement that HopLimit and attempt retransmission on behalf of the original sending node.
|
||||
|
||||
### Layer 4: DSR for multi-hop unicast messaging
|
||||
|
||||
This layer is not yet fully implemented (and not yet used). But eventually (if we stay with our own transport rather than switching to QMesh or Reticulum)
|
||||
we will use conventional DSR for unicast messaging. Currently (even when not requiring 'broadcasts') we send any multi-hop unicasts as 'broadcasts' so that we can
|
||||
leverage our (functional) flooding implementation. This is suboptimal but it is a very rare use-case, because the odds are high that most nodes (given our small networks and 'hiking' use case) are within a very small number of hops. When any node witnesses an ack for a packet, it will realize that it can abandon its own
|
||||
broadcast attempt for that packet.
|
||||
|
||||
## Misc notes on remaining tasks
|
||||
|
||||
This section is currently poorly formatted, it is mostly a mere set of todo lists and notes for @geeksville during his initial development. After release 1.0 ideas for future optimization include:
|
||||
|
||||
- Make flood-routing less naive (because we have GPS and radio signal strength as heuristics to avoid redundant retransmissions)
|
||||
- If nodes have been user marked as 'routers', preferentially do flooding via those nodes
|
||||
- Fully implement DSR to improve unicast efficiency (or switch to QMesh/Reticulum as these projects mature)
|
||||
|
||||
great source of papers and class notes: http://www.cs.jhu.edu/~cs647/
|
||||
|
||||
flood routing improvements
|
||||
@@ -32,8 +102,8 @@ optimizations / low priority:
|
||||
|
||||
- read this [this](http://pages.cs.wisc.edu/~suman/pubs/nadv-mobihoc05.pdf) paper and others and make our naive flood routing less naive
|
||||
- read @cyclomies long email with good ideas on optimizations and reply
|
||||
- Remove NodeNum assignment algorithm (now that we use 4 byte node nums)
|
||||
- make android app warn if firmware is too old or too new to talk to
|
||||
- DONE Remove NodeNum assignment algorithm (now that we use 4 byte node nums)
|
||||
- DONE make android app warn if firmware is too old or too new to talk to
|
||||
- change nodenums and packetids in protobuf to be fixed32
|
||||
- low priority: think more careful about reliable retransmit intervals
|
||||
- make ReliableRouter.pending threadsafe
|
||||
@@ -146,23 +216,3 @@ look into the literature for this idea specifically.
|
||||
build the most recent version of reality, and if some nodes are too far, then nodes closer in will eventually forward their changes to the distributed db.
|
||||
- construct non ambigious rules for who broadcasts to request db updates. ideally the algorithm should nicely realize node X can see most other nodes, so they should just listen to all those nodes and minimize the # of broadcasts. the distributed picture of nodes rssi could be useful here?
|
||||
- possibly view the BLE protocol to the radio the same way - just a process of reconverging the node/msgdb database.
|
||||
|
||||
# Old notes
|
||||
|
||||
FIXME, merge into the above:
|
||||
|
||||
good description of batman protocol: https://www.open-mesh.org/projects/open-mesh/wiki/BATMANConcept
|
||||
|
||||
interesting paper on lora mesh: https://portal.research.lu.se/portal/files/45735775/paper.pdf
|
||||
It seems like DSR might be the algorithm used by RadioheadMesh. DSR is described in https://tools.ietf.org/html/rfc4728
|
||||
https://en.wikipedia.org/wiki/Dynamic_Source_Routing
|
||||
|
||||
broadcast solution:
|
||||
Use naive flooding at first (FIXME - do some math for a 20 node, 3 hop mesh. A single flood will require a max of 20 messages sent)
|
||||
Then move to MPR later (http://www.olsr.org/docs/report_html/node28.html). Use altitude and location as heursitics in selecting the MPR set
|
||||
|
||||
compare to db sync algorithm?
|
||||
|
||||
what about never flooding gps broadcasts. instead only have them go one hop in the common case, but if any node X is looking at the position of Y on their gui, then send a unicast to Y asking for position update. Y replies.
|
||||
|
||||
If Y were to die, at least the neighbor nodes of Y would have their last known position of Y.
|
||||
|
||||
@@ -1,19 +1,93 @@
|
||||
# NRF52 TODO
|
||||
|
||||
- Possibly switch from softdevice to Apachy Newt: https://github.com/espressif/esp-nimble
|
||||
https://github.com/apache/mynewt-core - use nimble BLE on both ESP32 and NRF52
|
||||
|
||||
## RAK815
|
||||
|
||||
TODO:
|
||||
|
||||
- shrink soft device RAM usage
|
||||
- get nrf52832 working again (currently OOM)
|
||||
- i2c gps comms not quite right
|
||||
- ble: AdafruitBluefruit::begin - adafruit_ble_task was assigned an invalid stack pointer. out of memory?
|
||||
- measure power draw
|
||||
|
||||
### Bootloader
|
||||
|
||||
Install our (temporarily hacked up) adafruit bootloader
|
||||
|
||||
```
|
||||
kevinh@kevin-server:~/development/meshtastic/Adafruit_nRF52_Bootloader$ make BOARD=rak815 sd flash
|
||||
LD rak815_bootloader-0.3.2-111-g9478eb7-dirty.out
|
||||
text data bss dec hex filename
|
||||
20888 1124 15006 37018 909a _build/build-rak815/rak815_bootloader-0.3.2-111-g9478eb7-dirty.out
|
||||
Create rak815_bootloader-0.3.2-111-g9478eb7-dirty.hex
|
||||
Create rak815_bootloader-0.3.2-111-g9478eb7-dirty-nosd.hex
|
||||
Flashing: rak815_bootloader-0.3.2-111-g9478eb7-dirty-nosd.hex
|
||||
nrfjprog --program _build/build-rak815/rak815_bootloader-0.3.2-111-g9478eb7-dirty-nosd.hex --sectoranduicrerase -f nrf52 --reset
|
||||
Parsing hex file.
|
||||
Erasing page at address 0x0.
|
||||
Erasing page at address 0x74000.
|
||||
Erasing page at address 0x75000.
|
||||
Erasing page at address 0x76000.
|
||||
Erasing page at address 0x77000.
|
||||
Erasing page at address 0x78000.
|
||||
Erasing page at address 0x79000.
|
||||
Erasing UICR flash area.
|
||||
Applying system reset.
|
||||
Checking that the area to write is not protected.
|
||||
Programming device.
|
||||
Applying system reset.
|
||||
Run.
|
||||
```
|
||||
|
||||
### Appload
|
||||
|
||||
tips on installing https://github.com/platformio/platform-nordicnrf52/issues/8#issuecomment-374017768
|
||||
|
||||
to see console output over jlink:
|
||||
|
||||
```
|
||||
12:17
|
||||
in one tab run "bin/nrf52832-gdbserver.sh" - leave this running the whole time while developing/debugging
|
||||
12:17
|
||||
~/development/meshtastic/meshtastic-esp32$ bin/nrf52-console.sh
|
||||
###RTT Client: ************************************************************
|
||||
###RTT Client: * SEGGER Microcontroller GmbH *
|
||||
###RTT Client: * Solutions for real time microcontroller applications *
|
||||
###RTT Client: ************************************************************
|
||||
###RTT Client: * *
|
||||
###RTT Client: * (c) 2012 - 2016 SEGGER Microcontroller GmbH *
|
||||
###RTT Client: * *
|
||||
###RTT Client: * www.segger.com Support: support@segger.com *
|
||||
###RTT Client: * *
|
||||
###RTT Client: ************************************************************
|
||||
###RTT Client: * *
|
||||
###RTT Client: * SEGGER J-Link RTT Client Compiled Apr 7 2020 15:01:22 *
|
||||
###RTT Client: * *
|
||||
###RTT Client: ************************************************************
|
||||
###RTT Client: -----------------------------------------------
|
||||
###RTT Client: Connecting to J-Link RTT Server via localhost:19021 ..............
|
||||
###RTT Client: Connected.
|
||||
SEGGER J-Link V6.70c - Real time terminal output
|
||||
SEGGER J-Link ARM V9.6, SN=69663845
|
||||
Process: JLinkGDBServerCLExein another tab run:
|
||||
12:18
|
||||
On NRF52 I've been using the jlink fake serial console. But since the rak815 has the serial port hooked up we can switch back to that once the basics are working.
|
||||
```
|
||||
|
||||
## Misc work items
|
||||
|
||||
RAM investigation.
|
||||
nRF52832-QFAA 64KB ram, 512KB flash vs
|
||||
nrf52832-QFAB 32KB ram, 512kb flash
|
||||
nrf52833 128KB RAM
|
||||
nrf52840 256KB RAM, 1MB flash
|
||||
|
||||
platform.json
|
||||
Manual hacks needed to build (for now):
|
||||
|
||||
"framework-arduinoadafruitnrf52": {
|
||||
"type": "framework",
|
||||
"optional": true,
|
||||
"version": "https://github.com/meshtastic/Adafruit_nRF52_Arduino.git"
|
||||
},
|
||||
kevinh@kevin-server:~/.platformio/packages/framework-arduinoadafruitnrf52/variants\$ ln -s ~/development/meshtastic/meshtastic-esp32/variants/\* .
|
||||
|
||||
## Initial work items
|
||||
|
||||
|
||||
6
docs/software/rak815.md
Normal file
6
docs/software/rak815.md
Normal file
@@ -0,0 +1,6 @@
|
||||
# RAK815
|
||||
|
||||
Notes on trying to get the RAK815 working with meshtastic.
|
||||
|
||||
good tutorial: https://www.hackster.io/naresh-krish/getting-started-with-rak815-tracker-module-and-arduino-1c7bc9
|
||||
(includes software serial link - possibly useful for GPS)
|
||||
148
docs/software/read-error.txt
Normal file
148
docs/software/read-error.txt
Normal file
@@ -0,0 +1,148 @@
|
||||
nimble stress test error (private notes for @geeksville)
|
||||
|
||||
findings:
|
||||
only happens when stress testing multiple sleepwake cycles?
|
||||
failed packets all have initial mbuflen of zero (should be 1)
|
||||
restarting the connection on phone sometimes (but not always) fixes it (is the larger config nonce pushing packet size up too large?)
|
||||
- packets >= 79 bytes (FromRadio) cause INVALID_OFFSET (7) gatt errors to be sent to the app
|
||||
- some packets are missing
|
||||
|
||||
theory:
|
||||
some sort of leak in mbuf storage during unfortunately timed sleep shutdowns
|
||||
|
||||
|
||||
device side
|
||||
|
||||
|
||||
connection updated; status=0 handle=0 our_ota_addr_type=0 our_ota_addr=00:24:62:ab:dd:df
|
||||
our_id_addr_type=0 our_id_addr=00:24:62:ab:dd:df
|
||||
peer_ota_addr_type=0 peer_ota_addr=00:7c:d9:5c:ba:2e
|
||||
peer_id_addr_type=0 peer_id_addr=00:7c:d9:5c:ba:2e
|
||||
conn_itvl=36 conn_latency=0 supervision_timeout=500 encrypted=1 authenticated=1 bonded=1
|
||||
|
||||
BLE fromRadio called
|
||||
getFromRadio, !available
|
||||
toRadioWriteCb data 0x3ffc3d72, len 4
|
||||
Trigger powerFSM 9
|
||||
Transition powerFSM transition=Contact from phone, from=DARK to=DARK
|
||||
Client wants config, nonce=6864
|
||||
Reset nodeinfo read pointer
|
||||
toRadioWriteCb data 0x3ffc3d72, len 4
|
||||
Trigger powerFSM 9
|
||||
Transition powerFSM transition=Contact from phone, from=DARK to=DARK
|
||||
Client wants config, nonce=6863
|
||||
Reset nodeinfo read pointer
|
||||
BLE fromRadio called
|
||||
getFromRadio, state=2
|
||||
encoding toPhone packet to phone variant=3, 50 bytes
|
||||
BLE fromRadio called
|
||||
getFromRadio, state=3
|
||||
encoding toPhone packet to phone variant=6, 83 bytes
|
||||
BLE fromRadio called
|
||||
getFromRadio, state=4
|
||||
Sending nodeinfo: num=0xabdddf38, lastseen=1595606850, id=!2462abdddf38, name=Bob b
|
||||
encoding toPhone packet to phone variant=4, 67 bytes
|
||||
BLE fromRadio called
|
||||
getFromRadio, state=4
|
||||
Sending nodeinfo: num=0x28b200b4, lastseen=1595606804, id=!246f28b200b4, name=Unknown 00b4
|
||||
encoding toPhone packet to phone variant=4, 80 bytes
|
||||
BLE fromRadio called
|
||||
getFromRadio, state=4
|
||||
Sending nodeinfo: num=0xabf84098, lastseen=1593680756, id=!2462abf84098, name=bx n
|
||||
encoding toPhone packet to phone variant=4, 72 bytes
|
||||
BLE fromRadio called
|
||||
getFromRadio, state=4
|
||||
Sending nodeinfo: num=0x83f0d8e5, lastseen=1594686931, id=!e8e383f0d8e5, name=Unknown d8e5
|
||||
encoding toPhone packet to phone variant=4, 64 bytes
|
||||
BLE fromRadio called
|
||||
getFromRadio, state=4
|
||||
Sending nodeinfo: num=0xd1dc7764, lastseen=1595602082, id=!f008d1dc7764, name=dg
|
||||
encoding toPhone packet to phone variant=4, 52 bytes
|
||||
BLE fromRadio called
|
||||
getFromRadio, state=4
|
||||
Sending nodeinfo: num=0xd1dc7828, lastseen=1595598298, id=!f008d1dc7828, name=ryan
|
||||
encoding toPhone packet to phone variant=4, 54 bytes
|
||||
BLE fromRadio called
|
||||
getFromRadio, state=4
|
||||
Done sending nodeinfos
|
||||
getFromRadio, state=5
|
||||
|
||||
|
||||
|
||||
phone side
|
||||
|
||||
2020-07-24 09:11:00.642 6478-6545/com.geeksville.mesh W/com.geeksville.mesh.service.BluetoothInterface: Attempting reconnect
|
||||
2020-07-24 09:11:00.642 6478-6545/com.geeksville.mesh D/com.geeksville.mesh.service.SafeBluetooth: Enqueuing work: connect
|
||||
2020-07-24 09:11:00.642 6478-6545/com.geeksville.mesh D/com.geeksville.mesh.service.SafeBluetooth$BluetoothContinuation: Starting work: connect
|
||||
2020-07-24 09:11:00.643 6478-6545/com.geeksville.mesh D/BluetoothGatt: connect() - device: 24:62:AB:DD:DF:3A, auto: false
|
||||
2020-07-24 09:11:00.643 6478-6545/com.geeksville.mesh D/BluetoothGatt: registerApp()
|
||||
2020-07-24 09:11:00.643 6478-6545/com.geeksville.mesh D/BluetoothGatt: registerApp() - UUID=026baf7f-d2de-43f1-961f-4e00e04c6fbb
|
||||
2020-07-24 09:11:00.645 6478-27868/com.geeksville.mesh D/BluetoothGatt: onClientRegistered() - status=0 clientIf=4
|
||||
2020-07-24 09:11:01.022 6478-27868/com.geeksville.mesh D/BluetoothGatt: onClientConnectionState() - status=0 clientIf=4 device=24:62:AB:DD:DF:3A
|
||||
2020-07-24 09:11:01.022 6478-27868/com.geeksville.mesh I/com.geeksville.mesh.service.SafeBluetooth: new bluetooth connection state 2, status 0
|
||||
2020-07-24 09:11:01.023 6478-27868/com.geeksville.mesh D/com.geeksville.mesh.service.SafeBluetooth: work connect is completed, resuming status=0, res=kotlin.Unit
|
||||
2020-07-24 09:11:01.023 6478-6545/com.geeksville.mesh I/com.geeksville.mesh.service.BluetoothInterface: Connected to radio!
|
||||
2020-07-24 09:11:01.023 6478-6545/com.geeksville.mesh D/BluetoothGatt: refresh() - device: 24:62:AB:DD:DF:3A
|
||||
2020-07-24 09:11:01.526 6478-6545/com.geeksville.mesh D/com.geeksville.mesh.service.SafeBluetooth: Enqueuing work: discover
|
||||
2020-07-24 09:11:01.526 6478-6545/com.geeksville.mesh D/com.geeksville.mesh.service.SafeBluetooth$BluetoothContinuation: Starting work: discover
|
||||
2020-07-24 09:11:01.526 6478-6545/com.geeksville.mesh D/BluetoothGatt: discoverServices() - device: 24:62:AB:DD:DF:3A
|
||||
2020-07-24 09:11:01.829 6478-27868/com.geeksville.mesh D/BluetoothGatt: onConnectionUpdated() - Device=24:62:AB:DD:DF:3A interval=6 latency=0 timeout=500 status=0
|
||||
2020-07-24 09:11:02.008 6478-27868/com.geeksville.mesh D/BluetoothGatt: onSearchComplete() = Device=24:62:AB:DD:DF:3A Status=0
|
||||
2020-07-24 09:11:02.009 6478-27868/com.geeksville.mesh D/com.geeksville.mesh.service.SafeBluetooth: work discover is completed, resuming status=0, res=kotlin.Unit
|
||||
2020-07-24 09:11:02.009 6478-6545/com.geeksville.mesh D/com.geeksville.mesh.service.BluetoothInterface: Discovered services!
|
||||
2020-07-24 09:11:02.095 6478-27868/com.geeksville.mesh D/BluetoothGatt: onConnectionUpdated() - Device=24:62:AB:DD:DF:3A interval=36 latency=0 timeout=500 status=0
|
||||
2020-07-24 09:11:03.010 6478-6545/com.geeksville.mesh D/com.geeksville.mesh.service.RadioInterfaceService: Broadcasting connection=true
|
||||
2020-07-24 09:11:03.012 6478-6545/com.geeksville.mesh D/com.geeksville.mesh.service.SafeBluetooth: Enqueuing work: readC 8ba2bcc2-ee02-4a55-a531-c525c5e454d5
|
||||
2020-07-24 09:11:03.012 6478-6545/com.geeksville.mesh D/com.geeksville.mesh.service.SafeBluetooth$BluetoothContinuation: Starting work: readC 8ba2bcc2-ee02-4a55-a531-c525c5e454d5
|
||||
2020-07-24 09:11:03.012 6478-6478/com.geeksville.mesh D/com.geeksville.mesh.service.MeshService: Received broadcast com.geeksville.mesh.CONNECT_CHANGED
|
||||
2020-07-24 09:11:03.012 6478-6478/com.geeksville.mesh D/com.geeksville.mesh.service.MeshService: onConnectionChanged=CONNECTED
|
||||
2020-07-24 09:11:03.012 6478-6478/com.geeksville.mesh D/com.geeksville.mesh.service.MeshService: Starting config nonce=6878
|
||||
2020-07-24 09:11:03.013 6478-6545/com.geeksville.mesh D/com.geeksville.mesh.service.BluetoothInterface: queuing 4 bytes to f75c76d2-129e-4dad-a1dd-7866124401e7 *** sending start config
|
||||
2020-07-24 09:11:03.013 6478-6545/com.geeksville.mesh D/com.geeksville.mesh.service.SafeBluetooth: Enqueuing work: writeC f75c76d2-129e-4dad-a1dd-7866124401e7
|
||||
2020-07-24 09:11:03.015 6478-6478/com.geeksville.mesh D/com.geeksville.mesh.service.MeshService: Received broadcast com.geeksville.mesh.CONNECT_CHANGED
|
||||
2020-07-24 09:11:03.015 6478-6478/com.geeksville.mesh D/com.geeksville.mesh.service.MeshService: onConnectionChanged=CONNECTED
|
||||
2020-07-24 09:11:03.015 6478-6478/com.geeksville.mesh D/com.geeksville.mesh.service.MeshService: Starting config nonce=6877
|
||||
2020-07-24 09:11:03.016 6478-6545/com.geeksville.mesh D/com.geeksville.mesh.service.MeshService: device sleep timeout cancelled
|
||||
2020-07-24 09:11:03.016 6478-6545/com.geeksville.mesh D/com.geeksville.mesh.service.BluetoothInterface: queuing 4 bytes to f75c76d2-129e-4dad-a1dd-7866124401e7
|
||||
2020-07-24 09:11:03.016 6478-6545/com.geeksville.mesh D/com.geeksville.mesh.service.SafeBluetooth: Enqueuing work: writeC f75c76d2-129e-4dad-a1dd-7866124401e7
|
||||
2020-07-24 09:11:03.130 6478-19966/com.geeksville.mesh D/com.geeksville.mesh.service.SafeBluetooth$BluetoothContinuation: Starting work: writeC f75c76d2-129e-4dad-a1dd-7866124401e7
|
||||
2020-07-24 09:11:03.132 6478-19966/com.geeksville.mesh D/com.geeksville.mesh.service.SafeBluetooth: work readC 8ba2bcc2-ee02-4a55-a531-c525c5e454d5 is completed, resuming status=0, res=android.bluetooth.BluetoothGattCharacteristic@556a315
|
||||
2020-07-24 09:11:03.132 6478-19966/com.geeksville.mesh D/com.geeksville.mesh.service.BluetoothInterface: Done reading from radio, fromradio is empty
|
||||
2020-07-24 09:11:03.132 6478-19966/com.geeksville.mesh D/com.geeksville.mesh.service.SafeBluetooth: starting setNotify(ed9da18c-a800-4f66-a670-aa7547e34453, true) *** start notify
|
||||
2020-07-24 09:11:03.132 6478-19966/com.geeksville.mesh D/BluetoothGatt: setCharacteristicNotification() - uuid: ed9da18c-a800-4f66-a670-aa7547e34453 enable: true
|
||||
2020-07-24 09:11:03.133 6478-19966/com.geeksville.mesh D/com.geeksville.mesh.service.SafeBluetooth: Enqueuing work: writeD
|
||||
2020-07-24 09:11:03.220 6478-19966/com.geeksville.mesh D/com.geeksville.mesh.service.SafeBluetooth$BluetoothContinuation: Starting work: writeC f75c76d2-129e-4dad-a1dd-7866124401e7
|
||||
2020-07-24 09:11:03.221 6478-19966/com.geeksville.mesh D/com.geeksville.mesh.service.SafeBluetooth: work writeC f75c76d2-129e-4dad-a1dd-7866124401e7 is completed, resuming status=0, res=android.bluetooth.BluetoothGattCharacteristic@2d2062a
|
||||
2020-07-24 09:11:03.221 6478-19966/com.geeksville.mesh D/com.geeksville.mesh.service.BluetoothInterface: write of 4 bytes completed
|
||||
2020-07-24 09:11:03.221 6478-19966/com.geeksville.mesh D/com.geeksville.mesh.service.SafeBluetooth: Enqueuing work: readC 8ba2bcc2-ee02-4a55-a531-c525c5e454d5
|
||||
2020-07-24 09:11:03.310 6478-19966/com.geeksville.mesh D/com.geeksville.mesh.service.SafeBluetooth$BluetoothContinuation: Starting work: writeD
|
||||
2020-07-24 09:11:03.311 6478-19966/com.geeksville.mesh D/com.geeksville.mesh.service.SafeBluetooth: work writeC f75c76d2-129e-4dad-a1dd-7866124401e7 is completed, resuming status=0, res=android.bluetooth.BluetoothGattCharacteristic@2d2062a
|
||||
2020-07-24 09:11:03.311 6478-19966/com.geeksville.mesh D/com.geeksville.mesh.service.BluetoothInterface: write of 4 bytes completed
|
||||
2020-07-24 09:11:03.400 6478-19966/com.geeksville.mesh D/com.geeksville.mesh.service.SafeBluetooth$BluetoothContinuation: Starting work: readC 8ba2bcc2-ee02-4a55-a531-c525c5e454d5
|
||||
2020-07-24 09:11:03.402 6478-19966/com.geeksville.mesh D/com.geeksville.mesh.service.SafeBluetooth: work writeD is completed, resuming status=0, res=android.bluetooth.BluetoothGattDescriptor@fc99c1b
|
||||
2020-07-24 09:11:03.402 6478-19966/com.geeksville.mesh D/com.geeksville.mesh.service.SafeBluetooth: Notify enable=true completed
|
||||
2020-07-24 09:11:03.769 6478-19966/com.geeksville.mesh D/com.geeksville.mesh.service.SafeBluetooth: work readC 8ba2bcc2-ee02-4a55-a531-c525c5e454d5 is completed, resuming status=0, res=android.bluetooth.BluetoothGattCharacteristic@556a315
|
||||
2020-07-24 09:11:03.769 6478-19966/com.geeksville.mesh D/com.geeksville.mesh.service.BluetoothInterface: Received 80 bytes from radio **** received an 80 byte fromradio. Why did we miss three previous reads?
|
||||
2020-07-24 09:11:03.774 6478-19966/com.geeksville.mesh D/com.geeksville.mesh.service.SafeBluetooth: Enqueuing work: readC 8ba2bcc2-ee02-4a55-a531-c525c5e454d5
|
||||
2020-07-24 09:11:03.774 6478-19966/com.geeksville.mesh D/com.geeksville.mesh.service.SafeBluetooth$BluetoothContinuation: Starting work: readC 8ba2bcc2-ee02-4a55-a531-c525c5e454d5
|
||||
2020-07-24 09:11:03.774 6478-6478/com.geeksville.mesh D/com.geeksville.mesh.service.MeshService: Received broadcast com.geeksville.mesh.RECEIVE_FROMRADIO
|
||||
2020-07-24 09:11:03.776 6478-6478/com.geeksville.mesh E/com.geeksville.mesh.service.MeshService: Invalid Protobuf from radio, len=80 (exception Protocol message had invalid UTF-8.)
|
||||
2020-07-24 09:11:03.776 6478-6478/com.geeksville.mesh D/com.geeksville.mesh.service.MeshService: Received broadcast com.geeksville.mesh.RECEIVE_FROMRADIO
|
||||
2020-07-24 09:11:03.776 6478-6478/com.geeksville.mesh E/com.geeksville.mesh.service.MeshService: Invalid Protobuf from radio, len=80 (exception Protocol message had invalid UTF-8.)
|
||||
2020-07-24 09:11:04.031 6478-19966/com.geeksville.mesh D/com.geeksville.mesh.service.SafeBluetooth: work readC 8ba2bcc2-ee02-4a55-a531-c525c5e454d5 is completed, resuming status=0, res=android.bluetooth.BluetoothGattCharacteristic@556a315
|
||||
2020-07-24 09:11:04.031 6478-19966/com.geeksville.mesh D/com.geeksville.mesh.service.BluetoothInterface: Received 52 bytes from radio *** received 52 bytes - where did the previous two read results go?
|
||||
2020-07-24 09:11:04.033 6478-19966/com.geeksville.mesh D/com.geeksville.mesh.service.SafeBluetooth: Enqueuing work: readC 8ba2bcc2-ee02-4a55-a531-c525c5e454d5
|
||||
2020-07-24 09:11:04.033 6478-19966/com.geeksville.mesh D/com.geeksville.mesh.service.SafeBluetooth$BluetoothContinuation: Starting work: readC 8ba2bcc2-ee02-4a55-a531-c525c5e454d5
|
||||
2020-07-24 09:11:04.034 6478-6478/com.geeksville.mesh D/com.geeksville.mesh.service.MeshService: Received broadcast com.geeksville.mesh.RECEIVE_FROMRADIO
|
||||
2020-07-24 09:11:04.035 6478-6478/com.geeksville.mesh E/com.geeksville.mesh.service.MeshService: Invalid Protobuf from radio, len=52 (exception While parsing a protocol message, the input ended unexpectedly in the middle of a field. This could mean either that the input has been truncated or that an embedded message misreported its own length.)
|
||||
2020-07-24 09:11:04.036 6478-6478/com.geeksville.mesh D/com.geeksville.mesh.service.MeshService: Received broadcast com.geeksville.mesh.RECEIVE_FROMRADIO
|
||||
2020-07-24 09:11:04.036 6478-6478/com.geeksville.mesh E/com.geeksville.mesh.service.MeshService: Invalid Protobuf from radio, len=52 (exception While parsing a protocol message, the input ended unexpectedly in the middle of a field. This could mean either that the input has been truncated or that an embedded message misreported its own length.)
|
||||
2020-07-24 09:11:04.210 6478-19966/com.geeksville.mesh D/com.geeksville.mesh.service.SafeBluetooth: work readC 8ba2bcc2-ee02-4a55-a531-c525c5e454d5 is completed, resuming status=7, res=android.bluetooth.BluetoothGattCharacteristic@556a315 *** read failed
|
||||
2020-07-24 09:11:04.211 6478-19966/com.geeksville.mesh W/com.geeksville.mesh.service.BluetoothInterface: Scheduling reconnect because error during doReadFromRadio - disconnecting, Bluetooth status=7 while doing readC 8ba2bcc2-ee02-4a55-a531-c525c5e454d5
|
||||
2020-07-24 09:11:04.211 6478-6545/com.geeksville.mesh W/com.geeksville.mesh.service.BluetoothInterface: Forcing disconnect and hopefully device will comeback (disabling forced refresh)
|
||||
2020-07-24 09:11:04.211 6478-6545/com.geeksville.mesh I/com.geeksville.mesh.service.SafeBluetooth: Closing our GATT connection
|
||||
2020-07-24 09:11:04.211 6478-6545/com.geeksville.mesh D/BluetoothGatt: cancelOpen() - device: 24:62:AB:DD:DF:3A
|
||||
2020-07-24 09:11:04.214 6478-19966/com.geeksville.mesh D/BluetoothGatt: onClientConnectionState() - status=0 clientIf=4 device=24:62:AB:DD:DF:3A
|
||||
2020-07-24 09:11:04.215 6478-19966/com.geeksville.mesh I/com.geeksville.mesh.service.SafeBluetooth: new bluetooth connection state 0, status 0
|
||||
2020-07-24 09:11:04.215 6478-19966/com.geeksville.mesh I/com.geeksville.mesh.service.SafeBluetooth: Got disconnect because we are shutting down, closing gatt
|
||||
2020-07-24 09:11:04.215 6478-19966/com.geeksville.mesh D/BluetoothGatt: close()
|
||||
BIN
images/amazon-fire-button.png
Normal file
BIN
images/amazon-fire-button.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 12 KiB |
107
platformio.ini
107
platformio.ini
@@ -9,7 +9,7 @@
|
||||
; https://docs.platformio.org/page/projectconf.html
|
||||
|
||||
[platformio]
|
||||
default_envs = tbeam ; Note: the github actions CI test build can't yet build NRF52 targets
|
||||
default_envs = tbeam # lora-relay-v1
|
||||
|
||||
[common]
|
||||
; common is not currently used
|
||||
@@ -23,6 +23,8 @@ default_envs = tbeam ; Note: the github actions CI test build can't yet build NR
|
||||
|
||||
[env]
|
||||
|
||||
framework = arduino
|
||||
|
||||
; customize the partition table
|
||||
; http://docs.platformio.org/en/latest/platforms/espressif32.html#partition-tables
|
||||
board_build.partitions = partition-table.csv
|
||||
@@ -30,18 +32,10 @@ board_build.partitions = partition-table.csv
|
||||
; note: we add src to our include search path so that lmic_project_config can override
|
||||
; FIXME: fix lib/BluetoothOTA dependency back on src/ so we can remove -Isrc
|
||||
build_flags = -Wno-missing-field-initializers -Isrc -Isrc/mesh -Isrc/gps -Ilib/nanopb/include -Os -Wl,-Map,.pio/build/output.map
|
||||
-DAXP_DEBUG_PORT=Serial
|
||||
-DHW_VERSION_${sysenv.COUNTRY}
|
||||
-DAPP_VERSION=${sysenv.APP_VERSION}
|
||||
-DHW_VERSION=${sysenv.HW_VERSION}
|
||||
|
||||
; not needed included in ttgo-t-beam board file
|
||||
; also to use PSRAM https://docs.platformio.org/en/latest/platforms/espressif32.html#external-ram-psram
|
||||
; -DBOARD_HAS_PSRAM
|
||||
; -mfix-esp32-psram-cache-issue
|
||||
|
||||
; -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG -DCORE_DEBUG_LEVEL=ARDUHAL_LOG_LEVEL_DEBUG
|
||||
|
||||
; leave this commented out to avoid breaking Windows
|
||||
;upload_port = /dev/ttyUSB0
|
||||
;monitor_port = /dev/ttyUSB0
|
||||
@@ -72,24 +66,33 @@ lib_deps =
|
||||
Wire ; explicitly needed here because the AXP202 library forgets to add it
|
||||
https://github.com/meshtastic/arduino-fsm.git
|
||||
https://github.com/meshtastic/SparkFun_Ublox_Arduino_Library.git
|
||||
https://github.com/meshtastic/RadioLib.git
|
||||
https://github.com/meshtastic/RadioLib.git#d6b12f7eb0a06bd2414c79b437b25d377e3f603f
|
||||
https://github.com/meshtastic/TinyGPSPlus.git
|
||||
|
||||
https://github.com/meshtastic/AXP202X_Library.git#8404abb6d4b486748636bc6ad72d2a47baaf5460
|
||||
|
||||
; Common settings for ESP targes, mixin with extends = esp32_base
|
||||
[esp32_base]
|
||||
platform = espressif32
|
||||
framework = arduino
|
||||
src_filter =
|
||||
${env.src_filter} -<nrf52/>
|
||||
upload_speed = 921600
|
||||
debug_init_break = tbreak setup
|
||||
build_flags =
|
||||
${env.build_flags} -Wall -Wextra -Isrc/esp32 -mfix-esp32-psram-cache-issue
|
||||
${env.build_flags} -Wall -Wextra -Isrc/esp32 -mfix-esp32-psram-cache-issue -lnimble -std=c++11
|
||||
-DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG -DCORE_DEBUG_LEVEL=ARDUHAL_LOG_LEVEL_DEBUG
|
||||
-DAXP_DEBUG_PORT=Serial
|
||||
# Hmm - this doesn't work yet
|
||||
# board_build.ldscript = linker/esp32.extram.bss.ld
|
||||
lib_ignore = segger_rtt
|
||||
platform_packages =
|
||||
framework-arduinoespressif32 @ https://github.com/meshtastic/arduino-esp32.git#f26c4f96fefd13ed0ed042e27954f8aba6328f6b
|
||||
framework-arduinoespressif32 @ https://github.com/meshtastic/arduino-esp32.git#1adba3f11ca8406ac0a704d151697b572058b53d
|
||||
|
||||
; not needed included in ttgo-t-beam board file
|
||||
; also to use PSRAM https://docs.platformio.org/en/latest/platforms/espressif32.html#external-ram-psram
|
||||
; -DBOARD_HAS_PSRAM
|
||||
; -mfix-esp32-psram-cache-issue
|
||||
|
||||
; -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG -DCORE_DEBUG_LEVEL=ARDUHAL_LOG_LEVEL_DEBUG
|
||||
|
||||
; The 1.0 release of the TBEAM board
|
||||
[env:tbeam]
|
||||
@@ -97,43 +100,46 @@ extends = esp32_base
|
||||
board = ttgo-t-beam
|
||||
lib_deps =
|
||||
${env.lib_deps}
|
||||
https://github.com/meshtastic/AXP202X_Library.git
|
||||
|
||||
build_flags =
|
||||
${esp32_base.build_flags} -D TBEAM_V10
|
||||
|
||||
; The original TBEAM board without the AXP power chip and a few other changes
|
||||
; Note: I've heard reports this didn't work. Disabled until someone with a 0.7 can test and debug.
|
||||
;[env:tbeam0.7]
|
||||
;extends = esp32_base
|
||||
;board = ttgo-t-beam
|
||||
;build_flags =
|
||||
; ${esp32_base.build_flags} -D TBEAM_V07
|
||||
[env:tbeam0.7]
|
||||
extends = esp32_base
|
||||
board = ttgo-t-beam
|
||||
build_flags =
|
||||
${esp32_base.build_flags} -D TBEAM_V07
|
||||
|
||||
[env:heltec]
|
||||
;build_type = debug ; to make it possible to step through our jtag debugger
|
||||
extends = esp32_base
|
||||
board = heltec_wifi_lora_32_V2
|
||||
|
||||
[env:ttgo-lora32-v1]
|
||||
[env:tlora-v1]
|
||||
extends = esp32_base
|
||||
board = ttgo-lora32-v1
|
||||
build_flags =
|
||||
${esp32_base.build_flags} -D TTGO_LORA_V1
|
||||
${esp32_base.build_flags} -D TLORA_V1
|
||||
|
||||
; note: the platformio definition for lora32-v2 seems stale, it is missing a pins_arduino.h file, therefore I don't think it works
|
||||
[env:ttgo-lora32-v2]
|
||||
[env:tlora-v2]
|
||||
extends = esp32_base
|
||||
board = ttgo-lora32-v1
|
||||
build_flags =
|
||||
${esp32_base.build_flags} -D TTGO_LORA_V2
|
||||
${esp32_base.build_flags} -D TLORA_V2
|
||||
|
||||
[env:tlora-v2-1-1.6]
|
||||
extends = esp32_base
|
||||
board = ttgo-lora32-v1
|
||||
build_flags =
|
||||
${esp32_base.build_flags} -D TLORA_V2_1_16
|
||||
|
||||
; The Heltec Cubecell plus
|
||||
; IMPORTANT NOTE: This target doesn't yet work and probably won't ever work. I'm keeping it around for now.
|
||||
; For more details see my post in the forum.
|
||||
[env:cubecellplus]
|
||||
platform = https://github.com/HelTecAutomation/platform-asrmicro650x.git ; we use top-of-tree because stable version has too many bugs - asrmicro650x
|
||||
framework = arduino
|
||||
board = cubecell_board_plus
|
||||
; FIXME, bug in cubecell arduino - they are supposed to set ARDUINO
|
||||
build_flags = ${env.build_flags} -DARDUINO=100 -Isrc/cubecell
|
||||
@@ -142,16 +148,19 @@ src_filter =
|
||||
|
||||
; Common settings for NRF52 based targets
|
||||
[nrf52_base]
|
||||
platform = nordicnrf52
|
||||
framework = arduino
|
||||
; Instead of the standard nordicnrf52 platform, we use our fork which has our added variant files
|
||||
; platform = nordicnrf52
|
||||
platform = https://github.com/meshtastic/platform-nordicnrf52.git#62d185fe61b6c84c554046106529b4fd8f155e2c
|
||||
debug_tool = jlink
|
||||
build_type = debug ; I'm debugging with ICE a lot now
|
||||
; note: liboberon provides the AES256 implementation for NRF52 (though not using the hardware acceleration of the NRF52840 - FIXME)
|
||||
build_flags =
|
||||
${env.build_flags} -Wno-unused-variable -Isrc/nrf52 -Isdk-nrfxlib/crypto/nrf_oberon/include -Lsdk-nrfxlib/crypto/nrf_oberon/lib/cortex-m4/hard-float/ -lliboberon_3.0.3
|
||||
${env.build_flags} -Wno-unused-variable
|
||||
-Isrc/nrf52
|
||||
-Isdk-nrfxlib/crypto/nrf_oberon/include -Lsdk-nrfxlib/crypto/nrf_oberon/lib/cortex-m4/hard-float/ -lliboberon_3.0.3
|
||||
;-DCFG_DEBUG=3
|
||||
src_filter =
|
||||
${env.src_filter} -<esp32/>
|
||||
${env.src_filter} -<esp32/> -<nimble/>
|
||||
lib_ignore =
|
||||
BluetoothOTA
|
||||
monitor_port = /dev/ttyACM1
|
||||
@@ -168,9 +177,32 @@ debug_init_break =
|
||||
;debug_init_break = tbreak Reset_Handler
|
||||
|
||||
; The NRF52840-dk development board
|
||||
[env:nrf52dk]
|
||||
; Note: By default no lora device is created for this build - it uses a simulated interface
|
||||
[env:nrf52840dk]
|
||||
extends = nrf52_base
|
||||
board = nrf52840_dk
|
||||
|
||||
; The NRF52840-dk development board, but @geeksville's board - which has a busted oscilliator
|
||||
[env:nrf52840dk-geeksville]
|
||||
extends = nrf52_base
|
||||
board = nrf52840_dk_modified
|
||||
# add our variants files to the include and src paths
|
||||
build_flags = ${nrf52_base.build_flags} -Ivariants/pca10056-rc-clock
|
||||
src_filter = ${nrf52_base.src_filter} +<../variants/pca10056-rc-clock>
|
||||
|
||||
; Note: By default no lora device is created for this build - it uses a simulated interface
|
||||
[env:feather_nrf52832]
|
||||
extends = nrf52_base
|
||||
board = adafruit_feather_nrf52832
|
||||
|
||||
[env:rak815]
|
||||
extends = nrf52_base
|
||||
board = rak815
|
||||
debug_tool = jlink
|
||||
upload_protocol = jlink
|
||||
monitor_port = /dev/ttyUSB0
|
||||
; this board's serial chip can only run at 115200, not faster
|
||||
monitor_speed = 115200
|
||||
|
||||
# For experimenting with RAM sizes
|
||||
# board_build.ldscript = linker/nrf52840_s140_sim832.ld
|
||||
@@ -183,6 +215,19 @@ lib_deps =
|
||||
${env.lib_deps}
|
||||
UC1701
|
||||
|
||||
; The https://github.com/BigCorvus/SX1262-LoRa-BLE-Relay board by @BigCorvus
|
||||
[env:lora-relay-v1]
|
||||
extends = nrf52_base
|
||||
board = lora-relay-v1
|
||||
# add our variants files to the include and src paths
|
||||
build_flags = ${nrf52_base.build_flags} -Ivariants/lora_relay_v1
|
||||
src_filter = ${nrf52_base.src_filter} +<../variants/lora_relay_v1>
|
||||
lib_deps =
|
||||
${env.lib_deps}
|
||||
SparkFun BQ27441 LiPo Fuel Gauge Arduino Library
|
||||
TFT_eSPI
|
||||
# Adafruit ST7735 and ST7789 Library
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
2
proto
2
proto
Submodule proto updated: ab281311c4...3caee2e5b9
3
release/.gitignore
vendored
3
release/.gitignore
vendored
@@ -1,4 +1,5 @@
|
||||
*.elf
|
||||
*.bin
|
||||
*.map
|
||||
*.zip
|
||||
*.zip
|
||||
*.uf2
|
||||
|
||||
12
src/BluetoothCommon.cpp
Normal file
12
src/BluetoothCommon.cpp
Normal file
@@ -0,0 +1,12 @@
|
||||
#include "BluetoothCommon.h"
|
||||
|
||||
// NRF52 wants these constants as byte arrays
|
||||
// Generated here https://yupana-engineering.com/online-uuid-to-c-array-converter - but in REVERSE BYTE ORDER
|
||||
const uint8_t MESH_SERVICE_UUID_16[16u] = {0xfd, 0xea, 0x73, 0xe2, 0xca, 0x5d, 0xa8, 0x9f,
|
||||
0x1f, 0x46, 0xa8, 0x15, 0x18, 0xb2, 0xa1, 0x6b};
|
||||
const uint8_t TORADIO_UUID_16[16u] = {0xe7, 0x01, 0x44, 0x12, 0x66, 0x78, 0xdd, 0xa1,
|
||||
0xad, 0x4d, 0x9e, 0x12, 0xd2, 0x76, 0x5c, 0xf7};
|
||||
const uint8_t FROMRADIO_UUID_16[16u] = {0xd5, 0x54, 0xe4, 0xc5, 0x25, 0xc5, 0x31, 0xa5,
|
||||
0x55, 0x4a, 0x02, 0xee, 0xc2, 0xbc, 0xa2, 0x8b};
|
||||
const uint8_t FROMNUM_UUID_16[16u] = {0x53, 0x44, 0xe3, 0x47, 0x75, 0xaa, 0x70, 0xa6,
|
||||
0x66, 0x4f, 0x00, 0xa8, 0x8c, 0xa1, 0x9d, 0xed};
|
||||
20
src/BluetoothCommon.h
Normal file
20
src/BluetoothCommon.h
Normal file
@@ -0,0 +1,20 @@
|
||||
#pragma once
|
||||
|
||||
#include <Arduino.h>
|
||||
|
||||
/**
|
||||
* Common lib functions for all platforms that have bluetooth
|
||||
*/
|
||||
|
||||
#define MESH_SERVICE_UUID "6ba1b218-15a8-461f-9fa8-5dcae273eafd"
|
||||
|
||||
#define TORADIO_UUID "f75c76d2-129e-4dad-a1dd-7866124401e7"
|
||||
#define FROMRADIO_UUID "8ba2bcc2-ee02-4a55-a531-c525c5e454d5"
|
||||
#define FROMNUM_UUID "ed9da18c-a800-4f66-a670-aa7547e34453"
|
||||
|
||||
// NRF52 wants these constants as byte arrays
|
||||
// Generated here https://yupana-engineering.com/online-uuid-to-c-array-converter - but in REVERSE BYTE ORDER
|
||||
extern const uint8_t MESH_SERVICE_UUID_16[], TORADIO_UUID_16[16u], FROMRADIO_UUID_16[], FROMNUM_UUID_16[];
|
||||
|
||||
/// Given a level between 0-100, update the BLE attribute
|
||||
void updateBatteryLevel(uint8_t level);
|
||||
126
src/GPSStatus.h
Normal file
126
src/GPSStatus.h
Normal file
@@ -0,0 +1,126 @@
|
||||
#pragma once
|
||||
#include <Arduino.h>
|
||||
#include "Status.h"
|
||||
#include "configuration.h"
|
||||
|
||||
namespace meshtastic {
|
||||
|
||||
/// Describes the state of the GPS system.
|
||||
class GPSStatus : public Status
|
||||
{
|
||||
|
||||
private:
|
||||
CallbackObserver<GPSStatus, const GPSStatus *> statusObserver = CallbackObserver<GPSStatus, const GPSStatus *>(this, &GPSStatus::updateStatus);
|
||||
|
||||
bool hasLock = false; // default to false, until we complete our first read
|
||||
bool isConnected = false; // Do we have a GPS we are talking to
|
||||
int32_t latitude = 0, longitude = 0; // as an int mult by 1e-7 to get value as double
|
||||
int32_t altitude = 0;
|
||||
uint32_t dop = 0; // Diminution of position; PDOP where possible (UBlox), HDOP otherwise (TinyGPS) in 10^2 units (needs scaling before use)
|
||||
uint32_t heading = 0;
|
||||
uint32_t numSatellites = 0;
|
||||
|
||||
public:
|
||||
|
||||
GPSStatus() {
|
||||
statusType = STATUS_TYPE_GPS;
|
||||
}
|
||||
GPSStatus( bool hasLock, bool isConnected, int32_t latitude, int32_t longitude, int32_t altitude, uint32_t dop, uint32_t heading, uint32_t numSatellites ) : Status()
|
||||
{
|
||||
this->hasLock = hasLock;
|
||||
this->isConnected = isConnected;
|
||||
this->latitude = latitude;
|
||||
this->longitude = longitude;
|
||||
this->altitude = altitude;
|
||||
this->dop = dop;
|
||||
this->heading = heading;
|
||||
this->numSatellites = numSatellites;
|
||||
}
|
||||
GPSStatus(const GPSStatus &);
|
||||
GPSStatus &operator=(const GPSStatus &);
|
||||
|
||||
void observe(Observable<const GPSStatus *> *source)
|
||||
{
|
||||
statusObserver.observe(source);
|
||||
}
|
||||
|
||||
bool getHasLock() const
|
||||
{
|
||||
return hasLock;
|
||||
}
|
||||
|
||||
bool getIsConnected() const
|
||||
{
|
||||
return isConnected;
|
||||
}
|
||||
|
||||
int32_t getLatitude() const
|
||||
{
|
||||
return latitude;
|
||||
}
|
||||
|
||||
int32_t getLongitude() const
|
||||
{
|
||||
return longitude;
|
||||
}
|
||||
|
||||
int32_t getAltitude() const
|
||||
{
|
||||
return altitude;
|
||||
}
|
||||
|
||||
uint32_t getDOP() const
|
||||
{
|
||||
return dop;
|
||||
}
|
||||
|
||||
uint32_t getHeading() const
|
||||
{
|
||||
return heading;
|
||||
}
|
||||
|
||||
uint32_t getNumSatellites() const
|
||||
{
|
||||
return numSatellites;
|
||||
}
|
||||
|
||||
bool matches(const GPSStatus *newStatus) const
|
||||
{
|
||||
return (
|
||||
newStatus->hasLock != hasLock ||
|
||||
newStatus->isConnected != isConnected ||
|
||||
newStatus->latitude != latitude ||
|
||||
newStatus->longitude != longitude ||
|
||||
newStatus->altitude != altitude ||
|
||||
newStatus->dop != dop ||
|
||||
newStatus->heading != heading ||
|
||||
newStatus->numSatellites != numSatellites
|
||||
);
|
||||
}
|
||||
int updateStatus(const GPSStatus *newStatus) {
|
||||
// Only update the status if values have actually changed
|
||||
bool isDirty;
|
||||
{
|
||||
isDirty = matches(newStatus);
|
||||
initialized = true;
|
||||
hasLock = newStatus->hasLock;
|
||||
isConnected = newStatus->isConnected;
|
||||
latitude = newStatus->latitude;
|
||||
longitude = newStatus->longitude;
|
||||
altitude = newStatus->altitude;
|
||||
dop = newStatus->dop;
|
||||
heading = newStatus->heading;
|
||||
numSatellites = newStatus->numSatellites;
|
||||
}
|
||||
if(isDirty) {
|
||||
DEBUG_MSG("New GPS pos lat=%f, lon=%f, alt=%d, pdop=%f, heading=%f, sats=%d\n", latitude * 1e-7, longitude * 1e-7, altitude, dop * 1e-2, heading * 1e-5, numSatellites);
|
||||
onNewStatus.notifyObservers(this);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
extern meshtastic::GPSStatus *gpsStatus;
|
||||
83
src/NodeStatus.h
Normal file
83
src/NodeStatus.h
Normal file
@@ -0,0 +1,83 @@
|
||||
#pragma once
|
||||
#include <Arduino.h>
|
||||
#include "Status.h"
|
||||
#include "configuration.h"
|
||||
|
||||
namespace meshtastic {
|
||||
|
||||
/// Describes the state of the NodeDB system.
|
||||
class NodeStatus : public Status
|
||||
{
|
||||
|
||||
private:
|
||||
CallbackObserver<NodeStatus, const NodeStatus *> statusObserver = CallbackObserver<NodeStatus, const NodeStatus *>(this, &NodeStatus::updateStatus);
|
||||
|
||||
uint8_t numOnline = 0;
|
||||
uint8_t numTotal = 0;
|
||||
|
||||
uint8_t lastNumTotal = 0;
|
||||
|
||||
public:
|
||||
bool forceUpdate = false;
|
||||
|
||||
NodeStatus() {
|
||||
statusType = STATUS_TYPE_NODE;
|
||||
}
|
||||
NodeStatus( uint8_t numOnline, uint8_t numTotal, bool forceUpdate = false ) : Status()
|
||||
{
|
||||
this->forceUpdate = forceUpdate;
|
||||
this->numOnline = numOnline;
|
||||
this->numTotal = numTotal;
|
||||
}
|
||||
NodeStatus(const NodeStatus &);
|
||||
NodeStatus &operator=(const NodeStatus &);
|
||||
|
||||
void observe(Observable<const NodeStatus *> *source)
|
||||
{
|
||||
statusObserver.observe(source);
|
||||
}
|
||||
|
||||
uint8_t getNumOnline() const
|
||||
{
|
||||
return numOnline;
|
||||
}
|
||||
|
||||
uint8_t getNumTotal() const
|
||||
{
|
||||
return numTotal;
|
||||
}
|
||||
|
||||
uint8_t getLastNumTotal() const
|
||||
{
|
||||
return lastNumTotal;
|
||||
}
|
||||
|
||||
bool matches(const NodeStatus *newStatus) const
|
||||
{
|
||||
return (
|
||||
newStatus->getNumOnline() != numOnline ||
|
||||
newStatus->getNumTotal() != numTotal
|
||||
);
|
||||
}
|
||||
int updateStatus(const NodeStatus *newStatus) {
|
||||
// Only update the status if values have actually changed
|
||||
lastNumTotal = numTotal;
|
||||
bool isDirty;
|
||||
{
|
||||
isDirty = matches(newStatus);
|
||||
initialized = true;
|
||||
numOnline = newStatus->getNumOnline();
|
||||
numTotal = newStatus->getNumTotal();
|
||||
}
|
||||
if(isDirty || newStatus->forceUpdate) {
|
||||
DEBUG_MSG("Node status update: %d online, %d total\n", numOnline, numTotal);
|
||||
onNewStatus.notifyObservers(this);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
extern meshtastic::NodeStatus *nodeStatus;
|
||||
@@ -1,43 +0,0 @@
|
||||
#include "PeriodicTask.h"
|
||||
#include "Periodic.h"
|
||||
PeriodicScheduler periodicScheduler;
|
||||
|
||||
PeriodicTask::PeriodicTask(uint32_t initialPeriod) : period(initialPeriod) {}
|
||||
|
||||
void PeriodicTask::setup()
|
||||
{
|
||||
periodicScheduler.schedule(this);
|
||||
}
|
||||
|
||||
/// call this from loop
|
||||
void PeriodicScheduler::loop()
|
||||
{
|
||||
meshtastic::LockGuard lg(&lock);
|
||||
|
||||
uint32_t now = millis();
|
||||
for (auto t : tasks) {
|
||||
if (t->period && (now - t->lastMsec) >= t->period) {
|
||||
|
||||
t->doTask();
|
||||
t->lastMsec = now;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void PeriodicScheduler::schedule(PeriodicTask *t)
|
||||
{
|
||||
meshtastic::LockGuard lg(&lock);
|
||||
tasks.insert(t);
|
||||
}
|
||||
|
||||
void PeriodicScheduler::unschedule(PeriodicTask *t)
|
||||
{
|
||||
meshtastic::LockGuard lg(&lock);
|
||||
tasks.erase(t);
|
||||
}
|
||||
|
||||
void Periodic::doTask()
|
||||
{
|
||||
uint32_t p = callback();
|
||||
setPeriod(p);
|
||||
}
|
||||
@@ -1,85 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "lock.h"
|
||||
#include <Arduino.h>
|
||||
#include <cstdint>
|
||||
#include <unordered_set>
|
||||
|
||||
class PeriodicTask;
|
||||
|
||||
/**
|
||||
* Runs all PeriodicTasks in the system.
|
||||
*
|
||||
* Currently called from main loop() but eventually should be its own thread blocked on a freertos timer.
|
||||
*/
|
||||
class PeriodicScheduler
|
||||
{
|
||||
friend class PeriodicTask;
|
||||
|
||||
/**
|
||||
* This really should be some form of heap, and when the period gets changed on a task it should get
|
||||
* rescheduled in that heap. Currently it is just a dumb array and everytime we run loop() we check
|
||||
* _every_ tasks. If it was a heap we'd only have to check the first task.
|
||||
*/
|
||||
std::unordered_set<PeriodicTask *> tasks;
|
||||
|
||||
// Protects the above variables.
|
||||
meshtastic::Lock lock;
|
||||
|
||||
public:
|
||||
/// Run any next tasks which are due for execution
|
||||
void loop();
|
||||
|
||||
private:
|
||||
void schedule(PeriodicTask *t);
|
||||
void unschedule(PeriodicTask *t);
|
||||
};
|
||||
|
||||
extern PeriodicScheduler periodicScheduler;
|
||||
|
||||
/**
|
||||
* A base class for tasks that want their doTask() method invoked periodically
|
||||
*
|
||||
* FIXME: currently just syntatic sugar for polling in loop (you must call .loop), but eventually
|
||||
* generalize with the freertos scheduler so we can save lots of power by having everything either in
|
||||
* something like this or triggered off of an irq.
|
||||
*/
|
||||
class PeriodicTask
|
||||
{
|
||||
friend class PeriodicScheduler;
|
||||
|
||||
uint32_t lastMsec = 0;
|
||||
uint32_t period = 1; // call soon after creation
|
||||
|
||||
public:
|
||||
virtual ~PeriodicTask() { periodicScheduler.unschedule(this); }
|
||||
|
||||
/**
|
||||
* Constructor (will schedule with the global PeriodicScheduler)
|
||||
*/
|
||||
PeriodicTask(uint32_t initialPeriod = 1);
|
||||
|
||||
/** MUST be be called once at startup (but after threading is running - i.e. not from a constructor)
|
||||
*/
|
||||
void setup();
|
||||
|
||||
/**
|
||||
* Set a new period in msecs (can be called from doTask or elsewhere and the scheduler will cope)
|
||||
* While zero this task is disabled and will not run
|
||||
*/
|
||||
void setPeriod(uint32_t p)
|
||||
{
|
||||
lastMsec = millis(); // reset starting from now
|
||||
period = p;
|
||||
}
|
||||
|
||||
uint32_t getPeriod() const { return period; }
|
||||
|
||||
/**
|
||||
* Syntatic sugar for suspending tasks
|
||||
*/
|
||||
void disable() { setPeriod(0); }
|
||||
|
||||
protected:
|
||||
virtual void doTask() = 0;
|
||||
};
|
||||
252
src/Power.cpp
Normal file
252
src/Power.cpp
Normal file
@@ -0,0 +1,252 @@
|
||||
#include "power.h"
|
||||
#include "PowerFSM.h"
|
||||
#include "main.h"
|
||||
#include "sleep.h"
|
||||
#include "utils.h"
|
||||
|
||||
// FIXME. nasty hack cleanup how we load axp192
|
||||
#undef AXP192_SLAVE_ADDRESS
|
||||
#include "axp20x.h"
|
||||
|
||||
#ifdef TBEAM_V10
|
||||
AXP20X_Class axp;
|
||||
#endif
|
||||
|
||||
bool pmu_irq = false;
|
||||
|
||||
Power *power;
|
||||
|
||||
/**
|
||||
* If this board has a battery level sensor, set this to a valid implementation
|
||||
*/
|
||||
static HasBatteryLevel *batteryLevel; // Default to NULL for no battery level sensor
|
||||
|
||||
/**
|
||||
* A simple battery level sensor that assumes the battery voltage is attached via a voltage-divider to an analog input
|
||||
*/
|
||||
class AnalogBatteryLevel : public HasBatteryLevel
|
||||
{
|
||||
/**
|
||||
* Battery state of charge, from 0 to 100 or -1 for unknown
|
||||
*
|
||||
* FIXME - use a lipo lookup table, the current % full is super wrong
|
||||
*/
|
||||
virtual int getBattPercentage()
|
||||
{
|
||||
float v = getBattVoltage() / 1000;
|
||||
|
||||
if (v < 2.1)
|
||||
return -1;
|
||||
|
||||
return 100 * (v - 3.27) / (4.2 - 3.27);
|
||||
}
|
||||
|
||||
/**
|
||||
* The raw voltage of the batteryin millivolts or NAN if unknown
|
||||
*/
|
||||
virtual float getBattVoltage()
|
||||
{
|
||||
return
|
||||
#ifdef BATTERY_PIN
|
||||
1000.0 * analogRead(BATTERY_PIN) * 2.0 * (3.3 / 1024.0);
|
||||
#else
|
||||
NAN;
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* return true if there is a battery installed in this unit
|
||||
*/
|
||||
virtual bool isBatteryConnect() { return true; }
|
||||
} analogLevel;
|
||||
|
||||
bool Power::analogInit()
|
||||
{
|
||||
#ifdef BATTERY_PIN
|
||||
DEBUG_MSG("Using analog input for battery level\n");
|
||||
adcAttachPin(BATTERY_PIN);
|
||||
// adcStart(BATTERY_PIN);
|
||||
analogReadResolution(10); // Default of 12 is not very linear. Recommended to use 10 or 11 depending on needed resolution.
|
||||
batteryLevel = &analogLevel;
|
||||
return true;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool Power::setup()
|
||||
{
|
||||
bool found = axp192Init();
|
||||
|
||||
if (!found) {
|
||||
found = analogInit();
|
||||
}
|
||||
if (found) {
|
||||
concurrency::PeriodicTask::setup(); // We don't start our periodic task unless we actually found the device
|
||||
setPeriod(1);
|
||||
}
|
||||
|
||||
return found;
|
||||
}
|
||||
|
||||
/// Reads power status to powerStatus singleton.
|
||||
//
|
||||
// TODO(girts): move this and other axp stuff to power.h/power.cpp.
|
||||
void Power::readPowerStatus()
|
||||
{
|
||||
if (batteryLevel) {
|
||||
bool hasBattery = batteryLevel->isBatteryConnect();
|
||||
int batteryVoltageMv = 0;
|
||||
uint8_t batteryChargePercent = 0;
|
||||
if (hasBattery) {
|
||||
batteryVoltageMv = batteryLevel->getBattVoltage();
|
||||
// If the AXP192 returns a valid battery percentage, use it
|
||||
if (batteryLevel->getBattPercentage() >= 0) {
|
||||
batteryChargePercent = batteryLevel->getBattPercentage();
|
||||
} else {
|
||||
// If the AXP192 returns a percentage less than 0, the feature is either not supported or there is an error
|
||||
// In that case, we compute an estimate of the charge percent based on maximum and minimum voltages defined in
|
||||
// power.h
|
||||
batteryChargePercent =
|
||||
clamp((int)(((batteryVoltageMv - BAT_MILLIVOLTS_EMPTY) * 1e2) / (BAT_MILLIVOLTS_FULL - BAT_MILLIVOLTS_EMPTY)),
|
||||
0, 100);
|
||||
}
|
||||
}
|
||||
|
||||
// Notify any status instances that are observing us
|
||||
const meshtastic::PowerStatus powerStatus = meshtastic::PowerStatus(
|
||||
hasBattery, batteryLevel->isVBUSPlug(), batteryLevel->isChargeing(), batteryVoltageMv, batteryChargePercent);
|
||||
newStatus.notifyObservers(&powerStatus);
|
||||
|
||||
// If we have a battery at all and it is less than 10% full, force deep sleep
|
||||
if (powerStatus.getHasBattery() && !powerStatus.getHasUSB() && batteryLevel->getBattVoltage() < MIN_BAT_MILLIVOLTS)
|
||||
powerFSM.trigger(EVENT_LOW_BATTERY);
|
||||
}
|
||||
}
|
||||
|
||||
void Power::doTask()
|
||||
{
|
||||
readPowerStatus();
|
||||
|
||||
// Only read once every 20 seconds once the power status for the app has been initialized
|
||||
if (statusHandler && statusHandler->isInitialized())
|
||||
setPeriod(1000 * 20);
|
||||
}
|
||||
|
||||
/**
|
||||
* Init the power manager chip
|
||||
*
|
||||
* axp192 power
|
||||
DCDC1 0.7-3.5V @ 1200mA max -> OLED // If you turn this off you'll lose comms to the axp192 because the OLED and the axp192
|
||||
share the same i2c bus, instead use ssd1306 sleep mode DCDC2 -> unused DCDC3 0.7-3.5V @ 700mA max -> ESP32 (keep this on!) LDO1
|
||||
30mA -> charges GPS backup battery // charges the tiny J13 battery by the GPS to power the GPS ram (for a couple of days), can
|
||||
not be turned off LDO2 200mA -> LORA LDO3 200mA -> GPS
|
||||
*/
|
||||
bool Power::axp192Init()
|
||||
{
|
||||
#ifdef TBEAM_V10
|
||||
if (axp192_found) {
|
||||
if (!axp.begin(Wire, AXP192_SLAVE_ADDRESS)) {
|
||||
batteryLevel = &axp;
|
||||
|
||||
DEBUG_MSG("AXP192 Begin PASS\n");
|
||||
|
||||
// axp.setChgLEDMode(LED_BLINK_4HZ);
|
||||
DEBUG_MSG("DCDC1: %s\n", axp.isDCDC1Enable() ? "ENABLE" : "DISABLE");
|
||||
DEBUG_MSG("DCDC2: %s\n", axp.isDCDC2Enable() ? "ENABLE" : "DISABLE");
|
||||
DEBUG_MSG("LDO2: %s\n", axp.isLDO2Enable() ? "ENABLE" : "DISABLE");
|
||||
DEBUG_MSG("LDO3: %s\n", axp.isLDO3Enable() ? "ENABLE" : "DISABLE");
|
||||
DEBUG_MSG("DCDC3: %s\n", axp.isDCDC3Enable() ? "ENABLE" : "DISABLE");
|
||||
DEBUG_MSG("Exten: %s\n", axp.isExtenEnable() ? "ENABLE" : "DISABLE");
|
||||
DEBUG_MSG("----------------------------------------\n");
|
||||
|
||||
axp.setPowerOutPut(AXP192_LDO2, AXP202_ON); // LORA radio
|
||||
axp.setPowerOutPut(AXP192_LDO3, AXP202_ON); // GPS main power
|
||||
axp.setPowerOutPut(AXP192_DCDC2, AXP202_ON);
|
||||
axp.setPowerOutPut(AXP192_EXTEN, AXP202_ON);
|
||||
axp.setPowerOutPut(AXP192_DCDC1, AXP202_ON);
|
||||
axp.setDCDC1Voltage(3300); // for the OLED power
|
||||
|
||||
DEBUG_MSG("DCDC1: %s\n", axp.isDCDC1Enable() ? "ENABLE" : "DISABLE");
|
||||
DEBUG_MSG("DCDC2: %s\n", axp.isDCDC2Enable() ? "ENABLE" : "DISABLE");
|
||||
DEBUG_MSG("LDO2: %s\n", axp.isLDO2Enable() ? "ENABLE" : "DISABLE");
|
||||
DEBUG_MSG("LDO3: %s\n", axp.isLDO3Enable() ? "ENABLE" : "DISABLE");
|
||||
DEBUG_MSG("DCDC3: %s\n", axp.isDCDC3Enable() ? "ENABLE" : "DISABLE");
|
||||
DEBUG_MSG("Exten: %s\n", axp.isExtenEnable() ? "ENABLE" : "DISABLE");
|
||||
|
||||
axp.setChargeControlCur(AXP1XX_CHARGE_CUR_1320MA); // actual limit (in HW) on the tbeam is 450mA
|
||||
#if 0
|
||||
|
||||
// Not connected
|
||||
//val = 0xfc;
|
||||
//axp._writeByte(AXP202_VHTF_CHGSET, 1, &val); // Set temperature protection
|
||||
|
||||
//not used
|
||||
//val = 0x46;
|
||||
//axp._writeByte(AXP202_OFF_CTL, 1, &val); // enable bat detection
|
||||
#endif
|
||||
axp.debugCharging();
|
||||
|
||||
#ifdef PMU_IRQ
|
||||
pinMode(PMU_IRQ, INPUT);
|
||||
attachInterrupt(
|
||||
PMU_IRQ, [] { pmu_irq = true; }, FALLING);
|
||||
|
||||
axp.adc1Enable(AXP202_BATT_CUR_ADC1, 1);
|
||||
axp.enableIRQ(AXP202_BATT_REMOVED_IRQ | AXP202_BATT_CONNECT_IRQ | AXP202_CHARGING_FINISHED_IRQ | AXP202_CHARGING_IRQ |
|
||||
AXP202_VBUS_REMOVED_IRQ | AXP202_VBUS_CONNECT_IRQ | AXP202_PEK_SHORTPRESS_IRQ,
|
||||
1);
|
||||
|
||||
axp.clearIRQ();
|
||||
#endif
|
||||
readPowerStatus();
|
||||
} else {
|
||||
DEBUG_MSG("AXP192 Begin FAIL\n");
|
||||
}
|
||||
} else {
|
||||
DEBUG_MSG("AXP192 not found\n");
|
||||
}
|
||||
|
||||
return axp192_found;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
void Power::loop()
|
||||
{
|
||||
#ifdef PMU_IRQ
|
||||
if (pmu_irq) {
|
||||
pmu_irq = false;
|
||||
axp.readIRQ();
|
||||
|
||||
DEBUG_MSG("pmu irq!\n");
|
||||
|
||||
if (axp.isChargingIRQ()) {
|
||||
DEBUG_MSG("Battery start charging\n");
|
||||
}
|
||||
if (axp.isChargingDoneIRQ()) {
|
||||
DEBUG_MSG("Battery fully charged\n");
|
||||
}
|
||||
if (axp.isVbusRemoveIRQ()) {
|
||||
DEBUG_MSG("USB unplugged\n");
|
||||
}
|
||||
if (axp.isVbusPlugInIRQ()) {
|
||||
DEBUG_MSG("USB plugged In\n");
|
||||
}
|
||||
if (axp.isBattPlugInIRQ()) {
|
||||
DEBUG_MSG("Battery inserted\n");
|
||||
}
|
||||
if (axp.isBattRemoveIRQ()) {
|
||||
DEBUG_MSG("Battery removed\n");
|
||||
}
|
||||
if (axp.isPEKShortPressIRQ()) {
|
||||
DEBUG_MSG("PEK short button press\n");
|
||||
}
|
||||
|
||||
readPowerStatus();
|
||||
axp.clearIRQ();
|
||||
}
|
||||
|
||||
#endif
|
||||
}
|
||||
@@ -5,9 +5,10 @@
|
||||
#include "NodeDB.h"
|
||||
#include "configuration.h"
|
||||
#include "main.h"
|
||||
#include "screen.h"
|
||||
#include "graphics/Screen.h"
|
||||
#include "sleep.h"
|
||||
#include "target_specific.h"
|
||||
#include "timing.h"
|
||||
|
||||
static void sdsEnter()
|
||||
{
|
||||
@@ -15,7 +16,7 @@ static void sdsEnter()
|
||||
|
||||
// Don't deepsleep if we have USB power or if the user as pressed a button recently
|
||||
// !isUSBPowered <- doesn't work yet because the axp192 isn't letting the battery fully charge when we are awake - FIXME
|
||||
if (millis() - lastPressMs > radioConfig.preferences.mesh_sds_timeout_secs)
|
||||
if (timing::millis() - lastPressMs > radioConfig.preferences.mesh_sds_timeout_secs)
|
||||
{
|
||||
doDeepSleep(radioConfig.preferences.sds_secs);
|
||||
}
|
||||
@@ -130,7 +131,7 @@ static void onEnter()
|
||||
|
||||
static uint32_t lastPingMs;
|
||||
|
||||
uint32_t now = millis();
|
||||
uint32_t now = timing::millis();
|
||||
|
||||
if (now - lastPingMs > 30 * 1000) { // if more than a minute since our last press, ask other nodes to update their state
|
||||
if (displayedNodeNum)
|
||||
|
||||
103
src/PowerStatus.h
Normal file
103
src/PowerStatus.h
Normal file
@@ -0,0 +1,103 @@
|
||||
#pragma once
|
||||
#include <Arduino.h>
|
||||
#include "Status.h"
|
||||
#include "configuration.h"
|
||||
|
||||
namespace meshtastic {
|
||||
|
||||
/// Describes the state of the GPS system.
|
||||
class PowerStatus : public Status
|
||||
{
|
||||
|
||||
private:
|
||||
CallbackObserver<PowerStatus, const PowerStatus *> statusObserver = CallbackObserver<PowerStatus, const PowerStatus *>(this, &PowerStatus::updateStatus);
|
||||
|
||||
/// Whether we have a battery connected
|
||||
bool hasBattery;
|
||||
/// Battery voltage in mV, valid if haveBattery is true
|
||||
int batteryVoltageMv;
|
||||
/// Battery charge percentage, either read directly or estimated
|
||||
uint8_t batteryChargePercent;
|
||||
/// Whether USB is connected
|
||||
bool hasUSB;
|
||||
/// Whether we are charging the battery
|
||||
bool isCharging;
|
||||
|
||||
public:
|
||||
|
||||
PowerStatus() {
|
||||
statusType = STATUS_TYPE_POWER;
|
||||
}
|
||||
PowerStatus( bool hasBattery, bool hasUSB, bool isCharging, int batteryVoltageMv, uint8_t batteryChargePercent ) : Status()
|
||||
{
|
||||
this->hasBattery = hasBattery;
|
||||
this->hasUSB = hasUSB;
|
||||
this->isCharging = isCharging;
|
||||
this->batteryVoltageMv = batteryVoltageMv;
|
||||
this->batteryChargePercent = batteryChargePercent;
|
||||
}
|
||||
PowerStatus(const PowerStatus &);
|
||||
PowerStatus &operator=(const PowerStatus &);
|
||||
|
||||
void observe(Observable<const PowerStatus *> *source)
|
||||
{
|
||||
statusObserver.observe(source);
|
||||
}
|
||||
|
||||
bool getHasBattery() const
|
||||
{
|
||||
return hasBattery;
|
||||
}
|
||||
|
||||
bool getHasUSB() const
|
||||
{
|
||||
return hasUSB;
|
||||
}
|
||||
|
||||
bool getIsCharging() const
|
||||
{
|
||||
return isCharging;
|
||||
}
|
||||
|
||||
int getBatteryVoltageMv() const
|
||||
{
|
||||
return batteryVoltageMv;
|
||||
}
|
||||
|
||||
uint8_t getBatteryChargePercent() const
|
||||
{
|
||||
return batteryChargePercent;
|
||||
}
|
||||
|
||||
bool matches(const PowerStatus *newStatus) const
|
||||
{
|
||||
return (
|
||||
newStatus->getHasBattery() != hasBattery ||
|
||||
newStatus->getHasUSB() != hasUSB ||
|
||||
newStatus->getBatteryVoltageMv() != batteryVoltageMv
|
||||
);
|
||||
}
|
||||
int updateStatus(const PowerStatus *newStatus) {
|
||||
// Only update the status if values have actually changed
|
||||
bool isDirty;
|
||||
{
|
||||
isDirty = matches(newStatus);
|
||||
initialized = true;
|
||||
hasBattery = newStatus->getHasBattery();
|
||||
batteryVoltageMv = newStatus->getBatteryVoltageMv();
|
||||
batteryChargePercent = newStatus->getBatteryChargePercent();
|
||||
hasUSB = newStatus->getHasUSB();
|
||||
isCharging = newStatus->getIsCharging();
|
||||
}
|
||||
if(isDirty) {
|
||||
DEBUG_MSG("Battery %dmV %d%%\n", batteryVoltageMv, batteryChargePercent);
|
||||
onNewStatus.notifyObservers(this);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
extern meshtastic::PowerStatus *powerStatus;
|
||||
72
src/Status.h
Normal file
72
src/Status.h
Normal file
@@ -0,0 +1,72 @@
|
||||
#pragma once
|
||||
|
||||
#include "Observer.h"
|
||||
|
||||
// Constants for the various status types, so we can tell subclass instances apart
|
||||
#define STATUS_TYPE_BASE 0
|
||||
#define STATUS_TYPE_POWER 1
|
||||
#define STATUS_TYPE_GPS 2
|
||||
#define STATUS_TYPE_NODE 3
|
||||
|
||||
|
||||
namespace meshtastic
|
||||
{
|
||||
|
||||
// A base class for observable status
|
||||
class Status
|
||||
{
|
||||
protected:
|
||||
// Allows us to observe an Observable
|
||||
CallbackObserver<Status, const Status *> statusObserver = CallbackObserver<Status, const Status *>(this, &Status::updateStatus);
|
||||
bool initialized = false;
|
||||
// Workaround for no typeid support
|
||||
int statusType;
|
||||
|
||||
public:
|
||||
// Allows us to generate observable events
|
||||
Observable<const Status *> onNewStatus;
|
||||
|
||||
// Enable polymorphism ?
|
||||
virtual ~Status() = default;
|
||||
|
||||
Status() {
|
||||
if (!statusType)
|
||||
{
|
||||
statusType = STATUS_TYPE_BASE;
|
||||
}
|
||||
}
|
||||
|
||||
// Prevent object copy/move
|
||||
Status(const Status &) = delete;
|
||||
Status &operator=(const Status &) = delete;
|
||||
|
||||
// Start observing a source of data
|
||||
void observe(Observable<const Status *> *source)
|
||||
{
|
||||
statusObserver.observe(source);
|
||||
}
|
||||
|
||||
// Determines whether or not existing data matches the data in another Status instance
|
||||
bool matches(const Status *otherStatus) const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
bool isInitialized() const
|
||||
{
|
||||
return initialized;
|
||||
}
|
||||
|
||||
int getStatusType() const
|
||||
{
|
||||
return statusType;
|
||||
}
|
||||
|
||||
// Called when the Observable we're observing generates a new notification
|
||||
int updateStatus(const Status *newStatus)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
};
|
||||
};
|
||||
0
src/StatusHandler.h
Normal file
0
src/StatusHandler.h
Normal file
339
src/User_Setup.h
Normal file
339
src/User_Setup.h
Normal file
@@ -0,0 +1,339 @@
|
||||
// This file is used to set TFT/eInk preferences for the TFT.cpp driver
|
||||
|
||||
#include <variant.h>
|
||||
|
||||
// USER DEFINED SETTINGS
|
||||
// Set driver type, fonts to be loaded, pins used and SPI control method etc
|
||||
//
|
||||
// See the User_Setup_Select.h file if you wish to be able to define multiple
|
||||
// setups and then easily select which setup file is used by the compiler.
|
||||
//
|
||||
// If this file is edited correctly then all the library example sketches should
|
||||
// run without the need to make any more changes for a particular hardware setup!
|
||||
// Note that some sketches are designed for a particular TFT pixel width/height
|
||||
|
||||
|
||||
// ##################################################################################
|
||||
//
|
||||
// Section 1. Call up the right driver file and any options for it
|
||||
//
|
||||
// ##################################################################################
|
||||
|
||||
// Define STM32 to invoke optimised processor support (only for STM32)
|
||||
//#define STM32
|
||||
|
||||
// Defining the STM32 board allows the library to optimise the performance
|
||||
// for UNO compatible "MCUfriend" style shields
|
||||
//#define NUCLEO_64_TFT
|
||||
//#define NUCLEO_144_TFT
|
||||
|
||||
// STM32 8 bit parallel only:
|
||||
// If STN32 Port A or B pins 0-7 are used for 8 bit parallel data bus bits 0-7
|
||||
// then this will improve rendering performance by a factor of ~8x
|
||||
//#define STM_PORTA_DATA_BUS
|
||||
//#define STM_PORTA_DATA_BUS
|
||||
|
||||
// Tell the library to use 8 bit parallel mode (otherwise SPI is assumed)
|
||||
//#define TFT_PARALLEL_8_BIT
|
||||
|
||||
// Display type - only define if RPi display
|
||||
//#define RPI_DISPLAY_TYPE // 20MHz maximum SPI
|
||||
|
||||
// Only define one driver, the other ones must be commented out
|
||||
//#define ILI9341_DRIVER
|
||||
#define ST7735_DRIVER // Define additional parameters below for this display
|
||||
//#define ILI9163_DRIVER // Define additional parameters below for this display
|
||||
//#define S6D02A1_DRIVER
|
||||
//#define RPI_ILI9486_DRIVER // 20MHz maximum SPI
|
||||
//#define HX8357D_DRIVER
|
||||
//#define ILI9481_DRIVER
|
||||
//#define ILI9486_DRIVER
|
||||
//#define ILI9488_DRIVER // WARNING: Do not connect ILI9488 display SDO to MISO if other devices share the SPI bus (TFT SDO does NOT tristate when CS is high)
|
||||
//#define ST7789_DRIVER // Full configuration option, define additional parameters below for this display
|
||||
//#define ST7789_2_DRIVER // Minimal configuration option, define additional parameters below for this display
|
||||
//#define R61581_DRIVER
|
||||
//#define RM68140_DRIVER
|
||||
//#define ST7796_DRIVER
|
||||
//#define SSD1963_480_DRIVER // Untested
|
||||
//#define SSD1963_800_DRIVER // Untested
|
||||
//#define SSD1963_800ALT_DRIVER // Untested
|
||||
|
||||
// Some displays support SPI reads via the MISO pin, other displays have a single
|
||||
// bi-directional SDA pin and the library will try to read this via the MOSI line.
|
||||
// To use the SDA line for reading data from the TFT uncomment the following line:
|
||||
|
||||
// #define TFT_SDA_READ // This option is for ESP32 ONLY, tested with ST7789 display only
|
||||
|
||||
// For ST7789 and ILI9341 ONLY, define the colour order IF the blue and red are swapped on your display
|
||||
// Try ONE option at a time to find the correct colour order for your display
|
||||
|
||||
// #define TFT_RGB_ORDER TFT_RGB // Colour order Red-Green-Blue
|
||||
// #define TFT_RGB_ORDER TFT_BGR // Colour order Blue-Green-Red
|
||||
|
||||
// For M5Stack ESP32 module with integrated ILI9341 display ONLY, remove // in line below
|
||||
|
||||
// #define M5STACK
|
||||
|
||||
// For ST7789, ST7735 and ILI9163 ONLY, define the pixel width and height in portrait orientation
|
||||
#define TFT_WIDTH 80
|
||||
// #define TFT_WIDTH 128
|
||||
// #define TFT_WIDTH 240 // ST7789 240 x 240 and 240 x 320
|
||||
#define TFT_HEIGHT 160
|
||||
// #define TFT_HEIGHT 128
|
||||
// #define TFT_HEIGHT 240 // ST7789 240 x 240
|
||||
// #define TFT_HEIGHT 320 // ST7789 240 x 320
|
||||
|
||||
// For ST7735 ONLY, define the type of display, originally this was based on the
|
||||
// colour of the tab on the screen protector film but this is not always true, so try
|
||||
// out the different options below if the screen does not display graphics correctly,
|
||||
// e.g. colours wrong, mirror images, or tray pixels at the edges.
|
||||
// Comment out ALL BUT ONE of these options for a ST7735 display driver, save this
|
||||
// this User_Setup file, then rebuild and upload the sketch to the board again:
|
||||
|
||||
// #define ST7735_INITB
|
||||
#define ST7735_GREENTAB
|
||||
// #define ST7735_GREENTAB2
|
||||
// #define ST7735_GREENTAB3
|
||||
// #define ST7735_GREENTAB128 // For 128 x 128 display
|
||||
// #define ST7735_GREENTAB160x80 // For 160 x 80 display (BGR, inverted, 26 offset)
|
||||
// #define ST7735_REDTAB
|
||||
// #define ST7735_BLACKTAB
|
||||
// #define ST7735_REDTAB160x80 // For 160 x 80 display with 24 pixel offset
|
||||
|
||||
// If colours are inverted (white shows as black) then uncomment one of the next
|
||||
// 2 lines try both options, one of the options should correct the inversion.
|
||||
|
||||
// #define TFT_INVERSION_ON
|
||||
// #define TFT_INVERSION_OFF
|
||||
|
||||
|
||||
// ##################################################################################
|
||||
//
|
||||
// Section 2. Define the pins that are used to interface with the display here
|
||||
//
|
||||
// ##################################################################################
|
||||
|
||||
// If a backlight control signal is available then define the TFT_BL pin in Section 2
|
||||
// below. The backlight will be turned ON when tft.begin() is called, but the library
|
||||
// needs to know if the LEDs are ON with the pin HIGH or LOW. If the LEDs are to be
|
||||
// driven with a PWM signal or turned OFF/ON then this must be handled by the user
|
||||
// sketch. e.g. with digitalWrite(TFT_BL, LOW);
|
||||
|
||||
#define TFT_BL ST7735_BACKLIGHT_EN // LED back-light control pin
|
||||
#define TFT_BACKLIGHT_ON HIGH // Level to turn ON back-light (HIGH or LOW)
|
||||
|
||||
|
||||
|
||||
// We must use hardware SPI, a minimum of 3 GPIO pins is needed.
|
||||
// Typical setup for ESP8266 NodeMCU ESP-12 is :
|
||||
//
|
||||
// Display SDO/MISO to NodeMCU pin D6 (or leave disconnected if not reading TFT)
|
||||
// Display LED to NodeMCU pin VIN (or 5V, see below)
|
||||
// Display SCK to NodeMCU pin D5
|
||||
// Display SDI/MOSI to NodeMCU pin D7
|
||||
// Display DC (RS/AO)to NodeMCU pin D3
|
||||
// Display RESET to NodeMCU pin D4 (or RST, see below)
|
||||
// Display CS to NodeMCU pin D8 (or GND, see below)
|
||||
// Display GND to NodeMCU pin GND (0V)
|
||||
// Display VCC to NodeMCU 5V or 3.3V
|
||||
//
|
||||
// The TFT RESET pin can be connected to the NodeMCU RST pin or 3.3V to free up a control pin
|
||||
//
|
||||
// The DC (Data Command) pin may be labeled AO or RS (Register Select)
|
||||
//
|
||||
// With some displays such as the ILI9341 the TFT CS pin can be connected to GND if no more
|
||||
// SPI devices (e.g. an SD Card) are connected, in this case comment out the #define TFT_CS
|
||||
// line below so it is NOT defined. Other displays such at the ST7735 require the TFT CS pin
|
||||
// to be toggled during setup, so in these cases the TFT_CS line must be defined and connected.
|
||||
//
|
||||
// The NodeMCU D0 pin can be used for RST
|
||||
//
|
||||
//
|
||||
// Note: only some versions of the NodeMCU provide the USB 5V on the VIN pin
|
||||
// If 5V is not available at a pin you can use 3.3V but backlight brightness
|
||||
// will be lower.
|
||||
|
||||
|
||||
// ###### EDIT THE PIN NUMBERS IN THE LINES FOLLOWING TO SUIT YOUR ESP8266 SETUP ######
|
||||
|
||||
// For NodeMCU - use pin numbers in the form PIN_Dx where Dx is the NodeMCU pin designation
|
||||
#define TFT_CS ST7735_CS // Chip select control pin D8
|
||||
#define TFT_DC ST7735_RS // Data Command control pin
|
||||
#define TFT_RST ST7735_RESET // Reset pin (could connect to NodeMCU RST, see next line)
|
||||
//#define TFT_RST -1 // Set TFT_RST to -1 if the display RESET is connected to NodeMCU RST or 3.3V
|
||||
|
||||
//#define TFT_BL PIN_D1 // LED back-light (only for ST7789 with backlight control pin)
|
||||
|
||||
//#define TOUCH_CS PIN_D2 // Chip select pin (T_CS) of touch screen
|
||||
|
||||
//#define TFT_WR PIN_D2 // Write strobe for modified Raspberry Pi TFT only
|
||||
|
||||
|
||||
// ###### FOR ESP8266 OVERLAP MODE EDIT THE PIN NUMBERS IN THE FOLLOWING LINES ######
|
||||
|
||||
// Overlap mode shares the ESP8266 FLASH SPI bus with the TFT so has a performance impact
|
||||
// but saves pins for other functions. It is best not to connect MISO as some displays
|
||||
// do not tristate that line wjen chip select is high!
|
||||
// On NodeMCU 1.0 SD0=MISO, SD1=MOSI, CLK=SCLK to connect to TFT in overlap mode
|
||||
// On NodeMCU V3 S0 =MISO, S1 =MOSI, S2 =SCLK
|
||||
// In ESP8266 overlap mode the following must be defined
|
||||
|
||||
//#define TFT_SPI_OVERLAP
|
||||
|
||||
// In ESP8266 overlap mode the TFT chip select MUST connect to pin D3
|
||||
//#define TFT_CS PIN_D3
|
||||
//#define TFT_DC PIN_D5 // Data Command control pin
|
||||
//#define TFT_RST PIN_D4 // Reset pin (could connect to NodeMCU RST, see next line)
|
||||
//#define TFT_RST -1 // Set TFT_RST to -1 if the display RESET is connected to NodeMCU RST or 3.3V
|
||||
|
||||
|
||||
// ###### EDIT THE PIN NUMBERS IN THE LINES FOLLOWING TO SUIT YOUR ESP32 SETUP ######
|
||||
|
||||
// For ESP32 Dev board (only tested with ILI9341 display)
|
||||
// The hardware SPI can be mapped to any pins
|
||||
|
||||
//#define TFT_MISO 19
|
||||
//#define TFT_MOSI 23
|
||||
//#define TFT_SCLK 18
|
||||
//#define TFT_CS 15 // Chip select control pin
|
||||
//#define TFT_DC 2 // Data Command control pin
|
||||
//#define TFT_RST 4 // Reset pin (could connect to RST pin)
|
||||
//#define TFT_RST -1 // Set TFT_RST to -1 if display RESET is connected to ESP32 board RST
|
||||
|
||||
//#define TOUCH_CS 21 // Chip select pin (T_CS) of touch screen
|
||||
|
||||
//#define TFT_WR 22 // Write strobe for modified Raspberry Pi TFT only
|
||||
|
||||
// For the M5Stack module use these #define lines
|
||||
//#define TFT_MISO 19
|
||||
//#define TFT_MOSI 23
|
||||
//#define TFT_SCLK 18
|
||||
//#define TFT_CS 14 // Chip select control pin
|
||||
//#define TFT_DC 27 // Data Command control pin
|
||||
//#define TFT_RST 33 // Reset pin (could connect to Arduino RESET pin)
|
||||
//#define TFT_BL 32 // LED back-light (required for M5Stack)
|
||||
|
||||
// ###### EDIT THE PINs BELOW TO SUIT YOUR ESP32 PARALLEL TFT SETUP ######
|
||||
|
||||
// The library supports 8 bit parallel TFTs with the ESP32, the pin
|
||||
// selection below is compatible with ESP32 boards in UNO format.
|
||||
// Wemos D32 boards need to be modified, see diagram in Tools folder.
|
||||
// Only ILI9481 and ILI9341 based displays have been tested!
|
||||
|
||||
// Parallel bus is only supported for the STM32 and ESP32
|
||||
// Example below is for ESP32 Parallel interface with UNO displays
|
||||
|
||||
// Tell the library to use 8 bit parallel mode (otherwise SPI is assumed)
|
||||
//#define TFT_PARALLEL_8_BIT
|
||||
|
||||
// The ESP32 and TFT the pins used for testing are:
|
||||
//#define TFT_CS 33 // Chip select control pin (library pulls permanently low
|
||||
//#define TFT_DC 15 // Data Command control pin - must use a pin in the range 0-31
|
||||
//#define TFT_RST 32 // Reset pin, toggles on startup
|
||||
|
||||
//#define TFT_WR 4 // Write strobe control pin - must use a pin in the range 0-31
|
||||
//#define TFT_RD 2 // Read strobe control pin
|
||||
|
||||
//#define TFT_D0 12 // Must use pins in the range 0-31 for the data bus
|
||||
//#define TFT_D1 13 // so a single register write sets/clears all bits.
|
||||
//#define TFT_D2 26 // Pins can be randomly assigned, this does not affect
|
||||
//#define TFT_D3 25 // TFT screen update performance.
|
||||
//#define TFT_D4 17
|
||||
//#define TFT_D5 16
|
||||
//#define TFT_D6 27
|
||||
//#define TFT_D7 14
|
||||
|
||||
// ###### EDIT THE PINs BELOW TO SUIT YOUR STM32 SPI TFT SETUP ######
|
||||
|
||||
// The TFT can be connected to SPI port 1 or 2
|
||||
//#define TFT_SPI_PORT 1 // SPI port 1 maximum clock rate is 55MHz
|
||||
//#define TFT_MOSI PA7
|
||||
//#define TFT_MISO PA6
|
||||
//#define TFT_SCLK PA5
|
||||
|
||||
//#define TFT_SPI_PORT 2 // SPI port 2 maximum clock rate is 27MHz
|
||||
//#define TFT_MOSI PB15
|
||||
//#define TFT_MISO PB14
|
||||
//#define TFT_SCLK PB13
|
||||
|
||||
// Can use Ardiuno pin references, arbitrary allocation, TFT_eSPI controls chip select
|
||||
//#define TFT_CS D5 // Chip select control pin to TFT CS
|
||||
//#define TFT_DC D6 // Data Command control pin to TFT DC (may be labelled RS = Register Select)
|
||||
//#define TFT_RST D7 // Reset pin to TFT RST (or RESET)
|
||||
// OR alternatively, we can use STM32 port reference names PXnn
|
||||
//#define TFT_CS PE11 // Nucleo-F767ZI equivalent of D5
|
||||
//#define TFT_DC PE9 // Nucleo-F767ZI equivalent of D6
|
||||
//#define TFT_RST PF13 // Nucleo-F767ZI equivalent of D7
|
||||
|
||||
//#define TFT_RST -1 // Set TFT_RST to -1 if the display RESET is connected to processor reset
|
||||
// Use an Arduino pin for initial testing as connecting to processor reset
|
||||
// may not work (pulse too short at power up?)
|
||||
|
||||
// ##################################################################################
|
||||
//
|
||||
// Section 3. Define the fonts that are to be used here
|
||||
//
|
||||
// ##################################################################################
|
||||
|
||||
// Comment out the #defines below with // to stop that font being loaded
|
||||
// The ESP8366 and ESP32 have plenty of memory so commenting out fonts is not
|
||||
// normally necessary. If all fonts are loaded the extra FLASH space required is
|
||||
// about 17Kbytes. To save FLASH space only enable the fonts you need!
|
||||
|
||||
//#define LOAD_GLCD // Font 1. Original Adafruit 8 pixel font needs ~1820 bytes in FLASH
|
||||
//#define LOAD_FONT2 // Font 2. Small 16 pixel high font, needs ~3534 bytes in FLASH, 96 characters
|
||||
//#define LOAD_FONT4 // Font 4. Medium 26 pixel high font, needs ~5848 bytes in FLASH, 96 characters
|
||||
//#define LOAD_FONT6 // Font 6. Large 48 pixel font, needs ~2666 bytes in FLASH, only characters 1234567890:-.apm
|
||||
//#define LOAD_FONT7 // Font 7. 7 segment 48 pixel font, needs ~2438 bytes in FLASH, only characters 1234567890:-.
|
||||
//#define LOAD_FONT8 // Font 8. Large 75 pixel font needs ~3256 bytes in FLASH, only characters 1234567890:-.
|
||||
//#define LOAD_FONT8N // Font 8. Alternative to Font 8 above, slightly narrower, so 3 digits fit a 160 pixel TFT
|
||||
//#define LOAD_GFXFF // FreeFonts. Include access to the 48 Adafruit_GFX free fonts FF1 to FF48 and custom fonts
|
||||
|
||||
// Comment out the #define below to stop the SPIFFS filing system and smooth font code being loaded
|
||||
// this will save ~20kbytes of FLASH
|
||||
//#define SMOOTH_FONT
|
||||
|
||||
|
||||
// ##################################################################################
|
||||
//
|
||||
// Section 4. Other options
|
||||
//
|
||||
// ##################################################################################
|
||||
|
||||
// Define the SPI clock frequency, this affects the graphics rendering speed. Too
|
||||
// fast and the TFT driver will not keep up and display corruption appears.
|
||||
// With an ILI9341 display 40MHz works OK, 80MHz sometimes fails
|
||||
// With a ST7735 display more than 27MHz may not work (spurious pixels and lines)
|
||||
// With an ILI9163 display 27 MHz works OK.
|
||||
|
||||
// #define SPI_FREQUENCY 1000000
|
||||
// #define SPI_FREQUENCY 5000000
|
||||
// #define SPI_FREQUENCY 10000000
|
||||
// #define SPI_FREQUENCY 20000000
|
||||
#define SPI_FREQUENCY 27000000
|
||||
// #define SPI_FREQUENCY 40000000
|
||||
// #define SPI_FREQUENCY 55000000 // STM32 SPI1 only (SPI2 maximum is 27MHz)
|
||||
// #define SPI_FREQUENCY 80000000
|
||||
|
||||
// Optional reduced SPI frequency for reading TFT
|
||||
#define SPI_READ_FREQUENCY 20000000
|
||||
|
||||
// The XPT2046 requires a lower SPI clock rate of 2.5MHz so we define that here:
|
||||
#define SPI_TOUCH_FREQUENCY 2500000
|
||||
|
||||
// The ESP32 has 2 free SPI ports i.e. VSPI and HSPI, the VSPI is the default.
|
||||
// If the VSPI port is in use and pins are not accessible (e.g. TTGO T-Beam)
|
||||
// then uncomment the following line:
|
||||
//#define USE_HSPI_PORT
|
||||
|
||||
// Comment out the following #define if "SPI Transactions" do not need to be
|
||||
// supported. When commented out the code size will be smaller and sketches will
|
||||
// run slightly faster, so leave it commented out unless you need it!
|
||||
|
||||
// Transaction support is needed to work with SD library but not needed with TFT_SdFat
|
||||
// Transaction support is required if other SPI devices are connected.
|
||||
|
||||
// Transactions are automatically enabled by the library for an ESP32 (to use HAL mutex)
|
||||
// so changing it here has no effect
|
||||
|
||||
// #define SUPPORT_TRANSACTIONS
|
||||
@@ -1,49 +0,0 @@
|
||||
#include "WorkerThread.h"
|
||||
#include "debug.h"
|
||||
#include <assert.h>
|
||||
|
||||
#ifdef configUSE_PREEMPTION
|
||||
|
||||
void Thread::start(const char *name, size_t stackSize, uint32_t priority)
|
||||
{
|
||||
auto r = xTaskCreate(callRun, name, stackSize, this, priority, &taskHandle);
|
||||
assert(r == pdPASS);
|
||||
}
|
||||
|
||||
void Thread::callRun(void *_this)
|
||||
{
|
||||
((Thread *)_this)->doRun();
|
||||
}
|
||||
|
||||
void WorkerThread::doRun()
|
||||
{
|
||||
while (!wantExit) {
|
||||
block();
|
||||
|
||||
#ifdef DEBUG_STACK
|
||||
static uint32_t lastPrint = 0;
|
||||
if (millis() - lastPrint > 10 * 1000L) {
|
||||
lastPrint = millis();
|
||||
meshtastic::printThreadInfo("net");
|
||||
}
|
||||
#endif
|
||||
|
||||
loop();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Notify this thread so it can run
|
||||
*/
|
||||
void NotifiedWorkerThread::notify(uint32_t v, eNotifyAction action)
|
||||
{
|
||||
xTaskNotify(taskHandle, v, action);
|
||||
}
|
||||
|
||||
void NotifiedWorkerThread::block()
|
||||
{
|
||||
xTaskNotifyWait(0, // don't clear notification on entry
|
||||
clearOnRead, ¬ification, portMAX_DELAY); // Wait forever
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -1,96 +0,0 @@
|
||||
#include <Arduino.h>
|
||||
#include "freertosinc.h"
|
||||
|
||||
#ifdef HAS_FREE_RTOS
|
||||
|
||||
class Thread
|
||||
{
|
||||
protected:
|
||||
TaskHandle_t taskHandle = NULL;
|
||||
|
||||
/**
|
||||
* set this to true to ask thread to cleanly exit asap
|
||||
*/
|
||||
volatile bool wantExit = false;
|
||||
|
||||
public:
|
||||
void start(const char *name, size_t stackSize = 1024, uint32_t priority = tskIDLE_PRIORITY);
|
||||
|
||||
virtual ~Thread() { vTaskDelete(taskHandle); }
|
||||
|
||||
uint32_t getStackHighwaterMark() { return uxTaskGetStackHighWaterMark(taskHandle); }
|
||||
|
||||
protected:
|
||||
/**
|
||||
* The method that will be called when start is called.
|
||||
*/
|
||||
virtual void doRun() = 0;
|
||||
|
||||
private:
|
||||
static void callRun(void *_this);
|
||||
};
|
||||
|
||||
/**
|
||||
* This wraps threading (FreeRTOS for now) with a blocking API intended for efficiently converting onlyschool arduino loop() code.
|
||||
*
|
||||
* Use as a mixin base class for the classes you want to convert.
|
||||
*
|
||||
* https://www.freertos.org/RTOS_Task_Notification_As_Mailbox.html
|
||||
*/
|
||||
class WorkerThread : public Thread
|
||||
{
|
||||
protected:
|
||||
/**
|
||||
* A method that should block execution - either waiting ona queue/mutex or a "task notification"
|
||||
*/
|
||||
virtual void block() = 0;
|
||||
|
||||
virtual void loop() = 0;
|
||||
|
||||
/**
|
||||
* The method that will be called when start is called.
|
||||
*/
|
||||
virtual void doRun();
|
||||
};
|
||||
|
||||
/**
|
||||
* A worker thread that waits on a freertos notification
|
||||
*/
|
||||
class NotifiedWorkerThread : public WorkerThread
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Notify this thread so it can run
|
||||
*/
|
||||
void notify(uint32_t v = 0, eNotifyAction action = eNoAction);
|
||||
|
||||
/**
|
||||
* Notify from an ISR
|
||||
*
|
||||
* This must be inline or IRAM_ATTR on ESP32
|
||||
*/
|
||||
inline void notifyFromISR(BaseType_t *highPriWoken, uint32_t v = 0, eNotifyAction action = eNoAction)
|
||||
{
|
||||
xTaskNotifyFromISR(taskHandle, v, action, highPriWoken);
|
||||
}
|
||||
|
||||
protected:
|
||||
/**
|
||||
* The notification that was most recently used to wake the thread. Read from loop()
|
||||
*/
|
||||
uint32_t notification = 0;
|
||||
|
||||
/**
|
||||
* What notification bits should be cleared just after we read and return them in notification?
|
||||
*
|
||||
* Defaults to clear all of them.
|
||||
*/
|
||||
uint32_t clearOnRead = UINT32_MAX;
|
||||
|
||||
/**
|
||||
* A method that should block execution - either waiting ona queue/mutex or a "task notification"
|
||||
*/
|
||||
virtual void block();
|
||||
};
|
||||
|
||||
#endif
|
||||
15
src/commands.h
Normal file
15
src/commands.h
Normal file
@@ -0,0 +1,15 @@
|
||||
/**
|
||||
* @brief This class enables on the fly software and hardware setup.
|
||||
* It will contain all command messages to change internal settings.
|
||||
*/
|
||||
|
||||
enum class Cmd {
|
||||
INVALID,
|
||||
SET_ON,
|
||||
SET_OFF,
|
||||
ON_PRESS,
|
||||
START_BLUETOOTH_PIN_SCREEN,
|
||||
STOP_BLUETOOTH_PIN_SCREEN,
|
||||
STOP_BOOT_SCREEN,
|
||||
PRINT,
|
||||
};
|
||||
23
src/concurrency/Lock.cpp
Normal file
23
src/concurrency/Lock.cpp
Normal file
@@ -0,0 +1,23 @@
|
||||
#include "Lock.h"
|
||||
#include <cassert>
|
||||
|
||||
namespace concurrency {
|
||||
|
||||
Lock::Lock()
|
||||
{
|
||||
handle = xSemaphoreCreateBinary();
|
||||
assert(handle);
|
||||
assert(xSemaphoreGive(handle));
|
||||
}
|
||||
|
||||
void Lock::lock()
|
||||
{
|
||||
assert(xSemaphoreTake(handle, portMAX_DELAY));
|
||||
}
|
||||
|
||||
void Lock::unlock()
|
||||
{
|
||||
assert(xSemaphoreGive(handle));
|
||||
}
|
||||
|
||||
} // namespace concurrency
|
||||
33
src/concurrency/Lock.h
Normal file
33
src/concurrency/Lock.h
Normal file
@@ -0,0 +1,33 @@
|
||||
#pragma once
|
||||
|
||||
#include "../freertosinc.h"
|
||||
|
||||
namespace concurrency {
|
||||
|
||||
/**
|
||||
* @brief Simple wrapper around FreeRTOS API for implementing a mutex lock
|
||||
*/
|
||||
class Lock
|
||||
{
|
||||
public:
|
||||
Lock();
|
||||
|
||||
Lock(const Lock &) = delete;
|
||||
Lock &operator=(const Lock &) = delete;
|
||||
|
||||
/// Locks the lock.
|
||||
//
|
||||
// Must not be called from an ISR.
|
||||
void lock();
|
||||
|
||||
// Unlocks the lock.
|
||||
//
|
||||
// Must not be called from an ISR.
|
||||
void unlock();
|
||||
|
||||
private:
|
||||
SemaphoreHandle_t handle;
|
||||
|
||||
};
|
||||
|
||||
} // namespace concurrency
|
||||
15
src/concurrency/LockGuard.cpp
Normal file
15
src/concurrency/LockGuard.cpp
Normal file
@@ -0,0 +1,15 @@
|
||||
#include "LockGuard.h"
|
||||
|
||||
namespace concurrency {
|
||||
|
||||
LockGuard::LockGuard(Lock *lock) : lock(lock)
|
||||
{
|
||||
lock->lock();
|
||||
}
|
||||
|
||||
LockGuard::~LockGuard()
|
||||
{
|
||||
lock->unlock();
|
||||
}
|
||||
|
||||
} // namespace concurrency
|
||||
23
src/concurrency/LockGuard.h
Normal file
23
src/concurrency/LockGuard.h
Normal file
@@ -0,0 +1,23 @@
|
||||
#pragma once
|
||||
|
||||
#include "Lock.h"
|
||||
|
||||
namespace concurrency {
|
||||
|
||||
/**
|
||||
* @brief RAII lock guard
|
||||
*/
|
||||
class LockGuard
|
||||
{
|
||||
public:
|
||||
LockGuard(Lock *lock);
|
||||
~LockGuard();
|
||||
|
||||
LockGuard(const LockGuard &) = delete;
|
||||
LockGuard &operator=(const LockGuard &) = delete;
|
||||
|
||||
private:
|
||||
Lock *lock;
|
||||
};
|
||||
|
||||
} // namespace concurrency
|
||||
19
src/concurrency/NotifiedWorkerThread.cpp
Normal file
19
src/concurrency/NotifiedWorkerThread.cpp
Normal file
@@ -0,0 +1,19 @@
|
||||
#include "NotifiedWorkerThread.h"
|
||||
|
||||
namespace concurrency {
|
||||
|
||||
/**
|
||||
* Notify this thread so it can run
|
||||
*/
|
||||
void NotifiedWorkerThread::notify(uint32_t v, eNotifyAction action)
|
||||
{
|
||||
xTaskNotify(taskHandle, v, action);
|
||||
}
|
||||
|
||||
void NotifiedWorkerThread::block()
|
||||
{
|
||||
xTaskNotifyWait(0, // don't clear notification on entry
|
||||
clearOnRead, ¬ification, portMAX_DELAY); // Wait forever
|
||||
}
|
||||
|
||||
} // namespace concurrency
|
||||
47
src/concurrency/NotifiedWorkerThread.h
Normal file
47
src/concurrency/NotifiedWorkerThread.h
Normal file
@@ -0,0 +1,47 @@
|
||||
#pragma once
|
||||
|
||||
#include "WorkerThread.h"
|
||||
|
||||
namespace concurrency {
|
||||
|
||||
/**
|
||||
* @brief A worker thread that waits on a freertos notification
|
||||
*/
|
||||
class NotifiedWorkerThread : public WorkerThread
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Notify this thread so it can run
|
||||
*/
|
||||
void notify(uint32_t v = 0, eNotifyAction action = eNoAction);
|
||||
|
||||
/**
|
||||
* Notify from an ISR
|
||||
*
|
||||
* This must be inline or IRAM_ATTR on ESP32
|
||||
*/
|
||||
inline void notifyFromISR(BaseType_t *highPriWoken, uint32_t v = 0, eNotifyAction action = eNoAction)
|
||||
{
|
||||
xTaskNotifyFromISR(taskHandle, v, action, highPriWoken);
|
||||
}
|
||||
|
||||
protected:
|
||||
/**
|
||||
* The notification that was most recently used to wake the thread. Read from loop()
|
||||
*/
|
||||
uint32_t notification = 0;
|
||||
|
||||
/**
|
||||
* What notification bits should be cleared just after we read and return them in notification?
|
||||
*
|
||||
* Defaults to clear all of them.
|
||||
*/
|
||||
uint32_t clearOnRead = UINT32_MAX;
|
||||
|
||||
/**
|
||||
* A method that should block execution - either waiting ona queue/mutex or a "task notification"
|
||||
*/
|
||||
virtual void block();
|
||||
};
|
||||
|
||||
} // namespace concurrency
|
||||
@@ -1,12 +1,12 @@
|
||||
#pragma once
|
||||
|
||||
#include "PeriodicTask.h"
|
||||
#include <Arduino.h>
|
||||
|
||||
namespace concurrency {
|
||||
|
||||
/**
|
||||
* Periodically invoke a callback.
|
||||
*
|
||||
* This just provides C style callback conventions rather than a virtual function - FIXME, remove?
|
||||
* @brief Periodically invoke a callback. This just provides C-style callback conventions
|
||||
* rather than a virtual function - FIXME, remove?
|
||||
*/
|
||||
class Periodic : public PeriodicTask
|
||||
{
|
||||
@@ -17,5 +17,10 @@ class Periodic : public PeriodicTask
|
||||
Periodic(uint32_t (*_callback)()) : callback(_callback) {}
|
||||
|
||||
protected:
|
||||
void doTask();
|
||||
void doTask() {
|
||||
uint32_t p = callback();
|
||||
setPeriod(p);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace concurrency
|
||||
35
src/concurrency/PeriodicScheduler.cpp
Normal file
35
src/concurrency/PeriodicScheduler.cpp
Normal file
@@ -0,0 +1,35 @@
|
||||
#include "PeriodicScheduler.h"
|
||||
#include "PeriodicTask.h"
|
||||
#include "LockGuard.h"
|
||||
#include "../timing.h"
|
||||
|
||||
namespace concurrency {
|
||||
|
||||
/// call this from loop
|
||||
void PeriodicScheduler::loop()
|
||||
{
|
||||
LockGuard lg(&lock);
|
||||
|
||||
uint32_t now = timing::millis();
|
||||
for (auto t : tasks) {
|
||||
if (t->period && (now - t->lastMsec) >= t->period) {
|
||||
|
||||
t->doTask();
|
||||
t->lastMsec = now;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void PeriodicScheduler::schedule(PeriodicTask *t)
|
||||
{
|
||||
LockGuard lg(&lock);
|
||||
tasks.insert(t);
|
||||
}
|
||||
|
||||
void PeriodicScheduler::unschedule(PeriodicTask *t)
|
||||
{
|
||||
LockGuard lg(&lock);
|
||||
tasks.erase(t);
|
||||
}
|
||||
|
||||
} // namespace concurrency
|
||||
40
src/concurrency/PeriodicScheduler.h
Normal file
40
src/concurrency/PeriodicScheduler.h
Normal file
@@ -0,0 +1,40 @@
|
||||
#pragma once
|
||||
|
||||
#include "Lock.h"
|
||||
#include <cstdint>
|
||||
#include <unordered_set>
|
||||
|
||||
namespace concurrency {
|
||||
|
||||
class PeriodicTask;
|
||||
|
||||
/**
|
||||
* @brief Runs all PeriodicTasks in the system. Currently called from main loop()
|
||||
* but eventually should be its own thread blocked on a freertos timer.
|
||||
*/
|
||||
class PeriodicScheduler
|
||||
{
|
||||
friend class PeriodicTask;
|
||||
|
||||
/**
|
||||
* This really should be some form of heap, and when the period gets changed on a task it should get
|
||||
* rescheduled in that heap. Currently it is just a dumb array and everytime we run loop() we check
|
||||
* _every_ tasks. If it was a heap we'd only have to check the first task.
|
||||
*/
|
||||
std::unordered_set<PeriodicTask *> tasks;
|
||||
|
||||
// Protects the above variables.
|
||||
Lock lock;
|
||||
|
||||
public:
|
||||
/// Run any next tasks which are due for execution
|
||||
void loop();
|
||||
|
||||
private:
|
||||
void schedule(PeriodicTask *t);
|
||||
void unschedule(PeriodicTask *t);
|
||||
};
|
||||
|
||||
extern PeriodicScheduler periodicScheduler;
|
||||
|
||||
} // namespace concurrency
|
||||
16
src/concurrency/PeriodicTask.cpp
Normal file
16
src/concurrency/PeriodicTask.cpp
Normal file
@@ -0,0 +1,16 @@
|
||||
#include "PeriodicTask.h"
|
||||
#include "Periodic.h"
|
||||
#include "LockGuard.h"
|
||||
|
||||
namespace concurrency {
|
||||
|
||||
PeriodicScheduler periodicScheduler;
|
||||
|
||||
PeriodicTask::PeriodicTask(uint32_t initialPeriod) : period(initialPeriod) {}
|
||||
|
||||
void PeriodicTask::setup()
|
||||
{
|
||||
periodicScheduler.schedule(this);
|
||||
}
|
||||
|
||||
} // namespace concurrency
|
||||
56
src/concurrency/PeriodicTask.h
Normal file
56
src/concurrency/PeriodicTask.h
Normal file
@@ -0,0 +1,56 @@
|
||||
#pragma once
|
||||
|
||||
#include "PeriodicScheduler.h"
|
||||
#include "timing.h"
|
||||
|
||||
namespace concurrency {
|
||||
|
||||
/**
|
||||
* @brief A base class for tasks that want their doTask() method invoked periodically
|
||||
*
|
||||
* @todo currently just syntatic sugar for polling in loop (you must call .loop), but eventually
|
||||
* generalize with the freertos scheduler so we can save lots of power by having everything either in
|
||||
* something like this or triggered off of an irq.
|
||||
*/
|
||||
class PeriodicTask
|
||||
{
|
||||
friend class PeriodicScheduler;
|
||||
|
||||
uint32_t lastMsec = 0;
|
||||
uint32_t period = 1; // call soon after creation
|
||||
|
||||
public:
|
||||
virtual ~PeriodicTask() { periodicScheduler.unschedule(this); }
|
||||
|
||||
/**
|
||||
* Constructor (will schedule with the global PeriodicScheduler)
|
||||
*/
|
||||
PeriodicTask(uint32_t initialPeriod = 1);
|
||||
|
||||
/**
|
||||
* MUST be be called once at startup (but after threading is running - i.e. not from a constructor)
|
||||
*/
|
||||
void setup();
|
||||
|
||||
/**
|
||||
* Set a new period in msecs (can be called from doTask or elsewhere and the scheduler will cope)
|
||||
* While zero this task is disabled and will not run
|
||||
*/
|
||||
void setPeriod(uint32_t p)
|
||||
{
|
||||
lastMsec = timing::millis(); // reset starting from now
|
||||
period = p;
|
||||
}
|
||||
|
||||
uint32_t getPeriod() const { return period; }
|
||||
|
||||
/**
|
||||
* Syntatic sugar for suspending tasks
|
||||
*/
|
||||
void disable() { setPeriod(0); }
|
||||
|
||||
protected:
|
||||
virtual void doTask() = 0;
|
||||
};
|
||||
|
||||
} // namespace concurrency
|
||||
46
src/concurrency/Thread.cpp
Normal file
46
src/concurrency/Thread.cpp
Normal file
@@ -0,0 +1,46 @@
|
||||
#include "Thread.h"
|
||||
#include "timing.h"
|
||||
#include <assert.h>
|
||||
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
#include "esp_task_wdt.h"
|
||||
#endif
|
||||
|
||||
namespace concurrency
|
||||
{
|
||||
|
||||
void Thread::start(const char *name, size_t stackSize, uint32_t priority)
|
||||
{
|
||||
auto r = xTaskCreate(callRun, name, stackSize, this, priority, &taskHandle);
|
||||
assert(r == pdPASS);
|
||||
}
|
||||
|
||||
void Thread::callRun(void *_this)
|
||||
{
|
||||
((Thread *)_this)->doRun();
|
||||
}
|
||||
|
||||
void Thread::serviceWatchdog()
|
||||
{
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
esp_task_wdt_reset();
|
||||
#endif
|
||||
}
|
||||
|
||||
void Thread::startWatchdog()
|
||||
{
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
auto r = esp_task_wdt_add(taskHandle);
|
||||
assert(r == ESP_OK);
|
||||
#endif
|
||||
}
|
||||
|
||||
void Thread::stopWatchdog()
|
||||
{
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
auto r = esp_task_wdt_delete(taskHandle);
|
||||
assert(r == ESP_OK);
|
||||
#endif
|
||||
}
|
||||
|
||||
} // namespace concurrency
|
||||
47
src/concurrency/Thread.h
Normal file
47
src/concurrency/Thread.h
Normal file
@@ -0,0 +1,47 @@
|
||||
#pragma once
|
||||
|
||||
#include "freertosinc.h"
|
||||
|
||||
namespace concurrency {
|
||||
|
||||
/**
|
||||
* @brief Base threading
|
||||
*/
|
||||
class Thread
|
||||
{
|
||||
protected:
|
||||
TaskHandle_t taskHandle = NULL;
|
||||
|
||||
/**
|
||||
* set this to true to ask thread to cleanly exit asap
|
||||
*/
|
||||
volatile bool wantExit = false;
|
||||
|
||||
public:
|
||||
void start(const char *name, size_t stackSize = 1024, uint32_t priority = tskIDLE_PRIORITY);
|
||||
|
||||
virtual ~Thread() { vTaskDelete(taskHandle); }
|
||||
|
||||
uint32_t getStackHighwaterMark() { return uxTaskGetStackHighWaterMark(taskHandle); }
|
||||
|
||||
protected:
|
||||
/**
|
||||
* The method that will be called when start is called.
|
||||
*/
|
||||
virtual void doRun() = 0;
|
||||
|
||||
/**
|
||||
* All thread run methods must periodically call serviceWatchdog, or the system will declare them hung and panic.
|
||||
*
|
||||
* this only applies after startWatchdog() has been called. If you need to sleep for a long time call stopWatchdog()
|
||||
*/
|
||||
void serviceWatchdog();
|
||||
void startWatchdog();
|
||||
void stopWatchdog();
|
||||
|
||||
private:
|
||||
static void callRun(void *_this);
|
||||
};
|
||||
|
||||
|
||||
} // namespace concurrency
|
||||
32
src/concurrency/WorkerThread.cpp
Normal file
32
src/concurrency/WorkerThread.cpp
Normal file
@@ -0,0 +1,32 @@
|
||||
#include "WorkerThread.h"
|
||||
#include "timing.h"
|
||||
|
||||
namespace concurrency {
|
||||
|
||||
void WorkerThread::doRun()
|
||||
{
|
||||
startWatchdog();
|
||||
|
||||
while (!wantExit) {
|
||||
stopWatchdog();
|
||||
block();
|
||||
startWatchdog();
|
||||
|
||||
// no need - startWatchdog is guaranteed to give us one full watchdog interval
|
||||
// serviceWatchdog(); // Let our loop worker have one full watchdog interval (at least) to run
|
||||
|
||||
#ifdef DEBUG_STACK
|
||||
static uint32_t lastPrint = 0;
|
||||
if (timing::millis() - lastPrint > 10 * 1000L) {
|
||||
lastPrint = timing::millis();
|
||||
meshtastic::printThreadInfo("net");
|
||||
}
|
||||
#endif
|
||||
|
||||
loop();
|
||||
}
|
||||
|
||||
stopWatchdog();
|
||||
}
|
||||
|
||||
} // namespace concurrency
|
||||
29
src/concurrency/WorkerThread.h
Normal file
29
src/concurrency/WorkerThread.h
Normal file
@@ -0,0 +1,29 @@
|
||||
#pragma once
|
||||
|
||||
#include "Thread.h"
|
||||
|
||||
namespace concurrency {
|
||||
|
||||
/**
|
||||
* @brief This wraps threading (FreeRTOS for now) with a blocking API intended for efficiently converting
|
||||
* old-school arduino loop() code. Use as a mixin base class for the classes you want to convert.
|
||||
*
|
||||
* @link https://www.freertos.org/RTOS_Task_Notification_As_Mailbox.html
|
||||
*/
|
||||
class WorkerThread : public Thread
|
||||
{
|
||||
protected:
|
||||
/**
|
||||
* A method that should block execution - either waiting ona queue/mutex or a "task notification"
|
||||
*/
|
||||
virtual void block() = 0;
|
||||
|
||||
virtual void loop() = 0;
|
||||
|
||||
/**
|
||||
* The method that will be called when start is called.
|
||||
*/
|
||||
virtual void doRun();
|
||||
};
|
||||
|
||||
} // namespace concurrency
|
||||
@@ -55,13 +55,17 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
/// Convert a preprocessor name into a quoted string and if that string is empty use "unset"
|
||||
#define optstr(s) (xstr(s)[0] ? xstr(s) : "unset")
|
||||
|
||||
#ifdef NRF52840_XXAA // All of the NRF52 targets are configured using variant.h, so this section shouldn't need to be
|
||||
// board specific
|
||||
#ifdef NRF52_SERIES // All of the NRF52 targets are configured using variant.h, so this section shouldn't need to be
|
||||
// board specific
|
||||
|
||||
//
|
||||
// Standard definitions for NRF52 targets
|
||||
//
|
||||
|
||||
// Nop definition for these attributes - not used on NRF52
|
||||
#define EXT_RAM_ATTR
|
||||
#define IRAM_ATTR
|
||||
|
||||
#define NO_ESP32 // Don't use ESP32 libs (mainly bluetooth)
|
||||
|
||||
// We bind to the GPS using variant.h instead for this platform (Serial1)
|
||||
@@ -70,7 +74,11 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#define RTC_DATA_ATTR
|
||||
|
||||
#define LED_PIN PIN_LED1 // LED1 on nrf52840-DK
|
||||
|
||||
// If the variant filed defines as standard button
|
||||
#ifdef PIN_BUTTON1
|
||||
#define BUTTON_PIN PIN_BUTTON1
|
||||
#endif
|
||||
|
||||
// FIXME, use variant.h defs for all of this!!! (even on the ESP32 targets)
|
||||
#elif defined(CubeCell_BoardPlus)
|
||||
@@ -105,10 +113,13 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
// LoRa SPI
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
#define SCK_GPIO 5
|
||||
#define MISO_GPIO 19
|
||||
#define MOSI_GPIO 27
|
||||
#define NSS_GPIO 18
|
||||
// NRF52 boards will define this in variant.h
|
||||
#ifndef RF95_SCK
|
||||
#define RF95_SCK 5
|
||||
#define RF95_MISO 19
|
||||
#define RF95_MOSI 27
|
||||
#define RF95_NSS 18
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
@@ -144,19 +155,32 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#define I2C_SDA 21
|
||||
#define I2C_SCL 22
|
||||
|
||||
#define BUTTON_PIN 38 // The middle button GPIO on the T-Beam
|
||||
#define BUTTON_PIN_ALT 13 // Alternate GPIO for an external button if needed
|
||||
#define BUTTON_PIN 38 // The middle button GPIO on the T-Beam
|
||||
#define BUTTON_PIN_ALT 13 // Alternate GPIO for an external button if needed
|
||||
|
||||
#ifndef USE_JTAG
|
||||
#define RESET_GPIO 14
|
||||
// TTGO uses a common pinout for their SX1262 vs RF95 modules - both can be enabled and we will probe at runtime for RF95 and if
|
||||
// not found then probe for SX1262
|
||||
#define USE_RF95
|
||||
#define USE_SX1262
|
||||
|
||||
#define LORA_DIO0 26 // a No connect on the SX1262 module
|
||||
#define LORA_RESET 23
|
||||
#define LORA_DIO1 33 // SX1262 IRQ
|
||||
#define LORA_DIO2 32 // SX1262 BUSY
|
||||
#define LORA_DIO3 // Not connected on PCB, but internally on the TTGO SX1262, if DIO3 is high the TXCO is enabled
|
||||
|
||||
#ifdef USE_SX1262
|
||||
#define SX1262_CS RF95_NSS // FIXME - we really should define LORA_CS instead
|
||||
#define SX1262_DIO1 LORA_DIO1
|
||||
#define SX1262_BUSY LORA_DIO2
|
||||
#define SX1262_RESET LORA_RESET
|
||||
#define SX1262_E22 // Not really an E22 but TTGO seems to be trying to clone that
|
||||
// Internally the TTGO module hooks the SX1262-DIO2 in to control the TX/RX switch (which is the default for the sx1262interface
|
||||
// code)
|
||||
#endif
|
||||
#define RF95_IRQ_GPIO 26
|
||||
#define DIO1_GPIO 33 // Note: not really used on this board
|
||||
#define DIO2_GPIO 32 // Note: not really used on this board
|
||||
|
||||
// Leave undefined to disable our PMU IRQ handler
|
||||
#define PMU_IRQ 35
|
||||
|
||||
#define AXP192_SLAVE_ADDRESS 0x34
|
||||
|
||||
#elif defined(TBEAM_V07)
|
||||
@@ -169,13 +193,15 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#define I2C_SCL 22
|
||||
|
||||
#define BUTTON_PIN 39
|
||||
#define BATTERY_PIN 35 // A battery voltage measurement pin, voltage divider connected here to measure battery voltage
|
||||
|
||||
#ifndef USE_JTAG
|
||||
#define RESET_GPIO 23
|
||||
#endif
|
||||
#define RF95_IRQ_GPIO 26
|
||||
#define DIO1_GPIO 33 // Note: not really used on this board
|
||||
#define DIO2_GPIO 32 // Note: not really used on this board
|
||||
#define USE_RF95
|
||||
|
||||
#define USE_RF95
|
||||
#define LORA_DIO0 26 // a No connect on the SX1262 module
|
||||
#define LORA_RESET 23
|
||||
#define LORA_DIO1 33 // Not really used
|
||||
#define LORA_DIO2 32 // Not really used
|
||||
|
||||
// This board has different GPS pins than all other boards
|
||||
#undef GPS_RX_PIN
|
||||
@@ -205,15 +231,17 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#define LED_PIN 25 // If defined we will blink this LED
|
||||
#define BUTTON_PIN 0 // If defined, this will be used for user button presses
|
||||
|
||||
#define USE_RF95
|
||||
#define LORA_DIO0 26 // a No connect on the SX1262 module
|
||||
#ifndef USE_JTAG
|
||||
#define RESET_GPIO 14 // If defined, this pin will be used to reset the LORA radio
|
||||
#define LORA_RESET 14
|
||||
#endif
|
||||
#define RF95_IRQ_GPIO 26
|
||||
#define DIO1_GPIO 35 // DIO1 & DIO2 are not currently used, but they must be assigned to a pin number
|
||||
#define DIO2_GPIO 34 // DIO1 & DIO2 are not currently used, but they must be assigned to a pin number
|
||||
#elif defined(TTGO_LORA_V1)
|
||||
#define LORA_DIO1 35 // Not really used
|
||||
#define LORA_DIO2 34 // Not really used
|
||||
|
||||
#elif defined(TLORA_V1)
|
||||
// This string must exactly match the case used in release file names or the android updater won't work
|
||||
#define HW_VENDOR "ttgo-lora32-v1"
|
||||
#define HW_VENDOR "tlora-v1"
|
||||
#undef GPS_RX_PIN
|
||||
#undef GPS_TX_PIN
|
||||
#define GPS_RX_PIN 36
|
||||
@@ -228,13 +256,20 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#define LED_PIN 2 // If defined we will blink this LED
|
||||
#define BUTTON_PIN 0 // If defined, this will be used for user button presses
|
||||
|
||||
#define RESET_GPIO 14 // If defined, this pin will be used to reset the LORA radio
|
||||
#define RF95_IRQ_GPIO 26 // IRQ line for the LORA radio
|
||||
#define DIO1_GPIO 35 // DIO1 & DIO2 are not currently used, but they must be assigned to a pin number
|
||||
#define DIO2_GPIO 34 // DIO1 & DIO2 are not currently used, but they must be assigned to a pin number
|
||||
#elif defined(TTGO_LORA_V2)
|
||||
#define USE_RF95
|
||||
#define LORA_DIO0 26 // a No connect on the SX1262 module
|
||||
#define LORA_RESET 14
|
||||
#define LORA_DIO1 35 // Not really used
|
||||
#define LORA_DIO2 34 // Not really used
|
||||
|
||||
#elif defined(TLORA_V2)
|
||||
// This string must exactly match the case used in release file names or the android updater won't work
|
||||
#define HW_VENDOR "ttgo-lora32-v2"
|
||||
#define HW_VENDOR "tlora-v2"
|
||||
|
||||
#undef GPS_RX_PIN
|
||||
#undef GPS_TX_PIN
|
||||
#define GPS_RX_PIN 36
|
||||
#define GPS_TX_PIN 13 // per @eugene
|
||||
|
||||
#define I2C_SDA 21 // I2C pins for this board
|
||||
#define I2C_SCL 22
|
||||
@@ -245,12 +280,42 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#define LED_PIN 25 // If defined we will blink this LED
|
||||
#define BUTTON_PIN \
|
||||
0 // If defined, this will be used for user button presses, if your board doesn't have a physical switch, you can wire one
|
||||
// between this pin and ground
|
||||
// between this pin and ground
|
||||
|
||||
#define USE_RF95
|
||||
#define LORA_DIO0 26 // a No connect on the SX1262 module
|
||||
#define LORA_RESET 14
|
||||
#define LORA_DIO1 35 // Not really used
|
||||
#define LORA_DIO2 34 // Not really used
|
||||
|
||||
#elif defined(TLORA_V2_1_16)
|
||||
// This string must exactly match the case used in release file names or the android updater won't work
|
||||
#define HW_VENDOR "tlora-v2-1-1.6"
|
||||
|
||||
#undef GPS_RX_PIN
|
||||
#undef GPS_TX_PIN
|
||||
#define GPS_RX_PIN 36
|
||||
#define GPS_TX_PIN 39
|
||||
|
||||
#define BATTERY_PIN 35 // A battery voltage measurement pin, voltage divider connected here to measure battery voltage
|
||||
|
||||
#define I2C_SDA 21 // I2C pins for this board
|
||||
#define I2C_SCL 22
|
||||
|
||||
#define RESET_OLED 16 // If defined, this pin will be used to reset the display controller
|
||||
|
||||
#define VEXT_ENABLE 21 // active low, powers the oled display and the lora antenna boost
|
||||
#define LED_PIN 25 // If defined we will blink this LED
|
||||
#define BUTTON_PIN \
|
||||
12 // If defined, this will be used for user button presses, if your board doesn't have a physical switch, you can wire one
|
||||
// between this pin and ground
|
||||
|
||||
#define USE_RF95
|
||||
#define LORA_DIO0 26 // a No connect on the SX1262 module
|
||||
#define LORA_RESET 14
|
||||
#define LORA_DIO1 35 // Not really used
|
||||
#define LORA_DIO2 34 // Not really used
|
||||
|
||||
#define RESET_GPIO 14 // If defined, this pin will be used to reset the LORA radio
|
||||
#define RF95_IRQ_GPIO 26 // IRQ line for the LORA radio
|
||||
#define DIO1_GPIO 35 // DIO1 & DIO2 are not currently used, but they must be assigned to a pin number
|
||||
#define DIO2_GPIO 34 // DIO1 & DIO2 are not currently used, but they must be assigned to a pin number
|
||||
#endif
|
||||
|
||||
#ifdef ARDUINO_NRF52840_PCA10056
|
||||
@@ -262,25 +327,32 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#undef LED_INVERTED
|
||||
#define LED_INVERTED 1
|
||||
|
||||
// Uncomment to confirm if we can build the RF95 driver for NRF52
|
||||
#if 0
|
||||
#define RESET_GPIO 14 // If defined, this pin will be used to reset the LORA radio
|
||||
#define RF95_IRQ_GPIO 26 // IRQ line for the LORA radio
|
||||
#define DIO1_GPIO 35 // DIO1 & DIO2 are not currently used, but they must be assigned to a pin number
|
||||
#define DIO2_GPIO 34 // DIO1 & DIO2 are not currently used, but they must be assigned to a pin number
|
||||
#endif
|
||||
|
||||
#elif defined(ARDUINO_NRF52840_PPR)
|
||||
|
||||
#define HW_VENDOR "ppr"
|
||||
|
||||
#elif NRF52_SERIES
|
||||
|
||||
#define HW_VENDOR "nrf52unknown" // FIXME - unknown nrf52 board
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef USE_RF95
|
||||
#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
|
||||
#define RF95_DIO2 LORA_DIO2 // Note: not really used for RF95
|
||||
#endif
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// DEBUG
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
#ifdef CONSOLE_MAX_BAUD
|
||||
#define SERIAL_BAUD CONSOLE_MAX_BAUD
|
||||
#else
|
||||
#define SERIAL_BAUD 921600 // Serial debug baud rate
|
||||
#endif
|
||||
|
||||
#include "SerialConsole.h"
|
||||
|
||||
@@ -288,13 +360,25 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
// What platforms should use SEGGER?
|
||||
#ifdef NRF52_SERIES
|
||||
|
||||
// Always include the SEGGER code on NRF52 - because useful for debugging
|
||||
#include "SEGGER_RTT.h"
|
||||
|
||||
// Debug printing to segger console
|
||||
#define SEGGER_MSG(...) SEGGER_RTT_printf(0, __VA_ARGS__)
|
||||
|
||||
// If we are not on a NRF52840 (which has built in USB-ACM serial support) and we don't have serial pins hooked up, then we MUST
|
||||
// use SEGGER for debug output
|
||||
#if !defined(PIN_SERIAL_RX) && !defined(NRF52840_XXAA)
|
||||
// No serial ports on this board - ONLY use segger in memory console
|
||||
#define USE_SEGGER
|
||||
#endif
|
||||
|
||||
#else
|
||||
#define SERIAL0_RX_GPIO 3 // Always GPIO3 on ESP32
|
||||
#endif
|
||||
|
||||
#ifdef USE_SEGGER
|
||||
#include "SEGGER_RTT.h"
|
||||
#define DEBUG_MSG(...) SEGGER_RTT_printf(0, __VA_ARGS__)
|
||||
#else
|
||||
#ifdef DEBUG_PORT
|
||||
|
||||
@@ -1,20 +0,0 @@
|
||||
#include "debug.h"
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#include "freertosinc.h"
|
||||
#include "configuration.h"
|
||||
|
||||
namespace meshtastic
|
||||
{
|
||||
|
||||
void printThreadInfo(const char *extra)
|
||||
{
|
||||
#ifndef NO_ESP32
|
||||
uint32_t taskHandle = reinterpret_cast<uint32_t>(xTaskGetCurrentTaskHandle());
|
||||
DEBUG_MSG("printThreadInfo(%s) task: %" PRIx32 " core id: %u min free stack: %u\n", extra, taskHandle, xPortGetCoreID(),
|
||||
uxTaskGetStackHighWaterMark(nullptr));
|
||||
#endif
|
||||
}
|
||||
|
||||
} // namespace meshtastic
|
||||
10
src/debug.h
10
src/debug.h
@@ -1,10 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
namespace meshtastic
|
||||
{
|
||||
|
||||
/// Dumps out which core we are running on, and min level of remaining stack
|
||||
/// seen.
|
||||
void printThreadInfo(const char *extra);
|
||||
|
||||
} // namespace meshtastic
|
||||
@@ -3,7 +3,7 @@
|
||||
#include <Arduino.h>
|
||||
|
||||
/// Error codes for critical error
|
||||
enum CriticalErrorCode { NoError, ErrTxWatchdog, ErrSleepEnterWait, ErrNoRadio, ErrUnspecified };
|
||||
enum CriticalErrorCode { NoError, ErrTxWatchdog, ErrSleepEnterWait, ErrNoRadio, ErrUnspecified, UBloxInitFailed };
|
||||
|
||||
/// Record an error that should be reported via analytics
|
||||
void recordCriticalError(CriticalErrorCode code, uint32_t address = 0);
|
||||
|
||||
@@ -1,47 +1,41 @@
|
||||
#include <Arduino.h>
|
||||
|
||||
#include "../concurrency/LockGuard.h"
|
||||
#include "../timing.h"
|
||||
#include "BluetoothSoftwareUpdate.h"
|
||||
#include "BluetoothUtil.h"
|
||||
#include "CallbackCharacteristic.h"
|
||||
#include "PowerFSM.h"
|
||||
#include "RadioLibInterface.h"
|
||||
#include "configuration.h"
|
||||
#include "lock.h"
|
||||
#include <Arduino.h>
|
||||
#include <BLE2902.h>
|
||||
#include "nimble/BluetoothUtil.h"
|
||||
|
||||
#include <CRC32.h>
|
||||
#include <Update.h>
|
||||
#include <esp_gatt_defs.h>
|
||||
|
||||
using namespace meshtastic;
|
||||
int16_t updateResultHandle = -1;
|
||||
|
||||
CRC32 crc;
|
||||
uint32_t rebootAtMsec = 0; // If not zero we will reboot at this time (used to reboot shortly after the update completes)
|
||||
static CRC32 crc;
|
||||
static uint32_t rebootAtMsec = 0; // If not zero we will reboot at this time (used to reboot shortly after the update completes)
|
||||
|
||||
uint32_t updateExpectedSize, updateActualSize;
|
||||
static uint32_t updateExpectedSize, updateActualSize;
|
||||
|
||||
Lock *updateLock;
|
||||
static concurrency::Lock *updateLock;
|
||||
|
||||
class TotalSizeCharacteristic : public CallbackCharacteristic
|
||||
/// Handle writes & reads to total size
|
||||
int update_size_callback(uint16_t conn_handle, uint16_t attr_handle, struct ble_gatt_access_ctxt *ctxt, void *arg)
|
||||
{
|
||||
public:
|
||||
TotalSizeCharacteristic()
|
||||
: CallbackCharacteristic("e74dd9c0-a301-4a6f-95a1-f0e1dbea8e1e",
|
||||
BLECharacteristic::PROPERTY_WRITE | BLECharacteristic::PROPERTY_READ)
|
||||
{
|
||||
}
|
||||
concurrency::LockGuard g(updateLock);
|
||||
|
||||
void onWrite(BLECharacteristic *c)
|
||||
{
|
||||
LockGuard g(updateLock);
|
||||
// Check if there is enough to OTA Update
|
||||
uint32_t len = getValue32(c, 0);
|
||||
updateExpectedSize = len;
|
||||
// Check if there is enough to OTA Update
|
||||
chr_readwrite32le(&updateExpectedSize, ctxt);
|
||||
|
||||
if (ctxt->op == BLE_GATT_ACCESS_OP_WRITE_CHR && updateExpectedSize != 0) {
|
||||
updateActualSize = 0;
|
||||
crc.reset();
|
||||
bool canBegin = Update.begin(len);
|
||||
DEBUG_MSG("Setting update size %u, result %d\n", len, canBegin);
|
||||
bool canBegin = Update.begin(updateExpectedSize);
|
||||
DEBUG_MSG("Setting update size %u, result %d\n", updateExpectedSize, canBegin);
|
||||
if (!canBegin) {
|
||||
// Indicate failure by forcing the size to 0
|
||||
uint32_t zero = 0;
|
||||
c->setValue(zero);
|
||||
// Indicate failure by forcing the size to 0 (client will read it back)
|
||||
updateExpectedSize = 0;
|
||||
} else {
|
||||
// This totally breaks abstraction to up up into the app layer for this, but quick hack to make sure we only
|
||||
// talk to one service during the sw update.
|
||||
@@ -54,77 +48,85 @@ class TotalSizeCharacteristic : public CallbackCharacteristic
|
||||
// writing flash - shut the radio off during updates
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define MAX_BLOCKSIZE 512
|
||||
|
||||
class DataCharacteristic : public CallbackCharacteristic
|
||||
/// Handle writes to data
|
||||
int update_data_callback(uint16_t conn_handle, uint16_t attr_handle, struct ble_gatt_access_ctxt *ctxt, void *arg)
|
||||
{
|
||||
public:
|
||||
DataCharacteristic() : CallbackCharacteristic("e272ebac-d463-4b98-bc84-5cc1a39ee517", BLECharacteristic::PROPERTY_WRITE) {}
|
||||
concurrency::LockGuard g(updateLock);
|
||||
|
||||
void onWrite(BLECharacteristic *c)
|
||||
{
|
||||
LockGuard g(updateLock);
|
||||
std::string value = c->getValue();
|
||||
uint32_t len = value.length();
|
||||
assert(len <= MAX_BLOCKSIZE);
|
||||
static uint8_t
|
||||
data[MAX_BLOCKSIZE]; // we temporarily copy here because I'm worried that a fast sender might be able overwrite srcbuf
|
||||
memcpy(data, c->getData(), len);
|
||||
// DEBUG_MSG("Writing %u\n", len);
|
||||
crc.update(data, len);
|
||||
Update.write(data, len);
|
||||
updateActualSize += len;
|
||||
powerFSM.trigger(EVENT_RECEIVED_TEXT_MSG); // Not exactly correct, but we want to force the device to not sleep now
|
||||
}
|
||||
};
|
||||
static uint8_t
|
||||
data[MAX_BLOCKSIZE]; // we temporarily copy here because I'm worried that a fast sender might be able overwrite srcbuf
|
||||
|
||||
static BLECharacteristic *resultC;
|
||||
uint16_t len = 0;
|
||||
|
||||
class CRC32Characteristic : public CallbackCharacteristic
|
||||
auto rc = ble_hs_mbuf_to_flat(ctxt->om, data, sizeof(data), &len);
|
||||
assert(rc == 0);
|
||||
|
||||
// DEBUG_MSG("Writing %u\n", len);
|
||||
crc.update(data, len);
|
||||
Update.write(data, len);
|
||||
updateActualSize += len;
|
||||
powerFSM.trigger(EVENT_RECEIVED_TEXT_MSG); // Not exactly correct, but we want to force the device to not sleep now
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static uint8_t update_result;
|
||||
|
||||
/// Handle writes to crc32
|
||||
int update_crc32_callback(uint16_t conn_handle, uint16_t attr_handle, struct ble_gatt_access_ctxt *ctxt, void *arg)
|
||||
{
|
||||
public:
|
||||
CRC32Characteristic() : CallbackCharacteristic("4826129c-c22a-43a3-b066-ce8f0d5bacc6", BLECharacteristic::PROPERTY_WRITE) {}
|
||||
concurrency::LockGuard g(updateLock);
|
||||
uint32_t expectedCRC = 0;
|
||||
chr_readwrite32le(&expectedCRC, ctxt);
|
||||
|
||||
void onWrite(BLECharacteristic *c)
|
||||
uint32_t actualCRC = crc.finalize();
|
||||
DEBUG_MSG("expected CRC %u\n", expectedCRC);
|
||||
|
||||
uint8_t result = 0xff;
|
||||
|
||||
if (updateActualSize != updateExpectedSize) {
|
||||
DEBUG_MSG("Expected %u bytes, but received %u bytes!\n", updateExpectedSize, updateActualSize);
|
||||
result = 0xe1; // FIXME, use real error codes
|
||||
} else if (actualCRC != expectedCRC) // Check the CRC before asking the update to happen.
|
||||
{
|
||||
LockGuard g(updateLock);
|
||||
uint32_t expectedCRC = getValue32(c, 0);
|
||||
uint32_t actualCRC = crc.finalize();
|
||||
DEBUG_MSG("expected CRC %u\n", expectedCRC);
|
||||
|
||||
uint8_t result = 0xff;
|
||||
|
||||
if (updateActualSize != updateExpectedSize) {
|
||||
DEBUG_MSG("Expected %u bytes, but received %u bytes!\n", updateExpectedSize, updateActualSize);
|
||||
result = 0xe1; // FIXME, use real error codes
|
||||
} else if (actualCRC != expectedCRC) // Check the CRC before asking the update to happen.
|
||||
{
|
||||
DEBUG_MSG("Invalid CRC! expected=%u, actual=%u\n", expectedCRC, actualCRC);
|
||||
result = 0xe0; // FIXME, use real error codes
|
||||
DEBUG_MSG("Invalid CRC! expected=%u, actual=%u\n", expectedCRC, actualCRC);
|
||||
result = 0xe0; // FIXME, use real error codes
|
||||
} else {
|
||||
if (Update.end()) {
|
||||
DEBUG_MSG("OTA done, rebooting in 5 seconds!\n");
|
||||
rebootAtMsec = timing::millis() + 5000;
|
||||
} else {
|
||||
if (Update.end()) {
|
||||
DEBUG_MSG("OTA done, rebooting in 5 seconds!\n");
|
||||
rebootAtMsec = millis() + 5000;
|
||||
} else {
|
||||
DEBUG_MSG("Error Occurred. Error #: %d\n", Update.getError());
|
||||
}
|
||||
result = Update.getError();
|
||||
DEBUG_MSG("Error Occurred. Error #: %d\n", Update.getError());
|
||||
}
|
||||
|
||||
if (RadioLibInterface::instance)
|
||||
RadioLibInterface::instance->startReceive(); // Resume radio
|
||||
|
||||
assert(resultC);
|
||||
resultC->setValue(&result, 1);
|
||||
resultC->notify();
|
||||
result = Update.getError();
|
||||
}
|
||||
};
|
||||
|
||||
if (RadioLibInterface::instance)
|
||||
RadioLibInterface::instance->startReceive(); // Resume radio
|
||||
|
||||
assert(updateResultHandle >= 0);
|
||||
update_result = result;
|
||||
DEBUG_MSG("BLE notify update result\n");
|
||||
auto res = ble_gattc_notify(curConnectionHandle, updateResultHandle);
|
||||
assert(res == 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int update_result_callback(uint16_t conn_handle, uint16_t attr_handle, struct ble_gatt_access_ctxt *ctxt, void *arg)
|
||||
{
|
||||
return chr_readwrite8(&update_result, sizeof(update_result), ctxt);
|
||||
}
|
||||
|
||||
void bluetoothRebootCheck()
|
||||
{
|
||||
if (rebootAtMsec && millis() > rebootAtMsec) {
|
||||
if (rebootAtMsec && timing::millis() > rebootAtMsec) {
|
||||
DEBUG_MSG("Rebooting for update\n");
|
||||
ESP.restart();
|
||||
}
|
||||
@@ -134,45 +136,15 @@ void bluetoothRebootCheck()
|
||||
See bluetooth-api.md
|
||||
|
||||
*/
|
||||
BLEService *createUpdateService(BLEServer *server, std::string hwVendor, std::string swVersion, std::string hwVersion)
|
||||
void reinitUpdateService()
|
||||
{
|
||||
if (!updateLock)
|
||||
updateLock = new Lock();
|
||||
updateLock = new concurrency::Lock();
|
||||
|
||||
// Create the BLE Service
|
||||
BLEService *service = server->createService(BLEUUID("cb0b9a0b-a84c-4c0d-bdbb-442e3144ee30"), 25, 0);
|
||||
auto res = ble_gatts_count_cfg(gatt_update_svcs); // assigns handles? see docstring for note about clearing the handle list
|
||||
// before calling SLEEP SUPPORT
|
||||
assert(res == 0);
|
||||
|
||||
assert(!resultC);
|
||||
resultC = new BLECharacteristic("5e134862-7411-4424-ac4a-210937432c77",
|
||||
BLECharacteristic::PROPERTY_READ | BLECharacteristic::PROPERTY_NOTIFY);
|
||||
|
||||
addWithDesc(service, new TotalSizeCharacteristic, "total image size");
|
||||
addWithDesc(service, new DataCharacteristic, "data");
|
||||
addWithDesc(service, new CRC32Characteristic, "crc32");
|
||||
addWithDesc(service, resultC, "result code");
|
||||
|
||||
resultC->addDescriptor(addBLEDescriptor(new BLE2902())); // Needed so clients can request notification
|
||||
|
||||
BLECharacteristic *swC =
|
||||
new BLECharacteristic(BLEUUID((uint16_t)ESP_GATT_UUID_SW_VERSION_STR), BLECharacteristic::PROPERTY_READ);
|
||||
swC->setValue(swVersion);
|
||||
service->addCharacteristic(addBLECharacteristic(swC));
|
||||
|
||||
BLECharacteristic *mfC = new BLECharacteristic(BLEUUID((uint16_t)ESP_GATT_UUID_MANU_NAME), BLECharacteristic::PROPERTY_READ);
|
||||
mfC->setValue(hwVendor);
|
||||
service->addCharacteristic(addBLECharacteristic(mfC));
|
||||
|
||||
BLECharacteristic *hwvC =
|
||||
new BLECharacteristic(BLEUUID((uint16_t)ESP_GATT_UUID_HW_VERSION_STR), BLECharacteristic::PROPERTY_READ);
|
||||
hwvC->setValue(hwVersion);
|
||||
service->addCharacteristic(addBLECharacteristic(hwvC));
|
||||
|
||||
return service;
|
||||
res = ble_gatts_add_svcs(gatt_update_svcs);
|
||||
assert(res == 0);
|
||||
}
|
||||
|
||||
void destroyUpdateService()
|
||||
{
|
||||
assert(resultC);
|
||||
|
||||
resultC = NULL;
|
||||
}
|
||||
@@ -1,11 +1,26 @@
|
||||
#pragma once
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <BLEDevice.h>
|
||||
#include <BLEServer.h>
|
||||
#include <BLEUtils.h>
|
||||
#include "nimble/NimbleDefs.h"
|
||||
|
||||
BLEService *createUpdateService(BLEServer *server, std::string hwVendor, std::string swVersion, std::string hwVersion);
|
||||
void reinitUpdateService();
|
||||
|
||||
void destroyUpdateService();
|
||||
void bluetoothRebootCheck();
|
||||
void bluetoothRebootCheck();
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
int update_size_callback(uint16_t conn_handle, uint16_t attr_handle, struct ble_gatt_access_ctxt *ctxt, void *arg);
|
||||
int update_data_callback(uint16_t conn_handle, uint16_t attr_handle, struct ble_gatt_access_ctxt *ctxt, void *arg);
|
||||
int update_result_callback(uint16_t conn_handle, uint16_t attr_handle, struct ble_gatt_access_ctxt *ctxt, void *arg);
|
||||
int update_crc32_callback(uint16_t conn_handle, uint16_t attr_handle, struct ble_gatt_access_ctxt *ctxt, void *arg);
|
||||
|
||||
extern const struct ble_gatt_svc_def gatt_update_svcs[];
|
||||
|
||||
extern const ble_uuid128_t update_result_uuid;
|
||||
|
||||
extern int16_t updateResultHandle;
|
||||
|
||||
#ifdef __cplusplus
|
||||
};
|
||||
#endif
|
||||
@@ -1,323 +0,0 @@
|
||||
#include "BluetoothUtil.h"
|
||||
#include "BluetoothSoftwareUpdate.h"
|
||||
#include "configuration.h"
|
||||
#include <Arduino.h>
|
||||
#include <BLE2902.h>
|
||||
#include <Update.h>
|
||||
#include <esp_gatt_defs.h>
|
||||
|
||||
SimpleAllocator btPool;
|
||||
|
||||
bool _BLEClientConnected = false;
|
||||
|
||||
class MyServerCallbacks : public BLEServerCallbacks
|
||||
{
|
||||
void onConnect(BLEServer *pServer) { _BLEClientConnected = true; };
|
||||
|
||||
void onDisconnect(BLEServer *pServer) { _BLEClientConnected = false; }
|
||||
};
|
||||
|
||||
#define MAX_DESCRIPTORS 32
|
||||
#define MAX_CHARACTERISTICS 32
|
||||
|
||||
static BLECharacteristic *chars[MAX_CHARACTERISTICS];
|
||||
static size_t numChars;
|
||||
static BLEDescriptor *descs[MAX_DESCRIPTORS];
|
||||
static size_t numDescs;
|
||||
|
||||
/// Add a characteristic that we will delete when we restart
|
||||
BLECharacteristic *addBLECharacteristic(BLECharacteristic *c)
|
||||
{
|
||||
assert(numChars < MAX_CHARACTERISTICS);
|
||||
chars[numChars++] = c;
|
||||
return c;
|
||||
}
|
||||
|
||||
/// Add a characteristic that we will delete when we restart
|
||||
BLEDescriptor *addBLEDescriptor(BLEDescriptor *c)
|
||||
{
|
||||
assert(numDescs < MAX_DESCRIPTORS);
|
||||
descs[numDescs++] = c;
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
// Help routine to add a description to any BLECharacteristic and add it to the service
|
||||
// We default to require an encrypted BOND for all these these characterstics
|
||||
void addWithDesc(BLEService *service, BLECharacteristic *c, const char *description)
|
||||
{
|
||||
c->setAccessPermissions(ESP_GATT_PERM_READ_ENCRYPTED | ESP_GATT_PERM_WRITE_ENCRYPTED);
|
||||
|
||||
BLEDescriptor *desc = new BLEDescriptor(BLEUUID((uint16_t)ESP_GATT_UUID_CHAR_DESCRIPTION), strlen(description) + 1);
|
||||
assert(desc);
|
||||
desc->setAccessPermissions(ESP_GATT_PERM_READ_ENCRYPTED | ESP_GATT_PERM_WRITE_ENCRYPTED);
|
||||
desc->setValue(description);
|
||||
c->addDescriptor(desc);
|
||||
service->addCharacteristic(c);
|
||||
addBLECharacteristic(c);
|
||||
addBLEDescriptor(desc);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create standard device info service
|
||||
**/
|
||||
BLEService *createDeviceInfomationService(BLEServer *server, std::string hwVendor, std::string swVersion,
|
||||
std::string hwVersion = "")
|
||||
{
|
||||
BLEService *deviceInfoService = server->createService(BLEUUID((uint16_t)ESP_GATT_UUID_DEVICE_INFO_SVC));
|
||||
|
||||
BLECharacteristic *swC =
|
||||
new BLECharacteristic(BLEUUID((uint16_t)ESP_GATT_UUID_SW_VERSION_STR), BLECharacteristic::PROPERTY_READ);
|
||||
BLECharacteristic *mfC = new BLECharacteristic(BLEUUID((uint16_t)ESP_GATT_UUID_MANU_NAME), BLECharacteristic::PROPERTY_READ);
|
||||
// BLECharacteristic SerialNumberCharacteristic(BLEUUID((uint16_t) ESP_GATT_UUID_SERIAL_NUMBER_STR),
|
||||
// BLECharacteristic::PROPERTY_READ);
|
||||
|
||||
/*
|
||||
* Mandatory characteristic for device info service?
|
||||
|
||||
BLECharacteristic *m_pnpCharacteristic = m_deviceInfoService->createCharacteristic(ESP_GATT_UUID_PNP_ID,
|
||||
BLECharacteristic::PROPERTY_READ);
|
||||
|
||||
uint8_t sig, uint16_t vid, uint16_t pid, uint16_t version;
|
||||
uint8_t pnp[] = { sig, (uint8_t) (vid >> 8), (uint8_t) vid, (uint8_t) (pid >> 8), (uint8_t) pid, (uint8_t) (version >>
|
||||
8), (uint8_t) version }; m_pnpCharacteristic->setValue(pnp, sizeof(pnp));
|
||||
*/
|
||||
swC->setValue(swVersion);
|
||||
deviceInfoService->addCharacteristic(addBLECharacteristic(swC));
|
||||
mfC->setValue(hwVendor);
|
||||
deviceInfoService->addCharacteristic(addBLECharacteristic(mfC));
|
||||
if (!hwVersion.empty()) {
|
||||
BLECharacteristic *hwvC =
|
||||
new BLECharacteristic(BLEUUID((uint16_t)ESP_GATT_UUID_HW_VERSION_STR), BLECharacteristic::PROPERTY_READ);
|
||||
hwvC->setValue(hwVersion);
|
||||
deviceInfoService->addCharacteristic(addBLECharacteristic(hwvC));
|
||||
}
|
||||
// SerialNumberCharacteristic.setValue("FIXME");
|
||||
// deviceInfoService->addCharacteristic(&SerialNumberCharacteristic);
|
||||
|
||||
// m_manufacturerCharacteristic = m_deviceInfoService->createCharacteristic((uint16_t) 0x2a29,
|
||||
// BLECharacteristic::PROPERTY_READ); m_manufacturerCharacteristic->setValue(name);
|
||||
|
||||
/* add these later?
|
||||
ESP_GATT_UUID_SYSTEM_ID
|
||||
*/
|
||||
|
||||
// caller must call service->start();
|
||||
return deviceInfoService;
|
||||
}
|
||||
|
||||
static BLECharacteristic *batteryLevelC;
|
||||
|
||||
/**
|
||||
* Create a battery level service
|
||||
*/
|
||||
BLEService *createBatteryService(BLEServer *server)
|
||||
{
|
||||
// Create the BLE Service
|
||||
BLEService *pBattery = server->createService(BLEUUID((uint16_t)0x180F));
|
||||
|
||||
batteryLevelC = new BLECharacteristic(BLEUUID((uint16_t)ESP_GATT_UUID_BATTERY_LEVEL),
|
||||
BLECharacteristic::PROPERTY_READ | BLECharacteristic::PROPERTY_NOTIFY);
|
||||
|
||||
addWithDesc(pBattery, batteryLevelC, "Percentage 0 - 100");
|
||||
batteryLevelC->addDescriptor(addBLEDescriptor(new BLE2902())); // Needed so clients can request notification
|
||||
|
||||
// I don't think we need to advertise this? and some phones only see the first thing advertised anyways...
|
||||
// server->getAdvertising()->addServiceUUID(pBattery->getUUID());
|
||||
pBattery->start();
|
||||
|
||||
return pBattery;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the battery level we are currently telling clients.
|
||||
* level should be a pct between 0 and 100
|
||||
*/
|
||||
void updateBatteryLevel(uint8_t level)
|
||||
{
|
||||
if (batteryLevelC) {
|
||||
DEBUG_MSG("set BLE battery level %u\n", level);
|
||||
batteryLevelC->setValue(&level, 1);
|
||||
batteryLevelC->notify();
|
||||
}
|
||||
}
|
||||
|
||||
void dumpCharacteristic(BLECharacteristic *c)
|
||||
{
|
||||
std::string value = c->getValue();
|
||||
|
||||
if (value.length() > 0) {
|
||||
DEBUG_MSG("New value: ");
|
||||
for (int i = 0; i < value.length(); i++)
|
||||
DEBUG_MSG("%c", value[i]);
|
||||
|
||||
DEBUG_MSG("\n");
|
||||
}
|
||||
}
|
||||
|
||||
/** converting endianness pull out a 32 bit value */
|
||||
uint32_t getValue32(BLECharacteristic *c, uint32_t defaultValue)
|
||||
{
|
||||
std::string value = c->getValue();
|
||||
uint32_t r = defaultValue;
|
||||
|
||||
if (value.length() == 4)
|
||||
r = value[0] | (value[1] << 8UL) | (value[2] << 16UL) | (value[3] << 24UL);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
class MySecurity : public BLESecurityCallbacks
|
||||
{
|
||||
protected:
|
||||
bool onConfirmPIN(uint32_t pin)
|
||||
{
|
||||
Serial.printf("onConfirmPIN %u\n", pin);
|
||||
return false;
|
||||
}
|
||||
|
||||
uint32_t onPassKeyRequest()
|
||||
{
|
||||
Serial.println("onPassKeyRequest");
|
||||
return 123511; // not used
|
||||
}
|
||||
|
||||
void onPassKeyNotify(uint32_t pass_key)
|
||||
{
|
||||
Serial.printf("onPassKeyNotify %06u\n", pass_key);
|
||||
startCb(pass_key);
|
||||
}
|
||||
|
||||
bool onSecurityRequest()
|
||||
{
|
||||
Serial.println("onSecurityRequest");
|
||||
return true;
|
||||
}
|
||||
|
||||
void onAuthenticationComplete(esp_ble_auth_cmpl_t cmpl)
|
||||
{
|
||||
if (cmpl.success) {
|
||||
uint16_t length;
|
||||
esp_ble_gap_get_whitelist_size(&length);
|
||||
Serial.printf(" authenticated and connected to phone\n");
|
||||
} else {
|
||||
Serial.printf("phone authenticate failed %d\n", cmpl.fail_reason);
|
||||
}
|
||||
|
||||
// Remove our custom PIN request screen.
|
||||
stopCb();
|
||||
}
|
||||
|
||||
public:
|
||||
StartBluetoothPinScreenCallback startCb;
|
||||
StopBluetoothPinScreenCallback stopCb;
|
||||
};
|
||||
|
||||
BLEServer *pServer;
|
||||
|
||||
BLEService *pDevInfo, *pUpdate, *pBattery;
|
||||
|
||||
void deinitBLE()
|
||||
{
|
||||
assert(pServer);
|
||||
|
||||
pServer->getAdvertising()->stop();
|
||||
|
||||
if (pUpdate != NULL) {
|
||||
destroyUpdateService();
|
||||
|
||||
pUpdate->stop(); // we delete them below
|
||||
pUpdate->executeDelete();
|
||||
}
|
||||
|
||||
pBattery->stop();
|
||||
pBattery->executeDelete();
|
||||
|
||||
pDevInfo->stop();
|
||||
pDevInfo->executeDelete();
|
||||
|
||||
// First shutdown bluetooth
|
||||
BLEDevice::deinit(false);
|
||||
|
||||
// do not delete this - it is dynamically allocated, but only once - statically in BLEDevice
|
||||
// delete pServer->getAdvertising();
|
||||
|
||||
if (pUpdate != NULL)
|
||||
delete pUpdate;
|
||||
delete pDevInfo;
|
||||
delete pBattery;
|
||||
delete pServer;
|
||||
|
||||
batteryLevelC = NULL; // Don't let anyone generate bogus notifies
|
||||
|
||||
for (int i = 0; i < numChars; i++) {
|
||||
delete chars[i];
|
||||
}
|
||||
numChars = 0;
|
||||
|
||||
for (int i = 0; i < numDescs; i++)
|
||||
delete descs[i];
|
||||
numDescs = 0;
|
||||
|
||||
btPool.reset();
|
||||
}
|
||||
|
||||
BLEServer *initBLE(StartBluetoothPinScreenCallback startBtPinScreen, StopBluetoothPinScreenCallback stopBtPinScreen,
|
||||
std::string deviceName, std::string hwVendor, std::string swVersion, std::string hwVersion)
|
||||
{
|
||||
BLEDevice::init(deviceName);
|
||||
BLEDevice::setEncryptionLevel(ESP_BLE_SEC_ENCRYPT);
|
||||
|
||||
/*
|
||||
* Required in authentication process to provide displaying and/or input passkey or yes/no butttons confirmation
|
||||
*/
|
||||
static MySecurity mySecurity;
|
||||
mySecurity.startCb = startBtPinScreen;
|
||||
mySecurity.stopCb = stopBtPinScreen;
|
||||
BLEDevice::setSecurityCallbacks(&mySecurity);
|
||||
|
||||
// Create the BLE Server
|
||||
pServer = BLEDevice::createServer();
|
||||
static MyServerCallbacks myCallbacks;
|
||||
pServer->setCallbacks(&myCallbacks);
|
||||
|
||||
pDevInfo = createDeviceInfomationService(pServer, hwVendor, swVersion, hwVersion);
|
||||
|
||||
pBattery = createBatteryService(pServer);
|
||||
|
||||
#define BLE_SOFTWARE_UPDATE
|
||||
#ifdef BLE_SOFTWARE_UPDATE
|
||||
pUpdate = createUpdateService(pServer, hwVendor, swVersion,
|
||||
hwVersion); // We need to advertise this so our android ble scan operation can see it
|
||||
|
||||
pUpdate->start();
|
||||
#endif
|
||||
|
||||
// It seems only one service can be advertised - so for now don't advertise our updater
|
||||
// pServer->getAdvertising()->addServiceUUID(pUpdate->getUUID());
|
||||
|
||||
// start all our services (do this after creating all of them)
|
||||
pDevInfo->start();
|
||||
|
||||
// FIXME turn on this restriction only after the device is paired with a phone
|
||||
// advert->setScanFilter(false, true); // We let anyone scan for us (FIXME, perhaps only allow that until we are paired with a
|
||||
// phone and configured) but only let whitelist phones connect
|
||||
|
||||
static BLESecurity security; // static to avoid allocs
|
||||
BLESecurity *pSecurity = &security;
|
||||
pSecurity->setCapability(ESP_IO_CAP_OUT);
|
||||
|
||||
// FIXME - really should be ESP_LE_AUTH_REQ_SC_BOND but it seems there is a bug right now causing that bonding info to be lost
|
||||
// occasionally?
|
||||
pSecurity->setAuthenticationMode(ESP_LE_AUTH_REQ_SC_BOND);
|
||||
|
||||
pSecurity->setInitEncryptionKey(ESP_BLE_ENC_KEY_MASK | ESP_BLE_ID_KEY_MASK);
|
||||
|
||||
return pServer;
|
||||
}
|
||||
|
||||
// Called from loop
|
||||
void loopBLE()
|
||||
{
|
||||
bluetoothRebootCheck();
|
||||
}
|
||||
@@ -1,42 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <functional>
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <BLEDevice.h>
|
||||
#include <BLEServer.h>
|
||||
#include <BLEUtils.h>
|
||||
#include "SimpleAllocator.h"
|
||||
|
||||
// Now handled by BluetoothUtil.cpp
|
||||
// BLEService *createDeviceInfomationService(BLEServer* server, uint8_t sig, uint16_t vid, uint16_t pid, uint16_t version);
|
||||
|
||||
// Help routine to add a description to any BLECharacteristic and add it to the service
|
||||
void addWithDesc(BLEService *service, BLECharacteristic *c, const char *description);
|
||||
|
||||
void dumpCharacteristic(BLECharacteristic *c);
|
||||
|
||||
/** converting endianness pull out a 32 bit value */
|
||||
uint32_t getValue32(BLECharacteristic *c, uint32_t defaultValue);
|
||||
|
||||
// TODO(girts): create a class for the bluetooth utils helpers?
|
||||
using StartBluetoothPinScreenCallback = std::function<void(uint32_t pass_key)>;
|
||||
using StopBluetoothPinScreenCallback = std::function<void(void)>;
|
||||
|
||||
void loopBLE();
|
||||
BLEServer *initBLE(
|
||||
StartBluetoothPinScreenCallback startBtPinScreen, StopBluetoothPinScreenCallback stopBtPinScreen,
|
||||
std::string devName, std::string hwVendor, std::string swVersion, std::string hwVersion = "");
|
||||
void deinitBLE();
|
||||
|
||||
/// Add a characteristic that we will delete when we restart
|
||||
BLECharacteristic *addBLECharacteristic(BLECharacteristic *c);
|
||||
|
||||
/// Add a characteristic that we will delete when we restart
|
||||
BLEDescriptor *addBLEDescriptor(BLEDescriptor *c);
|
||||
|
||||
/// Given a level between 0-100, update the BLE attribute
|
||||
void updateBatteryLevel(uint8_t level);
|
||||
|
||||
/// Any bluetooth objects you allocate _must_ come from this pool if you want to be able to call deinitBLE()
|
||||
extern SimpleAllocator btPool;
|
||||
@@ -1,143 +0,0 @@
|
||||
#include "MeshBluetoothService.h"
|
||||
#include "BluetoothUtil.h"
|
||||
#include <Arduino.h>
|
||||
#include <BLE2902.h>
|
||||
#include <assert.h>
|
||||
#include <esp_gatt_defs.h>
|
||||
|
||||
#include "CallbackCharacteristic.h"
|
||||
#include "GPS.h"
|
||||
#include "MeshService.h"
|
||||
#include "NodeDB.h"
|
||||
#include "PhoneAPI.h"
|
||||
#include "PowerFSM.h"
|
||||
#include "configuration.h"
|
||||
#include "mesh-pb-constants.h"
|
||||
#include "mesh.pb.h"
|
||||
|
||||
// This scratch buffer is used for various bluetooth reads/writes - but it is safe because only one bt operation can be in
|
||||
// proccess at once
|
||||
static uint8_t trBytes[_max(_max(_max(_max(ToRadio_size, RadioConfig_size), User_size), MyNodeInfo_size), FromRadio_size)];
|
||||
|
||||
static CallbackCharacteristic *meshFromNumCharacteristic;
|
||||
|
||||
BLEService *meshService;
|
||||
|
||||
class BluetoothPhoneAPI : public PhoneAPI
|
||||
{
|
||||
/**
|
||||
* Subclasses can use this as a hook to provide custom notifications for their transport (i.e. bluetooth notifies)
|
||||
*/
|
||||
virtual void onNowHasData(uint32_t fromRadioNum)
|
||||
{
|
||||
PhoneAPI::onNowHasData(fromRadioNum);
|
||||
|
||||
if (meshFromNumCharacteristic) { // this ptr might change from sleep to sleep, or even be null
|
||||
meshFromNumCharacteristic->setValue(fromRadioNum);
|
||||
meshFromNumCharacteristic->notify();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
BluetoothPhoneAPI *bluetoothPhoneAPI;
|
||||
|
||||
|
||||
class ToRadioCharacteristic : public CallbackCharacteristic
|
||||
{
|
||||
public:
|
||||
ToRadioCharacteristic() : CallbackCharacteristic("f75c76d2-129e-4dad-a1dd-7866124401e7", BLECharacteristic::PROPERTY_WRITE) {}
|
||||
|
||||
void onWrite(BLECharacteristic *c)
|
||||
{
|
||||
bluetoothPhoneAPI->handleToRadio(c->getData(), c->getValue().length());
|
||||
}
|
||||
};
|
||||
|
||||
class FromRadioCharacteristic : public CallbackCharacteristic
|
||||
{
|
||||
public:
|
||||
FromRadioCharacteristic() : CallbackCharacteristic("8ba2bcc2-ee02-4a55-a531-c525c5e454d5", BLECharacteristic::PROPERTY_READ)
|
||||
{
|
||||
}
|
||||
|
||||
void onRead(BLECharacteristic *c)
|
||||
{
|
||||
size_t numBytes = bluetoothPhoneAPI->getFromRadio(trBytes);
|
||||
|
||||
// Someone is going to read our value as soon as this callback returns. So fill it with the next message in the queue
|
||||
// or make empty if the queue is empty
|
||||
if (numBytes) {
|
||||
c->setValue(trBytes, numBytes);
|
||||
} else {
|
||||
c->setValue((uint8_t *)"", 0);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
class FromNumCharacteristic : public CallbackCharacteristic
|
||||
{
|
||||
public:
|
||||
FromNumCharacteristic()
|
||||
: CallbackCharacteristic("ed9da18c-a800-4f66-a670-aa7547e34453", BLECharacteristic::PROPERTY_WRITE |
|
||||
BLECharacteristic::PROPERTY_READ |
|
||||
BLECharacteristic::PROPERTY_NOTIFY)
|
||||
{
|
||||
// observe(&service.fromNumChanged);
|
||||
}
|
||||
|
||||
void onRead(BLECharacteristic *c) { DEBUG_MSG("FIXME implement fromnum read\n"); }
|
||||
};
|
||||
|
||||
/*
|
||||
See bluetooth-api.md for documentation.
|
||||
*/
|
||||
BLEService *createMeshBluetoothService(BLEServer *server)
|
||||
{
|
||||
// Only create our phone API object once
|
||||
if (!bluetoothPhoneAPI) {
|
||||
bluetoothPhoneAPI = new BluetoothPhoneAPI();
|
||||
bluetoothPhoneAPI->init();
|
||||
}
|
||||
|
||||
// Create the BLE Service, we need more than the default of 15 handles
|
||||
BLEService *service = server->createService(BLEUUID("6ba1b218-15a8-461f-9fa8-5dcae273eafd"), 30, 0);
|
||||
|
||||
assert(!meshFromNumCharacteristic);
|
||||
meshFromNumCharacteristic = new FromNumCharacteristic;
|
||||
|
||||
addWithDesc(service, meshFromNumCharacteristic, "fromRadio");
|
||||
addWithDesc(service, new ToRadioCharacteristic, "toRadio");
|
||||
addWithDesc(service, new FromRadioCharacteristic, "fromNum");
|
||||
|
||||
meshFromNumCharacteristic->addDescriptor(addBLEDescriptor(new BLE2902())); // Needed so clients can request notification
|
||||
|
||||
service->start();
|
||||
|
||||
// We only add to advertisting once, because the ESP32 arduino code is dumb and that object never dies
|
||||
static bool firstTime = true;
|
||||
if (firstTime) {
|
||||
firstTime = false;
|
||||
server->getAdvertising()->addServiceUUID(service->getUUID());
|
||||
}
|
||||
|
||||
DEBUG_MSG("*** Mesh service:\n");
|
||||
service->dump();
|
||||
|
||||
meshService = service;
|
||||
return service;
|
||||
}
|
||||
|
||||
void stopMeshBluetoothService()
|
||||
{
|
||||
assert(meshService);
|
||||
meshService->stop();
|
||||
meshService->executeDelete();
|
||||
}
|
||||
|
||||
void destroyMeshBluetoothService()
|
||||
{
|
||||
assert(meshService);
|
||||
delete meshService;
|
||||
|
||||
meshFromNumCharacteristic = NULL;
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <BLEServer.h>
|
||||
#include <BLEService.h>
|
||||
|
||||
BLEService *createMeshBluetoothService(BLEServer *server);
|
||||
void destroyMeshBluetoothService();
|
||||
|
||||
/**
|
||||
* Tell any bluetooth clients that the number of rx packets has changed
|
||||
*/
|
||||
void bluetoothNotifyFromNum(uint32_t newValue);
|
||||
|
||||
void stopMeshBluetoothService();
|
||||
61
src/esp32/NimbleSoftwareUpdate.c
Normal file
61
src/esp32/NimbleSoftwareUpdate.c
Normal file
@@ -0,0 +1,61 @@
|
||||
#include "BluetoothSoftwareUpdate.h"
|
||||
|
||||
// NRF52 wants these constants as byte arrays
|
||||
// Generated here https://yupana-engineering.com/online-uuid-to-c-array-converter - but in REVERSE BYTE ORDER
|
||||
|
||||
// "cb0b9a0b-a84c-4c0d-bdbb-442e3144ee30"
|
||||
const ble_uuid128_t update_service_uuid =
|
||||
BLE_UUID128_INIT(0x30, 0xee, 0x44, 0x31, 0x2e, 0x44, 0xbb, 0xbd, 0x0d, 0x4c, 0x4c, 0xa8, 0x0b, 0x9a, 0x0b, 0xcb);
|
||||
|
||||
// "e74dd9c0-a301-4a6f-95a1-f0e1dbea8e1e" write|read
|
||||
const ble_uuid128_t update_size_uuid =
|
||||
BLE_UUID128_INIT(0x1e, 0x8e, 0xea, 0xdb, 0xe1, 0xf0, 0xa1, 0x95, 0x6f, 0x4a, 0x01, 0xa3, 0xc0, 0xd9, 0x4d, 0xe7);
|
||||
|
||||
// "e272ebac-d463-4b98-bc84-5cc1a39ee517" write
|
||||
const ble_uuid128_t update_data_uuid =
|
||||
BLE_UUID128_INIT(0x17, 0xe5, 0x9e, 0xa3, 0xc1, 0x5c, 0x84, 0xbc, 0x98, 0x4b, 0x63, 0xd4, 0xac, 0xeb, 0x72, 0xe2);
|
||||
|
||||
// "4826129c-c22a-43a3-b066-ce8f0d5bacc6" write
|
||||
const ble_uuid128_t update_crc32_uuid =
|
||||
BLE_UUID128_INIT(0xc6, 0xac, 0x5b, 0x0d, 0x8f, 0xce, 0x66, 0xb0, 0xa3, 0x43, 0x2a, 0xc2, 0x9c, 0x12, 0x26, 0x48);
|
||||
|
||||
// "5e134862-7411-4424-ac4a-210937432c77" read|notify
|
||||
const ble_uuid128_t update_result_uuid =
|
||||
BLE_UUID128_INIT(0x77, 0x2c, 0x43, 0x37, 0x09, 0x21, 0x4a, 0xac, 0x24, 0x44, 0x11, 0x74, 0x62, 0x48, 0x13, 0x5e);
|
||||
|
||||
const struct ble_gatt_svc_def gatt_update_svcs[] = {
|
||||
{
|
||||
/*** Service: Security test. */
|
||||
.type = BLE_GATT_SVC_TYPE_PRIMARY,
|
||||
.uuid = &update_service_uuid.u,
|
||||
.characteristics =
|
||||
(struct ble_gatt_chr_def[]){{
|
||||
.uuid = &update_size_uuid.u,
|
||||
.access_cb = update_size_callback,
|
||||
.flags = BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_WRITE_AUTHEN | BLE_GATT_CHR_F_READ |
|
||||
BLE_GATT_CHR_F_READ_AUTHEN,
|
||||
},
|
||||
{
|
||||
.uuid = &update_data_uuid.u,
|
||||
.access_cb = update_data_callback,
|
||||
.flags = BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_WRITE_AUTHEN,
|
||||
},
|
||||
{
|
||||
.uuid = &update_crc32_uuid.u,
|
||||
.access_cb = update_crc32_callback,
|
||||
.flags = BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_WRITE_AUTHEN,
|
||||
},
|
||||
{
|
||||
.uuid = &update_result_uuid.u,
|
||||
.access_cb = update_size_callback,
|
||||
.flags = BLE_GATT_CHR_F_READ | BLE_GATT_CHR_F_READ_AUTHEN | BLE_GATT_CHR_F_NOTIFY,
|
||||
},
|
||||
{
|
||||
0, /* No more characteristics in this service. */
|
||||
}},
|
||||
},
|
||||
|
||||
{
|
||||
0, /* No more services. */
|
||||
},
|
||||
};
|
||||
57
src/esp32/WiFiServerAPI.cpp
Normal file
57
src/esp32/WiFiServerAPI.cpp
Normal file
@@ -0,0 +1,57 @@
|
||||
#include "WiFiServerAPI.h"
|
||||
#include "PowerFSM.h"
|
||||
#include "configuration.h"
|
||||
#include <Arduino.h>
|
||||
|
||||
WiFiServerAPI::WiFiServerAPI(WiFiClient &_client) : StreamAPI(&client), client(_client)
|
||||
{
|
||||
DEBUG_MSG("Incoming connection from %s\n", client.remoteIP().toString().c_str());
|
||||
}
|
||||
|
||||
WiFiServerAPI::~WiFiServerAPI()
|
||||
{
|
||||
client.stop();
|
||||
|
||||
// FIXME - delete this if the client dropps the connection!
|
||||
}
|
||||
|
||||
/// Hookable to find out when connection changes
|
||||
void WiFiServerAPI::onConnectionChanged(bool connected)
|
||||
{
|
||||
// FIXME - we really should be doing global reference counting to see if anyone is currently using serial or wifi and if so,
|
||||
// block sleep
|
||||
|
||||
if (connected) { // To prevent user confusion, turn off bluetooth while using the serial port api
|
||||
powerFSM.trigger(EVENT_SERIAL_CONNECTED);
|
||||
} else {
|
||||
powerFSM.trigger(EVENT_SERIAL_DISCONNECTED);
|
||||
}
|
||||
}
|
||||
|
||||
void WiFiServerAPI::loop()
|
||||
{
|
||||
if (client.connected()) {
|
||||
StreamAPI::loop();
|
||||
} else {
|
||||
DEBUG_MSG("Client dropped connection, closing UDP server\n");
|
||||
delete this;
|
||||
}
|
||||
}
|
||||
|
||||
#define MESHTASTIC_PORTNUM 4403
|
||||
|
||||
WiFiServerPort::WiFiServerPort() : WiFiServer(MESHTASTIC_PORTNUM) {}
|
||||
|
||||
void WiFiServerPort::init()
|
||||
{
|
||||
DEBUG_MSG("Listening on TCP port %d\n", MESHTASTIC_PORTNUM);
|
||||
begin();
|
||||
}
|
||||
|
||||
void WiFiServerPort::loop()
|
||||
{
|
||||
auto client = available();
|
||||
if (client) {
|
||||
new WiFiServerAPI(client);
|
||||
}
|
||||
}
|
||||
38
src/esp32/WiFiServerAPI.h
Normal file
38
src/esp32/WiFiServerAPI.h
Normal file
@@ -0,0 +1,38 @@
|
||||
#pragma once
|
||||
|
||||
#include "StreamAPI.h"
|
||||
#include <WiFi.h>
|
||||
|
||||
/**
|
||||
* Provides both debug printing and, if the client starts sending protobufs to us, switches to send/receive protobufs
|
||||
* (and starts dropping debug printing - FIXME, eventually those prints should be encapsulated in protobufs).
|
||||
*/
|
||||
class WiFiServerAPI : public StreamAPI
|
||||
{
|
||||
private:
|
||||
WiFiClient client;
|
||||
|
||||
public:
|
||||
WiFiServerAPI(WiFiClient &_client);
|
||||
|
||||
virtual ~WiFiServerAPI();
|
||||
|
||||
virtual void loop(); // Check for dropped client connections
|
||||
|
||||
protected:
|
||||
/// Hookable to find out when connection changes
|
||||
virtual void onConnectionChanged(bool connected);
|
||||
};
|
||||
|
||||
/**
|
||||
* Listens for incoming connections and does accepts and creates instances of WiFiServerAPI as needed
|
||||
*/
|
||||
class WiFiServerPort : public WiFiServer
|
||||
{
|
||||
public:
|
||||
WiFiServerPort();
|
||||
|
||||
void init();
|
||||
|
||||
void loop();
|
||||
};
|
||||
@@ -1,170 +1,20 @@
|
||||
#include "BluetoothUtil.h"
|
||||
#include "MeshBluetoothService.h"
|
||||
#include "BluetoothSoftwareUpdate.h"
|
||||
#include "PowerFSM.h"
|
||||
#include "configuration.h"
|
||||
#include "esp_task_wdt.h"
|
||||
#include "main.h"
|
||||
#include "power.h"
|
||||
#include "nimble/BluetoothUtil.h"
|
||||
#include "sleep.h"
|
||||
#include "utils.h"
|
||||
#include "target_specific.h"
|
||||
|
||||
bool bluetoothOn;
|
||||
|
||||
// This routine is called multiple times, once each time we come back from sleep
|
||||
void reinitBluetooth()
|
||||
{
|
||||
DEBUG_MSG("Starting bluetooth\n");
|
||||
|
||||
// FIXME - we are leaking like crazy
|
||||
// AllocatorScope scope(btPool);
|
||||
|
||||
// Note: these callbacks might be coming in from a different thread.
|
||||
BLEServer *serve = initBLE(
|
||||
[](uint32_t pin) {
|
||||
powerFSM.trigger(EVENT_BLUETOOTH_PAIR);
|
||||
screen.startBluetoothPinScreen(pin);
|
||||
},
|
||||
[]() { screen.stopBluetoothPinScreen(); }, getDeviceName(), HW_VENDOR, optstr(APP_VERSION),
|
||||
optstr(HW_VERSION)); // FIXME, use a real name based on the macaddr
|
||||
createMeshBluetoothService(serve);
|
||||
|
||||
// Start advertising - this must be done _after_ creating all services
|
||||
serve->getAdvertising()->start();
|
||||
}
|
||||
|
||||
// Enable/disable bluetooth.
|
||||
void setBluetoothEnable(bool on)
|
||||
{
|
||||
if (on != bluetoothOn) {
|
||||
DEBUG_MSG("Setting bluetooth enable=%d\n", on);
|
||||
|
||||
bluetoothOn = on;
|
||||
if (on) {
|
||||
Serial.printf("Pre BT: %u heap size\n", ESP.getFreeHeap());
|
||||
// ESP_ERROR_CHECK( heap_trace_start(HEAP_TRACE_LEAKS) );
|
||||
reinitBluetooth();
|
||||
} else {
|
||||
// We have to totally teardown our bluetooth objects to prevent leaks
|
||||
stopMeshBluetoothService(); // Must do before shutting down bluetooth
|
||||
deinitBLE();
|
||||
destroyMeshBluetoothService(); // must do after deinit, because it frees our service
|
||||
Serial.printf("Shutdown BT: %u heap size\n", ESP.getFreeHeap());
|
||||
// ESP_ERROR_CHECK( heap_trace_stop() );
|
||||
// heap_trace_dump();
|
||||
}
|
||||
}
|
||||
}
|
||||
#include "utils.h"
|
||||
#include <nvs.h>
|
||||
#include <nvs_flash.h>
|
||||
|
||||
void getMacAddr(uint8_t *dmac)
|
||||
{
|
||||
assert(esp_efuse_mac_get_default(dmac) == ESP_OK);
|
||||
}
|
||||
|
||||
#ifdef TBEAM_V10
|
||||
|
||||
// FIXME. nasty hack cleanup how we load axp192
|
||||
#undef AXP192_SLAVE_ADDRESS
|
||||
#include "axp20x.h"
|
||||
AXP20X_Class axp;
|
||||
bool pmu_irq = false;
|
||||
|
||||
/// Reads power status to powerStatus singleton.
|
||||
//
|
||||
// TODO(girts): move this and other axp stuff to power.h/power.cpp.
|
||||
void readPowerStatus()
|
||||
{
|
||||
powerStatus.haveBattery = axp.isBatteryConnect();
|
||||
if (powerStatus.haveBattery) {
|
||||
powerStatus.batteryVoltageMv = axp.getBattVoltage();
|
||||
// If the AXP192 returns a valid battery percentage, use it
|
||||
if (axp.getBattPercentage() >= 0) {
|
||||
powerStatus.batteryChargePercent = axp.getBattPercentage();
|
||||
} else {
|
||||
// If the AXP192 returns a percentage less than 0, the feature is either not supported or there is an error
|
||||
// In that case, we compute an estimate of the charge percent based on maximum and minimum voltages defined in power.h
|
||||
powerStatus.batteryChargePercent = clamp((int)(((powerStatus.batteryVoltageMv - BAT_MILLIVOLTS_EMPTY) * 1e2) / (BAT_MILLIVOLTS_FULL - BAT_MILLIVOLTS_EMPTY)), 0, 100);
|
||||
}
|
||||
DEBUG_MSG("Battery %dmV %d%%\n", powerStatus.batteryVoltageMv, powerStatus.batteryChargePercent);
|
||||
}
|
||||
powerStatus.usb = axp.isVBUSPlug();
|
||||
powerStatus.charging = axp.isChargeing();
|
||||
}
|
||||
#endif // TBEAM_V10
|
||||
|
||||
#ifdef AXP192_SLAVE_ADDRESS
|
||||
/**
|
||||
* Init the power manager chip
|
||||
*
|
||||
* axp192 power
|
||||
DCDC1 0.7-3.5V @ 1200mA max -> OLED // If you turn this off you'll lose comms to the axp192 because the OLED and the axp192
|
||||
share the same i2c bus, instead use ssd1306 sleep mode DCDC2 -> unused DCDC3 0.7-3.5V @ 700mA max -> ESP32 (keep this on!) LDO1
|
||||
30mA -> charges GPS backup battery // charges the tiny J13 battery by the GPS to power the GPS ram (for a couple of days), can
|
||||
not be turned off LDO2 200mA -> LORA LDO3 200mA -> GPS
|
||||
*/
|
||||
void axp192Init()
|
||||
{
|
||||
if (axp192_found) {
|
||||
if (!axp.begin(Wire, AXP192_SLAVE_ADDRESS)) {
|
||||
DEBUG_MSG("AXP192 Begin PASS\n");
|
||||
|
||||
// axp.setChgLEDMode(LED_BLINK_4HZ);
|
||||
DEBUG_MSG("DCDC1: %s\n", axp.isDCDC1Enable() ? "ENABLE" : "DISABLE");
|
||||
DEBUG_MSG("DCDC2: %s\n", axp.isDCDC2Enable() ? "ENABLE" : "DISABLE");
|
||||
DEBUG_MSG("LDO2: %s\n", axp.isLDO2Enable() ? "ENABLE" : "DISABLE");
|
||||
DEBUG_MSG("LDO3: %s\n", axp.isLDO3Enable() ? "ENABLE" : "DISABLE");
|
||||
DEBUG_MSG("DCDC3: %s\n", axp.isDCDC3Enable() ? "ENABLE" : "DISABLE");
|
||||
DEBUG_MSG("Exten: %s\n", axp.isExtenEnable() ? "ENABLE" : "DISABLE");
|
||||
DEBUG_MSG("----------------------------------------\n");
|
||||
|
||||
axp.setPowerOutPut(AXP192_LDO2, AXP202_ON); // LORA radio
|
||||
axp.setPowerOutPut(AXP192_LDO3, AXP202_ON); // GPS main power
|
||||
axp.setPowerOutPut(AXP192_DCDC2, AXP202_ON);
|
||||
axp.setPowerOutPut(AXP192_EXTEN, AXP202_ON);
|
||||
axp.setPowerOutPut(AXP192_DCDC1, AXP202_ON);
|
||||
axp.setDCDC1Voltage(3300); // for the OLED power
|
||||
|
||||
DEBUG_MSG("DCDC1: %s\n", axp.isDCDC1Enable() ? "ENABLE" : "DISABLE");
|
||||
DEBUG_MSG("DCDC2: %s\n", axp.isDCDC2Enable() ? "ENABLE" : "DISABLE");
|
||||
DEBUG_MSG("LDO2: %s\n", axp.isLDO2Enable() ? "ENABLE" : "DISABLE");
|
||||
DEBUG_MSG("LDO3: %s\n", axp.isLDO3Enable() ? "ENABLE" : "DISABLE");
|
||||
DEBUG_MSG("DCDC3: %s\n", axp.isDCDC3Enable() ? "ENABLE" : "DISABLE");
|
||||
DEBUG_MSG("Exten: %s\n", axp.isExtenEnable() ? "ENABLE" : "DISABLE");
|
||||
|
||||
axp.setChargeControlCur(AXP1XX_CHARGE_CUR_1320MA); // actual limit (in HW) on the tbeam is 450mA
|
||||
#if 0
|
||||
|
||||
// Not connected
|
||||
//val = 0xfc;
|
||||
//axp._writeByte(AXP202_VHTF_CHGSET, 1, &val); // Set temperature protection
|
||||
|
||||
//not used
|
||||
//val = 0x46;
|
||||
//axp._writeByte(AXP202_OFF_CTL, 1, &val); // enable bat detection
|
||||
#endif
|
||||
axp.debugCharging();
|
||||
|
||||
#ifdef PMU_IRQ
|
||||
pinMode(PMU_IRQ, INPUT);
|
||||
attachInterrupt(
|
||||
PMU_IRQ, [] { pmu_irq = true; }, FALLING);
|
||||
|
||||
axp.adc1Enable(AXP202_BATT_CUR_ADC1, 1);
|
||||
axp.enableIRQ(AXP202_BATT_REMOVED_IRQ | AXP202_BATT_CONNECT_IRQ | AXP202_CHARGING_FINISHED_IRQ | AXP202_CHARGING_IRQ |
|
||||
AXP202_VBUS_REMOVED_IRQ | AXP202_VBUS_CONNECT_IRQ | AXP202_PEK_SHORTPRESS_IRQ,
|
||||
1);
|
||||
|
||||
axp.clearIRQ();
|
||||
#endif
|
||||
readPowerStatus();
|
||||
} else {
|
||||
DEBUG_MSG("AXP192 Begin FAIL\n");
|
||||
}
|
||||
} else {
|
||||
DEBUG_MSG("AXP192 not found\n");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
static void printBLEinfo() {
|
||||
int dev_num = esp_ble_get_bond_device_num();
|
||||
@@ -188,11 +38,23 @@ void esp32Setup()
|
||||
DEBUG_MSG("Total PSRAM: %d\n", ESP.getPsramSize());
|
||||
DEBUG_MSG("Free PSRAM: %d\n", ESP.getFreePsram());
|
||||
|
||||
nvs_stats_t nvs_stats;
|
||||
auto res = nvs_get_stats(NULL, &nvs_stats);
|
||||
assert(res == ESP_OK);
|
||||
DEBUG_MSG("NVS: UsedEntries %d, FreeEntries %d, AllEntries %d\n", nvs_stats.used_entries, nvs_stats.free_entries,
|
||||
nvs_stats.total_entries);
|
||||
|
||||
// enableModemSleep();
|
||||
|
||||
#ifdef AXP192_SLAVE_ADDRESS
|
||||
axp192Init();
|
||||
#endif
|
||||
// Since we are turning on watchdogs rather late in the release schedule, we really don't want to catch any
|
||||
// false positives. The wait-to-sleep timeout for shutting down radios is 30 secs, so pick 45 for now.
|
||||
#define APP_WATCHDOG_SECS 45
|
||||
|
||||
res = esp_task_wdt_init(APP_WATCHDOG_SECS, true);
|
||||
assert(res == ESP_OK);
|
||||
|
||||
res = esp_task_wdt_add(NULL);
|
||||
assert(res == ESP_OK);
|
||||
}
|
||||
|
||||
#if 0
|
||||
@@ -215,51 +77,13 @@ uint32_t axpDebugRead()
|
||||
Periodic axpDebugOutput(axpDebugRead);
|
||||
#endif
|
||||
|
||||
|
||||
/// loop code specific to ESP32 targets
|
||||
void esp32Loop()
|
||||
{
|
||||
esp_task_wdt_reset(); // service our app level watchdog
|
||||
loopBLE();
|
||||
bluetoothRebootCheck();
|
||||
|
||||
// for debug printing
|
||||
// radio.radioIf.canSleep();
|
||||
|
||||
#ifdef PMU_IRQ
|
||||
if (pmu_irq) {
|
||||
pmu_irq = false;
|
||||
axp.readIRQ();
|
||||
|
||||
DEBUG_MSG("pmu irq!\n");
|
||||
|
||||
if (axp.isChargingIRQ()) {
|
||||
DEBUG_MSG("Battery start charging\n");
|
||||
}
|
||||
if (axp.isChargingDoneIRQ()) {
|
||||
DEBUG_MSG("Battery fully charged\n");
|
||||
}
|
||||
if (axp.isVbusRemoveIRQ()) {
|
||||
DEBUG_MSG("USB unplugged\n");
|
||||
}
|
||||
if (axp.isVbusPlugInIRQ()) {
|
||||
DEBUG_MSG("USB plugged In\n");
|
||||
}
|
||||
if (axp.isBattPlugInIRQ()) {
|
||||
DEBUG_MSG("Battery inserted\n");
|
||||
}
|
||||
if (axp.isBattRemoveIRQ()) {
|
||||
DEBUG_MSG("Battery removed\n");
|
||||
}
|
||||
if (axp.isPEKShortPressIRQ()) {
|
||||
DEBUG_MSG("PEK short button press\n");
|
||||
}
|
||||
|
||||
readPowerStatus();
|
||||
axp.clearIRQ();
|
||||
}
|
||||
|
||||
if (powerStatus.haveBattery && !powerStatus.usb &&
|
||||
axp.getBattVoltage() < MIN_BAT_MILLIVOLTS) // If we have a battery at all and it is less than 10% full, force deep sleep
|
||||
powerFSM.trigger(EVENT_LOW_BATTERY);
|
||||
|
||||
#endif // T_BEAM_V10
|
||||
}
|
||||
@@ -1,16 +1,25 @@
|
||||
|
||||
#include "GPS.h"
|
||||
#include "configuration.h"
|
||||
#include "time.h"
|
||||
#include "timing.h"
|
||||
#include <assert.h>
|
||||
#include <sys/time.h>
|
||||
#include <time.h>
|
||||
|
||||
// If we have a serial GPS port it will not be null
|
||||
#ifdef GPS_RX_PIN
|
||||
HardwareSerial _serial_gps_real(GPS_SERIAL_NUM);
|
||||
HardwareSerial &GPS::_serial_gps = _serial_gps_real;
|
||||
HardwareSerial *GPS::_serial_gps = &_serial_gps_real;
|
||||
#elif defined(NRF52840_XXAA)
|
||||
// Assume NRF52840
|
||||
HardwareSerial *GPS::_serial_gps = &Serial1;
|
||||
#else
|
||||
// Assume NRF52
|
||||
HardwareSerial &GPS::_serial_gps = Serial1;
|
||||
HardwareSerial *GPS::_serial_gps = NULL;
|
||||
#endif
|
||||
|
||||
#ifdef GPS_I2C_ADDRESS
|
||||
uint8_t GPS::i2cAddress = GPS_I2C_ADDRESS;
|
||||
#else
|
||||
uint8_t GPS::i2cAddress = 0;
|
||||
#endif
|
||||
|
||||
bool timeSetFromGPS; // We try to set our time from GPS each time we wake from sleep
|
||||
@@ -27,7 +36,7 @@ void readFromRTC()
|
||||
struct timeval tv; /* btw settimeofday() is helpfull here too*/
|
||||
|
||||
if (!gettimeofday(&tv, NULL)) {
|
||||
uint32_t now = millis();
|
||||
uint32_t now = timing::millis();
|
||||
|
||||
DEBUG_MSG("Read RTC time as %ld (cur millis %u) valid=%d\n", tv.tv_sec, now, timeSetFromGPS);
|
||||
timeStartMsec = now;
|
||||
@@ -63,16 +72,14 @@ void perhapsSetRTC(struct tm &t)
|
||||
|
||||
// DEBUG_MSG("Got time from GPS month=%d, year=%d, unixtime=%ld\n", t.tm_mon, t.tm_year, tv.tv_sec);
|
||||
if (t.tm_year < 0 || t.tm_year >= 300)
|
||||
DEBUG_MSG("Ignoring invalid GPS time\n");
|
||||
DEBUG_MSG("Ignoring invalid GPS month=%d, year=%d, unixtime=%ld\n", t.tm_mon, t.tm_year, tv.tv_sec);
|
||||
else
|
||||
perhapsSetRTC(&tv);
|
||||
}
|
||||
|
||||
#include <time.h>
|
||||
|
||||
uint32_t getTime()
|
||||
{
|
||||
return ((millis() - timeStartMsec) / 1000) + zeroOffsetSecs;
|
||||
return ((timing::millis() - timeStartMsec) / 1000) + zeroOffsetSecs;
|
||||
}
|
||||
|
||||
uint32_t getValidTime()
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
#pragma once
|
||||
|
||||
#include "../concurrency/PeriodicTask.h"
|
||||
#include "GPSStatus.h"
|
||||
#include "Observer.h"
|
||||
#include "PeriodicTask.h"
|
||||
#include "sys/time.h"
|
||||
|
||||
/// If we haven't yet set our RTC this boot, set it from a GPS derived time
|
||||
@@ -24,22 +25,32 @@ void readFromRTC();
|
||||
*
|
||||
* When new data is available it will notify observers.
|
||||
*/
|
||||
class GPS : public Observable<void *>
|
||||
class GPS
|
||||
{
|
||||
protected:
|
||||
bool hasValidLocation = false; // default to false, until we complete our first read
|
||||
|
||||
static HardwareSerial &_serial_gps;
|
||||
|
||||
public:
|
||||
/** If !NULL we will use this serial port to construct our GPS */
|
||||
static HardwareSerial *_serial_gps;
|
||||
|
||||
/** If !0 we will attempt to connect to the GPS over I2C */
|
||||
static uint8_t i2cAddress;
|
||||
|
||||
int32_t latitude = 0, longitude = 0; // as an int mult by 1e-7 to get value as double
|
||||
int32_t altitude = 0;
|
||||
uint32_t dop = 0; // Diminution of position; PDOP where possible (UBlox), HDOP otherwise (TinyGPS) in 10^2 units (needs scaling before use)
|
||||
uint32_t dop = 0; // Diminution of position; PDOP where possible (UBlox), HDOP otherwise (TinyGPS) in 10^2 units (needs
|
||||
// scaling before use)
|
||||
uint32_t heading = 0; // Heading of motion, in degrees * 10^-5
|
||||
uint32_t numSatellites = 0;
|
||||
|
||||
bool isConnected = false; // Do we have a GPS we are talking to
|
||||
|
||||
virtual ~GPS() {}
|
||||
|
||||
/** We will notify this observable anytime GPS state has changed meaningfully */
|
||||
Observable<const meshtastic::GPSStatus *> newStatus;
|
||||
|
||||
/**
|
||||
* Returns true if we succeeded
|
||||
*/
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#include "NEMAGPS.h"
|
||||
#include "configuration.h"
|
||||
#include "timing.h"
|
||||
|
||||
static int32_t toDegInt(RawDegrees d)
|
||||
{
|
||||
@@ -12,14 +13,13 @@ static int32_t toDegInt(RawDegrees d)
|
||||
|
||||
void NEMAGPS::loop()
|
||||
{
|
||||
|
||||
while (_serial_gps.available() > 0) {
|
||||
int c = _serial_gps.read();
|
||||
while (_serial_gps->available() > 0) {
|
||||
int c = _serial_gps->read();
|
||||
// Serial.write(c);
|
||||
reader.encode(c);
|
||||
}
|
||||
|
||||
uint32_t now = millis();
|
||||
uint32_t now = timing::millis();
|
||||
if ((now - lastUpdateMsec) > 20 * 1000) { // Ugly hack for now - limit update checks to once every 20 secs (but still consume
|
||||
// serial chars at whatever rate)
|
||||
lastUpdateMsec = now;
|
||||
@@ -54,16 +54,22 @@ void NEMAGPS::loop()
|
||||
longitude = toDegInt(loc.lng);
|
||||
}
|
||||
// Diminution of precision (an accuracy metric) is reported in 10^2 units, so we need to scale down when we use it
|
||||
if(reader.hdop.isValid()) {
|
||||
if (reader.hdop.isValid()) {
|
||||
dop = reader.hdop.value();
|
||||
}
|
||||
if (reader.course.isValid()) {
|
||||
heading = reader.course.value() * 1e3; //Scale the heading (in degrees * 10^-2) to match the expected degrees * 10^-5
|
||||
}
|
||||
if (reader.satellites.isValid()) {
|
||||
numSatellites = reader.satellites.value();
|
||||
}
|
||||
|
||||
// expect gps pos lat=37.520825, lon=-122.309162, alt=158
|
||||
DEBUG_MSG("new NEMA GPS pos lat=%f, lon=%f, alt=%d, hdop=%f\n", latitude * 1e-7, longitude * 1e-7, altitude, dop * 1e-2);
|
||||
|
||||
hasValidLocation = (latitude != 0) || (longitude != 0); // bogus lat lon is reported as 0,0
|
||||
if (hasValidLocation)
|
||||
notifyObservers(NULL);
|
||||
DEBUG_MSG("new NEMA GPS pos lat=%f, lon=%f, alt=%d, hdop=%f, heading=%f\n", latitude * 1e-7, longitude * 1e-7, altitude, dop * 1e-2, heading * 1e-5);
|
||||
}
|
||||
|
||||
// Notify any status instances that are observing us
|
||||
const meshtastic::GPSStatus status = meshtastic::GPSStatus(hasLock(), isConnected, latitude, longitude, altitude, dop, heading, numSatellites);
|
||||
newStatus.notifyObservers(&status);
|
||||
}
|
||||
}
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
#include "GPS.h"
|
||||
#include "Observer.h"
|
||||
#include "PeriodicTask.h"
|
||||
#include "../concurrency/PeriodicTask.h"
|
||||
#include "TinyGPS++.h"
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,61 +1,61 @@
|
||||
#include "UBloxGPS.h"
|
||||
#include "error.h"
|
||||
#include "sleep.h"
|
||||
#include <assert.h>
|
||||
|
||||
UBloxGPS::UBloxGPS() : PeriodicTask()
|
||||
UBloxGPS::UBloxGPS() : concurrency::PeriodicTask()
|
||||
{
|
||||
notifySleepObserver.observe(¬ifySleep);
|
||||
}
|
||||
|
||||
bool UBloxGPS::tryConnect()
|
||||
{
|
||||
isConnected = false;
|
||||
|
||||
if (_serial_gps)
|
||||
isConnected = ublox.begin(*_serial_gps);
|
||||
|
||||
if (!isConnected && i2cAddress) {
|
||||
extern bool neo6M; // Super skanky - if we are talking to the device i2c we assume it is a neo7 on a RAK815, which
|
||||
// supports the newer API
|
||||
neo6M = true;
|
||||
|
||||
isConnected = ublox.begin(Wire, i2cAddress);
|
||||
}
|
||||
|
||||
return isConnected;
|
||||
}
|
||||
|
||||
bool UBloxGPS::setup()
|
||||
{
|
||||
if (_serial_gps) {
|
||||
#ifdef GPS_RX_PIN
|
||||
_serial_gps.begin(GPS_BAUDRATE, SERIAL_8N1, GPS_RX_PIN, GPS_TX_PIN);
|
||||
_serial_gps->begin(GPS_BAUDRATE, SERIAL_8N1, GPS_RX_PIN, GPS_TX_PIN);
|
||||
#else
|
||||
_serial_gps.begin(GPS_BAUDRATE);
|
||||
_serial_gps->begin(GPS_BAUDRATE);
|
||||
#endif
|
||||
// _serial_gps.setRxBufferSize(1024); // the default is 256
|
||||
// _serial_gps.setRxBufferSize(1024); // the default is 256
|
||||
}
|
||||
|
||||
#ifdef GPS_POWER_EN
|
||||
pinMode(GPS_POWER_EN, OUTPUT);
|
||||
digitalWrite(GPS_POWER_EN, 1);
|
||||
delay(200); // Give time for the GPS to startup after we gave power
|
||||
#endif
|
||||
|
||||
// ublox.enableDebugging(Serial);
|
||||
|
||||
// note: the lib's implementation has the wrong docs for what the return val is
|
||||
// it is not a bool, it returns zero for success
|
||||
isConnected = ublox.begin(_serial_gps);
|
||||
|
||||
// try a second time, the ublox lib serial parsing is buggy?
|
||||
if (!isConnected)
|
||||
isConnected = ublox.begin(_serial_gps);
|
||||
if (!tryConnect())
|
||||
tryConnect();
|
||||
|
||||
if (isConnected) {
|
||||
DEBUG_MSG("Connected to UBLOX GPS successfully\n");
|
||||
|
||||
bool factoryReset = false;
|
||||
bool ok;
|
||||
if (factoryReset) {
|
||||
// It is useful to force back into factory defaults (9600baud, NEMA to test the behavior of boards that don't have
|
||||
// GPS_TX connected)
|
||||
ublox.factoryReset();
|
||||
delay(3000);
|
||||
isConnected = ublox.begin(_serial_gps);
|
||||
DEBUG_MSG("Factory reset success=%d\n", isConnected);
|
||||
ok = ublox.saveConfiguration(3000);
|
||||
assert(ok);
|
||||
return false;
|
||||
} else {
|
||||
ok = ublox.setUART1Output(COM_TYPE_UBX, 500); // Use native API
|
||||
assert(ok);
|
||||
ok = ublox.setNavigationFrequency(1, 500); // Produce 4x/sec to keep the amount of time we stall in getPVT low
|
||||
assert(ok);
|
||||
// ok = ublox.setAutoPVT(false); // Not implemented on NEO-6M
|
||||
// assert(ok);
|
||||
// ok = ublox.setDynamicModel(DYN_MODEL_BIKE); // probably PEDESTRIAN but just in case assume bike speeds
|
||||
// assert(ok);
|
||||
ok = ublox.powerSaveMode(true, 2000); // use power save mode, the default timeout (1100ms seems a bit too tight)
|
||||
assert(ok);
|
||||
}
|
||||
ok = ublox.saveConfiguration(3000);
|
||||
assert(ok);
|
||||
if (!setUBXMode())
|
||||
recordCriticalError(UBloxInitFailed); // Don't halt the boot if saving the config fails, but do report the bug
|
||||
|
||||
PeriodicTask::setup(); // We don't start our periodic task unless we actually found the device
|
||||
concurrency::PeriodicTask::setup(); // We don't start our periodic task unless we actually found the device
|
||||
|
||||
return true;
|
||||
} else {
|
||||
@@ -63,6 +63,54 @@ bool UBloxGPS::setup()
|
||||
}
|
||||
}
|
||||
|
||||
bool UBloxGPS::setUBXMode()
|
||||
{
|
||||
if (_serial_gps) {
|
||||
if (!ublox.setUART1Output(COM_TYPE_UBX, 1000)) // Use native API
|
||||
return false;
|
||||
}
|
||||
if (i2cAddress) {
|
||||
if (!ublox.setI2COutput(COM_TYPE_UBX, 1000))
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!ublox.setNavigationFrequency(1, 1000)) // Produce 4x/sec to keep the amount of time we stall in getPVT low
|
||||
return false;
|
||||
|
||||
// ok = ublox.setAutoPVT(false); // Not implemented on NEO-6M
|
||||
// assert(ok);
|
||||
// ok = ublox.setDynamicModel(DYN_MODEL_BIKE); // probably PEDESTRIAN but just in case assume bike speeds
|
||||
// assert(ok);
|
||||
if (!ublox.powerSaveMode(true, 2000)) // use power save mode, the default timeout (1100ms seems a bit too tight)
|
||||
return false;
|
||||
|
||||
if (!ublox.saveConfiguration(3000))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset our GPS back to factory settings
|
||||
*
|
||||
* @return true for success
|
||||
*/
|
||||
bool UBloxGPS::factoryReset()
|
||||
{
|
||||
bool ok = false;
|
||||
|
||||
// It is useful to force back into factory defaults (9600baud, NEMA to test the behavior of boards that don't have
|
||||
// GPS_TX connected)
|
||||
ublox.factoryReset();
|
||||
delay(3000);
|
||||
tryConnect(); // sets isConnected
|
||||
DEBUG_MSG("GPS Factory reset success=%d\n", isConnected);
|
||||
if (isConnected)
|
||||
ok = setUBXMode();
|
||||
|
||||
return ok;
|
||||
}
|
||||
|
||||
/// Prepare the GPS for the cpu entering deep or light sleep, expect to be gone for at least 100s of msecs
|
||||
int UBloxGPS::prepareSleep(void *unused)
|
||||
{
|
||||
@@ -80,20 +128,22 @@ void UBloxGPS::doTask()
|
||||
|
||||
// Consume all characters that have arrived
|
||||
|
||||
// getPVT automatically calls checkUblox
|
||||
// if using i2c or serial look too see if any chars are ready
|
||||
ublox.checkUblox(); // See if new data is available. Process bytes as they come in.
|
||||
|
||||
// If we don't have a fix (a quick check), don't try waiting for a solution)
|
||||
// Hmmm my fix type reading returns zeros for fix, which doesn't seem correct, because it is still sptting out positions
|
||||
// turn off for now
|
||||
fixtype = ublox.getFixType(0);
|
||||
uint16_t maxWait = i2cAddress ? 300 : 0; // If using i2c we must poll with wait
|
||||
fixtype = ublox.getFixType(maxWait);
|
||||
DEBUG_MSG("GPS fix type %d\n", fixtype);
|
||||
|
||||
// DEBUG_MSG("sec %d\n", ublox.getSecond());
|
||||
// DEBUG_MSG("lat %d\n", ublox.getLatitude());
|
||||
|
||||
// any fix that has time
|
||||
if (ublox.getT(0)) {
|
||||
|
||||
if (ublox.getT(maxWait)) {
|
||||
/* Convert to unix time
|
||||
The Unix epoch (or Unix time or POSIX time or Unix timestamp) is the number of seconds that have elapsed since January 1, 1970
|
||||
(midnight UTC/GMT), not counting leap seconds (in ISO 8601: 1970-01-01T00:00:00Z).
|
||||
@@ -109,26 +159,33 @@ The Unix epoch (or Unix time or POSIX time or Unix timestamp) is the number of s
|
||||
perhapsSetRTC(t);
|
||||
}
|
||||
|
||||
if ((fixtype >= 3 && fixtype <= 4) && ublox.getP(0)) // rd fixes only
|
||||
{
|
||||
// we only notify if position has changed
|
||||
latitude = ublox.getLatitude(0);
|
||||
longitude = ublox.getLongitude(0);
|
||||
altitude = ublox.getAltitude(0) / 1000; // in mm convert to meters
|
||||
dop = ublox.getPDOP(0); // PDOP (an accuracy metric) is reported in 10^2 units so we have to scale down when we use it
|
||||
DEBUG_MSG("new gps pos lat=%f, lon=%f, alt=%d, pdop=%f\n", latitude * 1e-7, longitude * 1e-7, altitude, dop * 1e-2);
|
||||
latitude = ublox.getLatitude(0);
|
||||
longitude = ublox.getLongitude(0);
|
||||
altitude = ublox.getAltitude(0) / 1000; // in mm convert to meters
|
||||
dop = ublox.getPDOP(0); // PDOP (an accuracy metric) is reported in 10^2 units so we have to scale down when we use it
|
||||
heading = ublox.getHeading(0);
|
||||
numSatellites = ublox.getSIV(0);
|
||||
|
||||
// bogus lat lon is reported as 0 or 0 (can be bogus just for one)
|
||||
// Also: apparently when the GPS is initially reporting lock it can output a bogus latitude > 90 deg!
|
||||
hasValidLocation = (latitude != 0) && (longitude != 0) && (latitude <= 900000000 && latitude >= -900000000);
|
||||
// bogus lat lon is reported as 0 or 0 (can be bogus just for one)
|
||||
// Also: apparently when the GPS is initially reporting lock it can output a bogus latitude > 90 deg!
|
||||
hasValidLocation =
|
||||
(latitude != 0) && (longitude != 0) && (latitude <= 900000000 && latitude >= -900000000) && (numSatellites > 0);
|
||||
|
||||
// we only notify if position has changed due to a new fix
|
||||
if ((fixtype >= 3 && fixtype <= 4) && ublox.getP(maxWait)) // rd fixes only
|
||||
{
|
||||
if (hasValidLocation) {
|
||||
wantNewLocation = false;
|
||||
notifyObservers(NULL);
|
||||
// ublox.powerOff();
|
||||
}
|
||||
} else // we didn't get a location update, go back to sleep and hope the characters show up
|
||||
wantNewLocation = true;
|
||||
|
||||
// Notify any status instances that are observing us
|
||||
const meshtastic::GPSStatus status =
|
||||
meshtastic::GPSStatus(hasLock(), isConnected, latitude, longitude, altitude, dop, heading, numSatellites);
|
||||
newStatus.notifyObservers(&status);
|
||||
|
||||
// Once we have sent a location once we only poll the GPS rarely, otherwise check back every 1s until we have something over
|
||||
// the serial
|
||||
setPeriod(hasValidLocation && !wantNewLocation ? 30 * 1000 : 10 * 1000);
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
#pragma once
|
||||
|
||||
#include "../concurrency/PeriodicTask.h"
|
||||
#include "GPS.h"
|
||||
#include "Observer.h"
|
||||
#include "PeriodicTask.h"
|
||||
#include "SparkFun_Ublox_Arduino_Library.h"
|
||||
|
||||
/**
|
||||
@@ -10,7 +10,7 @@
|
||||
*
|
||||
* When new data is available it will notify observers.
|
||||
*/
|
||||
class UBloxGPS : public GPS, public PeriodicTask
|
||||
class UBloxGPS : public GPS, public concurrency::PeriodicTask
|
||||
{
|
||||
SFE_UBLOX_GPS ublox;
|
||||
|
||||
@@ -33,9 +33,22 @@ class UBloxGPS : public GPS, public PeriodicTask
|
||||
* called after the CPU wakes from light-sleep state */
|
||||
virtual void startLock();
|
||||
|
||||
private:
|
||||
/**
|
||||
* Reset our GPS back to factory settings
|
||||
*
|
||||
* @return true for success
|
||||
*/
|
||||
bool factoryReset();
|
||||
|
||||
private:
|
||||
/// Prepare the GPS for the cpu entering deep or light sleep, expect to be gone for at least 100s of msecs
|
||||
/// always returns 0 to indicate okay to sleep
|
||||
int prepareSleep(void *unused);
|
||||
|
||||
/// Attempt to connect to our GPS, returns false if no gps is present
|
||||
bool tryConnect();
|
||||
|
||||
/// Switch to our desired operating mode and save the settings to flash
|
||||
/// returns true for success
|
||||
bool setUBXMode();
|
||||
};
|
||||
|
||||
@@ -25,29 +25,17 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#include "GPS.h"
|
||||
#include "MeshService.h"
|
||||
#include "NodeDB.h"
|
||||
#include "Screen.h"
|
||||
#include "configs.h"
|
||||
#include "configuration.h"
|
||||
#include "fonts.h"
|
||||
#include "images.h"
|
||||
#include "graphics/images.h"
|
||||
#include "main.h"
|
||||
#include "mesh-pb-constants.h"
|
||||
#include "screen.h"
|
||||
#include "utils.h"
|
||||
|
||||
#define FONT_HEIGHT 14 // actually 13 for "ariel 10" but want a little extra space
|
||||
#define FONT_HEIGHT_16 (ArialMT_Plain_16[1] + 1)
|
||||
#ifdef USE_SH1106
|
||||
#define SCREEN_WIDTH 132
|
||||
#else
|
||||
#define SCREEN_WIDTH 128
|
||||
#endif
|
||||
#define SCREEN_HEIGHT 64
|
||||
#define TRANSITION_FRAMERATE 30 // fps
|
||||
#define IDLE_FRAMERATE 10 // in fps
|
||||
#define COMPASS_DIAM 44
|
||||
using namespace meshtastic; /** @todo remove */
|
||||
|
||||
#define NUM_EXTRA_FRAMES 2 // text message and debug frame
|
||||
|
||||
namespace meshtastic
|
||||
namespace graphics
|
||||
{
|
||||
|
||||
// A text message frame + debug frame + all the node infos
|
||||
@@ -55,7 +43,18 @@ static FrameCallback normalFrames[MAX_NUM_NODES + NUM_EXTRA_FRAMES];
|
||||
static uint32_t targetFramerate = IDLE_FRAMERATE;
|
||||
static char btPIN[16] = "888888";
|
||||
|
||||
uint8_t imgBattery[16] = { 0xFF, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0xE7, 0x3C };
|
||||
// This image definition is here instead of images.h because it's modified dynamically by the drawBattery function
|
||||
uint8_t imgBattery[16] = {0xFF, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0xE7, 0x3C};
|
||||
|
||||
// Threshold values for the GPS lock accuracy bar display
|
||||
uint32_t dopThresholds[5] = {2000, 1000, 500, 200, 100};
|
||||
|
||||
// Stores the last 4 of our hardware ID, to make finding the device for pairing easier
|
||||
static char ourId[5];
|
||||
|
||||
#ifdef SHOW_REDRAWS
|
||||
static bool heartbeat = false;
|
||||
#endif
|
||||
|
||||
static void drawBootScreen(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y)
|
||||
{
|
||||
@@ -69,10 +68,11 @@ static void drawBootScreen(OLEDDisplay *display, OLEDDisplayUiState *state, int1
|
||||
display->drawString(64 + x, SCREEN_HEIGHT - FONT_HEIGHT_16, "meshtastic.org");
|
||||
display->setFont(ArialMT_Plain_10);
|
||||
const char *region = xstr(HW_VERSION);
|
||||
if(*region && region[3] == '-') // Skip past 1.0- in the 1.0-EU865 string
|
||||
if (*region && region[3] == '-') // Skip past 1.0- in the 1.0-EU865 string
|
||||
region += 4;
|
||||
char buf[16];
|
||||
snprintf(buf, sizeof(buf), "%s", xstr(APP_VERSION)); // Note: we don't bother printing region or now, it makes the string too long
|
||||
snprintf(buf, sizeof(buf), "%s",
|
||||
xstr(APP_VERSION)); // Note: we don't bother printing region or now, it makes the string too long
|
||||
display->drawString(SCREEN_WIDTH - 20, 0, buf);
|
||||
}
|
||||
|
||||
@@ -91,8 +91,8 @@ static void drawFrameBluetooth(OLEDDisplay *display, OLEDDisplayUiState *state,
|
||||
display->setFont(ArialMT_Plain_10);
|
||||
char buf[30];
|
||||
const char *name = "Name: ";
|
||||
strcpy(buf,name);
|
||||
strcat(buf,getDeviceName());
|
||||
strcpy(buf, name);
|
||||
strcat(buf, getDeviceName());
|
||||
display->drawString(64 + x, 48 + y, buf);
|
||||
}
|
||||
|
||||
@@ -120,7 +120,7 @@ static void drawTextMessageFrame(OLEDDisplay *display, OLEDDisplayUiState *state
|
||||
assert(mp.decoded.which_payload == SubPacket_data_tag);
|
||||
snprintf(tempBuf, sizeof(tempBuf), " %s", mp.decoded.data.payload.bytes);
|
||||
|
||||
display->drawStringMaxWidth(4 + x, 10 + y, 128, tempBuf);
|
||||
display->drawStringMaxWidth(4 + x, 10 + y, SCREEN_WIDTH - (6 + x), tempBuf);
|
||||
}
|
||||
|
||||
/// Draw a series of fields in a column, wrapping to multiple colums if needed
|
||||
@@ -142,75 +142,101 @@ static void drawColumns(OLEDDisplay *display, int16_t x, int16_t y, const char *
|
||||
}
|
||||
}
|
||||
|
||||
/// Draw a series of fields in a row, wrapping to multiple rows if needed
|
||||
/// @return the max y we ended up printing to
|
||||
static uint32_t drawRows(OLEDDisplay *display, int16_t x, int16_t y, const char **fields)
|
||||
{
|
||||
// The coordinates define the left starting point of the text
|
||||
display->setTextAlignment(TEXT_ALIGN_LEFT);
|
||||
#if 0
|
||||
/// Draw a series of fields in a row, wrapping to multiple rows if needed
|
||||
/// @return the max y we ended up printing to
|
||||
static uint32_t drawRows(OLEDDisplay *display, int16_t x, int16_t y, const char **fields)
|
||||
{
|
||||
// The coordinates define the left starting point of the text
|
||||
display->setTextAlignment(TEXT_ALIGN_LEFT);
|
||||
|
||||
const char **f = fields;
|
||||
int xo = x, yo = y;
|
||||
const int COLUMNS = 2; // hardwired for two columns per row....
|
||||
int col = 0; // track which column we are on
|
||||
while (*f) {
|
||||
display->drawString(xo, yo, *f);
|
||||
xo += SCREEN_WIDTH / COLUMNS;
|
||||
// Wrap to next row, if needed.
|
||||
if (++col >= COLUMNS) {
|
||||
xo = x;
|
||||
yo += FONT_HEIGHT;
|
||||
col = 0;
|
||||
const char **f = fields;
|
||||
int xo = x, yo = y;
|
||||
const int COLUMNS = 2; // hardwired for two columns per row....
|
||||
int col = 0; // track which column we are on
|
||||
while (*f) {
|
||||
display->drawString(xo, yo, *f);
|
||||
xo += SCREEN_WIDTH / COLUMNS;
|
||||
// Wrap to next row, if needed.
|
||||
if (++col >= COLUMNS) {
|
||||
xo = x;
|
||||
yo += FONT_HEIGHT;
|
||||
col = 0;
|
||||
}
|
||||
f++;
|
||||
}
|
||||
if (col != 0) {
|
||||
// Include last incomplete line in our total.
|
||||
yo += FONT_HEIGHT;
|
||||
}
|
||||
f++;
|
||||
}
|
||||
if (col != 0) {
|
||||
// Include last incomplete line in our total.
|
||||
yo += FONT_HEIGHT;
|
||||
}
|
||||
|
||||
return yo;
|
||||
}
|
||||
return yo;
|
||||
}
|
||||
#endif
|
||||
|
||||
// Draw power bars or a charging indicator on an image of a battery, determined by battery charge voltage or percentage.
|
||||
static void drawBattery(OLEDDisplay *display, int16_t x, int16_t y, uint8_t *imgBuffer, PowerStatus *powerStatus) {
|
||||
static const uint8_t powerBar[3] = { 0x81, 0xBD, 0xBD };
|
||||
static const uint8_t lightning[8] = { 0xA1, 0xA1, 0xA5, 0xAD, 0xB5, 0xA5, 0x85, 0x85 };
|
||||
static void drawBattery(OLEDDisplay *display, int16_t x, int16_t y, uint8_t *imgBuffer, const PowerStatus *powerStatus)
|
||||
{
|
||||
static const uint8_t powerBar[3] = {0x81, 0xBD, 0xBD};
|
||||
static const uint8_t lightning[8] = {0xA1, 0xA1, 0xA5, 0xAD, 0xB5, 0xA5, 0x85, 0x85};
|
||||
// Clear the bar area on the battery image
|
||||
for (int i = 1; i < 14; i++) {
|
||||
imgBuffer[i] = 0x81;
|
||||
}
|
||||
// If charging, draw a charging indicator
|
||||
if (powerStatus->charging) {
|
||||
if (powerStatus->getIsCharging()) {
|
||||
memcpy(imgBuffer + 3, lightning, 8);
|
||||
// If not charging, Draw power bars
|
||||
// If not charging, Draw power bars
|
||||
} else {
|
||||
for (int i = 0; i < 4; i++) {
|
||||
if(powerStatus->batteryChargePercent >= 25 * i) memcpy(imgBuffer + 1 + (i * 3), powerBar, 3);
|
||||
if (powerStatus->getBatteryChargePercent() >= 25 * i)
|
||||
memcpy(imgBuffer + 1 + (i * 3), powerBar, 3);
|
||||
}
|
||||
}
|
||||
display->drawFastImage(x, y, 16, 8, imgBuffer);
|
||||
}
|
||||
|
||||
// Draw nodes status
|
||||
static void drawNodes(OLEDDisplay *display, int16_t x, int16_t y, int nodesOnline, int nodesTotal) {
|
||||
static void drawNodes(OLEDDisplay *display, int16_t x, int16_t y, NodeStatus *nodeStatus)
|
||||
{
|
||||
char usersString[20];
|
||||
sprintf(usersString, "%d/%d", nodesOnline, nodesTotal);
|
||||
sprintf(usersString, "%d/%d", nodeStatus->getNumOnline(), nodeStatus->getNumTotal());
|
||||
display->drawFastImage(x, y, 8, 8, imgUser);
|
||||
display->drawString(x + 10, y - 2, usersString);
|
||||
}
|
||||
|
||||
// Draw GPS status summary
|
||||
static void drawGPS(OLEDDisplay *display, int16_t x, int16_t y, GPS *gps) {
|
||||
if(!gps->isConnected) { display->drawString(x, y - 2, "No GPS"); return; }
|
||||
display->drawFastImage(x, y, 6, 8, gps->hasLock() ? imgPositionSolid : imgPositionEmpty );
|
||||
if(!gps->hasLock()) { display->drawString(x + 8, y - 2, "No sats"); return; }
|
||||
if(gps->dop <= 100) { display->drawString(x + 8, y - 2, "Ideal"); return; }
|
||||
if(gps->dop <= 200) { display->drawString(x + 8, y - 2, "Exc."); return; }
|
||||
if(gps->dop <= 500) { display->drawString(x + 8, y - 2, "Good"); return; }
|
||||
if(gps->dop <= 1000) { display->drawString(x + 8, y - 2, "Mod."); return; }
|
||||
if(gps->dop <= 2000) { display->drawString(x + 8, y - 2, "Fair"); return; }
|
||||
if(gps->dop > 0) { display->drawString(x + 8, y - 2, "Poor"); return; }
|
||||
static void drawGPS(OLEDDisplay *display, int16_t x, int16_t y, const GPSStatus *gps)
|
||||
{
|
||||
if (!gps->getIsConnected()) {
|
||||
display->drawString(x, y - 2, "No GPS");
|
||||
return;
|
||||
}
|
||||
display->drawFastImage(x, y, 6, 8, gps->getHasLock() ? imgPositionSolid : imgPositionEmpty);
|
||||
if (!gps->getHasLock()) {
|
||||
display->drawString(x + 8, y - 2, "No sats");
|
||||
return;
|
||||
} else {
|
||||
char satsString[3];
|
||||
uint8_t bar[2] = {0};
|
||||
|
||||
// Draw DOP signal bars
|
||||
for (int i = 0; i < 5; i++) {
|
||||
if (gps->getDOP() <= dopThresholds[i])
|
||||
bar[0] = ~((1 << (5 - i)) - 1);
|
||||
else
|
||||
bar[0] = 0b10000000;
|
||||
// bar[1] = bar[0];
|
||||
display->drawFastImage(x + 9 + (i * 2), y, 2, 8, bar);
|
||||
}
|
||||
|
||||
// Draw satellite image
|
||||
display->drawFastImage(x + 24, y, 8, 8, imgSatellite);
|
||||
|
||||
// Draw the number of satellites
|
||||
sprintf(satsString, "%d", gps->getNumSatellites());
|
||||
display->drawString(x + 34, y - 2, satsString);
|
||||
}
|
||||
}
|
||||
|
||||
/// Ported from my old java code, returns distance in meters along the globe
|
||||
@@ -352,28 +378,40 @@ static bool hasPosition(NodeInfo *n)
|
||||
static size_t nodeIndex;
|
||||
static int8_t prevFrame = -1;
|
||||
|
||||
// Draw the compass and arrow pointing to location
|
||||
static void drawCompass(OLEDDisplay *display, int16_t compassX, int16_t compassY, float headingRadian)
|
||||
// Draw the arrow pointing to a node's location
|
||||
static void drawNodeHeading(OLEDDisplay *display, int16_t compassX, int16_t compassY, float headingRadian)
|
||||
{
|
||||
// display->drawXbm(compassX, compassY, compass_width, compass_height,
|
||||
// (const uint8_t *)compass_bits);
|
||||
|
||||
Point tip(0.0f, 0.5f), tail(0.0f, -0.5f); // pointing up initially
|
||||
float arrowOffsetX = 0.2f, arrowOffsetY = 0.2f;
|
||||
Point leftArrow(tip.x - arrowOffsetX, tip.y - arrowOffsetY), rightArrow(tip.x + arrowOffsetX, tip.y - arrowOffsetY);
|
||||
|
||||
Point *points[] = {&tip, &tail, &leftArrow, &rightArrow};
|
||||
Point *arrowPoints[] = {&tip, &tail, &leftArrow, &rightArrow};
|
||||
|
||||
for (int i = 0; i < 4; i++) {
|
||||
points[i]->rotate(headingRadian);
|
||||
points[i]->scale(COMPASS_DIAM * 0.6);
|
||||
points[i]->translate(compassX, compassY);
|
||||
arrowPoints[i]->rotate(headingRadian);
|
||||
arrowPoints[i]->scale(COMPASS_DIAM * 0.6);
|
||||
arrowPoints[i]->translate(compassX, compassY);
|
||||
}
|
||||
drawLine(display, tip, tail);
|
||||
drawLine(display, leftArrow, tip);
|
||||
drawLine(display, rightArrow, tip);
|
||||
}
|
||||
|
||||
display->drawCircle(compassX, compassY, COMPASS_DIAM / 2);
|
||||
// Draw the compass heading
|
||||
static void drawCompassHeading(OLEDDisplay *display, int16_t compassX, int16_t compassY, float myHeading)
|
||||
{
|
||||
Point N1(-0.04f, -0.65f), N2(0.04f, -0.65f);
|
||||
Point N3(-0.04f, -0.55f), N4(0.04f, -0.55f);
|
||||
Point *rosePoints[] = {&N1, &N2, &N3, &N4};
|
||||
|
||||
for (int i = 0; i < 4; i++) {
|
||||
rosePoints[i]->rotate(myHeading);
|
||||
rosePoints[i]->scale(-1 * COMPASS_DIAM);
|
||||
rosePoints[i]->translate(compassX, compassY);
|
||||
}
|
||||
drawLine(display, N1, N3);
|
||||
drawLine(display, N2, N4);
|
||||
drawLine(display, N1, N4);
|
||||
}
|
||||
|
||||
/// Convert an integer GPS coords to a floating point
|
||||
@@ -393,10 +431,12 @@ static void drawNodeInfo(OLEDDisplay *display, OLEDDisplayUiState *state, int16_
|
||||
nodeIndex = (nodeIndex + 1) % nodeDB.getNumNodes();
|
||||
n = nodeDB.getNodeByIndex(nodeIndex);
|
||||
}
|
||||
|
||||
// We just changed to a new node screen, ask that node for updated state
|
||||
displayedNodeNum = n->num;
|
||||
service.sendNetworkPing(displayedNodeNum, true);
|
||||
|
||||
// We just changed to a new node screen, ask that node for updated state if it's older than 2 minutes
|
||||
if (sinceLastSeen(n) > 120) {
|
||||
service.sendNetworkPing(displayedNodeNum, true);
|
||||
}
|
||||
}
|
||||
|
||||
NodeInfo *node = nodeDB.getNodeByIndex(nodeIndex);
|
||||
@@ -421,36 +461,46 @@ static void drawNodeInfo(OLEDDisplay *display, OLEDDisplayUiState *state, int16_
|
||||
snprintf(lastStr, sizeof(lastStr), "%u hours ago", agoSecs / 60 / 60);
|
||||
|
||||
static char distStr[20];
|
||||
*distStr = 0; // might not have location data
|
||||
strcpy(distStr, "? km"); // might not have location data
|
||||
float headingRadian;
|
||||
NodeInfo *ourNode = nodeDB.getNode(nodeDB.getNodeNum());
|
||||
const char *fields[] = {username, distStr, signalStr, lastStr, NULL};
|
||||
drawColumns(display, x, y, fields);
|
||||
|
||||
// coordinates for the center of the compass/circle
|
||||
int16_t compassX = x + SCREEN_WIDTH - COMPASS_DIAM / 2 - 1, compassY = y + SCREEN_HEIGHT / 2;
|
||||
int16_t compassX = x + SCREEN_WIDTH - COMPASS_DIAM / 2 - 5, compassY = y + SCREEN_HEIGHT / 2;
|
||||
bool hasNodeHeading = false;
|
||||
|
||||
if (ourNode && hasPosition(ourNode) && hasPosition(node)) { // display direction toward node
|
||||
Position &op = ourNode->position, &p = node->position;
|
||||
float d = latLongToMeter(DegD(p.latitude_i), DegD(p.longitude_i), DegD(op.latitude_i), DegD(op.longitude_i));
|
||||
if (d < 2000)
|
||||
snprintf(distStr, sizeof(distStr), "%.0f m", d);
|
||||
else
|
||||
snprintf(distStr, sizeof(distStr), "%.1f km", d / 1000);
|
||||
if (ourNode && hasPosition(ourNode)) {
|
||||
Position &op = ourNode->position;
|
||||
float myHeading = estimatedHeading(DegD(op.latitude_i), DegD(op.longitude_i));
|
||||
drawCompassHeading(display, compassX, compassY, myHeading);
|
||||
|
||||
// FIXME, also keep the guess at the operators heading and add/substract
|
||||
// it. currently we don't do this and instead draw north up only.
|
||||
float bearingToOther = bearing(DegD(p.latitude_i), DegD(p.longitude_i), DegD(op.latitude_i), DegD(op.longitude_i));
|
||||
float myHeading = estimatedHeading(DegD(p.latitude_i), DegD(p.longitude_i));
|
||||
headingRadian = bearingToOther - myHeading;
|
||||
drawCompass(display, compassX, compassY, headingRadian);
|
||||
} else { // direction to node is unknown so display question mark
|
||||
if (hasPosition(node)) {
|
||||
// display direction toward node
|
||||
hasNodeHeading = true;
|
||||
Position &p = node->position;
|
||||
float d = latLongToMeter(DegD(p.latitude_i), DegD(p.longitude_i), DegD(op.latitude_i), DegD(op.longitude_i));
|
||||
if (d < 2000)
|
||||
snprintf(distStr, sizeof(distStr), "%.0f m", d);
|
||||
else
|
||||
snprintf(distStr, sizeof(distStr), "%.1f km", d / 1000);
|
||||
|
||||
// FIXME, also keep the guess at the operators heading and add/substract
|
||||
// it. currently we don't do this and instead draw north up only.
|
||||
float bearingToOther = bearing(DegD(p.latitude_i), DegD(p.longitude_i), DegD(op.latitude_i), DegD(op.longitude_i));
|
||||
headingRadian = bearingToOther - myHeading;
|
||||
drawNodeHeading(display, compassX, compassY, headingRadian);
|
||||
}
|
||||
}
|
||||
if (!hasNodeHeading)
|
||||
// direction to node is unknown so display question mark
|
||||
// Debug info for gps lock errors
|
||||
// DEBUG_MSG("ourNode %d, ourPos %d, theirPos %d\n", !!ourNode, ourNode && hasPosition(ourNode), hasPosition(node));
|
||||
display->drawString(compassX - FONT_HEIGHT / 4, compassY - FONT_HEIGHT / 2, "?");
|
||||
display->drawCircle(compassX, compassY, COMPASS_DIAM / 2);
|
||||
|
||||
display->drawString(compassX - FONT_HEIGHT/4, compassY - FONT_HEIGHT/2, "?");
|
||||
display->drawCircle(compassX, compassY, COMPASS_DIAM / 2);
|
||||
}
|
||||
// Must be after distStr is populated
|
||||
drawColumns(display, x, y, fields);
|
||||
}
|
||||
|
||||
#if 0
|
||||
@@ -498,7 +548,7 @@ void Screen::handleSetOn(bool on)
|
||||
|
||||
void Screen::setup()
|
||||
{
|
||||
PeriodicTask::setup();
|
||||
concurrency::PeriodicTask::setup();
|
||||
|
||||
// We don't set useDisplay until setup() is called, because some boards have a declaration of this object but the device
|
||||
// is never found when probing i2c and therefore we don't call setup and never want to do (invalid) accesses to this device.
|
||||
@@ -538,6 +588,11 @@ void Screen::setup()
|
||||
dispdev.flipScreenVertically();
|
||||
#endif
|
||||
|
||||
// Get our hardware ID
|
||||
uint8_t dmac[6];
|
||||
getMacAddr(dmac);
|
||||
sprintf(ourId, "%02x%02x", dmac[4], dmac[5]);
|
||||
|
||||
// Turn on the display.
|
||||
handleSetOn(true);
|
||||
|
||||
@@ -545,6 +600,11 @@ void Screen::setup()
|
||||
// twice initially.
|
||||
ui.update();
|
||||
ui.update();
|
||||
|
||||
// Subscribe to status updates
|
||||
powerStatusObserver.observe(&powerStatus->onNewStatus);
|
||||
gpsStatusObserver.observe(&gpsStatus->onNewStatus);
|
||||
nodeStatusObserver.observe(&nodeStatus->onNewStatus);
|
||||
}
|
||||
|
||||
void Screen::doTask()
|
||||
@@ -557,7 +617,7 @@ void Screen::doTask()
|
||||
|
||||
// Process incoming commands.
|
||||
for (;;) {
|
||||
CmdItem cmd;
|
||||
ScreenCmd cmd;
|
||||
if (!cmdQueue.dequeue(&cmd, 0)) {
|
||||
break;
|
||||
}
|
||||
@@ -606,14 +666,7 @@ void Screen::doTask()
|
||||
// While showing the bootscreen or Bluetooth pair screen all of our
|
||||
// standard screen switching is stopped.
|
||||
if (showingNormalScreen) {
|
||||
// TODO(girts): decouple nodeDB from screen.
|
||||
// standard screen loop handling ehre
|
||||
// If the # nodes changes, we need to regen our list of screens
|
||||
if (nodeDB.updateGUI || nodeDB.updateTextMessage) {
|
||||
setFrames();
|
||||
nodeDB.updateGUI = false;
|
||||
nodeDB.updateTextMessage = false;
|
||||
}
|
||||
// standard screen loop handling here
|
||||
}
|
||||
|
||||
ui.update();
|
||||
@@ -638,8 +691,8 @@ void Screen::setFrames()
|
||||
DEBUG_MSG("showing standard frames\n");
|
||||
showingNormalScreen = true;
|
||||
|
||||
size_t numnodes = nodeDB.getNumNodes();
|
||||
// We don't show the node info our our node (if we have it yet - we should)
|
||||
size_t numnodes = nodeStatus->getNumTotal();
|
||||
if (numnodes > 0)
|
||||
numnodes--;
|
||||
|
||||
@@ -715,36 +768,65 @@ void DebugInfo::drawFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16
|
||||
|
||||
char channelStr[20];
|
||||
{
|
||||
LockGuard guard(&lock);
|
||||
snprintf(channelStr, sizeof(channelStr), "#%s", channelName.c_str());
|
||||
concurrency::LockGuard guard(&lock);
|
||||
snprintf(channelStr, sizeof(channelStr), "%s", channelName.c_str());
|
||||
|
||||
// Display power status
|
||||
if (powerStatus.haveBattery) drawBattery(display, x, y + 2, imgBattery, &powerStatus); else display->drawFastImage(x, y + 2, 16, 8, powerStatus.usb ? imgUSB : imgPower);
|
||||
if (powerStatus->getHasBattery())
|
||||
drawBattery(display, x, y + 2, imgBattery, powerStatus);
|
||||
else
|
||||
display->drawFastImage(x, y + 2, 16, 8, powerStatus->getHasUSB() ? imgUSB : imgPower);
|
||||
// Display nodes status
|
||||
drawNodes(display, x + (SCREEN_WIDTH * 0.25), y + 2, nodesOnline, nodesTotal);
|
||||
drawNodes(display, x + (SCREEN_WIDTH * 0.25), y + 2, nodeStatus);
|
||||
// Display GPS status
|
||||
drawGPS(display, x + (SCREEN_WIDTH * 0.66), y + 2, gps);
|
||||
drawGPS(display, x + (SCREEN_WIDTH * 0.63), y + 2, gpsStatus);
|
||||
}
|
||||
|
||||
const char *fields[] = {channelStr, nullptr};
|
||||
uint32_t yo = drawRows(display, x, y + FONT_HEIGHT, fields);
|
||||
// Draw the channel name
|
||||
display->drawString(x, y + FONT_HEIGHT, channelStr);
|
||||
// Draw our hardware ID to assist with bluetooth pairing
|
||||
display->drawFastImage(x + SCREEN_WIDTH - (10) - display->getStringWidth(ourId), y + 2 + FONT_HEIGHT, 8, 8, imgInfo);
|
||||
display->drawString(x + SCREEN_WIDTH - display->getStringWidth(ourId), y + FONT_HEIGHT, ourId);
|
||||
|
||||
display->drawLogBuffer(x, yo);
|
||||
// Draw any log messages
|
||||
display->drawLogBuffer(x, y + (FONT_HEIGHT * 2));
|
||||
|
||||
/* Display a heartbeat pixel that blinks every time the frame is redrawn */
|
||||
#ifdef SHOW_REDRAWS
|
||||
if (heartbeat)
|
||||
display->setPixel(0, 0);
|
||||
heartbeat = !heartbeat;
|
||||
#endif
|
||||
}
|
||||
|
||||
//adjust Brightness cycle trough 1 to 254 as long as attachDuringLongPress is true
|
||||
void Screen::adjustBrightness(){
|
||||
if (brightness == 254)
|
||||
{
|
||||
// adjust Brightness cycle trough 1 to 254 as long as attachDuringLongPress is true
|
||||
void Screen::adjustBrightness()
|
||||
{
|
||||
if (brightness == 254) {
|
||||
brightness = 0;
|
||||
} else {
|
||||
brightness++;
|
||||
brightness++;
|
||||
}
|
||||
int width = brightness / 1.984375;
|
||||
dispdev.drawRect( 0, 30, 128, 4);
|
||||
dispdev.fillRect(0, 30, width, 4);
|
||||
int width = brightness / (254.00 / SCREEN_WIDTH);
|
||||
dispdev.drawRect(0, 30, SCREEN_WIDTH, 4);
|
||||
dispdev.fillRect(0, 31, width, 2);
|
||||
dispdev.display();
|
||||
dispdev.setBrightness(brightness);
|
||||
}
|
||||
|
||||
} // namespace meshtastic
|
||||
int Screen::handleStatusUpdate(const meshtastic::Status *arg)
|
||||
{
|
||||
// DEBUG_MSG("Screen got status update %d\n", arg->getStatusType());
|
||||
switch (arg->getStatusType()) {
|
||||
case STATUS_TYPE_NODE:
|
||||
if (nodeDB.updateTextMessage || nodeStatus->getLastNumTotal() != nodeStatus->getNumTotal())
|
||||
setFrames();
|
||||
prevFrame = -1;
|
||||
nodeDB.updateGUI = false;
|
||||
nodeDB.updateTextMessage = false;
|
||||
break;
|
||||
}
|
||||
setPeriod(1); // Update the screen right away
|
||||
return 0;
|
||||
}
|
||||
} // namespace graphics
|
||||
@@ -10,13 +10,14 @@
|
||||
#include <SSD1306Wire.h>
|
||||
#endif
|
||||
|
||||
#include "PeriodicTask.h"
|
||||
#include "concurrency/PeriodicTask.h"
|
||||
#include "TypedQueue.h"
|
||||
#include "lock.h"
|
||||
#include "concurrency/LockGuard.h"
|
||||
#include "power.h"
|
||||
#include "commands.h"
|
||||
#include <string>
|
||||
|
||||
namespace meshtastic
|
||||
namespace graphics
|
||||
{
|
||||
|
||||
// Forward declarations
|
||||
@@ -29,40 +30,13 @@ class DebugInfo
|
||||
DebugInfo(const DebugInfo &) = delete;
|
||||
DebugInfo &operator=(const DebugInfo &) = delete;
|
||||
|
||||
/// Sets user statistics.
|
||||
void setNodeNumbersStatus(int online, int total)
|
||||
{
|
||||
LockGuard guard(&lock);
|
||||
nodesOnline = online;
|
||||
nodesTotal = total;
|
||||
}
|
||||
|
||||
/// Sets the name of the channel.
|
||||
void setChannelNameStatus(const char *name)
|
||||
{
|
||||
LockGuard guard(&lock);
|
||||
concurrency::LockGuard guard(&lock);
|
||||
channelName = name;
|
||||
}
|
||||
|
||||
/// Sets battery/charging/etc status.
|
||||
//
|
||||
void setPowerStatus(const PowerStatus &status)
|
||||
{
|
||||
LockGuard guard(&lock);
|
||||
powerStatus = status;
|
||||
}
|
||||
|
||||
/// Sets GPS status.
|
||||
//
|
||||
// If this function never gets called, we assume GPS does not exist on this
|
||||
// device.
|
||||
// TODO(girts): figure out what the format should be.
|
||||
void setGPSStatus(const char *status)
|
||||
{
|
||||
LockGuard guard(&lock);
|
||||
gpsStatus = status;
|
||||
}
|
||||
|
||||
private:
|
||||
friend Screen;
|
||||
|
||||
@@ -71,28 +45,25 @@ class DebugInfo
|
||||
/// Renders the debug screen.
|
||||
void drawFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y);
|
||||
|
||||
int nodesOnline = 0;
|
||||
int nodesTotal = 0;
|
||||
|
||||
PowerStatus powerStatus;
|
||||
|
||||
std::string channelName;
|
||||
|
||||
std::string gpsStatus;
|
||||
|
||||
/// Protects all of internal state.
|
||||
Lock lock;
|
||||
concurrency::Lock lock;
|
||||
};
|
||||
|
||||
/// Deals with showing things on the screen of the device.
|
||||
//
|
||||
// Other than setup(), this class is thread-safe. All state-changing calls are
|
||||
// queued and executed when the main loop calls us.
|
||||
//
|
||||
// This class is thread-safe (as long as drawFrame is not called multiple times
|
||||
// simultaneously).
|
||||
class Screen : public PeriodicTask
|
||||
/**
|
||||
* @brief This class deals with showing things on the screen of the device.
|
||||
*
|
||||
* @details Other than setup(), this class is thread-safe as long as drawFrame is not called
|
||||
* multiple times simultaneously. All state-changing calls are queued and executed
|
||||
* when the main loop calls us.
|
||||
*/
|
||||
class Screen : public concurrency::PeriodicTask
|
||||
{
|
||||
CallbackObserver<Screen, const meshtastic::Status *> powerStatusObserver = CallbackObserver<Screen, const meshtastic::Status *>(this, &Screen::handleStatusUpdate);
|
||||
CallbackObserver<Screen, const meshtastic::Status *> gpsStatusObserver = CallbackObserver<Screen, const meshtastic::Status *>(this, &Screen::handleStatusUpdate);
|
||||
CallbackObserver<Screen, const meshtastic::Status *> nodeStatusObserver = CallbackObserver<Screen, const meshtastic::Status *>(this, &Screen::handleStatusUpdate);
|
||||
|
||||
public:
|
||||
Screen(uint8_t address, int sda = -1, int scl = -1);
|
||||
|
||||
@@ -111,15 +82,15 @@ class Screen : public PeriodicTask
|
||||
handleSetOn(
|
||||
false); // We handle off commands immediately, because they might be called because the CPU is shutting down
|
||||
else
|
||||
enqueueCmd(CmdItem{.cmd = on ? Cmd::SET_ON : Cmd::SET_OFF});
|
||||
enqueueCmd(ScreenCmd{.cmd = on ? Cmd::SET_ON : Cmd::SET_OFF});
|
||||
}
|
||||
|
||||
/// Handles a button press.
|
||||
void onPress() { enqueueCmd(CmdItem{.cmd = Cmd::ON_PRESS}); }
|
||||
void onPress() { enqueueCmd(ScreenCmd{.cmd = Cmd::ON_PRESS}); }
|
||||
|
||||
// Implementation to Adjust Brightness
|
||||
void adjustBrightness();
|
||||
int brightness = 150;
|
||||
uint8_t brightness = 150;
|
||||
|
||||
/// Starts showing the Bluetooth PIN screen.
|
||||
//
|
||||
@@ -127,22 +98,22 @@ class Screen : public PeriodicTask
|
||||
// with the PIN.
|
||||
void startBluetoothPinScreen(uint32_t pin)
|
||||
{
|
||||
CmdItem cmd;
|
||||
ScreenCmd cmd;
|
||||
cmd.cmd = Cmd::START_BLUETOOTH_PIN_SCREEN;
|
||||
cmd.bluetooth_pin = pin;
|
||||
enqueueCmd(cmd);
|
||||
}
|
||||
|
||||
/// Stops showing the bluetooth PIN screen.
|
||||
void stopBluetoothPinScreen() { enqueueCmd(CmdItem{.cmd = Cmd::STOP_BLUETOOTH_PIN_SCREEN}); }
|
||||
void stopBluetoothPinScreen() { enqueueCmd(ScreenCmd{.cmd = Cmd::STOP_BLUETOOTH_PIN_SCREEN}); }
|
||||
|
||||
/// Stops showing the boot screen.
|
||||
void stopBootScreen() { enqueueCmd(CmdItem{.cmd = Cmd::STOP_BOOT_SCREEN}); }
|
||||
void stopBootScreen() { enqueueCmd(ScreenCmd{.cmd = Cmd::STOP_BOOT_SCREEN}); }
|
||||
|
||||
/// Writes a string to the screen.
|
||||
void print(const char *text)
|
||||
{
|
||||
CmdItem cmd;
|
||||
ScreenCmd cmd;
|
||||
cmd.cmd = Cmd::PRINT;
|
||||
// TODO(girts): strdup() here is scary, but we can't use std::string as
|
||||
// FreeRTOS queue is just dumbly copying memory contents. It would be
|
||||
@@ -187,7 +158,9 @@ class Screen : public PeriodicTask
|
||||
/// Returns a handle to the DebugInfo screen.
|
||||
//
|
||||
// Use this handle to set things like battery status, user count, GPS status, etc.
|
||||
DebugInfo *debug() { return &debugInfo; }
|
||||
DebugInfo* debug_info() { return &debugInfo; }
|
||||
|
||||
int handleStatusUpdate(const meshtastic::Status *arg);
|
||||
|
||||
protected:
|
||||
/// Updates the UI.
|
||||
@@ -196,17 +169,7 @@ class Screen : public PeriodicTask
|
||||
void doTask() final;
|
||||
|
||||
private:
|
||||
enum class Cmd {
|
||||
INVALID,
|
||||
SET_ON,
|
||||
SET_OFF,
|
||||
ON_PRESS,
|
||||
START_BLUETOOTH_PIN_SCREEN,
|
||||
STOP_BLUETOOTH_PIN_SCREEN,
|
||||
STOP_BOOT_SCREEN,
|
||||
PRINT,
|
||||
};
|
||||
struct CmdItem {
|
||||
struct ScreenCmd {
|
||||
Cmd cmd;
|
||||
union {
|
||||
uint32_t bluetooth_pin;
|
||||
@@ -215,7 +178,7 @@ class Screen : public PeriodicTask
|
||||
};
|
||||
|
||||
/// Enques given command item to be processed by main loop().
|
||||
bool enqueueCmd(const CmdItem &cmd)
|
||||
bool enqueueCmd(const ScreenCmd &cmd)
|
||||
{
|
||||
if (!useDisplay)
|
||||
return true; // claim success if our display is not in use
|
||||
@@ -239,7 +202,7 @@ class Screen : public PeriodicTask
|
||||
static void drawDebugInfoTrampoline(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y);
|
||||
|
||||
/// Queue of commands to execute in doTask.
|
||||
TypedQueue<CmdItem> cmdQueue;
|
||||
TypedQueue<ScreenCmd> cmdQueue;
|
||||
/// Whether we are using a display
|
||||
bool useDisplay = false;
|
||||
/// Whether the display is currently powered
|
||||
@@ -250,7 +213,9 @@ class Screen : public PeriodicTask
|
||||
|
||||
/// Holds state for debug information
|
||||
DebugInfo debugInfo;
|
||||
|
||||
/// Display device
|
||||
/** @todo display abstraction */
|
||||
#ifdef USE_SH1106
|
||||
SH1106Wire dispdev;
|
||||
#else
|
||||
@@ -260,4 +225,4 @@ class Screen : public PeriodicTask
|
||||
OLEDDisplayUi ui;
|
||||
};
|
||||
|
||||
} // namespace meshtastic
|
||||
} // namespace graphics
|
||||
17
src/graphics/TFT.cpp
Normal file
17
src/graphics/TFT.cpp
Normal file
@@ -0,0 +1,17 @@
|
||||
#include "configuration.h"
|
||||
|
||||
#ifdef ST7735_CS
|
||||
|
||||
#include <SPI.h>
|
||||
#include <TFT_eSPI.h> // Graphics and font library for ST7735 driver chip
|
||||
|
||||
TFT_eSPI tft = TFT_eSPI(); // Invoke library, pins defined in User_Setup.h
|
||||
|
||||
void TFTinit()
|
||||
{
|
||||
tft.init();
|
||||
tft.setRotation(1);
|
||||
tft.fillScreen(TFT_BLUE);
|
||||
}
|
||||
|
||||
#endif
|
||||
3
src/graphics/TFT.h
Normal file
3
src/graphics/TFT.h
Normal file
@@ -0,0 +1,3 @@
|
||||
#pragma once
|
||||
|
||||
void TFTinit();
|
||||
17
src/graphics/configs.h
Normal file
17
src/graphics/configs.h
Normal file
@@ -0,0 +1,17 @@
|
||||
#pragma once
|
||||
|
||||
#include "fonts.h"
|
||||
|
||||
#define FONT_HEIGHT 14 // actually 13 for "Arial 10" but want a little extra space
|
||||
#define FONT_HEIGHT_16 (ArialMT_Plain_16[1] + 1)
|
||||
// This means the *visible* area (sh1106 can address 132, but shows 128 for example)
|
||||
#define SCREEN_WIDTH 128
|
||||
#define SCREEN_HEIGHT 64
|
||||
#define TRANSITION_FRAMERATE 30 // fps
|
||||
#define IDLE_FRAMERATE 1 // in fps
|
||||
#define COMPASS_DIAM 44
|
||||
|
||||
// DEBUG
|
||||
#define NUM_EXTRA_FRAMES 2 // text message and debug frame
|
||||
// if defined a pixel will blink to show redraws
|
||||
// #define SHOW_REDRAWS
|
||||
@@ -1,3 +1,5 @@
|
||||
#pragma once
|
||||
|
||||
const uint8_t Custom_ArialMT_Plain_10[] PROGMEM = {
|
||||
0x0A, // Width: 10
|
||||
0x0A, // Height: 10
|
||||
@@ -1,21 +1,25 @@
|
||||
#pragma once
|
||||
|
||||
#define SATELLITE_IMAGE_WIDTH 16
|
||||
#define SATELLITE_IMAGE_HEIGHT 15
|
||||
const uint8_t SATELLITE_IMAGE[] PROGMEM = {0x00, 0x08, 0x00, 0x1C, 0x00, 0x0E, 0x20, 0x07, 0x70, 0x02,
|
||||
0xF8, 0x00, 0xF0, 0x01, 0xE0, 0x03, 0xC8, 0x01, 0x9C, 0x54,
|
||||
0x0E, 0x52, 0x07, 0x48, 0x02, 0x26, 0x00, 0x10, 0x00, 0x0E};
|
||||
|
||||
const uint8_t imgSatellite[] PROGMEM = { 0x70, 0x71, 0x22, 0xFA, 0xFA, 0x22, 0x71, 0x70 };
|
||||
const uint8_t imgUSB[] PROGMEM = { 0x60, 0x60, 0x30, 0x18, 0x18, 0x18, 0x24, 0x42, 0x42, 0x42, 0x42, 0x7E, 0x24, 0x24, 0x24, 0x3C };
|
||||
const uint8_t imgPower[] PROGMEM = { 0x40, 0x40, 0x40, 0x58, 0x48, 0x08, 0x08, 0x08, 0x1C, 0x22, 0x22, 0x41, 0x7F, 0x22, 0x22, 0x22 };
|
||||
const uint8_t imgUser[] PROGMEM = { 0x3C, 0x42, 0x99, 0xA5, 0xA5, 0x99, 0x42, 0x3C };
|
||||
const uint8_t imgPositionEmpty[] PROGMEM = { 0x20, 0x30, 0x28, 0x24, 0x42, 0xFF };
|
||||
const uint8_t imgPositionSolid[] PROGMEM = { 0x20, 0x30, 0x38, 0x3C, 0x7E, 0xFF };
|
||||
const uint8_t imgUser[] PROGMEM = { 0x3C, 0x42, 0x99, 0xA5, 0xA5, 0x99, 0x42, 0x3C };
|
||||
const uint8_t imgPositionEmpty[] PROGMEM = { 0x20, 0x30, 0x28, 0x24, 0x42, 0xFF };
|
||||
const uint8_t imgPositionSolid[] PROGMEM = { 0x20, 0x30, 0x38, 0x3C, 0x7E, 0xFF };
|
||||
const uint8_t imgInfo[] PROGMEM = { 0xFF, 0x81, 0x81, 0xB5, 0xB5, 0x81, 0x81, 0xFF };
|
||||
|
||||
#include "icon.xbm"
|
||||
#include "img/icon.xbm"
|
||||
|
||||
// We now programmatically draw our compass
|
||||
#if 0
|
||||
const
|
||||
#include "compass.xbm"
|
||||
#include "img/compass.xbm"
|
||||
#endif
|
||||
|
||||
#if 0
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user