mirror of
https://github.com/meshtastic/firmware.git
synced 2026-01-10 20:07:45 +00:00
Compare commits
289 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2b10a03178 | ||
|
|
80fb7e4ab8 | ||
|
|
7c1ddd9447 | ||
|
|
c55074f7fe | ||
|
|
450e877cfb | ||
|
|
c0fbfccf43 | ||
|
|
51ccc3aa9e | ||
|
|
e35f137986 | ||
|
|
71c35304d6 | ||
|
|
c1733a4ac6 | ||
|
|
66a7e8eab9 | ||
|
|
a872231f8a | ||
|
|
52ec4d511c | ||
|
|
aa7fb86798 | ||
|
|
875eeb699c | ||
|
|
b239b4dc57 | ||
|
|
19db5ba421 | ||
|
|
e225af28dc | ||
|
|
eecf89a9c0 | ||
|
|
c9b1ee532d | ||
|
|
8c27baae84 | ||
|
|
8c225a3c65 | ||
|
|
7ff1f3a759 | ||
|
|
bbc8fc0269 | ||
|
|
9d81511153 | ||
|
|
16d63bd0ce | ||
|
|
f2b7ff2b79 | ||
|
|
bc8453283f | ||
|
|
2ff5046dcd | ||
|
|
917090856f | ||
|
|
b45d633a34 | ||
|
|
fdfe62edf0 | ||
|
|
b29bcbbd41 | ||
|
|
8e8170b667 | ||
|
|
2fa38c7dc4 | ||
|
|
ca8a0ca8d2 | ||
|
|
58bb7169a0 | ||
|
|
cb541d75a9 | ||
|
|
4ee01acb40 | ||
|
|
d678c48884 | ||
|
|
9f9f02fc6f | ||
|
|
abf135abce | ||
|
|
f7beec4728 | ||
|
|
ccf3450864 | ||
|
|
86553a4fc9 | ||
|
|
55349ea570 | ||
|
|
486b03e985 | ||
|
|
ccb232b6ac | ||
|
|
22af1b551a | ||
|
|
c696d226b2 | ||
|
|
9035a06b4a | ||
|
|
056940a4ad | ||
|
|
e7af338c31 | ||
|
|
9069e5b33e | ||
|
|
82db1f1db6 | ||
|
|
f46059ec4c | ||
|
|
0c71de4e59 | ||
|
|
0fa654e53a | ||
|
|
45c17659cc | ||
|
|
b901f8d9ae | ||
|
|
9c60a7966f | ||
|
|
6d66a53f8d | ||
|
|
324627482a | ||
|
|
0c6c189028 | ||
|
|
8a68ae0d04 | ||
|
|
5661e5dad6 | ||
|
|
e9affb50d2 | ||
|
|
c00173dbd2 | ||
|
|
e8c6fccd63 | ||
|
|
487b8c6e9e | ||
|
|
b2481d1450 | ||
|
|
399fbc5d65 | ||
|
|
48b38ed94b | ||
|
|
c0444ef16f | ||
|
|
1719a8e764 | ||
|
|
242bcc8353 | ||
|
|
092af0f9f9 | ||
|
|
133a7ff166 | ||
|
|
5df08410e7 | ||
|
|
9f9787bc03 | ||
|
|
7129a19f35 | ||
|
|
f45ffc8773 | ||
|
|
3162f74945 | ||
|
|
6cef3e41e7 | ||
|
|
c0e2ec8dec | ||
|
|
aee81c8dcd | ||
|
|
9e736ab0d7 | ||
|
|
85752b0fc7 | ||
|
|
c6f34c59b4 | ||
|
|
7f07725840 | ||
|
|
c81d090464 | ||
|
|
c524732849 | ||
|
|
5e303f8a1f | ||
|
|
2246564279 | ||
|
|
eff0c1fe89 | ||
|
|
ad322476d2 | ||
|
|
2561742683 | ||
|
|
fa9e31fe03 | ||
|
|
3ac5b045c4 | ||
|
|
6a593e01e1 | ||
|
|
6f6dd2291e | ||
|
|
2b4ddc07f5 | ||
|
|
63c650c33e | ||
|
|
dc29161f37 | ||
|
|
8a6fdafc79 | ||
|
|
ea40bd991c | ||
|
|
e19dd46f0f | ||
|
|
532b06c280 | ||
|
|
a8480d1eaf | ||
|
|
0cf7aaffff | ||
|
|
e2e1819ef1 | ||
|
|
31b89e2932 | ||
|
|
a021ff7eb8 | ||
|
|
bb5d0fac90 | ||
|
|
df5ed64514 | ||
|
|
9db5f9ff67 | ||
|
|
ca83a78e13 | ||
|
|
13eef9a309 | ||
|
|
2a8ac2c0c6 | ||
|
|
c97342db99 | ||
|
|
d7b2a0ed79 | ||
|
|
af0a1b5db5 | ||
|
|
9cf030d587 | ||
|
|
c04d70d5e5 | ||
|
|
2a47819fd6 | ||
|
|
4516c8f9b5 | ||
|
|
e4fdf26dc7 | ||
|
|
dd511588a2 | ||
|
|
79dad8ec8c | ||
|
|
39d14fedc2 | ||
|
|
1da38fc748 | ||
|
|
b5f50efdcd | ||
|
|
046e691d4e | ||
|
|
e72531b090 | ||
|
|
81e320c9cf | ||
|
|
fa8cc74141 | ||
|
|
c7d9ff7cc0 | ||
|
|
8704a9d08f | ||
|
|
c0d27e2ce9 | ||
|
|
84b9028ecb | ||
|
|
4fda7098c0 | ||
|
|
8e8264efb0 | ||
|
|
54e780a6ca | ||
|
|
125eb2b784 | ||
|
|
6ea9cdc83b | ||
|
|
c0711fde69 | ||
|
|
20b8d2c4a5 | ||
|
|
73ae151971 | ||
|
|
f4806c9dd7 | ||
|
|
79532210e8 | ||
|
|
d7f26493a5 | ||
|
|
b9d025dd58 | ||
|
|
f435086a5a | ||
|
|
3dcdf372d7 | ||
|
|
cd84f2867c | ||
|
|
cafe00e463 | ||
|
|
fd9ffbbb88 | ||
|
|
d1be7cf142 | ||
|
|
d1f0be215b | ||
|
|
3a2c17998e | ||
|
|
a0dd051511 | ||
|
|
4faff3ec6f | ||
|
|
f110225173 | ||
|
|
2684257e7e | ||
|
|
51fb1021df | ||
|
|
51d0d0d6c5 | ||
|
|
047df76373 | ||
|
|
6da4e30215 | ||
|
|
dbf0569e29 | ||
|
|
18220b88b3 | ||
|
|
665da2fb00 | ||
|
|
57ffe6622d | ||
|
|
485fec9649 | ||
|
|
bd85736226 | ||
|
|
4ec8986934 | ||
|
|
b963216764 | ||
|
|
813fd95bc8 | ||
|
|
3598c91c29 | ||
|
|
507cd1dd20 | ||
|
|
e39506824d | ||
|
|
f68a31ab28 | ||
|
|
b1181deb58 | ||
|
|
89b32dd7ee | ||
|
|
c54e87f9a2 | ||
|
|
eee7e1de57 | ||
|
|
3c60df1565 | ||
|
|
a827017bd2 | ||
|
|
95c502c658 | ||
|
|
0f573901d5 | ||
|
|
fdc9bf5783 | ||
|
|
37e0f9a325 | ||
|
|
0c06d8db3c | ||
|
|
0be4bbb369 | ||
|
|
f02ab88393 | ||
|
|
c9d4de8808 | ||
|
|
adb912b665 | ||
|
|
3f5da1e03e | ||
|
|
0a40d920e3 | ||
|
|
39311f1e40 | ||
|
|
9cd24a5646 | ||
|
|
1c0efde315 | ||
|
|
c82905bbdd | ||
|
|
275eace968 | ||
|
|
5688c8b81e | ||
|
|
8b2798abd5 | ||
|
|
6d977923b6 | ||
|
|
52dacaed37 | ||
|
|
7a381eaea1 | ||
|
|
69391e186b | ||
|
|
06f8beaa17 | ||
|
|
3798f4ca5b | ||
|
|
4fd243a6e4 | ||
|
|
d458f673be | ||
|
|
cfcb00b943 | ||
|
|
977e47d109 | ||
|
|
cfeb40f36d | ||
|
|
4fcc3ac1de | ||
|
|
f4afa6931b | ||
|
|
71be71d63d | ||
|
|
de9f7e6c39 | ||
|
|
7c8db2b501 | ||
|
|
cd653f9434 | ||
|
|
74bc05936d | ||
|
|
7aacfd66ef | ||
|
|
3636b87db0 | ||
|
|
d6bd328576 | ||
|
|
0af5b225c4 | ||
|
|
f7dcef39ce | ||
|
|
07042178d2 | ||
|
|
243878f2a0 | ||
|
|
d3f8a76cce | ||
|
|
20131a51a2 | ||
|
|
1c9a369774 | ||
|
|
dcb426f58f | ||
|
|
35bcb5297a | ||
|
|
84e3d7c276 | ||
|
|
9b03f0ac8e | ||
|
|
eb402809e2 | ||
|
|
e9c9e40624 | ||
|
|
01eed97b91 | ||
|
|
94a47dba7d | ||
|
|
bce2c9347b | ||
|
|
da8b1d41c7 | ||
|
|
3ddae5faec | ||
|
|
34faea6100 | ||
|
|
01848a9e5d | ||
|
|
10db80541f | ||
|
|
edd1268f5f | ||
|
|
11c16e8bbc | ||
|
|
7d411351c0 | ||
|
|
df21602c90 | ||
|
|
ce4ccf3cc4 | ||
|
|
a7f93de3ad | ||
|
|
8e8257adf3 | ||
|
|
e627725dfc | ||
|
|
b3ba557b8b | ||
|
|
bd03650140 | ||
|
|
42f51f33a8 | ||
|
|
8295b88d96 | ||
|
|
70313b2660 | ||
|
|
745d3775b4 | ||
|
|
aa176b6593 | ||
|
|
b0e3a7524f | ||
|
|
5ceee50bb5 | ||
|
|
ebdad76fb2 | ||
|
|
925829dc58 | ||
|
|
e04ea853dc | ||
|
|
9587729bb0 | ||
|
|
6ec368bf02 | ||
|
|
d71c7b512f | ||
|
|
349701ac14 | ||
|
|
d424fa5ea8 | ||
|
|
ca6293eefe | ||
|
|
d289e8a86f | ||
|
|
96328526b7 | ||
|
|
279c89dca3 | ||
|
|
a7a52e08d1 | ||
|
|
f6336855d0 | ||
|
|
727d8a6456 | ||
|
|
7b80b95381 | ||
|
|
2867f8fd53 | ||
|
|
cdf416cb73 | ||
|
|
7716d62018 | ||
|
|
d5f76b16b9 | ||
|
|
552406b15f | ||
|
|
9a3d558f61 | ||
|
|
85ddf3be1b | ||
|
|
3ca42b8f51 | ||
|
|
5595fb38c1 |
14
.github/workflows/main.yml
vendored
14
.github/workflows/main.yml
vendored
@@ -22,5 +22,15 @@ jobs:
|
||||
- name: Install extra python tools
|
||||
run: |
|
||||
pip install -U adafruit-nrfutil
|
||||
- name: Build
|
||||
run: platformio run -e tbeam -e heltec -e lora-relay-v1 -e linux
|
||||
- name: Install libs needed for linux build
|
||||
run: |
|
||||
sudo apt install -y libpsocksxx-dev
|
||||
- name: Build for tbeam
|
||||
run: platformio run -e tbeam
|
||||
- name: Build for heltec
|
||||
run: platformio run -e heltec
|
||||
- name: Build for lora-relay-v1
|
||||
run: platformio run -e lora-relay-v1
|
||||
- name: Build for linux
|
||||
run: platformio run -e linux
|
||||
|
||||
|
||||
3
.gitmodules
vendored
3
.gitmodules
vendored
@@ -4,3 +4,6 @@
|
||||
[submodule "sdk-nrfxlib"]
|
||||
path = sdk-nrfxlib
|
||||
url = https://github.com/nrfconnect/sdk-nrfxlib.git
|
||||
[submodule "design"]
|
||||
path = design
|
||||
url = https://github.com/meshtastic/meshtastic-design.git
|
||||
|
||||
@@ -4,7 +4,7 @@ This is the device side code for the [meshtastic.org](https://www.meshtastic.org
|
||||
|
||||

|
||||
|
||||
Meshtastic™ is a project that lets you use
|
||||
Meshtastic® is a project that lets you use
|
||||
inexpensive GPS mesh radios as an extensible, super long battery life mesh GPS communicator. These radios are great for hiking, skiing, paragliding -
|
||||
essentially any hobby where you don't have reliable internet access. Each member of your private mesh can always see the location and distance of all other
|
||||
members and any text messages sent to your group chat.
|
||||
|
||||
@@ -14,8 +14,6 @@ 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"
|
||||
|
||||
NUM_JOBS=2
|
||||
|
||||
OUTDIR=release/latest
|
||||
|
||||
# We keep all old builds (and their map files in the archive dir)
|
||||
@@ -23,7 +21,7 @@ ARCHIVEDIR=release/archive
|
||||
|
||||
rm -f $OUTDIR/firmware*
|
||||
|
||||
mkdir -p $OUTDIR/bins
|
||||
mkdir -p $OUTDIR/bins $ARCHIVEDIR
|
||||
rm -r $OUTDIR/bins/*
|
||||
mkdir -p $OUTDIR/bins/universal $OUTDIR/elfs/universal
|
||||
|
||||
@@ -51,7 +49,7 @@ function do_build() {
|
||||
basename=universal/firmware-$BOARD-$VERSION
|
||||
fi
|
||||
|
||||
pio run --jobs $NUM_JOBS --environment $BOARD # -v
|
||||
pio run --environment $BOARD # -v
|
||||
SRCELF=.pio/build/$BOARD/firmware.elf
|
||||
cp $SRCELF $OUTDIR/elfs/$basename.elf
|
||||
|
||||
|
||||
19
bin/gen-images.sh
Executable file
19
bin/gen-images.sh
Executable file
@@ -0,0 +1,19 @@
|
||||
|
||||
|
||||
set -e
|
||||
|
||||
# regen the design bins first
|
||||
cd design
|
||||
bin/generate-pngs.sh
|
||||
cd ..
|
||||
|
||||
# assumes 50 wide, 28 high
|
||||
convert design/logo/png/Mesh_Logo_Black_Small.png -background white -alpha Background src/graphics/img/icon.xbm
|
||||
|
||||
inkscape --batch-process -o images/compass.png -w 48 -h 48 images/location_searching-24px.svg
|
||||
convert compass.png -background white -alpha Background src/graphics/img/compass.xbm
|
||||
|
||||
inkscape --batch-process -o images/face.png -w 13 -h 13 images/face-24px.svg
|
||||
|
||||
inkscape --batch-process -o images/pin.png -w 13 -h 13 images/room-24px.svg
|
||||
convert pin.png -background white -alpha Background src/graphics/img/pin.xbm
|
||||
@@ -1,6 +1,6 @@
|
||||
# You probably don't need this - it is a basic test of the serial flash on the TTGO eink board
|
||||
|
||||
nrfjprog -qspiini nrf52/ttgo_eink_qpsi.ini --qspieraseall
|
||||
nrfjprog --qspiini nrf52/ttgo_eink_qpsi.ini --qspieraseall
|
||||
nrfjprog --qspiini nrf52/ttgo_eink_qpsi.ini --memwr 0x12000000 --val 0xdeadbeef --verify
|
||||
nrfjprog --qspiini nrf52/ttgo_eink_qpsi.ini --readqspi spi.hex
|
||||
objdump -s spi.hex | less
|
||||
|
||||
61
boards/eink0.1.json
Normal file
61
boards/eink0.1.json
Normal file
@@ -0,0 +1,61 @@
|
||||
{
|
||||
"build": {
|
||||
"arduino": {
|
||||
"ldscript": "nrf52840_s140_v6.ld"
|
||||
},
|
||||
"core": "nRF5",
|
||||
"cpu": "cortex-m4",
|
||||
"extra_flags": "-DARDUINO_NRF52840_TTGO_EINK -DNRF52840_XXAA",
|
||||
"f_cpu": "64000000L",
|
||||
"hwids": [
|
||||
[
|
||||
"0x239A",
|
||||
"0x4405"
|
||||
]
|
||||
],
|
||||
"usb_product": "TTGO_eink",
|
||||
"mcu": "nrf52840",
|
||||
"variant": "eink0.1",
|
||||
"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": "TTGO eink (Adafruit BSP)",
|
||||
"upload": {
|
||||
"maximum_ram_size": 248832,
|
||||
"maximum_size": 815104,
|
||||
"require_upload_port": true,
|
||||
"speed": 115200,
|
||||
"protocol": "jlink",
|
||||
"protocols": [
|
||||
"jlink",
|
||||
"nrfjprog",
|
||||
"stlink"
|
||||
]
|
||||
},
|
||||
"url": "FIXME",
|
||||
"vendor": "TTGO"
|
||||
}
|
||||
|
Before Width: | Height: | Size: 532 B After Width: | Height: | Size: 532 B |
|
Before Width: | Height: | Size: 442 B After Width: | Height: | Size: 442 B |
|
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 1.1 KiB |
1
design
Submodule
1
design
Submodule
Submodule design added at 73ba05ceef
@@ -1,13 +1,13 @@
|
||||
# What is Meshtastic?
|
||||
|
||||
Meshtastic™ is a project that lets you use
|
||||
Meshtastic® is a project that lets you use
|
||||
inexpensive (\$30 ish) GPS radios as an extensible, long battery life, secure, mesh GPS communicator. These radios are great for hiking, skiing, paragliding - essentially any hobby where you don't have reliable internet access. Each member of your private mesh can always see the location and distance of all other members and any text messages sent to your group chat.
|
||||
|
||||
The radios automatically create a mesh to forward packets as needed, so everyone in the group can receive messages from even the furthest member. The radios will optionally work with your phone, but no phone is required.
|
||||
|
||||
Note: Questions after reading this? See our new [forum](https://meshtastic.discourse.group/).
|
||||
|
||||
### Uses
|
||||
## Uses
|
||||
|
||||
- Outdoor sports where cellular coverage is limited. (Hiking, Skiing, Boating, Paragliding, Gliders etc..)
|
||||
- Applications where closed source GPS communicators just won't cut it (it is easy to add features for glider pilots etc...)
|
||||
@@ -17,7 +17,7 @@ Note: Questions after reading this? See our new [forum](https://meshtastic.disco
|
||||
|
||||
[](https://www.youtube.com/watch?v=WlNbMbVZlHI "Meshtastic early demo")
|
||||
|
||||
### Features
|
||||
## Features
|
||||
|
||||
Not all of these features are fully implemented yet - see **important** disclaimers below. But they should be in by the time we decide to call this project beta (three months?)
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
We use the same channel maps as LoRaWAN (though this is not LoRaWAN).
|
||||
|
||||

|
||||

|
||||
|
||||
See [this site](https://www.rfwireless-world.com/Tutorials/LoRa-channels-list.html) for more information.
|
||||
|
||||
@@ -28,3 +28,78 @@ The maximum output power for North America is +30 dBm ERP.
|
||||
|
||||
The band is from 902 to 928 MHz. It mentions channel number and its respective channel frequency. All the 13 channels are separated by 2.16 MHz with respect to the adjacent channels.
|
||||
Channel zero starts at 903.08 MHz center frequency.
|
||||
|
||||
## Data-rates
|
||||
|
||||
### About
|
||||
|
||||
Various data-rates are selectable when configuring a channel and are inversely proportional to the theoretical range of the devices.
|
||||
|
||||
Considerations:
|
||||
|
||||
* Spreading Factor - How much we "spread" our data over time.
|
||||
* * Each step up in Spreading Factor dobules the airtime to transmit.
|
||||
* * Each step up in Spreading Factor adds about 2.5db extra link budget.
|
||||
* Bandwidth - How big of a slice of the spectrum we use.
|
||||
* * Each doubling of the bandwidth is almost 3db less link budget.
|
||||
* * Bandwidths less than 31 may be unstable unless you have a high quality Crystal Ossilator.
|
||||
* Coding Rate - How much redundency we encode to resist noise.
|
||||
* * Increasing coding rate increases reliability while decrasing data-rate.
|
||||
* * 4/5 - 1.25x overhead
|
||||
* * 4/6 - 1.5x overhead
|
||||
* * 4/7 - 1.75x overhead
|
||||
* * 4/8 - 2x overhead
|
||||
|
||||
|
||||
### Pre-Defined
|
||||
|
||||
We have four predefined channels. These are the most common settings and have been proven to work well:
|
||||
|
||||
| Channel setting | Alt Channel Name | Data-rate | SF / Symbols | Coding Rate | Bandwidth | Link Budget |
|
||||
|:---------------------------|:-----------------|:---------------------|:-------------|:------------|:----------|:------------|
|
||||
| Short range (but fast) | Short Fast | 21.875 kbps | 7 / 128 | 4/5 | 125 | 134dB |
|
||||
| Medium range (but fast) | Medium | 5.469 kbps | 7 / 128 | 4/5 | 500 | 140dB |
|
||||
| Long range (but slower) | Long Alt | 0.275 kbps | 9 / 512 | 4/8 | 31 | 153dB |
|
||||
| Very long range (but slow) | Long Slow | 0.183 kbps (default) | 12 / 4096 | 4/8 | 125 | 154dB |
|
||||
|
||||
The link budget used by these calculations assumes a transmit power of 17dBm and an antenna with 0dB gain. Adjust your link budget assumptions based on your actual devices.
|
||||
|
||||
### Custom Settings
|
||||
|
||||
You may want to select other channels for your usage. The other settings can be set by using the Python API.
|
||||
|
||||
> meshtastic --setchan spread_factor 10 --setchan coding_rate 8 --setchan bandwidth 125
|
||||
|
||||
After applying the settings, you will need to restart the device. After your device is restarted, it will generate a new crypto key and you will need to share the newly generated QR Code or URL to all your other devices.
|
||||
|
||||
Some example settings:
|
||||
|
||||
| Data-rate | SF / Symbols | Coding Rate | Bandwidth | Link Budget | Note |
|
||||
|:---------------------|:-------------|:------------|:----------|:------------|:-----|
|
||||
| 37.50 kbps | 6 / 64 | 4/5 | 500 | 129dB | Fastest possible speed |
|
||||
| 3.125 kbps | 8 / 256 | 4/5 | 125 | 143dB | |
|
||||
| 1.953 kbps | 8 / 256 | 4/8 | 125 | 143dB | |
|
||||
| 1.343 kbps | 11 / 2048 | 4/8 | 500 | 145dB | |
|
||||
| 1.099 kbps | 9 / 512 | 4/8 | 125 | 146dB | |
|
||||
| 0.814 kbps | 10 / 1024 | 4/6 | 125 | 149dB | |
|
||||
| 0.610 kbps | 10 / 1024 | 4/8 | 125 | 149dB | |
|
||||
| 0.488 kbps | 11 / 2048 | 4/6 | 125 | 152dB | |
|
||||
| 0.336 kbps | 11 / 2048 | 4/8 | 125 | 152dB | |
|
||||
| 0.073 kbps | 12 / 4096 | 4/5 | 31 | 160dB | Twice the range and/or coverage of "Long Slow", low resliance to noise |
|
||||
| 0.046 kbps | 12 / 4096 | 4/8 | 31 | 160dB | Twice the range and/or coverage of "Long Slow", high resliance to noise |
|
||||
|
||||
The link budget used by these calculations assumes a transmit power of 17dBm and an antenna with 0dB gain. Adjust your link budget assumptions based on your actual devices.
|
||||
|
||||
These channel settings may have not been tested. Use at your own discression. Share on https://meshtastic.discourse.group with your successes or failure.
|
||||
|
||||
## Cryptography
|
||||
|
||||
The preshared key used by the devices can be modified.
|
||||
|
||||
* 0 = No crypto
|
||||
* 1 = Default channel key
|
||||
* 2 - 10 = The default channel key, except with 1 through 9 added to the last byte
|
||||
|
||||
Use of cryptography can also be modified. To disable cryptography (maybe useful if you have HAM radio license):
|
||||
|
||||
> meshtastic --setchan psk 0
|
||||
|
||||
@@ -2,6 +2,27 @@
|
||||
|
||||
You probably don't care about this section - skip to the next one.
|
||||
|
||||
eink:
|
||||
|
||||
* new battery level sensing
|
||||
* measure current draw
|
||||
* DONE: fix backlight
|
||||
* USB is busted because of power enable mode?
|
||||
* OHH BME280! THAT IS GREAT!
|
||||
* make new screen work, ask for datasheet
|
||||
* say I think you could ship this
|
||||
* leds seem busted
|
||||
* usb doesn't stay connected
|
||||
* check GPS works
|
||||
* check GPS fast locking
|
||||
* send email about variants & faster flash programming - https://github.com/geeksville/Meshtastic-esp32/commit/f110225173a77326aac029321cdb6491bfa640f6
|
||||
* send PR for bootloader
|
||||
* fix nrf52 time/date
|
||||
* send new master bin file
|
||||
* send email about low power mode problems
|
||||
* support new flash chip in appload, possibly use low power mode
|
||||
* swbug! stuck busy tx occurred!
|
||||
|
||||
For app cleanup:
|
||||
|
||||
* use structured logging to kep logs in ram. Also send logs as packets to api clients
|
||||
|
||||
@@ -39,9 +39,20 @@ cd Meshtastic-device
|
||||
|
||||
## Decoding stack traces
|
||||
|
||||
### Option 1
|
||||
|
||||
If you get a crash, you can decode the addresses from the `Backtrace:` line:
|
||||
|
||||
1. Save the `Backtrace: 0x....` line to a file, e.g., `backtrace.txt`.
|
||||
2. Run `bin/exception_decoder.py backtrace.txt` (this uses symbols from the
|
||||
last `firmware.elf`, so you must be running the same binary that's still in
|
||||
your `.pio/build` directory).
|
||||
|
||||
### Option 2
|
||||
|
||||
You can run the exception decoder to monitor the serial output and decode backtraces in real time.
|
||||
|
||||
1. From within PlatformIO, open a new terminal.
|
||||
2. At the the terminal, enter:
|
||||
`pio device monitor --port /dev/cu.SLAB_USBtoUART -f esp32_exception_decoder`
|
||||
Replace the value of port with the location of your serial port.
|
||||
|
||||
@@ -13,7 +13,7 @@ the project developers are not cryptography experts. Therefore we ask two things
|
||||
Based on comments from reviewers (see below), here's some tips for usage of these radios. So you can know the level of protection offered:
|
||||
|
||||
* It is pretty likely that the AES256 security is implemented 'correctly' and an observer will not be able to decode your messages.
|
||||
* Warning: If an attacker is able to get one of the radios in their position, they could either a) extract the channel key from that device or b) use that radio to listen to new communications.
|
||||
* Warning: If an attacker is able to get one of the radios in their posession, they could either a) extract the channel key from that device or b) use that radio to listen to new communications.
|
||||
* Warning: If an attacker is able to get the "Channel QR code/URL" that you share with others - that attacker could then be able to read any messages sent on the channel (either tomorrow or in the past - if they kept a raw copy of those broadcast packets)
|
||||
|
||||
Possible future areas of work (if there is enough interest - post in our [forum](https://meshtastic.discourse.group) if you want this):
|
||||
@@ -48,4 +48,4 @@ I'm assuming that meshtastic is being used to hike in places where someone capab
|
||||
* I think the bigger encryption question is "what does the encryption need to do"? As it stands, an attacker who has yet to capture any of the devices cannot reasonably capture text or location data. An attacker who captures any device in the channel/mesh can read everything going to that device, everything stored on that device, and any other communication within the channel that they captured in encrypted form. If that capability basically matches your expectations, it is suitable for whatever adventures this was intended for, then, based on information publicly available or widely disclosed, the encryption is good. If those properties are distressing (like, device history is deliberately limited and you don't want a device captured today to endanger the information sent over the channel yesterday) we could talk about ways to achieve that (most likely synchronizing time and replacing the key with its own SHA256 every X hours, and ensuring the old key is not retained unnecessarily).
|
||||
* Two other things to keep in mind are that AES-CTR does not itself provide authenticity (e.g. an attacker can flip bits in replaying data and scramble the resulting plaintext), and that the current scheme gives some hints about transmission in the size. So, if you worry about an adversary deliberately messing-up messages or knowing the length of a text message, it looks like those might be possible.
|
||||
|
||||
I'm guessing that the network behaves somewhat like a store-and-forward network - or, at least, that the goal is to avoid establishing a two-way connection to transmit data. I'm afraid I haven't worked with mesh networks much, but remember studying them briefly in school about ten years ago.
|
||||
I'm guessing that the network behaves somewhat like a store-and-forward network - or, at least, that the goal is to avoid establishing a two-way connection to transmit data. I'm afraid I haven't worked with mesh networks much, but remember studying them briefly in school about ten years ago.
|
||||
|
||||
@@ -15,10 +15,10 @@ Packets can be sent/received either as raw binary structures or as [Protobufs](h
|
||||
The relevant bits of the class heirarchy are as follows
|
||||
|
||||
* [MeshPlugin](/src/mesh/MeshPlugin.h) (in src/mesh/MeshPlugin.h) - you probably don't want to use this baseclass directly
|
||||
* [SinglePortPlugin](/src/mesh/SinglePortPlugin.h) (in src/mesh/SinglePortPlugin.h) - for plugins that receive from a single port number (the normal case)
|
||||
* [ProtobufPlugin](/src/mesh/ProtobufPlugin.h) (in src/mesh/ProtobufPlugin.h) - for plugins that are sending/receiving a single particular Protobuf type. Inherit from this if you are using protocol buffers in your plugin.
|
||||
* [SinglePortPlugin](/src/mesh/SinglePortPlugin.h) (in src/mesh/SinglePortPlugin.h) - for plugins that send/receive from a single port number (the normal case)
|
||||
* [ProtobufPlugin](/src/mesh/ProtobufPlugin.h) (in src/mesh/ProtobufPlugin.h) - for plugins that send/receive a single particular Protobuf type. Inherit from this if you are using protocol buffers in your plugin.
|
||||
|
||||
You will typically want to inherit from either SinglePortPlugin (if you are just sending raw bytes) or ProtobufPlugin (if you are sending protobufs). You'll implement your own handleReceived/handleReceivedProtobuf - probably based on the example code.
|
||||
You will typically want to inherit from either SinglePortPlugin (if you are just sending/receiving raw bytes) or ProtobufPlugin (if you are sending/receiving protobufs). You'll implement your own handleReceived/handleReceivedProtobuf - probably based on the example code.
|
||||
|
||||
If your plugin needs to perform any operations at startup you can override and implement the setup() method to run your code.
|
||||
|
||||
@@ -45,25 +45,30 @@ A number of [key services](/src/plugins) are implemented using the plugin API, t
|
||||
The easiest way to get started is:
|
||||
|
||||
* [Build and install](build-instructions.md) the standard codebase from github.
|
||||
* Copy [src/plugins/ReplyPlugin.*](/src/plugins/ReplyPlugin.cpp) into src/plugins/YourPlugin.*. Then change the port number from REPLY_APP to PRIVATE_APP.
|
||||
* Copy [src/plugins/ReplyPlugin.*](/src/plugins/ReplyPlugin.cpp) into src/plugins/YourPlugin.*. Then change the port number from *PortNum_REPLY_APP* to *PortNum_PRIVATE_APP*.
|
||||
* Edit plugins/Plugins.cpp:setupPlugins() to add a call to create an instance of your plugin (see comment at head of that function)
|
||||
* Rebuild with your new messaging goodness and install on the device
|
||||
* Use the [meshtastic commandline tool](https://github.com/meshtastic/Meshtastic-python) to send a packet to your board "meshtastic --dest 1234 --ping"
|
||||
* Use the [meshtastic commandline tool](https://github.com/meshtastic/Meshtastic-python) to send a packet to your board, for example "*meshtastic --dest 1234 --sendping*", where *1234* is another mesh node to send the ping to.
|
||||
|
||||
## Threading
|
||||
|
||||
It is very common that you would like your plugin to be invoked periodically.
|
||||
We use a crude/basic cooperative threading system to allow this on any of our supported platforms. Simply inherit from OSThread and implement runOnce(). See the OSThread [documentation](/src/concurrency/OSThread.h) for more details. For an example consumer of this API see RemoteHardwarePlugin::runOnce.
|
||||
|
||||
## Sending messages
|
||||
|
||||
If you would like to proactively send messages (rather than just responding to them), just call service.sendToMesh(). For an example of this see [NodeInfoPlugin::sendOurNodeInfo(...)](/src/plugins/NodeInfoPlugin.cpp).
|
||||
|
||||
## Picking a port number
|
||||
|
||||
For any new 'apps' that run on the device or via sister apps on phones/PCs they should pick and use a unique 'portnum' for their application.
|
||||
|
||||
If you are making a new app using meshtastic, please send in a pull request to add your 'portnum' to [the master list](https://github.com/meshtastic/Meshtastic-protobufs/blob/master/portnums.proto). PortNums should be assigned in the following range:
|
||||
|
||||
* 0-63 Core Meshtastic use, do not use for third party apps
|
||||
* 64-127 Registered 3rd party apps, send in a pull request that adds a new entry to portnums.proto to register your application
|
||||
* 256-511 Use one of these portnums for your private applications that you don't want to register publically
|
||||
* 1024-66559 Are reserved for use by IP tunneling (see FIXME for more information)
|
||||
* **0-63** Core Meshtastic use; do not use for third party apps
|
||||
* **64-127** Registered 3rd party apps. Send in a pull request that adds a new entry to portnums.proto to register your application
|
||||
* **256-511** Use one of these portnums for your private applications that you don't want to register publically
|
||||
* **1024-66559** Are reserved for use by IP tunneling (see *FIXME* for more information)
|
||||
|
||||
All other values are reserved.
|
||||
|
||||
@@ -73,4 +78,4 @@ If you would like to use protocol buffers to define the structures you send over
|
||||
|
||||
* Create a new .proto file in the protos directory. You can use the existing [remote_hardware.proto](https://github.com/meshtastic/Meshtastic-protobufs/blob/master/remote_hardware.proto) file as an example.
|
||||
* Run "bin/regen-protos.sh" to regenerate the C code for accessing the protocol buffers. If you don't have the required nanopb tool, follow the instructions printed by the script to get it.
|
||||
* Done! You can now use your new protobuf just like any of the existing protobufs in meshtastic.
|
||||
* Done! You can now use your new protobuf just like any of the existing protobufs in meshtastic.
|
||||
|
||||
89
docs/software/plugins/ExternalNotificationPlugin.md
Normal file
89
docs/software/plugins/ExternalNotificationPlugin.md
Normal file
@@ -0,0 +1,89 @@
|
||||
# About
|
||||
|
||||
The ExternalNotification Plugin will allow you to connect a speaker, LED or other device to notify you when a message has been received from the mesh network.
|
||||
|
||||
# Configuration
|
||||
|
||||
These are the settings that can be configured.
|
||||
|
||||
ext_notification_plugin_enabled
|
||||
Is the plugin enabled?
|
||||
|
||||
0 = Disabled (Default)
|
||||
1 = Enabled
|
||||
|
||||
ext_notification_plugin_active
|
||||
Is your external circuit triggered when our GPIO is low or high?
|
||||
|
||||
0 = Active Low (Default)
|
||||
1 = Active High
|
||||
|
||||
ext_notification_plugin_alert_message
|
||||
Do you want to be notified on an incoming message?
|
||||
|
||||
0 = Disabled (Default)
|
||||
1 = Alert when a text message comes
|
||||
|
||||
ext_notification_plugin_alert_bell
|
||||
Do you want to be notified on an incoming bell?
|
||||
|
||||
0 = Disabled (Default)
|
||||
1 = Alert when the bell character is received
|
||||
|
||||
ext_notification_plugin_output
|
||||
What GPIO is your external circuit attached?
|
||||
|
||||
GPIO of the output. (Default = 13)
|
||||
|
||||
ext_notification_plugin_output_ms
|
||||
How long do you want us to trigger your external circuit?
|
||||
|
||||
Amount of time in ms for the alert. Default is 1000.
|
||||
|
||||
|
||||
# Usage Notes
|
||||
|
||||
For basic usage, start with:
|
||||
|
||||
ext_notification_plugin_enabled = 1
|
||||
ext_notification_plugin_alert_message = 1
|
||||
|
||||
Depending on how your external cirtcuit configured is configured, you may need to set the active state to true.
|
||||
|
||||
ext_notification_plugin_active = 1
|
||||
|
||||
## Alert Types
|
||||
|
||||
We support being alerted on two events:
|
||||
|
||||
1) Incoming Text Message
|
||||
|
||||
2) Incoming Text Message that contains the ascii bell character. At present, only the Python API can send an ascii bell character, but more support may be added in the future.
|
||||
|
||||
### Bell Character
|
||||
|
||||
The bell character is ASCII 0x07. Include 0x07 anywhere in the text message and with ext_notification_plugin_alert_bell enabled, we will issue an external notification.
|
||||
|
||||
# External Hardware
|
||||
|
||||
Be mindful of the max current sink and source of the esp32 GPIO. The easiest devices to interface with would be either an LED or Active Buzzer.
|
||||
|
||||
Ideas for external hardware:
|
||||
|
||||
* LED
|
||||
* Active Buzzer
|
||||
* Flame thrower
|
||||
* Strobe Light
|
||||
* Siren
|
||||
|
||||
# Known Problems
|
||||
|
||||
* This won't directly support an passive (normal) speaker as it does not generate any audio wave forms.
|
||||
* This currently only supports the esp32. Other targets may be possible, I just don't have to test with.
|
||||
* This plugin only monitors text messages. We won't trigger on any other packet types.
|
||||
|
||||
# Need more help?
|
||||
|
||||
Go to the Meshtastic Discourse Group if you have any questions or to share how you have used this.
|
||||
|
||||
https://meshtastic.discourse.group
|
||||
40
docs/software/plugins/SerialPlugin.md
Normal file
40
docs/software/plugins/SerialPlugin.md
Normal file
@@ -0,0 +1,40 @@
|
||||
# About
|
||||
|
||||
A simple interface to send messages over the mesh network by sending strings
|
||||
over a serial port.
|
||||
|
||||
Default is to use RX GPIO 16 and TX GPIO 17.
|
||||
|
||||
|
||||
# Basic Usage:
|
||||
|
||||
1) Enable the plugin by setting serialplugin_enabled to 1.
|
||||
2) Set the pins (serialplugin_rxd / serialplugin_rxd) for your preferred RX and TX GPIO pins.
|
||||
On tbeam, recommend to use:
|
||||
RXD 35
|
||||
TXD 15
|
||||
3) Set serialplugin_timeout to the amount of time to wait before we consider
|
||||
your packet as "done".
|
||||
4) (Optional) In SerialPlugin.h set the port to PortNum_TEXT_MESSAGE_APP if you want to
|
||||
send messages to/from the general text message channel.
|
||||
5) Connect to your device over the serial interface at 38400 8N1.
|
||||
6) Send a packet up to 240 bytes in length. This will get relayed over the mesh network.
|
||||
7) (Optional) Set serialplugin_echo to 1 and any message you send out will be echoed back
|
||||
to your device.
|
||||
|
||||
# TODO (in this order):
|
||||
|
||||
* Define a verbose RX mode to report on mesh and packet infomration.
|
||||
- This won't happen any time soon.
|
||||
|
||||
# Known Problems
|
||||
|
||||
* Until the plugin is initilized by the startup sequence, the TX pin is in a floating
|
||||
state. Device connected to that pin may see this as "noise".
|
||||
* Will not work on NRF and the Linux device targets.
|
||||
|
||||
# Need help?
|
||||
|
||||
Need help with this plugin? Post your question on the Meshtastic Discourse:
|
||||
|
||||
https://meshtastic.discourse.group
|
||||
21
docs/software/plugins/StoreForwardPlugin.md
Normal file
21
docs/software/plugins/StoreForwardPlugin.md
Normal file
@@ -0,0 +1,21 @@
|
||||
# About
|
||||
|
||||
This is a work in progress and is not yet available.
|
||||
|
||||
The Store Request Plugin is an implementation of a Store and Forward system to enable resilient messaging in the event that a client device is disconnected from the main network.
|
||||
|
||||
Because of the increased network traffic for this overhead, it's not adviced to use this if you are duty cycle limited for your airtime usage nor is it adviced to use this for SF12.
|
||||
|
||||
# Running notes
|
||||
|
||||
This will only work on nodes that are designated as a Router.
|
||||
|
||||
Initial Requirements:
|
||||
|
||||
* Must be installed on a router node.
|
||||
* * This is an artificial limitation, but is in place to enforce best practices.
|
||||
* * Router nodes are intended to be always online. If this plugin misses any messages, the reliability of the stored messages will be reduced
|
||||
* Esp32 Processor based device with external PSRAM. (tbeam v1.0 and tbeamv1.1, maybe others)
|
||||
|
||||
Initial Features
|
||||
*
|
||||
@@ -1,11 +0,0 @@
|
||||
# using height of 50 to have 14 pixels beneath icon for text
|
||||
inkscape -z -e icon.png -w 50 -h 50 icon-24px.svg
|
||||
convert icon.png -background white -alpha Background ../src/icon.xbm
|
||||
|
||||
inkscape -z -e compass.png -w 48 -h 48 location_searching-24px.svg
|
||||
convert compass.png -background white -alpha Background ../src/compass.xbm
|
||||
|
||||
inkscape -z -e face.png -w 13 -h 13 face-24px.svg
|
||||
|
||||
inkscape -z -e pin.png -w 13 -h 13 room-24px.svg
|
||||
convert pin.png -background white -alpha Background ../src/pin.xbm
|
||||
@@ -8,10 +8,10 @@
|
||||
MemSize = 0x200000
|
||||
|
||||
; Define the desired ReadMode. Valid options are FASTREAD, READ2O, READ2IO, READ4O and READ4IO
|
||||
ReadMode = READ2IO
|
||||
ReadMode = READ4IO
|
||||
|
||||
; Define the desired WriteMode. Valid options are PP, PP2O, PP4O and PP4IO
|
||||
WriteMode = PP
|
||||
WriteMode = PP4IO
|
||||
|
||||
; Define the desired AddressMode. Valid options are BIT24 and BIT32
|
||||
AddressMode = BIT24
|
||||
@@ -38,12 +38,10 @@ DIO0Pin = 12
|
||||
DIO0Port = 1
|
||||
DIO1Pin = 13
|
||||
DIO1Port = 1
|
||||
|
||||
;These two pins are not connected, but we must name something
|
||||
DIO2Pin = 3
|
||||
DIO2Port = 1
|
||||
DIO2Pin = 7
|
||||
DIO2Port = 0
|
||||
DIO3Pin = 5
|
||||
DIO3Port = 1
|
||||
DIO3Port = 0
|
||||
|
||||
; Define the Index of the Write In Progress (WIP) bit in the status register. Valid options are in the range of 0 to 7.
|
||||
WIPIndex = 0
|
||||
@@ -57,13 +55,8 @@ PPSize = PAGE256
|
||||
; Numbers can be given in decimal, hex (starting with either 0x or 0X) and binary (starting with either 0b or 0B) formats.
|
||||
; The custom instructions will be executed in the order found.
|
||||
|
||||
; This example includes two commands, first a WREN (WRite ENable) and then a WRSR (WRite Satus Register) enabling the Quad Operation and the High Performance
|
||||
; mode for the MX25R6435F memory present in the nRF52840 DK.
|
||||
;InitializationCustomInstruction = 0x06
|
||||
;InitializationCustomInstruction = 0x01, [0x40, 0, 0x2]
|
||||
|
||||
; For MX25R1635F on TTGO board, only two data lines are connected
|
||||
; This example includes two commands, first a WREN (WRite ENable) and then a WRSR (WRite Satus Register) disabling Quad Operation and the High Performance
|
||||
; This example includes two commands, first a WREN (WRite ENable) and then a WRSR (WRite Satus Register) enabling Quad Operation and the High Performance
|
||||
; mode. For normal operation you might want low power mode instead.
|
||||
InitializationCustomInstruction = 0x06
|
||||
InitializationCustomInstruction = 0x01, [0x00, 0, 0x2]
|
||||
InitializationCustomInstruction = 0x01, [0x40, 0, 0x2]
|
||||
|
||||
69
nrf52/ttgo_eink_qpsi0.1.ini
Normal file
69
nrf52/ttgo_eink_qpsi0.1.ini
Normal file
@@ -0,0 +1,69 @@
|
||||
; nrfjprog.exe configuration file.
|
||||
|
||||
; Note: QSPI flash is mapped into memory at address 0x12000000
|
||||
|
||||
[DEFAULT_CONFIGURATION]
|
||||
; Define the capacity of the flash memory device in bytes. Set to 0 if no external memory device is present in your board.
|
||||
; MX25R1635F is 16Mbit/2Mbyte
|
||||
MemSize = 0x200000
|
||||
|
||||
; Define the desired ReadMode. Valid options are FASTREAD, READ2O, READ2IO, READ4O and READ4IO
|
||||
ReadMode = READ2IO
|
||||
|
||||
; Define the desired WriteMode. Valid options are PP, PP2O, PP4O and PP4IO
|
||||
WriteMode = PP
|
||||
|
||||
; Define the desired AddressMode. Valid options are BIT24 and BIT32
|
||||
AddressMode = BIT24
|
||||
|
||||
; Define the desired Frequency. Valid options are M2, M4, M8, M16 and M32
|
||||
Frequency = M16
|
||||
|
||||
; Define the desired SPI mode. Valid options are MODE0 and MODE3
|
||||
SpiMode = MODE0
|
||||
|
||||
; Define the desired SckDelay. Valid options are in the range 0 to 255
|
||||
SckDelay = 0x80
|
||||
|
||||
; Define the desired IO level for DIO2 and DIO3 during a custom instruction. Valid options are LEVEL_HIGH and LEVEL_LOW
|
||||
CustomInstructionIO2Level = LEVEL_LOW
|
||||
CustomInstructionIO3Level = LEVEL_HIGH
|
||||
|
||||
; Define the assigned pins for the QSPI peripheral. Valid options are those existing in your device
|
||||
CSNPin = 15
|
||||
CSNPort = 1
|
||||
SCKPin = 14
|
||||
SCKPort = 1
|
||||
DIO0Pin = 12
|
||||
DIO0Port = 1
|
||||
DIO1Pin = 13
|
||||
DIO1Port = 1
|
||||
|
||||
;These two pins are not connected, but we must name something
|
||||
DIO2Pin = 3
|
||||
DIO2Port = 1
|
||||
DIO3Pin = 5
|
||||
DIO3Port = 1
|
||||
|
||||
; Define the Index of the Write In Progress (WIP) bit in the status register. Valid options are in the range of 0 to 7.
|
||||
WIPIndex = 0
|
||||
|
||||
; Define page size for commands. Valid sizes are PAGE256 and PAGE512.
|
||||
PPSize = PAGE256
|
||||
|
||||
; Custom instructions to send to the external memory after initialization. Format is instruction code plus data to send in between optional brakets.
|
||||
; These instructions will be executed each time the qspi peripheral is initiated by nrfjprog.
|
||||
; To improve execution speed on consecutive interations with QSPI, you can run nrfjprog once with custom initialization, and then comment out the lines below.
|
||||
; Numbers can be given in decimal, hex (starting with either 0x or 0X) and binary (starting with either 0b or 0B) formats.
|
||||
; The custom instructions will be executed in the order found.
|
||||
|
||||
; This example includes two commands, first a WREN (WRite ENable) and then a WRSR (WRite Satus Register) enabling the Quad Operation and the High Performance
|
||||
; mode for the MX25R6435F memory present in the nRF52840 DK.
|
||||
;InitializationCustomInstruction = 0x06
|
||||
;InitializationCustomInstruction = 0x01, [0x40, 0, 0x2]
|
||||
|
||||
; For MX25R1635F on TTGO board, only two data lines are connected
|
||||
; This example includes two commands, first a WREN (WRite ENable) and then a WRSR (WRite Satus Register) disabling Quad Operation and the High Performance
|
||||
; mode. For normal operation you might want low power mode instead.
|
||||
InitializationCustomInstruction = 0x06
|
||||
InitializationCustomInstruction = 0x01, [0x00, 0, 0x2]
|
||||
@@ -9,8 +9,13 @@
|
||||
; https://docs.platformio.org/page/projectconf.html
|
||||
|
||||
[platformio]
|
||||
default_envs = tbeam # lora-relay-v1 # nrf52840dk-geeksville # linux # or if you'd like to change the default to something like lora-relay-v1 put that here
|
||||
;default_envs = heltec # lora-relay-v1 # nrf52840dk-geeksville # linux # or if you'd like to change the default to something like lora-relay-v1 put that here
|
||||
default_envs = tbeam
|
||||
;default_envs = tbeam0.7
|
||||
;default_envs = heltec
|
||||
;default_envs = tlora-v1
|
||||
;default_envs = tlora-v2
|
||||
;default_envs = lora-relay-v1 # nrf board
|
||||
;default_envs = linux # lora-relay-v1 # nrf52840dk-geeksville # linux # or if you'd like to change the default to something like lora-relay-v1 put that here
|
||||
|
||||
[common]
|
||||
; common is not currently used
|
||||
@@ -65,12 +70,12 @@ lib_deps =
|
||||
1202 ; CRC32, explicitly needed because dependency is missing in the ble ota update lib
|
||||
https://github.com/meshtastic/arduino-fsm.git#2f106146071fc7bc620e1e8d4b88dc4e0266ce39
|
||||
https://github.com/meshtastic/SparkFun_Ublox_Arduino_Library.git#31015a55e630a2df77d9d714669c621a5bf355ad
|
||||
https://github.com/meshtastic/RadioLib.git#8657380241bce681c33aab46598bbf13b11f876c
|
||||
https://github.com/meshtastic/RadioLib.git#07de964e929238949035fb0d5887026a3058df1a
|
||||
https://github.com/meshtastic/TinyGPSPlus.git#9c1d584d2469523381e077b0b9c1bf868d6c0206
|
||||
https://github.com/meshtastic/AXP202X_Library.git#8404abb6d4b486748636bc6ad72d2a47baaf5460
|
||||
Wire ; explicitly needed here because the AXP202 library forgets to add it
|
||||
SPI
|
||||
https://github.com/geeksville/ArduinoThread.git#333ffd09b596977c217ba25da4258f588b462ac6
|
||||
https://github.com/geeksville/ArduinoThread.git#72921ac222eed6f526ba1682023cee290d9aa1b3
|
||||
|
||||
; Common settings for conventional (non Portduino) Ardino targets
|
||||
[arduino_base]
|
||||
@@ -180,10 +185,10 @@ build_type = debug ; I'm debugging with ICE a lot now
|
||||
build_flags =
|
||||
${arduino_base.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
|
||||
-Isdk-nrfxlib/crypto/nrf_oberon/include -Lsdk-nrfxlib/crypto/nrf_oberon/lib/cortex-m4/hard-float/ -lliboberon_3.0.7
|
||||
;-DCFG_DEBUG=3
|
||||
src_filter =
|
||||
${arduino_base.src_filter} -<esp32/> -<nimble/> -<meshwifi/>
|
||||
${arduino_base.src_filter} -<esp32/> -<nimble/> -<mesh/wifi/> -<mesh/http/>
|
||||
lib_ignore =
|
||||
BluetoothOTA
|
||||
monitor_port = /dev/ttyACM1
|
||||
@@ -247,7 +252,7 @@ src_filter = ${nrf52_base.src_filter} +<../variants/ppr1>
|
||||
lib_deps =
|
||||
${arduino_base.lib_deps}
|
||||
|
||||
; Prototype eink/nrf52840/sx1262 device
|
||||
; First prototype eink/nrf52840/sx1262 device
|
||||
[env:eink]
|
||||
extends = nrf52_base
|
||||
board = eink
|
||||
@@ -261,6 +266,20 @@ lib_deps =
|
||||
https://github.com/geeksville/EPD_Libraries.git
|
||||
TFT_eSPI
|
||||
|
||||
; First prototype eink/nrf52840/sx1262 device
|
||||
[env:eink0.1]
|
||||
extends = nrf52_base
|
||||
board = eink0.1
|
||||
# add our variants files to the include and src paths
|
||||
# define build flags for the TFT_eSPI library
|
||||
build_flags = ${nrf52_base.build_flags} -Ivariants/eink0.1
|
||||
-DBUSY_PIN=3 -DRST_PIN=2 -DDC_PIN=28 -DCS_PIN=30
|
||||
src_filter = ${nrf52_base.src_filter} +<../variants/eink0.1>
|
||||
lib_deps =
|
||||
${arduino_base.lib_deps}
|
||||
https://github.com/geeksville/EPD_Libraries.git
|
||||
TFT_eSPI
|
||||
|
||||
; The https://github.com/BigCorvus/SX1262-LoRa-BLE-Relay board by @BigCorvus
|
||||
[env:lora-relay-v1]
|
||||
extends = nrf52_base
|
||||
@@ -310,7 +329,17 @@ lib_deps =
|
||||
; The Portduino based sim environment on top of linux
|
||||
[env:linux]
|
||||
platform = https://github.com/geeksville/platform-portduino.git
|
||||
src_filter = ${env.src_filter} -<esp32/> -<nimble/> -<nrf52/> -<meshwifi/>
|
||||
src_filter = ${env.src_filter} -<esp32/> -<nimble/> -<nrf52/> -<mesh/http/>
|
||||
build_flags = ${arduino_base.build_flags} -O0
|
||||
framework = arduino
|
||||
board = linux_x86_64
|
||||
lib_deps =
|
||||
${arduino_base.lib_deps}
|
||||
rweather/Crypto
|
||||
|
||||
; The GenieBlocks LORA prototype board
|
||||
[env:genieblocks_lora]
|
||||
extends = esp32_base
|
||||
board = genieblocks_lora
|
||||
build_flags =
|
||||
${esp32_base.build_flags} -D GENIEBLOCKS
|
||||
2
proto
2
proto
Submodule proto updated: dfe7bc1217...0cadaed395
Submodule sdk-nrfxlib updated: 17e8453553...e6e02cb83d
@@ -1,4 +1,5 @@
|
||||
#include "power.h"
|
||||
#include "NodeDB.h"
|
||||
#include "PowerFSM.h"
|
||||
#include "main.h"
|
||||
#include "sleep.h"
|
||||
@@ -83,7 +84,7 @@ class AnalogBatteryLevel : public HasBatteryLevel
|
||||
|
||||
/// If we see a battery voltage higher than physics allows - assume charger is pumping
|
||||
/// in power
|
||||
virtual bool isVBUSPlug() { return getBattVoltage() > chargingVolt; }
|
||||
virtual bool isVBUSPlug() { return getBattVoltage() > 1000 * chargingVolt; }
|
||||
|
||||
/// Assume charging if we have a battery and external power is connected.
|
||||
/// we can't be smart enough to say 'full'?
|
||||
@@ -268,9 +269,42 @@ bool Power::axp192Init()
|
||||
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
|
||||
axp.setChargeControlCur(AXP1XX_CHARGE_CUR_450MA); // There's no HW limit on the tbeam. Setting to 450mz to be a good neighbor on the usb bus.
|
||||
|
||||
if (radioConfig.preferences.charge_current == ChargeCurrent_MAUnset) {
|
||||
axp.setChargeControlCur(AXP1XX_CHARGE_CUR_450MA);
|
||||
} else if (radioConfig.preferences.charge_current == ChargeCurrent_MA100) {
|
||||
axp.setChargeControlCur(AXP1XX_CHARGE_CUR_100MA);
|
||||
} else if (radioConfig.preferences.charge_current == ChargeCurrent_MA190) {
|
||||
axp.setChargeControlCur(AXP1XX_CHARGE_CUR_190MA);
|
||||
} else if (radioConfig.preferences.charge_current == ChargeCurrent_MA280) {
|
||||
axp.setChargeControlCur(AXP1XX_CHARGE_CUR_280MA);
|
||||
} else if (radioConfig.preferences.charge_current == ChargeCurrent_MA360) {
|
||||
axp.setChargeControlCur(AXP1XX_CHARGE_CUR_360MA);
|
||||
} else if (radioConfig.preferences.charge_current == ChargeCurrent_MA450) {
|
||||
axp.setChargeControlCur(AXP1XX_CHARGE_CUR_450MA);
|
||||
} else if (radioConfig.preferences.charge_current == ChargeCurrent_MA550) {
|
||||
axp.setChargeControlCur(AXP1XX_CHARGE_CUR_550MA);
|
||||
} else if (radioConfig.preferences.charge_current == ChargeCurrent_MA630) {
|
||||
axp.setChargeControlCur(AXP1XX_CHARGE_CUR_630MA);
|
||||
} else if (radioConfig.preferences.charge_current == ChargeCurrent_MA700) {
|
||||
axp.setChargeControlCur(AXP1XX_CHARGE_CUR_700MA);
|
||||
} else if (radioConfig.preferences.charge_current == ChargeCurrent_MA780) {
|
||||
axp.setChargeControlCur(AXP1XX_CHARGE_CUR_780MA);
|
||||
} else if (radioConfig.preferences.charge_current == ChargeCurrent_MA880) {
|
||||
axp.setChargeControlCur(AXP1XX_CHARGE_CUR_880MA);
|
||||
} else if (radioConfig.preferences.charge_current == ChargeCurrent_MA960) {
|
||||
axp.setChargeControlCur(AXP1XX_CHARGE_CUR_960MA);
|
||||
} else if (radioConfig.preferences.charge_current == ChargeCurrent_MA1000) {
|
||||
axp.setChargeControlCur(AXP1XX_CHARGE_CUR_1000MA);
|
||||
} else if (radioConfig.preferences.charge_current == ChargeCurrent_MA1080) {
|
||||
axp.setChargeControlCur(AXP1XX_CHARGE_CUR_1080MA);
|
||||
} else if (radioConfig.preferences.charge_current == ChargeCurrent_MA1160) {
|
||||
axp.setChargeControlCur(AXP1XX_CHARGE_CUR_1160MA);
|
||||
} else if (radioConfig.preferences.charge_current == ChargeCurrent_MA1240) {
|
||||
axp.setChargeControlCur(AXP1XX_CHARGE_CUR_1240MA);
|
||||
} else if (radioConfig.preferences.charge_current == ChargeCurrent_MA1320) {
|
||||
axp.setChargeControlCur(AXP1XX_CHARGE_CUR_1320MA);
|
||||
}
|
||||
|
||||
#if 0
|
||||
|
||||
// Not connected
|
||||
|
||||
@@ -11,7 +11,7 @@ namespace meshtastic
|
||||
*/
|
||||
enum OptionalBool { OptFalse = 0, OptTrue = 1, OptUnknown = 2 };
|
||||
|
||||
/// Describes the state of the GPS system.
|
||||
/// Describes the state of the Power system.
|
||||
class PowerStatus : public Status
|
||||
{
|
||||
|
||||
|
||||
@@ -3,33 +3,35 @@
|
||||
|
||||
#define periodsToLog 48
|
||||
|
||||
// A reminder that there are 3600 seconds in an hour so I don't have
|
||||
// to keep googling it.
|
||||
// This can be changed to a smaller number to speed up testing.
|
||||
//
|
||||
AirTime *airTime;
|
||||
|
||||
uint32_t secondsPerPeriod = 3600;
|
||||
uint32_t lastMillis = 0;
|
||||
uint32_t secSinceBoot = 0;
|
||||
|
||||
// AirTime at;
|
||||
|
||||
// Don't read out of this directly. Use the helper functions.
|
||||
struct airtimeStruct {
|
||||
uint16_t periodTX[periodsToLog];
|
||||
uint16_t periodRX[periodsToLog];
|
||||
uint16_t periodRX_ALL[periodsToLog];
|
||||
uint32_t periodTX[periodsToLog]; // AirTime transmitted
|
||||
uint32_t periodRX[periodsToLog]; // AirTime received and repeated (Only valid mesh packets)
|
||||
uint32_t periodRX_ALL[periodsToLog]; // AirTime received regardless of valid mesh packet. Could include noise.
|
||||
uint8_t lastPeriodIndex;
|
||||
} airtimes;
|
||||
|
||||
void logAirtime(reportTypes reportType, uint32_t airtime_ms)
|
||||
void AirTime::logAirtime(reportTypes reportType, uint32_t airtime_ms)
|
||||
{
|
||||
|
||||
if (reportType == TX_LOG) {
|
||||
airtimes.periodTX[0] = airtimes.periodTX[0] + round(airtime_ms / 1000);
|
||||
DEBUG_MSG("AirTime - Packet transmitted : %ums\n", airtime_ms);
|
||||
airtimes.periodTX[0] = airtimes.periodTX[0] + airtime_ms;
|
||||
} else if (reportType == RX_LOG) {
|
||||
airtimes.periodRX[0] = airtimes.periodRX[0] + round(airtime_ms / 1000);
|
||||
DEBUG_MSG("AirTime - Packet received : %ums\n", airtime_ms);
|
||||
airtimes.periodRX[0] = airtimes.periodRX[0] + airtime_ms;
|
||||
} else if (reportType == RX_ALL_LOG) {
|
||||
airtimes.periodRX_ALL[0] = airtimes.periodRX_ALL[0] + round(airtime_ms / 1000);
|
||||
DEBUG_MSG("AirTime - Packet received (noise?) : %ums\n", airtime_ms);
|
||||
airtimes.periodRX_ALL[0] = airtimes.periodRX_ALL[0] + airtime_ms;
|
||||
} else {
|
||||
// Unknown report type
|
||||
DEBUG_MSG("AirTime - Unknown report time. This should never happen!!\n");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -38,29 +40,27 @@ uint8_t currentPeriodIndex()
|
||||
return ((getSecondsSinceBoot() / secondsPerPeriod) % periodsToLog);
|
||||
}
|
||||
|
||||
void airtimeCalculator()
|
||||
void airtimeRotatePeriod()
|
||||
{
|
||||
if (millis() - lastMillis > 1000) {
|
||||
lastMillis = millis();
|
||||
secSinceBoot++;
|
||||
if (airtimes.lastPeriodIndex != currentPeriodIndex()) {
|
||||
for (int i = periodsToLog - 2; i >= 0; --i) {
|
||||
airtimes.periodTX[i + 1] = airtimes.periodTX[i];
|
||||
airtimes.periodRX[i + 1] = airtimes.periodRX[i];
|
||||
airtimes.periodRX_ALL[i + 1] = airtimes.periodRX_ALL[i];
|
||||
}
|
||||
airtimes.periodTX[0] = 0;
|
||||
airtimes.periodRX[0] = 0;
|
||||
airtimes.periodRX_ALL[0] = 0;
|
||||
|
||||
airtimes.lastPeriodIndex = currentPeriodIndex();
|
||||
if (airtimes.lastPeriodIndex != currentPeriodIndex()) {
|
||||
DEBUG_MSG("Rotating airtimes to a new period = %u\n", currentPeriodIndex());
|
||||
|
||||
for (int i = periodsToLog - 2; i >= 0; --i) {
|
||||
airtimes.periodTX[i + 1] = airtimes.periodTX[i];
|
||||
airtimes.periodRX[i + 1] = airtimes.periodRX[i];
|
||||
airtimes.periodRX_ALL[i + 1] = airtimes.periodRX_ALL[i];
|
||||
}
|
||||
airtimes.periodTX[0] = 0;
|
||||
airtimes.periodRX[0] = 0;
|
||||
airtimes.periodRX_ALL[0] = 0;
|
||||
|
||||
airtimes.lastPeriodIndex = currentPeriodIndex();
|
||||
}
|
||||
}
|
||||
|
||||
uint16_t *airtimeReport(reportTypes reportType)
|
||||
uint32_t *airtimeReport(reportTypes reportType)
|
||||
{
|
||||
// currentHourIndexReset();
|
||||
|
||||
if (reportType == TX_LOG) {
|
||||
return airtimes.periodTX;
|
||||
@@ -86,3 +86,22 @@ uint32_t getSecondsSinceBoot()
|
||||
{
|
||||
return secSinceBoot;
|
||||
}
|
||||
|
||||
AirTime::AirTime() : concurrency::OSThread("AirTime") {}
|
||||
|
||||
int32_t AirTime::runOnce()
|
||||
{
|
||||
//DEBUG_MSG("AirTime::runOnce()\n");
|
||||
|
||||
airtimeRotatePeriod();
|
||||
secSinceBoot++;
|
||||
|
||||
/*
|
||||
This actually doesn't need to be run once per second but we currently use it for the
|
||||
secSinceBoot counter.
|
||||
|
||||
If we have a better counter of how long the device has been online (and not millis())
|
||||
then we can change this to something less frequent. Maybe once ever 5 seconds?
|
||||
*/
|
||||
return (1000 * 1);
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include "concurrency/OSThread.h"
|
||||
#include "configuration.h"
|
||||
#include <Arduino.h>
|
||||
#include <functional>
|
||||
@@ -18,7 +19,7 @@
|
||||
TX_LOG + RX_LOG = Total air time for a perticular meshtastic channel.
|
||||
|
||||
TX_LOG + RX_LOG = Total air time for a perticular meshtastic channel, including
|
||||
other lora radios.
|
||||
other lora radios.
|
||||
|
||||
RX_ALL_LOG - RX_LOG = Other lora radios on our frequency channel.
|
||||
*/
|
||||
@@ -26,13 +27,28 @@ enum reportTypes { TX_LOG, RX_LOG, RX_ALL_LOG };
|
||||
|
||||
void logAirtime(reportTypes reportType, uint32_t airtime_ms);
|
||||
|
||||
void airtimeCalculator();
|
||||
void airtimeRotatePeriod();
|
||||
|
||||
uint8_t currentPeriodIndex();
|
||||
uint8_t getPeriodsToLog();
|
||||
|
||||
uint32_t getSecondsSinceBoot();
|
||||
|
||||
uint16_t *airtimeReport(reportTypes reportType);
|
||||
uint32_t *airtimeReport(reportTypes reportType);
|
||||
|
||||
uint32_t getSecondsPerPeriod();
|
||||
uint32_t getSecondsPerPeriod();
|
||||
|
||||
class AirTime : private concurrency::OSThread
|
||||
{
|
||||
|
||||
public:
|
||||
AirTime();
|
||||
|
||||
void logAirtime(reportTypes reportType, uint32_t airtime_ms);
|
||||
|
||||
protected:
|
||||
|
||||
virtual int32_t runOnce();
|
||||
};
|
||||
|
||||
extern AirTime *airTime;
|
||||
@@ -9,6 +9,7 @@ enum class Cmd {
|
||||
SET_OFF,
|
||||
ON_PRESS,
|
||||
START_BLUETOOTH_PIN_SCREEN,
|
||||
START_FIRMWARE_UPDATE_SCREEN,
|
||||
STOP_BLUETOOTH_PIN_SCREEN,
|
||||
STOP_BOOT_SCREEN,
|
||||
PRINT,
|
||||
|
||||
@@ -28,10 +28,14 @@ void OSThread::setup()
|
||||
OSThread::OSThread(const char *_name, uint32_t period, ThreadController *_controller)
|
||||
: Thread(NULL, period), controller(_controller)
|
||||
{
|
||||
assertIsSetup();
|
||||
|
||||
ThreadName = _name;
|
||||
|
||||
if (controller)
|
||||
controller->add(this);
|
||||
if (controller) {
|
||||
bool added = controller->add(this);
|
||||
assert(added);
|
||||
}
|
||||
}
|
||||
|
||||
OSThread::~OSThread()
|
||||
@@ -81,4 +85,36 @@ void OSThread::run()
|
||||
currentThread = NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* This flag is set **only** when setup() starts, to provide a way for us to check for sloppy static constructor calls.
|
||||
* Call assertIsSetup() to force a crash if someone tries to create an instance too early.
|
||||
*
|
||||
* it is super important to never allocate those object statically. instead, you should explicitly
|
||||
* new them at a point where you are guaranteed that other objects that this instance
|
||||
* depends on have already been created.
|
||||
*
|
||||
* in particular, for OSThread that means "all instances must be declared via new() in setup() or later" -
|
||||
* this makes it guaranteed that the global mainController is fully constructed first.
|
||||
*/
|
||||
bool hasBeenSetup;
|
||||
|
||||
void assertIsSetup()
|
||||
{
|
||||
|
||||
/**
|
||||
* Dear developer comrade - If this assert fails() that means you need to fix the following:
|
||||
*
|
||||
* This flag is set **only** when setup() starts, to provide a way for us to check for sloppy static constructor calls.
|
||||
* Call assertIsSetup() to force a crash if someone tries to create an instance too early.
|
||||
*
|
||||
* it is super important to never allocate those object statically. instead, you should explicitly
|
||||
* new them at a point where you are guaranteed that other objects that this instance
|
||||
* depends on have already been created.
|
||||
*
|
||||
* in particular, for OSThread that means "all instances must be declared via new() in setup() or later" -
|
||||
* this makes it guaranteed that the global mainController is fully constructed first.
|
||||
*/
|
||||
assert(hasBeenSetup);
|
||||
}
|
||||
|
||||
} // namespace concurrency
|
||||
|
||||
@@ -17,14 +17,14 @@ extern InterruptableDelay mainDelay;
|
||||
|
||||
/**
|
||||
* @brief Base threading
|
||||
*
|
||||
*
|
||||
* This is a pseudo threading layer that is super easy to port, well suited to our slow network and very ram & power efficient.
|
||||
*
|
||||
* TODO FIXME @geeksville
|
||||
*
|
||||
* move more things into OSThreads
|
||||
* remove lock/lockguard
|
||||
*
|
||||
*
|
||||
* move typedQueue into concurrency
|
||||
* remove freertos from typedqueue
|
||||
*/
|
||||
@@ -42,7 +42,6 @@ class OSThread : public Thread
|
||||
static bool showWaiting;
|
||||
|
||||
public:
|
||||
|
||||
/// For debug printing only (might be null)
|
||||
static const OSThread *currentThread;
|
||||
|
||||
@@ -71,4 +70,19 @@ class OSThread : public Thread
|
||||
virtual void run();
|
||||
};
|
||||
|
||||
/**
|
||||
* This flag is set **only** when setup() starts, to provide a way for us to check for sloppy static constructor calls.
|
||||
* Call assertIsSetup() to force a crash if someone tries to create an instance too early.
|
||||
*
|
||||
* it is super important to never allocate those object statically. instead, you should explicitly
|
||||
* new them at a point where you are guaranteed that other objects that this instance
|
||||
* depends on have already been created.
|
||||
*
|
||||
* in particular, for OSThread that means "all instances must be declared via new() in setup() or later" -
|
||||
* this makes it guaranteed that the global mainController is fully constructed first.
|
||||
*/
|
||||
extern bool hasBeenSetup;
|
||||
|
||||
void assertIsSetup();
|
||||
|
||||
} // namespace concurrency
|
||||
|
||||
@@ -309,6 +309,33 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#define LORA_DIO1 35 // Not really used
|
||||
#define LORA_DIO2 34 // Not really used
|
||||
|
||||
#elif defined(TLORA_V1_3)
|
||||
// This string must exactly match the case used in release file names or the android updater won't work
|
||||
#define HW_VENDOR "tlora-v1-3"
|
||||
|
||||
#undef GPS_RX_PIN
|
||||
#undef GPS_TX_PIN
|
||||
#define GPS_RX_PIN 36
|
||||
#define GPS_TX_PIN 13 // per @eugene
|
||||
|
||||
#define BATTERY_PIN 35 // A battery voltage measurement pin, voltage divider connected here to measure battery voltage
|
||||
|
||||
#define I2C_SDA 4 // I2C pins for this board
|
||||
#define I2C_SCL 15
|
||||
|
||||
#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 36
|
||||
#define BUTTON_NEED_PULLUP
|
||||
|
||||
#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"
|
||||
@@ -338,6 +365,35 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#define LORA_DIO1 35 // Not really used
|
||||
#define LORA_DIO2 34 // Not really used
|
||||
|
||||
#elif defined(GENIEBLOCKS)
|
||||
// This string must exactly match the case used in release file names or the android updater won't work
|
||||
#define HW_VENDOR "genieblocks"
|
||||
#undef GPS_RX_PIN
|
||||
#undef GPS_TX_PIN
|
||||
#define GPS_RX_PIN 5
|
||||
#define GPS_TX_PIN 18
|
||||
#define GPS_RESET_N 10
|
||||
#define GPS_EXTINT 23 // On MAX-M8 module pin name is EXTINT. On L70 module pin name is STANDBY.
|
||||
|
||||
#define BATTERY_PIN 39 // A battery voltage measurement pin, voltage divider connected here to measure battery voltage
|
||||
#define BATTERY_EN_PIN 14 // Voltage voltage divider enable pin connected to mosfet
|
||||
|
||||
#define I2C_SDA 4 // I2C pins for this board
|
||||
#define I2C_SCL 2
|
||||
|
||||
#define LED_PIN 12 // If defined we will blink this LED
|
||||
//#define BUTTON_PIN 36 // If defined, this will be used for user button presses (ToDo problem on that line on debug screen --> Long press start!)
|
||||
//#define BUTTON_NEED_PULLUP //GPIOs 34 to 39 are GPIs – input only pins. These pins don’t have internal pull-ups or pull-down resistors.
|
||||
|
||||
#define USE_RF95
|
||||
#define LORA_DIO0 38 // a No connect on the SX1262 module
|
||||
#define LORA_RESET 9
|
||||
|
||||
#define RF95_SCK 22
|
||||
#define RF95_MISO 19
|
||||
#define RF95_MOSI 13
|
||||
#define RF95_NSS 21
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef ARDUINO_NRF52840_PCA10056
|
||||
@@ -365,7 +421,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
#define USE_RF95
|
||||
#define LORA_DIO0 26 // a No connect on the SX1262 module
|
||||
#define LORA_RESET 23
|
||||
#define LORA_RESET RADIOLIB_NC
|
||||
#define LORA_DIO1 33 // Not really used
|
||||
#define LORA_DIO2 32 // Not really used
|
||||
|
||||
@@ -373,7 +429,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#define RF95_SCK 5
|
||||
#define RF95_MISO 19
|
||||
#define RF95_MOSI 27
|
||||
#define RF95_NSS 18
|
||||
#define RF95_NSS RADIOLIB_NC // the ch341f spi controller does CS for us
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
@@ -7,6 +7,8 @@
|
||||
#include "configuration.h"
|
||||
#include "nimble/BluetoothUtil.h"
|
||||
#include "NodeDB.h"
|
||||
#include "../graphics/Screen.h"
|
||||
#include "../main.h"
|
||||
|
||||
#include <CRC32.h>
|
||||
#include <Update.h>
|
||||
@@ -47,8 +49,9 @@ int update_size_callback(uint16_t conn_handle, uint16_t attr_handle, struct ble_
|
||||
// void stopMeshBluetoothService();
|
||||
// stopMeshBluetoothService();
|
||||
|
||||
screen->startFirmwareUpdateScreen();
|
||||
if (RadioLibInterface::instance)
|
||||
RadioLibInterface::instance->sleep(); // FIXME, nasty hack - the RF95 ISR/SPI code on ESP32 can fail while we are
|
||||
RadioLibInterface::instance->disable(); // FIXME, nasty hack - the RF95 ISR/SPI code on ESP32 can fail while we are
|
||||
// writing flash - shut the radio off during updates
|
||||
}
|
||||
}
|
||||
|
||||
@@ -49,7 +49,8 @@ void esp32Setup()
|
||||
|
||||
// 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
|
||||
// #define APP_WATCHDOG_SECS 45
|
||||
#define APP_WATCHDOG_SECS 90
|
||||
|
||||
res = esp_task_wdt_init(APP_WATCHDOG_SECS, true);
|
||||
assert(res == ESP_OK);
|
||||
|
||||
@@ -239,6 +239,7 @@ int32_t GPS::runOnce()
|
||||
}
|
||||
|
||||
// We've been awake too long - force sleep
|
||||
now = millis();
|
||||
auto wakeTime = getWakeTime();
|
||||
bool tooLong = wakeTime != UINT32_MAX && (now - lastWakeStartMsec) > wakeTime;
|
||||
|
||||
|
||||
@@ -47,6 +47,8 @@ The Unix epoch (or Unix time or POSIX time or Unix timestamp) is the number of s
|
||||
t.tm_mon = d.month() - 1;
|
||||
t.tm_year = d.year() - 1900;
|
||||
t.tm_isdst = false;
|
||||
DEBUG_MSG("NMEA GPS time %d\n", t.tm_sec);
|
||||
|
||||
perhapsSetRTC(RTCQualityGPS, t);
|
||||
|
||||
return true;
|
||||
@@ -87,11 +89,17 @@ bool NMEAGPS::lookForLocation()
|
||||
auto loc = reader.location.value();
|
||||
latitude = toDegInt(loc.lat);
|
||||
longitude = toDegInt(loc.lng);
|
||||
foundLocation = true;
|
||||
|
||||
// expect gps pos lat=37.520825, lon=-122.309162, alt=158
|
||||
DEBUG_MSG("new NMEA GPS pos lat=%f, lon=%f, alt=%d, hdop=%g, heading=%f\n", latitude * 1e-7, longitude * 1e-7, altitude,
|
||||
dop * 1e-2, heading * 1e-5);
|
||||
// Some GPSes (Air530) seem to send a zero longitude when the current fix is bogus
|
||||
if(longitude == 0)
|
||||
DEBUG_MSG("Ignoring bogus NMEA position\n");
|
||||
else {
|
||||
foundLocation = true;
|
||||
|
||||
// expect gps pos lat=37.520825, lon=-122.309162, alt=158
|
||||
DEBUG_MSG("new NMEA GPS pos lat=%f, lon=%f, alt=%d, hdop=%g, heading=%f\n", latitude * 1e-7, longitude * 1e-7, altitude,
|
||||
dop * 1e-2, heading * 1e-5);
|
||||
}
|
||||
}
|
||||
|
||||
return foundLocation;
|
||||
|
||||
@@ -36,6 +36,7 @@ bool perhapsSetRTC(RTCQuality q, const struct timeval *tv)
|
||||
|
||||
bool shouldSet;
|
||||
if (q > currentQuality) {
|
||||
currentQuality = q;
|
||||
shouldSet = true;
|
||||
DEBUG_MSG("Upgrading time to RTC %ld secs (quality %d)\n", tv->tv_sec, q);
|
||||
} else if(q == RTCQualityGPS && (now - lastSetMsec) > (12 * 60 * 60 * 1000L)) {
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
#include "EInkDisplay.h"
|
||||
#include "SPILock.h"
|
||||
#include "epd1in54.h" // Screen specific library
|
||||
#include "graphics/configs.h"
|
||||
#include <SPI.h>
|
||||
#include <TFT_eSPI.h> // Graphics library and Sprite class
|
||||
|
||||
@@ -123,7 +122,8 @@ bool EInkDisplay::connect()
|
||||
#endif
|
||||
|
||||
#ifdef PIN_EINK_EN
|
||||
digitalWrite(PIN_EINK_EN, HIGH);
|
||||
// backlight power, HIGH is backlight on, LOW is off
|
||||
digitalWrite(PIN_EINK_EN, LOW);
|
||||
pinMode(PIN_EINK_EN, OUTPUT);
|
||||
#endif
|
||||
|
||||
|
||||
@@ -27,14 +27,17 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#include "NodeDB.h"
|
||||
#include "Screen.h"
|
||||
#include "configuration.h"
|
||||
#include "fonts.h"
|
||||
#include "graphics/images.h"
|
||||
#include "main.h"
|
||||
#include "mesh-pb-constants.h"
|
||||
#include "meshwifi/meshwifi.h"
|
||||
#include "plugins/TextMessagePlugin.h"
|
||||
#include "target_specific.h"
|
||||
#include "utils.h"
|
||||
#include "fonts.h"
|
||||
|
||||
#ifndef NO_ESP32
|
||||
#include "mesh/http/WiFiAPClient.h"
|
||||
#endif
|
||||
|
||||
using namespace meshtastic; /** @todo remove */
|
||||
|
||||
@@ -42,7 +45,7 @@ namespace graphics
|
||||
{
|
||||
|
||||
// This means the *visible* area (sh1106 can address 132, but shows 128 for example)
|
||||
#define IDLE_FRAMERATE 1 // in fps
|
||||
#define IDLE_FRAMERATE 1 // in fps
|
||||
#define COMPASS_DIAM 44
|
||||
|
||||
// DEBUG
|
||||
@@ -161,6 +164,20 @@ static void drawFrameBluetooth(OLEDDisplay *display, OLEDDisplayUiState *state,
|
||||
display->drawString(64 + x, 48 + y, buf);
|
||||
}
|
||||
|
||||
static void drawFrameFirmware(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y)
|
||||
{
|
||||
display->setTextAlignment(TEXT_ALIGN_CENTER);
|
||||
display->setFont(FONT_MEDIUM);
|
||||
display->drawString(64 + x, y, "Updating");
|
||||
|
||||
display->setFont(FONT_SMALL);
|
||||
display->drawString(64 + x, FONT_HEIGHT_SMALL + y + 2, "Please wait...");
|
||||
|
||||
//display->setFont(FONT_LARGE);
|
||||
//display->drawString(64 + x, 26 + y, btPIN);
|
||||
}
|
||||
|
||||
|
||||
/// Draw the last text message we received
|
||||
static void drawCriticalFaultFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y)
|
||||
{
|
||||
@@ -177,7 +194,6 @@ static void drawCriticalFaultFrame(OLEDDisplay *display, OLEDDisplayUiState *sta
|
||||
display->drawString(0 + x, FONT_HEIGHT_MEDIUM + y, "For help, please post on\nmeshtastic.discourse.group");
|
||||
}
|
||||
|
||||
|
||||
/// Draw the last text message we received
|
||||
static void drawTextMessageFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y)
|
||||
{
|
||||
@@ -199,7 +215,7 @@ static void drawTextMessageFrame(OLEDDisplay *display, OLEDDisplayUiState *state
|
||||
|
||||
// the max length of this buffer is much longer than we can possibly print
|
||||
static char tempBuf[96];
|
||||
assert(mp.decoded.which_payload == SubPacket_data_tag);
|
||||
assert(mp.decoded.which_payloadVariant == SubPacket_data_tag);
|
||||
snprintf(tempBuf, sizeof(tempBuf), " %s", mp.decoded.data.payload.bytes);
|
||||
|
||||
display->drawStringMaxWidth(4 + x, 10 + y, SCREEN_WIDTH - (6 + x), tempBuf);
|
||||
@@ -316,7 +332,7 @@ static void drawGPS(OLEDDisplay *display, int16_t x, int16_t y, const GPSStatus
|
||||
display->drawFastImage(x + 24, y, 8, 8, imgSatellite);
|
||||
|
||||
// Draw the number of satellites
|
||||
sprintf(satsString, "%lu", gps->getNumSatellites());
|
||||
sprintf(satsString, "%u", gps->getNumSatellites());
|
||||
display->drawString(x + 34, y - 2, satsString);
|
||||
}
|
||||
}
|
||||
@@ -569,11 +585,11 @@ static void drawNodeInfo(OLEDDisplay *display, OLEDDisplayUiState *state, int16_
|
||||
uint32_t agoSecs = sinceLastSeen(node);
|
||||
static char lastStr[20];
|
||||
if (agoSecs < 120) // last 2 mins?
|
||||
snprintf(lastStr, sizeof(lastStr), "%lu seconds ago", agoSecs);
|
||||
snprintf(lastStr, sizeof(lastStr), "%u seconds ago", agoSecs);
|
||||
else if (agoSecs < 120 * 60) // last 2 hrs
|
||||
snprintf(lastStr, sizeof(lastStr), "%lu minutes ago", agoSecs / 60);
|
||||
snprintf(lastStr, sizeof(lastStr), "%u minutes ago", agoSecs / 60);
|
||||
else
|
||||
snprintf(lastStr, sizeof(lastStr), "%lu hours ago", agoSecs / 60 / 60);
|
||||
snprintf(lastStr, sizeof(lastStr), "%u hours ago", agoSecs / 60 / 60);
|
||||
|
||||
static char distStr[20];
|
||||
strcpy(distStr, "? km"); // might not have location data
|
||||
@@ -747,7 +763,7 @@ void Screen::setup()
|
||||
powerStatusObserver.observe(&powerStatus->onNewStatus);
|
||||
gpsStatusObserver.observe(&gpsStatus->onNewStatus);
|
||||
nodeStatusObserver.observe(&nodeStatus->onNewStatus);
|
||||
textMessageObserver.observe(&textMessagePlugin);
|
||||
textMessageObserver.observe(textMessagePlugin);
|
||||
}
|
||||
|
||||
void Screen::forceDisplay()
|
||||
@@ -796,6 +812,9 @@ int32_t Screen::runOnce()
|
||||
case Cmd::START_BLUETOOTH_PIN_SCREEN:
|
||||
handleStartBluetoothPinScreen(cmd.bluetooth_pin);
|
||||
break;
|
||||
case Cmd::START_FIRMWARE_UPDATE_SCREEN:
|
||||
handleStartFirmwareUpdateScreen();
|
||||
break;
|
||||
case Cmd::STOP_BLUETOOTH_PIN_SCREEN:
|
||||
case Cmd::STOP_BOOT_SCREEN:
|
||||
setFrames();
|
||||
@@ -896,10 +915,12 @@ void Screen::setFrames()
|
||||
// call a method on debugInfoScreen object (for more details)
|
||||
normalFrames[numframes++] = &Screen::drawDebugInfoSettingsTrampoline;
|
||||
|
||||
#ifndef NO_ESP32
|
||||
if (isWifiAvailable()) {
|
||||
// call a method on debugInfoScreen object (for more details)
|
||||
normalFrames[numframes++] = &Screen::drawDebugInfoWiFiTrampoline;
|
||||
}
|
||||
#endif
|
||||
|
||||
ui.setFrames(normalFrames, numframes);
|
||||
ui.enableAllIndicators();
|
||||
@@ -924,20 +945,33 @@ void Screen::handleStartBluetoothPinScreen(uint32_t pin)
|
||||
setFastFramerate();
|
||||
}
|
||||
|
||||
void Screen::blink() {
|
||||
void Screen::handleStartFirmwareUpdateScreen()
|
||||
{
|
||||
DEBUG_MSG("showing firmware screen\n");
|
||||
showingNormalScreen = false;
|
||||
|
||||
static FrameCallback btFrames[] = {drawFrameFirmware};
|
||||
|
||||
ui.disableAllIndicators();
|
||||
ui.setFrames(btFrames, 1);
|
||||
setFastFramerate();
|
||||
}
|
||||
|
||||
void Screen::blink()
|
||||
{
|
||||
setFastFramerate();
|
||||
uint8_t count = 10;
|
||||
dispdev.setBrightness(254);
|
||||
while(count>0) {
|
||||
while (count > 0) {
|
||||
dispdev.fillRect(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT);
|
||||
dispdev.display();
|
||||
delay(50);
|
||||
dispdev.clear();
|
||||
dispdev.display();
|
||||
delay(50);
|
||||
count = count -1;
|
||||
count = count - 1;
|
||||
}
|
||||
dispdev.setBrightness(brightness);
|
||||
dispdev.setBrightness(brightness);
|
||||
}
|
||||
|
||||
void Screen::handlePrint(const char *text)
|
||||
@@ -1060,11 +1094,13 @@ void DebugInfo::drawFrameWiFi(OLEDDisplay *display, OLEDDisplayUiState *state, i
|
||||
if (WiFi.status() == WL_CONNECTED || isSoftAPForced() || radioConfig.preferences.wifi_ap_mode) {
|
||||
if (radioConfig.preferences.wifi_ap_mode || isSoftAPForced()) {
|
||||
display->drawString(x, y + FONT_HEIGHT_SMALL * 1, "IP: " + String(WiFi.softAPIP().toString().c_str()));
|
||||
|
||||
// Number of connections to the AP. Default mmax for the esp32 is 4
|
||||
display->drawString(x + SCREEN_WIDTH - display->getStringWidth("(" + String(WiFi.softAPgetStationNum()) + "/4)"),
|
||||
y + FONT_HEIGHT_SMALL * 1, "(" + String(WiFi.softAPgetStationNum()) + "/4)");
|
||||
} else {
|
||||
display->drawString(x, y + FONT_HEIGHT_SMALL * 1, "IP: " + String(WiFi.localIP().toString().c_str()));
|
||||
}
|
||||
display->drawString(x + SCREEN_WIDTH - display->getStringWidth("(" + String(WiFi.softAPgetStationNum()) + "/4)"),
|
||||
y + FONT_HEIGHT_SMALL * 1, "(" + String(WiFi.softAPgetStationNum()) + "/4)");
|
||||
|
||||
} else if (WiFi.status() == WL_NO_SSID_AVAIL) {
|
||||
display->drawString(x, y + FONT_HEIGHT_SMALL * 1, "SSID Not Found");
|
||||
|
||||
@@ -128,6 +128,14 @@ class Screen : public concurrency::OSThread
|
||||
enqueueCmd(cmd);
|
||||
}
|
||||
|
||||
void startFirmwareUpdateScreen()
|
||||
{
|
||||
ScreenCmd cmd;
|
||||
cmd.cmd = Cmd::START_FIRMWARE_UPDATE_SCREEN;
|
||||
enqueueCmd(cmd);
|
||||
}
|
||||
|
||||
|
||||
/// Stops showing the bluetooth PIN screen.
|
||||
void stopBluetoothPinScreen() { enqueueCmd(ScreenCmd{.cmd = Cmd::STOP_BLUETOOTH_PIN_SCREEN}); }
|
||||
|
||||
@@ -233,6 +241,7 @@ class Screen : public concurrency::OSThread
|
||||
void handleOnPress();
|
||||
void handleStartBluetoothPinScreen(uint32_t pin);
|
||||
void handlePrint(const char *text);
|
||||
void handleStartFirmwareUpdateScreen();
|
||||
|
||||
/// Rebuilds our list of frames (screens) to default ones.
|
||||
void setFrames();
|
||||
|
||||
@@ -1,33 +1,20 @@
|
||||
#define icon_width 50
|
||||
#define icon_height 50
|
||||
#define icon_height 28
|
||||
static char icon_bits[] = {
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0xFC, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x80, 0xFF,
|
||||
0xFF, 0x07, 0x00, 0x00, 0x00, 0xE0, 0xFF, 0xFF, 0x1F, 0x00, 0x00, 0x00,
|
||||
0xF0, 0xFF, 0xFF, 0x3F, 0x00, 0x00, 0x00, 0xFC, 0xFF, 0xFF, 0xFF, 0x00,
|
||||
0x00, 0x00, 0xFE, 0x0F, 0xC0, 0xFF, 0x01, 0x00, 0x00, 0xFF, 0x03, 0x00,
|
||||
0xFF, 0x03, 0x00, 0x80, 0xFF, 0x00, 0x00, 0xFC, 0x07, 0x00, 0xC0, 0x3F,
|
||||
0xE0, 0x1F, 0xF0, 0x0F, 0x00, 0xC0, 0x1F, 0xFC, 0xFF, 0xE0, 0x0F, 0x00,
|
||||
0xE0, 0x0F, 0xFF, 0xFF, 0xC3, 0x1F, 0x00, 0xF0, 0x87, 0xFF, 0xFF, 0x87,
|
||||
0x3F, 0x00, 0xF0, 0xC3, 0xFF, 0xFF, 0x0F, 0x3F, 0x00, 0xF8, 0xE3, 0x7F,
|
||||
0xF8, 0x1F, 0x7F, 0x00, 0xF8, 0xF1, 0x0F, 0xC0, 0x3F, 0x7E, 0x00, 0xF8,
|
||||
0xF1, 0x07, 0x80, 0x3F, 0x7E, 0x00, 0xFC, 0xF8, 0x03, 0x00, 0x7F, 0xFC,
|
||||
0x00, 0xFC, 0xF8, 0x81, 0x07, 0x7E, 0xFC, 0x00, 0x7C, 0xF8, 0xE0, 0x1F,
|
||||
0x7C, 0xF8, 0x00, 0x7C, 0xFC, 0xF0, 0x3F, 0xFC, 0xF8, 0x00, 0x7C, 0xFC,
|
||||
0xF0, 0x3F, 0xFC, 0xF8, 0x00, 0x7C, 0x7C, 0xF8, 0x7F, 0xF8, 0xF8, 0x00,
|
||||
0x7C, 0x7C, 0xF8, 0x7F, 0xF8, 0xF8, 0x00, 0x00, 0x00, 0xF8, 0x7F, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0xF8, 0x7F, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0,
|
||||
0x3F, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0x3F, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0xE0, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0x0F, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0xC0, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0x0F,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0xC0, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0x0F, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0xC0, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0x1F, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0xF0, 0x3F, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF8,
|
||||
0x7F, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFC, 0xFF, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0xFE, 0xFF, 0x01, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFC, 0x03, 0x00,
|
||||
0x00, 0x00, 0x80, 0x7F, 0xF8, 0x07, 0x00, 0x00, 0x00, 0x00, 0x3F, 0xF0,
|
||||
0x03, 0x00, 0x00, 0x00, 0x00, 0x1E, 0xE0, 0x01, 0x00, 0x00, 0x00, 0x00,
|
||||
0x0C, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, };
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x03, 0x00, 0x03,
|
||||
0x00, 0x00, 0x00, 0x80, 0x07, 0xC0, 0x07, 0x00, 0x00, 0x00, 0xC0, 0x1F,
|
||||
0xC0, 0x0F, 0x00, 0x00, 0x00, 0xE0, 0x0F, 0xE0, 0x0F, 0x00, 0x00, 0x00,
|
||||
0xE0, 0x0F, 0xF0, 0x1F, 0x00, 0x00, 0x00, 0xF0, 0x07, 0xF0, 0x3F, 0x00,
|
||||
0x00, 0x00, 0xF8, 0x03, 0xF8, 0x7F, 0x00, 0x00, 0x00, 0xF8, 0x03, 0xFC,
|
||||
0x7F, 0x00, 0x00, 0x00, 0xFC, 0x01, 0xFC, 0xFE, 0x00, 0x00, 0x00, 0xFE,
|
||||
0x00, 0xFE, 0xFC, 0x01, 0x00, 0x00, 0xFE, 0x00, 0x7F, 0xFC, 0x01, 0x00,
|
||||
0x00, 0x7F, 0x00, 0x3F, 0xF8, 0x03, 0x00, 0x80, 0x3F, 0x80, 0x3F, 0xF0,
|
||||
0x07, 0x00, 0x80, 0x3F, 0xC0, 0x1F, 0xF0, 0x07, 0x00, 0xC0, 0x1F, 0xC0,
|
||||
0x0F, 0xE0, 0x0F, 0x00, 0xE0, 0x0F, 0xE0, 0x0F, 0xC0, 0x1F, 0x00, 0xE0,
|
||||
0x0F, 0xF0, 0x07, 0x80, 0x1F, 0x00, 0xF0, 0x07, 0xF8, 0x03, 0x80, 0x3F,
|
||||
0x00, 0xF8, 0x03, 0xF8, 0x03, 0x00, 0x7F, 0x00, 0xFC, 0x03, 0xFC, 0x01,
|
||||
0x00, 0x7E, 0x00, 0xFC, 0x01, 0xFE, 0x00, 0x00, 0xFE, 0x00, 0xFE, 0x00,
|
||||
0xFE, 0x00, 0x00, 0xFC, 0x01, 0x7E, 0x00, 0x7F, 0x00, 0x00, 0xF8, 0x01,
|
||||
0x7E, 0x00, 0x3E, 0x00, 0x00, 0xF8, 0x01, 0x38, 0x00, 0x3C, 0x00, 0x00,
|
||||
0x70, 0x00, 0x10, 0x00, 0x10, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, };
|
||||
|
||||
59
src/main.cpp
59
src/main.cpp
@@ -19,8 +19,7 @@
|
||||
#include "concurrency/Periodic.h"
|
||||
#include "graphics/Screen.h"
|
||||
#include "main.h"
|
||||
#include "meshwifi/meshhttp.h"
|
||||
#include "meshwifi/meshwifi.h"
|
||||
#include "plugins/Plugins.h"
|
||||
#include "sleep.h"
|
||||
#include "target_specific.h"
|
||||
#include <OneButton.h>
|
||||
@@ -28,6 +27,8 @@
|
||||
// #include <driver/rtc_io.h>
|
||||
|
||||
#ifndef NO_ESP32
|
||||
#include "mesh/http/WebServer.h"
|
||||
#include "mesh/http/WiFiAPClient.h"
|
||||
#include "nimble/BluetoothUtil.h"
|
||||
#endif
|
||||
|
||||
@@ -275,6 +276,8 @@ RadioInterface *rIf = NULL;
|
||||
|
||||
void setup()
|
||||
{
|
||||
concurrency::hasBeenSetup = true;
|
||||
|
||||
#ifdef SEGGER_STDOUT_CH
|
||||
SEGGER_RTT_ConfigUpBuffer(SEGGER_STDOUT_CH, NULL, NULL, 1024, SEGGER_RTT_MODE_NO_BLOCK_TRIM);
|
||||
#endif
|
||||
@@ -300,17 +303,20 @@ void setup()
|
||||
digitalWrite(RESET_OLED, 1);
|
||||
#endif
|
||||
|
||||
#ifdef BUTTON_PIN
|
||||
#ifndef NO_ESP32
|
||||
// If BUTTON_PIN is held down during the startup process,
|
||||
// force the device to go into a SoftAP mode.
|
||||
bool forceSoftAP = 0;
|
||||
#ifdef BUTTON_PIN
|
||||
#ifndef NO_ESP32
|
||||
pinMode(BUTTON_PIN, INPUT);
|
||||
#ifdef BUTTON_NEED_PULLUP
|
||||
gpio_pullup_en((gpio_num_t)BUTTON_PIN);
|
||||
#endif
|
||||
|
||||
// BUTTON_PIN is pulled high by a 12k resistor.
|
||||
if (!digitalRead(BUTTON_PIN)) {
|
||||
forceSoftAP = 1;
|
||||
DEBUG_MSG("-------------------- Setting forceSoftAP = 1\n");
|
||||
DEBUG_MSG("Setting forceSoftAP = 1\n");
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -389,6 +395,19 @@ void setup()
|
||||
|
||||
readFromRTC(); // read the main CPU RTC at first (in case we can't get GPS time)
|
||||
|
||||
#ifdef GENIEBLOCKS
|
||||
// gps setup
|
||||
pinMode(GPS_RESET_N, OUTPUT);
|
||||
pinMode(GPS_EXTINT, OUTPUT);
|
||||
digitalWrite(GPS_RESET_N, HIGH);
|
||||
digitalWrite(GPS_EXTINT, LOW);
|
||||
// battery setup
|
||||
// If we want to read battery level, we need to set BATTERY_EN_PIN pin to low.
|
||||
// ToDo: For low power consumption after read battery level, set that pin to high.
|
||||
pinMode(BATTERY_EN_PIN, OUTPUT);
|
||||
digitalWrite(BATTERY_EN_PIN, LOW);
|
||||
#endif
|
||||
|
||||
// If we don't have bidirectional comms, we can't even try talking to UBLOX
|
||||
UBloxGPS *ublox = NULL;
|
||||
#ifdef GPS_TX_PIN
|
||||
@@ -426,14 +445,17 @@ void setup()
|
||||
|
||||
service.init();
|
||||
|
||||
// Now that the mesh service is created, create any plugins
|
||||
setupPlugins();
|
||||
|
||||
// Do this after service.init (because that clears error_code)
|
||||
#ifdef AXP192_SLAVE_ADDRESS
|
||||
if(!axp192_found)
|
||||
if (!axp192_found)
|
||||
recordCriticalError(CriticalErrorCode_NoAXP192); // Record a hardware fault for missing hardware
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// Don't call screen setup until after nodedb is setup (because we need
|
||||
// the current region name)
|
||||
// Don't call screen setup until after nodedb is setup (because we need
|
||||
// the current region name)
|
||||
#if defined(ST7735_CS) || defined(HAS_EINK)
|
||||
screen->setup();
|
||||
#else
|
||||
@@ -468,6 +490,8 @@ void setup()
|
||||
DEBUG_MSG("Warning: Failed to find RF95 radio\n");
|
||||
delete rIf;
|
||||
rIf = NULL;
|
||||
} else {
|
||||
DEBUG_MSG("Radio init succeeded, using RF95 radio\n");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -479,6 +503,8 @@ void setup()
|
||||
DEBUG_MSG("Warning: Failed to find SX1262 radio\n");
|
||||
delete rIf;
|
||||
rIf = NULL;
|
||||
} else {
|
||||
DEBUG_MSG("Radio init succeeded, using SX1262 radio\n");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -490,13 +516,23 @@ void setup()
|
||||
DEBUG_MSG("Warning: Failed to find simulated radio\n");
|
||||
delete rIf;
|
||||
rIf = NULL;
|
||||
} else {
|
||||
DEBUG_MSG("Using SIMULATED radio!\n");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifndef NO_ESP32
|
||||
// Initialize Wifi
|
||||
initWifi(forceSoftAP);
|
||||
|
||||
// Start web server thread.
|
||||
webServerThread = new WebServerThread();
|
||||
#endif
|
||||
|
||||
// Start airtime logger thread.
|
||||
airTime = new AirTime();
|
||||
|
||||
if (!rIf)
|
||||
recordCriticalError(CriticalErrorCode_NoRadio);
|
||||
else
|
||||
@@ -557,7 +593,7 @@ void loop()
|
||||
#endif
|
||||
|
||||
// TODO: This should go into a thread handled by FreeRTOS.
|
||||
handleWebResponse();
|
||||
// handleWebResponse();
|
||||
|
||||
service.loop();
|
||||
|
||||
@@ -570,7 +606,4 @@ void loop()
|
||||
// We want to sleep as long as possible here - because it saves power
|
||||
mainDelay.delay(delayMsec);
|
||||
// if (didWake) DEBUG_MSG("wake!\n");
|
||||
|
||||
// Handles cleanup for the airtime calculator.
|
||||
airtimeCalculator();
|
||||
}
|
||||
|
||||
@@ -72,7 +72,7 @@ void DSRRouter::sniffReceived(const MeshPacket *p)
|
||||
addRoute(p->from, p->from, 0); // We are adjacent with zero hops
|
||||
}
|
||||
|
||||
switch (p->decoded.which_payload) {
|
||||
switch (p->decoded.which_payloadVariant) {
|
||||
case SubPacket_route_request_tag:
|
||||
// Handle route discovery packets (will be a broadcast message)
|
||||
// FIXME - always start request with the senders nodenum
|
||||
@@ -105,7 +105,7 @@ void DSRRouter::sniffReceived(const MeshPacket *p)
|
||||
// packets until ack arrives)
|
||||
// FIXME, if we don't get a route reply at all (or a route error), timeout and generate a routeerror TIMEOUT on our own...
|
||||
break;
|
||||
case SubPacket_route_error_tag:
|
||||
case SubPacket_error_reason_tag:
|
||||
removeRoute(p->decoded.dest);
|
||||
|
||||
// FIXME: if any pending packets were waiting on this route, delete them
|
||||
@@ -131,7 +131,7 @@ void DSRRouter::sniffReceived(const MeshPacket *p)
|
||||
assert(p->decoded.source); // I think this is guaranteed by now
|
||||
|
||||
// FIXME - what if the current packet _is_ a route error packet?
|
||||
sendRouteError(p, RouteError_NO_ROUTE);
|
||||
sendRouteError(p, ErrorReason_NO_ROUTE);
|
||||
}
|
||||
|
||||
// FIXME, stop local processing of this packet
|
||||
@@ -139,13 +139,13 @@ void DSRRouter::sniffReceived(const MeshPacket *p)
|
||||
|
||||
// handle naks - convert them to route error packets
|
||||
// All naks are generated locally, because we failed resending the packet too many times
|
||||
PacketId nakId = p->decoded.which_ack == SubPacket_fail_id_tag ? p->decoded.ack.fail_id : 0;
|
||||
PacketId nakId = p->decoded.which_ackVariant == SubPacket_fail_id_tag ? p->decoded.ackVariant.fail_id : 0;
|
||||
if (nakId) {
|
||||
auto pending = findPendingPacket(p->to, nakId);
|
||||
if (pending && pending->packet->decoded.source) { // if source not set, this was not a multihop packet, just ignore
|
||||
removeRoute(pending->packet->decoded.dest); // We no longer have a route to the specified node
|
||||
|
||||
sendRouteError(p, RouteError_GOT_NAK);
|
||||
sendRouteError(p, ErrorReason_GOT_NAK);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -230,7 +230,7 @@ void DSRRouter::sendNextHop(NodeNum n, const MeshPacket *p)
|
||||
/**
|
||||
* Send a route error packet towards whoever originally sent this message
|
||||
*/
|
||||
void DSRRouter::sendRouteError(const MeshPacket *p, RouteError err)
|
||||
void DSRRouter::sendRouteError(const MeshPacket *p, ErrorReason err)
|
||||
{
|
||||
DEBUG_MSG("FIXME not implemented sendRouteError\n");
|
||||
}
|
||||
|
||||
@@ -70,7 +70,7 @@ class DSRRouter : public ReliableRouter
|
||||
/**
|
||||
* Send a route error packet towards whoever originally sent this message
|
||||
*/
|
||||
void sendRouteError(const MeshPacket *p, RouteError err);
|
||||
void sendRouteError(const MeshPacket *p, ErrorReason err);
|
||||
|
||||
/** make a copy of p, start discovery, but only if we don't
|
||||
* already a discovery in progress for that node number. Caller has already scheduled this message for retransmission
|
||||
|
||||
91
src/mesh/MeshPacketQueue.cpp
Normal file
91
src/mesh/MeshPacketQueue.cpp
Normal file
@@ -0,0 +1,91 @@
|
||||
#include "MeshPacketQueue.h"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
/// @return the priority of the specified packet
|
||||
inline uint32_t getPriority(MeshPacket *p)
|
||||
{
|
||||
auto pri = p->priority;
|
||||
return pri;
|
||||
}
|
||||
|
||||
/// @return "true" if "p1" is ordered before "p2"
|
||||
bool CompareMeshPacket::operator()(MeshPacket *p1, MeshPacket *p2)
|
||||
{
|
||||
assert(p1 && p2);
|
||||
auto p1p = getPriority(p1), p2p = getPriority(p2);
|
||||
|
||||
// If priorities differ, use that
|
||||
// for equal priorities, order by id (older packets have higher priority - this will briefly be wrong when IDs roll over but
|
||||
// no big deal)
|
||||
return (p1p != p2p) ? (p1p < p2p) // prefer bigger priorities
|
||||
: (p1->id >= p2->id); // prefer smaller packet ids
|
||||
}
|
||||
|
||||
MeshPacketQueue::MeshPacketQueue(size_t _maxLen) : maxLen(_maxLen) {}
|
||||
|
||||
/** Some clients might not properly set priority, therefore we fix it here.
|
||||
*/
|
||||
void fixPriority(MeshPacket *p)
|
||||
{
|
||||
// We might receive acks from other nodes (and since generated remotely, they won't have priority assigned. Check for that
|
||||
// and fix it
|
||||
if (p->priority == MeshPacket_Priority_UNSET) {
|
||||
// if acks give high priority
|
||||
// if a reliable message give a bit higher default priority
|
||||
p->priority = p->decoded.which_ackVariant ? MeshPacket_Priority_ACK :
|
||||
(p->want_ack ? MeshPacket_Priority_RELIABLE : MeshPacket_Priority_DEFAULT);
|
||||
}
|
||||
}
|
||||
|
||||
/** enqueue a packet, return false if full */
|
||||
bool MeshPacketQueue::enqueue(MeshPacket *p)
|
||||
{
|
||||
|
||||
fixPriority(p);
|
||||
|
||||
// fixme if there is something lower priority in the queue that can be deleted to make space, delete that instead
|
||||
if (size() >= maxLen)
|
||||
return false;
|
||||
else {
|
||||
push(p);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
MeshPacket *MeshPacketQueue::dequeue()
|
||||
{
|
||||
if (empty())
|
||||
return NULL;
|
||||
else {
|
||||
auto p = top();
|
||||
pop(); // remove the first item
|
||||
return p;
|
||||
}
|
||||
}
|
||||
|
||||
// this is kinda yucky, but I'm not sure if all arduino c++ compilers support closuers. And we only have one
|
||||
// thread that can run at a time - so safe
|
||||
static NodeNum findFrom;
|
||||
static PacketId findId;
|
||||
|
||||
static bool isMyPacket(MeshPacket *p)
|
||||
{
|
||||
return p->id == findId && p->from == findFrom;
|
||||
}
|
||||
|
||||
/** Attempt to find and remove a packet from this queue. Returns true the packet which was removed from the queue */
|
||||
MeshPacket *MeshPacketQueue::remove(NodeNum from, PacketId id)
|
||||
{
|
||||
findFrom = from;
|
||||
findId = id;
|
||||
auto it = std::find_if(this->c.begin(), this->c.end(), isMyPacket);
|
||||
if (it != this->c.end()) {
|
||||
auto p = *it;
|
||||
this->c.erase(it);
|
||||
std::make_heap(this->c.begin(), this->c.end(), this->comp);
|
||||
return p;
|
||||
} else {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
33
src/mesh/MeshPacketQueue.h
Normal file
33
src/mesh/MeshPacketQueue.h
Normal file
@@ -0,0 +1,33 @@
|
||||
#pragma once
|
||||
|
||||
#include "MeshTypes.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <queue>
|
||||
|
||||
// this is an strucure which implements the
|
||||
// operator overloading
|
||||
struct CompareMeshPacket {
|
||||
bool operator()(MeshPacket *p1, MeshPacket *p2);
|
||||
};
|
||||
|
||||
/**
|
||||
* A priority queue of packets.
|
||||
*
|
||||
*/
|
||||
class MeshPacketQueue : public std::priority_queue<MeshPacket *, std::vector<MeshPacket *>, CompareMeshPacket>
|
||||
{
|
||||
size_t maxLen;
|
||||
public:
|
||||
MeshPacketQueue(size_t _maxLen);
|
||||
|
||||
/** enqueue a packet, return false if full */
|
||||
bool enqueue(MeshPacket *p);
|
||||
|
||||
// bool isEmpty();
|
||||
|
||||
MeshPacket *dequeue();
|
||||
|
||||
/** Attempt to find and remove a packet from this queue. Returns true the packet which was removed from the queue */
|
||||
MeshPacket *remove(NodeNum from, PacketId id);
|
||||
};
|
||||
@@ -51,21 +51,7 @@ MeshService service;
|
||||
|
||||
#include "Router.h"
|
||||
|
||||
static int32_t sendOwnerCb()
|
||||
{
|
||||
static uint32_t currentGeneration;
|
||||
|
||||
// If we changed channels, ask everyone else for their latest info
|
||||
bool requestReplies = currentGeneration != radioGeneration;
|
||||
currentGeneration = radioGeneration;
|
||||
|
||||
DEBUG_MSG("Sending our nodeinfo to mesh (wantReplies=%d)\n", requestReplies);
|
||||
nodeInfoPlugin.sendOurNodeInfo(NODENUM_BROADCAST, requestReplies); // Send our info (don't request replies)
|
||||
|
||||
return getPref_send_owner_interval() * getPref_position_broadcast_secs() * 1000;
|
||||
}
|
||||
|
||||
static concurrency::Periodic *sendOwnerPeriod;
|
||||
|
||||
MeshService::MeshService() : toPhoneQueue(MAX_RX_TOPHONE)
|
||||
{
|
||||
@@ -74,9 +60,6 @@ MeshService::MeshService() : toPhoneQueue(MAX_RX_TOPHONE)
|
||||
|
||||
void MeshService::init()
|
||||
{
|
||||
sendOwnerPeriod = new concurrency::Periodic("SendOwner", sendOwnerCb);
|
||||
sendOwnerPeriod->setIntervalFromNow(30 * 1000); // Send our initial owner announcement 30 seconds after we start (to give network time to setup)
|
||||
|
||||
// moved much earlier in boot (called from setup())
|
||||
// nodeDB.init();
|
||||
|
||||
@@ -134,7 +117,8 @@ bool MeshService::reloadConfig()
|
||||
/// The owner User record just got updated, update our node DB and broadcast the info into the mesh
|
||||
void MeshService::reloadOwner()
|
||||
{
|
||||
nodeInfoPlugin.sendOurNodeInfo();
|
||||
assert(nodeInfoPlugin);
|
||||
nodeInfoPlugin->sendOurNodeInfo();
|
||||
nodeDB.saveToDisk();
|
||||
}
|
||||
|
||||
@@ -167,22 +151,15 @@ void MeshService::handleToRadio(MeshPacket &p)
|
||||
}
|
||||
}
|
||||
|
||||
/** Attempt to cancel a previously sent packet from this _local_ node. Returns true if a packet was found we could cancel */
|
||||
bool MeshService::cancelSending(PacketId id) {
|
||||
return router->cancelSending(nodeDB.getNodeNum(), id);
|
||||
}
|
||||
|
||||
void MeshService::sendToMesh(MeshPacket *p)
|
||||
{
|
||||
nodeDB.updateFrom(*p); // update our local DB for this packet (because phone might have sent position packets etc...)
|
||||
|
||||
// Strip out any time information before sending packets to other nodes - to keep the wire size small (and because other
|
||||
// nodes shouldn't trust it anyways) Note: we allow a device with a local GPS to include the time, so that gpsless
|
||||
// devices can get time.
|
||||
if (p->which_payload == MeshPacket_decoded_tag && p->decoded.which_payload == SubPacket_position_tag &&
|
||||
p->decoded.position.time) {
|
||||
if (getRTCQuality() < RTCQualityGPS) {
|
||||
DEBUG_MSG("Stripping time %u from position send\n", p->decoded.position.time);
|
||||
p->decoded.position.time = 0;
|
||||
} else
|
||||
DEBUG_MSG("Providing time to mesh %u\n", p->decoded.position.time);
|
||||
}
|
||||
|
||||
// Note: We might return !OK if our fifo was full, at that point the only option we have is to drop it
|
||||
router->sendLocal(p);
|
||||
}
|
||||
@@ -193,17 +170,40 @@ void MeshService::sendNetworkPing(NodeNum dest, bool wantReplies)
|
||||
assert(node);
|
||||
|
||||
DEBUG_MSG("Sending network ping to 0x%x, with position=%d, wantReplies=%d\n", dest, node->has_position, wantReplies);
|
||||
assert(positionPlugin && nodeInfoPlugin);
|
||||
if (node->has_position)
|
||||
positionPlugin.sendOurPosition(dest, wantReplies);
|
||||
positionPlugin->sendOurPosition(dest, wantReplies);
|
||||
else
|
||||
nodeInfoPlugin.sendOurNodeInfo(dest, wantReplies);
|
||||
nodeInfoPlugin->sendOurNodeInfo(dest, wantReplies);
|
||||
}
|
||||
|
||||
|
||||
NodeInfo *MeshService::refreshMyNodeInfo() {
|
||||
NodeInfo *node = nodeDB.getNode(nodeDB.getNodeNum());
|
||||
assert(node);
|
||||
|
||||
// We might not have a position yet for our local node, in that case, at least try to send the time
|
||||
if(!node->has_position) {
|
||||
memset(&node->position, 0, sizeof(node->position));
|
||||
node->has_position = true;
|
||||
}
|
||||
|
||||
Position &position = node->position;
|
||||
|
||||
// Update our local node info with our position (even if we don't decide to update anyone else)
|
||||
position.time = getValidTime(RTCQualityFromNet); // This nodedb timestamp might be stale, so update it if our clock is kinda valid
|
||||
|
||||
position.battery_level = powerStatus->getBatteryChargePercent();
|
||||
updateBatteryLevel(position.battery_level);
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
int MeshService::onGPSChanged(const meshtastic::GPSStatus *unused)
|
||||
{
|
||||
// Update our local node info with our position (even if we don't decide to update anyone else)
|
||||
|
||||
Position pos = Position_init_default;
|
||||
NodeInfo *node = refreshMyNodeInfo();
|
||||
Position pos = node->position;
|
||||
|
||||
if (gps->hasLock()) {
|
||||
if (gps->altitude != 0)
|
||||
@@ -215,40 +215,19 @@ int MeshService::onGPSChanged(const meshtastic::GPSStatus *unused)
|
||||
// The GPS has lost lock, if we are fixed position we should just keep using
|
||||
// the old position
|
||||
if(radioConfig.preferences.fixed_position) {
|
||||
NodeInfo *node = nodeDB.getNode(nodeDB.getNodeNum());
|
||||
assert(node);
|
||||
assert(node->has_position);
|
||||
pos = node->position;
|
||||
DEBUG_MSG("WARNING: Using fixed position\n");
|
||||
} else {
|
||||
// throw away old position
|
||||
pos.latitude_i = 0;
|
||||
pos.longitude_i = 0;
|
||||
pos.altitude = 0;
|
||||
}
|
||||
}
|
||||
|
||||
pos.time = getValidTime(RTCQualityGPS);
|
||||
|
||||
// Include our current battery voltage in our position announcement
|
||||
pos.battery_level = powerStatus->getBatteryChargePercent();
|
||||
updateBatteryLevel(pos.battery_level);
|
||||
|
||||
DEBUG_MSG("got gps notify time=%u, lat=%d, bat=%d\n", pos.latitude_i, pos.time, pos.battery_level);
|
||||
DEBUG_MSG("got gps notify time=%u, lat=%d, bat=%d\n", pos.time, pos.latitude_i, pos.battery_level);
|
||||
|
||||
// Update our current position in the local DB
|
||||
nodeDB.updatePosition(nodeDB.getNodeNum(), pos);
|
||||
|
||||
// We limit our GPS broadcasts to a max rate
|
||||
static uint32_t lastGpsSend;
|
||||
uint32_t now = millis();
|
||||
if (lastGpsSend == 0 || now - lastGpsSend > getPref_position_broadcast_secs() * 1000) {
|
||||
lastGpsSend = now;
|
||||
|
||||
static uint32_t currentGeneration;
|
||||
|
||||
// If we changed channels, ask everyone else for their latest info
|
||||
bool requestReplies = currentGeneration != radioGeneration;
|
||||
currentGeneration = radioGeneration;
|
||||
|
||||
DEBUG_MSG("Sending position to mesh (wantReplies=%d)\n", requestReplies);
|
||||
positionPlugin.sendOurPosition(NODENUM_BROADCAST, requestReplies);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -79,6 +79,12 @@ class MeshService
|
||||
/// cache
|
||||
void sendToMesh(MeshPacket *p);
|
||||
|
||||
/** Attempt to cancel a previously sent packet from this _local_ node. Returns true if a packet was found we could cancel */
|
||||
bool cancelSending(PacketId id);
|
||||
|
||||
/// Pull the latest power and time info into my nodeinfo
|
||||
NodeInfo *refreshMyNodeInfo();
|
||||
|
||||
private:
|
||||
|
||||
/// Called when our gps position has changed - updates nodedb and sends Location message out into the mesh
|
||||
|
||||
@@ -13,6 +13,7 @@ typedef uint32_t PacketId; // A packet sequence number
|
||||
#define ERRNO_OK 0
|
||||
#define ERRNO_NO_INTERFACES 33
|
||||
#define ERRNO_UNKNOWN 32 // pick something that doesn't conflict with RH_ROUTER_ERROR_UNABLE_TO_DELIVER
|
||||
#define ERRNO_DISABLED 34 // the itnerface is disabled
|
||||
|
||||
/**
|
||||
* the max number of hops a message can pass through, used as the default max for hop_limit in MeshPacket.
|
||||
|
||||
@@ -5,7 +5,9 @@
|
||||
#include "FS.h"
|
||||
|
||||
#include "CryptoEngine.h"
|
||||
#include "FSCommon.h"
|
||||
#include "GPS.h"
|
||||
#include "main.h"
|
||||
#include "MeshRadio.h"
|
||||
#include "NodeDB.h"
|
||||
#include "PacketHistory.h"
|
||||
@@ -15,11 +17,13 @@
|
||||
#include "configuration.h"
|
||||
#include "error.h"
|
||||
#include "mesh-pb-constants.h"
|
||||
#include "meshwifi/meshwifi.h"
|
||||
#include "FSCommon.h"
|
||||
#include <pb_decode.h>
|
||||
#include <pb_encode.h>
|
||||
|
||||
#ifndef NO_ESP32
|
||||
#include "mesh/http/WiFiAPClient.h"
|
||||
#endif
|
||||
|
||||
NodeDB nodeDB;
|
||||
|
||||
// we have plenty of ram so statically alloc this tempbuf (for now)
|
||||
@@ -28,7 +32,7 @@ MyNodeInfo &myNodeInfo = devicestate.my_node;
|
||||
RadioConfig &radioConfig = devicestate.radio;
|
||||
ChannelSettings &channelSettings = radioConfig.channel_settings;
|
||||
|
||||
/** The current change # for radio settings. Starts at 0 on boot and any time the radio settings
|
||||
/** The current change # for radio settings. Starts at 0 on boot and any time the radio settings
|
||||
* might have changed is incremented. Allows others to detect they might now be on a new channel.
|
||||
*/
|
||||
uint32_t radioGeneration;
|
||||
@@ -41,8 +45,6 @@ DeviceState versions used to be defined in the .proto file but really only this
|
||||
#define DEVICESTATE_CUR_VER 11
|
||||
#define DEVICESTATE_MIN_VER DEVICESTATE_CUR_VER
|
||||
|
||||
|
||||
|
||||
// FIXME - move this somewhere else
|
||||
extern void getMacAddr(uint8_t *dmac);
|
||||
|
||||
@@ -90,7 +92,7 @@ const char *getChannelName()
|
||||
static char buf[32];
|
||||
|
||||
char suffix;
|
||||
if(channelSettings.psk.size != 1) {
|
||||
if (channelSettings.psk.size != 1) {
|
||||
// We have a standard PSK, so generate a letter based hash.
|
||||
uint8_t code = 0;
|
||||
for (int i = 0; i < activePSKSize; i++)
|
||||
@@ -140,43 +142,55 @@ bool NodeDB::resetRadioConfig()
|
||||
}
|
||||
|
||||
// Convert the old string "Default" to our new short representation
|
||||
if(strcmp(channelSettings.name, "Default") == 0)
|
||||
if (strcmp(channelSettings.name, "Default") == 0)
|
||||
*channelSettings.name = '\0';
|
||||
|
||||
// Convert the short "" representation for Default into a usable string
|
||||
channelName = channelSettings.name;
|
||||
if(!*channelName) { // emptystring
|
||||
if (!*channelName) { // emptystring
|
||||
// Per mesh.proto spec, if bandwidth is specified we must ignore modemConfig enum, we assume that in that case
|
||||
// the app fucked up and forgot to set channelSettings.name
|
||||
channelName = "Unset";
|
||||
if(channelSettings.bandwidth == 0) switch(channelSettings.modem_config) {
|
||||
|
||||
if (channelSettings.bandwidth != 0)
|
||||
channelName = "Unset";
|
||||
else
|
||||
switch (channelSettings.modem_config) {
|
||||
case ChannelSettings_ModemConfig_Bw125Cr45Sf128:
|
||||
channelName = "Medium"; break;
|
||||
channelName = "Medium";
|
||||
break;
|
||||
case ChannelSettings_ModemConfig_Bw500Cr45Sf128:
|
||||
channelName = "ShortFast"; break;
|
||||
channelName = "ShortFast";
|
||||
break;
|
||||
case ChannelSettings_ModemConfig_Bw31_25Cr48Sf512:
|
||||
channelName = "LongAlt"; break;
|
||||
channelName = "LongAlt";
|
||||
break;
|
||||
case ChannelSettings_ModemConfig_Bw125Cr48Sf4096:
|
||||
channelName = "LongSlow"; break;
|
||||
channelName = "LongSlow";
|
||||
break;
|
||||
default:
|
||||
channelName = "Invalid"; break;
|
||||
}
|
||||
channelName = "Invalid";
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Convert any old usage of the defaultpsk into our new short representation.
|
||||
if(channelSettings.psk.size == sizeof(defaultpsk) &&
|
||||
memcmp(channelSettings.psk.bytes, defaultpsk, sizeof(defaultpsk)) == 0) {
|
||||
if (channelSettings.psk.size == sizeof(defaultpsk) &&
|
||||
memcmp(channelSettings.psk.bytes, defaultpsk, sizeof(defaultpsk)) == 0) {
|
||||
*channelSettings.psk.bytes = 1;
|
||||
channelSettings.psk.size = 1;
|
||||
}
|
||||
|
||||
// Convert the short single byte variants of psk into variant that can be used more generally
|
||||
memset(activePSK, 0, sizeof(activePSK)); // In case the user provided a short key, we want to pad the rest with zeros
|
||||
memcpy(activePSK, channelSettings.psk.bytes, channelSettings.psk.size);
|
||||
activePSKSize = channelSettings.psk.size;
|
||||
if(activePSKSize == 1) {
|
||||
if(activePSKSize == 0)
|
||||
DEBUG_MSG("Warning: User disabled encryption\n");
|
||||
else if (activePSKSize == 1) {
|
||||
// Convert the short single byte variants of psk into variant that can be used more generally
|
||||
|
||||
uint8_t pskIndex = activePSK[0];
|
||||
DEBUG_MSG("Expanding short PSK #%d\n", pskIndex);
|
||||
if(pskIndex == 0)
|
||||
if (pskIndex == 0)
|
||||
activePSKSize = 0; // Turn off encryption
|
||||
else {
|
||||
memcpy(activePSK, defaultpsk, sizeof(defaultpsk));
|
||||
@@ -185,6 +199,16 @@ bool NodeDB::resetRadioConfig()
|
||||
uint8_t *last = activePSK + sizeof(defaultpsk) - 1;
|
||||
*last = *last + pskIndex - 1; // index of 1 means no change vs defaultPSK
|
||||
}
|
||||
} else if(activePSKSize < 16) {
|
||||
// Error! The user specified only the first few bits of an AES128 key. So by convention we just pad the rest of the key
|
||||
// with zeros
|
||||
DEBUG_MSG("Warning: User provided a too short AES128 key - padding\n");
|
||||
activePSKSize = 16;
|
||||
} else if(activePSKSize < 32 && activePSKSize != 16) {
|
||||
// Error! The user specified only the first few bits of an AES256 key. So by convention we just pad the rest of the key
|
||||
// with zeros
|
||||
DEBUG_MSG("Warning: User provided a too short AES256 key - padding\n");
|
||||
activePSKSize = 32;
|
||||
}
|
||||
|
||||
// Tell our crypto engine about the psk
|
||||
@@ -241,16 +265,15 @@ void NodeDB::installDefaultDeviceState()
|
||||
|
||||
// Init our blank owner info to reasonable defaults
|
||||
getMacAddr(ourMacAddr);
|
||||
sprintf(owner.id, "!%02x%02x%02x%02x%02x%02x", ourMacAddr[0], ourMacAddr[1], ourMacAddr[2], ourMacAddr[3], ourMacAddr[4],
|
||||
ourMacAddr[5]);
|
||||
memcpy(owner.macaddr, ourMacAddr, sizeof(owner.macaddr));
|
||||
|
||||
// Set default owner name
|
||||
pickNewNodeNum(); // Note: we will repick later, just in case the settings are corrupted, but we need a valid
|
||||
// owner.short_name now
|
||||
pickNewNodeNum(); // based on macaddr now
|
||||
sprintf(owner.long_name, "Unknown %02x%02x", ourMacAddr[4], ourMacAddr[5]);
|
||||
sprintf(owner.short_name, "?%02X", (unsigned)(myNodeInfo.my_node_num & 0xff));
|
||||
|
||||
sprintf(owner.id, "!%08x", getNodeNum()); // Default node ID now based on nodenum
|
||||
memcpy(owner.macaddr, ourMacAddr, sizeof(owner.macaddr));
|
||||
|
||||
// Restore region if possible
|
||||
if (oldRegionCode != RegionCode_Unset)
|
||||
radioConfig.preferences.region = oldRegionCode;
|
||||
@@ -271,12 +294,13 @@ void NodeDB::init()
|
||||
myNodeInfo.node_num_bits = sizeof(NodeNum) * 8;
|
||||
myNodeInfo.packet_id_bits = sizeof(PacketId) * 8;
|
||||
|
||||
myNodeInfo.error_code = CriticalErrorCode_None; // For the error code, only show values from this boot (discard value from flash)
|
||||
myNodeInfo.error_code =
|
||||
CriticalErrorCode_None; // For the error code, only show values from this boot (discard value from flash)
|
||||
myNodeInfo.error_address = 0;
|
||||
|
||||
// likewise - we always want the app requirements to come from the running appload
|
||||
myNodeInfo.min_app_version = 20120; // format is Mmmss (where M is 1+the numeric major number. i.e. 20120 means 1.1.20
|
||||
|
||||
|
||||
// Note! We do this after loading saved settings, so that if somehow an invalid nodenum was stored in preferences we won't
|
||||
// keep using that nodenum forever. Crummy guess at our nodenum (but we will check against the nodedb to avoid conflicts)
|
||||
pickNewNodeNum();
|
||||
@@ -501,7 +525,7 @@ void NodeDB::updateUser(uint32_t nodeId, const User &p)
|
||||
/// we updateGUI and updateGUIforNode if we think our this change is big enough for a redraw
|
||||
void NodeDB::updateFrom(const MeshPacket &mp)
|
||||
{
|
||||
if (mp.which_payload == MeshPacket_decoded_tag) {
|
||||
if (mp.which_payloadVariant == MeshPacket_decoded_tag) {
|
||||
const SubPacket &p = mp.decoded;
|
||||
DEBUG_MSG("Update DB node 0x%x, rx_time=%u\n", mp.from, mp.rx_time);
|
||||
|
||||
@@ -514,7 +538,7 @@ void NodeDB::updateFrom(const MeshPacket &mp)
|
||||
|
||||
info->snr = mp.rx_snr; // keep the most recent SNR we received for this node.
|
||||
|
||||
switch (p.which_payload) {
|
||||
switch (p.which_payloadVariant) {
|
||||
case SubPacket_position_tag: {
|
||||
// handle a legacy position packet
|
||||
DEBUG_MSG("WARNING: Processing a (deprecated) position packet from %d\n", mp.from);
|
||||
@@ -573,8 +597,14 @@ NodeInfo *NodeDB::getOrCreateNode(NodeNum n)
|
||||
/// Record an error that should be reported via analytics
|
||||
void recordCriticalError(CriticalErrorCode code, uint32_t address)
|
||||
{
|
||||
DEBUG_MSG("NOTE! Recording critical error %d, address=%x\n", code, address);
|
||||
// Print error to screen and serial port
|
||||
String lcd = String("Critical error ") + code + "!\n";
|
||||
screen->print(lcd.c_str());
|
||||
DEBUG_MSG("NOTE! Recording critical error %d, address=%lx\n", code, address);
|
||||
|
||||
// Record error to DB
|
||||
myNodeInfo.error_code = code;
|
||||
myNodeInfo.error_address = address;
|
||||
myNodeInfo.error_count++;
|
||||
|
||||
}
|
||||
|
||||
@@ -21,11 +21,17 @@ void PhoneAPI::init()
|
||||
observe(&service.fromNumChanged);
|
||||
}
|
||||
|
||||
PhoneAPI::~PhoneAPI() {
|
||||
close();
|
||||
}
|
||||
|
||||
void PhoneAPI::close() {
|
||||
unobserve();
|
||||
state = STATE_SEND_NOTHING;
|
||||
bool oldConnected = isConnected;
|
||||
isConnected = false;
|
||||
onConnectionChanged(isConnected);
|
||||
if(oldConnected != isConnected)
|
||||
onConnectionChanged(isConnected);
|
||||
}
|
||||
|
||||
void PhoneAPI::checkConnectionTimeout()
|
||||
@@ -53,15 +59,15 @@ void PhoneAPI::handleToRadio(const uint8_t *buf, size_t bufLength)
|
||||
// return (lastContactMsec != 0) &&
|
||||
|
||||
if (pb_decode_from_bytes(buf, bufLength, ToRadio_fields, &toRadioScratch)) {
|
||||
switch (toRadioScratch.which_variant) {
|
||||
switch (toRadioScratch.which_payloadVariant) {
|
||||
case ToRadio_packet_tag: {
|
||||
MeshPacket &p = toRadioScratch.variant.packet;
|
||||
MeshPacket &p = toRadioScratch.packet;
|
||||
printPacket("PACKET FROM PHONE", &p);
|
||||
service.handleToRadio(p);
|
||||
break;
|
||||
}
|
||||
case ToRadio_want_config_id_tag:
|
||||
config_nonce = toRadioScratch.variant.want_config_id;
|
||||
config_nonce = toRadioScratch.want_config_id;
|
||||
DEBUG_MSG("Client wants config, nonce=%u\n", config_nonce);
|
||||
state = STATE_SEND_MY_INFO;
|
||||
|
||||
@@ -73,12 +79,12 @@ void PhoneAPI::handleToRadio(const uint8_t *buf, size_t bufLength)
|
||||
|
||||
case ToRadio_set_owner_tag:
|
||||
DEBUG_MSG("Client is setting owner\n");
|
||||
handleSetOwner(toRadioScratch.variant.set_owner);
|
||||
handleSetOwner(toRadioScratch.set_owner);
|
||||
break;
|
||||
|
||||
case ToRadio_set_radio_tag:
|
||||
DEBUG_MSG("Client is setting radio\n");
|
||||
handleSetRadio(toRadioScratch.variant.set_radio);
|
||||
handleSetRadio(toRadioScratch.set_radio);
|
||||
break;
|
||||
|
||||
default:
|
||||
@@ -125,20 +131,22 @@ size_t PhoneAPI::getFromRadio(uint8_t *buf)
|
||||
myNodeInfo.has_gps = (radioConfig.preferences.location_share == LocationSharing_LocDisabled)
|
||||
? true
|
||||
: (gps && gps->isConnected()); // Update with latest GPS connect info
|
||||
fromRadioScratch.which_variant = FromRadio_my_info_tag;
|
||||
fromRadioScratch.variant.my_info = myNodeInfo;
|
||||
fromRadioScratch.which_payloadVariant = FromRadio_my_info_tag;
|
||||
fromRadioScratch.my_info = myNodeInfo;
|
||||
state = STATE_SEND_RADIO;
|
||||
|
||||
service.refreshMyNodeInfo(); // Update my NodeInfo because the client will be asking for it soon.
|
||||
break;
|
||||
|
||||
case STATE_SEND_RADIO:
|
||||
fromRadioScratch.which_variant = FromRadio_radio_tag;
|
||||
fromRadioScratch.which_payloadVariant = FromRadio_radio_tag;
|
||||
|
||||
fromRadioScratch.variant.radio = radioConfig;
|
||||
fromRadioScratch.radio = radioConfig;
|
||||
|
||||
// NOTE: The phone app needs to know the ls_secs value so it can properly expect sleep behavior.
|
||||
// So even if we internally use 0 to represent 'use default' we still need to send the value we are
|
||||
// using to the app (so that even old phone apps work with new device loads).
|
||||
fromRadioScratch.variant.radio.preferences.ls_secs = getPref_ls_secs();
|
||||
fromRadioScratch.radio.preferences.ls_secs = getPref_ls_secs();
|
||||
|
||||
state = STATE_SEND_NODEINFO;
|
||||
break;
|
||||
@@ -150,8 +158,8 @@ size_t PhoneAPI::getFromRadio(uint8_t *buf)
|
||||
if (info) {
|
||||
DEBUG_MSG("Sending nodeinfo: num=0x%x, lastseen=%u, id=%s, name=%s\n", info->num, info->position.time, info->user.id,
|
||||
info->user.long_name);
|
||||
fromRadioScratch.which_variant = FromRadio_node_info_tag;
|
||||
fromRadioScratch.variant.node_info = *info;
|
||||
fromRadioScratch.which_payloadVariant = FromRadio_node_info_tag;
|
||||
fromRadioScratch.node_info = *info;
|
||||
// Stay in current state until done sending nodeinfos
|
||||
} else {
|
||||
DEBUG_MSG("Done sending nodeinfos\n");
|
||||
@@ -163,8 +171,8 @@ size_t PhoneAPI::getFromRadio(uint8_t *buf)
|
||||
}
|
||||
|
||||
case STATE_SEND_COMPLETE_ID:
|
||||
fromRadioScratch.which_variant = FromRadio_config_complete_id_tag;
|
||||
fromRadioScratch.variant.config_complete_id = config_nonce;
|
||||
fromRadioScratch.which_payloadVariant = FromRadio_config_complete_id_tag;
|
||||
fromRadioScratch.config_complete_id = config_nonce;
|
||||
config_nonce = 0;
|
||||
state = STATE_SEND_PACKETS;
|
||||
break;
|
||||
@@ -177,8 +185,8 @@ size_t PhoneAPI::getFromRadio(uint8_t *buf)
|
||||
printPacket("phone downloaded packet", packetForPhone);
|
||||
|
||||
// Encapsulate as a FromRadio packet
|
||||
fromRadioScratch.which_variant = FromRadio_packet_tag;
|
||||
fromRadioScratch.variant.packet = *packetForPhone;
|
||||
fromRadioScratch.which_payloadVariant = FromRadio_packet_tag;
|
||||
fromRadioScratch.packet = *packetForPhone;
|
||||
|
||||
service.releaseToPool(packetForPhone); // we just copied the bytes, so don't need this buffer anymore
|
||||
packetForPhone = NULL;
|
||||
@@ -190,9 +198,9 @@ size_t PhoneAPI::getFromRadio(uint8_t *buf)
|
||||
}
|
||||
|
||||
// Do we have a message from the mesh?
|
||||
if (fromRadioScratch.which_variant != 0) {
|
||||
if (fromRadioScratch.which_payloadVariant != 0) {
|
||||
// Encapsulate as a FromRadio packet
|
||||
DEBUG_MSG("encoding toPhone packet to phone variant=%d", fromRadioScratch.which_variant);
|
||||
DEBUG_MSG("encoding toPhone packet to phone variant=%d", fromRadioScratch.which_payloadVariant);
|
||||
size_t numbytes = pb_encode_to_bytes(buf, FromRadio_size, FromRadio_fields, &fromRadioScratch);
|
||||
DEBUG_MSG(", %d bytes\n", numbytes);
|
||||
return numbytes;
|
||||
|
||||
@@ -55,12 +55,15 @@ class PhoneAPI
|
||||
public:
|
||||
PhoneAPI();
|
||||
|
||||
/// Destructor - calls close()
|
||||
virtual ~PhoneAPI();
|
||||
|
||||
/// Do late init that can't happen at constructor time
|
||||
virtual void init();
|
||||
|
||||
// Call this when the client drops the connection, resets the state to STATE_SEND_NOTHING
|
||||
// Unregisters our observer
|
||||
void close();
|
||||
// Unregisters our observer. A closed connection **can** be reopened by calling init again.
|
||||
virtual void close();
|
||||
|
||||
/**
|
||||
* Handle a ToRadio protobuf
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
#define POWER_DEFAULT 17 // How much power to use if the user hasn't set a power level
|
||||
|
||||
RF95Interface::RF95Interface(RADIOLIB_PIN_TYPE cs, RADIOLIB_PIN_TYPE irq, RADIOLIB_PIN_TYPE rst, SPIClass &spi)
|
||||
: RadioLibInterface(cs, irq, rst, 0, spi)
|
||||
: RadioLibInterface(cs, irq, rst, RADIOLIB_NC, spi)
|
||||
{
|
||||
// FIXME - we assume devices never get destroyed
|
||||
}
|
||||
|
||||
@@ -14,6 +14,9 @@ class RF95Interface : public RadioLibInterface
|
||||
public:
|
||||
RF95Interface(RADIOLIB_PIN_TYPE cs, RADIOLIB_PIN_TYPE irq, RADIOLIB_PIN_TYPE rst, SPIClass &spi);
|
||||
|
||||
/// Some boards (Pinetab Lora module) have broken IRQ wires, so we need to poll via i2c registers
|
||||
bool isIRQPending() { return lora->getPendingIRQ(); }
|
||||
|
||||
/// Initialise the Driver transport hardware and software.
|
||||
/// Make sure the Driver is properly configured before calling init().
|
||||
/// \return true if initialisation succeeded.
|
||||
|
||||
@@ -83,7 +83,7 @@ uint32_t RadioInterface::getPacketTime(uint32_t pl)
|
||||
|
||||
uint32_t RadioInterface::getPacketTime(MeshPacket *p)
|
||||
{
|
||||
assert(p->which_payload == MeshPacket_encrypted_tag); // It should have already been encoded by now
|
||||
assert(p->which_payloadVariant == MeshPacket_encrypted_tag); // It should have already been encoded by now
|
||||
uint32_t pl = p->encrypted.size + sizeof(PacketHeader);
|
||||
|
||||
return getPacketTime(pl);
|
||||
@@ -119,9 +119,9 @@ void printPacket(const char *prefix, const MeshPacket *p)
|
||||
{
|
||||
DEBUG_MSG("%s (id=0x%08x Fr0x%02x To0x%02x, WantAck%d, HopLim%d", prefix, p->id, p->from & 0xff, p->to & 0xff, p->want_ack,
|
||||
p->hop_limit);
|
||||
if (p->which_payload == MeshPacket_decoded_tag) {
|
||||
if (p->which_payloadVariant == MeshPacket_decoded_tag) {
|
||||
auto &s = p->decoded;
|
||||
switch (s.which_payload) {
|
||||
switch (s.which_payloadVariant) {
|
||||
case SubPacket_data_tag:
|
||||
DEBUG_MSG(" Portnum=%d", s.data.portnum);
|
||||
break;
|
||||
@@ -135,7 +135,7 @@ void printPacket(const char *prefix, const MeshPacket *p)
|
||||
DEBUG_MSG(" Payload:None");
|
||||
break;
|
||||
default:
|
||||
DEBUG_MSG(" Payload:%d", s.which_payload);
|
||||
DEBUG_MSG(" Payload:%d", s.which_payloadVariant);
|
||||
break;
|
||||
}
|
||||
if (s.want_response)
|
||||
@@ -147,10 +147,10 @@ void printPacket(const char *prefix, const MeshPacket *p)
|
||||
if (s.dest != 0)
|
||||
DEBUG_MSG(" dest=%08x", s.dest);
|
||||
|
||||
if (s.which_ack == SubPacket_success_id_tag)
|
||||
DEBUG_MSG(" successId=%08x", s.ack.success_id);
|
||||
else if (s.which_ack == SubPacket_fail_id_tag)
|
||||
DEBUG_MSG(" failId=%08x", s.ack.fail_id);
|
||||
if (s.which_ackVariant == SubPacket_success_id_tag)
|
||||
DEBUG_MSG(" successId=%08x", s.ackVariant.success_id);
|
||||
else if (s.which_ackVariant == SubPacket_fail_id_tag)
|
||||
DEBUG_MSG(" failId=%08x", s.ackVariant.fail_id);
|
||||
} else {
|
||||
DEBUG_MSG(" encrypted");
|
||||
}
|
||||
@@ -161,6 +161,9 @@ void printPacket(const char *prefix, const MeshPacket *p)
|
||||
if (p->rx_snr != 0.0) {
|
||||
DEBUG_MSG(" rxSNR=%g", p->rx_snr);
|
||||
}
|
||||
if(p->priority != 0)
|
||||
DEBUG_MSG(" priority=%d", p->priority);
|
||||
|
||||
DEBUG_MSG(")\n");
|
||||
}
|
||||
|
||||
@@ -209,6 +212,38 @@ unsigned long hash(const char *str)
|
||||
return hash;
|
||||
}
|
||||
|
||||
/**
|
||||
* Save our frequency for later reuse.
|
||||
*/
|
||||
void RadioInterface::saveFreq(float freq)
|
||||
{
|
||||
savedFreq = freq;
|
||||
}
|
||||
|
||||
/**
|
||||
* Save our channel for later reuse.
|
||||
*/
|
||||
void RadioInterface::saveChannelNum(uint32_t channel_num)
|
||||
{
|
||||
savedChannelNum = channel_num;
|
||||
}
|
||||
|
||||
/**
|
||||
* Save our frequency for later reuse.
|
||||
*/
|
||||
float RadioInterface::getFreq()
|
||||
{
|
||||
return savedFreq;
|
||||
}
|
||||
|
||||
/**
|
||||
* Save our channel for later reuse.
|
||||
*/
|
||||
uint32_t RadioInterface::getChannelNum()
|
||||
{
|
||||
return savedChannelNum;
|
||||
}
|
||||
|
||||
/**
|
||||
* Pull our channel settings etc... from protobufs to the dumb interface settings
|
||||
*/
|
||||
@@ -261,18 +296,19 @@ void RadioInterface::applyModemConfig()
|
||||
assert(myRegion); // Should have been found in init
|
||||
|
||||
// If user has manually specified a channel num, then use that, otherwise generate one by hashing the name
|
||||
int channel_num =
|
||||
(channelSettings.channel_num ? channelSettings.channel_num - 1 : hash(channelName)) % myRegion->numChannels;
|
||||
int channel_num = (channelSettings.channel_num ? channelSettings.channel_num - 1 : hash(channelName)) % myRegion->numChannels;
|
||||
freq = myRegion->freq + myRegion->spacing * channel_num;
|
||||
|
||||
DEBUG_MSG("Set radio: name=%s, config=%u, ch=%d, power=%d\n", channelName, channelSettings.modem_config, channel_num,
|
||||
power);
|
||||
DEBUG_MSG("Set radio: name=%s, config=%u, ch=%d, power=%d\n", channelName, channelSettings.modem_config, channel_num, power);
|
||||
DEBUG_MSG("Radio myRegion->freq: %f\n", myRegion->freq);
|
||||
DEBUG_MSG("Radio myRegion->spacing: %f\n", myRegion->spacing);
|
||||
DEBUG_MSG("Radio myRegion->numChannels: %d\n", myRegion->numChannels);
|
||||
DEBUG_MSG("Radio channel_num: %d\n", channel_num);
|
||||
DEBUG_MSG("Radio frequency: %f\n", freq);
|
||||
DEBUG_MSG("Short packet time: %u msec\n", shortPacketMsec);
|
||||
|
||||
saveChannelNum(channel_num);
|
||||
saveFreq(freq);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -315,7 +351,7 @@ size_t RadioInterface::beginSending(MeshPacket *p)
|
||||
assert(!sendingPacket);
|
||||
|
||||
// DEBUG_MSG("sending queued packet on mesh (txGood=%d,rxGood=%d,rxBad=%d)\n", rf95.txGood(), rf95.rxGood(), rf95.rxBad());
|
||||
assert(p->which_payload == MeshPacket_encrypted_tag); // It should have already been encoded by now
|
||||
assert(p->which_payloadVariant == MeshPacket_encrypted_tag); // It should have already been encoded by now
|
||||
|
||||
lastTxStart = millis();
|
||||
|
||||
|
||||
@@ -54,6 +54,8 @@ class RadioInterface
|
||||
uint32_t shortPacketMsec;
|
||||
|
||||
protected:
|
||||
bool disabled = false;
|
||||
|
||||
float bw = 125;
|
||||
uint8_t sf = 9;
|
||||
uint8_t cr = 7;
|
||||
@@ -98,6 +100,9 @@ class RadioInterface
|
||||
/// Prepare hardware for sleep. Call this _only_ for deep sleep, not needed for light sleep.
|
||||
virtual bool sleep() { return true; }
|
||||
|
||||
/// Disable this interface (while disabled, no packets can be sent or received)
|
||||
void disable() { disabled = true; sleep(); }
|
||||
|
||||
/**
|
||||
* Send a packet (possibly by enquing in a private fifo). This routine will
|
||||
* later free() the packet to pool. This routine is not allowed to stall.
|
||||
@@ -105,6 +110,9 @@ class RadioInterface
|
||||
*/
|
||||
virtual ErrorCode send(MeshPacket *p) = 0;
|
||||
|
||||
/** Attempt to cancel a previously sent packet. Returns true if a packet was found we could cancel */
|
||||
virtual bool cancelSending(NodeNum from, PacketId id) { return false; }
|
||||
|
||||
// methods from radiohead
|
||||
|
||||
/// Initialise the Driver transport hardware and software.
|
||||
@@ -133,9 +141,22 @@ class RadioInterface
|
||||
uint32_t getPacketTime(MeshPacket *p);
|
||||
uint32_t getPacketTime(uint32_t totalPacketLen);
|
||||
|
||||
/**
|
||||
* Get the channel we saved.
|
||||
*/
|
||||
uint32_t getChannelNum();
|
||||
|
||||
/**
|
||||
* Get the frequency we saved.
|
||||
*/
|
||||
float getFreq();
|
||||
|
||||
protected:
|
||||
int8_t power = 17; // Set by applyModemConfig()
|
||||
|
||||
float savedFreq;
|
||||
uint32_t savedChannelNum;
|
||||
|
||||
/***
|
||||
* given a packet set sendingPacket and decode the protobufs into radiobuf. Returns # of bytes to send (including the
|
||||
* PacketHeader & payload).
|
||||
@@ -157,6 +178,16 @@ class RadioInterface
|
||||
*/
|
||||
virtual void applyModemConfig();
|
||||
|
||||
/**
|
||||
* Save the frequency we selected for later reuse.
|
||||
*/
|
||||
virtual void saveFreq(float savedFreq);
|
||||
|
||||
/**
|
||||
* Save the chanel we selected for later reuse.
|
||||
*/
|
||||
virtual void saveChannelNum(uint32_t savedChannelNum);
|
||||
|
||||
private:
|
||||
/// Return 0 if sleep is okay
|
||||
int preflightSleepCb(void *unused = NULL) { return canSleep() ? 0 : 1; }
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
#include "MeshTypes.h"
|
||||
#include "NodeDB.h"
|
||||
#include "SPILock.h"
|
||||
#include "error.h"
|
||||
#include "mesh-pb-constants.h"
|
||||
#include <configuration.h>
|
||||
#include <pb_decode.h>
|
||||
@@ -70,6 +71,16 @@ bool RadioLibInterface::canSendImmediately()
|
||||
if (busyTx || busyRx) {
|
||||
if (busyTx)
|
||||
DEBUG_MSG("Can not send yet, busyTx\n");
|
||||
// If we've been trying to send the same packet more than one minute and we haven't gotten a
|
||||
// TX IRQ from the radio, the radio is probably broken.
|
||||
if (busyTx && (millis() - lastTxStart > 60000)) {
|
||||
DEBUG_MSG("Hardware Failure! busyTx for more than 60s\n");
|
||||
recordCriticalError(CriticalErrorCode_TransmitFailed);
|
||||
#ifndef NO_ESP32
|
||||
if (busyTx && (millis() - lastTxStart > 65000)) // After 5s more, reboot
|
||||
ESP.restart();
|
||||
#endif
|
||||
}
|
||||
if (busyRx)
|
||||
DEBUG_MSG("Can not send yet, busyRx\n");
|
||||
return false;
|
||||
@@ -82,13 +93,18 @@ bool RadioLibInterface::canSendImmediately()
|
||||
/// bluetooth comms code. If the txmit queue is empty it might return an error
|
||||
ErrorCode RadioLibInterface::send(MeshPacket *p)
|
||||
{
|
||||
if (disabled) {
|
||||
packetPool.release(p);
|
||||
return ERRNO_DISABLED;
|
||||
}
|
||||
|
||||
// Sometimes when testing it is useful to be able to never turn on the xmitter
|
||||
#ifndef LORA_DISABLE_SENDING
|
||||
printPacket("enqueuing for send", p);
|
||||
uint32_t xmitMsec = getPacketTime(p);
|
||||
|
||||
DEBUG_MSG("txGood=%d,rxGood=%d,rxBad=%d\n", txGood, rxGood, rxBad);
|
||||
ErrorCode res = txQueue.enqueue(p, 0) ? ERRNO_OK : ERRNO_UNKNOWN;
|
||||
ErrorCode res = txQueue.enqueue(p) ? ERRNO_OK : ERRNO_UNKNOWN;
|
||||
|
||||
if (res != ERRNO_OK) { // we weren't able to queue it, so we must drop it to prevent leaks
|
||||
packetPool.release(p);
|
||||
@@ -97,7 +113,8 @@ ErrorCode RadioLibInterface::send(MeshPacket *p)
|
||||
|
||||
// Count the packet toward our TX airtime utilization.
|
||||
// We only count it if it can be added to the TX queue.
|
||||
logAirtime(TX_LOG, xmitMsec);
|
||||
airTime->logAirtime(TX_LOG, xmitMsec);
|
||||
// airTime.logAirtime(TX_LOG, xmitMsec);
|
||||
|
||||
// We want all sending/receiving to be done by our daemon thread, We use a delay here because this packet might have been sent
|
||||
// in response to a packet we just received. So we want to make sure the other side has had a chance to reconfigure its radio
|
||||
@@ -106,19 +123,31 @@ ErrorCode RadioLibInterface::send(MeshPacket *p)
|
||||
return res;
|
||||
#else
|
||||
packetPool.release(p);
|
||||
return ERRNO_UNKNOWN;
|
||||
return ERRNO_DISABLED;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool RadioLibInterface::canSleep()
|
||||
{
|
||||
bool res = txQueue.isEmpty();
|
||||
bool res = txQueue.empty();
|
||||
if (!res) // only print debug messages if we are vetoing sleep
|
||||
DEBUG_MSG("radio wait to sleep, txEmpty=%d\n", res);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
/** Attempt to cancel a previously sent packet. Returns true if a packet was found we could cancel */
|
||||
bool RadioLibInterface::cancelSending(NodeNum from, PacketId id)
|
||||
{
|
||||
auto p = txQueue.remove(from, id);
|
||||
if (p)
|
||||
packetPool.release(p); // free the packet we just removed
|
||||
|
||||
bool result = (p != NULL);
|
||||
DEBUG_MSG("cancelSending id=0x%x, removed=%d\n", id, result);
|
||||
return result;
|
||||
}
|
||||
|
||||
/** radio helper thread callback.
|
||||
|
||||
We never immediately transmit after any operation (either rx or tx). Instead we should start receiving and
|
||||
@@ -152,12 +181,12 @@ void RadioLibInterface::onNotify(uint32_t notification)
|
||||
|
||||
// If we are not currently in receive mode, then restart the timer and try again later (this can happen if the main thread
|
||||
// has placed the unit into standby) FIXME, how will this work if the chipset is in sleep mode?
|
||||
if (!txQueue.isEmpty()) {
|
||||
if (!txQueue.empty()) {
|
||||
if (!canSendImmediately()) {
|
||||
startTransmitTimer(); // try again in a little while
|
||||
} else {
|
||||
// Send any outgoing packets we have ready
|
||||
MeshPacket *txp = txQueue.dequeuePtr(0);
|
||||
MeshPacket *txp = txQueue.dequeue();
|
||||
assert(txp);
|
||||
startSend(txp);
|
||||
}
|
||||
@@ -173,7 +202,7 @@ void RadioLibInterface::onNotify(uint32_t notification)
|
||||
void RadioLibInterface::startTransmitTimer(bool withDelay)
|
||||
{
|
||||
// If we have work to do and the timer wasn't already scheduled, schedule it now
|
||||
if (!txQueue.isEmpty()) {
|
||||
if (!txQueue.empty()) {
|
||||
uint32_t delay = !withDelay ? 1 : getTxDelayMsec();
|
||||
// DEBUG_MSG("xmit timer %d\n", delay);
|
||||
notifyLater(delay, TRANSMIT_DELAY_COMPLETED, false); // This will implicitly enable
|
||||
@@ -216,7 +245,8 @@ void RadioLibInterface::handleReceiveInterrupt()
|
||||
size_t length = iface->getPacketLength();
|
||||
|
||||
xmitMsec = getPacketTime(length);
|
||||
logAirtime(RX_ALL_LOG, xmitMsec);
|
||||
airTime->logAirtime(RX_ALL_LOG, xmitMsec);
|
||||
// airTime.logAirtime(RX_ALL_LOG, xmitMsec);
|
||||
|
||||
int state = iface->readData(radiobuf, length);
|
||||
if (state != ERR_NONE) {
|
||||
@@ -250,15 +280,16 @@ void RadioLibInterface::handleReceiveInterrupt()
|
||||
|
||||
addReceiveMetadata(mp);
|
||||
|
||||
mp->which_payload = MeshPacket_encrypted_tag; // Mark that the payload is still encrypted at this point
|
||||
assert(((uint32_t) payloadLen) <= sizeof(mp->encrypted.bytes));
|
||||
mp->which_payloadVariant = MeshPacket_encrypted_tag; // Mark that the payload is still encrypted at this point
|
||||
assert(((uint32_t)payloadLen) <= sizeof(mp->encrypted.bytes));
|
||||
memcpy(mp->encrypted.bytes, payload, payloadLen);
|
||||
mp->encrypted.size = payloadLen;
|
||||
|
||||
printPacket("Lora RX", mp);
|
||||
|
||||
xmitMsec = getPacketTime(mp);
|
||||
logAirtime(RX_LOG, xmitMsec);
|
||||
airTime->logAirtime(RX_LOG, xmitMsec);
|
||||
// airTime.logAirtime(RX_LOG, xmitMsec);
|
||||
|
||||
deliverToReceiver(mp);
|
||||
}
|
||||
@@ -268,16 +299,21 @@ void RadioLibInterface::handleReceiveInterrupt()
|
||||
/** start an immediate transmit */
|
||||
void RadioLibInterface::startSend(MeshPacket *txp)
|
||||
{
|
||||
printPacket("Starting low level send", txp);
|
||||
setStandby(); // Cancel any already in process receives
|
||||
printPacket("Starting low level send", txp);
|
||||
if (disabled) {
|
||||
DEBUG_MSG("startSend is dropping tx packet because we are disabled\n");
|
||||
packetPool.release(txp);
|
||||
} else {
|
||||
setStandby(); // Cancel any already in process receives
|
||||
|
||||
configHardwareForSend(); // must be after setStandby
|
||||
configHardwareForSend(); // must be after setStandby
|
||||
|
||||
size_t numbytes = beginSending(txp);
|
||||
size_t numbytes = beginSending(txp);
|
||||
|
||||
int res = iface->startTransmit(radiobuf, numbytes);
|
||||
assert(res == ERR_NONE);
|
||||
int res = iface->startTransmit(radiobuf, numbytes);
|
||||
assert(res == ERR_NONE);
|
||||
|
||||
// Must be done AFTER, starting transmit, because startTransmit clears (possibly stale) interrupt pending register bits
|
||||
enableInterrupt(isrTxLevel0);
|
||||
// Must be done AFTER, starting transmit, because startTransmit clears (possibly stale) interrupt pending register bits
|
||||
enableInterrupt(isrTxLevel0);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
#include "../concurrency/OSThread.h"
|
||||
#include "RadioInterface.h"
|
||||
#include "MeshPacketQueue.h"
|
||||
|
||||
#ifdef CubeCell_BoardPlus
|
||||
#define RADIOLIB_SOFTWARE_SERIAL_UNSUPPORTED
|
||||
@@ -74,7 +75,7 @@ class RadioLibInterface : public RadioInterface, protected concurrency::Notified
|
||||
*/
|
||||
uint32_t rxBad = 0, rxGood = 0, txGood = 0;
|
||||
|
||||
PointerQueue<MeshPacket> txQueue = PointerQueue<MeshPacket>(MAX_TX_QUEUE);
|
||||
MeshPacketQueue txQueue = MeshPacketQueue(MAX_TX_QUEUE);
|
||||
|
||||
protected:
|
||||
|
||||
@@ -136,6 +137,9 @@ class RadioLibInterface : public RadioInterface, protected concurrency::Notified
|
||||
*/
|
||||
virtual bool isActivelyReceiving() = 0;
|
||||
|
||||
/** Attempt to cancel a previously sent packet. Returns true if a packet was found we could cancel */
|
||||
virtual bool cancelSending(NodeNum from, PacketId id);
|
||||
|
||||
private:
|
||||
/** if we have something waiting to send, start a short random timer so we can come check for collision before actually doing
|
||||
* the transmit
|
||||
|
||||
@@ -34,7 +34,7 @@ bool ReliableRouter::shouldFilterReceived(const MeshPacket *p)
|
||||
// the original sending process.
|
||||
if (stopRetransmission(p->from, p->id)) {
|
||||
DEBUG_MSG("Someone is retransmitting for us, generate implicit ack\n");
|
||||
sendAckNak(true, p->from, p->id);
|
||||
sendAckNak(ErrorReason_NONE, p->from, p->id);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -60,13 +60,13 @@ void ReliableRouter::sniffReceived(const MeshPacket *p)
|
||||
if (p->to == ourNode) { // ignore ack/nak/want_ack packets that are not address to us (we only handle 0 hop reliability
|
||||
// - not DSR routing)
|
||||
if (p->want_ack) {
|
||||
sendAckNak(true, p->from, p->id);
|
||||
sendAckNak(ErrorReason_NONE, p->from, p->id);
|
||||
}
|
||||
|
||||
// If the payload is valid, look for ack/nak
|
||||
|
||||
PacketId ackId = p->decoded.which_ack == SubPacket_success_id_tag ? p->decoded.ack.success_id : 0;
|
||||
PacketId nakId = p->decoded.which_ack == SubPacket_fail_id_tag ? p->decoded.ack.fail_id : 0;
|
||||
PacketId ackId = p->decoded.which_ackVariant == SubPacket_success_id_tag ? p->decoded.ackVariant.success_id : 0;
|
||||
PacketId nakId = p->decoded.which_ackVariant == SubPacket_fail_id_tag ? p->decoded.ackVariant.fail_id : 0;
|
||||
|
||||
// We intentionally don't check wasSeenRecently, because it is harmless to delete non existent retransmission records
|
||||
if (ackId || nakId) {
|
||||
@@ -84,27 +84,6 @@ void ReliableRouter::sniffReceived(const MeshPacket *p)
|
||||
FloodingRouter::sniffReceived(p);
|
||||
}
|
||||
|
||||
/**
|
||||
* Send an ack or a nak packet back towards whoever sent idFrom
|
||||
*/
|
||||
void ReliableRouter::sendAckNak(bool isAck, NodeNum to, PacketId idFrom)
|
||||
{
|
||||
auto p = allocForSending();
|
||||
p->hop_limit = 0; // Assume just immediate neighbors for now
|
||||
p->to = to;
|
||||
DEBUG_MSG("Sending an ack=0x%x,to=0x%x,idFrom=0x%x,id=0x%x\n", isAck, to, idFrom, p->id);
|
||||
|
||||
if (isAck) {
|
||||
p->decoded.ack.success_id = idFrom;
|
||||
p->decoded.which_ack = SubPacket_success_id_tag;
|
||||
} else {
|
||||
p->decoded.ack.fail_id = idFrom;
|
||||
p->decoded.which_ack = SubPacket_fail_id_tag;
|
||||
}
|
||||
|
||||
sendLocal(p); // we sometimes send directly to the local node
|
||||
}
|
||||
|
||||
#define NUM_RETRANSMISSIONS 3
|
||||
|
||||
PendingPacket::PendingPacket(MeshPacket *p)
|
||||
@@ -176,7 +155,7 @@ int32_t ReliableRouter::doRetransmissions()
|
||||
if (p.numRetransmissions == 0) {
|
||||
DEBUG_MSG("Reliable send failed, returning a nak fr=0x%x,to=0x%x,id=%d\n", p.packet->from, p.packet->to,
|
||||
p.packet->id);
|
||||
sendAckNak(false, p.packet->from, p.packet->id);
|
||||
sendAckNak(ErrorReason_MAX_RETRANSMIT, p.packet->from, p.packet->id);
|
||||
// Note: we don't stop retransmission here, instead the Nak packet gets processed in sniffReceived - which
|
||||
// allows the DSR version to still be able to look at the PendingPacket
|
||||
stopRetransmission(it->first);
|
||||
|
||||
@@ -109,10 +109,6 @@ class ReliableRouter : public FloodingRouter
|
||||
PendingPacket *startRetransmission(MeshPacket *p);
|
||||
|
||||
private:
|
||||
/**
|
||||
* Send an ack or a nak packet back towards whoever sent idFrom
|
||||
*/
|
||||
void sendAckNak(bool isAck, NodeNum to, PacketId idFrom);
|
||||
|
||||
/**
|
||||
* Stop any retransmissions we are doing of the specified node/packet ID pair
|
||||
|
||||
@@ -88,7 +88,7 @@ MeshPacket *Router::allocForSending()
|
||||
{
|
||||
MeshPacket *p = packetPool.allocZeroed();
|
||||
|
||||
p->which_payload = MeshPacket_decoded_tag; // Assume payload is decoded at start.
|
||||
p->which_payloadVariant = MeshPacket_decoded_tag; // Assume payload is decoded at start.
|
||||
p->from = nodeDB.getNodeNum();
|
||||
p->to = NODENUM_BROADCAST;
|
||||
p->hop_limit = HOP_RELIABLE;
|
||||
@@ -99,6 +99,34 @@ MeshPacket *Router::allocForSending()
|
||||
return p;
|
||||
}
|
||||
|
||||
/**
|
||||
* Send an ack or a nak packet back towards whoever sent idFrom
|
||||
*/
|
||||
void Router::sendAckNak(ErrorReason err, NodeNum to, PacketId idFrom)
|
||||
{
|
||||
auto p = allocForSending();
|
||||
p->hop_limit = 0; // Assume just immediate neighbors for now
|
||||
p->to = to;
|
||||
DEBUG_MSG("Sending an err=%d,to=0x%x,idFrom=0x%x,id=0x%x\n", err, to, idFrom, p->id);
|
||||
|
||||
if (!err) {
|
||||
p->decoded.ackVariant.success_id = idFrom;
|
||||
p->decoded.which_ackVariant = SubPacket_success_id_tag;
|
||||
} else {
|
||||
p->decoded.ackVariant.fail_id = idFrom;
|
||||
p->decoded.which_ackVariant = SubPacket_fail_id_tag;
|
||||
|
||||
// Also send back the error reason
|
||||
p->decoded.which_payloadVariant = SubPacket_error_reason_tag;
|
||||
p->decoded.error_reason = err;
|
||||
}
|
||||
p->priority = MeshPacket_Priority_ACK;
|
||||
|
||||
sendLocal(p); // we sometimes send directly to the local node
|
||||
}
|
||||
|
||||
|
||||
|
||||
ErrorCode Router::sendLocal(MeshPacket *p)
|
||||
{
|
||||
// No need to deliver externally if the destination is the local node
|
||||
@@ -106,15 +134,24 @@ ErrorCode Router::sendLocal(MeshPacket *p)
|
||||
printPacket("Enqueuing local", p);
|
||||
fromRadioQueue.enqueue(p);
|
||||
return ERRNO_OK;
|
||||
}
|
||||
} else if (!iface) {
|
||||
// We must be sending to remote nodes also, fail if no interface found
|
||||
|
||||
// If we are sending a broadcast, we also treat it as if we just received it ourself
|
||||
// this allows local apps (and PCs) to see broadcasts sourced locally
|
||||
if (p->to == NODENUM_BROADCAST) {
|
||||
handleReceived(p);
|
||||
}
|
||||
// ERROR! no radio found, report failure back to the client and drop the packet
|
||||
DEBUG_MSG("Error: No interface, returning NAK and dropping packet.\n");
|
||||
sendAckNak(ErrorReason_NO_INTERFACE, p->from, p->id);
|
||||
packetPool.release(p);
|
||||
|
||||
return send(p);
|
||||
return ERRNO_NO_INTERFACES;
|
||||
} else {
|
||||
// If we are sending a broadcast, we also treat it as if we just received it ourself
|
||||
// this allows local apps (and PCs) to see broadcasts sourced locally
|
||||
if (p->to == NODENUM_BROADCAST) {
|
||||
handleReceived(p);
|
||||
}
|
||||
|
||||
return send(p);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -126,7 +163,7 @@ ErrorCode Router::send(MeshPacket *p)
|
||||
{
|
||||
assert(p->to != nodeDB.getNodeNum()); // should have already been handled by sendLocal
|
||||
|
||||
PacketId nakId = p->decoded.which_ack == SubPacket_fail_id_tag ? p->decoded.ack.fail_id : 0;
|
||||
PacketId nakId = p->decoded.which_ackVariant == SubPacket_fail_id_tag ? p->decoded.ackVariant.fail_id : 0;
|
||||
assert(
|
||||
!nakId); // I don't think we ever send 0hop naks over the wire (other than to the phone), test that assumption with assert
|
||||
|
||||
@@ -136,11 +173,11 @@ ErrorCode Router::send(MeshPacket *p)
|
||||
|
||||
// If the packet hasn't yet been encrypted, do so now (it might already be encrypted if we are just forwarding it)
|
||||
|
||||
assert(p->which_payload == MeshPacket_encrypted_tag ||
|
||||
p->which_payload == MeshPacket_decoded_tag); // I _think_ all packets should have a payload by now
|
||||
assert(p->which_payloadVariant == MeshPacket_encrypted_tag ||
|
||||
p->which_payloadVariant == MeshPacket_decoded_tag); // I _think_ all packets should have a payload by now
|
||||
|
||||
// First convert from protobufs to raw bytes
|
||||
if (p->which_payload == MeshPacket_decoded_tag) {
|
||||
if (p->which_payloadVariant == MeshPacket_decoded_tag) {
|
||||
static uint8_t bytes[MAX_RHPACKETLEN]; // we have to use a scratch buffer because a union
|
||||
|
||||
size_t numbytes = pb_encode_to_bytes(bytes, sizeof(bytes), SubPacket_fields, &p->decoded);
|
||||
@@ -151,19 +188,27 @@ ErrorCode Router::send(MeshPacket *p)
|
||||
// Copy back into the packet and set the variant type
|
||||
memcpy(p->encrypted.bytes, bytes, numbytes);
|
||||
p->encrypted.size = numbytes;
|
||||
p->which_payload = MeshPacket_encrypted_tag;
|
||||
p->which_payloadVariant = MeshPacket_encrypted_tag;
|
||||
}
|
||||
|
||||
if (iface) {
|
||||
// DEBUG_MSG("Sending packet via interface fr=0x%x,to=0x%x,id=%d\n", p->from, p->to, p->id);
|
||||
return iface->send(p);
|
||||
} else {
|
||||
assert(iface); // This should have been detected already in sendLocal (or we just received a packet from outside)
|
||||
// if (iface) {
|
||||
// DEBUG_MSG("Sending packet via interface fr=0x%x,to=0x%x,id=%d\n", p->from, p->to, p->id);
|
||||
return iface->send(p);
|
||||
/* } else {
|
||||
DEBUG_MSG("Dropping packet - no interfaces - fr=0x%x,to=0x%x,id=%d\n", p->from, p->to, p->id);
|
||||
packetPool.release(p);
|
||||
return ERRNO_NO_INTERFACES;
|
||||
}
|
||||
} */
|
||||
}
|
||||
|
||||
/** Attempt to cancel a previously sent packet. Returns true if a packet was found we could cancel */
|
||||
bool Router::cancelSending(NodeNum from, PacketId id) {
|
||||
return iface ? iface->cancelSending(from, id) : false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Every (non duplicate) packet this node receives will be passed through this method. This allows subclasses to
|
||||
* update routing tables etc... based on what we overhear (even for messages not destined to our node)
|
||||
@@ -176,10 +221,10 @@ void Router::sniffReceived(const MeshPacket *p)
|
||||
|
||||
bool Router::perhapsDecode(MeshPacket *p)
|
||||
{
|
||||
if (p->which_payload == MeshPacket_decoded_tag)
|
||||
if (p->which_payloadVariant == MeshPacket_decoded_tag)
|
||||
return true; // If packet was already decoded just return
|
||||
|
||||
assert(p->which_payload == MeshPacket_encrypted_tag);
|
||||
assert(p->which_payloadVariant == MeshPacket_encrypted_tag);
|
||||
|
||||
// FIXME - someday don't send routing packets encrypted. That would allow us to route for other channels without
|
||||
// being able to decrypt their data.
|
||||
@@ -195,7 +240,7 @@ bool Router::perhapsDecode(MeshPacket *p)
|
||||
return false;
|
||||
} else {
|
||||
// parsing was successful
|
||||
p->which_payload = MeshPacket_decoded_tag;
|
||||
p->which_payloadVariant = MeshPacket_decoded_tag;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -48,13 +48,19 @@ class Router : protected concurrency::OSThread
|
||||
virtual int32_t runOnce();
|
||||
|
||||
/**
|
||||
* Works like send, but if we are sending to the local node, we directly put the message in the receive queue
|
||||
* Works like send, but if we are sending to the local node, we directly put the message in the receive queue.
|
||||
* This is the primary method used for sending packets, because it handles both the remote and local cases.
|
||||
*
|
||||
* NOTE: This method will free the provided packet (even if we return an error code)
|
||||
*/
|
||||
ErrorCode sendLocal(MeshPacket *p);
|
||||
|
||||
/// Allocate and return a meshpacket which defaults as send to broadcast from the current node.
|
||||
/** Attempt to cancel a previously sent packet. Returns true if a packet was found we could cancel */
|
||||
bool cancelSending(NodeNum from, PacketId id);
|
||||
|
||||
/** Allocate and return a meshpacket which defaults as send to broadcast from the current node.
|
||||
* The returned packet is guaranteed to have a unique packet ID already assigned
|
||||
*/
|
||||
MeshPacket *allocForSending();
|
||||
|
||||
/**
|
||||
@@ -92,6 +98,11 @@ class Router : protected concurrency::OSThread
|
||||
*/
|
||||
bool perhapsDecode(MeshPacket *p);
|
||||
|
||||
/**
|
||||
* Send an ack or a nak packet back towards whoever sent idFrom
|
||||
*/
|
||||
void sendAckNak(ErrorReason err, NodeNum to, PacketId idFrom);
|
||||
|
||||
private:
|
||||
/**
|
||||
* Called from loop()
|
||||
|
||||
@@ -32,7 +32,7 @@ class SinglePortPlugin : public MeshPlugin
|
||||
{
|
||||
// Update our local node info with our position (even if we don't decide to update anyone else)
|
||||
MeshPacket *p = router->allocForSending();
|
||||
p->decoded.which_payload = SubPacket_data_tag;
|
||||
p->decoded.which_payloadVariant = SubPacket_data_tag;
|
||||
p->decoded.data.portnum = ourPortNum;
|
||||
|
||||
return p;
|
||||
|
||||
@@ -84,8 +84,8 @@ void StreamAPI::emitRebooted()
|
||||
{
|
||||
// In case we send a FromRadio packet
|
||||
memset(&fromRadioScratch, 0, sizeof(fromRadioScratch));
|
||||
fromRadioScratch.which_variant = FromRadio_rebooted_tag;
|
||||
fromRadioScratch.variant.rebooted = true;
|
||||
fromRadioScratch.which_payloadVariant = FromRadio_rebooted_tag;
|
||||
fromRadioScratch.rebooted = true;
|
||||
|
||||
DEBUG_MSG("Emitting reboot packet for serial shell\n");
|
||||
emitTxBuffer(pb_encode_to_bytes(txBuf + HEADER_LEN, FromRadio_size, FromRadio_fields, &fromRadioScratch));
|
||||
|
||||
@@ -80,10 +80,10 @@ extern const pb_msgdesc_t DeviceState_msg;
|
||||
#define DeviceState_fields &DeviceState_msg
|
||||
|
||||
/* Maximum encoded size of messages (where known) */
|
||||
#define DeviceState_size 6176
|
||||
#define DeviceState_size 6266
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /* extern "C" */
|
||||
#endif
|
||||
|
||||
#endif
|
||||
#endif
|
||||
@@ -57,3 +57,5 @@ PB_BIND(ToRadio, ToRadio, 2)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -11,12 +11,14 @@
|
||||
#endif
|
||||
|
||||
/* Enum definitions */
|
||||
typedef enum _RouteError {
|
||||
RouteError_NONE = 0,
|
||||
RouteError_NO_ROUTE = 1,
|
||||
RouteError_GOT_NAK = 2,
|
||||
RouteError_TIMEOUT = 3
|
||||
} RouteError;
|
||||
typedef enum _ErrorReason {
|
||||
ErrorReason_NONE = 0,
|
||||
ErrorReason_NO_ROUTE = 1,
|
||||
ErrorReason_GOT_NAK = 2,
|
||||
ErrorReason_TIMEOUT = 3,
|
||||
ErrorReason_NO_INTERFACE = 4,
|
||||
ErrorReason_MAX_RETRANSMIT = 5
|
||||
} ErrorReason;
|
||||
|
||||
typedef enum _Constants {
|
||||
Constants_Unused = 0,
|
||||
@@ -35,8 +37,29 @@ typedef enum _RegionCode {
|
||||
RegionCode_TW = 8
|
||||
} RegionCode;
|
||||
|
||||
typedef enum _ChargeCurrent {
|
||||
ChargeCurrent_MAUnset = 0,
|
||||
ChargeCurrent_MA100 = 1,
|
||||
ChargeCurrent_MA190 = 2,
|
||||
ChargeCurrent_MA280 = 3,
|
||||
ChargeCurrent_MA360 = 4,
|
||||
ChargeCurrent_MA450 = 5,
|
||||
ChargeCurrent_MA550 = 6,
|
||||
ChargeCurrent_MA630 = 7,
|
||||
ChargeCurrent_MA700 = 8,
|
||||
ChargeCurrent_MA780 = 9,
|
||||
ChargeCurrent_MA880 = 10,
|
||||
ChargeCurrent_MA960 = 11,
|
||||
ChargeCurrent_MA1000 = 12,
|
||||
ChargeCurrent_MA1080 = 13,
|
||||
ChargeCurrent_MA1160 = 14,
|
||||
ChargeCurrent_MA1240 = 15,
|
||||
ChargeCurrent_MA1320 = 16
|
||||
} ChargeCurrent;
|
||||
|
||||
typedef enum _GpsOperation {
|
||||
GpsOperation_GpsOpUnset = 0,
|
||||
GpsOperation_GpsOpStationary = 1,
|
||||
GpsOperation_GpsOpMobile = 2,
|
||||
GpsOperation_GpsOpTimeOnly = 3,
|
||||
GpsOperation_GpsOpDisabled = 4
|
||||
@@ -56,9 +79,20 @@ typedef enum _CriticalErrorCode {
|
||||
CriticalErrorCode_Unspecified = 4,
|
||||
CriticalErrorCode_UBloxInitFailed = 5,
|
||||
CriticalErrorCode_NoAXP192 = 6,
|
||||
CriticalErrorCode_InvalidRadioSetting = 7
|
||||
CriticalErrorCode_InvalidRadioSetting = 7,
|
||||
CriticalErrorCode_TransmitFailed = 8
|
||||
} CriticalErrorCode;
|
||||
|
||||
typedef enum _MeshPacket_Priority {
|
||||
MeshPacket_Priority_UNSET = 0,
|
||||
MeshPacket_Priority_MIN = 1,
|
||||
MeshPacket_Priority_BACKGROUND = 10,
|
||||
MeshPacket_Priority_DEFAULT = 64,
|
||||
MeshPacket_Priority_RELIABLE = 70,
|
||||
MeshPacket_Priority_ACK = 120,
|
||||
MeshPacket_Priority_MAX = 127
|
||||
} MeshPacket_Priority;
|
||||
|
||||
typedef enum _ChannelSettings_ModemConfig {
|
||||
ChannelSettings_ModemConfig_Bw125Cr45Sf128 = 0,
|
||||
ChannelSettings_ModemConfig_Bw500Cr45Sf128 = 1,
|
||||
@@ -145,6 +179,7 @@ typedef struct _RadioConfig_UserPreferences {
|
||||
char wifi_password[64];
|
||||
bool wifi_ap_mode;
|
||||
RegionCode region;
|
||||
ChargeCurrent charge_current;
|
||||
LocationSharing location_share;
|
||||
GpsOperation gps_operation;
|
||||
uint32_t gps_update_interval;
|
||||
@@ -156,6 +191,23 @@ typedef struct _RadioConfig_UserPreferences {
|
||||
bool debug_log_enabled;
|
||||
pb_size_t ignore_incoming_count;
|
||||
uint32_t ignore_incoming[3];
|
||||
bool serialplugin_enabled;
|
||||
bool serialplugin_echo;
|
||||
uint32_t serialplugin_rxd;
|
||||
uint32_t serialplugin_txd;
|
||||
uint32_t serialplugin_timeout;
|
||||
uint32_t serialplugin_mode;
|
||||
bool ext_notification_plugin_enabled;
|
||||
uint32_t ext_notification_plugin_output_ms;
|
||||
uint32_t ext_notification_plugin_output;
|
||||
bool ext_notification_plugin_active;
|
||||
bool ext_notification_plugin_alert_message;
|
||||
bool ext_notification_plugin_alert_bell;
|
||||
bool range_test_plugin_enabled;
|
||||
uint32_t range_test_plugin_sender;
|
||||
bool range_test_plugin_save;
|
||||
bool store_forward_plugin_enabled;
|
||||
uint32_t store_forward_plugin_records;
|
||||
} RadioConfig_UserPreferences;
|
||||
|
||||
typedef struct _RouteDiscovery {
|
||||
@@ -188,23 +240,23 @@ typedef struct _RadioConfig {
|
||||
} RadioConfig;
|
||||
|
||||
typedef struct _SubPacket {
|
||||
pb_size_t which_payload;
|
||||
pb_size_t which_payloadVariant;
|
||||
union {
|
||||
Position position;
|
||||
Data data;
|
||||
User user;
|
||||
RouteDiscovery route_request;
|
||||
RouteDiscovery route_reply;
|
||||
RouteError route_error;
|
||||
ErrorReason error_reason;
|
||||
};
|
||||
uint32_t original_id;
|
||||
bool want_response;
|
||||
uint32_t dest;
|
||||
pb_size_t which_ack;
|
||||
pb_size_t which_ackVariant;
|
||||
union {
|
||||
uint32_t success_id;
|
||||
uint32_t fail_id;
|
||||
} ack;
|
||||
} ackVariant;
|
||||
uint32_t source;
|
||||
} SubPacket;
|
||||
|
||||
@@ -212,7 +264,7 @@ typedef PB_BYTES_ARRAY_T(256) MeshPacket_encrypted_t;
|
||||
typedef struct _MeshPacket {
|
||||
uint32_t from;
|
||||
uint32_t to;
|
||||
pb_size_t which_payload;
|
||||
pb_size_t which_payloadVariant;
|
||||
union {
|
||||
SubPacket decoded;
|
||||
MeshPacket_encrypted_t encrypted;
|
||||
@@ -223,11 +275,12 @@ typedef struct _MeshPacket {
|
||||
uint32_t rx_time;
|
||||
uint32_t hop_limit;
|
||||
bool want_ack;
|
||||
MeshPacket_Priority priority;
|
||||
} MeshPacket;
|
||||
|
||||
typedef struct _FromRadio {
|
||||
uint32_t num;
|
||||
pb_size_t which_variant;
|
||||
pb_size_t which_payloadVariant;
|
||||
union {
|
||||
MeshPacket packet;
|
||||
MyNodeInfo my_info;
|
||||
@@ -237,25 +290,25 @@ typedef struct _FromRadio {
|
||||
uint32_t config_complete_id;
|
||||
bool rebooted;
|
||||
ChannelSettings channel;
|
||||
} variant;
|
||||
};
|
||||
} FromRadio;
|
||||
|
||||
typedef struct _ToRadio {
|
||||
pb_size_t which_variant;
|
||||
pb_size_t which_payloadVariant;
|
||||
union {
|
||||
MeshPacket packet;
|
||||
uint32_t want_config_id;
|
||||
RadioConfig set_radio;
|
||||
User set_owner;
|
||||
ChannelSettings set_channel;
|
||||
} variant;
|
||||
};
|
||||
} ToRadio;
|
||||
|
||||
|
||||
/* Helper constants for enums */
|
||||
#define _RouteError_MIN RouteError_NONE
|
||||
#define _RouteError_MAX RouteError_TIMEOUT
|
||||
#define _RouteError_ARRAYSIZE ((RouteError)(RouteError_TIMEOUT+1))
|
||||
#define _ErrorReason_MIN ErrorReason_NONE
|
||||
#define _ErrorReason_MAX ErrorReason_MAX_RETRANSMIT
|
||||
#define _ErrorReason_ARRAYSIZE ((ErrorReason)(ErrorReason_MAX_RETRANSMIT+1))
|
||||
|
||||
#define _Constants_MIN Constants_Unused
|
||||
#define _Constants_MAX Constants_DATA_PAYLOAD_LEN
|
||||
@@ -265,6 +318,10 @@ typedef struct _ToRadio {
|
||||
#define _RegionCode_MAX RegionCode_TW
|
||||
#define _RegionCode_ARRAYSIZE ((RegionCode)(RegionCode_TW+1))
|
||||
|
||||
#define _ChargeCurrent_MIN ChargeCurrent_MAUnset
|
||||
#define _ChargeCurrent_MAX ChargeCurrent_MA1320
|
||||
#define _ChargeCurrent_ARRAYSIZE ((ChargeCurrent)(ChargeCurrent_MA1320+1))
|
||||
|
||||
#define _GpsOperation_MIN GpsOperation_GpsOpUnset
|
||||
#define _GpsOperation_MAX GpsOperation_GpsOpDisabled
|
||||
#define _GpsOperation_ARRAYSIZE ((GpsOperation)(GpsOperation_GpsOpDisabled+1))
|
||||
@@ -274,8 +331,12 @@ typedef struct _ToRadio {
|
||||
#define _LocationSharing_ARRAYSIZE ((LocationSharing)(LocationSharing_LocDisabled+1))
|
||||
|
||||
#define _CriticalErrorCode_MIN CriticalErrorCode_None
|
||||
#define _CriticalErrorCode_MAX CriticalErrorCode_InvalidRadioSetting
|
||||
#define _CriticalErrorCode_ARRAYSIZE ((CriticalErrorCode)(CriticalErrorCode_InvalidRadioSetting+1))
|
||||
#define _CriticalErrorCode_MAX CriticalErrorCode_TransmitFailed
|
||||
#define _CriticalErrorCode_ARRAYSIZE ((CriticalErrorCode)(CriticalErrorCode_TransmitFailed+1))
|
||||
|
||||
#define _MeshPacket_Priority_MIN MeshPacket_Priority_UNSET
|
||||
#define _MeshPacket_Priority_MAX MeshPacket_Priority_MAX
|
||||
#define _MeshPacket_Priority_ARRAYSIZE ((MeshPacket_Priority)(MeshPacket_Priority_MAX+1))
|
||||
|
||||
#define _ChannelSettings_ModemConfig_MIN ChannelSettings_ModemConfig_Bw125Cr45Sf128
|
||||
#define _ChannelSettings_ModemConfig_MAX ChannelSettings_ModemConfig_Bw125Cr48Sf4096
|
||||
@@ -296,10 +357,10 @@ extern "C" {
|
||||
#define User_init_default {"", "", "", {0}}
|
||||
#define RouteDiscovery_init_default {0, {0, 0, 0, 0, 0, 0, 0, 0}}
|
||||
#define SubPacket_init_default {0, {Position_init_default}, 0, 0, 0, 0, {0}, 0}
|
||||
#define MeshPacket_init_default {0, 0, 0, {SubPacket_init_default}, 0, 0, 0, 0, 0, 0}
|
||||
#define MeshPacket_init_default {0, 0, 0, {SubPacket_init_default}, 0, 0, 0, 0, 0, 0, _MeshPacket_Priority_MIN}
|
||||
#define ChannelSettings_init_default {0, _ChannelSettings_ModemConfig_MIN, {0, {0}}, "", 0, 0, 0, 0, 0, 0, 0}
|
||||
#define RadioConfig_init_default {false, RadioConfig_UserPreferences_init_default, false, ChannelSettings_init_default}
|
||||
#define RadioConfig_UserPreferences_init_default {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, "", "", 0, _RegionCode_MIN, _LocationSharing_MIN, _GpsOperation_MIN, 0, 0, 0, 0, 0, 0, 0, 0, {0, 0, 0}}
|
||||
#define RadioConfig_UserPreferences_init_default {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, "", "", 0, _RegionCode_MIN, _ChargeCurrent_MIN, _LocationSharing_MIN, _GpsOperation_MIN, 0, 0, 0, 0, 0, 0, 0, 0, {0, 0, 0}, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
|
||||
#define NodeInfo_init_default {0, false, User_init_default, false, Position_init_default, 0, 0}
|
||||
#define MyNodeInfo_init_default {0, 0, 0, "", "", "", _CriticalErrorCode_MIN, 0, 0, 0, 0, 0, 0, 0}
|
||||
#define LogRecord_init_default {"", 0, "", _LogRecord_Level_MIN}
|
||||
@@ -310,10 +371,10 @@ extern "C" {
|
||||
#define User_init_zero {"", "", "", {0}}
|
||||
#define RouteDiscovery_init_zero {0, {0, 0, 0, 0, 0, 0, 0, 0}}
|
||||
#define SubPacket_init_zero {0, {Position_init_zero}, 0, 0, 0, 0, {0}, 0}
|
||||
#define MeshPacket_init_zero {0, 0, 0, {SubPacket_init_zero}, 0, 0, 0, 0, 0, 0}
|
||||
#define MeshPacket_init_zero {0, 0, 0, {SubPacket_init_zero}, 0, 0, 0, 0, 0, 0, _MeshPacket_Priority_MIN}
|
||||
#define ChannelSettings_init_zero {0, _ChannelSettings_ModemConfig_MIN, {0, {0}}, "", 0, 0, 0, 0, 0, 0, 0}
|
||||
#define RadioConfig_init_zero {false, RadioConfig_UserPreferences_init_zero, false, ChannelSettings_init_zero}
|
||||
#define RadioConfig_UserPreferences_init_zero {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, "", "", 0, _RegionCode_MIN, _LocationSharing_MIN, _GpsOperation_MIN, 0, 0, 0, 0, 0, 0, 0, 0, {0, 0, 0}}
|
||||
#define RadioConfig_UserPreferences_init_zero {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, "", "", 0, _RegionCode_MIN, _ChargeCurrent_MIN, _LocationSharing_MIN, _GpsOperation_MIN, 0, 0, 0, 0, 0, 0, 0, 0, {0, 0, 0}, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
|
||||
#define NodeInfo_init_zero {0, false, User_init_zero, false, Position_init_zero, 0, 0}
|
||||
#define MyNodeInfo_init_zero {0, 0, 0, "", "", "", _CriticalErrorCode_MIN, 0, 0, 0, 0, 0, 0, 0}
|
||||
#define LogRecord_init_zero {"", 0, "", _LogRecord_Level_MIN}
|
||||
@@ -371,6 +432,7 @@ extern "C" {
|
||||
#define RadioConfig_UserPreferences_wifi_password_tag 13
|
||||
#define RadioConfig_UserPreferences_wifi_ap_mode_tag 14
|
||||
#define RadioConfig_UserPreferences_region_tag 15
|
||||
#define RadioConfig_UserPreferences_charge_current_tag 16
|
||||
#define RadioConfig_UserPreferences_location_share_tag 32
|
||||
#define RadioConfig_UserPreferences_gps_operation_tag 33
|
||||
#define RadioConfig_UserPreferences_gps_update_interval_tag 34
|
||||
@@ -381,6 +443,23 @@ extern "C" {
|
||||
#define RadioConfig_UserPreferences_factory_reset_tag 100
|
||||
#define RadioConfig_UserPreferences_debug_log_enabled_tag 101
|
||||
#define RadioConfig_UserPreferences_ignore_incoming_tag 103
|
||||
#define RadioConfig_UserPreferences_serialplugin_enabled_tag 120
|
||||
#define RadioConfig_UserPreferences_serialplugin_echo_tag 121
|
||||
#define RadioConfig_UserPreferences_serialplugin_rxd_tag 122
|
||||
#define RadioConfig_UserPreferences_serialplugin_txd_tag 123
|
||||
#define RadioConfig_UserPreferences_serialplugin_timeout_tag 124
|
||||
#define RadioConfig_UserPreferences_serialplugin_mode_tag 125
|
||||
#define RadioConfig_UserPreferences_ext_notification_plugin_enabled_tag 126
|
||||
#define RadioConfig_UserPreferences_ext_notification_plugin_output_ms_tag 127
|
||||
#define RadioConfig_UserPreferences_ext_notification_plugin_output_tag 128
|
||||
#define RadioConfig_UserPreferences_ext_notification_plugin_active_tag 129
|
||||
#define RadioConfig_UserPreferences_ext_notification_plugin_alert_message_tag 130
|
||||
#define RadioConfig_UserPreferences_ext_notification_plugin_alert_bell_tag 131
|
||||
#define RadioConfig_UserPreferences_range_test_plugin_enabled_tag 132
|
||||
#define RadioConfig_UserPreferences_range_test_plugin_sender_tag 133
|
||||
#define RadioConfig_UserPreferences_range_test_plugin_save_tag 134
|
||||
#define RadioConfig_UserPreferences_store_forward_plugin_enabled_tag 136
|
||||
#define RadioConfig_UserPreferences_store_forward_plugin_records_tag 137
|
||||
#define RouteDiscovery_route_tag 2
|
||||
#define User_id_tag 1
|
||||
#define User_long_name_tag 2
|
||||
@@ -398,7 +477,7 @@ extern "C" {
|
||||
#define SubPacket_user_tag 4
|
||||
#define SubPacket_route_request_tag 6
|
||||
#define SubPacket_route_reply_tag 7
|
||||
#define SubPacket_route_error_tag 13
|
||||
#define SubPacket_error_reason_tag 13
|
||||
#define SubPacket_original_id_tag 2
|
||||
#define SubPacket_want_response_tag 5
|
||||
#define SubPacket_dest_tag 9
|
||||
@@ -415,6 +494,7 @@ extern "C" {
|
||||
#define MeshPacket_rx_time_tag 9
|
||||
#define MeshPacket_hop_limit_tag 10
|
||||
#define MeshPacket_want_ack_tag 11
|
||||
#define MeshPacket_priority_tag 12
|
||||
#define FromRadio_num_tag 1
|
||||
#define FromRadio_packet_tag 2
|
||||
#define FromRadio_my_info_tag 3
|
||||
@@ -460,40 +540,41 @@ X(a, STATIC, REPEATED, INT32, route, 2)
|
||||
#define RouteDiscovery_DEFAULT NULL
|
||||
|
||||
#define SubPacket_FIELDLIST(X, a) \
|
||||
X(a, STATIC, ONEOF, MESSAGE, (payload,position,position), 1) \
|
||||
X(a, STATIC, ONEOF, MESSAGE, (payload,data,data), 3) \
|
||||
X(a, STATIC, ONEOF, MESSAGE, (payload,user,user), 4) \
|
||||
X(a, STATIC, ONEOF, MESSAGE, (payload,route_request,route_request), 6) \
|
||||
X(a, STATIC, ONEOF, MESSAGE, (payload,route_reply,route_reply), 7) \
|
||||
X(a, STATIC, ONEOF, UENUM, (payload,route_error,route_error), 13) \
|
||||
X(a, STATIC, ONEOF, MESSAGE, (payloadVariant,position,position), 1) \
|
||||
X(a, STATIC, ONEOF, MESSAGE, (payloadVariant,data,data), 3) \
|
||||
X(a, STATIC, ONEOF, MESSAGE, (payloadVariant,user,user), 4) \
|
||||
X(a, STATIC, ONEOF, MESSAGE, (payloadVariant,route_request,route_request), 6) \
|
||||
X(a, STATIC, ONEOF, MESSAGE, (payloadVariant,route_reply,route_reply), 7) \
|
||||
X(a, STATIC, ONEOF, UENUM, (payloadVariant,error_reason,error_reason), 13) \
|
||||
X(a, STATIC, SINGULAR, UINT32, original_id, 2) \
|
||||
X(a, STATIC, SINGULAR, BOOL, want_response, 5) \
|
||||
X(a, STATIC, SINGULAR, UINT32, dest, 9) \
|
||||
X(a, STATIC, ONEOF, UINT32, (ack,success_id,ack.success_id), 10) \
|
||||
X(a, STATIC, ONEOF, UINT32, (ack,fail_id,ack.fail_id), 11) \
|
||||
X(a, STATIC, ONEOF, UINT32, (ackVariant,success_id,ackVariant.success_id), 10) \
|
||||
X(a, STATIC, ONEOF, UINT32, (ackVariant,fail_id,ackVariant.fail_id), 11) \
|
||||
X(a, STATIC, SINGULAR, UINT32, source, 12)
|
||||
#define SubPacket_CALLBACK NULL
|
||||
#define SubPacket_DEFAULT NULL
|
||||
#define SubPacket_payload_position_MSGTYPE Position
|
||||
#define SubPacket_payload_data_MSGTYPE Data
|
||||
#define SubPacket_payload_user_MSGTYPE User
|
||||
#define SubPacket_payload_route_request_MSGTYPE RouteDiscovery
|
||||
#define SubPacket_payload_route_reply_MSGTYPE RouteDiscovery
|
||||
#define SubPacket_payloadVariant_position_MSGTYPE Position
|
||||
#define SubPacket_payloadVariant_data_MSGTYPE Data
|
||||
#define SubPacket_payloadVariant_user_MSGTYPE User
|
||||
#define SubPacket_payloadVariant_route_request_MSGTYPE RouteDiscovery
|
||||
#define SubPacket_payloadVariant_route_reply_MSGTYPE RouteDiscovery
|
||||
|
||||
#define MeshPacket_FIELDLIST(X, a) \
|
||||
X(a, STATIC, SINGULAR, UINT32, from, 1) \
|
||||
X(a, STATIC, SINGULAR, UINT32, to, 2) \
|
||||
X(a, STATIC, ONEOF, MESSAGE, (payload,decoded,decoded), 3) \
|
||||
X(a, STATIC, ONEOF, BYTES, (payload,encrypted,encrypted), 8) \
|
||||
X(a, STATIC, ONEOF, MESSAGE, (payloadVariant,decoded,decoded), 3) \
|
||||
X(a, STATIC, ONEOF, BYTES, (payloadVariant,encrypted,encrypted), 8) \
|
||||
X(a, STATIC, SINGULAR, UINT32, channel_index, 4) \
|
||||
X(a, STATIC, SINGULAR, UINT32, id, 6) \
|
||||
X(a, STATIC, SINGULAR, FLOAT, rx_snr, 7) \
|
||||
X(a, STATIC, SINGULAR, FIXED32, rx_time, 9) \
|
||||
X(a, STATIC, SINGULAR, UINT32, hop_limit, 10) \
|
||||
X(a, STATIC, SINGULAR, BOOL, want_ack, 11)
|
||||
X(a, STATIC, SINGULAR, BOOL, want_ack, 11) \
|
||||
X(a, STATIC, SINGULAR, UENUM, priority, 12)
|
||||
#define MeshPacket_CALLBACK NULL
|
||||
#define MeshPacket_DEFAULT NULL
|
||||
#define MeshPacket_payload_decoded_MSGTYPE SubPacket
|
||||
#define MeshPacket_payloadVariant_decoded_MSGTYPE SubPacket
|
||||
|
||||
#define ChannelSettings_FIELDLIST(X, a) \
|
||||
X(a, STATIC, SINGULAR, INT32, tx_power, 1) \
|
||||
@@ -533,6 +614,7 @@ X(a, STATIC, SINGULAR, STRING, wifi_ssid, 12) \
|
||||
X(a, STATIC, SINGULAR, STRING, wifi_password, 13) \
|
||||
X(a, STATIC, SINGULAR, BOOL, wifi_ap_mode, 14) \
|
||||
X(a, STATIC, SINGULAR, UENUM, region, 15) \
|
||||
X(a, STATIC, SINGULAR, UENUM, charge_current, 16) \
|
||||
X(a, STATIC, SINGULAR, UENUM, location_share, 32) \
|
||||
X(a, STATIC, SINGULAR, UENUM, gps_operation, 33) \
|
||||
X(a, STATIC, SINGULAR, UINT32, gps_update_interval, 34) \
|
||||
@@ -542,7 +624,24 @@ X(a, STATIC, SINGULAR, BOOL, is_low_power, 38) \
|
||||
X(a, STATIC, SINGULAR, BOOL, fixed_position, 39) \
|
||||
X(a, STATIC, SINGULAR, BOOL, factory_reset, 100) \
|
||||
X(a, STATIC, SINGULAR, BOOL, debug_log_enabled, 101) \
|
||||
X(a, STATIC, REPEATED, UINT32, ignore_incoming, 103)
|
||||
X(a, STATIC, REPEATED, UINT32, ignore_incoming, 103) \
|
||||
X(a, STATIC, SINGULAR, BOOL, serialplugin_enabled, 120) \
|
||||
X(a, STATIC, SINGULAR, BOOL, serialplugin_echo, 121) \
|
||||
X(a, STATIC, SINGULAR, UINT32, serialplugin_rxd, 122) \
|
||||
X(a, STATIC, SINGULAR, UINT32, serialplugin_txd, 123) \
|
||||
X(a, STATIC, SINGULAR, UINT32, serialplugin_timeout, 124) \
|
||||
X(a, STATIC, SINGULAR, UINT32, serialplugin_mode, 125) \
|
||||
X(a, STATIC, SINGULAR, BOOL, ext_notification_plugin_enabled, 126) \
|
||||
X(a, STATIC, SINGULAR, UINT32, ext_notification_plugin_output_ms, 127) \
|
||||
X(a, STATIC, SINGULAR, UINT32, ext_notification_plugin_output, 128) \
|
||||
X(a, STATIC, SINGULAR, BOOL, ext_notification_plugin_active, 129) \
|
||||
X(a, STATIC, SINGULAR, BOOL, ext_notification_plugin_alert_message, 130) \
|
||||
X(a, STATIC, SINGULAR, BOOL, ext_notification_plugin_alert_bell, 131) \
|
||||
X(a, STATIC, SINGULAR, BOOL, range_test_plugin_enabled, 132) \
|
||||
X(a, STATIC, SINGULAR, UINT32, range_test_plugin_sender, 133) \
|
||||
X(a, STATIC, SINGULAR, BOOL, range_test_plugin_save, 134) \
|
||||
X(a, STATIC, SINGULAR, BOOL, store_forward_plugin_enabled, 136) \
|
||||
X(a, STATIC, SINGULAR, UINT32, store_forward_plugin_records, 137)
|
||||
#define RadioConfig_UserPreferences_CALLBACK NULL
|
||||
#define RadioConfig_UserPreferences_DEFAULT NULL
|
||||
|
||||
@@ -585,35 +684,35 @@ X(a, STATIC, SINGULAR, UENUM, level, 4)
|
||||
|
||||
#define FromRadio_FIELDLIST(X, a) \
|
||||
X(a, STATIC, SINGULAR, UINT32, num, 1) \
|
||||
X(a, STATIC, ONEOF, MESSAGE, (variant,packet,variant.packet), 2) \
|
||||
X(a, STATIC, ONEOF, MESSAGE, (variant,my_info,variant.my_info), 3) \
|
||||
X(a, STATIC, ONEOF, MESSAGE, (variant,node_info,variant.node_info), 4) \
|
||||
X(a, STATIC, ONEOF, MESSAGE, (variant,radio,variant.radio), 6) \
|
||||
X(a, STATIC, ONEOF, MESSAGE, (variant,log_record,variant.log_record), 7) \
|
||||
X(a, STATIC, ONEOF, UINT32, (variant,config_complete_id,variant.config_complete_id), 8) \
|
||||
X(a, STATIC, ONEOF, BOOL, (variant,rebooted,variant.rebooted), 9) \
|
||||
X(a, STATIC, ONEOF, MESSAGE, (variant,channel,variant.channel), 10)
|
||||
X(a, STATIC, ONEOF, MESSAGE, (payloadVariant,packet,packet), 2) \
|
||||
X(a, STATIC, ONEOF, MESSAGE, (payloadVariant,my_info,my_info), 3) \
|
||||
X(a, STATIC, ONEOF, MESSAGE, (payloadVariant,node_info,node_info), 4) \
|
||||
X(a, STATIC, ONEOF, MESSAGE, (payloadVariant,radio,radio), 6) \
|
||||
X(a, STATIC, ONEOF, MESSAGE, (payloadVariant,log_record,log_record), 7) \
|
||||
X(a, STATIC, ONEOF, UINT32, (payloadVariant,config_complete_id,config_complete_id), 8) \
|
||||
X(a, STATIC, ONEOF, BOOL, (payloadVariant,rebooted,rebooted), 9) \
|
||||
X(a, STATIC, ONEOF, MESSAGE, (payloadVariant,channel,channel), 10)
|
||||
#define FromRadio_CALLBACK NULL
|
||||
#define FromRadio_DEFAULT NULL
|
||||
#define FromRadio_variant_packet_MSGTYPE MeshPacket
|
||||
#define FromRadio_variant_my_info_MSGTYPE MyNodeInfo
|
||||
#define FromRadio_variant_node_info_MSGTYPE NodeInfo
|
||||
#define FromRadio_variant_radio_MSGTYPE RadioConfig
|
||||
#define FromRadio_variant_log_record_MSGTYPE LogRecord
|
||||
#define FromRadio_variant_channel_MSGTYPE ChannelSettings
|
||||
#define FromRadio_payloadVariant_packet_MSGTYPE MeshPacket
|
||||
#define FromRadio_payloadVariant_my_info_MSGTYPE MyNodeInfo
|
||||
#define FromRadio_payloadVariant_node_info_MSGTYPE NodeInfo
|
||||
#define FromRadio_payloadVariant_radio_MSGTYPE RadioConfig
|
||||
#define FromRadio_payloadVariant_log_record_MSGTYPE LogRecord
|
||||
#define FromRadio_payloadVariant_channel_MSGTYPE ChannelSettings
|
||||
|
||||
#define ToRadio_FIELDLIST(X, a) \
|
||||
X(a, STATIC, ONEOF, MESSAGE, (variant,packet,variant.packet), 1) \
|
||||
X(a, STATIC, ONEOF, UINT32, (variant,want_config_id,variant.want_config_id), 100) \
|
||||
X(a, STATIC, ONEOF, MESSAGE, (variant,set_radio,variant.set_radio), 101) \
|
||||
X(a, STATIC, ONEOF, MESSAGE, (variant,set_owner,variant.set_owner), 102) \
|
||||
X(a, STATIC, ONEOF, MESSAGE, (variant,set_channel,variant.set_channel), 103)
|
||||
X(a, STATIC, ONEOF, MESSAGE, (payloadVariant,packet,packet), 1) \
|
||||
X(a, STATIC, ONEOF, UINT32, (payloadVariant,want_config_id,want_config_id), 100) \
|
||||
X(a, STATIC, ONEOF, MESSAGE, (payloadVariant,set_radio,set_radio), 101) \
|
||||
X(a, STATIC, ONEOF, MESSAGE, (payloadVariant,set_owner,set_owner), 102) \
|
||||
X(a, STATIC, ONEOF, MESSAGE, (payloadVariant,set_channel,set_channel), 103)
|
||||
#define ToRadio_CALLBACK NULL
|
||||
#define ToRadio_DEFAULT NULL
|
||||
#define ToRadio_variant_packet_MSGTYPE MeshPacket
|
||||
#define ToRadio_variant_set_radio_MSGTYPE RadioConfig
|
||||
#define ToRadio_variant_set_owner_MSGTYPE User
|
||||
#define ToRadio_variant_set_channel_MSGTYPE ChannelSettings
|
||||
#define ToRadio_payloadVariant_packet_MSGTYPE MeshPacket
|
||||
#define ToRadio_payloadVariant_set_radio_MSGTYPE RadioConfig
|
||||
#define ToRadio_payloadVariant_set_owner_MSGTYPE User
|
||||
#define ToRadio_payloadVariant_set_channel_MSGTYPE ChannelSettings
|
||||
|
||||
extern const pb_msgdesc_t Position_msg;
|
||||
extern const pb_msgdesc_t Data_msg;
|
||||
@@ -652,15 +751,15 @@ extern const pb_msgdesc_t ToRadio_msg;
|
||||
#define User_size 72
|
||||
#define RouteDiscovery_size 88
|
||||
#define SubPacket_size 275
|
||||
#define MeshPacket_size 320
|
||||
#define MeshPacket_size 322
|
||||
#define ChannelSettings_size 95
|
||||
#define RadioConfig_size 319
|
||||
#define RadioConfig_UserPreferences_size 219
|
||||
#define RadioConfig_size 405
|
||||
#define RadioConfig_UserPreferences_size 305
|
||||
#define NodeInfo_size 132
|
||||
#define MyNodeInfo_size 106
|
||||
#define LogRecord_size 81
|
||||
#define FromRadio_size 329
|
||||
#define ToRadio_size 323
|
||||
#define FromRadio_size 414
|
||||
#define ToRadio_size 409
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /* extern "C" */
|
||||
|
||||
@@ -18,13 +18,17 @@ typedef enum _PortNum {
|
||||
PortNum_NODEINFO_APP = 4,
|
||||
PortNum_REPLY_APP = 32,
|
||||
PortNum_IP_TUNNEL_APP = 33,
|
||||
PortNum_PRIVATE_APP = 256
|
||||
PortNum_SERIAL_APP = 64,
|
||||
PortNum_STORE_FORWARD_APP = 65,
|
||||
PortNum_RANGE_TEST_APP = 66,
|
||||
PortNum_PRIVATE_APP = 256,
|
||||
PortNum_ATAK_FORWARDER = 257
|
||||
} PortNum;
|
||||
|
||||
/* Helper constants for enums */
|
||||
#define _PortNum_MIN PortNum_UNKNOWN_APP
|
||||
#define _PortNum_MAX PortNum_PRIVATE_APP
|
||||
#define _PortNum_ARRAYSIZE ((PortNum)(PortNum_PRIVATE_APP+1))
|
||||
#define _PortNum_MAX PortNum_ATAK_FORWARDER
|
||||
#define _PortNum_ARRAYSIZE ((PortNum)(PortNum_ATAK_FORWARDER+1))
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
46
src/mesh/http/ContentHandler.h
Normal file
46
src/mesh/http/ContentHandler.h
Normal file
@@ -0,0 +1,46 @@
|
||||
#pragma once
|
||||
|
||||
void registerHandlers(HTTPServer *insecureServer, HTTPSServer *secureServer);
|
||||
|
||||
// Declare some handler functions for the various URLs on the server
|
||||
void handleAPIv1FromRadio(HTTPRequest *req, HTTPResponse *res);
|
||||
void handleAPIv1ToRadio(HTTPRequest *req, HTTPResponse *res);
|
||||
void handleStyleCSS(HTTPRequest *req, HTTPResponse *res);
|
||||
void handleHotspot(HTTPRequest *req, HTTPResponse *res);
|
||||
void handleRoot(HTTPRequest *req, HTTPResponse *res);
|
||||
void handleStaticBrowse(HTTPRequest *req, HTTPResponse *res);
|
||||
void handleStaticPost(HTTPRequest *req, HTTPResponse *res);
|
||||
void handleStatic(HTTPRequest *req, HTTPResponse *res);
|
||||
void handleRestart(HTTPRequest *req, HTTPResponse *res);
|
||||
void handle404(HTTPRequest *req, HTTPResponse *res);
|
||||
void handleFormUpload(HTTPRequest *req, HTTPResponse *res);
|
||||
void handleScanNetworks(HTTPRequest *req, HTTPResponse *res);
|
||||
void handleSpiffsBrowseStatic(HTTPRequest *req, HTTPResponse *res);
|
||||
void handleSpiffsDeleteStatic(HTTPRequest *req, HTTPResponse *res);
|
||||
void handleBlinkLED(HTTPRequest *req, HTTPResponse *res);
|
||||
void handleReport(HTTPRequest *req, HTTPResponse *res);
|
||||
void handleFavicon(HTTPRequest *req, HTTPResponse *res);
|
||||
|
||||
void middlewareSpeedUp240(HTTPRequest *req, HTTPResponse *res, std::function<void()> next);
|
||||
void middlewareSpeedUp160(HTTPRequest *req, HTTPResponse *res, std::function<void()> next);
|
||||
void middlewareSession(HTTPRequest *req, HTTPResponse *res, std::function<void()> next);
|
||||
|
||||
uint32_t getTimeSpeedUp();
|
||||
void setTimeSpeedUp();
|
||||
|
||||
|
||||
// Interface to the PhoneAPI to access the protobufs with messages
|
||||
class HttpAPI : public PhoneAPI
|
||||
{
|
||||
|
||||
public:
|
||||
// Nothing here yet
|
||||
|
||||
private:
|
||||
// Nothing here yet
|
||||
|
||||
protected:
|
||||
// Nothing here yet
|
||||
};
|
||||
|
||||
|
||||
14
src/mesh/http/ContentHelper.cpp
Normal file
14
src/mesh/http/ContentHelper.cpp
Normal file
@@ -0,0 +1,14 @@
|
||||
#include "mesh/http/ContentHelper.h"
|
||||
//#include <Arduino.h>
|
||||
//#include "main.h"
|
||||
|
||||
void replaceAll(std::string &str, const std::string &from, const std::string &to)
|
||||
{
|
||||
if (from.empty())
|
||||
return;
|
||||
size_t start_pos = 0;
|
||||
while ((start_pos = str.find(from, start_pos)) != std::string::npos) {
|
||||
str.replace(start_pos, from.length(), to);
|
||||
start_pos += to.length(); // In case 'to' contains 'from', like replacing 'x' with 'yx'
|
||||
}
|
||||
}
|
||||
8
src/mesh/http/ContentHelper.h
Normal file
8
src/mesh/http/ContentHelper.h
Normal file
@@ -0,0 +1,8 @@
|
||||
#include <Arduino.h>
|
||||
#include <functional>
|
||||
|
||||
#define BoolToString(x) ((x) ? "true" : "false")
|
||||
|
||||
|
||||
void replaceAll(std::string &str, const std::string &from, const std::string &to);
|
||||
|
||||
@@ -2,14 +2,7 @@
|
||||
#include <functional>
|
||||
|
||||
/*
|
||||
Steps:
|
||||
- Compress the .js file to .js.gz
|
||||
- Convert to hex:
|
||||
http://tomeko.net/online_tools/file_to_hex.php?lang=en
|
||||
- Paste into the array
|
||||
- Note the filesize of your .gz file and write the file
|
||||
size into the length int.
|
||||
|
||||
This file contains static content.
|
||||
*/
|
||||
|
||||
// Length of the binary data
|
||||
231
src/mesh/http/WebServer.cpp
Normal file
231
src/mesh/http/WebServer.cpp
Normal file
@@ -0,0 +1,231 @@
|
||||
#include "mesh/http/WebServer.h"
|
||||
#include "NodeDB.h"
|
||||
#include "mesh/http/WiFiAPClient.h"
|
||||
#include <HTTPBodyParser.hpp>
|
||||
#include <HTTPMultipartBodyParser.hpp>
|
||||
#include <HTTPURLEncodedBodyParser.hpp>
|
||||
|
||||
|
||||
#include <WebServer.h>
|
||||
#include <WiFi.h>
|
||||
|
||||
#ifndef NO_ESP32
|
||||
#include "esp_task_wdt.h"
|
||||
#endif
|
||||
|
||||
|
||||
// Persistant Data Storage
|
||||
#include <Preferences.h>
|
||||
Preferences prefs;
|
||||
|
||||
/*
|
||||
Including the esp32_https_server library will trigger a compile time error. I've
|
||||
tracked it down to a reoccurrance of this bug:
|
||||
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=57824
|
||||
The work around is described here:
|
||||
https://forums.xilinx.com/t5/Embedded-Development-Tools/Error-with-Standard-Libaries-in-Zynq/td-p/450032
|
||||
|
||||
Long story short is we need "#undef str" before including the esp32_https_server.
|
||||
- Jm Casler (jm@casler.org) Oct 2020
|
||||
*/
|
||||
#undef str
|
||||
|
||||
// Includes for the https server
|
||||
// https://github.com/fhessel/esp32_https_server
|
||||
#include <HTTPRequest.hpp>
|
||||
#include <HTTPResponse.hpp>
|
||||
#include <HTTPSServer.hpp>
|
||||
#include <HTTPServer.hpp>
|
||||
#include <SSLCert.hpp>
|
||||
|
||||
// The HTTPS Server comes in a separate namespace. For easier use, include it here.
|
||||
using namespace httpsserver;
|
||||
#include "mesh/http/ContentHandler.h"
|
||||
|
||||
SSLCert *cert;
|
||||
HTTPSServer *secureServer;
|
||||
HTTPServer *insecureServer;
|
||||
|
||||
|
||||
|
||||
|
||||
bool isWebServerReady = 0;
|
||||
bool isCertReady = 0;
|
||||
|
||||
|
||||
void handleWebResponse()
|
||||
{
|
||||
if (isWifiAvailable() == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (isWebServerReady) {
|
||||
// We're going to handle the DNS responder here so it
|
||||
// will be ignored by the NRF boards.
|
||||
handleDNSResponse();
|
||||
|
||||
secureServer->loop();
|
||||
insecureServer->loop();
|
||||
}
|
||||
|
||||
/*
|
||||
Slow down the CPU if we have not received a request within the last few
|
||||
seconds.
|
||||
*/
|
||||
|
||||
if (millis() - getTimeSpeedUp() >= (25 * 1000)) {
|
||||
setCpuFrequencyMhz(80);
|
||||
setTimeSpeedUp();
|
||||
}
|
||||
}
|
||||
|
||||
void taskCreateCert(void *parameter)
|
||||
{
|
||||
|
||||
prefs.begin("MeshtasticHTTPS", false);
|
||||
|
||||
// Delete the saved certs
|
||||
if (0) {
|
||||
DEBUG_MSG("Deleting any saved SSL keys ...\n");
|
||||
// prefs.clear();
|
||||
prefs.remove("PK");
|
||||
prefs.remove("cert");
|
||||
}
|
||||
|
||||
size_t pkLen = prefs.getBytesLength("PK");
|
||||
size_t certLen = prefs.getBytesLength("cert");
|
||||
|
||||
DEBUG_MSG("Checking if we have a previously saved SSL Certificate.\n");
|
||||
|
||||
if (pkLen && certLen) {
|
||||
DEBUG_MSG("Existing SSL Certificate found!\n");
|
||||
} else {
|
||||
DEBUG_MSG("Creating the certificate. This may take a while. Please wait...\n");
|
||||
yield();
|
||||
cert = new SSLCert();
|
||||
yield();
|
||||
int createCertResult = createSelfSignedCert(*cert, KEYSIZE_2048, "CN=meshtastic.local,O=Meshtastic,C=US",
|
||||
"20190101000000", "20300101000000");
|
||||
yield();
|
||||
|
||||
if (createCertResult != 0) {
|
||||
DEBUG_MSG("Creating the certificate failed\n");
|
||||
|
||||
// Serial.printf("Creating the certificate failed. Error Code = 0x%02X, check SSLCert.hpp for details",
|
||||
// createCertResult);
|
||||
// while (true)
|
||||
// delay(500);
|
||||
} else {
|
||||
DEBUG_MSG("Creating the certificate was successful\n");
|
||||
|
||||
DEBUG_MSG("Created Private Key: %d Bytes\n", cert->getPKLength());
|
||||
// for (int i = 0; i < cert->getPKLength(); i++)
|
||||
// Serial.print(cert->getPKData()[i], HEX);
|
||||
// Serial.println();
|
||||
|
||||
DEBUG_MSG("Created Certificate: %d Bytes\n", cert->getCertLength());
|
||||
// for (int i = 0; i < cert->getCertLength(); i++)
|
||||
// Serial.print(cert->getCertData()[i], HEX);
|
||||
// Serial.println();
|
||||
|
||||
prefs.putBytes("PK", (uint8_t *)cert->getPKData(), cert->getPKLength());
|
||||
prefs.putBytes("cert", (uint8_t *)cert->getCertData(), cert->getCertLength());
|
||||
}
|
||||
}
|
||||
|
||||
isCertReady = 1;
|
||||
vTaskDelete(NULL);
|
||||
}
|
||||
|
||||
void createSSLCert()
|
||||
{
|
||||
|
||||
if (isWifiAvailable() == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Create a new process just to handle creating the cert.
|
||||
// This is a workaround for Bug: https://github.com/fhessel/esp32_https_server/issues/48
|
||||
// jm@casler.org (Oct 2020)
|
||||
xTaskCreate(taskCreateCert, /* Task function. */
|
||||
"createCert", /* String with name of task. */
|
||||
16384, /* Stack size in bytes. */
|
||||
NULL, /* Parameter passed as input of the task */
|
||||
16, /* Priority of the task. */
|
||||
NULL); /* Task handle. */
|
||||
|
||||
DEBUG_MSG("Waiting for SSL Cert to be generated.\n");
|
||||
while (!isCertReady) {
|
||||
DEBUG_MSG(".");
|
||||
delay(1000);
|
||||
yield();
|
||||
esp_task_wdt_reset();
|
||||
}
|
||||
DEBUG_MSG("SSL Cert Ready!\n");
|
||||
}
|
||||
|
||||
WebServerThread *webServerThread;
|
||||
|
||||
WebServerThread::WebServerThread() : concurrency::OSThread("WebServerThread") {}
|
||||
|
||||
int32_t WebServerThread::runOnce()
|
||||
{
|
||||
// DEBUG_MSG("WebServerThread::runOnce()\n");
|
||||
handleWebResponse();
|
||||
|
||||
// Loop every 5ms.
|
||||
return (5);
|
||||
}
|
||||
|
||||
void initWebServer()
|
||||
{
|
||||
DEBUG_MSG("Initializing Web Server ...\n");
|
||||
|
||||
prefs.begin("MeshtasticHTTPS", false);
|
||||
|
||||
size_t pkLen = prefs.getBytesLength("PK");
|
||||
size_t certLen = prefs.getBytesLength("cert");
|
||||
|
||||
DEBUG_MSG("Checking if we have a previously saved SSL Certificate.\n");
|
||||
|
||||
if (pkLen && certLen) {
|
||||
|
||||
uint8_t *pkBuffer = new uint8_t[pkLen];
|
||||
prefs.getBytes("PK", pkBuffer, pkLen);
|
||||
|
||||
uint8_t *certBuffer = new uint8_t[certLen];
|
||||
prefs.getBytes("cert", certBuffer, certLen);
|
||||
|
||||
cert = new SSLCert(certBuffer, certLen, pkBuffer, pkLen);
|
||||
|
||||
DEBUG_MSG("Retrieved Private Key: %d Bytes\n", cert->getPKLength());
|
||||
// DEBUG_MSG("Retrieved Private Key: " + String(cert->getPKLength()) + " Bytes");
|
||||
// for (int i = 0; i < cert->getPKLength(); i++)
|
||||
// Serial.print(cert->getPKData()[i], HEX);
|
||||
// Serial.println();
|
||||
|
||||
DEBUG_MSG("Retrieved Certificate: %d Bytes\n", cert->getCertLength());
|
||||
// for (int i = 0; i < cert->getCertLength(); i++)
|
||||
// Serial.print(cert->getCertData()[i], HEX);
|
||||
// Serial.println();
|
||||
} else {
|
||||
DEBUG_MSG("Web Server started without SSL keys! How did this happen?\n");
|
||||
}
|
||||
|
||||
// We can now use the new certificate to setup our server as usual.
|
||||
secureServer = new HTTPSServer(cert);
|
||||
insecureServer = new HTTPServer();
|
||||
|
||||
registerHandlers(insecureServer, secureServer);
|
||||
|
||||
DEBUG_MSG("Starting Web Servers...\n");
|
||||
secureServer->start();
|
||||
insecureServer->start();
|
||||
if (secureServer->isRunning() && insecureServer->isRunning()) {
|
||||
DEBUG_MSG("HTTP and HTTPS Web Servers Ready! :-) \n");
|
||||
isWebServerReady = 1;
|
||||
} else {
|
||||
DEBUG_MSG("HTTP and HTTPS Web Servers Failed! ;-( \n");
|
||||
}
|
||||
}
|
||||
|
||||
26
src/mesh/http/WebServer.h
Normal file
26
src/mesh/http/WebServer.h
Normal file
@@ -0,0 +1,26 @@
|
||||
#pragma once
|
||||
|
||||
#include "PhoneAPI.h"
|
||||
#include "concurrency/OSThread.h"
|
||||
#include <Arduino.h>
|
||||
#include <functional>
|
||||
|
||||
void initWebServer();
|
||||
void createSSLCert();
|
||||
|
||||
|
||||
void handleWebResponse();
|
||||
|
||||
|
||||
class WebServerThread : private concurrency::OSThread
|
||||
{
|
||||
|
||||
public:
|
||||
WebServerThread();
|
||||
|
||||
protected:
|
||||
|
||||
virtual int32_t runOnce();
|
||||
};
|
||||
|
||||
extern WebServerThread *webServerThread;
|
||||
@@ -1,9 +1,9 @@
|
||||
#include "meshwifi.h"
|
||||
#include "mesh/http/WiFiAPClient.h"
|
||||
#include "NodeDB.h"
|
||||
#include "WiFiServerAPI.h"
|
||||
#include "configuration.h"
|
||||
#include "main.h"
|
||||
#include "meshwifi/meshhttp.h"
|
||||
#include "mesh/http/WebServer.h"
|
||||
#include "mesh/wifi/WiFiServerAPI.h"
|
||||
#include "target_specific.h"
|
||||
#include <DNSServer.h>
|
||||
#include <ESPmDNS.h>
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
WiFiServerAPI::WiFiServerAPI(WiFiClient &_client) : StreamAPI(&client), client(_client)
|
||||
{
|
||||
DEBUG_MSG("Incoming connection from %s\n", client.remoteIP().toString().c_str());
|
||||
DEBUG_MSG("Incoming wifi connection\n");
|
||||
}
|
||||
|
||||
WiFiServerAPI::~WiFiServerAPI()
|
||||
@@ -28,18 +28,19 @@ void WiFiServerAPI::onConnectionChanged(bool connected)
|
||||
}
|
||||
}
|
||||
|
||||
void WiFiServerAPI::loop()
|
||||
/// override close to also shutdown the TCP link
|
||||
void WiFiServerAPI::close() {
|
||||
client.stop(); // drop tcp connection
|
||||
StreamAPI::close();
|
||||
}
|
||||
|
||||
bool WiFiServerAPI::loop()
|
||||
{
|
||||
if (client.connected()) {
|
||||
StreamAPI::loop();
|
||||
} else if(isConnected) {
|
||||
// If our API link was up, shut it down
|
||||
|
||||
DEBUG_MSG("Client dropped connection, closing API client\n");
|
||||
// Note: we can't call delete here because this object includes other state
|
||||
// besides the stream API. Instead kill it later when we start a new instance
|
||||
// delete this;
|
||||
close();
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -58,15 +59,25 @@ int32_t WiFiServerPort::runOnce()
|
||||
auto client = available();
|
||||
if (client) {
|
||||
// Close any previous connection (see FIXME in header file)
|
||||
if (openAPI)
|
||||
if (openAPI) {
|
||||
DEBUG_MSG("Force closing previous TCP connection\n");
|
||||
delete openAPI;
|
||||
}
|
||||
|
||||
openAPI = new WiFiServerAPI(client);
|
||||
}
|
||||
|
||||
if (openAPI) {
|
||||
// Allow idle processing so the API can read from its incoming stream
|
||||
openAPI->loop();
|
||||
if(!openAPI->loop()) {
|
||||
// If our API link was up, shut it down
|
||||
|
||||
DEBUG_MSG("Client dropped connection, closing API client\n");
|
||||
// Note: we can't call delete here because this object includes other state
|
||||
// besides the stream API. Instead kill it later when we start a new instance
|
||||
delete openAPI;
|
||||
openAPI = NULL;
|
||||
}
|
||||
return 0; // run fast while our API server is running
|
||||
} else
|
||||
return 100; // only check occasionally for incoming connections
|
||||
@@ -18,7 +18,11 @@ class WiFiServerAPI : public StreamAPI
|
||||
|
||||
virtual ~WiFiServerAPI();
|
||||
|
||||
virtual void loop(); // Check for dropped client connections
|
||||
/// @return true if we want to keep running, or false if we are ready to be destroyed
|
||||
virtual bool loop(); // Check for dropped client connections
|
||||
|
||||
/// override close to also shutdown the TCP link
|
||||
virtual void close();
|
||||
|
||||
protected:
|
||||
/// Hookable to find out when connection changes
|
||||
@@ -1,38 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "PhoneAPI.h"
|
||||
#include <Arduino.h>
|
||||
#include <functional>
|
||||
|
||||
void initWebServer();
|
||||
void createSSLCert();
|
||||
|
||||
void handleNotFound();
|
||||
|
||||
void handleWebResponse();
|
||||
|
||||
void handleJSONChatHistory();
|
||||
|
||||
void notifyWebUI();
|
||||
|
||||
void handleHotspot();
|
||||
|
||||
void handleStyleCSS();
|
||||
void handleRoot();
|
||||
void handleScriptsScriptJS();
|
||||
void handleJSONChatHistoryDummy();
|
||||
|
||||
void replaceAll(std::string& str, const std::string& from, const std::string& to);
|
||||
|
||||
class HttpAPI : public PhoneAPI
|
||||
{
|
||||
|
||||
public:
|
||||
// Nothing here yet
|
||||
|
||||
private:
|
||||
// Nothing here yet
|
||||
|
||||
protected:
|
||||
// Nothing here yet
|
||||
};
|
||||
@@ -7,13 +7,17 @@
|
||||
#include "esp_bt.h"
|
||||
#include "host/util/util.h"
|
||||
#include "main.h"
|
||||
#include "meshwifi/meshwifi.h"
|
||||
#include "nimble/NimbleDefs.h"
|
||||
#include "services/gap/ble_svc_gap.h"
|
||||
#include "services/gatt/ble_svc_gatt.h"
|
||||
#include "sleep.h"
|
||||
#include <Arduino.h>
|
||||
#include <WiFi.h>
|
||||
|
||||
#ifndef NO_ESP32
|
||||
#include "mesh/http/WiFiAPClient.h"
|
||||
#endif
|
||||
|
||||
static bool pinShowing;
|
||||
static uint32_t doublepressed;
|
||||
|
||||
@@ -223,16 +227,14 @@ static int gap_event(struct ble_gap_event *event, void *arg)
|
||||
|
||||
if (event->passkey.params.action == BLE_SM_IOACT_DISP) {
|
||||
pkey.action = event->passkey.params.action;
|
||||
DEBUG_MSG("dp: %d now:%d\n",doublepressed, now);
|
||||
if (doublepressed > 0 && (doublepressed + (30*1000)) > now)
|
||||
{
|
||||
DEBUG_MSG("dp: %d now:%d\n", doublepressed, now);
|
||||
if (doublepressed > 0 && (doublepressed + (30 * 1000)) > now) {
|
||||
DEBUG_MSG("User has overridden passkey or no display available\n");
|
||||
pkey.passkey = defaultBLEPin;
|
||||
}
|
||||
else {
|
||||
pkey.passkey = defaultBLEPin;
|
||||
} else {
|
||||
DEBUG_MSG("Using random passkey\n");
|
||||
pkey.passkey = random(
|
||||
100000, 999999); // This is the passkey to be entered on peer - we pick a number >100,000 to ensure 6 digits
|
||||
100000, 999999); // This is the passkey to be entered on peer - we pick a number >100,000 to ensure 6 digits
|
||||
}
|
||||
DEBUG_MSG("*** Enter passkey %d on the peer side ***\n", pkey.passkey);
|
||||
|
||||
@@ -392,7 +394,6 @@ void gatt_svr_register_cb(struct ble_gatt_register_ctxt *ctxt, void *arg)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* A helper function that implements simple read and write handling for a uint32_t
|
||||
*
|
||||
@@ -446,8 +447,7 @@ int chr_readwrite8(uint8_t *v, size_t vlen, struct ble_gatt_access_ctxt *ctxt)
|
||||
if (len < vlen) {
|
||||
DEBUG_MSG("Error: wrongsized write\n");
|
||||
return BLE_ATT_ERR_UNLIKELY;
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
DEBUG_MSG("BLE writing bytes\n");
|
||||
}
|
||||
} else {
|
||||
@@ -462,7 +462,21 @@ void disablePin()
|
||||
{
|
||||
DEBUG_MSG("User Override, disabling bluetooth pin requirement\n");
|
||||
// keep track of when it was pressed, so we know it was within X seconds
|
||||
doublepressed = millis();
|
||||
|
||||
// Flash the LED
|
||||
setLed(true);
|
||||
delay(100);
|
||||
setLed(false);
|
||||
delay(100);
|
||||
setLed(true);
|
||||
delay(100);
|
||||
setLed(false);
|
||||
delay(100);
|
||||
setLed(true);
|
||||
delay(100);
|
||||
setLed(false);
|
||||
|
||||
doublepressed = millis();
|
||||
}
|
||||
|
||||
// This routine is called multiple times, once each time we come back from sleep
|
||||
@@ -545,7 +559,9 @@ void setBluetoothEnable(bool on)
|
||||
if (firstTime) {
|
||||
firstTime = 0;
|
||||
} else {
|
||||
#ifndef NO_ESP32
|
||||
initWifi(0);
|
||||
#endif
|
||||
}
|
||||
} else {
|
||||
|
||||
@@ -557,7 +573,9 @@ void setBluetoothEnable(bool on)
|
||||
*/
|
||||
|
||||
// shutdown wifi
|
||||
#ifndef NO_ESP32
|
||||
deinitWifi();
|
||||
#endif
|
||||
|
||||
// We have to totally teardown our bluetooth objects to prevent leaks
|
||||
deinitBLE();
|
||||
|
||||
@@ -16,8 +16,14 @@ static void printUsageErrorMsg(uint32_t cfsr)
|
||||
cfsr >>= SCB_CFSR_USGFAULTSR_Pos; // right shift to lsb
|
||||
if ((cfsr & (1 << 9)) != 0)
|
||||
FAULT_MSG("Divide by zero\n");
|
||||
if ((cfsr & (1 << 8)) != 0)
|
||||
else if ((cfsr & (1 << 8)) != 0)
|
||||
FAULT_MSG("Unaligned\n");
|
||||
else if ((cfsr & (1 << 1)) != 0)
|
||||
FAULT_MSG("Invalid state\n");
|
||||
else if ((cfsr & (1 << 0)) != 0)
|
||||
FAULT_MSG("Invalid instruction\n");
|
||||
else
|
||||
FAULT_MSG("FIXME add to printUsageErrorMsg!\n");
|
||||
}
|
||||
|
||||
static void printBusErrorMsg(uint32_t cfsr)
|
||||
@@ -71,8 +77,9 @@ extern "C" void HardFault_Impl(uint32_t stack[])
|
||||
|
||||
FAULT_MSG("Done with fault report - Waiting to reboot\n");
|
||||
asm volatile("bkpt #01"); // Enter the debugger if one is connected
|
||||
while (1)
|
||||
;
|
||||
|
||||
// Don't spin, so that the debugger will let the user step to next instruction
|
||||
// while (1) ;
|
||||
}
|
||||
|
||||
extern "C" void HardFault_Handler(void)
|
||||
|
||||
@@ -51,7 +51,7 @@ void getMacAddr(uint8_t *dmac)
|
||||
NRF52Bluetooth *nrf52Bluetooth;
|
||||
|
||||
static bool bleOn = false;
|
||||
static const bool enableBle = true; // Set to false for easier debugging
|
||||
static const bool enableBle = false; // Set to false for easier debugging
|
||||
|
||||
void setBluetoothEnable(bool on)
|
||||
{
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
#include "meshwifi/meshhttp.h"
|
||||
#include "meshwifi/meshwifi.h"
|
||||
//#include "mesh/wifi/WebServer.h"
|
||||
#include "configuration.h"
|
||||
|
||||
#ifndef NO_ESP32
|
||||
|
||||
//#include "mesh/wifi/WiFiAPClient.h"
|
||||
|
||||
void initWifi(bool forceSoftAP) {}
|
||||
|
||||
@@ -10,7 +14,4 @@ bool isWifiAvailable()
|
||||
return false;
|
||||
}
|
||||
|
||||
void handleWebResponse() {}
|
||||
|
||||
/// Perform idle loop processing required by the wifi layer
|
||||
void loopWifi() {}
|
||||
#endif
|
||||
|
||||
176
src/plugins/ExternalNotificationPlugin.cpp
Normal file
176
src/plugins/ExternalNotificationPlugin.cpp
Normal file
@@ -0,0 +1,176 @@
|
||||
#include "ExternalNotificationPlugin.h"
|
||||
#include "MeshService.h"
|
||||
#include "NodeDB.h"
|
||||
#include "RTC.h"
|
||||
#include "Router.h"
|
||||
#include "configuration.h"
|
||||
#include <Arduino.h>
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
/*
|
||||
|
||||
Documentation:
|
||||
https://github.com/meshtastic/Meshtastic-device/blob/master/docs/software/plugins/ExternalNotificationPlugin.md
|
||||
|
||||
This plugin supports:
|
||||
https://github.com/meshtastic/Meshtastic-device/issues/654
|
||||
|
||||
|
||||
Quick reference:
|
||||
|
||||
radioConfig.preferences.ext_notification_plugin_enabled
|
||||
0 = Disabled (Default)
|
||||
1 = Enabled
|
||||
|
||||
radioConfig.preferences.ext_notification_plugin_active
|
||||
0 = Active Low (Default)
|
||||
1 = Active High
|
||||
|
||||
radioConfig.preferences.ext_notification_plugin_alert_message
|
||||
0 = Disabled (Default)
|
||||
1 = Alert when a text message comes
|
||||
|
||||
radioConfig.preferences.ext_notification_plugin_alert_bell
|
||||
0 = Disabled (Default)
|
||||
1 = Alert when the bell character is received
|
||||
|
||||
radioConfig.preferences.ext_notification_plugin_output
|
||||
GPIO of the output. (Default = 13)
|
||||
|
||||
radioConfig.preferences.ext_notification_plugin_output_ms
|
||||
Amount of time in ms for the alert. Default is 1000.
|
||||
|
||||
*/
|
||||
|
||||
|
||||
// Default configurations
|
||||
#define EXT_NOTIFICATION_PLUGIN_OUTPUT 13
|
||||
#define EXT_NOTIFICATION_PLUGIN_OUTPUT_MS 1000
|
||||
|
||||
#define ASCII_BELL 0x07
|
||||
|
||||
ExternalNotificationPlugin *externalNotificationPlugin;
|
||||
ExternalNotificationPluginRadio *externalNotificationPluginRadio;
|
||||
|
||||
ExternalNotificationPlugin::ExternalNotificationPlugin() : concurrency::OSThread("ExternalNotificationPlugin") {}
|
||||
|
||||
bool externalCurrentState = 0;
|
||||
uint32_t externalTurnedOn = 0;
|
||||
|
||||
int32_t ExternalNotificationPlugin::runOnce()
|
||||
{
|
||||
#ifndef NO_ESP32
|
||||
|
||||
/*
|
||||
Uncomment the preferences below if you want to use the plugin
|
||||
without having to configure it from the PythonAPI or WebUI.
|
||||
*/
|
||||
|
||||
// radioConfig.preferences.ext_notification_plugin_enabled = 1;
|
||||
// radioConfig.preferences.ext_notification_plugin_alert_message = 1;
|
||||
|
||||
// radioConfig.preferences.ext_notification_plugin_active = 1;
|
||||
// radioConfig.preferences.ext_notification_plugin_alert_bell = 1;
|
||||
// radioConfig.preferences.ext_notification_plugin_output_ms = 1000;
|
||||
// radioConfig.preferences.ext_notification_plugin_output = 13;
|
||||
|
||||
if (radioConfig.preferences.ext_notification_plugin_enabled) {
|
||||
|
||||
if (firstTime) {
|
||||
|
||||
DEBUG_MSG("Initializing External Notification Plugin\n");
|
||||
|
||||
// Set the direction of a pin
|
||||
pinMode((radioConfig.preferences.ext_notification_plugin_output
|
||||
? radioConfig.preferences.ext_notification_plugin_output
|
||||
: EXT_NOTIFICATION_PLUGIN_OUTPUT),
|
||||
OUTPUT);
|
||||
|
||||
// Turn off the pin
|
||||
setExternalOff();
|
||||
|
||||
externalNotificationPluginRadio = new ExternalNotificationPluginRadio();
|
||||
|
||||
firstTime = 0;
|
||||
|
||||
} else {
|
||||
if (externalCurrentState) {
|
||||
|
||||
// If the output is turned on, turn it back off after the given period of time.
|
||||
if (externalTurnedOn + (radioConfig.preferences.ext_notification_plugin_output_ms
|
||||
? radioConfig.preferences.ext_notification_plugin_output_ms
|
||||
: EXT_NOTIFICATION_PLUGIN_OUTPUT_MS) <
|
||||
millis()) {
|
||||
DEBUG_MSG("Turning off external notification\n");
|
||||
setExternalOff();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return (25);
|
||||
} else {
|
||||
DEBUG_MSG("External Notification Plugin Disabled\n");
|
||||
|
||||
return (INT32_MAX);
|
||||
}
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
void ExternalNotificationPlugin::setExternalOn()
|
||||
{
|
||||
externalCurrentState = 1;
|
||||
externalTurnedOn = millis();
|
||||
|
||||
digitalWrite((radioConfig.preferences.ext_notification_plugin_output ? radioConfig.preferences.ext_notification_plugin_output
|
||||
: EXT_NOTIFICATION_PLUGIN_OUTPUT),
|
||||
(radioConfig.preferences.ext_notification_plugin_active ? true : false));
|
||||
}
|
||||
|
||||
void ExternalNotificationPlugin::setExternalOff()
|
||||
{
|
||||
externalCurrentState = 0;
|
||||
|
||||
digitalWrite((radioConfig.preferences.ext_notification_plugin_output ? radioConfig.preferences.ext_notification_plugin_output
|
||||
: EXT_NOTIFICATION_PLUGIN_OUTPUT),
|
||||
(radioConfig.preferences.ext_notification_plugin_active ? false : true));
|
||||
}
|
||||
|
||||
// --------
|
||||
|
||||
bool ExternalNotificationPluginRadio::handleReceived(const MeshPacket &mp)
|
||||
{
|
||||
#ifndef NO_ESP32
|
||||
|
||||
if (radioConfig.preferences.ext_notification_plugin_enabled) {
|
||||
|
||||
auto &p = mp.decoded.data;
|
||||
|
||||
if (mp.from != nodeDB.getNodeNum()) {
|
||||
|
||||
// TODO: This may be a problem if messages are sent in unicide, but I'm not sure if it will.
|
||||
// Need to know if and how this could be a problem.
|
||||
if (radioConfig.preferences.ext_notification_plugin_alert_bell) {
|
||||
DEBUG_MSG("externalNotificationPlugin - Notification Bell\n");
|
||||
for (int i = 0; i < p.payload.size; i++) {
|
||||
if (p.payload.bytes[i] == ASCII_BELL) {
|
||||
externalNotificationPlugin->setExternalOn();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (radioConfig.preferences.ext_notification_plugin_alert_message) {
|
||||
DEBUG_MSG("externalNotificationPlugin - Notification Plugin\n");
|
||||
externalNotificationPlugin->setExternalOn();
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
DEBUG_MSG("External Notification Plugin Disabled\n");
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
return true; // Let others look at this message also if they want
|
||||
}
|
||||
47
src/plugins/ExternalNotificationPlugin.h
Normal file
47
src/plugins/ExternalNotificationPlugin.h
Normal file
@@ -0,0 +1,47 @@
|
||||
#pragma once
|
||||
|
||||
#include "SinglePortPlugin.h"
|
||||
#include "concurrency/OSThread.h"
|
||||
#include "configuration.h"
|
||||
#include <Arduino.h>
|
||||
#include <functional>
|
||||
|
||||
|
||||
class ExternalNotificationPlugin : private concurrency::OSThread
|
||||
{
|
||||
bool firstTime = 1;
|
||||
|
||||
public:
|
||||
ExternalNotificationPlugin();
|
||||
|
||||
void setExternalOn();
|
||||
void setExternalOff();
|
||||
void getExternal();
|
||||
|
||||
protected:
|
||||
virtual int32_t runOnce();
|
||||
};
|
||||
|
||||
extern ExternalNotificationPlugin *externalNotificationPlugin;
|
||||
|
||||
/*
|
||||
* Radio interface for ExternalNotificationPlugin
|
||||
*
|
||||
*/
|
||||
class ExternalNotificationPluginRadio : public SinglePortPlugin
|
||||
{
|
||||
|
||||
public:
|
||||
ExternalNotificationPluginRadio() : SinglePortPlugin("ExternalNotificationPluginRadio", PortNum_TEXT_MESSAGE_APP) {}
|
||||
|
||||
protected:
|
||||
//virtual MeshPacket *allocReply();
|
||||
|
||||
/** Called to handle a particular incoming message
|
||||
|
||||
@return true if you've guaranteed you've handled this message and no other handlers should be considered for it
|
||||
*/
|
||||
virtual bool handleReceived(const MeshPacket &mp);
|
||||
};
|
||||
|
||||
extern ExternalNotificationPluginRadio *externalNotificationPluginRadio;
|
||||
@@ -6,7 +6,7 @@
|
||||
#include "configuration.h"
|
||||
#include "main.h"
|
||||
|
||||
NodeInfoPlugin nodeInfoPlugin;
|
||||
NodeInfoPlugin *nodeInfoPlugin;
|
||||
|
||||
bool NodeInfoPlugin::handleReceivedProtobuf(const MeshPacket &mp, const User &p)
|
||||
{
|
||||
@@ -28,9 +28,15 @@ bool NodeInfoPlugin::handleReceivedProtobuf(const MeshPacket &mp, const User &p)
|
||||
|
||||
void NodeInfoPlugin::sendOurNodeInfo(NodeNum dest, bool wantReplies)
|
||||
{
|
||||
// cancel any not yet sent (now stale) position packets
|
||||
if (prevPacketId) // if we wrap around to zero, we'll simply fail to cancel in that rare case (no big deal)
|
||||
service.cancelSending(prevPacketId);
|
||||
|
||||
MeshPacket *p = allocReply();
|
||||
p->to = dest;
|
||||
p->decoded.want_response = wantReplies;
|
||||
p->priority = MeshPacket_Priority_BACKGROUND;
|
||||
prevPacketId = p->id;
|
||||
|
||||
service.sendToMesh(p);
|
||||
}
|
||||
@@ -42,3 +48,26 @@ MeshPacket *NodeInfoPlugin::allocReply()
|
||||
DEBUG_MSG("sending owner %s/%s/%s\n", u.id, u.long_name, u.short_name);
|
||||
return allocDataProtobuf(u);
|
||||
}
|
||||
|
||||
NodeInfoPlugin::NodeInfoPlugin()
|
||||
: ProtobufPlugin("nodeinfo", PortNum_NODEINFO_APP, User_fields), concurrency::OSThread("NodeInfoPlugin")
|
||||
{
|
||||
setIntervalFromNow(30 *
|
||||
1000); // Send our initial owner announcement 30 seconds after we start (to give network time to setup)
|
||||
}
|
||||
|
||||
int32_t NodeInfoPlugin::runOnce()
|
||||
{
|
||||
static uint32_t currentGeneration;
|
||||
|
||||
// If we changed channels, ask everyone else for their latest info
|
||||
bool requestReplies = currentGeneration != radioGeneration;
|
||||
currentGeneration = radioGeneration;
|
||||
|
||||
DEBUG_MSG("Sending our nodeinfo to mesh (wantReplies=%d)\n", requestReplies);
|
||||
assert(nodeInfoPlugin);
|
||||
nodeInfoPlugin->sendOurNodeInfo(NODENUM_BROADCAST, requestReplies); // Send our info (don't request replies)
|
||||
|
||||
return getPref_position_broadcast_secs() * 1000;
|
||||
}
|
||||
|
||||
|
||||
@@ -4,14 +4,18 @@
|
||||
/**
|
||||
* NodeInfo plugin for sending/receiving NodeInfos into the mesh
|
||||
*/
|
||||
class NodeInfoPlugin : public ProtobufPlugin<User>
|
||||
class NodeInfoPlugin : public ProtobufPlugin<User>, private concurrency::OSThread
|
||||
{
|
||||
/// The id of the last packet we sent, to allow us to cancel it if we make something fresher
|
||||
PacketId prevPacketId = 0;
|
||||
|
||||
uint32_t currentGeneration = 0;
|
||||
public:
|
||||
/** Constructor
|
||||
* name is for debugging output
|
||||
*/
|
||||
NodeInfoPlugin() : ProtobufPlugin("nodeinfo", PortNum_NODEINFO_APP, User_fields) {}
|
||||
|
||||
NodeInfoPlugin();
|
||||
|
||||
/**
|
||||
* Send our NodeInfo into the mesh
|
||||
*/
|
||||
@@ -27,6 +31,9 @@ class NodeInfoPlugin : public ProtobufPlugin<User>
|
||||
/** Messages can be received that have the want_response bit set. If set, this callback will be invoked
|
||||
* so that subclasses can (optionally) send a response back to the original sender. */
|
||||
virtual MeshPacket *allocReply();
|
||||
|
||||
/** Does our periodic broadcast */
|
||||
virtual int32_t runOnce();
|
||||
};
|
||||
|
||||
extern NodeInfoPlugin nodeInfoPlugin;
|
||||
extern NodeInfoPlugin *nodeInfoPlugin;
|
||||
37
src/plugins/Plugins.cpp
Normal file
37
src/plugins/Plugins.cpp
Normal file
@@ -0,0 +1,37 @@
|
||||
#include "plugins/ExternalNotificationPlugin.h"
|
||||
#include "plugins/NodeInfoPlugin.h"
|
||||
#include "plugins/PositionPlugin.h"
|
||||
#include "plugins/RangeTestPlugin.h"
|
||||
#include "plugins/RemoteHardwarePlugin.h"
|
||||
#include "plugins/ReplyPlugin.h"
|
||||
#include "plugins/SerialPlugin.h"
|
||||
#include "plugins/StoreForwardPlugin.h"
|
||||
#include "plugins/TextMessagePlugin.h"
|
||||
|
||||
/**
|
||||
* Create plugin instances here. If you are adding a new plugin, you must 'new' it here (or somewhere else)
|
||||
*/
|
||||
void setupPlugins()
|
||||
{
|
||||
nodeInfoPlugin = new NodeInfoPlugin();
|
||||
positionPlugin = new PositionPlugin();
|
||||
textMessagePlugin = new TextMessagePlugin();
|
||||
|
||||
// Note: if the rest of meshtastic doesn't need to explicitly use your plugin, you do not need to assign the instance
|
||||
// to a global variable.
|
||||
|
||||
new RemoteHardwarePlugin();
|
||||
new ReplyPlugin();
|
||||
|
||||
#ifndef NO_ESP32
|
||||
// Only run on an esp32 based device.
|
||||
|
||||
/*
|
||||
Maintained by MC Hamster (Jm Casler) jm@casler.org
|
||||
*/
|
||||
new SerialPlugin();
|
||||
new ExternalNotificationPlugin();
|
||||
//storeForwardPlugin = new StoreForwardPlugin();
|
||||
rangeTestPlugin = new RangeTestPlugin();
|
||||
#endif
|
||||
}
|
||||
6
src/plugins/Plugins.h
Normal file
6
src/plugins/Plugins.h
Normal file
@@ -0,0 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
/**
|
||||
* Create plugin instances here. If you are adding a new plugin, you must 'new' it here (or somewhere else)
|
||||
*/
|
||||
void setupPlugins();
|
||||
@@ -5,7 +5,15 @@
|
||||
#include "Router.h"
|
||||
#include "configuration.h"
|
||||
|
||||
PositionPlugin positionPlugin;
|
||||
PositionPlugin *positionPlugin;
|
||||
|
||||
PositionPlugin::PositionPlugin()
|
||||
: ProtobufPlugin("position", PortNum_POSITION_APP, Position_fields), concurrency::OSThread("PositionPlugin")
|
||||
{
|
||||
setIntervalFromNow(60 *
|
||||
1000); // Send our initial position 60 seconds after we start (to give GPS time to setup)
|
||||
|
||||
}
|
||||
|
||||
bool PositionPlugin::handleReceivedProtobuf(const MeshPacket &mp, const Position &p)
|
||||
{
|
||||
@@ -29,23 +37,53 @@ bool PositionPlugin::handleReceivedProtobuf(const MeshPacket &mp, const Position
|
||||
|
||||
MeshPacket *PositionPlugin::allocReply()
|
||||
{
|
||||
NodeInfo *node = nodeDB.getNode(nodeDB.getNodeNum());
|
||||
assert(node);
|
||||
NodeInfo *node = service.refreshMyNodeInfo(); // should guarantee there is now a position
|
||||
assert(node->has_position);
|
||||
|
||||
// Update our local node info with our position (even if we don't decide to update anyone else)
|
||||
auto position = node->position;
|
||||
position.time = getValidTime(RTCQualityGPS); // This nodedb timestamp might be stale, so update it if our clock is valid.
|
||||
Position p = node->position;
|
||||
|
||||
return allocDataProtobuf(position);
|
||||
// Strip out any time information before sending packets to other nodes - to keep the wire size small (and because other
|
||||
// nodes shouldn't trust it anyways) Note: we allow a device with a local GPS to include the time, so that gpsless
|
||||
// devices can get time.
|
||||
if (getRTCQuality() < RTCQualityGPS) {
|
||||
DEBUG_MSG("Stripping time %u from position send\n", p.time);
|
||||
p.time = 0;
|
||||
} else
|
||||
DEBUG_MSG("Providing time to mesh %u\n", p.time);
|
||||
|
||||
return allocDataProtobuf(p);
|
||||
}
|
||||
|
||||
void PositionPlugin::sendOurPosition(NodeNum dest, bool wantReplies)
|
||||
{
|
||||
// cancel any not yet sent (now stale) position packets
|
||||
if (prevPacketId) // if we wrap around to zero, we'll simply fail to cancel in that rare case (no big deal)
|
||||
service.cancelSending(prevPacketId);
|
||||
|
||||
MeshPacket *p = allocReply();
|
||||
p->to = dest;
|
||||
p->decoded.want_response = wantReplies;
|
||||
p->priority = MeshPacket_Priority_BACKGROUND;
|
||||
prevPacketId = p->id;
|
||||
|
||||
service.sendToMesh(p);
|
||||
}
|
||||
|
||||
int32_t PositionPlugin::runOnce()
|
||||
{
|
||||
|
||||
// We limit our GPS broadcasts to a max rate
|
||||
uint32_t now = millis();
|
||||
if (lastGpsSend == 0 || now - lastGpsSend >= getPref_position_broadcast_secs() * 1000) {
|
||||
lastGpsSend = now;
|
||||
|
||||
// If we changed channels, ask everyone else for their latest info
|
||||
bool requestReplies = currentGeneration != radioGeneration;
|
||||
currentGeneration = radioGeneration;
|
||||
|
||||
DEBUG_MSG("Sending position to mesh (wantReplies=%d)\n", requestReplies);
|
||||
sendOurPosition(NODENUM_BROADCAST, requestReplies);
|
||||
}
|
||||
|
||||
return 5000; // to save power only wake for our callback occasionally
|
||||
}
|
||||
@@ -1,17 +1,27 @@
|
||||
#pragma once
|
||||
#include "ProtobufPlugin.h"
|
||||
#include "concurrency/OSThread.h"
|
||||
|
||||
/**
|
||||
* Position plugin for sending/receiving positions into the mesh
|
||||
*/
|
||||
class PositionPlugin : public ProtobufPlugin<Position>
|
||||
class PositionPlugin : public ProtobufPlugin<Position>, private concurrency::OSThread
|
||||
{
|
||||
/// The id of the last packet we sent, to allow us to cancel it if we make something fresher
|
||||
PacketId prevPacketId = 0;
|
||||
|
||||
/// We limit our GPS broadcasts to a max rate
|
||||
uint32_t lastGpsSend = 0;
|
||||
|
||||
/// We force a rebroadcast if the radio settings change
|
||||
uint32_t currentGeneration = 0;
|
||||
|
||||
public:
|
||||
/** Constructor
|
||||
* name is for debugging output
|
||||
*/
|
||||
PositionPlugin() : ProtobufPlugin("position", PortNum_POSITION_APP, Position_fields) {}
|
||||
|
||||
PositionPlugin();
|
||||
|
||||
/**
|
||||
* Send our position into the mesh
|
||||
*/
|
||||
@@ -28,6 +38,9 @@ class PositionPlugin : public ProtobufPlugin<Position>
|
||||
/** Messages can be received that have the want_response bit set. If set, this callback will be invoked
|
||||
* so that subclasses can (optionally) send a response back to the original sender. */
|
||||
virtual MeshPacket *allocReply();
|
||||
|
||||
/** Does our periodic broadcast */
|
||||
virtual int32_t runOnce();
|
||||
};
|
||||
|
||||
extern PositionPlugin positionPlugin;
|
||||
extern PositionPlugin *positionPlugin;
|
||||
173
src/plugins/RangeTestPlugin.cpp
Normal file
173
src/plugins/RangeTestPlugin.cpp
Normal file
@@ -0,0 +1,173 @@
|
||||
#include "RangeTestPlugin.h"
|
||||
#include "MeshService.h"
|
||||
#include "NodeDB.h"
|
||||
#include "RTC.h"
|
||||
#include "Router.h"
|
||||
#include "configuration.h"
|
||||
#include <Arduino.h>
|
||||
#include <assert.h>
|
||||
|
||||
/*
|
||||
As a sender, I can send packets every n-seonds. These packets include an incramented PacketID.
|
||||
|
||||
As a receiver, I can receive packets from multiple senders. These packets can be saved to the spiffs.
|
||||
|
||||
*/
|
||||
|
||||
RangeTestPlugin *rangeTestPlugin;
|
||||
RangeTestPluginRadio *rangeTestPluginRadio;
|
||||
|
||||
RangeTestPlugin::RangeTestPlugin() : concurrency::OSThread("RangeTestPlugin") {}
|
||||
|
||||
uint16_t packetSequence = 0;
|
||||
|
||||
// char serialStringChar[Constants_DATA_PAYLOAD_LEN];
|
||||
|
||||
int32_t RangeTestPlugin::runOnce()
|
||||
{
|
||||
#ifndef NO_ESP32
|
||||
|
||||
/*
|
||||
Uncomment the preferences below if you want to use the plugin
|
||||
without having to configure it from the PythonAPI or WebUI.
|
||||
*/
|
||||
|
||||
// radioConfig.preferences.range_test_plugin_enabled = 1;
|
||||
// radioConfig.preferences.range_test_plugin_sender = 0;
|
||||
// radioConfig.preferences.fixed_position = 1;
|
||||
|
||||
uint32_t senderHeartbeat = radioConfig.preferences.range_test_plugin_sender * 1000;
|
||||
|
||||
if (radioConfig.preferences.range_test_plugin_enabled) {
|
||||
|
||||
if (firstTime) {
|
||||
|
||||
// Interface with the serial peripheral from in here.
|
||||
|
||||
rangeTestPluginRadio = new RangeTestPluginRadio();
|
||||
|
||||
firstTime = 0;
|
||||
|
||||
if (radioConfig.preferences.range_test_plugin_sender) {
|
||||
DEBUG_MSG("Initializing Range Test Plugin -- Sender\n");
|
||||
return (senderHeartbeat);
|
||||
} else {
|
||||
DEBUG_MSG("Initializing Range Test Plugin -- Receiver\n");
|
||||
return (500);
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
if (radioConfig.preferences.range_test_plugin_sender) {
|
||||
// If sender
|
||||
DEBUG_MSG("Range Test Plugin - Sending heartbeat every %d ms\n", (senderHeartbeat));
|
||||
|
||||
DEBUG_MSG("gpsStatus->getLatitude() %d\n", gpsStatus->getLatitude());
|
||||
DEBUG_MSG("gpsStatus->getLongitude() %d\n", gpsStatus->getLongitude());
|
||||
DEBUG_MSG("gpsStatus->getHasLock() %d\n", gpsStatus->getHasLock());
|
||||
DEBUG_MSG("gpsStatus->getDOP() %d\n", gpsStatus->getDOP());
|
||||
DEBUG_MSG("gpsStatus->getHasLock() %d\n", gpsStatus->getHasLock());
|
||||
DEBUG_MSG("pref.fixed_position() %d\n", radioConfig.preferences.fixed_position);
|
||||
|
||||
rangeTestPluginRadio->sendPayload();
|
||||
return ((senderHeartbeat));
|
||||
} else {
|
||||
// Otherwise, we're a receiver.
|
||||
|
||||
return (500);
|
||||
}
|
||||
// TBD
|
||||
}
|
||||
|
||||
} else {
|
||||
DEBUG_MSG("Range Test Plugin - Disabled\n");
|
||||
}
|
||||
|
||||
return (INT32_MAX);
|
||||
#endif
|
||||
}
|
||||
|
||||
MeshPacket *RangeTestPluginRadio::allocReply()
|
||||
{
|
||||
|
||||
auto reply = allocDataPacket(); // Allocate a packet for sending
|
||||
|
||||
return reply;
|
||||
}
|
||||
|
||||
void RangeTestPluginRadio::sendPayload(NodeNum dest, bool wantReplies)
|
||||
{
|
||||
MeshPacket *p = allocReply();
|
||||
p->to = dest;
|
||||
p->decoded.want_response = wantReplies;
|
||||
|
||||
p->want_ack = true;
|
||||
|
||||
packetSequence++;
|
||||
|
||||
static char heartbeatString[20];
|
||||
snprintf(heartbeatString, sizeof(heartbeatString), "seq %d", packetSequence);
|
||||
|
||||
p->decoded.data.payload.size = strlen(heartbeatString); // You must specify how many bytes are in the reply
|
||||
memcpy(p->decoded.data.payload.bytes, heartbeatString, p->decoded.data.payload.size);
|
||||
|
||||
service.sendToMesh(p);
|
||||
}
|
||||
|
||||
bool RangeTestPluginRadio::handleReceived(const MeshPacket &mp)
|
||||
{
|
||||
#ifndef NO_ESP32
|
||||
|
||||
if (radioConfig.preferences.range_test_plugin_enabled) {
|
||||
|
||||
auto &p = mp.decoded.data;
|
||||
// DEBUG_MSG("Received text msg self=0x%0x, from=0x%0x, to=0x%0x, id=%d, msg=%.*s\n",
|
||||
// nodeDB.getNodeNum(), mp.from, mp.to, mp.id, p.payload.size, p.payload.bytes);
|
||||
|
||||
if (mp.from != nodeDB.getNodeNum()) {
|
||||
|
||||
// DEBUG_MSG("* * Message came from the mesh\n");
|
||||
// Serial2.println("* * Message came from the mesh");
|
||||
// Serial2.printf("%s", p.payload.bytes);
|
||||
/*
|
||||
|
||||
|
||||
|
||||
*/
|
||||
|
||||
NodeInfo *n = nodeDB.getNode(mp.from);
|
||||
|
||||
|
||||
DEBUG_MSG("-----------------------------------------\n");
|
||||
DEBUG_MSG("p.payload.bytes \"%s\"\n", p.payload.bytes);
|
||||
DEBUG_MSG("p.payload.size %d\n", p.payload.size);
|
||||
DEBUG_MSG("---- Received Packet:\n");
|
||||
DEBUG_MSG("mp.from %d\n", mp.from);
|
||||
DEBUG_MSG("mp.rx_snr %f\n", mp.rx_snr);
|
||||
DEBUG_MSG("mp.hop_limit %d\n", mp.hop_limit);
|
||||
DEBUG_MSG("mp.decoded.position.latitude_i %d\n", mp.decoded.position.latitude_i);
|
||||
DEBUG_MSG("mp.decoded.position.longitude_i %d\n", mp.decoded.position.longitude_i);
|
||||
DEBUG_MSG("---- Node Information of Received Packet (mp.from):\n");
|
||||
DEBUG_MSG("n->user.long_name %s\n", n->user.long_name);
|
||||
DEBUG_MSG("n->user.short_name %s\n", n->user.short_name);
|
||||
DEBUG_MSG("n->user.macaddr %X\n", n->user.macaddr);
|
||||
DEBUG_MSG("n->has_position %d\n", n->has_position);
|
||||
DEBUG_MSG("n->position.latitude_i %d\n", n->position.latitude_i);
|
||||
DEBUG_MSG("n->position.longitude_i %d\n", n->position.longitude_i);
|
||||
DEBUG_MSG("n->position.battery_level %d\n", n->position.battery_level);
|
||||
DEBUG_MSG("---- Current device location information:\n");
|
||||
DEBUG_MSG("gpsStatus->getLatitude() %d\n", gpsStatus->getLatitude());
|
||||
DEBUG_MSG("gpsStatus->getLongitude() %d\n", gpsStatus->getLongitude());
|
||||
DEBUG_MSG("gpsStatus->getHasLock() %d\n", gpsStatus->getHasLock());
|
||||
DEBUG_MSG("gpsStatus->getDOP() %d\n", gpsStatus->getDOP());
|
||||
DEBUG_MSG("-----------------------------------------\n");
|
||||
}
|
||||
|
||||
} else {
|
||||
DEBUG_MSG("Range Test Plugin Disabled\n");
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
return true; // Let others look at this message also if they want
|
||||
}
|
||||
49
src/plugins/RangeTestPlugin.h
Normal file
49
src/plugins/RangeTestPlugin.h
Normal file
@@ -0,0 +1,49 @@
|
||||
#pragma once
|
||||
|
||||
#include "SinglePortPlugin.h"
|
||||
#include "concurrency/OSThread.h"
|
||||
#include "configuration.h"
|
||||
#include <Arduino.h>
|
||||
#include <functional>
|
||||
|
||||
class RangeTestPlugin : private concurrency::OSThread
|
||||
{
|
||||
bool firstTime = 1;
|
||||
|
||||
public:
|
||||
RangeTestPlugin();
|
||||
|
||||
protected:
|
||||
virtual int32_t runOnce();
|
||||
};
|
||||
|
||||
extern RangeTestPlugin *rangeTestPlugin;
|
||||
|
||||
/*
|
||||
* Radio interface for RangeTestPlugin
|
||||
*
|
||||
*/
|
||||
class RangeTestPluginRadio : public SinglePortPlugin
|
||||
{
|
||||
uint32_t lastRxID;
|
||||
|
||||
public:
|
||||
|
||||
RangeTestPluginRadio() : SinglePortPlugin("RangeTestPluginRadio", PortNum_TEXT_MESSAGE_APP) {}
|
||||
|
||||
/**
|
||||
* Send our payload into the mesh
|
||||
*/
|
||||
void sendPayload(NodeNum dest = NODENUM_BROADCAST, bool wantReplies = false);
|
||||
|
||||
protected:
|
||||
virtual MeshPacket *allocReply();
|
||||
|
||||
/** Called to handle a particular incoming message
|
||||
|
||||
@return true if you've guaranteed you've handled this message and no other handlers should be considered for it
|
||||
*/
|
||||
virtual bool handleReceived(const MeshPacket &mp);
|
||||
};
|
||||
|
||||
extern RangeTestPluginRadio *rangeTestPluginRadio;
|
||||
@@ -6,8 +6,6 @@
|
||||
#include "configuration.h"
|
||||
#include "main.h"
|
||||
|
||||
RemoteHardwarePlugin remoteHardwarePlugin;
|
||||
|
||||
#define NUM_GPIOS 64
|
||||
|
||||
// Because (FIXME) we currently don't tell API clients status on sent messages
|
||||
|
||||
@@ -5,9 +5,6 @@
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
// Create an a static instance of our plugin - this registers with the plugin system
|
||||
ReplyPlugin replyPlugin;
|
||||
|
||||
MeshPacket *ReplyPlugin::allocReply()
|
||||
{
|
||||
assert(currentRequest); // should always be !NULL
|
||||
|
||||
194
src/plugins/SerialPlugin.cpp
Normal file
194
src/plugins/SerialPlugin.cpp
Normal file
@@ -0,0 +1,194 @@
|
||||
#include "SerialPlugin.h"
|
||||
#include "MeshService.h"
|
||||
#include "NodeDB.h"
|
||||
#include "RTC.h"
|
||||
#include "Router.h"
|
||||
#include "configuration.h"
|
||||
#include <Arduino.h>
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
/*
|
||||
SerialPlugin
|
||||
A simple interface to send messages over the mesh network by sending strings
|
||||
over a serial port.
|
||||
|
||||
Default is to use RX GPIO 16 and TX GPIO 17.
|
||||
|
||||
Need help with this plugin? Post your question on the Meshtastic Discourse:
|
||||
https://meshtastic.discourse.group
|
||||
|
||||
Basic Usage:
|
||||
|
||||
1) Enable the plugin by setting serialplugin_enabled to 1.
|
||||
2) Set the pins (serialplugin_rxd / serialplugin_rxd) for your preferred RX and TX GPIO pins.
|
||||
On tbeam, recommend to use:
|
||||
RXD 35
|
||||
TXD 15
|
||||
3) Set serialplugin_timeout to the amount of time to wait before we consider
|
||||
your packet as "done".
|
||||
4) (Optional) In SerialPlugin.h set the port to PortNum_TEXT_MESSAGE_APP if you want to
|
||||
send messages to/from the general text message channel.
|
||||
5) Connect to your device over the serial interface at 38400 8N1.
|
||||
6) Send a packet up to 240 bytes in length. This will get relayed over the mesh network.
|
||||
7) (Optional) Set serialplugin_echo to 1 and any message you send out will be echoed back
|
||||
to your device.
|
||||
|
||||
TODO (in this order):
|
||||
* Define a verbose RX mode to report on mesh and packet infomration.
|
||||
- This won't happen any time soon.
|
||||
|
||||
KNOWN PROBLEMS
|
||||
* Until the plugin is initilized by the startup sequence, the TX pin is in a floating
|
||||
state. Device connected to that pin may see this as "noise".
|
||||
* Will not work on NRF and the Linux device targets.
|
||||
|
||||
|
||||
*/
|
||||
|
||||
#define RXD2 16
|
||||
#define TXD2 17
|
||||
#define SERIALPLUGIN_RX_BUFFER 128
|
||||
#define SERIALPLUGIN_STRING_MAX Constants_DATA_PAYLOAD_LEN
|
||||
#define SERIALPLUGIN_TIMEOUT 250
|
||||
#define SERIALPLUGIN_BAUD 38400
|
||||
#define SERIALPLUGIN_ACK 1
|
||||
|
||||
SerialPlugin *serialPlugin;
|
||||
SerialPluginRadio *serialPluginRadio;
|
||||
|
||||
SerialPlugin::SerialPlugin() : concurrency::OSThread("SerialPlugin") {}
|
||||
|
||||
char serialStringChar[Constants_DATA_PAYLOAD_LEN];
|
||||
|
||||
int32_t SerialPlugin::runOnce()
|
||||
{
|
||||
#ifndef NO_ESP32
|
||||
|
||||
/*
|
||||
Uncomment the preferences below if you want to use the plugin
|
||||
without having to configure it from the PythonAPI or WebUI.
|
||||
*/
|
||||
|
||||
// radioConfig.preferences.serialplugin_enabled = 1;
|
||||
// radioConfig.preferences.serialplugin_rxd = 35;
|
||||
// radioConfig.preferences.serialplugin_txd = 15;
|
||||
// radioConfig.preferences.serialplugin_timeout = 1000;
|
||||
// radioConfig.preferences.serialplugin_echo = 1;
|
||||
|
||||
if (radioConfig.preferences.serialplugin_enabled) {
|
||||
|
||||
if (firstTime) {
|
||||
|
||||
// Interface with the serial peripheral from in here.
|
||||
DEBUG_MSG("Initializing serial peripheral interface\n");
|
||||
|
||||
if (radioConfig.preferences.serialplugin_rxd && radioConfig.preferences.serialplugin_txd) {
|
||||
Serial2.begin(SERIALPLUGIN_BAUD, SERIAL_8N1, radioConfig.preferences.serialplugin_rxd,
|
||||
radioConfig.preferences.serialplugin_txd);
|
||||
|
||||
} else {
|
||||
Serial2.begin(SERIALPLUGIN_BAUD, SERIAL_8N1, RXD2, TXD2);
|
||||
}
|
||||
|
||||
if (radioConfig.preferences.serialplugin_timeout) {
|
||||
Serial2.setTimeout(
|
||||
radioConfig.preferences.serialplugin_timeout); // Number of MS to wait to set the timeout for the string.
|
||||
|
||||
} else {
|
||||
Serial2.setTimeout(SERIALPLUGIN_TIMEOUT); // Number of MS to wait to set the timeout for the string.
|
||||
}
|
||||
|
||||
Serial2.setRxBufferSize(SERIALPLUGIN_RX_BUFFER);
|
||||
|
||||
serialPluginRadio = new SerialPluginRadio();
|
||||
|
||||
firstTime = 0;
|
||||
|
||||
} else {
|
||||
String serialString;
|
||||
|
||||
while (Serial2.available()) {
|
||||
serialString = Serial2.readString();
|
||||
serialString.toCharArray(serialStringChar, Constants_DATA_PAYLOAD_LEN);
|
||||
|
||||
serialPluginRadio->sendPayload();
|
||||
|
||||
DEBUG_MSG("Received: %s\n", serialStringChar);
|
||||
}
|
||||
}
|
||||
|
||||
return (10);
|
||||
} else {
|
||||
DEBUG_MSG("Serial Plugin Disabled\n");
|
||||
|
||||
return (INT32_MAX);
|
||||
}
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
MeshPacket *SerialPluginRadio::allocReply()
|
||||
{
|
||||
|
||||
auto reply = allocDataPacket(); // Allocate a packet for sending
|
||||
|
||||
return reply;
|
||||
}
|
||||
|
||||
void SerialPluginRadio::sendPayload(NodeNum dest, bool wantReplies)
|
||||
{
|
||||
MeshPacket *p = allocReply();
|
||||
p->to = dest;
|
||||
p->decoded.want_response = wantReplies;
|
||||
|
||||
p->want_ack = SERIALPLUGIN_ACK;
|
||||
|
||||
p->decoded.data.payload.size = strlen(serialStringChar); // You must specify how many bytes are in the reply
|
||||
memcpy(p->decoded.data.payload.bytes, serialStringChar, p->decoded.data.payload.size);
|
||||
|
||||
service.sendToMesh(p);
|
||||
}
|
||||
|
||||
bool SerialPluginRadio::handleReceived(const MeshPacket &mp)
|
||||
{
|
||||
#ifndef NO_ESP32
|
||||
|
||||
if (radioConfig.preferences.serialplugin_enabled) {
|
||||
|
||||
auto &p = mp.decoded.data;
|
||||
// DEBUG_MSG("Received text msg self=0x%0x, from=0x%0x, to=0x%0x, id=%d, msg=%.*s\n",
|
||||
// nodeDB.getNodeNum(), mp.from, mp.to, mp.id, p.payload.size, p.payload.bytes);
|
||||
|
||||
if (mp.from == nodeDB.getNodeNum()) {
|
||||
|
||||
/*
|
||||
* If radioConfig.preferences.serialplugin_echo is true, then echo the packets that are sent out back to the TX
|
||||
* of the serial interface.
|
||||
*/
|
||||
if (radioConfig.preferences.serialplugin_echo) {
|
||||
|
||||
// For some reason, we get the packet back twice when we send out of the radio.
|
||||
// TODO: need to find out why.
|
||||
if (lastRxID != mp.id) {
|
||||
lastRxID = mp.id;
|
||||
// DEBUG_MSG("* * Message came this device\n");
|
||||
// Serial2.println("* * Message came this device");
|
||||
Serial2.printf("%s", p.payload.bytes);
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
// DEBUG_MSG("* * Message came from the mesh\n");
|
||||
// Serial2.println("* * Message came from the mesh");
|
||||
Serial2.printf("%s", p.payload.bytes);
|
||||
}
|
||||
|
||||
} else {
|
||||
DEBUG_MSG("Serial Plugin Disabled\n");
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
return true; // Let others look at this message also if they want
|
||||
}
|
||||
54
src/plugins/SerialPlugin.h
Normal file
54
src/plugins/SerialPlugin.h
Normal file
@@ -0,0 +1,54 @@
|
||||
#pragma once
|
||||
|
||||
#include "SinglePortPlugin.h"
|
||||
#include "concurrency/OSThread.h"
|
||||
#include "configuration.h"
|
||||
#include <Arduino.h>
|
||||
#include <functional>
|
||||
|
||||
class SerialPlugin : private concurrency::OSThread
|
||||
{
|
||||
bool firstTime = 1;
|
||||
|
||||
public:
|
||||
SerialPlugin();
|
||||
|
||||
protected:
|
||||
virtual int32_t runOnce();
|
||||
};
|
||||
|
||||
extern SerialPlugin *serialPlugin;
|
||||
|
||||
/*
|
||||
* Radio interface for SerialPlugin
|
||||
*
|
||||
*/
|
||||
class SerialPluginRadio : public SinglePortPlugin
|
||||
{
|
||||
uint32_t lastRxID;
|
||||
|
||||
public:
|
||||
/*
|
||||
TODO: Switch this to PortNum_SERIAL_APP once the change is able to be merged back here
|
||||
from the main code.
|
||||
*/
|
||||
|
||||
// SerialPluginRadio() : SinglePortPlugin("SerialPluginRadio", PortNum_TEXT_MESSAGE_APP) {}
|
||||
SerialPluginRadio() : SinglePortPlugin("SerialPluginRadio", PortNum_SERIAL_APP) {}
|
||||
|
||||
/**
|
||||
* Send our payload into the mesh
|
||||
*/
|
||||
void sendPayload(NodeNum dest = NODENUM_BROADCAST, bool wantReplies = false);
|
||||
|
||||
protected:
|
||||
virtual MeshPacket *allocReply();
|
||||
|
||||
/** Called to handle a particular incoming message
|
||||
|
||||
@return true if you've guaranteed you've handled this message and no other handlers should be considered for it
|
||||
*/
|
||||
virtual bool handleReceived(const MeshPacket &mp);
|
||||
};
|
||||
|
||||
extern SerialPluginRadio *serialPluginRadio;
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user