Compare commits

...

371 Commits

Author SHA1 Message Date
Kevin Hester
286686137f Merge pull request #757 from geeksville/dev1.2
getting ready for 1.2.13
2021-03-24 13:34:57 +08:00
Kevin Hester
77c1112fe8 Merge remote-tracking branch 'root/master' into dev1.2 2021-03-24 13:28:21 +08:00
Kevin Hester
2d4ba357f7 Merge pull request #755 from vfurman-gh/master
Push RSSI to the phone
2021-03-24 13:28:01 +08:00
Kevin Hester
455d0f8d66 1.2.13 2021-03-24 13:27:18 +08:00
Kevin Hester
5b0e7c6e82 fix has_preferences init 2021-03-24 13:25:21 +08:00
Kevin Hester
78c665abb9 properly discard messages with fromradio queue is full (Rather than blocking forever) 2021-03-24 13:25:10 +08:00
Kevin Hester
9a86d52d00 Merge remote-tracking branch 'root/master' into dev1.2 2021-03-24 12:01:38 +08:00
Kevin Hester
c5973f9a55 @mc-hamster it is VERY important to not accidentally turn this in in master
;-)
2021-03-24 11:41:14 +08:00
Kevin Hester
eb684aac03 tested OTA is_router 2021-03-23 14:54:56 +08:00
Kevin Hester
7b4f8fb6d6 Fix firmware OTA update while is_router 2021-03-23 14:44:50 +08:00
Vadim Furman
8065dbb2b7 Merge remote-tracking branch 'origin/master' 2021-03-22 21:40:30 -07:00
Vadim Furman
049e791382 Updated proto 2021-03-22 21:36:24 -07:00
Kevin Hester
4fb8552563 Merge pull request #756 from geeksville/dev1.2
Dev1.2
2021-03-23 12:18:56 +08:00
Kevin Hester
90576f44d8 Merge remote-tracking branch 'root/master' into dev1.2
# Conflicts:
#	proto
2021-03-23 12:17:46 +08:00
Kevin Hester
9e0a2964a4 move channel docs into git 2021-03-23 12:16:23 +08:00
Kevin Hester
49b16fdf0c fix channel !authorized check 2021-03-23 12:07:04 +08:00
Kevin Hester
1fcec8ce3b always fix up channel list, even if we just did factory reset 2021-03-23 11:54:53 +08:00
Kevin Hester
d32386a027 Return errors for unauthorized requests or out of bound channel nums 2021-03-23 11:44:51 +08:00
Vadim Furman
9b57d28c7d Merge remote-tracking branch 'origin/master' 2021-03-22 19:41:23 -07:00
Vadim Furman
b9fd726c14 Push RSSI to the phone 2021-03-22 19:39:49 -07:00
Kevin Hester
f165418b18 Merge pull request #751 from timgunter/set_device_install_python
Add option to set python interpreter used for device-install.sh scripts
2021-03-22 10:48:07 +08:00
Tim Gunter
13889124c1 Add option to set python interpreter used for device-install.sh and device-update.sh 2021-03-21 18:29:20 -07:00
Jm Casler
9005aaa14e Merge pull request #750 from mc-hamster/master
Update nightly build script
2021-03-21 10:00:39 -07:00
Jm Casler
df4e325e43 Merge branch 'master' of https://github.com/mc-hamster/Meshtastic-device 2021-03-21 09:42:57 -07:00
Jm Casler
4ebc07b691 Update nightly build to use the same release build archive for the nightly 2021-03-21 09:42:55 -07:00
Jm Casler
79a8d023ca Merge pull request #744 from mc-hamster/master
Update serial plugin, update range test doc and add support for tlora 1.3
2021-03-21 07:54:13 -07:00
Jm Casler
330d83e7c3 Merge pull request #78 from meshtastic/master
Update from head
2021-03-21 07:51:45 -07:00
Jm Casler
a74384f3f5 Update airtime.h to add override 2021-03-21 07:44:08 -07:00
Jm Casler
da732c291f Update build-all for tlora_v1_3 2021-03-21 07:43:55 -07:00
Kevin Hester
648d9dd19f Merge pull request #749 from geeksville/dev1.2
Dev1.2
2021-03-21 18:59:30 +08:00
Kevin Hester
e9faf657df move GPS_RX_PIN for the TLORA_V2_1_16 from 36 to 15 2021-03-21 18:58:43 +08:00
Jm Casler
103ffde025 Fixes for build of tlora_v1_3 2021-03-20 20:47:48 -07:00
Jm Casler
baeb002245 Fix small bug in range test plugin.
packetSequence was an unsigned int but i was using %d. oops
2021-03-20 00:38:53 -07:00
Jm
0ce7a3f0ec Update to how S&F reserves space on PSRAM 2021-03-19 23:43:06 -07:00
Jm Casler
0befad82a7 Merge branch 'master' into master 2021-03-19 23:38:42 -07:00
Jm
b357d8ae5b Update RangeTestPlugin.md 2021-03-19 23:35:46 -07:00
Jm
dd9beff9a5 Script to publish nightly builds. 2021-03-19 23:34:36 -07:00
Kevin Hester
d652664126 TODO updates 2021-03-20 13:35:27 +08:00
Kevin Hester
4666c12547 change android-too-old webpage to be less scary 2021-03-20 13:13:39 +08:00
Kevin Hester
2b74260e2b only show time on OLED if we have a valid UTC clock 2021-03-20 10:22:06 +08:00
Kevin Hester
620d336e55 Merge pull request #747 from vfurman-gh/master
Removed SX1262_USE_DIO3_FOR_TCXO
2021-03-20 10:01:42 +08:00
Kevin Hester
e845a3388b Merge branch 'master' into master 2021-03-20 10:01:35 +08:00
Vadim Furman
a25235dc03 Merge remote-tracking branch 'origin/master' 2021-03-19 18:50:30 -07:00
Vadim Furman
d3cbc8ea78 WIRE_INTERFACES_COUNT -> NO_WIRE 2021-03-19 18:46:11 -07:00
Vadim Furman
b6e197371d Removed SX1262_USE_DIO3_FOR_TCXO 2021-03-19 18:30:00 -07:00
Kevin Hester
5cc3ff16a3 Merge pull request #740 from vfurman-gh/master
Port to isp4520-based board
2021-03-20 09:01:06 +08:00
Vadim Furman
d93d5d2e37 Revert clock update for Net quality clocks 2021-03-19 17:21:08 -07:00
Vadim Furman
7491af8ad7 Merge remote-tracking branch 'origin/master' 2021-03-19 17:07:16 -07:00
Kevin Hester
fce95431e6 Merge pull request #746 from geeksville/dev1.2
fixing bugs for the sat release
2021-03-19 23:47:12 +08:00
Kevin Hester
591a07c0fe 1.2.11 2021-03-19 23:41:28 +08:00
Kevin Hester
c410f2d151 fix tlora 1.6 build 2021-03-19 23:40:41 +08:00
Kevin Hester
9502fa62e6 todo updates 2021-03-19 23:40:24 +08:00
Kevin Hester
2a6480ec48 update release test scripts 2021-03-19 23:40:04 +08:00
Kevin Hester
7c5ab885be geeksville todo 2021-03-19 15:24:24 +08:00
Kevin Hester
21cfb151a8 specify clang for c++ formatting conventions 2021-03-19 15:24:05 +08:00
Kevin Hester
84505b1717 Merge remote-tracking branch 'root/master' into portduino 2021-03-19 15:22:56 +08:00
Kevin Hester
d735e3006e Merge pull request #745 from geeksville/portduino
add CI integration test using meshtastic-native
2021-03-18 19:59:27 +08:00
Kevin Hester
af5d82dbde Merge remote-tracking branch 'root/master' into portduino 2021-03-18 19:49:13 +08:00
Kevin Hester
a97072eca0 add first integration test for CI 2021-03-18 19:48:06 +08:00
Kevin Hester
cef6e248e7 add newline 2021-03-18 19:40:00 +08:00
Kevin Hester
b4c379f5fc add sim exit cmd 2021-03-18 19:09:31 +08:00
Jm
3bb1206b9c Update RangeTestPlugin.md 2021-03-17 22:47:19 -07:00
Jm
002532401d #743 - Stub out for "mode 10" - NMEA string. 2021-03-17 21:52:30 -07:00
Jm Casler
8957c5892f Merge pull request #77 from meshtastic/master
update from head
2021-03-17 21:08:50 -07:00
Jm
1b8f41d353 Partial work on s&f 2021-03-17 21:03:11 -07:00
Vadim Furman
0c51cc3738 Fix hardware model 2021-03-17 10:56:45 -07:00
Vadim Furman
2b9a8f0822 Merge remote-tracking branch 'origin/master' 2021-03-17 10:49:04 -07:00
Vadim Furman
ddcfff3b59 Fixed compilation on nrf52 and brownout code 2021-03-17 10:44:42 -07:00
Kevin Hester
449a3959b0 Merge pull request #742 from geeksville/portduino
wifi now works on portduino
2021-03-17 20:43:47 +08:00
Kevin Hester
719a0c485b Merge remote-tracking branch 'root/master' into portduino 2021-03-17 20:30:41 +08:00
Kevin Hester
a4bbdc443f Merge pull request #739 from geeksville/dev
Dev
2021-03-17 20:30:25 +08:00
Kevin Hester
999afdf05e wifi now works on portduino 2021-03-17 20:29:27 +08:00
Kevin Hester
f492f6deb6 progress on Wifi in portduino 2021-03-15 19:59:31 +08:00
Kevin Hester
60f7ec8998 setup for intellij 2021-03-15 13:55:08 +08:00
Kevin Hester
469d0ade72 remove old intellij defs 2021-03-15 13:49:21 +08:00
Kevin Hester
1f33b03c30 turn off broken genieblocks_lora build 2021-03-15 13:43:22 +08:00
Kevin Hester
351be2f327 change portduino so it has a higher chance of building on OS-X and windows 2021-03-15 13:09:52 +08:00
Kevin Hester
3f401e8cac Merge branch 'dev' into portduino
# Conflicts:
#	.vscode/settings.json
2021-03-15 12:09:07 +08:00
Vadim Furman
b20b21c553 pitches.h no more 2021-03-14 20:46:58 -07:00
Vadim Furman
c62863b1dc Merge from origin 2021-03-14 19:42:57 -07:00
Vadim Furman
8505a0f260 Chargeing... 2021-03-14 19:17:28 -07:00
Vadim Furman
98d878cdfe Port to lora_isp4520 board 2021-03-14 19:00:20 -07:00
Jm Casler
6730731652 Merge pull request #76 from meshtastic/master
Update from head
2021-03-14 11:32:38 -07:00
Kevin Hester
27c35f69aa Fix flash flippy-flop error after commandline update (thanks @Stephen304) 2021-03-14 12:41:31 +08:00
Kevin Hester
53671283ae change hw_model_deprecated to intentionally break old apps 2021-03-14 10:57:26 +08:00
Kevin Hester
d9fc7b32c3 move hw_model into User 2021-03-14 10:48:20 +08:00
Kevin Hester
9a03536e3d Use enums for hw_model per @sachaw idea. 2021-03-14 10:37:32 +08:00
Kevin Hester
efebb8bb0b remove country from rom support 2021-03-14 10:13:28 +08:00
Kevin Hester
3bd1ae0be4 Merge remote-tracking branch 'root/master' into dev 2021-03-13 14:11:31 +08:00
Kevin Hester
a07291d904 cleanup external notification plugin 2021-03-13 13:32:23 +08:00
Kevin Hester
c0ac457cad lockdown plugins that touch hardware 2021-03-13 13:14:27 +08:00
Kevin Hester
6813a31895 fix hop limit defaults for android 2021-03-13 09:27:44 +08:00
Kevin Hester
8f5251583f Merge pull request #738 from geeksville/eink
Eink
2021-03-13 09:00:57 +08:00
Kevin Hester
c2122a6859 Merge remote-tracking branch 'root/master' into eink 2021-03-13 08:55:06 +08:00
Kevin Hester
6dd65adebd 1.2.10 2021-03-13 08:54:02 +08:00
Kevin Hester
c227143b53 @mc-hamster, I think storeandforward was accidentally wrong (no worries though) 2021-03-13 08:41:50 +08:00
Kevin Hester
cdd696c1ff todo updates 2021-03-13 08:30:07 +08:00
Kevin Hester
3e6817cd18 fix board reboot due to forwarding packets we don't have keys for 2021-03-13 08:29:58 +08:00
Kevin Hester
a5ed607261 fix memory corruption in storeandforward 2021-03-13 08:29:32 +08:00
Kevin Hester
7118200885 Merge pull request #737 from geeksville/eink
Eink
2021-03-12 20:47:48 +08:00
Kevin Hester
b7f9064f0d turn off linux on CI for now 2021-03-12 20:42:22 +08:00
Kevin Hester
5dc5bce1b2 cleanup memtest 2021-03-12 20:41:18 +08:00
Kevin Hester
bc7fef1d1a 1.2.9 2021-03-12 20:22:45 +08:00
Kevin Hester
1908d131ca turn on access control for admin plugin, only allow remote access over special channel 2021-03-12 20:22:40 +08:00
Kevin Hester
8cd2a00a25 fix nasty bug 2021-03-12 15:47:00 +08:00
Kevin Hester
c097852ab0 SERIOUS BUG: we've been discarding devicestate when we should not 2021-03-12 15:45:28 +08:00
Kevin Hester
b02212009a progress on remote settings 2021-03-12 14:10:36 +08:00
Kevin Hester
9d1971f0fa Merge remote-tracking branch 'root/master' into eink 2021-03-11 18:54:08 +08:00
Kevin Hester
2d6261703a fix null pointer exception in storeandforwardplugin. if null the vtable is busted
cc @mc-hamster.  In some cases storeForwardPluginRadio can be null ;-)

~/development/meshtastic/meshtastic-esp32$ bin/exception_decoder.py -e .pio/build/tbeam/firmware.elf ex
stack:
0x401db467: StoreForwardPluginRadio::sendPayload(unsigned int, bool) at /home/kevinh/development/meshtastic/meshtastic-esp32/src/plugins/esp32/StoreForwardPlugin.cpp:235
0x400e7cbd: StoreForwardPlugin::runOnce() at /home/kevinh/development/meshtastic/meshtastic-esp32/src/plugins/esp32/StoreForwardPlugin.cpp:225
0x400d4cca: concurrency::OSThread::run() at /home/kevinh/development/meshtastic/meshtastic-esp32/src/concurrency/OSThread.cpp:45
0x400f015d: ThreadController::runOrDelay() at /home/kevinh/development/meshtastic/meshtastic-esp32/.pio/libdeps/tbeam/Thread/ThreadController.cpp:153
0x400da070: loop() at /home/kevinh/development/meshtastic/meshtastic-esp32/src/main.cpp:621
0x400ff709: loopTask(void*) at /home/kevinh/.platformio/packages/framework-arduinoespressif32/cores/esp32/main.cpp:19

Signed-off-by: Kevin Hester <kevinh@geeksville.com>
2021-03-11 18:53:17 +08:00
Kevin Hester
a97c2ae6eb minimize radioconfig file writes 2021-03-11 18:29:47 +08:00
Kevin Hester
76e2c39c63 completed moving prefs to new files 2021-03-11 17:54:16 +08:00
Kevin Hester
ab9fe42f58 wip - move channels 2021-03-11 13:02:00 +08:00
Jm Casler
9d78ce6193 Merge pull request #75 from meshtastic/master
update from head
2021-03-10 19:29:08 -08:00
Kevin Hester
959b540c02 begin restricting admin ops to the admin channel 2021-03-11 10:01:57 +08:00
Kevin Hester
68781492ad don't block on segger console 2021-03-11 10:00:08 +08:00
Kevin Hester
590e147186 todo updates 2021-03-11 09:59:54 +08:00
Kevin Hester
0b358674ff Merge pull request #736 from geeksville/eink
Eink
2021-03-10 15:59:32 +08:00
Kevin Hester
0df01f2586 Merge remote-tracking branch 'root/master' into eink 2021-03-10 15:30:51 +08:00
Kevin Hester
ca23665463 Merge pull request #735 from meshtastic/sachaw-patch-1
Update main.yml
2021-03-10 15:30:40 +08:00
Kevin Hester
f55ac8e9c9 turn off (buggy) custom_fields in tinygps, and used fixed version of lib 2021-03-10 15:29:25 +08:00
Kevin Hester
6e37fe6343 turn radio back on 2021-03-10 15:24:00 +08:00
Kevin Hester
217bd934d7 fix GPS fixme wrt deletion 2021-03-10 15:21:54 +08:00
Kevin Hester
58715f454c add consolePrintf for C style code 2021-03-10 15:21:30 +08:00
Kevin Hester
772f2a15ff check more error codes 2021-03-09 16:45:40 +08:00
Kevin Hester
5b0d8381b9 fxi log formatting 2021-03-09 15:07:26 +08:00
Kevin Hester
d841d86bbc brownout detect 2021-03-09 15:07:16 +08:00
Kevin Hester
ecaae87b79 make screen optional 2021-03-09 15:07:02 +08:00
Kevin Hester
5835abbcf6 fix eink leds 2021-03-09 15:06:28 +08:00
Sacha Weatherstone
2f7c2a2aea Update main.yml 2021-03-09 16:16:41 +11:00
Kevin Hester
87ec7b09aa add crude check for brownout 2021-03-08 18:12:21 +08:00
Kevin Hester
f8ec072093 make debug console stall until host has downloaded 2021-03-08 17:10:48 +08:00
Kevin Hester
781d2f0ad6 fix warnings 2021-03-08 17:09:35 +08:00
Kevin Hester
7bbd2c0e80 make textmessageplugin optional 2021-03-08 15:30:08 +08:00
Jm Casler
77bac11d82 Merge pull request #74 from meshtastic/master
Update from head
2021-03-07 23:25:52 -08:00
Jm Casler
315cfe4f2d Merge branch 'master' into master 2021-03-07 23:25:43 -08:00
Kevin Hester
707ed75138 fix warnings found in nrf52 build 2021-03-08 15:20:43 +08:00
Kevin Hester
c0e180759d improve debugging on cortex m 2021-03-08 15:20:29 +08:00
Kevin Hester
6ceb423033 TODO update 2021-03-08 12:03:04 +08:00
Kevin Hester
96c4286e7d Merge pull request #733 from geeksville/dev1.2
1.2.6
2021-03-07 10:00:00 +08:00
Kevin Hester
f320ecbde8 1.2.6 2021-03-07 09:51:51 +08:00
Kevin Hester
d014ae0bff fix ls_sleeps communication to device clients 2021-03-07 09:51:17 +08:00
Kevin Hester
12a7934ca1 add RU 2021-03-07 09:34:35 +08:00
Kevin Hester
64bc791e48 fix docs 2021-03-07 09:34:29 +08:00
Kevin Hester
1f33506962 Add RU region 2021-03-06 21:10:36 +08:00
Kevin Hester
ba9a94d026 fix is_low_power detection 2021-03-06 18:00:20 +08:00
Kevin Hester
6f13966d19 fix missing acks for broadcasts 2021-03-06 17:48:35 +08:00
Kevin Hester
96cfad4e57 less logspam 2021-03-06 14:52:26 +08:00
Kevin Hester
a26ebb1b69 Merge pull request #732 from geeksville/dev1.2
Dev1.2 - fix lora message rx
2021-03-06 14:31:59 +08:00
Kevin Hester
7a764efc10 1.2.5 2021-03-06 14:30:53 +08:00
Kevin Hester
49b1f4c5af oops - fix failed text message rx 2021-03-06 14:21:20 +08:00
Kevin Hester
fbe56531d2 Merge remote-tracking branch 'root/master' into dev1.2 2021-03-06 11:20:08 +08:00
Kevin Hester
aa6b29a4b5 fix from address on naks 2021-03-06 11:19:52 +08:00
Kevin Hester
c88b9732eb REALLY IMPORTANT: fix bug with retransmissions not happening 2021-03-06 11:13:33 +08:00
Kevin Hester
2c29e8b179 make nodeinfo & position plugins optional 2021-03-06 10:36:30 +08:00
Kevin Hester
d2d6b8e12f fix log formatting 2021-03-06 10:27:48 +08:00
Kevin Hester
badfaa8545 make error message clearer for packets that are too big 2021-03-06 10:27:31 +08:00
Kevin Hester
c5b67d821d Merge pull request #730 from geeksville/dev1.2
1.2.4
2021-03-05 13:46:18 +08:00
Kevin Hester
63bf7a29f3 Merge branch 'master' into dev1.2 2021-03-05 13:46:10 +08:00
Kevin Hester
845dd1f9e3 1.2.4 2021-03-05 12:39:39 +08:00
Kevin Hester
c9c44a934d don't generate acks for locally sourced msgs 2021-03-05 12:39:31 +08:00
Kevin Hester
2f6981a27f Merge pull request #729 from geeksville/dev1.2
important fixes for 1.2
2021-03-05 12:07:27 +08:00
Kevin Hester
8739469db3 oops typo 2021-03-05 11:49:37 +08:00
Kevin Hester
0c0c0babba combine acks works 2021-03-05 11:44:45 +08:00
Kevin Hester
950b32232f don't send messages the phone sent us back towards the phone 2021-03-05 10:19:27 +08:00
Kevin Hester
2f6034b067 update todos 2021-03-04 22:10:09 +08:00
Kevin Hester
159f7622e4 Merge pull request #724 from crossan007/improvements_to_env_plugin
Improvements to Environmental Measurement Plugin
2021-03-04 22:00:25 +08:00
Kevin Hester
2cc2fa906a Merge branch 'master' into improvements_to_env_plugin 2021-03-04 20:06:27 +08:00
Kevin Hester
249390a75e Merge pull request #727 from geeksville/dev1.2
Dev1.2
2021-03-04 11:41:44 +08:00
Kevin Hester
189cf7ea5e Merge remote-tracking branch 'root/master' into dev1.2 2021-03-04 11:32:34 +08:00
Kevin Hester
56fe211466 1.2.1 2021-03-04 11:29:55 +08:00
Kevin Hester
955d03acb1 fix symbol conflict with new nrf52 libs 2021-03-04 11:28:50 +08:00
Charles Crossan
1b71a0f436 refactor ENV to single multi-inheritance class with cleaner user preferences
Fix merge issues

don't log when drawing blank frames

remove useless logging

re-comment stuff

fix comment

unused var
2021-03-03 20:30:20 -05:00
Charles Crossan
2af9e1431e Improvements to EnvironmentalMeasurementPlugin
Instead of holding onto only the last measurement, hold onto a copy of the last MeshPacket containing a measurement

This will make it easier to display the last time received

make DHT sensor reads more reliable

user preference for Farenheit vs Celsius
2021-03-03 08:23:03 -05:00
Jm
ee961d01ed Merge remote-tracking branch 'origin/master' 2021-03-02 20:50:13 -08:00
Jm Casler
a3343bc1af Revert "Merge pull request #73 from meshtastic/master"
This reverts commit ee04d57a7f.
2021-03-02 20:48:34 -08:00
Jm Casler
ee04d57a7f Merge pull request #73 from meshtastic/master
pulling 1.2 into my fork
2021-03-02 20:46:21 -08:00
Kevin Hester
076251e6a8 todo cleanup 2021-03-03 11:49:09 +08:00
Kevin Hester
fdb3cee3f2 Merge pull request #725 from geeksville/dev1.2
release to devs
2021-03-03 11:47:17 +08:00
Kevin Hester
92e05bc438 release to devs 2021-03-03 11:46:09 +08:00
Kevin Hester
69de2a047f Merge pull request #706 from geeksville/dev1.2
merging 1.2 soon!
2021-03-03 11:23:40 +08:00
Kevin Hester
05c458225f Merge remote-tracking branch 'root/master' into dev1.2 2021-03-02 15:46:16 +08:00
Kevin Hester
866125f2ab fix release script for universal builds 2021-03-02 15:37:56 +08:00
Kevin Hester
d0263e4fa3 Merge branch 'dev' into dev1.2 2021-03-02 15:16:28 +08:00
Kevin Hester
e884ace0ab show build name 2021-03-02 15:16:22 +08:00
Kevin Hester
2ac4106004 add play store link 2021-03-02 12:53:23 +08:00
Kevin Hester
59a63e4aea Merge pull request #721 from geeksville/dev
add (editable) android warning landing page
2021-03-02 12:47:19 +08:00
Kevin Hester
fbdf1f19f0 add (editable) android warning landing page 2021-03-02 12:46:19 +08:00
Kevin Hester
082101f99f wire compat 2021-03-02 12:44:48 +08:00
Kevin Hester
f6761d637f proto update 2021-03-02 11:46:00 +08:00
Jm
2cf704abe0 Crashes after 7 seconds. 2021-02-28 19:35:00 -08:00
Kevin Hester
ef612d0b58 Merge remote-tracking branch 'root/master' into dev1.2
# Conflicts:
#	src/plugins/Plugins.cpp
#	version.properties
2021-03-01 10:26:16 +08:00
Kevin Hester
43673c0598 Merge pull request #719 from geeksville/dev
1.1.50
2021-03-01 09:21:42 +08:00
Kevin Hester
9b45749bf0 1.1.50 2021-03-01 08:33:01 +08:00
Jm Casler
f22243a209 Merge pull request #716 from apt105/master
Add files via upload
2021-02-28 12:30:18 -08:00
Jm
ef32ac5cd4 Update rangetest docs with api example 2021-02-28 11:55:54 -08:00
Jm Casler
52d85c9a41 Partial work from laptop -- non-routers can send a heartbeat 2021-02-27 22:34:53 -08:00
Kevin Hester
a51c08bfb1 todo updated 2021-02-27 14:42:07 +08:00
Kevin Hester
fb47c3a03d move channel and settings protos 2021-02-27 13:17:18 +08:00
Adrian
3b8f5ea099 Add files via upload 2021-02-27 02:01:51 +00:00
Kevin Hester
63e0b53054 1 index channel index 2021-02-27 09:17:58 +08:00
Kevin Hester
989f52494d clean up ack packets 2021-02-26 20:36:22 +08:00
Kevin Hester
0307e4161e get settings message works 2021-02-26 20:10:41 +08:00
Kevin Hester
c7c8b34adf impl get channels/get radio remote api 2021-02-26 15:34:00 +08:00
Jm Casler
1faf6c99db Merge pull request #712 from mc-hamster/master
Update how airtime report gathers data, update stable storeandforward build code
2021-02-25 08:25:42 -08:00
Jm
5f041ea7e9 Update Plugins.cpp 2021-02-25 08:14:07 -08:00
Jm Casler
14271be816 Rename src/plugins/StoreForwardPlugin.h to src/plugins/esp32/StoreForwardPlugin.h 2021-02-25 08:12:57 -08:00
Jm Casler
30d600040b Rename src/plugins/StoreForwardPlugin.cpp to src/plugins/esp32/StoreForwardPlugin.cpp 2021-02-25 08:12:30 -08:00
Jm Casler
10dd2337fc Merge branch 'master' into master 2021-02-25 08:04:41 -08:00
Kevin Hester
5ae4edf8fd move admin out of core proto 2021-02-25 20:36:37 +08:00
Kevin Hester
d3df6d3749 Merge pull request #711 from geeksville/dev
fixes for 1.1.49
2021-02-25 20:04:05 +08:00
Kevin Hester
8ba43e1745 merge latest from master 2021-02-25 20:02:32 +08:00
Kevin Hester
cedd87001d Merge branch 'dev' into dev1.2 2021-02-25 19:49:50 +08:00
Kevin Hester
afc901ddbe fix build. platformio isn't smart enough to git pull latest without
specifying a tag/commit/etc (I haven't tried specifying a branch?)

Without specifying the hash, the build breaks for anyone who already had
a repo (the CI build doesn't see this because it always pulls from scratch)

cc @crossan007 & @mc-hamster
2021-02-25 19:49:34 +08:00
Kevin Hester
e4eb7ab589 Merge branch 'dev' into dev1.2
# Conflicts:
#	proto
#	src/mesh/generated/deviceonly.pb.h
#	src/mesh/generated/mesh.pb.h
#	src/plugins/esp32/EnvironmentalMeasurementPlugin.cpp
2021-02-25 19:44:08 +08:00
Kevin Hester
b052928af2 Merge remote-tracking branch 'root/master' into dev 2021-02-25 19:33:06 +08:00
Kevin Hester
a3b1c4608e Merge pull request #713 from Eninspace/patch-2
Update README.md
2021-02-25 18:28:05 +08:00
Niko
204c42234c Update README.md
Added line break
2021-02-25 13:12:47 +03:00
Jm Casler
0fe046b92d Merge pull request #72 from meshtastic/master
Update from head
2021-02-24 20:27:58 -08:00
Jm
fd6842cb5b Partial Work, stable build. 2021-02-24 20:27:21 -08:00
Jm Casler
4f40128bbe Merge pull request #705 from crossan007/AddEnvironmentMessage
Changes to Environmental Measurement App
2021-02-24 20:26:44 -08:00
Jm Casler
3d4580c03e Merge branch 'master' into AddEnvironmentMessage 2021-02-24 20:20:23 -08:00
Kevin Hester
66638655a5 Merge branch 'dev' into dev1.2
# Conflicts:
#	src/mesh/NodeDB.cpp
2021-02-25 08:56:36 +08:00
Kevin Hester
2d214df76f Merge remote-tracking branch 'root/master' into dev 2021-02-25 08:52:26 +08:00
Kevin Hester
8f1cd33d14 Merge pull request #709 from Eninspace/patch-1
Update README.md
2021-02-25 08:51:47 +08:00
Kevin Hester
b2c47a7dea fix #710. nodes disappearing from map. thanks @DylanHoen for noticing! 2021-02-25 08:50:46 +08:00
Niko
5f3f62ed46 Update README.md
Added Russian community group link
2021-02-24 13:45:21 +03:00
Charles Crossan
bfdad7d4d5 Merge branch 'master' into AddEnvironmentMessage 2021-02-23 20:23:13 -05:00
Kevin Hester
577336d2df oh well - github actions defeated me 2021-02-23 17:16:12 +08:00
Kevin Hester
245638a1d4 get more parallism in CI build 2021-02-23 17:07:38 +08:00
Kevin Hester
ada05a1374 fix linux 2021-02-23 16:56:28 +08:00
Kevin Hester
4b07f9e160 todo update 2021-02-23 14:45:02 +08:00
Kevin Hester
f8d8dc25c0 properly route messages to phone again 2021-02-23 14:35:34 +08:00
Jm
256ba8fa1b partial work, untested 2021-02-22 20:07:19 -08:00
Kevin Hester
aa8b86c6b2 multichannel send/rx kinda works 2021-02-23 11:43:30 +08:00
Kevin Hester
ae6b7e7259 multichannel code is done! (only basic testing completed though) 2021-02-23 10:45:03 +08:00
Kevin Hester
2761c85564 clean up the crypto api 2021-02-23 10:10:35 +08:00
Charles Crossan
ec41c11e58 initialize the last measurement values to something obviously "unread" 2021-02-22 21:00:41 -05:00
Charles Crossan
3a91da5e52 remove UI render comment log spam 2021-02-22 21:00:05 -05:00
Charles Crossan
690b843678 add missing newline 2021-02-22 20:52:10 -05:00
Charles Crossan
3f1ada29ac support independently enabling/disabling the on-device screen and actual measurement 2021-02-22 20:47:35 -05:00
Charles Crossan
0c9df6ccbd disable the app by default 2021-02-22 20:33:42 -05:00
Charles Crossan
b62b01fe7c consume the additional details from OLEDDisplayUiState to handle transitions to/from plugin screens better
It's still a bit janky, but should work

https://github.com/meshtastic/esp8266-oled-ssd1306/pull/2
2021-02-22 20:18:36 -05:00
Charles Crossan
efaf669479 update Environmental Measurement plugin to use radio config prefs 2021-02-22 19:50:51 -05:00
Kevin Hester
94cd96cfde begin multichannel hash impl 2021-02-22 12:57:26 +08:00
Kevin Hester
923ecc9d8a fix printf warnings 2021-02-22 12:57:03 +08:00
Jm Casler
d5cb7ebf3b check in partial work. 2021-02-21 20:15:31 -08:00
Kevin Hester
d6c2e9063a 1.2 channel cleanup 2021-02-22 11:16:38 +08:00
Kevin Hester
3088fe3f91 Stay up-to-date wrt master (cc @mc-hamster)
# Conflicts:
#	src/plugins/Plugins.cpp
#	src/plugins/RangeTestPlugin.cpp
#	src/plugins/StoreForwardPlugin.cpp
2021-02-22 10:39:46 +08:00
Kevin Hester
d4781280b7 update protos 2021-02-22 10:29:27 +08:00
Kevin Hester
008187caa4 1.2 wip - psk work 2021-02-22 10:26:11 +08:00
Charles Crossan
6dffaae72e fix typo 2021-02-21 16:49:35 -05:00
Charles Crossan
ae76ce4024 Implement interface for plugins to have custom UI Frames 2021-02-21 16:46:46 -05:00
Charles Crossan
087945d7cb udpate protobufs 2021-02-21 12:05:23 -05:00
Charles Crossan
fc72d16bcb Disable the plugin by default 2021-02-21 11:48:32 -05:00
Charles Crossan
5817afd4ac fix port number for ENVIRONMENTAL_MEASUREMENT_APP 2021-02-21 11:42:03 -05:00
Charles Crossan
765a6fcc62 Display received measurements in logs and on screen 2021-02-21 11:39:45 -05:00
Charles Crossan
0b20c46b79 standardize log line beginnings 2021-02-21 11:39:20 -05:00
Jm Casler
c6864417c6 Merge pull request #646 from crossan007/AddEnvironmentMessage
Add Environmental Measurement Plugin
2021-02-21 08:31:13 -08:00
Charles Crossan
27f74382aa Fix CI build for non-esp32 devices 2021-02-21 11:09:58 -05:00
Charles Crossan
77961e8e93 Un-derp testing details 2021-02-21 11:00:58 -05:00
Charles Crossan
3c564796e0 2021-02-21 10:55:33 -05:00
Jm
a4e5c7224f Add sawNodeReport to storeforwardplugin 2021-02-20 23:53:53 -08:00
Jm
7ffe601743 Changes to how airtime noise is gathered. 2021-02-20 23:53:17 -08:00
Kevin Hester
c6091338ab admin ops 2021-02-21 14:03:44 +08:00
Kevin Hester
99467cd874 1.2 WIP at least doesn't crash 2021-02-21 12:59:47 +08:00
Kevin Hester
587102f6bc fix android lint warning 2021-02-21 11:36:34 +08:00
Jm Casler
f41d79d586 Merge pull request #703 from mc-hamster/master
#701, #697 - Bug fix and update to debug output.
2021-02-20 00:53:07 -08:00
Jm Casler
fc5030ff7b Merge branch 'master' into master 2021-02-20 00:46:03 -08:00
Jm
ca40cb4be3 Update RangeTestPlugin.cpp 2021-02-20 00:43:56 -08:00
Jm
e4141df0e9 #697 GPIO 12 is an odd pin. 2021-02-20 00:34:25 -08:00
Jm
c690f81574 #697 Disable internal pullup on this board. 2021-02-19 18:19:14 -08:00
Jm
47b942ca00 #697 - Fixed button selection for tlora v2 1.6 2021-02-19 17:46:15 -08:00
Jm
621313d63c #701 - Add system up time to debug output 2021-02-19 16:58:52 -08:00
Jm Casler
d361935c09 Merge pull request #699 from crossan007/fix-exception-decoder
Fix exception decoder output
2021-02-19 16:46:46 -08:00
Charles Crossan
a2bea87332 Fix exception decoder output 2021-02-19 19:24:18 -05:00
Jm
13b8c140b4 Update StoreForwardPlugin.md 2021-02-18 21:47:16 -08:00
Kevin Hester
69a11e7375 WIP phone api changes for dev1.2 2021-02-17 19:04:41 +08:00
Kevin Hester
60a01567d9 remove region specific builds 2021-02-17 19:01:20 +08:00
Kevin Hester
f064e56dc9 fix esp32 build for dev1.2 2021-02-17 16:17:46 +08:00
Kevin Hester
42ae27973e WIP 1.2 move routing into plugin 2021-02-17 13:06:23 +08:00
Jm Casler
f6e2695a4d Merge pull request #695 from mc-hamster/master
Range test plugin done-enough
2021-02-16 18:54:42 -08:00
Jm
e840465ef3 fix for failed builds on nrf and linux 2021-02-16 18:46:16 -08:00
Jm Casler
f24e8e5f5c Rename src/plugins/RangeTestPlugin.h to src/plugins/esp32/RangeTestPlugin.h 2021-02-16 18:36:58 -08:00
Jm Casler
6376ab51f1 Moving to plugins/esp32 2021-02-16 18:36:30 -08:00
Jm Casler
bd29d78a29 Update StoreForwardPlugin.md 2021-02-16 18:24:37 -08:00
Jm Casler
b35cd76854 Update StoreForwardPlugin.md 2021-02-16 18:19:24 -08:00
Kevin Hester
205282c4bc show hwvendor at boot 2021-02-17 10:08:33 +08:00
Jm
a4fd74b58e Update Plugins.cpp 2021-02-16 18:07:02 -08:00
Jm Casler
25072f0637 Merge branch 'master' into master 2021-02-16 17:44:17 -08:00
Jm
937955b36d Updating range test and storeforward. 2021-02-16 17:42:46 -08:00
Jm
2e8867eda6 Merge branch 'master' of https://github.com/mc-hamster/Meshtastic-device 2021-02-16 17:42:25 -08:00
Jm Casler
bd399a134b Merge pull request #693 from geoffreyheller/tlora-v2116-tx-pin-fix
Fix for non-working tx pin
2021-02-16 12:31:09 -08:00
Kevin Hester
45caf394f0 WIP multichannel support 2021-02-16 15:41:52 +08:00
Jm Casler
d67e2187d0 Update RangeTestPlugin.md 2021-02-15 21:17:06 -08:00
geoffreyheller
c8584d576a Fix for non-working tx pin 2021-02-15 19:33:35 -08:00
Jm Casler
418a12e75f Merge pull request #692 from crossan007/add_set_channel
add handleSetChannel
2021-02-15 18:49:29 -08:00
Charles Crossan
05d237ee84 add handleSetChannel 2021-02-15 21:02:30 -05:00
Jm
9a044f31a3 Tweak to the startup condition of the range test plugin. 2021-02-15 09:11:28 -08:00
Jm
8320754b98 Added recommended settings as a sender 2021-02-14 21:45:26 -08:00
Jm
703ce2e292 Keep device from sleeping while transmitting in range test plugin. 2021-02-14 21:34:47 -08:00
Jm
c9353ebee3 Update RangeTestPlugin.md 2021-02-14 21:24:41 -08:00
Jm Casler
9601868e0f Merge pull request #71 from mc-hamster/RangeTestPlugin
Merge pull request #69 from mc-hamster/master
2021-02-14 20:18:41 -08:00
Jm
fea2228b16 Update RangeTestPlugin.md 2021-02-14 20:17:16 -08:00
Jm
7d4ce483c5 Update RangeTestPlugin.md 2021-02-14 20:16:29 -08:00
Jm
e6605e5ac8 Store Forward can see All(?) rx packets! 2021-02-14 20:13:52 -08:00
Jm Casler
2472d6fd1b Merge pull request #70 from meshtastic/master
pull from main
2021-02-14 18:40:38 -08:00
Jm
3311146aba Turning storeforward back on for @geeksville 2021-02-14 16:19:06 -08:00
Jm Casler
793528b057 Merge pull request #69 from mc-hamster/master
#671 Range test plugin
2021-02-14 16:18:36 -08:00
Jm
b8adaf6fbe #671 Range test plugin 2021-02-14 16:17:40 -08:00
Jm
24329a26de Framework for storeforward 2021-02-14 14:01:08 -08:00
Jm
f1a65f9d0e Update to store and forward. Don't merge this to main! 2021-02-14 13:31:11 -08:00
Jm
ae46b3df32 Update StoreForwardPlugin.md 2021-02-14 11:10:56 -08:00
Jm
d5c1e3c6e0 Update StoreForwardPlugin.md 2021-02-14 11:00:11 -08:00
Jm
a95f612452 Update StoreForwardPlugin.md 2021-02-14 10:56:08 -08:00
Jm Casler
3201d1c3bc #668 Partial work for store & forward 2021-02-14 08:44:49 -08:00
Kevin Hester
2b10a03178 Merge pull request #690 from geeksville/dev
oops - 1.1.47 had bad bug
2021-02-14 15:26:00 +08:00
Kevin Hester
80fb7e4ab8 Merge remote-tracking branch 'root/master' into dev 2021-02-14 15:15:28 +08:00
Kevin Hester
7c1ddd9447 1.1.48 2021-02-14 15:13:58 +08:00
Kevin Hester
c55074f7fe oops: we should crash if we >MAX_THREADS, also raise max threads 2021-02-14 15:06:49 +08:00
Jm Casler
36643cf5f5 #671 range test plugin documentation
#671 range test plugin documentation
2021-02-13 22:40:04 -08:00
Jm Casler
92a62d93ef #671 Rangetest - This just needs to be tested. 2021-02-13 22:21:01 -08:00
Kevin Hester
450e877cfb fix #661 - create $archivedir 2021-02-14 14:14:50 +08:00
Kevin Hester
c0fbfccf43 Merge pull request #689 from geeksville/dev
Dev
2021-02-14 13:48:39 +08:00
Kevin Hester
51ccc3aa9e 1.1.47 2021-02-14 12:28:27 +08:00
Kevin Hester
e35f137986 clean up nodeinfo broadcasts and move into plugin 2021-02-14 12:27:10 +08:00
Kevin Hester
71c35304d6 delay position plugin start until 60 secs after first boot 2021-02-14 12:26:51 +08:00
Kevin Hester
c1733a4ac6 Merge remote-tracking branch 'root/master' into dev 2021-02-14 12:07:11 +08:00
Kevin Hester
66a7e8eab9 oops - we were never sending 'fixed' gps positions to other nodes 2021-02-14 12:02:24 +08:00
Kevin Hester
a872231f8a clean up position broadcasts, send them even if we don't have gps lock 2021-02-14 11:57:48 +08:00
Kevin Hester
52ec4d511c oops - we weren't properly stripping timestamps from new style positions 2021-02-14 11:37:32 +08:00
Jm Casler
aa7fb86798 Merge pull request #687 from sachaw/master
Fix report formatting
2021-02-13 14:02:12 -08:00
Sacha Weatherstone
875eeb699c Fix report formatting 2021-02-13 19:51:31 +11:00
Jm Casler
b239b4dc57 Merge pull request #686 from mc-hamster/master
Finished and partial work
2021-02-12 19:07:13 -08:00
Jm
19db5ba421 Merge branch 'master' of https://github.com/mc-hamster/Meshtastic-device 2021-02-12 18:53:08 -08:00
Jm
e225af28dc Disable plugins 2021-02-12 18:52:31 -08:00
Jm Casler
eecf89a9c0 Merge pull request #68 from meshtastic/master
from main to my fork
2021-02-12 18:49:39 -08:00
Jm
c9b1ee532d Update deviceonly.pb.h 2021-02-12 18:49:24 -08:00
Jm
8c27baae84 Update platformio.ini 2021-02-12 18:48:18 -08:00
Kevin Hester
8c225a3c65 disable lora while updating, show "Updating" on oled screen 2021-02-12 13:48:12 +08:00
Kevin Hester
da03490310 Merge branch 'dev' into portduino 2021-02-10 16:20:29 +08:00
Kevin Hester
aedca25fa8 Merge branch 'dev' into portduino 2021-02-10 15:51:34 +08:00
Jm
b29bcbbd41 #682 Exposing the actual radio center frequency to /static/report 2021-02-09 21:59:00 -08:00
Jm
ca8a0ca8d2 update rangetest 2021-02-07 19:20:29 -08:00
Jm
cb541d75a9 Update radio-settings.md with note of coverage. 2021-02-07 13:19:52 -08:00
Jm Casler
4ee01acb40 Change where the location information is taken from. 2021-02-07 09:31:29 -08:00
Jm
d678c48884 work on rangetestplugin and storeforwardplugin
done for the night.
2021-02-06 23:29:18 -08:00
Jm
9f9f02fc6f Reverting platform.ini back to the way it was. 2021-02-06 21:38:54 -08:00
Jm Casler
abf135abce Merge pull request #66 from meshtastic/master
mc-hamster/meshtastic-device <- meshtastic/meshtastic-device
2021-02-06 19:56:44 -08:00
Kevin Hester
c25efac0c1 portduino wip 2021-02-07 09:17:46 +08:00
Jm
22af1b551a Add note about antenna gain. 2021-02-06 10:06:20 -08:00
Jm
c696d226b2 Merge branch 'master' of https://github.com/mc-hamster/Meshtastic-device 2021-02-06 10:04:13 -08:00
Jm
9035a06b4a Add link budget to radio-settings.md 2021-02-06 10:02:23 -08:00
Jm Casler
056940a4ad Documentation 2021-02-06 09:13:49 -08:00
Jm
82db1f1db6 Merge branch 'master' of https://github.com/mc-hamster/Meshtastic-device 2021-02-03 08:10:15 -08:00
Jm
f46059ec4c update to range test plugin 2021-02-03 08:10:13 -08:00
Jm
0c71de4e59 Update for rangetest plugin 2021-02-03 08:09:59 -08:00
Jm Casler
0fa654e53a update radio-settings.md 2021-02-03 07:01:35 -08:00
Jm Casler
45c17659cc radio-settings.md - add note about TCXO stability 2021-02-03 01:04:23 -08:00
Jm Casler
b901f8d9ae Update to radio-settings.md 2021-02-03 00:15:41 -08:00
Jm Casler
9c60a7966f Update radio-settings.md 2021-02-03 00:01:52 -08:00
Jm Casler
6d66a53f8d Another update to radio-settings.md 2021-02-02 23:59:34 -08:00
Jm Casler
324627482a Update radio-settings.md 2021-02-02 23:57:25 -08:00
Jm Casler
0c6c189028 Update to radio-settings.md 2021-02-02 23:01:05 -08:00
Jm Casler
8a68ae0d04 Update radio-settings.md 2021-02-02 22:47:16 -08:00
Jm Casler
5661e5dad6 Update radio-settings.md 2021-02-02 22:43:24 -08:00
Jm
e9affb50d2 #674 add support for lora32 v1.3 2021-02-02 17:34:50 -08:00
Jm Casler
c00173dbd2 partial work 2021-01-31 18:20:08 -08:00
Jm Casler
e8c6fccd63 #671 Range Test Plugin
Initial work for Range Test Plugin
2021-01-31 09:12:36 -08:00
Jm Casler
487b8c6e9e #668 Initial work for Store & Forward Plugin 2021-01-31 09:12:01 -08:00
Jm
b2481d1450 Merge branch 'master' of https://github.com/mc-hamster/Meshtastic-device 2021-01-30 19:42:08 -08:00
Jm
242bcc8353 Update documentation location to the main mesthastic repo 2021-01-30 09:52:21 -08:00
153 changed files with 8082 additions and 1734 deletions

View File

@@ -7,7 +7,7 @@
is appreciated." This will allow other devs to potentially save you time by not accidentially duplicating work etc...
- Please do not check in files that don't have real changes
- Please do not reformat lines that you didn't have to change the code on
- We recommend using the [Visual Studio Code](https://platformio.org/install/ide?install=vscode) editor,
- We recommend using the [Visual Studio Code](https://platformio.org/install/ide?install=vscode) editor and the 'clang-format' extension,
because automatically follows our indentation rules and it's auto reformatting will not cause spurious changes to lines.
- If your PR fixes a bug, mention "fixes #bugnum" somewhere in your pull request description.
- If your other co-developers have comments on your PR please tweak as needed.

View File

@@ -4,21 +4,21 @@ on:
- pull_request
jobs:
main:
name: Main
setup:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@master
- name: Checkout submodules
uses: textbook/git-checkout-submodule-action@master
- name: Checkout code
uses: actions/checkout@v2
with:
submodules: true
- name: Setup Python
uses: actions/setup-python@master
uses: actions/setup-python@v2
with:
python-version: 3.x
- name: Install Platform IO
- name: Install Platform IO and meshtastic-python
run: |
python -m pip install --upgrade pip
pip install -U platformio
pip install -U platformio meshtastic
- name: Install extra python tools
run: |
pip install -U adafruit-nrfutil
@@ -31,6 +31,11 @@ jobs:
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
- name: Build for native
run: platformio run -e native
- name: Integration test
run: |
.pio/build/native/program &
sleep 1
python3 -c 'from meshtastic.test import testSimulator; testSimulator()'

3
.gitignore vendored
View File

@@ -15,7 +15,8 @@ Thumbs.db
.built
.context
.cproject
.idea/*
.vagrant
nanopb*
flash.uf2
cmake-build*

7
.idea/codeStyles/Project.xml generated Normal file
View File

@@ -0,0 +1,7 @@
<component name="ProjectCodeStyleConfiguration">
<code_scheme name="Project" version="173">
<clangFormatSettings>
<option name="ENABLED" value="true" />
</clangFormatSettings>
</code_scheme>
</component>

5
.idea/codeStyles/codeStyleConfig.xml generated Normal file
View File

@@ -0,0 +1,5 @@
<component name="ProjectCodeStyleConfiguration">
<state>
<option name="USE_PER_PROJECT_SETTINGS" value="true" />
</state>
</component>

2
.idea/meshtastic-esp32.iml generated Normal file
View File

@@ -0,0 +1,2 @@
<?xml version="1.0" encoding="UTF-8"?>
<module classpath="CMake" type="CPP_MODULE" version="4" />

4
.idea/misc.xml generated Normal file
View File

@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="CMakeWorkspace" PROJECT_DIR="$PROJECT_DIR$" />
</project>

8
.idea/modules.xml generated Normal file
View File

@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/.idea/meshtastic-esp32.iml" filepath="$PROJECT_DIR$/.idea/meshtastic-esp32.iml" />
</modules>
</component>
</project>

9
.idea/vcs.xml generated Normal file
View File

@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$" vcs="Git" />
<mapping directory="$PROJECT_DIR$/design" vcs="Git" />
<mapping directory="$PROJECT_DIR$/proto" vcs="Git" />
<mapping directory="$PROJECT_DIR$/sdk-nrfxlib" vcs="Git" />
</component>
</project>

137
.idea/workspace.xml generated Normal file
View File

@@ -0,0 +1,137 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="CMakeRunConfigurationManager" shouldGenerate="true" shouldDeleteObsolete="true">
<generated>
<config projectName="meshtastic-esp32" targetName="Debug" />
<config projectName="meshtastic-esp32" targetName="Production" />
<config projectName="meshtastic-esp32" targetName="Z_DUMMY_TARGET" />
</generated>
</component>
<component name="CMakeSettings">
<configurations>
<configuration PROFILE_NAME="native" CONFIG_NAME="native" ENABLED="true" />
</configurations>
</component>
<component name="ChangeListManager">
<list default="true" id="58922733-b05b-4b90-9655-b9b18914977a" name="Default Changelist" comment="">
<change beforePath="$PROJECT_DIR$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" />
<change beforePath="$PROJECT_DIR$/docs/software/TODO.md" beforeDir="false" afterPath="$PROJECT_DIR$/docs/software/TODO.md" afterDir="false" />
<change beforePath="$PROJECT_DIR$/proto" beforeDir="false" afterPath="$PROJECT_DIR$/proto" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/mesh/generated/mesh.pb.h" beforeDir="false" afterPath="$PROJECT_DIR$/src/mesh/generated/mesh.pb.h" afterDir="false" />
</list>
<option name="SHOW_DIALOG" value="false" />
<option name="HIGHLIGHT_CONFLICTS" value="true" />
<option name="HIGHLIGHT_NON_ACTIVE_CHANGELIST" value="false" />
<option name="LAST_RESOLUTION" value="IGNORE" />
</component>
<component name="ExecutionTargetManager" SELECTED_TARGET="CMakeBuildProfile:native" />
<component name="Git.Settings">
<option name="RECENT_GIT_ROOT_PATH" value="$PROJECT_DIR$" />
</component>
<component name="ProjectId" id="1pmWHw2wau2TbdKvXvmQUB0EUE9" />
<component name="ProjectLevelVcsManager" settingsEditedManually="true" />
<component name="ProjectViewState">
<option name="hideEmptyMiddlePackages" value="true" />
<option name="showLibraryContents" value="true" />
</component>
<component name="PropertiesComponent">
<property name="ASKED_ADD_EXTERNAL_FILES" value="true" />
<property name="RunOnceActivity.OpenProjectViewOnStart" value="true" />
<property name="RunOnceActivity.ShowReadmeOnStart" value="true" />
<property name="WebServerToolWindowFactoryState" value="false" />
<property name="cf.advertisement.text.overridden" value="true" />
<property name="cf.first.check.clang-format" value="false" />
<property name="node.js.detected.package.eslint" value="true" />
<property name="node.js.detected.package.tslint" value="true" />
<property name="node.js.path.for.package.eslint" value="project" />
<property name="node.js.path.for.package.tslint" value="project" />
<property name="node.js.selected.package.eslint" value="(autodetect)" />
<property name="node.js.selected.package.tslint" value="(autodetect)" />
<property name="settings.editor.selected.configurable" value="CMakeSettings" />
</component>
<component name="RunManager" selected="PlatformIO.PlatformIO Upload">
<configuration default="true" type="CLion_Remote" version="1" remoteCommand="tcp:localhost:2345" symbolFile="" sysroot="">
<debugger kind="GDB" isBundled="true" />
<method v="2" />
</configuration>
<configuration name="gdbremote-localhost-2345" type="CLion_Remote" version="1" remoteCommand="tcp:localhost:2345" symbolFile="" sysroot="">
<debugger kind="GDB" isBundled="true" />
<method v="2" />
</configuration>
<configuration name="Z_DUMMY_TARGET" type="CMakeRunConfiguration" factoryName="Application" REDIRECT_INPUT="false" ELEVATE="false" PASS_PARENT_ENVS_2="true" PROJECT_NAME="meshtastic-esp32" TARGET_NAME="Z_DUMMY_TARGET" CONFIG_NAME="native" RUN_TARGET_PROJECT_NAME="meshtastic-esp32" RUN_TARGET_NAME="Z_DUMMY_TARGET">
<method v="2">
<option name="com.jetbrains.cidr.execution.CidrBuildBeforeRunTaskProvider$BuildBeforeRunTask" enabled="true" />
</method>
</configuration>
<configuration default="true" type="GradleAppRunConfiguration" factoryName="Application" REDIRECT_INPUT="false" ELEVATE="false" PASS_PARENT_ENVS_2="true">
<method v="2">
<option name="com.jetbrains.cidr.cpp.gradle.execution.GradleNativeBuildBeforeRunTaskProvider$BuildBeforeRunTask" enabled="true" />
</method>
</configuration>
<configuration name="PlatformIO Debug" type="platformio" factoryName="PlatformIO Debug" REDIRECT_INPUT="false" ELEVATE="false" PASS_PARENT_ENVS_2="true" PROJECT_NAME="meshtastic-esp32" TARGET_NAME="Debug" CONFIG_NAME="native" RUN_TARGET_PROJECT_NAME="meshtastic-esp32" RUN_TARGET_NAME="Debug">
<method v="2">
<option name="com.jetbrains.cidr.execution.CidrBuildBeforeRunTaskProvider$BuildBeforeRunTask" enabled="true" />
</method>
</configuration>
<configuration name="PlatformIO Upload" type="platformio" factoryName="PlatformIO Upload" REDIRECT_INPUT="false" ELEVATE="false" PASS_PARENT_ENVS_2="true" PROJECT_NAME="meshtastic-esp32" TARGET_NAME="Production" CONFIG_NAME="native" RUN_TARGET_PROJECT_NAME="meshtastic-esp32" RUN_TARGET_NAME="Production">
<method v="2">
<option name="com.jetbrains.cidr.execution.CidrBuildBeforeRunTaskProvider$BuildBeforeRunTask" enabled="true" />
</method>
</configuration>
<list>
<item itemvalue="CMake Application.Z_DUMMY_TARGET" />
<item itemvalue="GDB Remote Debug.gdbremote-localhost-2345" />
<item itemvalue="PlatformIO.PlatformIO Debug" />
<item itemvalue="PlatformIO.PlatformIO Upload" />
</list>
</component>
<component name="SpellCheckerSettings" RuntimeDictionaries="0" Folders="0" CustomDictionaries="0" DefaultDictionary="application-level" UseSingleDictionary="true" transferred="true" />
<component name="TaskManager">
<task active="true" id="Default" summary="Default task">
<changelist id="58922733-b05b-4b90-9655-b9b18914977a" name="Default Changelist" comment="" />
<created>1615788661896</created>
<option name="number" value="Default" />
<option name="presentableId" value="Default" />
<updated>1615788661896</updated>
<workItem from="1615788663210" duration="6661000" />
<workItem from="1615938346019" duration="1208000" />
<workItem from="1615971126983" duration="5945000" />
</task>
<servers />
</component>
<component name="TypeScriptGeneratedFilesManager">
<option name="version" value="3" />
</component>
<component name="Vcs.Log.Tabs.Properties">
<option name="TAB_STATES">
<map>
<entry key="MAIN">
<value>
<State />
</value>
</entry>
</map>
</option>
<option name="oldMeFiltersMigrated" value="true" />
</component>
<component name="XDebuggerManager">
<breakpoint-manager>
<breakpoints>
<line-breakpoint enabled="true" type="com.jetbrains.cidr.execution.debugger.OCBreakpointType">
<url>file://$PROJECT_DIR$/src/mesh/StreamAPI.cpp</url>
<line>20</line>
<option name="timeStamp" value="4" />
</line-breakpoint>
<line-breakpoint enabled="true" type="com.jetbrains.cidr.execution.debugger.OCBreakpointType">
<url>file://$PROJECT_DIR$/src/mesh/wifi/WiFiServerAPI.cpp</url>
<line>53</line>
<option name="timeStamp" value="6" />
</line-breakpoint>
</breakpoints>
</breakpoint-manager>
</component>
<component name="XSLT-Support.FileAssociations.UIState">
<expand />
<select />
</component>
</project>

View File

@@ -2,6 +2,7 @@
// See http://go.microsoft.com/fwlink/?LinkId=827846
// for the documentation about the extensions.json format
"recommendations": [
"platformio.platformio-ide"
"platformio.platformio-ide",
"xaver.clang-format"
]
}

12
.vscode/settings.json vendored
View File

@@ -68,5 +68,15 @@
"protobufs",
"wifi"
],
"C_Cpp.dimInactiveRegions": true
"C_Cpp.dimInactiveRegions": true,
"cmake.configureOnOpen": true,
"protoc": {
"compile_on_save": false,
"compile_all_path": "/home/kevinh/development/meshtastic/meshtastic-esp32/proto",
"options": [
"--java_out=/tmp",
"-I=/home/kevinh/development/meshtastic/meshtastic-esp32/proto"
]
},
"editor.formatOnSave": true
}

36
CMakeLists.txt Normal file
View File

@@ -0,0 +1,36 @@
# !!! WARNING !!! AUTO-GENERATED FILE, PLEASE DO NOT MODIFY IT AND USE
# https://docs.platformio.org/page/projectconf/section_env_build.html#build-flags
#
# If you need to override existing CMake configuration or add extra,
# please create `CMakeListsUser.txt` in the root of project.
# The `CMakeListsUser.txt` will not be overwritten by PlatformIO.
cmake_minimum_required(VERSION 3.13)
set(CMAKE_SYSTEM_NAME Generic)
set(CMAKE_C_COMPILER_WORKS 1)
set(CMAKE_CXX_COMPILER_WORKS 1)
project("meshtastic-esp32" C CXX)
include(CMakeListsPrivate.txt)
if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/CMakeListsUser.txt)
include(CMakeListsUser.txt)
endif()
include_directories("$ENV{HOME}/.platformio/packages/framework-portduino")
include_directories("/usr/include")
add_custom_target(
Production ALL
COMMAND platformio -c clion run "$<$<NOT:$<CONFIG:All>>:-e${CMAKE_BUILD_TYPE}>"
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
)
add_custom_target(
Debug ALL
COMMAND platformio -c clion run --target debug "$<$<NOT:$<CONFIG:All>>:-e${CMAKE_BUILD_TYPE}>"
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
)
add_executable(Z_DUMMY_TARGET ${SRC_LIST})

2041
CMakeListsPrivate.txt Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -4,11 +4,7 @@ set -e
VERSION=`bin/buildinfo.py`
COUNTRIES="US EU433 EU865 CN JP ANZ KR"
#COUNTRIES=US
#COUNTRIES=CN
BOARDS_ESP32="tlora-v2 tlora-v1 tlora-v2-1-1.6 tbeam heltec tbeam0.7"
BOARDS_ESP32="tlora-v2 tlora-v1 tlora_v1_3 tlora-v2-1-1.6 tbeam heltec tbeam0.7"
#BOARDS_ESP32=tbeam
# FIXME note nrf52840dk build is for some reason only generating a BIN file but not a HEX file nrf52840dk-geeksville is fine
@@ -21,33 +17,24 @@ 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
# build the named environment and copy the bins to the release directory
function do_build() {
BOARD=$1
COUNTRY=$2
isNrf=$3
echo "Building $COUNTRY for $BOARD with $PLATFORMIO_BUILD_FLAGS"
echo "Building for $BOARD with $PLATFORMIO_BUILD_FLAGS"
rm -f .pio/build/$BOARD/firmware.*
# The shell vars the build tool expects to find
export APP_VERSION=$VERSION
# Are we building a universal/regionless rom?
if [ "x$COUNTRY" != "x" ]
then
export HW_VERSION="1.0-$COUNTRY"
export COUNTRY
basename=firmware-$BOARD-$COUNTRY-$VERSION
else
export HW_VERSION="1.0"
unset COUNTRY
basename=universal/firmware-$BOARD-$VERSION
fi
export HW_VERSION="1.0"
basename=universal/firmware-$BOARD-$VERSION
pio run --environment $BOARD # -v
SRCELF=.pio/build/$BOARD/firmware.elf
@@ -69,10 +56,6 @@ function do_boards() {
declare boards=$1
declare isNrf=$2
for board in $boards; do
for country in $COUNTRIES; do
do_build $board $country "$isNrf"
done
# Build universal
do_build $board "" "$isNrf"
done
@@ -92,7 +75,7 @@ pio run --environment tbeam -t buildfs
cp .pio/build/tbeam/spiffs.bin $OUTDIR/bins/universal/spiffs-$VERSION.bin
# keep the bins in archive also
cp $OUTDIR/bins/firmware* $OUTDIR/bins/universal/spiffs* $OUTDIR/elfs/firmware* $OUTDIR/bins/universal/firmware* $OUTDIR/elfs/universal/firmware* $ARCHIVEDIR
cp $OUTDIR/bins/universal/spiffs* $OUTDIR/bins/universal/firmware* $OUTDIR/elfs/universal/firmware* $ARCHIVEDIR
echo Updating android bins $OUTDIR/forandroid
rm -rf $OUTDIR/forandroid
@@ -107,12 +90,12 @@ release. It is used by the android app for forcing software updates. Do not ed
Generated by bin/buildall.sh -->
<resources>
<string name="cur_firmware_version">$VERSION</string>
<string name="cur_firmware_version" translatable="false">$VERSION</string>
</resources>
XML
echo Generating $ARCHIVEDIR/firmware-$VERSION.zip
rm -f $ARCHIVEDIR/firmware-$VERSION.zip
zip --junk-paths $ARCHIVEDIR/firmware-$VERSION.zip $ARCHIVEDIR/spiffs-$VERSION.bin $OUTDIR/bins/firmware-*-$VERSION.* images/system-info.bin bin/device-install.sh bin/device-update.sh
zip --junk-paths $ARCHIVEDIR/firmware-$VERSION.zip $ARCHIVEDIR/spiffs-$VERSION.bin $OUTDIR/bins/universal/firmware-*-$VERSION.* images/system-info.bin bin/device-install.sh bin/device-update.sh
echo BUILT ALL

38
bin/build-nightly.sh Executable file
View File

@@ -0,0 +1,38 @@
#!/bin/bash
source ~/.bashrc
# Meshtastic Nightly Build Script.
# McHamster (jm@casler.org)
#
# This is the script that is used for the nightly build server.
#
# It's probably not useful for most people, but you may want to run your own
# nightly builds.
#
# The last line of ~/.bashrc contains an inclusion of platformio in the path.
# Without this, the build script won't run from the crontab:
#
# export PATH="$HOME/.platformio/penv/bin:$PATH"
#
# The crontab contains:
# 0 2 * * * cd ~/meshtastic/github/meshtastic && source "~/.bashrc"; ./build-nightly.sh > ~/cronout.txt 2> ~/cronout.txt
cd Meshtastic-device
git pull
bin/build-all.sh
date_stamp=$(date +'%Y-%m-%d')
cd ..
# TODO: Archive the same binaries used by the build-all script.
#zip -r meshtastic_device_nightly_${date_stamp} Meshtastic-device/release/latest/bins
cp Meshtastic-device/release/archive/`ls -t ./Meshtastic-device/release/archive/| head -1` meshtastic_device_nightly_${date_stamp}.zip
# Copy the file to the webserver
scp meshtastic_device_nightly_${date_stamp}.zip jm@10.11.12.20:/volume1/web/meshtastic/nightly_builds/
# Delete the local copy
rm meshtastic_device_nightly_${date_stamp}.zip

View File

@@ -1,21 +1,24 @@
#!/bin/sh
PYTHON=${PYTHON:-python}
set -e
# Usage info
show_help() {
cat << EOF
Usage: ${0##*/} [-h] [-p ESPTOOL_PORT] [-f FILENAME]
Usage: ${0##*/} [-h] [-p ESPTOOL_PORT] [-P PYTHON] [-f FILENAME]
Flash image file to device, but first erasing and writing system information"
-h Display this help and exit
-p ESPTOOL_PORT Set the environment variable for ESPTOOL_PORT. If not set, ESPTOOL iterates all ports (Dangerrous).
-f FILENAME The .bin file to flash. Custom to your device type and region.
-P PYTHON Specify alternate python interpreter to use to invoke esptool. (Default: "$PYTHON")
-f FILENAME The .bin file to flash. Custom to your device type and region.
EOF
}
while getopts ":h:p:f:" opt; do
while getopts ":hp:P:f:" opt; do
case "${opt}" in
h)
show_help
@@ -23,6 +26,8 @@ while getopts ":h:p:f:" opt; do
;;
p) export ESPTOOL_PORT=${OPTARG}
;;
P) PYTHON=${OPTARG}
;;
f) FILENAME=${OPTARG}
;;
*)
@@ -36,10 +41,10 @@ shift "$((OPTIND-1))"
if [ -f "${FILENAME}" ]; then
echo "Trying to flash ${FILENAME}, but first erasing and writing system information"
esptool.py --baud 921600 erase_flash
esptool.py --baud 921600 write_flash 0x1000 system-info.bin
esptool.py --baud 921600 write_flash 0x00390000 spiffs-*.bin
esptool.py --baud 921600 write_flash 0x10000 ${FILENAME}
$PYTHON -m esptool --baud 921600 erase_flash
$PYTHON -m esptool --baud 921600 write_flash 0x1000 system-info.bin
$PYTHON -m esptool --baud 921600 write_flash 0x00390000 spiffs-*.bin
$PYTHON -m esptool --baud 921600 write_flash 0x10000 ${FILENAME}
else
echo "Invalid file: ${FILENAME}"
show_help

View File

@@ -1,19 +1,22 @@
#!/bin/sh
PYTHON=${PYTHON:-python}
# Usage info
show_help() {
cat << EOF
Usage: ${0##*/} [-h] [-p ESPTOOL_PORT] -f FILENAME
Usage: ${0##*/} [-h] [-p ESPTOOL_PORT] [-P PYTHON] -f FILENAME
Flash image file to device, leave existing system intact."
-h Display this help and exit
-p ESPTOOL_PORT Set the environment variable for ESPTOOL_PORT. If not set, ESPTOOL iterates all ports (Dangerrous).
-f FILENAME The .bin file to flash. Custom to your device type and region.
-P PYTHON Specify alternate python interpreter to use to invoke esptool. (Default: "$PYTHON")
-f FILENAME The .bin file to flash. Custom to your device type and region.
EOF
}
while getopts ":h:p:f:" opt; do
while getopts ":hp:P:f:" opt; do
case "${opt}" in
h)
show_help
@@ -21,6 +24,8 @@ while getopts ":h:p:f:" opt; do
;;
p) export ESPTOOL_PORT=${OPTARG}
;;
P) PYTHON=${OPTARG}
;;
f) FILENAME=${OPTARG}
;;
*)
@@ -34,7 +39,9 @@ shift "$((OPTIND-1))"
if [ -f "${FILENAME}" ]; then
echo "Trying to flash update ${FILENAME}."
esptool.py --baud 921600 write_flash 0x10000 ${FILENAME}
$PYTHON -m esptool --baud 921600 write_flash 0x10000 ${FILENAME}
echo "Erasing the otadata partition, which will turn off flash flippy-flop and force the first image to be used"
$PYTHON -m esptool --baud 921600 erase_region 0xe000 0x2000
else
echo "Invalid file: ${FILENAME}"
show_help

View File

@@ -241,7 +241,7 @@ def print_addr(name, value, resolver):
def print_stack_full(lines, resolver):
print("stack:")
for line in lines:
print(line.offset + ":")
print(str(line.offset) + ":")
for content in line.content:
print(" " + resolver.resolve_stack_addr(content))

View File

@@ -6,6 +6,8 @@ BOOTDIR=/home/kevinh/development/meshtastic/Adafruit_nRF52_Bootloader
nrfjprog --eraseall -f nrf52
# to get tool run "sudo apt-get install srecord"
# this generates an intel hex file that can be programmed into a NRF52 to tell the adafruit bootloader that the current app image is valid
# Bootloader settings are at BOOTLOADER_SETTINGS (rw) : ORIGIN = 0xFF000, LENGTH = 0x1000
# first 4 bytes should be 0x01 to indicate valid app image
@@ -14,7 +16,7 @@ echo "01 00 00 00 00 00 00 00" | xxd -r -p - >/tmp/bootconf.bin
srec_cat /tmp/bootconf.bin -binary -offset 0xff000 -output /tmp/bootconf.hex -intel
echo Generating merged hex file
mergehex -m $BOOTDIR/_build/build-ttgo_eink/ttgo_eink_bootloader-0.3.2-125-gf38f8f4-dirty_s140_6.1.1.hex .pio/build/eink/firmware.hex /tmp/bootconf.hex -o ttgo_eink_full.hex
mergehex -m $BOOTDIR/_build/build-ttgo_eink/ttgo_eink_bootloader-0.3.2-213-gf67f592-dirty_s140_6.1.1.hex .pio/build/eink/firmware.hex /tmp/bootconf.hex -o ttgo_eink_full.hex
echo Telling bootloader app region is valid and telling CPU to run
nrfjprog --program ttgo_eink_full.hex -f nrf52 --reset

3
bin/native-gdbserver.sh Executable file
View File

@@ -0,0 +1,3 @@
set -e
pio run --environment native
gdbserver --once localhost:2345 .pio/build/native/program

3
bin/native-run.sh Executable file
View File

@@ -0,0 +1,3 @@
set -e
pio run --environment native
.pio/build/native/program

View File

@@ -1,3 +1,3 @@
JLinkGDBServerCLExe -if SWD -select USB -port 2331 -device NRF52840_XXAA
JLinkGDBServerCLExe -if SWD -select USB -port 2331 -device NRF52840_XXAA -SuppressInfoUpdateFW -DisableAutoUpdateFW -rtos GDBServer/RTOSPlugin_FreeRTOS

3
bin/program-1.0-tbeam.sh Executable file
View File

@@ -0,0 +1,3 @@
esptool.py --baud 921600 write_flash 0x10000 release/archive/old/firmware-tbeam-EU865-1.0.0.bin
echo "Erasing the otadata partition, which will turn off flash flippy-flop and force the first image to be used"
esptool.py --baud 921600 erase_region 0xe000 0x2000

1
bin/program-1.1-tbeam.sh Executable file
View File

@@ -0,0 +1 @@
esptool.py --baud 921600 write_flash 0x10000 release/archive/old/firmware-tbeam-1.1.50.bin

View File

@@ -2,5 +2,7 @@
set -e
VERSION=`bin/buildinfo.py`
FILENAME=release/latest/bins/universal/firmware-tbeam-$VERSION.bin
esptool.py --baud 921600 write_flash 0x10000 release/latest/bins/firmware-tbeam-US-$VERSION.bin
echo Installing $FILENAME
esptool.py --baud 921600 write_flash 0x10000 $FILENAME

11
bin/run-both.sh Executable file
View File

@@ -0,0 +1,11 @@
set -e
pio run
echo uploading to usb1
pio run --upload-port /dev/ttyUSB1 -t upload &
echo uploading to usb0
pio run --upload-port /dev/ttyUSB0 -t upload &
wait

48
boards/lora_isp4520.json Normal file
View File

@@ -0,0 +1,48 @@
{
"build": {
"arduino": {
"ldscript": "nrf52832_s132_v6.ld"
},
"core": "nRF5",
"cpu": "cortex-m4",
"extra_flags": "-DNRF52832_XXAA -DNRF52",
"f_cpu": "64000000L",
"mcu": "nrf52832",
"variant": "lora_isp4520",
"bsp": {
"name": "adafruit"
},
"softdevice": {
"sd_flags": "-DS132",
"sd_name": "s132",
"sd_version": "6.1.1",
"sd_fwid": "0x00B7"
}
},
"connectivity": [
"bluetooth"
],
"debug": {
"jlink_device": "nRF52832_xxAA",
"svd_path": "nrf52.svd"
},
"frameworks": [
"arduino"
],
"name": "lora ISP4520",
"upload": {
"maximum_ram_size": 65536,
"maximum_size": 524288,
"require_upload_port": true,
"speed": 115200,
"protocol": "nrfutil",
"protocols": [
"jlink",
"nrfjprog",
"nrfutil",
"stlink"
]
},
"url": "",
"vendor": "PsiSoft"
}

View File

@@ -41,7 +41,8 @@ For an detailed walk-through aimed at beginners, we recommend [meshtastic.letsta
### Related Groups
Telegram group for **Italy**-based users [t.me/meshtastic_italia](http://t.me/meshtastic_italia) (Italian language, unofficial).
Telegram group for **Italy**-based users [t.me/meshtastic_italia](http://t.me/meshtastic_italia) (Italian language, unofficial).<br/>
Telegram group for **Russian**-based users [t.me/meshtastic_russia](https://t.me/meshtastic_russia) (Russian language, unofficial).
# Updates

View File

@@ -31,11 +31,75 @@ Channel zero starts at 903.08 MHz center frequency.
## Data-rates
Various data-rates are selectable when configuring a channel and are inversely proportional to the theoretical range of the devices:
### About
| Channel setting | Data-rate |
|----------------------------|----------------------|
| Short range (but fast) | 21.875 kbps |
| Medium range (but fast) | 5.469 kbps |
| Long range (but slower) | 0.275 kbps |
| Very long range (but slow) | 0.183 kbps (default) |
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

View File

@@ -2,27 +2,131 @@
You probably don't care about this section - skip to the next one.
eink:
## before next release
* 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!
* DONE test latest firmware update with is_router
* DONE firmware OTA updates of is_router true nodes fails?
* DONE add UI in android app to reset to defaults https://github.com/meshtastic/Meshtastic-Android/issues/263
* DONE TEST THIS! changing channels requires a reboot to take effect https://github.com/meshtastic/Meshtastic-device/issues/752
* DIBE bug report with remote info request timing out
* DONE retest channel changing in android (using sim?)
* DONE move remote admin doc from forum into git
* DONE check crashlytics
* DONE ask for a documentation czar
* DONE timestamps on oled screen are wrong - don't seem to be updating based on message rx (actually: this is expected behavior when no node on the mesh has GPS time)
* DONE add ch-del
* DONE channel hash suffixes are wrong on android
* DONE before next relase: test empty channel sets on android
* DONE channel sharing in android
* DONE test 1.0 firmware update on android
* DONE test 1.1 firmware update on android
* DONE test 1.2.10 firmware update on android
* DONE test link sharing on android
* FIXED? luxon bug report - seeing rx acks for nodes that are not on the network
* DONE release py
* DONE show GPS time only if we know what global time is
* DONE android should always provide time to nodes - so that it is easier for the mesh to learn the current time
## MQTT
## Multichannel support
* DONE cleanup the external notification and serial plugins
* non ack version of stress test fails sometimes!
* tx fault test has a bug #734 - * turn off fault 8: https://github.com/meshtastic/Meshtastic-device/issues/734
* DONE move device types into an enum in nodeinfo
* DONE fix android to use new device types for firmware update
* nrf52 should preserve local time across reset
* cdcacm bug on nrf52: emittx thinks it emitted but client sees nothing. works again later
* nrf52: segger logs have errors in formatting that should be impossible (because not going through serial, try stalling on segger)
* DONE call RouterPlugin for *all* packets - not just Router packets
* DONE generate channel hash from the name of the channel+the psk (not just one or the other)
* DONE send a hint that can be used to select which channel to try and hash against with each message
* DONE remove deprecated
* DONE fix setchannel in phoneapi.cpp
* DONE set mynodeinfo.max_channels
* DONE set mynodeinfo.num_bands (formerly num_channels)
* DONE fix sniffing of non Routing packets
* DONE enable remote setttings access by moving settings operations into a regular plugin (move settings ops out of PhoneAPI)
* DONE move portnum up?
* DONE remove region specific builds from the firmware
* DONE test single channel without python
* DONE Use "default" for name if name is empty
* DONE fix python data packet receiving (nothing showing in log?)
* DONE implement 'get channels' Admin plugin operation
* DONE use get-channels from python
* DONE use get channels & get settings from android
* DONE use set-channel from python
* DONE make settings changes from python work
* DONE pthon should stop fetching channels once we've reached our first empty channel definition (hasSettings == true)
* DONE add check for old devices with new API library
* DONE release python api
* DONE release protobufs
* DONE release to developers
* DONE fix setch-fast in python tool
* age out pendingrequests in the python API
* DONE stress test channel download from python, sometimes it seems like we don't get all replies, bug was due to simultaneous android connection
* DONE combine acks and responses in a single message if possible (do routing plugin LAST and drop ACK if someone else has already replied)
* DONE don't send packets we received from the phone BACK TOWARDS THE PHONE (possibly use fromnode 0 for packets the phone sends?)
* DONE fix 1.1.50 android debug panel display
* DONE test android channel setting
* DONE release to users
* DONE warn in android app about unset regions
* DONE use set-channel from android
* DONE add gui in android app for setting region
* DONE clean up python channel usage
* DONE use bindToChannel to limit admin access for remote nodes
* DONE move channels and radio config out of device settings
* DONE test remote info and remote settings changes
* make python tests more exhaustive
* DONE pick default random admin key
* exclude admin channels from URL?
* make a way to share just secondary channels via URL
* generalize the concept of "shortstrings" use it for both PSKs and well known channel names. Possibly use a ShortString class.
* use single byte 'well known' channel names for admin, gpio, etc...
* use presence of gpio channel to enable gpio ops, same for serial etc...
* DONE restrict gpio & serial & settings operations to the admin channel (unless local to the current node)
* DONE add channel restrictions for plugins (and restrict routing plugin to the "control" channel)
* stress test multi channel
* DONE investigate @mc-hamster report of heap corruption
* DONE use set-user from android
* untrusted users should not be allowed to provide bogus times (via position broadcasts) to the rest of the mesh. Invent a new lowest quality notion of UntrustedTime.
* use portuino TCP connection to debug with python API
* document the relationship between want_response (indicating remote node received it) and want_ack (indicating that this message should be sent reliably - and also get acks from the first rx node and naks if it is never delivered)
* DONE android should stop fetching channels once we've reached our first empty channel definition (hasSettings == true)
* DONE warn in python api if we are too new to talk to the device code
* DONE make a post warning about 1.2, telling how to stay on old android & python clients. link to this from the android dialog message and python version warning.
* DONE "FIXME - move the radioconfig/user/channel READ operations into SettingsMessage as well"
* DONE scrub protobufs to make sure they are absoloute minimum wiresize (in particular Data, ChannelSets and positions)
* DONE change syncword (now ox2b)
* allow chaning packets in single transmission - to increase airtime efficiency and amortize packet overhead
* DONE move most parts of meshpacket into the Data packet, so that we can chain multiple Data for sending when they all have a common destination and key.
* when selecting a MeshPacket for transmit, scan the TX queue for any Data packets we can merge together as a WirePayload. In the low level send/rx code expand that into multiple MeshPackets as needed (thus 'hiding' from MeshPacket that over the wire we send multiple datapackets
* DONE confirm we are still calling the plugins for messages inbound from the phone (or generated locally)
* DONE confirm we are still multi hop routing flood broadcasts
* DONE confirm we are still doing resends on unicast reliable packets
* add history to routed packets: https://meshtastic.discourse.group/t/packet-source-tracking/2764/2
* add support for full DSR unicast delivery
* DONE move acks into routing
* DONE make all subpackets different versions of data
* DONE move routing control into a data packet
* have phoneapi done via plugin (will allow multiple simultaneous API clients - stop disabling BLE while using phone API)
* use reference counting and dynamic sizing for meshpackets.
* let multiple PhoneAPI endpoints work at once
* allow multiple simultaneous bluetooth connections (create the bluetooth phoneapi instance dynamically based on client id)
* DONE figure out how to add micro_delta to position, make it so that phone apps don't need to understand it?
* only send battery updates a max of once a minute
* DONE add python channel selection for sending
* DONE record recevied channel in meshpacket
* test remote settings operations (confirm it works 3 hops away)
* DONE make a primaryChannel global and properly maintain it when the phone sends setChannel
* DONE move setCrypto call into packet send and packet decode code
* implement 'small location diffs' change
* move battery level out of position?
* consider "A special exception (FIXME, not sure if this is a good idea) - packets that arrive on the local interface
are allowed on any channel (this lets the local user do anything)." Probably by adding a "secure_local_interface" settings bool.
* DOUBLE CHECK android app can still upgrade 1.1 and 1.0 loads
For app cleanup:
* use structured logging to kep logs in ram. Also send logs as packets to api clients
@@ -74,6 +178,44 @@ This should nicely help 'router' nodes do the right thing when long range, or if
* turn on amazon reviews support
* add a tablet layout (with map next to messages) in the android app
# Completed
## eink 1.0
* DONE check email of reported issues
* DONE turn off vbus driving (in bootloader)
* new battery level sensing
* current draw no good
* DONE: fix backlight
* DONE - USB is busted because of power enable mode?
* test CPU voltage? something is bad with RAM (removing eink module does not help)
* test that board leaves bootloader always
* test USB - works in bootloader
* test LEDs
* Test BME280
* test gps
* check GPS fast locking
* tested! dlora
* test eink backlight
* tested! eink
* test buttons
* test battery charging
* test serial flash
* send updated app and bootloader image
* OHH BME280! THAT IS GREAT!
* make new screen work, ask for datasheet
* say I think you could ship this
* leds seem busted
* fix hw_model: "nrf52unknown"
* use larger icon for meshtastic logo
* 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!
# Old docs to merge
MESH RADIO PROTOCOL

View File

@@ -0,0 +1,7 @@
# Your android application needs updating
Hi.
If you've landed here that means your android application is too old for the running device firmware. Usually our updates are backwards compatible, but about once a year we have a "major protocol update" which requires all apps and devices to update to keep working with that version. Version 1.2 in March 2021 was one of those updates.
If you have problems/questions please post in our [forum](https://meshtastic.discourse.group) and some nice person will probably help.

17
docs/software/channels.md Normal file
View File

@@ -0,0 +1,17 @@
# Multiple channel support
Version 1.2 of the software adds support for "multiple (simultaneous) channels". The idea behind this feature is that a mesh can allow multiple users/groups to be share common mesh infrastructure. Even including routing messages for others when no one except that subgroup of users has the encryption keys for their private channel.
### What is the PRIMARY channel
The way this works is that each node keeps a list of channels it knows about. One of those channels (normally the first 1) is labelled as the "PRIMARY" channel. The primary channel is the **only** channel that is used to set radio parameters. i.e. this channel controls things like spread factor, coding rate, bandwidth etc... Indirectly this channel also is used to select the specific frequency that all members of this mesh are talking over.
This channel may or may not have a PSK (encryption). If you are providing mesh to 'the public' we recommend that you always leave this channel with its default psk. The default PSK is technically encrypted (and random users sniffing the ether would have to use meshtastic to decode it), but the key is included in the github source code and you should assume any 'attacker' would have it. But for a 'public' mesh you want this, because it allows anyone using meshtastic in your area to send packets through 'your' mesh.
Note: Older meshtastic applications that don't yet understand multi-channel support will only show the user this channel.
### How to use SECONDARY channels
Any channel you add after that PRIMARY channel is SECONDARY. Secondary channels are used only for encyryption and (in the case of some special applications) security. If you would like to have a private channel over a more public mesh, you probably want to create a SECONDARY channel. When sharing that URL with your private group you will share the "Complete URL". The complete URL includes your secondary channel (for encryption) and the primary channel (to provide radio/mesh access).
Secondary channels **must** have a PSK (encryption).

View File

@@ -0,0 +1,135 @@
# About
The RangeTest Plugin will help you perform range and coverage tests.
# Configuration
These are the settings that can be configured.
range_test_plugin_enabled
Is the plugin enabled?
0 = Disabled (Default)
1 = Enabled
range_test_plugin_save
If enabled, we will save a log of all received messages to /static/rangetest.csv which you can access from the webserver. We will abort
writing if there is less than 50k of space on the filesystem to prevent filling up the storage.
0 = Disabled (Default)
1 = Enabled
range_test_plugin_sender
Number of seconds to wait between sending packets. Using the long_slow channel configuration, it's best not to go more frequent than once every 60 seconds. You can be more agressive with faster settings. 0 is default which disables sending messages.
# Usage Notes
For basic usage, you will need two devices both with a GPS. A device with a paired phone with GPS may work, I have not tried it.
The first thing to do is to turn on the plugin. With the plugin turned on, the other settings will be available:
range_test_plugin_enabled = 1
If you want to send a message every 60 seconds:
range_test_plugin_sender = 60
To save a log of the messages:
range_test_plugin_save = 1
Recommended settings for a sender at different radio settings:
Long Slow ... range_test_plugin_sender = 60
Long Alt ... range_test_plugin_sender = 30
Medium ... range_test_plugin_sender = 15
Short Fast ... range_test_plugin_sender = 15
## Python API Examples
### Sender
meshtastic --set range_test_plugin_enabled 1
meshtastic --set range_test_plugin_sender 60
### Receiver
meshtastic --set range_test_plugin_enabled 1
meshtastic --set range_test_plugin_save 1
## Other things to keep in mind
Be sure to turn off either the plugin configured as a sender or the device where the plugin setup as sender when not in use. This will use a lot of time on air and will spam your channel.
Also be mindful of your space usage on the file system. It has protections from filling up the space but it's best to delete old range test results.
# Application Examples
## Google Integration
@jfirwin on our forum [meshtastic.discourse.org](https://meshtastic.discourse.group/t/new-plugin-rangetestplugin/2591/49?u=mc-hamster) shared how to integrate the resulting csv file with Google Products.
### Earth
Steps:
1. [Download](https://www.google.com/earth/versions/#download-pro) 1 and open Google Earth
1. Select File > Import
2. Select CSV
3. Select Delimited, Comma
4. Make sure the button that states “This dataset does not contain latitude/longitude information, but street addresses” is unchecked
5. Select “rx lat” & “rx long” for the appropriate lat/lng fields
6. Click finish
2. When it prompts you to create a style template, click yes.
1. Set the name field to whichever column you want to be displayed on the map (dont worry about this too much, when you click on an icon, all the relavant data appears)
2. select a color, icon, etc. and hit ok.
Your data will load onto the map, make sure to click the checkbox next to your dataset in the sidebar to view it.
### My Maps
You can use [My Maps](http://mymaps.google.com/). It takes CSVs and the whole interface is much easier to work with.
Google has instructions on how to do that [here](https://support.google.com/mymaps/answer/3024836?co=GENIE.Platform%3DDesktop&hl=en#zippy=%2Cstep-prepare-your-info%2Cstep-import-info-into-the-map).
You can style the ranges differently based on the values, so you can have the pins be darker the if the SNR or RSSI (if that gets added) is higher.
# Known Problems
* If turned on, using mesh network will become unwieldly because messages are sent over the same channel as the other messages. See TODO below.
# TODO
* Right now range test messages go over the TEXT_MESSAGE_APP port. We need a toggle to switch to optionally send over RANGE_TEST_APP.
# FAQ
Q: Where is rangetest.csv saved?
A: Turn on the WiFi on your device as either a WiFi client or a WiFi AP. Once you can connect to your device, go to /static and you will see rangetest.csv.
Q: Do I need to have WiFi turned on for the file to be saved?
A: Nope, it'll just work.
Q: Do I need a phone for this plugin?
A: There's no need for a phone.
Q: Can I use this as a message logger?
A: While it's not the intended purpose, sure, why not. Do it!
Q: What will happen if I run out of space on my device?
A: We have a protection in place to keep you from completly filling up your device. This will make sure that other device critical functions will continue to work. We will reserve at least 50k of free space.
Q: What do I do with the rangetest.csv file when I'm done?
A: Go to /static and delete the file.
Q: Can I use this as a sender while on battery power?
A: Yes, but your battery will run down quicker than normal. While sending, we tell the device not to go into low-power mode since it needs to keep to a fairly strict timer.
Q: Why is this operating on incoming messages instead of the existing location discovery protocol?
A: This plugin is still young and currently supports monitoring just one port at a time. I decided to use the existing message port because that is easy to test with. A future version will listen to multiple ports to be more promiscuous.
# 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

View File

@@ -0,0 +1,105 @@
# About
This is a work in progress and is not yet available.
The Store Forward 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 (Long range but Slow).
# Requirements
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)
# Implementation timeline
Not necessarily in this order:
UC 1) MVP - automagically forward packets to a client that may have missed packets.
UC 2) Client Interface (Web, Android, Python or iOS when that happens) to optionally request packets be resent. This is to support the case where Router has not detected that the client was away. This is because the router will only know you're away if you've been gone for a period of time but will have no way of knowing if you were offline for a short number of minutes. This will cover the case where you have ducked into a cave or you're swapping out your battery.
UC 3) router sends a periodic “heartbeat” to let the clients know theyre part of the main mesh
UC 4) support for a mesh to have multiple routers that have the store & forward functionality (for redundancy)
UC 5) Support for "long term" delayed messages and "short term" delayed messages. Handle the cases slightly different to improve user expierence. A short term delayed message would be a message that was resent becaue a node was not heard from for <5 minutes. A long term delayed message is a message that has not been delivered in >5 minutes.
UC 6) Eventually we could add a "want_store_and_forward" bit to MeshPacket and that could be nicer than whitelists in this plugin. Initially we'd only set that bit in text messages (and any other plugin messages that can cope with this). This change would be backward wire compatible so can add easily later.
UC 7) Currently the way we allocate messages in the device code is super inefficient. It always allocates the worst case message size. Really we should dynamically allocate just the # of bytes we need. This would allow many more MeshPackets to be kept in RAM.
UC 8) We'll want a "delayed" bit in MeshPacket. This will indicate that the message was not received in real time.
# Things to consider
Not all these cases will be initially implemented. It's just a running stream of thoughts to be considered.
## Main Mesh Network with Router
The store and forward plugin is intended to be enabled on a router that designates your "main" mesh network.
## Store and Forward on Multiple Routers
If multiple routers with the plugin are enabled, they should be able to share their stored database amongst each other. This enable resilliancy from one router going offline.
## Fragmented networks - No router
In this case, the mesh network has been fragmented by two client devices leaving the main network.
If two Meshtastic devices walk away from the main mesh, they will be able to message each other but not message the main network. When they return to the main network, they will receive the messages they have missed from the main mesh network.
## Fragmented network - With routers
In this case, we have two routers separate by a great distance, each serving multiple devices. One of the routers have gone offline. This has now created two physically seaprated mesh networks using the same channel configuration.
Q: How do we rejoin both fragmented networks? Do we care about messages that were unrouted between fagments?
## Identifing Delayed Messages
When a message is replayed for a node, identify the packet as "Delayed". This will indicate that the message was not received in real time.
# Router Data Structures
Structure of received messages:
receivedMessages
Port_No
packetID
to
from
rxTimeMsec
data
Structure of nodes and last time we heard from them. This is a record of any packet type.
receivedRecord
From
rxTimeMillis
# General Operation for UC1 - automagically forward packets to a client that may have missed packets
On every handled packet
* Record the sender from and the time we heard from that sender into senderRecord.
On every handled packet
* If the packet is a message, save the messsage into receivedMessages
On every handled packet, if we have not heard from that sender in a period of time greater than timeAway, let's assume that they have been away from the network.
* In this case, we will resend them all the messages they have missed since they were gone
## Expected problems this implementation
* If the client has been away for less than 5 minutes and has received the previously sent message, the client will gracefully ignore it. This is thanks to PacketHistory::wasSeenRecently in PacketHistory.cpp.
* * If the client has been away for more than 5 minutes and we resend packets that they have already received, it's possible they will see duplicate messages. This should be unlikely but is still possible.
# Designed limitations
The Store and Forward plugin will subscribe to specific packet types and channels and only save those. This will both reduce the amount of data we will need to store and reduce the overhead on the network. Eg: There's no need to replay ACK packets nor is there's no need to replay old location packets.

View File

@@ -1,6 +0,0 @@
# About
# Running notes

View File

@@ -0,0 +1,121 @@
# Remote node administration
This is the first documentation for how to use the [multiple channels](channels.md) feature to enable remote adminstration of meshtastic nodes. i.e. let you talk through the mesh to some far away node and change that nodes settings. This is an advanced feature that (currently) few users would need. Also, keep in mind it is possible (if you are not careful) to assign settings to that remote node that cause it to completely drop off of your mesh.
Btw: I promised to document how multi-channel is now used to secure remote GPIO/serial access. But probably best to debug these instructions first, so I'll wait on that. If you **do** need to use remote GPIO/serial now, just follow these instructions but name your new channel "gpio" or "serial".
## Creating the "admin" channel
Okay - now that we've summarized what multiple-channel support is, we can move on to using it to provide remote administrative access to a node.
By default, nodes will **only** respond to adminstrative commands via the local USB/bluetooth/TCP interface. This provides basic security to prevent unauthorized access. This is actually how 'normal' administration and settings changes work. The only difference for the remote case is that we are sending those commands over the mesh.
Before a node will allow remote admin access, it must find a channel
```
meshtastic --info
Connected to radio
...
Channels:
PRIMARY psk=default { "modemConfig": "Bw125Cr48Sf4096", "psk": "AQ==" }
Primary channel URL: https://www.meshtastic.org/d/#CgUYAyIBAQ
```
So from this output you see that this node knows about only one channel and that its PSK is set to the default value.
But if you then add an admin channel (with "meshtastic --ch-add admin"). Note: the name is important it must be "admin" (sorry):
Your channels will now look like this:
```
meshtastic --ch-add admin
Connected to radio
Writing modified channels to device
meshtastic --info
Connected to radio
...
Channels:
PRIMARY psk=default { "modemConfig": "Bw125Cr48Sf4096", "psk": "AQ==" }
SECONDARY psk=secret { "psk": "HW7E3nMbiNbvr6MhsDonLCmj7eSAhttzjbIx/r5OQmg=", "name": "admin" }
Primary channel URL: https://www.meshtastic.org/d/#CgUYAyIBAQ
Complete URL (includes all channels): https://www.meshtastic.org/d/#CgUYAyIBAQopIiAdbsTecxuI1u-voyGwOicsKaPt5ICG23ONsjH-vk5CaCoFYWRtaW4
```
Notice that now we have a new secondary channel. Also, the "--info" option prints out TWO URLs. The "complete URL" includes all of the channels this node understands. You should consider this URL something you should be very cautious about sharing. In the case of remote adminstration, you only need the node you want to adminster and the node you are locally connected to know this new "admin" channel.
## Sharing the admin channel with other nodes
I'm going to assume you've already created the admin channel on your "local node" i.e. the meshtastic node sitting on your desk at your home. But now you want to enable access on the "remote node" you want to eventually have far away from you.
For this step you need physical access to both the nodes.
1. Create the "admin" channel on the "local node" using the instructions above.
2. Copy the "Complete URL" someplace for permanent reference/access.
3. Connect meshtastic-python to the "remote node" over the USB port.
4. For the "remote node" type "meshtastic --seturl the-url-from-step-2".
5. Run "meshtastic --info" and confirm that the "Complete URL" is the same for both of the nodes.
6. Done!
At this point you can take your remote node and install it far away and still be able to change any of its settings.
## Remotely administering your node
Now that both your local node and the remote node contain your secret admin channel key, you can do things like this:
Get the node list from the local node.
```
meshtastic --nodes
Connected to radio
/----------------------------------------------------------------------------------------------------------\
|N| User |AKA| ID | Position |Battery| SNR | LastHeard | Since |
|-+------------+---+---------+------------------------+-------+---------+-------------------+--------------|
|1|Unknown 9058|?58|!28979058|25.0382°, 121.5731°, N/A| N/A |-13.50 dB|2021-03-22 09:25:42|19 seconds ago|
\----------------------------------------------------------------------------------------------------------/
```
Using the node ID from that list, send a message through the mesh telling that node to change its owner name.
```
meshtastic --dest \!28979058 --set-owner "Im Remote"
Connected to radio
Setting device owner to Im Remote
INFO:root:Requesting configuration from remote node (this could take a while)
```
And you can now confirm via the local node that the remote node has changed:
```
meshtastic --nodes
Connected to radio
/----------------------------------------------------------------------------------------------------\
|N| User |AKA| ID | Position |Battery| SNR | LastHeard | Since |
|-+---------+---+---------+------------------------+-------+-------+-------------------+-------------|
|1|Im Remote|IR |!28979058|25.0382°, 121.5731°, N/A| N/A |8.75 dB|2021-03-22 09:35:42|3 minutes ago|
\----------------------------------------------------------------------------------------------------/
```
Note: you can change **any** parameter, add channels or get info from the remote node. Here's an example of setting ls_secs and printing the complete device info from the remote node.
```
meshtastic --dest \!28979058 --set ls_secs 301 --info
Connected to radio
INFO:root:Requesting configuration from remote node (this could take a while)
Set ls_secs to 301
Writing modified preferences to device
Preferences: { "lsSecs": 301, "region": "TW" }
Channels:
PRIMARY psk=default { "modemConfig": "Bw125Cr48Sf4096", "psk": "AQ==" }
SECONDARY psk=secret { "psk": "HW7E3nMbiNbvr6MhsDonLCmj7eSAhttzjbIx/r5OQmg=", "name": "admin" }
Primary channel URL: https://www.meshtastic.org/d/#CgUYAyIBAQ
Complete URL (includes all channels): https://www.meshtastic.org/d/#CgUYAyIBAQopIiAdbsTecxuI1u-voyGwOicsKaPt5ICG23ONsjH-vk5CaCoFYWRtaW4
```
## Areas for future development
In the future we will add a "deadman timer" to this feature so that the remote node will revert any changes if you fail to send a special "commit changes" command. This will protect against sending bad settings to nodes that you can't physically access. Instead if the node does not receive a commit message within 10 minutes it will revert all changes and (hopefully) rejoin the mesh.

View File

@@ -6,5 +6,7 @@ This is a mini design doc for developing the meshtastic software.
* Our [project board](https://github.com/orgs/meshtastic/projects/1) - shows what things we are currently working on and remaining work items for the current release.
* [Power Management](power.md)
* [Mesh algorithm](mesh-alg.md)
* [Channels](channels.md) - documentation on how multiple simultaneous channels are used
* [Remote adminstration](remote-admin.md)
* [External client API](device-api.md) and porting guide for new clients (iOS, python, etc...)
* TODO: how to port the device code to a new device.

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

BIN
images/platformio-erase.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 52 KiB

View File

@@ -13,9 +13,12 @@ default_envs = tbeam
;default_envs = tbeam0.7
;default_envs = heltec
;default_envs = tlora-v1
;default_envs = tlora_v1_3
;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
;default_envs = eink
;default_envs = nrf52840dk-geeksville
;default_envs = native # 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
@@ -32,19 +35,16 @@ default_envs = tbeam
extra_scripts = bin/platformio-custom.py
; note: we add src to our include search path so that lmic_project_config can override
; note: TINYGPS_OPTION_NO_CUSTOM_FIELDS is VERY important. We don't use custom fields and somewhere in that pile
; of code is a heap corruption bug!
; FIXME: fix lib/BluetoothOTA dependency back on src/ so we can remove -Isrc
build_flags = -Wno-missing-field-initializers -Isrc -Isrc/mesh -Isrc/gps -Ilib/nanopb/include -Wl,-Map,.pio/build/output.map
build_flags = -Wno-missing-field-initializers
-Wno-format
-Isrc -Isrc/mesh -Isrc/gps -Ilib/nanopb/include -Isrc/buzz -Wl,-Map,.pio/build/output.map
-DHW_VERSION_${sysenv.COUNTRY}
-DHW_VERSION=${sysenv.HW_VERSION}
-DUSE_THREAD_NAMES
-DTINYGPSPLUS_OPTION_NO_CUSTOM_FIELDS
; leave this commented out to avoid breaking Windows
;upload_port = /dev/ttyUSB0
;monitor_port = /dev/ttyUSB0
;upload_port = /dev/cu.SLAB_USBtoUART
;monitor_port = /dev/cu.SLAB_USBtoUART
-DTINYGPS_OPTION_NO_CUSTOM_FIELDS
; the default is esptool
; upload_protocol = esp-prog
@@ -65,17 +65,17 @@ debug_tool = jlink
; monitor adapter_khz 10000
lib_deps =
https://github.com/meshtastic/esp8266-oled-ssd1306.git ; ESP8266_SSD1306
https://github.com/meshtastic/esp8266-oled-ssd1306.git#35d796226b853b0c0ff818b2f1aa3d35e7296a96 ; ESP8266_SSD1306
https://github.com/geeksville/OneButton.git ; OneButton library for non-blocking button debounce
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/arduino-fsm.git#55c47b6cded91645aff05a27b6e5821d8d0f64be
https://github.com/meshtastic/SparkFun_Ublox_Arduino_Library.git#31015a55e630a2df77d9d714669c621a5bf355ad
https://github.com/meshtastic/RadioLib.git#07de964e929238949035fb0d5887026a3058df1a
https://github.com/meshtastic/TinyGPSPlus.git#9c1d584d2469523381e077b0b9c1bf868d6c0206
https://github.com/meshtastic/TinyGPSPlus.git#f0f47067ef2f67c856475933188251c1ef615e79
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]
@@ -104,12 +104,21 @@ build_flags =
lib_deps =
${arduino_base.lib_deps}
https://github.com/meshtastic/esp32_https_server.git
adafruit/DHT sensor library@^1.4.1
adafruit/Adafruit Unified Sensor@^1.1.4
# Hmm - this doesn't work yet
# board_build.ldscript = linker/esp32.extram.bss.ld
lib_ignore = segger_rtt
platform_packages =
framework-arduinoespressif32@https://github.com/meshtastic/arduino-esp32.git#352c8ea7cb73f10433ed139f34251979c470ad56
; leave this commented out to avoid breaking Windows
upload_port = /dev/ttyUSB0
;monitor_port = /dev/ttyUSB0
;upload_port = /dev/cu.SLAB_USBtoUART
;monitor_port = /dev/cu.SLAB_USBtoUART
; customize the partition table
; http://docs.platformio.org/en/latest/platforms/espressif32.html#partition-tables
board_build.partitions = partition-table.csv
@@ -150,6 +159,12 @@ build_flags =
${esp32_base.build_flags} -D TLORA_V1
; note: the platformio definition for lora32-v2 seems stale, it is missing a pins_arduino.h file, therefore I don't think it works
[env:tlora_v1_3]
extends = esp32_base
board = ttgo-lora32-v1
build_flags =
${esp32_base.build_flags} -D TLORA_V1_3
[env:tlora-v2]
extends = esp32_base
board = ttgo-lora32-v1
@@ -179,7 +194,6 @@ src_filter =
; platform = nordicnrf52
platform = https://github.com/meshtastic/platform-nordicnrf52.git#1a2639a6b0f79b5df66bea3e3089f0d5285fdc63
extends = arduino_base
debug_tool = jlink
build_type = debug ; I'm debugging with ICE a lot now
; note: liboberon provides the AES256 implementation for NRF52 (though not using the hardware acceleration of the NRF52840 - FIXME)
build_flags =
@@ -188,11 +202,32 @@ build_flags =
-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/> -<mesh/wifi/> -<mesh/http/>
${arduino_base.src_filter} -<esp32/> -<nimble/> -<mesh/wifi/> -<mesh/http/> -<plugins/esp32>
lib_ignore =
BluetoothOTA
monitor_port = /dev/ttyACM1
# we pass in options to jlink so it can understand freertos (note: we don't use "jlink" as the tool)
debug_tool = jlink
debug_port = :2331
# Note: the ARGUMENTS MUST BE on multiple lines. Otherwise platformio/commands/debug/helpers.py misparses everything into the "executable"
# attribute and leaves "arguments" empty
# /home/kevinh/.platformio/packages/tool-jlink/JLinkGDBServerCLExe
# This doesn't work yet, so not using for now
disabled_debug_server =
/usr/bin/JLinkGDBServerCLExe
-singlerun
-if
SWD
-select
USB
-device
nRF52840_xxAA
-port
2331
-rtos
GDBServer/RTOSPlugin_FreeRTOS
debug_extra_cmds =
source gdbinit
@@ -204,6 +239,24 @@ debug_init_break =
;debug_init_break = tbreak loop
;debug_init_break = tbreak Reset_Handler
[env:lora_isp4520]
extends = nrf52_base
board = lora_isp4520
# add our variants files to the include and src paths
build_flags = ${nrf52_base.build_flags} -Ivariants/lora_isp4520
# No screen and GPS on the board. We still need RTC.cpp for the RTC clock.
src_filter = ${nrf52_base.src_filter} +<../variants/lora_isp4520> -<graphics> -<gps> +<gps/GPS.cpp> +<gps/RTC.cpp>
lib_ignore = ${nrf52_base.lib_ignore}
ESP8266_SSD1306
SparkFun Ublox Arduino Library
AXP202X_Library
TinyGPSPlus
upload_protocol = jlink
monitor_port = /dev/ttyUSB0
; The NRF52840-dk development board
; Note: By default no lora device is created for this build - it uses a simulated interface
[env:nrf52840dk]
@@ -327,19 +380,20 @@ lib_deps =
TFT_eSPI
; 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/> -<mesh/http/>
[env:native]
platform = https://github.com/geeksville/platform-native.git
src_filter = ${env.src_filter} -<esp32/> -<nimble/> -<nrf52/> -<mesh/http/> -<plugins/esp32>
build_flags = ${arduino_base.build_flags} -O0
framework = arduino
board = linux_x86_64
board = native
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
; note: @geeksville disabled because genieblocks_lora is not checked into the boards directory, please send in a PR to add it ;-)
;[env:genieblocks_lora]
;extends = esp32_base
;board = genieblocks_lora
;build_flags =
; ${esp32_base.build_flags} -D GENIEBLOCKS

2
proto

Submodule proto updated: b1aed06442...820fa497df

View File

@@ -5,12 +5,35 @@
#include "sleep.h"
#include "utils.h"
#ifdef TBEAM_V10
// FIXME. nasty hack cleanup how we load axp192
#undef AXP192_SLAVE_ADDRESS
#include "axp20x.h"
#ifdef TBEAM_V10
AXP20X_Class axp;
#else
// Copy of the base class defined in axp20x.h.
// I'd rather not inlude axp20x.h as it brings Wire dependency.
class HasBatteryLevel {
public:
/**
* Battery state of charge, from 0 to 100 or -1 for unknown
*/
virtual int getBattPercentage() { return -1; }
/**
* The raw voltage of the battery or NAN if unknown
*/
virtual float getBattVoltage() { return NAN; }
/**
* return true if there is a battery installed in this unit
*/
virtual bool isBatteryConnect() { return false; }
virtual bool isVBUSPlug() { return false; }
virtual bool isChargeing() { return false; }
};
#endif
bool pmu_irq = false;
@@ -51,7 +74,7 @@ class AnalogBatteryLevel : public HasBatteryLevel
*/
virtual int getBattPercentage()
{
float v = getBattVoltage() / 1000;
float v = getBattVoltage();
if (v < noBatVolt)
return -1; // If voltage is super low assume no battery installed
@@ -67,13 +90,26 @@ class AnalogBatteryLevel : public HasBatteryLevel
*/
virtual float getBattVoltage()
{
// Tested ttgo eink nrf52 board and the reported value is perfect
// DEBUG_MSG("raw val %u", raw);
return
#ifndef ADC_MULTIPLIER
#define ADC_MULTIPLIER 2.0
#endif
#ifdef BATTERY_PIN
1000.0 * 2.0 * (AREF_VOLTAGE / 1024.0) * analogRead(BATTERY_PIN);
// Do not call analogRead() often.
const uint32_t min_read_interval = 5000;
if (millis() - last_read_time_ms > min_read_interval) {
last_read_time_ms = millis();
uint32_t raw = analogRead(BATTERY_PIN);
float scaled = 1000.0 * ADC_MULTIPLIER * (AREF_VOLTAGE / 1024.0) * raw;
// DEBUG_MSG("raw val=%u scaled=%u\n", raw, (uint32_t)(scaled));
last_read_value = scaled;
return scaled;
} else {
return last_read_value;
}
#else
NAN;
return NAN;
#endif
}
@@ -93,7 +129,9 @@ class AnalogBatteryLevel : public HasBatteryLevel
private:
/// If we see a battery voltage higher than physics allows - assume charger is pumping
/// in power
const float fullVolt = 4.2, emptyVolt = 3.27, chargingVolt = 4.3, noBatVolt = 2.1;
const float fullVolt = 4200, emptyVolt = 3270, chargingVolt = 4210, noBatVolt = 2100;
float last_read_value = 0.0;
uint32_t last_read_time_ms = 0;
} analogLevel;
Power::Power() : OSThread("Power") {}
@@ -140,6 +178,8 @@ void Power::shutdown()
#ifdef TBEAM_V10
DEBUG_MSG("Shutting down\n");
axp.shutdown();
#elif NRF52_SERIES
doDeepSleep(DELAY_FOREVER);
#endif
}

View File

@@ -97,7 +97,8 @@ static void lsIdle()
static void lsExit()
{
// setGPSPower(true); // restore GPS power
gps->forceWake(true);
if (gps)
gps->forceWake(true);
}
static void nbEnter()
@@ -172,16 +173,17 @@ Fsm powerFSM(&stateBOOT);
void PowerFSM_setup()
{
bool isRouter = radioConfig.preferences.is_router;
// If we are not a router and we already have AC power go to POWER state after init, otherwise go to ON
// We assume routers might be powered all the time, but from a low current (solar) source
bool isLowPower = radioConfig.preferences.is_low_power || isRouter;
/* To determine if we're externally powered, assumptions
1) If we're powered up and there's no battery, we must be getting power externally.
1) If we're powered up and there's no battery, we must be getting power externally. (because we'd be dead otherwise)
2) If we detect USB power from the power management chip, we must be getting power externally.
*/
bool hasPower = (powerStatus && !powerStatus->getHasBattery()) || (!isLowPower && powerStatus && powerStatus->getHasUSB());
bool hasPower = !isLowPower && powerStatus && (!powerStatus->getHasBattery() || powerStatus->getHasUSB());
DEBUG_MSG("PowerFSM init, USB power=%d\n", hasPower);
powerFSM.add_timed_transition(&stateBOOT, hasPower ? &statePOWER : &stateON, 3 * 1000, NULL, "boot timeout");
@@ -248,6 +250,11 @@ void PowerFSM_setup()
powerFSM.add_transition(&stateDARK, &stateDARK, EVENT_CONTACT_FROM_PHONE, NULL, "Contact from phone");
// each time we get a new update packet make sure we are staying in the ON state so the screen stays awake (also we don't
// shutdown bluetooth if is_router)
powerFSM.add_transition(&stateDARK, &stateON, EVENT_FIRMWARE_UPDATE, NULL, "Got firmware update");
powerFSM.add_transition(&stateON, &stateON, EVENT_FIRMWARE_UPDATE, NULL, "Got firmware update");
powerFSM.add_transition(&stateNB, &stateDARK, EVENT_PACKET_FOR_PHONE, NULL, "Packet for phone");
powerFSM.add_timed_transition(&stateON, &stateDARK, getPref_screen_on_secs() * 1000, NULL, "Screen-on timeout");

View File

@@ -18,6 +18,7 @@
#define EVENT_SERIAL_DISCONNECTED 12
#define EVENT_POWER_CONNECTED 13
#define EVENT_POWER_DISCONNECTED 14
#define EVENT_FIRMWARE_UPDATE 15 // We just received a new firmware update packet from the phone
extern Fsm powerFSM;
extern State statePOWER, stateSERIAL;

View File

@@ -1,9 +1,10 @@
#include "RedirectablePrint.h"
#include "RTC.h"
#include "concurrency/OSThread.h"
#include "configuration.h"
#include <assert.h>
#include <sys/time.h>
#include <time.h>
#include "concurrency/OSThread.h"
/**
* A printer that doesn't go anywhere
@@ -20,11 +21,12 @@ size_t RedirectablePrint::write(uint8_t c)
{
// Always send the characters to our segger JTAG debugger
#ifdef SEGGER_STDOUT_CH
SEGGER_RTT_PutCharSkip(SEGGER_STDOUT_CH, c);
SEGGER_RTT_PutChar(SEGGER_STDOUT_CH, c);
#endif
dest->write(c);
return 1; // We always claim one was written, rather than trusting what the serial port said (which could be zero)
return 1; // We always claim one was written, rather than trusting what the
// serial port said (which could be zero)
}
size_t RedirectablePrint::vprintf(const char *format, va_list arg)
@@ -49,51 +51,54 @@ size_t RedirectablePrint::vprintf(const char *format, va_list arg)
return len;
}
#define SEC_PER_DAY 86400
#define SEC_PER_HOUR 3600
#define SEC_PER_MIN 60
size_t RedirectablePrint::logDebug(const char *format, ...)
{
va_list arg;
va_start(arg, format);
// Cope with 0 len format strings, but look for new line terminator
bool hasNewline = *format && format[strlen(format) - 1] == '\n';
size_t r = 0;
// If we are the first message on a report, include the header
if (!isContinuationMessage) {
struct timeval tv;
if (!gettimeofday(&tv, NULL)) {
long hms = tv.tv_sec % SEC_PER_DAY;
//hms += tz.tz_dsttime * SEC_PER_HOUR;
//hms -= tz.tz_minuteswest * SEC_PER_MIN;
// mod `hms` to ensure in positive range of [0...SEC_PER_DAY)
hms = (hms + SEC_PER_DAY) % SEC_PER_DAY;
if (!inDebugPrint) {
inDebugPrint = true;
// Tear apart hms into h:m:s
int hour = hms / SEC_PER_HOUR;
int min = (hms % SEC_PER_HOUR) / SEC_PER_MIN;
int sec = (hms % SEC_PER_HOUR) % SEC_PER_MIN; // or hms % SEC_PER_MIN
va_list arg;
va_start(arg, format);
r += printf("%02d:%02d:%02d ", hour, min, sec);
} else
r += printf("??:??:?? ");
// Cope with 0 len format strings, but look for new line terminator
bool hasNewline = *format && format[strlen(format) - 1] == '\n';
auto thread = concurrency::OSThread::currentThread;
if(thread) {
print("[");
print(thread->ThreadName);
print("] ");
// If we are the first message on a report, include the header
if (!isContinuationMessage) {
uint32_t rtc_sec = getValidTime(RTCQuality::RTCQualityFromNet);
if (rtc_sec > 0) {
long hms = rtc_sec % SEC_PER_DAY;
// hms += tz.tz_dsttime * SEC_PER_HOUR;
// hms -= tz.tz_minuteswest * SEC_PER_MIN;
// mod `hms` to ensure in positive range of [0...SEC_PER_DAY)
hms = (hms + SEC_PER_DAY) % SEC_PER_DAY;
// Tear apart hms into h:m:s
int hour = hms / SEC_PER_HOUR;
int min = (hms % SEC_PER_HOUR) / SEC_PER_MIN;
int sec = (hms % SEC_PER_HOUR) % SEC_PER_MIN; // or hms % SEC_PER_MIN
r += printf("%02d:%02d:%02d %u ", hour, min, sec, millis() / 1000);
} else
r += printf("??:??:?? %u ", millis() / 1000);
auto thread = concurrency::OSThread::currentThread;
if (thread) {
print("[");
// printf("%p ", thread);
// assert(thread->ThreadName.length());
print(thread->ThreadName);
print("] ");
}
}
r += vprintf(format, arg);
va_end(arg);
isContinuationMessage = !hasNewline;
inDebugPrint = false;
}
r += vprintf(format, arg);
va_end(arg);
isContinuationMessage = !hasNewline;
return r;
}

View File

@@ -19,6 +19,8 @@ class RedirectablePrint : public Print
/// Used to allow multiple logDebug messages to appear on a single log line
bool isContinuationMessage = false;
volatile bool inDebugPrint = false;
public:
RedirectablePrint(Print *_dest) : dest(_dest) {}

View File

@@ -1,13 +1,21 @@
#include "SerialConsole.h"
#include "NodeDB.h"
#include "PowerFSM.h"
#include "configuration.h"
#include "NodeDB.h"
#include <Arduino.h>
#define Port Serial
SerialConsole console;
void consolePrintf(const char *format, ...)
{
va_list arg;
va_start(arg, format);
console.vprintf(format, arg);
va_end(arg);
}
SerialConsole::SerialConsole() : StreamAPI(&Port), RedirectablePrint(&Port)
{
canWrite = false; // We don't send packets to our port until it has talked to us first
@@ -29,7 +37,7 @@ void SerialConsole::init()
void SerialConsole::handleToRadio(const uint8_t *buf, size_t len)
{
// Turn off debug serial printing once the API is activated, because other threads could print and corrupt packets
if(!radioConfig.preferences.debug_log_enabled)
if (!radioConfig.preferences.debug_log_enabled)
setDestination(&noopPrint);
canWrite = true;

View File

@@ -32,4 +32,7 @@ class SerialConsole : public StreamAPI, public RedirectablePrint
virtual void onConnectionChanged(bool connected);
};
// A simple wrapper to allow non class aware code write to the console
void consolePrintf(const char *format, ...);
extern SerialConsole console;

View File

@@ -47,8 +47,7 @@ class AirTime : private concurrency::OSThread
void logAirtime(reportTypes reportType, uint32_t airtime_ms);
protected:
virtual int32_t runOnce();
virtual int32_t runOnce() override;
};
extern AirTime *airTime;

66
src/buzz/buzz.cpp Normal file
View File

@@ -0,0 +1,66 @@
#include "buzz.h"
#include "configuration.h"
#ifdef NRF52_SERIES
#include "variant.h"
#endif
#ifndef PIN_BUZZER
// Noop methods for boards w/o buzzer
void playBeep(){};
void playStartMelody(){};
void playShutdownMelody(){};
#else
#include "Tone.h"
extern "C" void delay(uint32_t dwMs);
struct ToneDuration {
int frequency_khz;
int duration_ms;
};
// Some common frequencies.
#define NOTE_C3 131
#define NOTE_CS3 139
#define NOTE_D3 147
#define NOTE_DS3 156
#define NOTE_E3 165
#define NOTE_F3 175
#define NOTE_FS3 185
#define NOTE_G3 196
#define NOTE_GS3 208
#define NOTE_A3 220
#define NOTE_AS3 233
#define NOTE_B3 247
const int DURATION_1_8 = 125; // 1/8 note
const int DURATION_1_4 = 250; // 1/4 note
void playTones(const ToneDuration *tone_durations, int size) {
for (int i = 0; i < size; i++) {
const auto &tone_duration = tone_durations[i];
tone(PIN_BUZZER, tone_duration.frequency_khz, tone_duration.duration_ms);
// to distinguish the notes, set a minimum time between them.
delay(1.3 * tone_duration.duration_ms);
}
}
void playBeep() { tone(PIN_BUZZER, NOTE_B3, DURATION_1_4); }
void playStartMelody() {
ToneDuration melody[] = {{NOTE_B3, DURATION_1_4},
{NOTE_B3, DURATION_1_8},
{NOTE_B3, DURATION_1_8}};
playTones(melody, sizeof(melody) / sizeof(ToneDuration));
}
void playShutdownMelody() {
ToneDuration melody[] = {{NOTE_B3, DURATION_1_4},
{NOTE_G3, DURATION_1_8},
{NOTE_D3, DURATION_1_8}};
playTones(melody, sizeof(melody) / sizeof(ToneDuration));
}
#endif

5
src/buzz/buzz.h Normal file
View File

@@ -0,0 +1,5 @@
#pragma once
void playBeep();
void playStartMelody();
void playShutdownMelody();

View File

@@ -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,

View File

@@ -1,5 +1,6 @@
#include "concurrency/BinarySemaphoreFreeRTOS.h"
#include "configuration.h"
#include <assert.h>
#ifdef HAS_FREE_RTOS
@@ -9,6 +10,7 @@ namespace concurrency
BinarySemaphoreFreeRTOS::BinarySemaphoreFreeRTOS()
{
semaphore = xSemaphoreCreateBinary();
assert(semaphore);
}
BinarySemaphoreFreeRTOS::~BinarySemaphoreFreeRTOS()

View File

@@ -32,8 +32,10 @@ OSThread::OSThread(const char *_name, uint32_t period, ThreadController *_contro
ThreadName = _name;
if (controller)
controller->add(this);
if (controller) {
bool added = controller->add(this);
assert(added);
}
}
OSThread::~OSThread()

View File

@@ -163,7 +163,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#if defined(TBEAM_V10)
// This string must exactly match the case used in release file names or the android updater won't work
#define HW_VENDOR "tbeam"
#define HW_VENDOR HardwareModel_TBEAM
// #define BUTTON_NEED_PULLUP // if set we need to turn on the internal CPU pullup during sleep
@@ -204,7 +204,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#elif defined(TBEAM_V07)
// This string must exactly match the case used in release file names or the android updater won't work
#define HW_VENDOR "tbeam0.7"
#define HW_VENDOR HardwareModel_TBEAM0p7
// #define BUTTON_NEED_PULLUP // if set we need to turn on the internal CPU pullup during sleep
@@ -228,7 +228,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#elif defined(ARDUINO_HELTEC_WIFI_LORA_32_V2)
// This string must exactly match the case used in release file names or the android updater won't work
#define HW_VENDOR "heltec"
#define HW_VENDOR HardwareModel_HELTEC
// the default ESP32 Pin of 15 is the Oled SCL, set to 36 and 37 and works fine.
// Tested on Neo6m module.
@@ -258,7 +258,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#elif defined(TLORA_V1)
// This string must exactly match the case used in release file names or the android updater won't work
#define HW_VENDOR "tlora-v1"
#define HW_VENDOR HardwareModel_TLORA_V1
#undef GPS_RX_PIN
#undef GPS_TX_PIN
#define GPS_RX_PIN 36
@@ -282,7 +282,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#elif defined(TLORA_V2)
// This string must exactly match the case used in release file names or the android updater won't work
#define HW_VENDOR "tlora-v2"
#define HW_VENDOR HardwareModel_TLORA_V2
#undef GPS_RX_PIN
#undef GPS_TX_PIN
@@ -309,14 +309,41 @@ 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_V2_1_16)
#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-v2-1-1.6"
#define HW_VENDOR HardwareModel_TLORA_V1_1p3
#undef GPS_RX_PIN
#undef GPS_TX_PIN
#define GPS_RX_PIN 36
#define GPS_TX_PIN 39
#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 HardwareModel_TLORA_V2_1_1p6
#undef GPS_RX_PIN
#undef GPS_TX_PIN
#define GPS_RX_PIN 15 // per @der_bear on the forum, 36 is incorrect for this board type and 15 is a better pick
#define GPS_TX_PIN 13
#define BATTERY_PIN 35 // A battery voltage measurement pin, voltage divider connected here to measure battery voltage
@@ -327,9 +354,8 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#define VEXT_ENABLE 21 // active low, powers the oled display and the lora antenna boost
#define LED_PIN 25 // If defined we will blink this LED
#define BUTTON_PIN \
12 // If defined, this will be used for user button presses, if your board doesn't have a physical switch, you can wire one
// between this pin and ground
#define BUTTON_PIN 12 // If defined, this will be used for user button presses,
#define BUTTON_NEED_PULLUP
#define USE_RF95
@@ -340,7 +366,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#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"
#define HW_VENDOR HardwareModel_GENIEBLOCKS
#undef GPS_RX_PIN
#undef GPS_TX_PIN
#define GPS_RX_PIN 5
@@ -348,15 +374,16 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#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_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 dont have internal pull-ups or pull-down resistors.
#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 dont have internal
// pull-ups or pull-down resistors.
#define USE_RF95
#define LORA_DIO0 38 // a No connect on the SX1262 module
@@ -372,7 +399,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#ifdef ARDUINO_NRF52840_PCA10056
// This string must exactly match the case used in release file names or the android updater won't work
#define HW_VENDOR "nrf52dk"
#define HW_VENDOR HardwareModel_NRF52840DK
// This board uses 0 to be mean LED on
#undef LED_INVERTED
@@ -380,15 +407,15 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#elif defined(ARDUINO_NRF52840_PPR)
#define HW_VENDOR "ppr"
#define HW_VENDOR HardwareModel_PPR
#elif NRF52_SERIES
#define HW_VENDOR "nrf52unknown" // FIXME - unknown nrf52 board
#define HW_VENDOR HardwareModel_NRF52_UNKNOWN
#elif PORTDUINO
#define HW_VENDOR "portduino"
#define HW_VENDOR HardwareModel_PORTDUINO
#define USE_SIM_RADIO

View File

@@ -1,12 +1,14 @@
#include <Arduino.h>
#include "../concurrency/LockGuard.h"
#include "../graphics/Screen.h"
#include "../main.h"
#include "BluetoothSoftwareUpdate.h"
#include "NodeDB.h"
#include "PowerFSM.h"
#include "RadioLibInterface.h"
#include "configuration.h"
#include "nimble/BluetoothUtil.h"
#include "NodeDB.h"
#include <CRC32.h>
#include <Update.h>
@@ -47,9 +49,10 @@ 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
// writing flash - shut the radio off during updates
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
}
}
@@ -75,7 +78,7 @@ int update_data_callback(uint16_t conn_handle, uint16_t attr_handle, struct ble_
crc.update(data, len);
Update.write(data, len);
updateActualSize += len;
powerFSM.trigger(EVENT_CONTACT_FROM_PHONE);
powerFSM.trigger(EVENT_FIRMWARE_UPDATE);
return 0;
}
@@ -104,8 +107,7 @@ int update_crc32_callback(uint16_t conn_handle, uint16_t attr_handle, struct ble
if (update_region == U_SPIFFS) {
DEBUG_MSG("SPIFFS updated!\n");
nodeDB.saveToDisk(); // Since we just wiped spiffs, we need to save our current state
}
else {
} else {
DEBUG_MSG("Appload updated, rebooting in 5 seconds!\n");
rebootAtMsec = millis() + 5000;
}

View File

@@ -18,9 +18,6 @@ class ESP32CryptoEngine : public CryptoEngine
mbedtls_aes_context aes;
/// How many bytes in our key
uint8_t keySize = 0;
public:
ESP32CryptoEngine() { mbedtls_aes_init(&aes); }
@@ -35,12 +32,12 @@ class ESP32CryptoEngine : public CryptoEngine
* @param bytes a _static_ buffer that will remain valid for the life of this crypto instance (i.e. this class will cache the
* provided pointer)
*/
virtual void setKey(size_t numBytes, uint8_t *bytes)
virtual void setKey(const CryptoKey &k)
{
keySize = numBytes;
DEBUG_MSG("Installing AES%d key!\n", numBytes * 8);
if (numBytes != 0) {
auto res = mbedtls_aes_setkey_enc(&aes, bytes, numBytes * 8);
CryptoEngine::setKey(k);
if (key.length != 0) {
auto res = mbedtls_aes_setkey_enc(&aes, key.bytes, key.length * 8);
assert(!res);
}
}
@@ -52,12 +49,12 @@ class ESP32CryptoEngine : public CryptoEngine
*/
virtual void encrypt(uint32_t fromNode, uint64_t packetNum, size_t numBytes, uint8_t *bytes)
{
if (keySize != 0) {
if (key.length > 0) {
uint8_t stream_block[16];
static uint8_t scratch[MAX_BLOCKSIZE];
size_t nc_off = 0;
// DEBUG_MSG("ESP32 encrypt!\n");
// DEBUG_MSG("ESP32 crypt fr=%x, num=%x, numBytes=%d!\n", fromNode, (uint32_t) packetNum, numBytes);
initNonce(fromNode, packetNum);
assert(numBytes <= MAX_BLOCKSIZE);
memcpy(scratch, bytes, numBytes);

View File

@@ -25,7 +25,7 @@ uint8_t GPS::i2cAddress = 0;
GPS *gps;
/// Multiple GPS instances might use the same serial port (in sequence), but we can
/// Multiple GPS instances might use the same serial port (in sequence), but we can
/// only init that port once.
static bool didSerialInit;
@@ -33,7 +33,7 @@ bool GPS::setupGPS()
{
if (_serial_gps && !didSerialInit) {
didSerialInit = true;
#ifdef GPS_RX_PIN
_serial_gps->begin(GPS_BAUDRATE, SERIAL_8N1, GPS_RX_PIN, GPS_TX_PIN);
#else
@@ -73,6 +73,13 @@ bool GPS::setup()
return ok;
}
GPS::~GPS()
{
// we really should unregister our sleep observer
notifySleepObserver.unobserve();
notifyDeepSleepObserver.unobserve();
}
// Allow defining the polarity of the WAKE output. default is active high
#ifndef GPS_WAKE_ACTIVE
#define GPS_WAKE_ACTIVE 1
@@ -86,8 +93,8 @@ void GPS::wake()
#endif
}
void GPS::sleep() {
void GPS::sleep()
{
#ifdef PIN_GPS_WAKE
digitalWrite(PIN_GPS_WAKE, GPS_WAKE_ACTIVE ? 0 : 1);
pinMode(PIN_GPS_WAKE, OUTPUT);
@@ -158,7 +165,8 @@ uint32_t GPS::getWakeTime() const
return t; // already maxint
if (t == 0)
t = radioConfig.preferences.is_router ? 5 * 60 : 15 * 60; // Allow up to 15 mins for each attempt (probably will be much less if we can find sats) or less if a router
t = radioConfig.preferences.is_router ? 5 * 60 : 15 * 60; // Allow up to 15 mins for each attempt (probably will be much
// less if we can find sats) or less if a router
t *= 1000; // msecs
@@ -179,8 +187,8 @@ uint32_t GPS::getSleepTime() const
if (t == UINT32_MAX)
return t; // already maxint
if (t == 0) // default - unset in preferences
t = radioConfig.preferences.is_router ? 24 * 60 * 60 : 2 * 60; // 2 mins or once per day for routers
if (t == 0) // default - unset in preferences
t = radioConfig.preferences.is_router ? 24 * 60 * 60 : 2 * 60; // 2 mins or once per day for routers
t *= 1000;
@@ -300,3 +308,49 @@ int GPS::prepareDeepSleep(void *unused)
return 0;
}
#ifdef GPS_TX_PIN
#include "UBloxGPS.h"
#endif
#ifdef HAS_AIR530_GPS
#include "Air530GPS.h"
#elif !defined(NO_GPS)
#include "NMEAGPS.h"
#endif
GPS* createGps() {
#ifdef NO_GPS
return nullptr;
#else
// If we don't have bidirectional comms, we can't even try talking to UBLOX
#ifdef GPS_TX_PIN
// Init GPS - first try ublox
UBloxGPS *ublox = new UBloxGPS();
if (!ublox->setup()) {
DEBUG_MSG("ERROR: No UBLOX GPS found\n");
delete ublox;
ublox = NULL;
} else {
return ublox;
}
#endif
if (GPS::_serial_gps) {
// Some boards might have only the TX line from the GPS connected, in that case, we can't configure it at all. Just
// assume NMEA at 9600 baud.
DEBUG_MSG("Hoping that NMEA might work\n");
#ifdef HAS_AIR530_GPS
GPS* new_gps = new Air530GPS();
#else
GPS* new_gps = new NMEAGPS();
#endif
new_gps->setup();
return new_gps;
}
return nullptr;
#endif
}

View File

@@ -47,7 +47,7 @@ class GPS : private concurrency::OSThread
GPS() : concurrency::OSThread("GPS") {}
virtual ~GPS() {} // FIXME, we really should unregister our sleep observer
virtual ~GPS();
/** We will notify this observable anytime GPS state has changed meaningfully */
Observable<const meshtastic::GPSStatus *> newStatus;
@@ -71,6 +71,9 @@ class GPS : private concurrency::OSThread
* */
void forceWake(bool on);
// Some GPS modules (ublock) require factory reset
virtual bool factoryReset() { return true; }
protected:
/// Do gps chipset specific init, return true for success
virtual bool setupGPS();
@@ -145,4 +148,8 @@ class GPS : private concurrency::OSThread
virtual int32_t runOnce();
};
// Creates an instance of the GPS class.
// Returns the new instance or null if the GPS is not present.
GPS* createGps();
extern GPS *gps;

View File

@@ -42,19 +42,20 @@ bool perhapsSetRTC(RTCQuality q, const struct timeval *tv)
} else if(q == RTCQualityGPS && (now - lastSetMsec) > (12 * 60 * 60 * 1000L)) {
// Every 12 hrs we will slam in a new GPS time, to correct for local RTC clock drift
shouldSet = true;
DEBUG_MSG("Reapplying GPS time to correct clock drift %ld secs\n", tv->tv_sec);
DEBUG_MSG("Reapplying external time to correct clock drift %ld secs\n", tv->tv_sec);
}
else
shouldSet = false;
if (shouldSet) {
lastSetMsec = now;
lastSetMsec = now;
#ifndef NO_ESP32
settimeofday(tv, NULL);
#else
DEBUG_MSG("ERROR TIME SETTING NOT IMPLEMENTED!\n");
#endif
readFromRTC();
#else
timeStartMsec = now;
zeroOffsetSecs = tv->tv_sec;
#endif
return true;
} else {
return false;

View File

@@ -27,4 +27,8 @@ uint32_t getTime();
/// Return time since 1970 in secs. If quality is RTCQualityNone return zero
uint32_t getValidTime(RTCQuality minQuality);
void readFromRTC();
void readFromRTC();
#define SEC_PER_DAY 86400
#define SEC_PER_HOUR 3600
#define SEC_PER_MIN 60

View File

@@ -28,9 +28,11 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#include "Screen.h"
#include "configuration.h"
#include "fonts.h"
#include "gps/RTC.h"
#include "graphics/images.h"
#include "main.h"
#include "mesh-pb-constants.h"
#include "mesh/Channels.h"
#include "plugins/TextMessagePlugin.h"
#include "target_specific.h"
#include "utils.h"
@@ -64,6 +66,10 @@ uint8_t imgBattery[16] = {0xFF, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81,
// Threshold values for the GPS lock accuracy bar display
uint32_t dopThresholds[5] = {2000, 1000, 500, 200, 100};
// At some point, we're going to ask all of the plugins if they would like to display a screen frame
// we'll need to hold onto pointers for the plugins that can draw a frame.
std::vector<MeshPlugin *> pluginFrames;
// Stores the last 4 of our hardware ID, to make finding the device for pairing easier
static char ourId[5];
@@ -138,11 +144,35 @@ static void drawBootScreen(OLEDDisplay *display, OLEDDisplayUiState *state, int1
drawIconScreen(region, display, state, x, y);
}
#ifdef HAS_EINK
/// Used on eink displays while in deep sleep
static void drawSleepScreen(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y)
{
drawIconScreen("Sleeping...", display, state, x, y);
}
#endif
static void drawPluginFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y)
{
uint8_t plugin_frame;
// there's a little but in the UI transition code
// where it invokes the function at the correct offset
// in the array of "drawScreen" functions; however,
// the passed-state doesn't quite reflect the "current"
// screen, so we have to detect it.
if (state->frameState == IN_TRANSITION && state->transitionFrameRelationship == INCOMING) {
// if we're transitioning from the end of the frame list back around to the first
// frame, then we want this to be `0`
plugin_frame = state->transitionFrameTarget;
} else {
// otherwise, just display the plugin frame that's aligned with the current frame
plugin_frame = state->currentFrame;
// DEBUG_MSG("Screen is not in transition. Frame: %d\n\n", plugin_frame);
}
// DEBUG_MSG("Drawing Plugin Frame %d\n\n", plugin_frame);
MeshPlugin &pi = *pluginFrames.at(plugin_frame);
pi.drawFrame(display, state, x, y);
}
static void drawFrameBluetooth(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y)
{
@@ -164,6 +194,19 @@ 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)
{
@@ -186,7 +229,7 @@ static void drawTextMessageFrame(OLEDDisplay *display, OLEDDisplayUiState *state
displayedNodeNum = 0; // Not currently showing a node pane
MeshPacket &mp = devicestate.rx_text_message;
NodeInfo *node = nodeDB.getNode(mp.from);
NodeInfo *node = nodeDB.getNode(getFrom(&mp));
// DEBUG_MSG("drawing text message from 0x%x: %s\n", mp.from,
// mp.decoded.variant.data.decoded.bytes);
@@ -201,8 +244,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_payloadVariant == SubPacket_data_tag);
snprintf(tempBuf, sizeof(tempBuf), " %s", mp.decoded.data.payload.bytes);
snprintf(tempBuf, sizeof(tempBuf), " %s", mp.decoded.payload.bytes);
display->drawStringMaxWidth(4 + x, 10 + y, SCREEN_WIDTH - (6 + x), tempBuf);
}
@@ -749,7 +791,8 @@ void Screen::setup()
powerStatusObserver.observe(&powerStatus->onNewStatus);
gpsStatusObserver.observe(&gpsStatus->onNewStatus);
nodeStatusObserver.observe(&nodeStatus->onNewStatus);
textMessageObserver.observe(textMessagePlugin);
if (textMessagePlugin)
textMessageObserver.observe(textMessagePlugin);
}
void Screen::forceDisplay()
@@ -776,9 +819,6 @@ int32_t Screen::runOnce()
showingBootScreen = false;
}
// Update the screen last, after we've figured out what to show.
debug_info()->setChannelNameStatus(getChannelName());
// Process incoming commands.
for (;;) {
ScreenCmd cmd;
@@ -798,6 +838,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();
@@ -807,7 +850,7 @@ int32_t Screen::runOnce()
free(cmd.print_text);
break;
default:
DEBUG_MSG("BUG: invalid cmd");
DEBUG_MSG("BUG: invalid cmd\n");
}
}
@@ -870,6 +913,11 @@ void Screen::setFrames()
DEBUG_MSG("showing standard frames\n");
showingNormalScreen = true;
pluginFrames = MeshPlugin::GetMeshPluginsWithUIFrames();
DEBUG_MSG("Showing %d plugin frames\n", pluginFrames.size());
int totalFrameCount = MAX_NUM_NODES + NUM_EXTRA_FRAMES + pluginFrames.size();
DEBUG_MSG("Total frame count: %d\n", totalFrameCount);
// We don't show the node info our our node (if we have it yet - we should)
size_t numnodes = nodeStatus->getNumTotal();
if (numnodes > 0)
@@ -877,6 +925,18 @@ void Screen::setFrames()
size_t numframes = 0;
// put all of the plugin frames first.
// this is a little bit of a dirty hack; since we're going to call
// the same drawPluginFrame handler here for all of these plugin frames
// and then we'll just assume that the state->currentFrame value
// is the same offset into the pluginFrames vector
// so that we can invoke the plugin's callback
for (auto i = pluginFrames.begin(); i != pluginFrames.end(); ++i) {
normalFrames[numframes++] = drawPluginFrame;
}
DEBUG_MSG("Added plugins. numframes: %d\n", numframes);
// If we have a critical fault, show it first
if (myNodeInfo.error_code)
normalFrames[numframes++] = drawCriticalFaultFrame;
@@ -905,6 +965,8 @@ void Screen::setFrames()
}
#endif
DEBUG_MSG("Finished building frames. numframes: %d\n", numframes);
ui.setFrames(normalFrames, numframes);
ui.enableAllIndicators();
@@ -928,6 +990,18 @@ void Screen::handleStartBluetoothPinScreen(uint32_t pin)
setFastFramerate();
}
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();
@@ -993,7 +1067,8 @@ void DebugInfo::drawFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16
char channelStr[20];
{
concurrency::LockGuard guard(&lock);
snprintf(channelStr, sizeof(channelStr), "%s", channelName.c_str());
auto chName = channels.getPrimaryName();
snprintf(channelStr, sizeof(channelStr), "%s", chName);
}
// Display power status
@@ -1200,8 +1275,8 @@ void DebugInfo::drawFrameSettings(OLEDDisplay *display, OLEDDisplayUiState *stat
display->drawString(x, y, String("USB"));
}
display->drawString(x + SCREEN_WIDTH - display->getStringWidth("Mode " + String(channelSettings.modem_config)), y,
"Mode " + String(channelSettings.modem_config));
auto mode = "Mode " + String(channels.getPrimary().modem_config);
display->drawString(x + SCREEN_WIDTH - display->getStringWidth(mode), y, mode);
// Line 2
uint32_t currentMillis = millis();
@@ -1209,14 +1284,41 @@ void DebugInfo::drawFrameSettings(OLEDDisplay *display, OLEDDisplayUiState *stat
uint32_t minutes = seconds / 60;
uint32_t hours = minutes / 60;
uint32_t days = hours / 24;
currentMillis %= 1000;
seconds %= 60;
minutes %= 60;
hours %= 24;
// currentMillis %= 1000;
// seconds %= 60;
// minutes %= 60;
// hours %= 24;
display->drawString(x, y + FONT_HEIGHT_SMALL * 1,
String(days) + "d " + (hours < 10 ? "0" : "") + String(hours) + ":" + (minutes < 10 ? "0" : "") +
String(minutes) + ":" + (seconds < 10 ? "0" : "") + String(seconds));
// Show uptime as days, hours, minutes OR seconds
String uptime;
if (days >= 2)
uptime += String(days) + "d ";
else if (hours >= 2)
uptime += String(hours) + "h ";
else if (minutes >= 1)
uptime += String(minutes) + "m ";
else
uptime += String(seconds) + "s ";
uint32_t rtc_sec = getValidTime(RTCQuality::RTCQualityFromNet);
if (rtc_sec > 0) {
long hms = rtc_sec % SEC_PER_DAY;
// hms += tz.tz_dsttime * SEC_PER_HOUR;
// hms -= tz.tz_minuteswest * SEC_PER_MIN;
// mod `hms` to ensure in positive range of [0...SEC_PER_DAY)
hms = (hms + SEC_PER_DAY) % SEC_PER_DAY;
// Tear apart hms into h:m:s
int hour = hms / SEC_PER_HOUR;
int min = (hms % SEC_PER_HOUR) / SEC_PER_MIN;
int sec = (hms % SEC_PER_HOUR) % SEC_PER_MIN; // or hms % SEC_PER_MIN
char timebuf[9];
snprintf(timebuf, sizeof(timebuf), "%02d:%02d:%02d", hour, min, sec);
uptime += timebuf;
}
display->drawString(x, y + FONT_HEIGHT_SMALL * 1, uptime);
#ifndef NO_ESP32
// Show CPU Frequency.

View File

@@ -1,5 +1,23 @@
#pragma once
#ifdef NO_SCREEN
namespace graphics
{
// Noop class for boards without screen.
class Screen
{
public:
Screen(char){}
void onPress() {}
void setup() {}
void setOn(bool) {}
void print(const char*){}
void adjustBrightness(){}
void doDeepSleep() {}
};
}
#else
#include <cstring>
#include <OLEDDisplayUi.h>
@@ -39,13 +57,6 @@ class DebugInfo
DebugInfo(const DebugInfo &) = delete;
DebugInfo &operator=(const DebugInfo &) = delete;
/// Sets the name of the channel.
void setChannelNameStatus(const char *name)
{
concurrency::LockGuard guard(&lock);
channelName = name;
}
private:
friend Screen;
@@ -56,8 +67,6 @@ class DebugInfo
void drawFrameSettings(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y);
void drawFrameWiFi(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y);
std::string channelName;
/// Protects all of internal state.
concurrency::Lock lock;
};
@@ -128,6 +137,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 +250,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();
@@ -278,3 +296,4 @@ class Screen : public concurrency::OSThread
};
} // namespace graphics
#endif

View File

@@ -1,11 +1,11 @@
#include "Air530GPS.h"
#include "GPS.h"
#include "MeshRadio.h"
#include "MeshService.h"
#include "NodeDB.h"
#include "PowerFSM.h"
#include "UBloxGPS.h"
#include "airtime.h"
#include "buzz.h"
#include "configuration.h"
#include "error.h"
#include "power.h"
@@ -32,6 +32,10 @@
#include "nimble/BluetoothUtil.h"
#endif
#ifdef PORTDUINO
#include "mesh/wifi/WiFiServerAPI.h"
#endif
#include "RF95Interface.h"
#include "SX1262Interface.h"
@@ -63,7 +67,7 @@ Router *router = NULL; // Users of router don't care what sort of subclass imple
// -----------------------------------------------------------------------------
// Application
// -----------------------------------------------------------------------------
#ifndef NO_WIRE
void scanI2Cdevice(void)
{
byte err, addr;
@@ -100,6 +104,9 @@ void scanI2Cdevice(void)
else
DEBUG_MSG("done\n");
}
#else
void scanI2Cdevice(void) {}
#endif
const char *getDeviceName()
{
@@ -169,7 +176,7 @@ class ButtonThread : public OSThread
#ifdef BUTTON_PIN_ALT
OneButton userButtonAlt;
#endif
static bool shutdown_on_long_stop;
public:
static uint32_t longPressTime;
@@ -235,13 +242,23 @@ class ButtonThread : public OSThread
// DEBUG_MSG("Long press!\n");
screen->adjustBrightness();
// If user button is held down for 10 seconds, shutdown the device.
if (millis() - longPressTime > 10 * 1000) {
// If user button is held down for 5 seconds, shutdown the device.
if (millis() - longPressTime > 5 * 1000) {
#ifdef TBEAM_V10
if (axp192_found == true) {
setLed(false);
power->shutdown();
}
#elif NRF52_SERIES
// Do actual shutdown when button released, otherwise the button release
// may wake the board immediatedly.
if (!shutdown_on_long_stop) {
DEBUG_MSG("Shutdown from long press");
playBeep();
ledOff(PIN_LED1);
ledOff(PIN_LED2);
shutdown_on_long_stop = true;
}
#endif
} else {
// DEBUG_MSG("Long press %u\n", (millis() - longPressTime));
@@ -265,9 +282,15 @@ class ButtonThread : public OSThread
{
DEBUG_MSG("Long press stop!\n");
longPressTime = 0;
if (shutdown_on_long_stop) {
playShutdownMelody();
power->shutdown();
}
}
};
bool ButtonThread::shutdown_on_long_stop = false;
static Periodic *ledPeriodic;
static OSThread *powerFSMthread, *buttonThread;
uint32_t ButtonThread::longPressTime = 0;
@@ -279,7 +302,13 @@ void setup()
concurrency::hasBeenSetup = true;
#ifdef SEGGER_STDOUT_CH
SEGGER_RTT_ConfigUpBuffer(SEGGER_STDOUT_CH, NULL, NULL, 1024, SEGGER_RTT_MODE_NO_BLOCK_TRIM);
auto mode = false ? SEGGER_RTT_MODE_BLOCK_IF_FIFO_FULL : SEGGER_RTT_MODE_NO_BLOCK_TRIM;
#ifdef NRF52840_XXAA
auto buflen = 4096; // this board has a fair amount of ram
#else
auto buflen = 256; // this board has a fair amount of ram
#endif
SEGGER_RTT_ConfigUpBuffer(SEGGER_STDOUT_CH, NULL, NULL, buflen, mode);
#endif
#ifdef USE_SEGGER
@@ -305,12 +334,15 @@ void setup()
#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;
// If the button is connected to GPIO 12, don't enable the ability to use
// meshtasticAdmin on the device.
pinMode(BUTTON_PIN, INPUT);
#ifdef BUTTON_NEED_PULLUP
gpio_pullup_en((gpio_num_t)BUTTON_PIN);
delay(10);
#endif
// BUTTON_PIN is pulled high by a 12k resistor.
@@ -332,7 +364,7 @@ void setup()
#ifdef I2C_SDA
Wire.begin(I2C_SDA, I2C_SCL);
#else
#elif !defined(NO_WIRE)
Wire.begin();
#endif
@@ -356,7 +388,7 @@ void setup()
#endif
// Hello
DEBUG_MSG("Meshtastic swver=%s, hwver=%s\n", optstr(APP_VERSION), optstr(HW_VERSION));
DEBUG_MSG("Meshtastic hwvendor=%d, swver=%s, hwver=%s\n", HW_VENDOR, optstr(APP_VERSION), optstr(HW_VERSION));
#ifndef NO_ESP32
// Don't init display if we don't have one or we are waking headless due to a timer event
@@ -369,7 +401,7 @@ void setup()
#ifdef NRF52_SERIES
nrf52Setup();
#endif
playStartMelody();
// We do this as early as possible because this loads preferences from flash
// but we need to do this after main cpu iniot (esp32setup), because we need the random seed set
nodeDB.init();
@@ -407,34 +439,7 @@ void setup()
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
// Init GPS - first try ublox
ublox = new UBloxGPS();
gps = ublox;
if (!gps->setup()) {
DEBUG_MSG("ERROR: No UBLOX GPS found\n");
delete ublox;
gps = ublox = NULL;
}
#endif
if (!gps && GPS::_serial_gps) {
// Some boards might have only the TX line from the GPS connected, in that case, we can't configure it at all. Just
// assume NMEA at 9600 baud.
// dumb NMEA access only work for serial GPSes)
DEBUG_MSG("Hoping that NMEA might work\n");
#ifdef HAS_AIR530_GPS
gps = new Air530GPS();
#else
gps = new NMEAGPS();
#endif
gps->setup();
}
gps = createGps();
if (gps)
gpsStatus->observe(&gps->newStatus);
@@ -468,8 +473,8 @@ void setup()
// We have now loaded our saved preferences from flash
// ONCE we will factory reset the GPS for bug #327
if (ublox && !devicestate.did_gps_reset) {
if (ublox->factoryReset()) { // If we don't succeed try again next time
if (gps && !devicestate.did_gps_reset) {
if (gps->factoryReset()) { // If we don't succeed try again next time
devicestate.did_gps_reset = true;
nodeDB.saveToDisk();
}
@@ -530,6 +535,10 @@ void setup()
webServerThread = new WebServerThread();
#endif
#ifdef PORTDUINO
initApiServer();
#endif
// Start airtime logger thread.
airTime = new AirTime();
@@ -580,6 +589,9 @@ void loop()
#ifndef NO_ESP32
esp32Loop();
#endif
#ifdef NRF52_SERIES
nrf52Loop();
#endif
// For debugging
// if (rIf) ((RadioLibInterface *)rIf)->isActivelyReceiving();

318
src/memtest.cpp Normal file
View File

@@ -0,0 +1,318 @@
/*
* mtest - Perform a memory test
*
* (C) Copyright 2000
* Wolfgang Denk, DENX Software Engineering, wd@denx.de.
*
* See file CREDITS for list of people who contributed to this
* project.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
*/
#include "configuration.h"
/*
* Perform a memory test. A more complete alternative test can be
* configured using CONFIG_CMD_MTEST_ALTERNATIVE. The complete test
* loops until interrupted by ctrl-c or by a failure of one of the
* sub-tests.
*/
#ifdef CONFIG_CMD_MTEST_ALTERNATIVE
static int mem_test(uint32_t _start, uint32_t _end, uint32_t pattern_unused)
{
volatile uint32_t *start = (volatile uint32_t *)_start;
volatile uint32_t *end = (volatile uint32_t *)_end;
volatile uint32_t *addr;
uint32_t val;
uint32_t readback;
vu_long addr_mask;
vu_long offset;
vu_long test_offset;
vu_long pattern;
vu_long temp;
vu_long anti_pattern;
vu_long num_words;
#ifdef CFG_MEMTEST_SCRATCH
volatile uint32_t *dummy = (vu_long *)CFG_MEMTEST_SCRATCH;
#else
volatile uint32_t *dummy = start;
#endif
int j;
int iterations = 1;
static const uint32_t bitpattern[] = {
0x00000001, /* single bit */
0x00000003, /* two adjacent bits */
0x00000007, /* three adjacent bits */
0x0000000F, /* four adjacent bits */
0x00000005, /* two non-adjacent bits */
0x00000015, /* three non-adjacent bits */
0x00000055, /* four non-adjacent bits */
0xaaaaaaaa, /* alternating 1/0 */
};
/* XXX: enforce alignment of start and end? */
for (;;) {
if (ctrlc()) {
putchar('\n');
return 1;
}
printf("Iteration: %6d\r", iterations);
iterations++;
/*
* Data line test: write a pattern to the first
* location, write the 1's complement to a 'parking'
* address (changes the state of the data bus so a
* floating bus doen't give a false OK), and then
* read the value back. Note that we read it back
* into a variable because the next time we read it,
* it might be right (been there, tough to explain to
* the quality guys why it prints a failure when the
* "is" and "should be" are obviously the same in the
* error message).
*
* Rather than exhaustively testing, we test some
* patterns by shifting '1' bits through a field of
* '0's and '0' bits through a field of '1's (i.e.
* pattern and ~pattern).
*/
addr = start;
/* XXX */
if (addr == dummy)
++addr;
for (j = 0; j < sizeof(bitpattern) / sizeof(bitpattern[0]); j++) {
val = bitpattern[j];
for (; val != 0; val <<= 1) {
*addr = val;
*dummy = ~val; /* clear the test data off of the bus */
readback = *addr;
if (readback != val) {
printf("FAILURE (data line): "
"expected 0x%08lx, actual 0x%08lx at address 0x%p\n",
val, readback, addr);
}
*addr = ~val;
*dummy = val;
readback = *addr;
if (readback != ~val) {
printf("FAILURE (data line): "
"Is 0x%08lx, should be 0x%08lx at address 0x%p\n",
readback, ~val, addr);
}
}
}
/*
* Based on code whose Original Author and Copyright
* information follows: Copyright (c) 1998 by Michael
* Barr. This software is placed into the public
* domain and may be used for any purpose. However,
* this notice must not be changed or removed and no
* warranty is either expressed or implied by its
* publication or distribution.
*/
/*
* Address line test
*
* Description: Test the address bus wiring in a
* memory region by performing a walking
* 1's test on the relevant bits of the
* address and checking for aliasing.
* This test will find single-bit
* address failures such as stuck -high,
* stuck-low, and shorted pins. The base
* address and size of the region are
* selected by the caller.
*
* Notes: For best results, the selected base
* address should have enough LSB 0's to
* guarantee single address bit changes.
* For example, to test a 64-Kbyte
* region, select a base address on a
* 64-Kbyte boundary. Also, select the
* region size as a power-of-two if at
* all possible.
*
* Returns: 0 if the test succeeds, 1 if the test fails.
*
* ## NOTE ## Be sure to specify start and end
* addresses such that addr_mask has
* lots of bits set. For example an
* address range of 01000000 02000000 is
* bad while a range of 01000000
* 01ffffff is perfect.
*/
addr_mask = ((uint32_t)end - (uint32_t)start) / sizeof(vu_long);
pattern = (vu_long)0xaaaaaaaa;
anti_pattern = (vu_long)0x55555555;
debug("%s:%d: addr mask = 0x%.8lx\n", __FUNCTION__, __LINE__, addr_mask);
/*
* Write the default pattern at each of the
* power-of-two offsets.
*/
for (offset = 1; (offset & addr_mask) != 0; offset <<= 1)
start[offset] = pattern;
/*
* Check for address bits stuck high.
*/
test_offset = 0;
start[test_offset] = anti_pattern;
for (offset = 1; (offset & addr_mask) != 0; offset <<= 1) {
temp = start[offset];
if (temp != pattern) {
printf("\nFAILURE: Address bit stuck high @ 0x%.8lx:"
" expected 0x%.8lx, actual 0x%.8lx\n",
(uint32_t)&start[offset], pattern, temp);
return 1;
}
}
start[test_offset] = pattern;
/*
* Check for addr bits stuck low or shorted.
*/
for (test_offset = 1; (test_offset & addr_mask) != 0; test_offset <<= 1) {
start[test_offset] = anti_pattern;
for (offset = 1; (offset & addr_mask) != 0; offset <<= 1) {
temp = start[offset];
if ((temp != pattern) && (offset != test_offset)) {
printf("\nFAILURE: Address bit stuck low or shorted @"
" 0x%.8lx: expected 0x%.8lx, actual 0x%.8lx\n",
(uint32_t)&start[offset], pattern, temp);
return 1;
}
}
start[test_offset] = pattern;
}
/*
* Description: Test the integrity of a physical
* memory device by performing an
* increment/decrement test over the
* entire region. In the process every
* storage bit in the device is tested
* as a zero and a one. The base address
* and the size of the region are
* selected by the caller.
*
* Returns: 0 if the test succeeds, 1 if the test fails.
*/
num_words = ((uint32_t)end - (uint32_t)start) / sizeof(vu_long) + 1;
/*
* Fill memory with a known pattern.
*/
for (pattern = 1, offset = 0; offset < num_words; pattern++, offset++) {
start[offset] = pattern;
}
/*
* Check each location and invert it for the second pass.
*/
for (pattern = 1, offset = 0; offset < num_words; pattern++, offset++) {
temp = start[offset];
if (temp != pattern) {
printf("\nFAILURE (read/write) @ 0x%.8lx:"
" expected 0x%.8lx, actual 0x%.8lx)\n",
(uint32_t)&start[offset], pattern, temp);
return 1;
}
anti_pattern = ~pattern;
start[offset] = anti_pattern;
}
/*
* Check each location for the inverted pattern and zero it.
*/
for (pattern = 1, offset = 0; offset < num_words; pattern++, offset++) {
anti_pattern = ~pattern;
temp = start[offset];
if (temp != anti_pattern) {
printf("\nFAILURE (read/write): @ 0x%.8lx:"
" expected 0x%.8lx, actual 0x%.8lx)\n",
(uint32_t)&start[offset], anti_pattern, temp);
return 1;
}
start[offset] = 0;
}
}
}
#else
static int mem_test(uint32_t *_start, size_t len, bool doRead = true, bool doWrite = true)
{
volatile uint32_t *addr;
volatile uint32_t *start = (volatile uint32_t *)_start;
volatile uint32_t *end = start + len / sizeof(uint32_t);
uint32_t pattern = 0;
uint32_t val;
uint32_t readback;
uint32_t incr;
int rcode = 0;
incr = 1;
//DEBUG_MSG("memtest read=%d, write=%d\n", doRead, doWrite);
if (doWrite) {
//DEBUG_MSG("writing\n");
for (addr = start, val = pattern; addr < end; addr++) {
*addr = val;
val += incr;
}
}
if (doRead) {
//DEBUG_MSG("reading\n");
for (addr = start, val = pattern; addr < end; addr++) {
readback = *addr;
if (readback != val) {
DEBUG_MSG("Mem error @ 0x%08X: "
"found 0x%08lX, expected 0x%08lX\n",
addr, readback, val);
rcode++;
}
val += incr;
}
}
#if 0
/*
* Flip the pattern each time to make lots of zeros and
* then, the next time, lots of ones. We decrement
* the "negative" patterns and increment the "positive"
* patterns to preserve this feature.
*/
if(pattern & 0x80000000) {
pattern = -pattern; /* complement & increment */
}
else {
pattern = ~pattern;
}
#endif
return rcode;
}
#endif
#define TESTBUF_LEN 16384
#include <assert.h>
void doMemTest()
{
static uint32_t *testBuf;
static int iter;
if (!testBuf)
testBuf = (uint32_t *)malloc(TESTBUF_LEN);
assert(testBuf);
if (mem_test(testBuf, TESTBUF_LEN, iter % 2 == 1, iter % 2 == 0) > 0)
assert(0); // FIXME report error better
iter++;
}

301
src/mesh/Channels.cpp Normal file
View File

@@ -0,0 +1,301 @@
#include "Channels.h"
#include "CryptoEngine.h"
#include "NodeDB.h"
#include <assert.h>
/// 16 bytes of random PSK for our _public_ default channel that all devices power up on (AES128)
static const uint8_t defaultpsk[] = {0xd4, 0xf1, 0xbb, 0x3a, 0x20, 0x29, 0x07, 0x59,
0xf0, 0xbc, 0xff, 0xab, 0xcf, 0x4e, 0x69, 0xbf};
Channels channels;
const char *Channels::adminChannel = "admin";
const char *Channels::gpioChannel = "gpio";
const char *Channels::serialChannel = "serial";
uint8_t xorHash(const uint8_t *p, size_t len)
{
uint8_t code = 0;
for (size_t i = 0; i < len; i++)
code ^= p[i];
return code;
}
/** Given a channel number, return the (0 to 255) hash for that channel.
* The hash is just an xor of the channel name followed by the channel PSK being used for encryption
* If no suitable channel could be found, return -1
*/
int16_t Channels::generateHash(ChannelIndex channelNum)
{
auto k = getKey(channelNum);
if (k.length < 0)
return -1; // invalid
else {
const char *name = getName(channelNum);
uint8_t h = xorHash((const uint8_t *)name, strlen(name));
h ^= xorHash(k.bytes, k.length);
return h;
}
}
/**
* Validate a channel, fixing any errors as needed
*/
Channel &Channels::fixupChannel(ChannelIndex chIndex)
{
Channel &ch = getByIndex(chIndex);
ch.index = chIndex; // Preinit the index so it be ready to share with the phone (we'll never change it later)
if (!ch.has_settings) {
// No settings! Must disable and skip
ch.role = Channel_Role_DISABLED;
memset(&ch.settings, 0, sizeof(ch.settings));
ch.has_settings = true;
} else {
ChannelSettings &channelSettings = ch.settings;
// Convert the old string "Default" to our new short representation
if (strcmp(channelSettings.name, "Default") == 0)
*channelSettings.name = '\0';
/* 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) {
*channelSettings.psk.bytes = 1;
channelSettings.psk.size = 1;
} */
}
hashes[chIndex] = generateHash(chIndex);
return ch;
}
/**
* Write a default channel to the specified channel index
*/
void Channels::initDefaultChannel(ChannelIndex chIndex)
{
Channel &ch = getByIndex(chIndex);
ChannelSettings &channelSettings = ch.settings;
// radioConfig.modem_config = RadioConfig_ModemConfig_Bw125Cr45Sf128; // medium range and fast
// channelSettings.modem_config = ChannelSettings_ModemConfig_Bw500Cr45Sf128; // short range and fast, but wide
// bandwidth so incompatible radios can talk together
channelSettings.modem_config = ChannelSettings_ModemConfig_Bw125Cr48Sf4096; // slow and long range
channelSettings.tx_power = 0; // default
uint8_t defaultpskIndex = 1;
channelSettings.psk.bytes[0] = defaultpskIndex;
channelSettings.psk.size = 1;
strcpy(channelSettings.name, "");
ch.has_settings = true;
ch.role = Channel_Role_PRIMARY;
}
CryptoKey Channels::getKey(ChannelIndex chIndex)
{
Channel &ch = getByIndex(chIndex);
ChannelSettings &channelSettings = ch.settings;
assert(ch.has_settings);
CryptoKey k;
memset(k.bytes, 0, sizeof(k.bytes)); // In case the user provided a short key, we want to pad the rest with zeros
if (ch.role == Channel_Role_DISABLED) {
k.length = -1; // invalid
} else {
memcpy(k.bytes, channelSettings.psk.bytes, channelSettings.psk.size);
k.length = channelSettings.psk.size;
if (k.length == 0) {
if (ch.role == Channel_Role_SECONDARY) {
DEBUG_MSG("Unset PSK for secondary channel %s. using primary key\n", ch.settings.name);
k = getKey(primaryIndex);
} else
DEBUG_MSG("Warning: User disabled encryption\n");
} else if (k.length == 1) {
// Convert the short single byte variants of psk into variant that can be used more generally
uint8_t pskIndex = k.bytes[0];
DEBUG_MSG("Expanding short PSK #%d\n", pskIndex);
if (pskIndex == 0)
k.length = 0; // Turn off encryption
else {
memcpy(k.bytes, defaultpsk, sizeof(defaultpsk));
k.length = sizeof(defaultpsk);
// Bump up the last byte of PSK as needed
uint8_t *last = k.bytes + sizeof(defaultpsk) - 1;
*last = *last + pskIndex - 1; // index of 1 means no change vs defaultPSK
}
} else if (k.length < 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");
k.length = 16;
} else if (k.length < 32 && k.length != 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");
k.length = 32;
}
}
return k;
}
/** Given a channel index, change to use the crypto key specified by that index
*/
int16_t Channels::setCrypto(ChannelIndex chIndex)
{
CryptoKey k = getKey(chIndex);
if (k.length < 0)
return -1;
else {
// Tell our crypto engine about the psk
crypto->setKey(k);
return getHash(chIndex);
}
}
void Channels::initDefaults()
{
channelFile.channels_count = MAX_NUM_CHANNELS;
for (int i = 0; i < channelFile.channels_count; i++)
fixupChannel(i);
initDefaultChannel(0);
}
void Channels::onConfigChanged()
{
// Make sure the phone hasn't mucked anything up
for (int i = 0; i < channelFile.channels_count; i++) {
Channel &ch = fixupChannel(i);
if (ch.role == Channel_Role_PRIMARY)
primaryIndex = i;
}
}
Channel &Channels::getByIndex(ChannelIndex chIndex)
{
assert(chIndex < channelFile.channels_count); // This should be equal to MAX_NUM_CHANNELS
Channel *ch = channelFile.channels + chIndex;
return *ch;
}
void Channels::setChannel(const Channel &c)
{
Channel &old = getByIndex(c.index);
// if this is the new primary, demote any existing roles
if (c.role == Channel_Role_PRIMARY)
for (int i = 0; i < getNumChannels(); i++)
if (channelFile.channels[i].role == Channel_Role_PRIMARY)
channelFile.channels[i].role = Channel_Role_SECONDARY;
old = c; // slam in the new settings/role
}
const char *Channels::getName(size_t chIndex)
{
// Convert the short "" representation for Default into a usable string
ChannelSettings &channelSettings = getByIndex(chIndex).settings;
const char *channelName = channelSettings.name;
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
if (channelSettings.bandwidth != 0)
channelName = "Unset";
else
switch (channelSettings.modem_config) {
case ChannelSettings_ModemConfig_Bw125Cr45Sf128:
channelName = "Medium";
break;
case ChannelSettings_ModemConfig_Bw500Cr45Sf128:
channelName = "ShortFast";
break;
case ChannelSettings_ModemConfig_Bw31_25Cr48Sf512:
channelName = "LongAlt";
break;
case ChannelSettings_ModemConfig_Bw125Cr48Sf4096:
channelName = "LongSlow";
break;
default:
channelName = "Invalid";
break;
}
}
return channelName;
}
/**
* Generate a short suffix used to disambiguate channels that might have the same "name" entered by the human but different PSKs.
* The ideas is that the PSK changing should be visible to the user so that they see they probably messed up and that's why they
their nodes
* aren't talking to each other.
*
* This string is of the form "#name-X".
*
* Where X is either:
* (for custom PSKS) a letter from A to Z (base26), and formed by xoring all the bytes of the PSK together,
*
* This function will also need to be implemented in GUI apps that talk to the radio.
*
* https://github.com/meshtastic/Meshtastic-device/issues/269
*/
const char *Channels::getPrimaryName()
{
static char buf[32];
char suffix;
// auto channelSettings = getPrimary();
// if (channelSettings.psk.size != 1) {
// We have a standard PSK, so generate a letter based hash.
uint8_t code = getHash(primaryIndex);
suffix = 'A' + (code % 26);
/* } else {
suffix = '0' + channelSettings.psk.bytes[0];
} */
snprintf(buf, sizeof(buf), "#%s-%c", getName(primaryIndex), suffix);
return buf;
}
/** Given a channel hash setup crypto for decoding that channel (or the primary channel if that channel is unsecured)
*
* This method is called before decoding inbound packets
*
* @return false if the channel hash or channel is invalid
*/
bool Channels::decryptForHash(ChannelIndex chIndex, ChannelHash channelHash)
{
if (chIndex > getNumChannels() || getHash(chIndex) != channelHash) {
// DEBUG_MSG("Skipping channel %d (hash %x) due to invalid hash/index, want=%x\n", chIndex, getHash(chIndex),
// channelHash);
return false;
} else {
DEBUG_MSG("Using channel %d (hash 0x%x)\n", chIndex, channelHash);
setCrypto(chIndex);
return true;
}
}
/** Given a channel index setup crypto for encoding that channel (or the primary channel if that channel is unsecured)
*
* This method is called before encoding outbound packets
*
* @eturn the (0 to 255) hash for that channel - if no suitable channel could be found, return -1
*/
int16_t Channels::setActiveByIndex(ChannelIndex channelIndex)
{
return setCrypto(channelIndex);
}

131
src/mesh/Channels.h Normal file
View File

@@ -0,0 +1,131 @@
#pragma once
#include "CryptoEngine.h"
#include "NodeDB.h"
#include "mesh-pb-constants.h"
#include <Arduino.h>
/** A channel number (index into the channel table)
*/
typedef uint8_t ChannelIndex;
/** A low quality hash of the channel PSK and the channel name. created by generateHash(chIndex)
* Used as a hint to limit which PSKs are considered for packet decoding.
*/
typedef uint8_t ChannelHash;
/** The container/on device API for working with channels */
class Channels
{
/// The index of the primary channel
ChannelIndex primaryIndex = 0;
/** The channel index that was requested for sending/receving. Note: if this channel is a secondary
channel and does not have a PSK, we will use the PSK from the primary channel. If this channel is disabled
no sending or receiving will be allowed */
ChannelIndex activeChannelIndex = 0;
/// the precomputed hashes for each of our channels, or -1 for invalid
int16_t hashes[MAX_NUM_CHANNELS];
public:
/// Well known channel names
static const char *adminChannel, *gpioChannel, *serialChannel;
const ChannelSettings &getPrimary() { return getByIndex(getPrimaryIndex()).settings; }
/** Return the Channel for a specified index */
Channel &getByIndex(ChannelIndex chIndex);
/** Using the index inside the channel, update the specified channel's settings and role. If this channel is being promoted
* to be primary, force all other channels to be secondary.
*/
void setChannel(const Channel &c);
const char *getName(size_t chIndex);
/** The index of the primary channel */
ChannelIndex getPrimaryIndex() const { return primaryIndex; }
ChannelIndex getNumChannels() { return channelFile.channels_count; }
/**
* Generate a short suffix used to disambiguate channels that might have the same "name" entered by the human but different
PSKs.
* The ideas is that the PSK changing should be visible to the user so that they see they probably messed up and that's why
they their nodes
* aren't talking to each other.
*
* This string is of the form "#name-X".
*
* Where X is either:
* (for custom PSKS) a letter from A to Z (base26), and formed by xoring all the bytes of the PSK together,
* OR (for the standard minimially secure PSKs) a number from 0 to 9.
*
* This function will also need to be implemented in GUI apps that talk to the radio.
*
* https://github.com/meshtastic/Meshtastic-device/issues/269
*/
const char *getPrimaryName();
/// Called by NodeDB on initial boot when the radio config settings are unset. Set a default single channel config.
void initDefaults();
/// called when the user has just changed our radio config and we might need to change channel keys
void onConfigChanged();
/** Given a channel hash setup crypto for decoding that channel (or the primary channel if that channel is unsecured)
*
* This method is called before decoding inbound packets
*
* @return false if the channel hash or channel is invalid
*/
bool decryptForHash(ChannelIndex chIndex, ChannelHash channelHash);
/** Given a channel index setup crypto for encoding that channel (or the primary channel if that channel is unsecured)
*
* This method is called before encoding outbound packets
*
* @eturn the (0 to 255) hash for that channel - if no suitable channel could be found, return -1
*/
int16_t setActiveByIndex(ChannelIndex channelIndex);
private:
/** Given a channel index, change to use the crypto key specified by that index
*
* @eturn the (0 to 255) hash for that channel - if no suitable channel could be found, return -1
*/
int16_t setCrypto(ChannelIndex chIndex);
/** Return the channel index for the specified channel hash, or -1 for not found */
int8_t getIndexByHash(ChannelHash channelHash);
/** Given a channel number, return the (0 to 255) hash for that channel
* If no suitable channel could be found, return -1
*
* called by fixupChannel when a new channel is set
*/
int16_t generateHash(ChannelIndex channelNum);
int16_t getHash(ChannelIndex i) { return hashes[i]; }
/**
* Validate a channel, fixing any errors as needed
*/
Channel &fixupChannel(ChannelIndex chIndex);
/**
* Write a default channel to the specified channel index
*/
void initDefaultChannel(ChannelIndex chIndex);
/**
* Return the key used for encrypting this channel (if channel is secondary and no key provided, use the primary channel's
* PSK)
*/
CryptoKey getKey(ChannelIndex chIndex);
};
/// Singleton channel table
extern Channels channels;

View File

@@ -1,9 +1,14 @@
#include "CryptoEngine.h"
#include "configuration.h"
void CryptoEngine::setKey(size_t numBytes, uint8_t *bytes)
void CryptoEngine::setKey(const CryptoKey &k)
{
DEBUG_MSG("WARNING: Using stub crypto - all crypto is sent in plaintext!\n");
DEBUG_MSG("Installing AES%d key!\n", k.length * 8);
/* for(uint8_t i = 0; i < k.length; i++)
DEBUG_MSG("%02x ", k.bytes[i]);
DEBUG_MSG("\n"); */
key = k;
}
/**

View File

@@ -2,6 +2,13 @@
#include <Arduino.h>
struct CryptoKey {
uint8_t bytes[32];
/// # of bytes, or -1 to mean "invalid key - do not use"
int8_t length;
};
/**
* see docs/software/crypto.md for details.
*
@@ -15,6 +22,8 @@ class CryptoEngine
/** Our per packet nonce */
uint8_t nonce[16];
CryptoKey key;
public:
virtual ~CryptoEngine() {}
@@ -27,7 +36,7 @@ class CryptoEngine
* @param bytes a _static_ buffer that will remain valid for the life of this crypto instance (i.e. this class will cache the
* provided pointer)
*/
virtual void setKey(size_t numBytes, uint8_t *bytes);
virtual void setKey(const CryptoKey &k);
/**
* Encrypt a packet

View File

@@ -62,57 +62,59 @@ ErrorCode DSRRouter::send(MeshPacket *p)
return ReliableRouter::send(p);
}
void DSRRouter::sniffReceived(const MeshPacket *p)
void DSRRouter::sniffReceived(const MeshPacket *p, const Routing *c)
{
// Learn 0 hop routes by just hearing any adjacent nodes
// But treat broadcasts carefully, because when flood broadcasts go out they keep the same original "from". So we want to
// ignore rebroadcasts.
// this will also add records for any ACKs we receive for our messages
if (p->to != NODENUM_BROADCAST || p->hop_limit != HOP_RELIABLE) {
addRoute(p->from, p->from, 0); // We are adjacent with zero hops
addRoute(getFrom(p), getFrom(p), 0); // We are adjacent with zero hops
}
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
if (weAreInRoute(p->decoded.route_request)) {
DEBUG_MSG("Ignoring a route request that contains us\n");
} else {
updateRoutes(p->decoded.route_request,
true); // Update our routing tables based on the route that came in so far on this request
if (p->decoded.dest == getNodeNum()) {
// They were looking for us, send back a route reply (the sender address will be first in the list)
sendRouteReply(p->decoded.route_request);
if (c)
switch (c->which_variant) {
case Routing_route_request_tag:
// Handle route discovery packets (will be a broadcast message)
// FIXME - always start request with the senders nodenum
if (weAreInRoute(c->route_request)) {
DEBUG_MSG("Ignoring a route request that contains us\n");
} else {
// They were looking for someone else, forward it along (as a zero hop broadcast)
NodeNum nextHop = getNextHop(p->decoded.dest);
if (nextHop) {
// in our route cache, reply to the requester (the sender address will be first in the list)
sendRouteReply(p->decoded.route_request, nextHop);
updateRoutes(c->route_request,
true); // Update our routing tables based on the route that came in so far on this request
if (p->decoded.dest == getNodeNum()) {
// They were looking for us, send back a route reply (the sender address will be first in the list)
sendRouteReply(c->route_request);
} else {
// Not in our route cache, rebroadcast on their behalf (after adding ourselves to the request route)
resendRouteRequest(p);
// They were looking for someone else, forward it along (as a zero hop broadcast)
NodeNum nextHop = getNextHop(p->decoded.dest);
if (nextHop) {
// in our route cache, reply to the requester (the sender address will be first in the list)
sendRouteReply(c->route_request, nextHop);
} else {
// Not in our route cache, rebroadcast on their behalf (after adding ourselves to the request route)
resendRouteRequest(p);
}
}
}
break;
case Routing_route_reply_tag:
updateRoutes(c->route_reply, false);
// FIXME, if any of our current pending packets were waiting for this route, send them (and leave them as regular
// pending 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 Routing_error_reason_tag:
removeRoute(p->decoded.dest);
// FIXME: if any pending packets were waiting on this route, delete them
break;
default:
break;
}
break;
case SubPacket_route_reply_tag:
updateRoutes(p->decoded.route_reply, false);
// FIXME, if any of our current pending packets were waiting for this route, send them (and leave them as regular pending
// 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_error_reason_tag:
removeRoute(p->decoded.dest);
// FIXME: if any pending packets were waiting on this route, delete them
break;
default:
break;
}
// We simply ignore ACKs - because ReliableRouter will delete the pending packet for us
@@ -131,26 +133,29 @@ 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, ErrorReason_NO_ROUTE);
sendRouteError(p, Routing_Error_NO_ROUTE);
}
// FIXME, stop local processing of this packet
}
// 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_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
if (c) {
// handle naks - convert them to route error packets
// All naks are generated locally, because we failed resending the packet too many times
PacketId nakId = c->error_reason ? p->decoded.request_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, ErrorReason_GOT_NAK);
sendRouteError(p, Routing_Error_GOT_NAK);
}
}
}
}
return ReliableRouter::sniffReceived(p);
ReliableRouter::sniffReceived(p, c);
}
/**
@@ -230,7 +235,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, ErrorReason err)
void DSRRouter::sendRouteError(const MeshPacket *p, Routing_Error err)
{
DEBUG_MSG("FIXME not implemented sendRouteError\n");
}

View File

@@ -8,7 +8,7 @@ class DSRRouter : public ReliableRouter
* 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)
*/
virtual void sniffReceived(const MeshPacket *p);
virtual void sniffReceived(const MeshPacket *p, const Routing *c);
/**
* Send a packet on a suitable interface. This routine will
@@ -70,7 +70,7 @@ class DSRRouter : public ReliableRouter
/**
* Send a route error packet towards whoever originally sent this message
*/
void sendRouteError(const MeshPacket *p, ErrorReason err);
void sendRouteError(const MeshPacket *p, Routing_Error 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

View File

@@ -27,11 +27,11 @@ bool FloodingRouter::shouldFilterReceived(const MeshPacket *p)
return Router::shouldFilterReceived(p);
}
void FloodingRouter::sniffReceived(const MeshPacket *p)
void FloodingRouter::sniffReceived(const MeshPacket *p, const Routing *c)
{
// If a broadcast, possibly _also_ send copies out into the mesh.
// (FIXME, do something smarter than naive flooding here)
if (p->to == NODENUM_BROADCAST && p->hop_limit > 0) {
if (p->to == NODENUM_BROADCAST && p->hop_limit > 0 && getFrom(p) != getNodeNum()) {
if (p->id != 0) {
MeshPacket *tosend = packetPool.allocCopy(*p); // keep a copy because we will be sending it
@@ -48,5 +48,5 @@ void FloodingRouter::sniffReceived(const MeshPacket *p)
}
// handle the packet as normal
Router::sniffReceived(p);
Router::sniffReceived(p, c);
}

View File

@@ -55,5 +55,5 @@ class FloodingRouter : public Router, protected PacketHistory
/**
* Look for broadcasts we need to rebroadcast
*/
virtual void sniffReceived(const MeshPacket *p);
virtual void sniffReceived(const MeshPacket *p, const Routing *c);
};

View File

@@ -101,25 +101,30 @@ template <class T> class MemoryPool : public Allocator<T>
/// Return a buffer for use by others
virtual void release(T *p)
{
assert(dead.enqueue(p, 0));
assert(p >= buf &&
(size_t)(p - buf) <
maxElements); // sanity check to make sure a programmer didn't free something that didn't come from this pool
assert(dead.enqueue(p, 0));
}
#ifdef HAS_FREE_RTOS
/// Return a buffer from an ISR, if higherPriWoken is set to true you have some work to do ;-)
void releaseFromISR(T *p, BaseType_t *higherPriWoken)
{
assert(dead.enqueueFromISR(p, higherPriWoken));
assert(p >= buf &&
(size_t)(p - buf) <
maxElements); // sanity check to make sure a programmer didn't free something that didn't come from this pool
assert(dead.enqueueFromISR(p, higherPriWoken));
}
#endif
protected:
/// Return a queable object which has been prefilled with zeros - allow timeout to wait for available buffers (you
/// probably don't want this version).
virtual T *alloc(TickType_t maxWait) { return dead.dequeuePtr(maxWait); }
virtual T *alloc(TickType_t maxWait)
{
T *p = dead.dequeuePtr(maxWait);
assert(p);
return p;
}
};

View File

@@ -33,7 +33,7 @@ void fixPriority(MeshPacket *p)
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->priority = (p->decoded.portnum == PortNum_ROUTING_APP) ? MeshPacket_Priority_ACK :
(p->want_ack ? MeshPacket_Priority_RELIABLE : MeshPacket_Priority_DEFAULT);
}
}
@@ -71,7 +71,7 @@ static PacketId findId;
static bool isMyPacket(MeshPacket *p)
{
return p->id == findId && p->from == findFrom;
return p->id == findId && getFrom(p) == findFrom;
}
/** Attempt to find and remove a packet from this queue. Returns true the packet which was removed from the queue */

View File

@@ -1,78 +1,195 @@
#include "MeshPlugin.h"
#include "NodeDB.h"
#include "Channels.h"
#include "MeshService.h"
#include "NodeDB.h"
#include "plugins/RoutingPlugin.h"
#include <assert.h>
std::vector<MeshPlugin *> *MeshPlugin::plugins;
const MeshPacket *MeshPlugin::currentRequest;
/**
* If any of the current chain of plugins has already sent a reply, it will be here. This is useful to allow
* the RoutingPlugin to avoid sending redundant acks
*/
MeshPacket *MeshPlugin::currentReply;
MeshPlugin::MeshPlugin(const char *_name) : name(_name)
{
// Can't trust static initalizer order, so we check each time
if(!plugins)
if (!plugins)
plugins = new std::vector<MeshPlugin *>();
plugins->push_back(this);
}
void MeshPlugin::setup() {
}
void MeshPlugin::setup() {}
MeshPlugin::~MeshPlugin()
{
assert(0); // FIXME - remove from list of plugins once someone needs this feature
}
MeshPacket *MeshPlugin::allocAckNak(Routing_Error err, NodeNum to, PacketId idFrom, ChannelIndex chIndex)
{
Routing c = Routing_init_default;
c.error_reason = err;
// Now that we have moded sendAckNak up one level into the class heirarchy we can no longer assume we are a RoutingPlugin
// So we manually call pb_encode_to_bytes and specify routing port number
// auto p = allocDataProtobuf(c);
MeshPacket *p = router->allocForSending();
p->decoded.portnum = PortNum_ROUTING_APP;
p->decoded.payload.size = pb_encode_to_bytes(p->decoded.payload.bytes, sizeof(p->decoded.payload.bytes), Routing_fields, &c);
p->priority = MeshPacket_Priority_ACK;
p->hop_limit = 0; // Assume just immediate neighbors for now
p->to = to;
p->decoded.request_id = idFrom;
p->channel = chIndex;
DEBUG_MSG("Alloc an err=%d,to=0x%x,idFrom=0x%x,id=0x%x\n", err, to, idFrom, p->id);
return p;
}
MeshPacket *MeshPlugin::allocErrorResponse(Routing_Error err, const MeshPacket *p)
{
auto r = allocAckNak(err, getFrom(p), p->id, p->channel);
setReplyTo(r, *p);
return r;
}
void MeshPlugin::callPlugins(const MeshPacket &mp)
{
// DEBUG_MSG("In call plugins\n");
bool pluginFound = false;
assert(mp.which_payloadVariant == MeshPacket_decoded_tag); // I think we are guarnteed the packet is decoded by this point?
currentReply = NULL; // No reply yet
// Was this message directed to us specifically? Will be false if we are sniffing someone elses packets
auto ourNodeNum = nodeDB.getNodeNum();
bool toUs = mp.to == NODENUM_BROADCAST || mp.to == ourNodeNum;
for (auto i = plugins->begin(); i != plugins->end(); ++i) {
auto &pi = **i;
pi.currentRequest = &mp;
if (pi.wantPortnum(mp.decoded.data.portnum)) {
/// received channel
auto ch = channels.getByIndex(mp.channel);
assert(ch.has_settings);
/// We only call plugins that are interested in the packet (and the message is destined to us or we are promiscious)
bool wantsPacket = (pi.isPromiscuous || toUs) && pi.wantPacket(&mp);
if (wantsPacket) {
// DEBUG_MSG("Plugin %s wantsPacket=%d\n", pi.name, wantsPacket);
pluginFound = true;
bool handled = pi.handleReceived(mp);
/// Is the channel this packet arrived on acceptable? (security check)
bool rxChannelOk = !pi.boundChannel || (mp.from == 0) || (strcmp(ch.settings.name, pi.boundChannel) == 0);
// Possibly send replies
if (mp.decoded.want_response)
pi.sendResponse(mp);
if (!rxChannelOk) {
// no one should have already replied!
assert(!currentReply);
DEBUG_MSG("Plugin %s handled=%d\n", pi.name, handled);
if (handled)
break;
if (mp.decoded.want_response) {
DEBUG_MSG("packet on wrong channel, returning error\n");
currentReply = pi.allocErrorResponse(Routing_Error_NOT_AUTHORIZED, &mp);
} else
DEBUG_MSG("packet on wrong channel, but client didn't want response\n");
} else {
bool handled = pi.handleReceived(mp);
// Possibly send replies (but only if the message was directed to us specifically, i.e. not for promiscious
// sniffing) also: we only let the one plugin send a reply, once that happens, remaining plugins are not
// considered
// NOTE: we send a reply *even if the (non broadcast) request was from us* which is unfortunate but necessary
// because currently when the phone sends things, it sends things using the local node ID as the from address. A
// better solution (FIXME) would be to let phones have their own distinct addresses and we 'route' to them like
// any other node.
if (mp.decoded.want_response && toUs && (getFrom(&mp) != ourNodeNum || mp.to == ourNodeNum) && !currentReply) {
pi.sendResponse(mp);
DEBUG_MSG("Plugin %s sent a response\n", pi.name);
} else {
DEBUG_MSG("Plugin %s considered\n", pi.name);
}
if (handled) {
DEBUG_MSG("Plugin %s handled and skipped other processing\n", pi.name);
break;
}
}
}
pi.currentRequest = NULL;
}
if(!pluginFound)
DEBUG_MSG("No plugins interested in portnum=%d\n", mp.decoded.data.portnum);
if (mp.decoded.want_response && toUs) {
if (currentReply) {
DEBUG_MSG("Sending response\n");
service.sendToMesh(currentReply);
currentReply = NULL;
} else {
// No one wanted to reply to this requst, tell the requster that happened
DEBUG_MSG("No one responded, send a nak\n");
routingPlugin->sendAckNak(Routing_Error_NO_RESPONSE, getFrom(&mp), mp.id, mp.channel);
}
}
if (!pluginFound)
DEBUG_MSG("No plugins interested in portnum=%d\n", mp.decoded.portnum);
}
/** 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. Implementing this method
* is optional
*/
void MeshPlugin::sendResponse(const MeshPacket &req) {
void MeshPlugin::sendResponse(const MeshPacket &req)
{
auto r = allocReply();
if(r) {
DEBUG_MSG("Sending response\n");
if (r) {
setReplyTo(r, req);
service.sendToMesh(r);
}
else {
DEBUG_MSG("WARNING: Client requested response but this plugin did not provide\n");
currentReply = r;
} else {
// Ignore - this is now expected behavior for routing plugin (because it ignores some replies)
// DEBUG_MSG("WARNING: Client requested response but this plugin did not provide\n");
}
}
/** set the destination and packet parameters of packet p intended as a reply to a particular "to" packet
/** set the destination and packet parameters of packet p intended as a reply to a particular "to" packet
* This ensures that if the request packet was sent reliably, the reply is sent that way as well.
*/
void setReplyTo(MeshPacket *p, const MeshPacket &to) {
p->to = to.from;
p->want_ack = to.want_ack;
}
*/
void setReplyTo(MeshPacket *p, const MeshPacket &to)
{
assert(p->which_payloadVariant == MeshPacket_decoded_tag); // Should already be set by now
p->to = getFrom(&to);
p->channel = to.channel; // Use the same channel that the request came in on
// No need for an ack if we are just delivering locally (it just generates an ignored ack)
p->want_ack = (to.from != 0) ? to.want_ack : false;
if (p->priority == MeshPacket_Priority_UNSET)
p->priority = MeshPacket_Priority_RELIABLE;
p->decoded.request_id = to.id;
}
std::vector<MeshPlugin *> MeshPlugin::GetMeshPluginsWithUIFrames()
{
std::vector<MeshPlugin *> pluginsWithUIFrames;
for (auto i = plugins->begin(); i != plugins->end(); ++i) {
auto &pi = **i;
if (pi.wantUIFrame()) {
DEBUG_MSG("Plugin wants a UI Frame\n");
pluginsWithUIFrames.push_back(&pi);
}
}
return pluginsWithUIFrames;
}

View File

@@ -1,7 +1,14 @@
#pragma once
#include "mesh/Channels.h"
#include "mesh/MeshTypes.h"
#include <vector>
#ifndef NO_SCREEN
#include <OLEDDisplay.h>
#include <OLEDDisplayUi.h>
#endif
/** A baseclass for any mesh "plugin".
*
* A plugin allows you to add new features to meshtastic device code, without needing to know messaging details.
@@ -28,13 +35,31 @@ class MeshPlugin
*/
static void callPlugins(const MeshPacket &mp);
static std::vector<MeshPlugin *> GetMeshPluginsWithUIFrames();
#ifndef NO_SCREEN
virtual void drawFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y) { return; }
#endif
protected:
const char *name;
/* Most plugins only care about packets that are destined for their node (i.e. broadcasts or has their node as the specific
recipient) But some plugs might want to 'sniff' packets that are merely being routed (passing through the current node). Those
plugins can set this to true and their handleReceived() will be called for every packet.
*/
bool isPromiscuous = false;
/** If a bound channel name is set, we will only accept received packets that come in on that channel.
* A special exception (FIXME, not sure if this is a good idea) - packets that arrive on the local interface
* are allowed on any channel (this lets the local user do anything).
*
* We will send responses on the same channel that the request arrived on.
*/
const char *boundChannel = NULL;
/**
* If this plugin is currently handling a request currentRequest will be preset
* to the packet with the request. This is mostly useful for reply handlers.
*
*
* Note: this can be static because we are guaranteed to be processing only one
* plugin at a time.
*/
@@ -49,7 +74,7 @@ class MeshPlugin
/**
* @return true if you want to receive the specified portnum
*/
virtual bool wantPortnum(PortNum p) = 0;
virtual bool wantPacket(const MeshPacket *p) = 0;
/** Called to handle a particular incoming message
@@ -61,7 +86,23 @@ class MeshPlugin
* so that subclasses can (optionally) send a response back to the original sender. */
virtual MeshPacket *allocReply() { return NULL; }
/***
* @return true if you want to be alloced a UI screen frame
*/
virtual bool wantUIFrame() { return false; }
MeshPacket *allocAckNak(Routing_Error err, NodeNum to, PacketId idFrom, ChannelIndex chIndex);
/// Send an error response for the specified packet.
MeshPacket *allocErrorResponse(Routing_Error err, const MeshPacket *p);
private:
/**
* If any of the current chain of plugins has already sent a reply, it will be here. This is useful to allow
* the RoutingPlugin to avoid sending redundant acks
*/
static MeshPacket *currentReply;
friend class ReliableRouter;
/** 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. This method calls allocReply()
@@ -70,7 +111,7 @@ class MeshPlugin
void sendResponse(const MeshPacket &req);
};
/** set the destination and packet parameters of packet p intended as a reply to a particular "to" packet
/** set the destination and packet parameters of packet p intended as a reply to a particular "to" packet
* This ensures that if the request packet was sent reliably, the reply is sent that way as well.
*/
*/
void setReplyTo(MeshPacket *p, const MeshPacket &to);

View File

@@ -13,8 +13,8 @@
#include "RTC.h"
#include "main.h"
#include "mesh-pb-constants.h"
#include "plugins/PositionPlugin.h"
#include "plugins/NodeInfoPlugin.h"
#include "plugins/PositionPlugin.h"
#include "power.h"
/*
@@ -51,23 +51,6 @@ 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);
assert(nodeInfoPlugin);
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)
{
// assert(MAX_RX_TOPHONE == 32); // FIXME, delete this, just checking my clever macro
@@ -75,18 +58,13 @@ 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();
if (gps)
gpsObserver.observe(&gps->newStatus);
packetReceivedObserver.observe(&router->notifyPacketReceived);
}
int MeshService::handleFromRadio(const MeshPacket *mp)
{
powerFSM.trigger(EVENT_RECEIVED_PACKET); // Possibly keep the node from sleeping
@@ -126,7 +104,7 @@ bool MeshService::reloadConfig()
// This will also update the region as needed
bool didReset = nodeDB.resetRadioConfig(); // Don't let the phone send us fatally bad settings
configChanged.notifyObservers(NULL);
configChanged.notifyObservers(NULL); // This will cause radio hardware to change freqs etc
nodeDB.saveToDisk();
return didReset;
@@ -136,7 +114,8 @@ bool MeshService::reloadConfig()
void MeshService::reloadOwner()
{
assert(nodeInfoPlugin);
nodeInfoPlugin->sendOurNodeInfo();
if(nodeInfoPlugin)
nodeInfoPlugin->sendOurNodeInfo();
nodeDB.saveToDisk();
}
@@ -147,8 +126,12 @@ void MeshService::reloadOwner()
*/
void MeshService::handleToRadio(MeshPacket &p)
{
if (p.from == 0) // If the phone didn't set a sending node ID, use ours
p.from = nodeDB.getNodeNum();
if (p.from != 0) { // We don't let phones assign nodenums to their sent messages
DEBUG_MSG("Warning: phone tried to pick a nodenum, we don't allow that.\n");
p.from = 0;
} else {
// p.from = nodeDB.getNodeNum();
}
if (p.id == 0)
p.id = generatePacketId(); // If the phone didn't supply one, then pick one
@@ -170,7 +153,8 @@ 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) {
bool MeshService::cancelSending(PacketId id)
{
return router->cancelSending(nodeDB.getNodeNum(), id);
}
@@ -178,18 +162,6 @@ 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_payloadVariant == MeshPacket_decoded_tag && p->decoded.which_payloadVariant == 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);
}
@@ -199,29 +171,36 @@ void MeshService::sendNetworkPing(NodeNum dest, bool wantReplies)
NodeInfo *node = nodeDB.getNode(nodeDB.getNodeNum());
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);
else
nodeInfoPlugin->sendOurNodeInfo(dest, wantReplies);
if (node->has_position) {
if(positionPlugin) {
DEBUG_MSG("Sending position ping to 0x%x, wantReplies=%d\n", dest, wantReplies);
positionPlugin->sendOurPosition(dest, wantReplies);
}
}
else {
if(nodeInfoPlugin) {
DEBUG_MSG("Sending nodeinfo ping to 0x%x, wantReplies=%d\n", dest, wantReplies);
nodeInfoPlugin->sendOurNodeInfo(dest, wantReplies);
}
}
}
NodeInfo *MeshService::refreshMyNodeInfo() {
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) {
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(RTCQualityGPS); // This nodedb timestamp might be stale, so update it if our clock is valid.
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);
@@ -240,11 +219,10 @@ int MeshService::onGPSChanged(const meshtastic::GPSStatus *unused)
pos.altitude = gps->altitude;
pos.latitude_i = gps->latitude;
pos.longitude_i = gps->longitude;
}
else {
} else {
// The GPS has lost lock, if we are fixed position we should just keep using
// the old position
if(!radioConfig.preferences.fixed_position) {
if (radioConfig.preferences.fixed_position) {
DEBUG_MSG("WARNING: Using fixed position\n");
} else {
// throw away old position
@@ -254,27 +232,10 @@ int MeshService::onGPSChanged(const meshtastic::GPSStatus *unused)
}
}
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);
assert(positionPlugin);
positionPlugin->sendOurPosition(NODENUM_BROADCAST, requestReplies);
}
return 0;
}

View File

@@ -19,8 +19,6 @@ class MeshService
{
CallbackObserver<MeshService, const meshtastic::GPSStatus *> gpsObserver =
CallbackObserver<MeshService, const meshtastic::GPSStatus *>(this, &MeshService::onGPSChanged);
CallbackObserver<MeshService, const MeshPacket *> packetReceivedObserver =
CallbackObserver<MeshService, const MeshPacket *>(this, &MeshService::handleFromRadio);
/// received packets waiting for the phone to process them
/// FIXME, change to a DropOldestQueue and keep a count of the number of dropped packets to ensure
@@ -91,9 +89,10 @@ class MeshService
/// returns 0 to allow futher processing
int onGPSChanged(const meshtastic::GPSStatus *arg);
/// Handle a packet that just arrived from the radio. This method does _not_ free the provided packet. If it needs
/// Handle a packet that just arrived from the radio. This method does _ReliableRouternot_ free the provided packet. If it needs
/// to keep the packet around it makes a copy
int handleFromRadio(const MeshPacket *p);
friend class RoutingPlugin;
};
extern MeshService service;

View File

@@ -13,6 +13,9 @@ typedef uint32_t PacketId; // A packet sequence number
#define ERRNO_OK 0
#define ERRNO_NO_INTERFACES 33
#define ERRNO_UNKNOWN 32 // pick something that doesn't conflict with RH_ROUTER_ERROR_UNABLE_TO_DELIVER
#define ERRNO_DISABLED 34 // the itnerface is disabled
#define ERRNO_TOO_LARGE 35
#define ERRNO_NO_CHANNEL 36
/**
* the max number of hops a message can pass through, used as the default max for hop_limit in MeshPacket.
@@ -29,4 +32,10 @@ typedef uint32_t PacketId; // A packet sequence number
typedef int ErrorCode;
/// Alloc and free packets to our global, ISR safe pool
extern Allocator<MeshPacket> &packetPool;
extern Allocator<MeshPacket> &packetPool;
/**
* Most (but not always) of the time we want to treat packets 'from' the local phone (where from == 0), as if they originated on the local node.
* If from is zero this function returns our node number instead
*/
NodeNum getFrom(const MeshPacket *p);

View File

@@ -4,10 +4,10 @@
#include "FS.h"
#include "Channels.h"
#include "CryptoEngine.h"
#include "FSCommon.h"
#include "GPS.h"
#include "main.h"
#include "MeshRadio.h"
#include "NodeDB.h"
#include "PacketHistory.h"
@@ -16,6 +16,7 @@
#include "Router.h"
#include "configuration.h"
#include "error.h"
#include "main.h"
#include "mesh-pb-constants.h"
#include <pb_decode.h>
#include <pb_encode.h>
@@ -29,8 +30,8 @@ NodeDB nodeDB;
// we have plenty of ram so statically alloc this tempbuf (for now)
EXT_RAM_ATTR DeviceState devicestate;
MyNodeInfo &myNodeInfo = devicestate.my_node;
RadioConfig &radioConfig = devicestate.radio;
ChannelSettings &channelSettings = radioConfig.channel_settings;
RadioConfig radioConfig;
ChannelFile channelFile;
/** 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.
@@ -64,155 +65,37 @@ static uint8_t ourMacAddr[6];
*/
NodeNum displayedNodeNum;
/// A usable (but bigger) version of the channel name in the channelSettings object
const char *channelName;
/// A usable psk - which has been constructed based on the (possibly short psk) in channelSettings
static uint8_t activePSK[32];
static uint8_t activePSKSize;
NodeDB::NodeDB() : nodes(devicestate.node_db), numNodes(&devicestate.node_db_count) {}
/**
* Generate a short suffix used to disambiguate channels that might have the same "name" entered by the human but different PSKs.
* The ideas is that the PSK changing should be visible to the user so that they see they probably messed up and that's why they
their nodes
* aren't talking to each other.
*
* This string is of the form "#name-X".
*
* Where X is either:
* (for custom PSKS) a letter from A to Z (base26), and formed by xoring all the bytes of the PSK together,
* OR (for the standard minimially secure PSKs) a number from 0 to 9.
*
* This function will also need to be implemented in GUI apps that talk to the radio.
*
* https://github.com/meshtastic/Meshtastic-device/issues/269
* Most (but not always) of the time we want to treat packets 'from' the local phone (where from == 0), as if they originated on
* the local node. If from is zero this function returns our node number instead
*/
const char *getChannelName()
NodeNum getFrom(const MeshPacket *p)
{
static char buf[32];
char suffix;
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++)
code ^= activePSK[i];
suffix = 'A' + (code % 26);
} else {
suffix = '0' + channelSettings.psk.bytes[0];
}
snprintf(buf, sizeof(buf), "#%s-%c", channelName, suffix);
return buf;
return (p->from == 0) ? nodeDB.getNodeNum() : p->from;
}
NodeDB::NodeDB() : nodes(devicestate.node_db), numNodes(&devicestate.node_db_count) {}
bool NodeDB::resetRadioConfig()
{
bool didFactoryReset = false;
radioGeneration++;
/// 16 bytes of random PSK for our _public_ default channel that all devices power up on (AES128)
static const uint8_t defaultpsk[] = {0xd4, 0xf1, 0xbb, 0x3a, 0x20, 0x29, 0x07, 0x59,
0xf0, 0xbc, 0xff, 0xab, 0xcf, 0x4e, 0x69, 0xbf};
radioConfig.has_preferences = true;
if (radioConfig.preferences.factory_reset) {
DEBUG_MSG("Performing factory reset!\n");
installDefaultDeviceState();
didFactoryReset = true;
} else if (!channelSettings.psk.size) {
DEBUG_MSG("Setting default preferences!\n");
radioConfig.has_channel_settings = true;
radioConfig.has_preferences = true;
// radioConfig.modem_config = RadioConfig_ModemConfig_Bw125Cr45Sf128; // medium range and fast
// channelSettings.modem_config = ChannelSettings_ModemConfig_Bw500Cr45Sf128; // short range and fast, but wide
// bandwidth so incompatible radios can talk together
channelSettings.modem_config = ChannelSettings_ModemConfig_Bw125Cr48Sf4096; // slow and long range
channelSettings.tx_power = 0; // default
uint8_t defaultpskIndex = 1;
channelSettings.psk.bytes[0] = defaultpskIndex;
channelSettings.psk.size = 1;
strcpy(channelSettings.name, "");
}
// Convert the old string "Default" to our new short representation
if (strcmp(channelSettings.name, "Default") == 0)
*channelSettings.name = '\0';
if (channelFile.channels_count != MAX_NUM_CHANNELS) {
DEBUG_MSG("Setting default channel and radio preferences!\n");
// Convert the short "" representation for Default into a usable string
channelName = channelSettings.name;
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
if (channelSettings.bandwidth != 0)
channelName = "Unset";
else
switch (channelSettings.modem_config) {
case ChannelSettings_ModemConfig_Bw125Cr45Sf128:
channelName = "Medium";
break;
case ChannelSettings_ModemConfig_Bw500Cr45Sf128:
channelName = "ShortFast";
break;
case ChannelSettings_ModemConfig_Bw31_25Cr48Sf512:
channelName = "LongAlt";
break;
case ChannelSettings_ModemConfig_Bw125Cr48Sf4096:
channelName = "LongSlow";
break;
default:
channelName = "Invalid";
break;
}
channels.initDefaults();
}
// 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) {
*channelSettings.psk.bytes = 1;
channelSettings.psk.size = 1;
}
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 == 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)
activePSKSize = 0; // Turn off encryption
else {
memcpy(activePSK, defaultpsk, sizeof(defaultpsk));
activePSKSize = sizeof(defaultpsk);
// Bump up the last byte of PSK as needed
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
crypto->setKey(activePSKSize, activePSK);
channels.onConfigChanged();
// temp hack for quicker testing
// devicestate.no_save = true;
@@ -237,6 +120,18 @@ bool NodeDB::resetRadioConfig()
return didFactoryReset;
}
void NodeDB::installDefaultRadioConfig()
{
memset(&radioConfig, 0, sizeof(radioConfig));
radioConfig.has_preferences = true;
resetRadioConfig();
}
void NodeDB::installDefaultChannels()
{
memset(&channelFile, 0, sizeof(channelFile));
}
void NodeDB::installDefaultDeviceState()
{
// We try to preserve the region setting because it will really bum users out if we discard it
@@ -249,15 +144,11 @@ void NodeDB::installDefaultDeviceState()
// init our devicestate with valid flags so protobuf writing/reading will work
devicestate.has_my_node = true;
devicestate.has_radio = true;
devicestate.has_owner = true;
devicestate.radio.has_channel_settings = true;
devicestate.radio.has_preferences = true;
devicestate.node_db_count = 0;
devicestate.version = DEVICESTATE_CUR_VER;
devicestate.receive_queue_count = 0; // Not yet implemented FIXME
resetRadioConfig();
// default to no GPS, until one has been found by probing
myNodeInfo.has_gps = false;
myNodeInfo.message_timeout_msec = FLOOD_EXPIRE_TIME;
@@ -277,8 +168,11 @@ void NodeDB::installDefaultDeviceState()
// Restore region if possible
if (oldRegionCode != RegionCode_Unset)
radioConfig.preferences.region = oldRegionCode;
if (oldRegion.length())
if (oldRegion.length()) // If the old style region was set, try to keep it up-to-date
strcpy(myNodeInfo.region, oldRegion.c_str());
installDefaultChannels();
installDefaultRadioConfig();
}
void NodeDB::init()
@@ -289,33 +183,38 @@ void NodeDB::init()
loadFromDisk();
// saveToDisk();
// We set node_num and packet_id _after_ loading from disk, because we always want to use the values this
// rom was compiled for, not what happens to be in the save file.
myNodeInfo.node_num_bits = sizeof(NodeNum) * 8;
myNodeInfo.packet_id_bits = sizeof(PacketId) * 8;
myNodeInfo.max_channels = MAX_NUM_CHANNELS; // tell others the max # of channels we can understand
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
myNodeInfo.min_app_version = 20200; // 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();
// Set our board type so we can share it with others
owner.hw_model = HW_VENDOR;
// Include our owner in the node db under our nodenum
NodeInfo *info = getOrCreateNode(getNodeNum());
info->user = owner;
info->has_user = true;
// removed from 1.2 (though we do use old values if found)
// We set these _after_ loading from disk - because they come from the build and are more trusted than
// what is stored in flash
if (xstr(HW_VERSION)[0])
strncpy(myNodeInfo.region, optstr(HW_VERSION), sizeof(myNodeInfo.region));
else
DEBUG_MSG("This build does not specify a HW_VERSION\n"); // Eventually new builds will no longer include this build flag
// if (xstr(HW_VERSION)[0])
// strncpy(myNodeInfo.region, optstr(HW_VERSION), sizeof(myNodeInfo.region));
// else DEBUG_MSG("This build does not specify a HW_VERSION\n"); // Eventually new builds will no longer include this build
// flag
// DEBUG_MSG("legacy region %d\n", devicestate.legacyRadio.preferences.region);
if (radioConfig.preferences.region == RegionCode_Unset)
radioConfig.preferences.region = devicestate.legacyRadio.preferences.region;
// Check for the old style of region code strings, if found, convert to the new enum.
// Those strings will look like "1.0-EU433"
@@ -329,12 +228,14 @@ void NodeDB::init()
}
strncpy(myNodeInfo.firmware_version, optstr(APP_VERSION), sizeof(myNodeInfo.firmware_version));
strncpy(myNodeInfo.hw_model, HW_VENDOR, sizeof(myNodeInfo.hw_model));
// hw_model is no longer stored in myNodeInfo (as of 1.2.11) - we now store it as an enum in nodeinfo
myNodeInfo.hw_model_deprecated[0] = '\0';
// strncpy(myNodeInfo.hw_model, HW_VENDOR, sizeof(myNodeInfo.hw_model));
resetRadioConfig(); // If bogus settings got saved, then fix them
DEBUG_MSG("legacy_region=%s, region=%d, NODENUM=0x%x, dbsize=%d\n", myNodeInfo.region, radioConfig.preferences.region,
myNodeInfo.my_node_num, *numNodes);
DEBUG_MSG("region=%d, NODENUM=0x%x, dbsize=%d\n", radioConfig.preferences.region, myNodeInfo.my_node_num, *numNodes);
}
// We reserve a few nodenums for future use
@@ -364,84 +265,134 @@ void NodeDB::pickNewNodeNum()
myNodeInfo.my_node_num = r;
}
const char *preffile = "/db.proto";
const char *preftmp = "/db.proto.tmp";
static const char *preffileOld = "/db.proto";
static const char *preffile = "/prefs/db.proto";
static const char *radiofile = "/prefs/radio.proto";
static const char *channelfile = "/prefs/channels.proto";
// const char *preftmp = "/db.proto.tmp";
void NodeDB::loadFromDisk()
/** Load a protobuf from a file, return true for success */
bool loadProto(const char *filename, size_t protoSize, size_t objSize, const pb_msgdesc_t *fields, void *dest_struct)
{
#ifdef FS
// static DeviceState scratch; We no longer read into a tempbuf because this structure is 15KB of valuable RAM
auto f = FS.open(preffile);
auto f = FS.open(filename);
// FIXME, temporary hack until every node in the universe is 1.2 or later - look for prefs in the old location (so we can
// preserve region)
if (!f && filename == preffile) {
filename = preffileOld;
f = FS.open(filename);
}
bool okay = false;
if (f) {
DEBUG_MSG("Loading saved preferences\n");
pb_istream_t stream = {&readcb, &f, DeviceState_size};
DEBUG_MSG("Loading %s\n", filename);
pb_istream_t stream = {&readcb, &f, protoSize};
// DEBUG_MSG("Preload channel name=%s\n", channelSettings.name);
memset(&devicestate, 0, sizeof(devicestate));
if (!pb_decode(&stream, DeviceState_fields, &devicestate)) {
memset(dest_struct, 0, objSize);
if (!pb_decode(&stream, fields, dest_struct)) {
DEBUG_MSG("Error: can't decode protobuf %s\n", PB_GET_ERROR(&stream));
installDefaultDeviceState(); // Our in RAM copy might now be corrupt
// FIXME - report failure to phone
} else {
if (devicestate.version < DEVICESTATE_MIN_VER) {
DEBUG_MSG("Warn: devicestate is old, discarding\n");
installDefaultDeviceState();
} else {
DEBUG_MSG("Loaded saved preferences version %d\n", devicestate.version);
}
// DEBUG_MSG("Postload channel name=%s\n", channelSettings.name);
okay = true;
}
f.close();
} else {
DEBUG_MSG("No saved preferences found\n");
DEBUG_MSG("No %s preferences found\n", filename);
}
#else
DEBUG_MSG("ERROR: Filesystem not implemented\n");
#endif
return okay;
}
void NodeDB::loadFromDisk()
{
// static DeviceState scratch; We no longer read into a tempbuf because this structure is 15KB of valuable RAM
if (!loadProto(preffile, DeviceState_size, sizeof(devicestate), DeviceState_fields, &devicestate)) {
installDefaultDeviceState(); // Our in RAM copy might now be corrupt
} else {
if (devicestate.version < DEVICESTATE_MIN_VER) {
DEBUG_MSG("Warn: devicestate %d is old, discarding\n", devicestate.version);
installDefaultDeviceState();
} else {
DEBUG_MSG("Loaded saved preferences version %d\n", devicestate.version);
}
}
if (!loadProto(radiofile, RadioConfig_size, sizeof(RadioConfig), RadioConfig_fields, &radioConfig)) {
installDefaultRadioConfig(); // Our in RAM copy might now be corrupt
}
if (!loadProto(channelfile, ChannelFile_size, sizeof(ChannelFile), ChannelFile_fields, &channelFile)) {
installDefaultChannels(); // Our in RAM copy might now be corrupt
}
}
/** Save a protobuf from a file, return true for success */
bool saveProto(const char *filename, size_t protoSize, size_t objSize, const pb_msgdesc_t *fields, const void *dest_struct)
{
#ifdef FS
// static DeviceState scratch; We no longer read into a tempbuf because this structure is 15KB of valuable RAM
String filenameTmp = filename;
filenameTmp += ".tmp";
auto f = FS.open(filenameTmp.c_str(), FILE_O_WRITE);
bool okay = false;
if (f) {
DEBUG_MSG("Saving %s\n", filename);
pb_ostream_t stream = {&writecb, &f, protoSize};
if (!pb_encode(&stream, fields, dest_struct)) {
DEBUG_MSG("Error: can't encode protobuf %s\n", PB_GET_ERROR(&stream));
} else {
okay = true;
}
f.close();
// brief window of risk here ;-)
if (!FS.remove(filename))
DEBUG_MSG("Warning: Can't remove old pref file\n");
if (!FS.rename(filenameTmp.c_str(), filename))
DEBUG_MSG("Error: can't rename new pref file\n");
} else {
DEBUG_MSG("Can't write prefs\n");
}
#else
DEBUG_MSG("ERROR: Filesystem not implemented\n");
#endif
return okay;
}
void NodeDB::saveChannelsToDisk()
{
if (!devicestate.no_save) {
#ifdef FS
FS.mkdir("/prefs");
#endif
saveProto(channelfile, ChannelFile_size, sizeof(ChannelFile), ChannelFile_fields, &channelFile);
}
}
void NodeDB::saveToDisk()
{
#ifdef FS
if (!devicestate.no_save) {
auto f = FS.open(preftmp, FILE_O_WRITE);
if (f) {
DEBUG_MSG("Writing preferences\n");
#ifdef FS
FS.mkdir("/prefs");
#endif
bool okay = saveProto(preffile, DeviceState_size, sizeof(devicestate), DeviceState_fields, &devicestate);
okay &= saveProto(radiofile, RadioConfig_size, sizeof(RadioConfig), RadioConfig_fields, &radioConfig);
saveChannelsToDisk();
pb_ostream_t stream = {&writecb, &f, SIZE_MAX, 0};
// DEBUG_MSG("Presave channel name=%s\n", channelSettings.name);
devicestate.version = DEVICESTATE_CUR_VER;
if (!pb_encode(&stream, DeviceState_fields, &devicestate)) {
DEBUG_MSG("Error: can't write protobuf %s\n", PB_GET_ERROR(&stream));
// FIXME - report failure to phone
f.close();
} else {
// Success - replace the old file
f.close();
// brief window of risk here ;-)
if (!FS.remove(preffile))
DEBUG_MSG("Warning: Can't remove old pref file\n");
if (!FS.rename(preftmp, preffile))
DEBUG_MSG("Error: can't rename new pref file\n");
}
} else {
DEBUG_MSG("ERROR: can't write prefs\n"); // FIXME report to app
}
// remove any pre 1.2 pref files, turn on after 1.2 is in beta
// if(okay) FS.remove(preffileOld);
} else {
DEBUG_MSG("***** DEVELOPMENT MODE - DO NOT RELEASE - not saving to flash *****\n");
}
#else
DEBUG_MSG("ERROR filesystem not implemented\n");
#endif
}
const NodeInfo *NodeDB::readNextInfo()
@@ -489,7 +440,17 @@ void NodeDB::updatePosition(uint32_t nodeId, const Position &p)
DEBUG_MSG("DB update position node=0x%x time=%u, latI=%d, lonI=%d\n", nodeId, p.time, p.latitude_i, p.longitude_i);
info->position = p;
// Be careful to only update fields that have been set by the sender
// A lot of position reports don't have time populated. In that case, be careful to not blow away the time we
// recorded based on the packet rxTime
if (!info->position.time && p.time)
info->position.time = p.time;
if (p.battery_level)
info->position.battery_level = p.battery_level;
if (p.latitude_i || p.longitude_i) {
info->position.latitude_i = p.latitude_i;
info->position.longitude_i = p.longitude_i;
}
info->has_position = true;
updateGUIforNode = info;
notifyObservers(true); // Force an update whether or not our node counts have changed
@@ -525,43 +486,18 @@ 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_payloadVariant == MeshPacket_decoded_tag) {
const SubPacket &p = mp.decoded;
if (mp.which_payloadVariant == MeshPacket_decoded_tag && mp.from) {
DEBUG_MSG("Update DB node 0x%x, rx_time=%u\n", mp.from, mp.rx_time);
NodeInfo *info = getOrCreateNode(mp.from);
NodeInfo *info = getOrCreateNode(getFrom(&mp));
if (mp.rx_time) { // if the packet has a valid timestamp use it to update our last_seen
info->has_position = true; // at least the time is valid
info->position.time = mp.rx_time;
}
info->snr = mp.rx_snr; // keep the most recent SNR we received for this node.
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);
updatePosition(mp.from, p.position);
break;
}
case SubPacket_data_tag: {
if (mp.to == NODENUM_BROADCAST || mp.to == nodeDB.getNodeNum())
MeshPlugin::callPlugins(mp);
break;
}
case SubPacket_user_tag: {
DEBUG_MSG("WARNING: Processing a (deprecated) user packet from %d\n", mp.from);
updateUser(mp.from, p.user);
break;
}
default: {
notifyObservers(); // If the node counts have changed, notify observers
}
}
if (mp.rx_snr)
info->snr = mp.rx_snr; // keep the most recent SNR we received for this node.
}
}
@@ -601,10 +537,9 @@ void recordCriticalError(CriticalErrorCode code, uint32_t address)
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++;
}

View File

@@ -9,11 +9,10 @@
#include "mesh-pb-constants.h"
extern DeviceState devicestate;
extern ChannelFile channelFile;
extern MyNodeInfo &myNodeInfo;
extern RadioConfig &radioConfig;
extern ChannelSettings &channelSettings;
extern RadioConfig radioConfig;
extern User &owner;
extern const char *channelName;
/// Given a node, return how many seconds in the past (vs now) that we last heard from it
uint32_t sinceLastSeen(const NodeInfo *n);
@@ -44,7 +43,7 @@ class NodeDB
void init();
/// write to flash
void saveToDisk();
void saveToDisk(), saveChannelsToDisk();
/** Reinit radio config if needed, because either:
* a) sometimes a buggy android app might send us bogus settings or
@@ -119,7 +118,7 @@ class NodeDB
void loadFromDisk();
/// Reinit device state from scratch (not loading from disk)
void installDefaultDeviceState();
void installDefaultDeviceState(), installDefaultRadioConfig(), installDefaultChannels();
};
/**
@@ -130,23 +129,6 @@ extern NodeNum displayedNodeNum;
extern NodeDB nodeDB;
/**
* Generate a short suffix used to disambiguate channels that might have the same "name" entered by the human but different PSKs.
* The ideas is that the PSK changing should be visible to the user so that they see they probably messed up and that's why they
their nodes
* aren't talking to each other.
*
* This string is of the form "#name-XY".
*
* Where X is a letter from A to Z (base26), and formed by xoring all the bytes of the PSK together.
* Y is not yet used but should eventually indicate 'speed/range' of the link
*
* This function will also need to be implemented in GUI apps that talk to the radio.
*
* https://github.com/meshtastic/Meshtastic-device/issues/269
*/
const char *getChannelName();
/*
If is_router is set, we use a number of different default values
@@ -173,7 +155,7 @@ const char *getChannelName();
*/
// Our delay functions check for this for times that should never expire
#define DELAY_FOREVER 0xffffffff
#define NODE_DELAY_FOREVER 0xffffffff
#define IF_ROUTER(routerVal, normalVal) (radioConfig.preferences.is_router ? (routerVal) : (normalVal))
@@ -187,8 +169,8 @@ PREF_GET(position_broadcast_secs, IF_ROUTER(12 * 60 * 60, 15 * 60))
PREF_GET(wait_bluetooth_secs, IF_ROUTER(1, 60))
PREF_GET(screen_on_secs, 60)
PREF_GET(mesh_sds_timeout_secs, IF_ROUTER(DELAY_FOREVER, 2 * 60 * 60))
PREF_GET(phone_sds_timeout_sec, IF_ROUTER(DELAY_FOREVER, 2 * 60 * 60))
PREF_GET(mesh_sds_timeout_secs, IF_ROUTER(NODE_DELAY_FOREVER, 2 * 60 * 60))
PREF_GET(phone_sds_timeout_sec, IF_ROUTER(NODE_DELAY_FOREVER, 2 * 60 * 60))
PREF_GET(sds_secs, 365 * 24 * 60 * 60)
// We default to sleeping (with bluetooth off for 5 minutes at a time). This seems to be a good tradeoff between
@@ -202,3 +184,4 @@ PREF_GET(min_wake_secs, 10)
* might have changed is incremented. Allows others to detect they might now be on a new channel.
*/
extern uint32_t radioGeneration;

View File

@@ -26,7 +26,7 @@ bool PacketHistory::wasSeenRecently(const MeshPacket *p, bool withUpdate)
// DEBUG_MSG("Deleting old broadcast record %d\n", i);
recentPackets.erase(recentPackets.begin() + i); // delete old record
} else {
if (r.id == p->id && r.sender == p->from) {
if (r.id == p->id && r.sender == getFrom(p)) {
DEBUG_MSG("Found existing packet record for fr=0x%x,to=0x%x,id=%d\n", p->from, p->to, p->id);
// Update the time on this record to now
@@ -43,7 +43,7 @@ bool PacketHistory::wasSeenRecently(const MeshPacket *p, bool withUpdate)
if (withUpdate) {
PacketRecord r;
r.id = p->id;
r.sender = p->from;
r.sender = getFrom(p);
r.rxTimeMsec = now;
recentPackets.push_back(r);
printPacket("Adding packet record", p);

View File

@@ -4,6 +4,7 @@
#include "NodeDB.h"
#include "PowerFSM.h"
#include "RadioInterface.h"
#include "Channels.h"
#include <assert.h>
#if FromRadio_size > MAX_TO_FROM_RADIO_SIZE
@@ -58,6 +59,7 @@ void PhoneAPI::handleToRadio(const uint8_t *buf, size_t bufLength)
}
// return (lastContactMsec != 0) &&
memset(&toRadioScratch, 0, sizeof(toRadioScratch));
if (pb_decode_from_bytes(buf, bufLength, ToRadio_fields, &toRadioScratch)) {
switch (toRadioScratch.which_payloadVariant) {
case ToRadio_packet_tag: {
@@ -77,16 +79,6 @@ void PhoneAPI::handleToRadio(const uint8_t *buf, size_t bufLength)
// this will break once we have multiple instances of PhoneAPI running independently
break;
case ToRadio_set_owner_tag:
DEBUG_MSG("Client is setting owner\n");
handleSetOwner(toRadioScratch.set_owner);
break;
case ToRadio_set_radio_tag:
DEBUG_MSG("Client is setting radio\n");
handleSetRadio(toRadioScratch.set_radio);
break;
default:
DEBUG_MSG("Error: unexpected ToRadio variant\n");
break;
@@ -133,24 +125,11 @@ size_t PhoneAPI::getFromRadio(uint8_t *buf)
: (gps && gps->isConnected()); // Update with latest GPS connect info
fromRadioScratch.which_payloadVariant = FromRadio_my_info_tag;
fromRadioScratch.my_info = myNodeInfo;
state = STATE_SEND_RADIO;
state = STATE_SEND_NODEINFO;
service.refreshMyNodeInfo(); // Update my NodeInfo because the client will be asking for it soon.
break;
case STATE_SEND_RADIO:
fromRadioScratch.which_payloadVariant = FromRadio_radio_tag;
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.radio.preferences.ls_secs = getPref_ls_secs();
state = STATE_SEND_NODEINFO;
break;
case STATE_SEND_NODEINFO: {
const NodeInfo *info = nodeInfoForPhone;
nodeInfoForPhone = NULL; // We just consumed a nodeinfo, will need a new one next time
@@ -177,7 +156,6 @@ size_t PhoneAPI::getFromRadio(uint8_t *buf)
state = STATE_SEND_PACKETS;
break;
case STATE_LEGACY: // Treat as the same as send packets
case STATE_SEND_PACKETS:
// Do we have a message from the mesh?
if (packetForPhone) {
@@ -200,9 +178,8 @@ size_t PhoneAPI::getFromRadio(uint8_t *buf)
// Do we have a message from the mesh?
if (fromRadioScratch.which_payloadVariant != 0) {
// Encapsulate as a FromRadio packet
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);
// DEBUG_MSG("encoding toPhone packet to phone variant=%d, %d bytes\n", fromRadioScratch.which_payloadVariant, numbytes);
return numbytes;
}
@@ -227,13 +204,9 @@ bool PhoneAPI::available()
nodeInfoForPhone = nodeDB.readNextInfo();
return true; // Always say we have something, because we might need to advance our state machine
case STATE_SEND_RADIO:
return true;
case STATE_SEND_COMPLETE_ID:
return true;
case STATE_LEGACY: // Treat as the same as send packets
case STATE_SEND_PACKETS: {
// Try to pull a new packet from the service (if we haven't already)
if (!packetForPhone)
@@ -250,41 +223,6 @@ bool PhoneAPI::available()
return false;
}
//
// The following routines are only public for now - until the rev1 bluetooth API is removed
//
void PhoneAPI::handleSetOwner(const User &o)
{
int changed = 0;
if (*o.long_name) {
changed |= strcmp(owner.long_name, o.long_name);
strcpy(owner.long_name, o.long_name);
}
if (*o.short_name) {
changed |= strcmp(owner.short_name, o.short_name);
strcpy(owner.short_name, o.short_name);
}
if (*o.id) {
changed |= strcmp(owner.id, o.id);
strcpy(owner.id, o.id);
}
if (changed) // If nothing really changed, don't broadcast on the network or write to flash
service.reloadOwner();
}
void PhoneAPI::handleSetRadio(const RadioConfig &r)
{
radioConfig = r;
bool didReset = service.reloadConfig();
if (didReset) {
state = STATE_SEND_MY_INFO; // Squirt a completely new set of configs to the client
}
}
/**
* Handle a packet that the phone wants us to send. It is our responsibility to free the packet to the pool
*/
@@ -296,7 +234,7 @@ int PhoneAPI::onNotify(uint32_t newValue)
checkConnectionTimeout(); // a handy place to check if we've heard from the phone (since the BLE version doesn't call this
// from idle)
if (state == STATE_SEND_PACKETS || state == STATE_LEGACY) {
if (state == STATE_SEND_PACKETS) {
DEBUG_MSG("Telling client we have new packets %u\n", newValue);
onNowHasData(newValue);
} else

View File

@@ -20,10 +20,10 @@ class PhoneAPI
: public Observer<uint32_t> // FIXME, we shouldn't be inheriting from Observer, instead use CallbackObserver as a member
{
enum State {
STATE_LEGACY, // (no longer used) old default state - until Android apps are all updated, uses the old BLE API
STATE_UNUSED, // (no longer used) old default state - until Android apps are all updated, uses the old BLE API
STATE_SEND_NOTHING, // (Eventual) Initial state, don't send anything until the client starts asking for config
STATE_SEND_MY_INFO, // send our my info record
STATE_SEND_RADIO,
// STATE_SEND_RADIO, // in 1.2 we now send this as a regular mesh packet
// STATE_SEND_OWNER, no need to send Owner specially, it is just part of the nodedb
STATE_SEND_NODEINFO, // states progress in this order as the device sends to to the client
STATE_SEND_COMPLETE_ID,
@@ -83,13 +83,6 @@ class PhoneAPI
*/
bool available();
//
// The following routines are only public for now - until the rev1 bluetooth API is removed
//
void handleSetOwner(const User &o);
void handleSetRadio(const RadioConfig &r);
protected:
/// Are we currently connected to a client?
bool isConnected = false;

View File

@@ -8,7 +8,7 @@
* If you are using protobufs to encode your packets (recommended) you can use this as a baseclass for your plugin
* and avoid a bunch of boilerplate code.
*/
template <class T> class ProtobufPlugin : private SinglePortPlugin
template <class T> class ProtobufPlugin : protected SinglePortPlugin
{
const pb_msgdesc_t *fields;
@@ -25,8 +25,11 @@ template <class T> class ProtobufPlugin : private SinglePortPlugin
/**
* Handle a received message, the data field in the message is already decoded and is provided
*
* In general decoded will always be !NULL. But in some special applications (where you have handling packets
* for multiple port numbers, decoding will ONLY be attempted for packets where the portnum matches our expected ourPortNum.
*/
virtual bool handleReceivedProtobuf(const MeshPacket &mp, const T &decoded) = 0;
virtual bool handleReceivedProtobuf(const MeshPacket &mp, const T *decoded) = 0;
/**
* Return a mesh packet which has been preinited with a particular protobuf data payload and port number.
@@ -38,8 +41,8 @@ template <class T> class ProtobufPlugin : private SinglePortPlugin
// Update our local node info with our position (even if we don't decide to update anyone else)
MeshPacket *p = allocDataPacket();
p->decoded.data.payload.size =
pb_encode_to_bytes(p->decoded.data.payload.bytes, sizeof(p->decoded.data.payload.bytes), fields, &payload);
p->decoded.payload.size =
pb_encode_to_bytes(p->decoded.payload.bytes, sizeof(p->decoded.payload.bytes), fields, &payload);
// DEBUG_MSG("did encode\n");
return p;
}
@@ -54,13 +57,19 @@ template <class T> class ProtobufPlugin : private SinglePortPlugin
// FIXME - we currently update position data in the DB only if the message was a broadcast or destined to us
// it would be better to update even if the message was destined to others.
auto &p = mp.decoded.data;
DEBUG_MSG("Received %s from=0x%0x, id=0x%x, payloadlen=%d\n", name, mp.from, mp.id, p.payload.size);
auto &p = mp.decoded;
DEBUG_MSG("Received %s from=0x%0x, id=0x%x, portnum=%d, payloadlen=%d\n", name, mp.from, mp.id, p.portnum, p.payload.size);
T scratch;
if (pb_decode_from_bytes(p.payload.bytes, p.payload.size, fields, &scratch))
return handleReceivedProtobuf(mp, scratch);
T *decoded = NULL;
if(mp.decoded.portnum == ourPortNum) {
memset(&scratch, 0, sizeof(scratch));
if (pb_decode_from_bytes(p.payload.bytes, p.payload.size, fields, &scratch))
decoded = &scratch;
else
DEBUG_MSG("Error decoding protobuf plugin!\n");
}
return false; // Let others look at this message also if they want
return handleReceivedProtobuf(mp, decoded);
}
};

View File

@@ -1,10 +1,12 @@
#include "configuration.h"
#include "RadioInterface.h"
#include "Channels.h"
#include "MeshRadio.h"
#include "MeshService.h"
#include "NodeDB.h"
#include "assert.h"
#include "configuration.h"
#include "Router.h"
#include "sleep.h"
#include <assert.h>
#include <pb_decode.h>
@@ -23,9 +25,27 @@ const RegionInfo regions[] = {
RDEF(KR, 921.9f, 0.2f, 8, 0), // KR channel settings (KR920-923) Start from TTN download channel
// freq. (921.9f is for download, others are for uplink)
RDEF(TW, 923.0f, 0.2f, 10, 0), // TW channel settings (AS2 bandplan 923-925MHz)
RDEF(RU, 868.9f, 0.2f, 2, 20), // See notes below
RDEF(Unset, 903.08f, 2.16f, 13, 0) // Assume US freqs if unset, Must be last
};
/* Notes about the RU bandplan (from @denis-d in https://meshtastic.discourse.group/t/russian-band-plan-proposal/2786/2):
According to Annex 12 to GKRCh (National Radio Frequency Commission) decision № 18-46-03-1 (September 11th 2018) https://digital.gov.ru/uploaded/files/prilozhenie-12-k-reshenyu-gkrch-18-46-03-1.pdf 1
We have 3 options for 868 MHz:
864,0 - 865,0 MHz ERP 25mW, Duty Cycle 0.1% (3.6 sec in hour) or LBT (Listen Before Talk), prohibited in airports.
866,0 - 868,0 MHz ERP 25mW, Duty Cycle 1% or LBT, PSD (Power Spectrum Density) 1000mW/MHz, prohibited in airports
868,7 - 869,2 MHz ERP 100mW, Duty Cycle 10% or LBT, no resctrictions
and according to RP2-1.0.2 LoRaWAN® Regional Parameters RP2-1.0.2 LoRaWAN® Regional Parameters - LoRa Alliance®
I propose to use following meshtastic bandplan:
1 channel - central frequency 868.9MHz 125kHz band
Protective band - 75kHz
2 channel - central frequency 869.1MHz 125kHz band
RDEF(RU, 868.9f, 0.2f, 2, 20)
*/
const RegionInfo *myRegion;
void initRegion()
@@ -36,7 +56,7 @@ void initRegion()
myRegion = r;
DEBUG_MSG("Wanted region %d, using %s\n", radioConfig.preferences.region, r->name);
myNodeInfo.num_channels = myRegion->numChannels; // Tell our android app how many channels we have
myNodeInfo.num_bands = myRegion->numChannels; // Tell our android app how many channels we have
}
/**
@@ -117,27 +137,13 @@ uint32_t RadioInterface::getTxDelayMsec()
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);
DEBUG_MSG("%s (id=0x%08x Fr0x%02x To0x%02x, WantAck%d, HopLim%d Ch0x%x", prefix, p->id, p->from & 0xff, p->to & 0xff,
p->want_ack, p->hop_limit, p->channel);
if (p->which_payloadVariant == MeshPacket_decoded_tag) {
auto &s = p->decoded;
switch (s.which_payloadVariant) {
case SubPacket_data_tag:
DEBUG_MSG(" Portnum=%d", s.data.portnum);
break;
case SubPacket_position_tag:
DEBUG_MSG(" Payload:Position");
break;
case SubPacket_user_tag:
DEBUG_MSG(" Payload:User");
break;
case 0:
DEBUG_MSG(" Payload:None");
break;
default:
DEBUG_MSG(" Payload:%d", s.which_payloadVariant);
break;
}
DEBUG_MSG(" Portnum=%d", s.portnum);
if (s.want_response)
DEBUG_MSG(" WANTRESP");
@@ -147,10 +153,14 @@ void printPacket(const char *prefix, const MeshPacket *p)
if (s.dest != 0)
DEBUG_MSG(" dest=%08x", s.dest);
if(s.request_id)
DEBUG_MSG(" requestId=%0x", s.request_id);
/* now inside Data and therefore kinda opaque
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);
DEBUG_MSG(" failId=%08x", s.ackVariant.fail_id); */
} else {
DEBUG_MSG(" encrypted");
}
@@ -161,15 +171,15 @@ 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)
if (p->priority != 0)
DEBUG_MSG(" priority=%d", p->priority);
DEBUG_MSG(")\n");
}
RadioInterface::RadioInterface()
{
assert(sizeof(PacketHeader) == 4 || sizeof(PacketHeader) == 16); // make sure the compiler did what we expected
assert(sizeof(PacketHeader) == 16); // make sure the compiler did what we expected
// Can't print strings this early - serial not setup yet
// DEBUG_MSG("Set meshradio defaults name=%s\n", channelSettings.name);
@@ -212,6 +222,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
*/
@@ -220,6 +262,7 @@ void RadioInterface::applyModemConfig()
// Set up default configuration
// No Sync Words in LORA mode
auto channelSettings = channels.getPrimary();
if (channelSettings.spread_factor == 0) {
switch (channelSettings.modem_config) {
case ChannelSettings_ModemConfig_Bw125Cr45Sf128: ///< Bw = 125 kHz, Cr = 4/5, Sf = 128chips/symbol, CRC on. Default medium
@@ -264,18 +307,20 @@ 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;
const char *channelName = channels.getName(channels.getPrimaryIndex());
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);
}
/**
@@ -308,6 +353,10 @@ void RadioInterface::deliverToReceiver(MeshPacket *p)
{
assert(rxDest);
assert(rxDest->enqueue(p, 0)); // NOWAIT - fixme, if queue is full, delete older messages
// Nasty hack because our threading is primitive. interfaces shouldn't need to know about routers FIXME
if (router)
router->setReceivedMessage();
}
/***
@@ -327,6 +376,7 @@ size_t RadioInterface::beginSending(MeshPacket *p)
h->from = p->from;
h->to = p->to;
h->id = p->id;
h->channel = p->channel;
assert(p->hop_limit <= HOP_MAX);
h->flags = p->hop_limit | (p->want_ack ? PACKET_FLAGS_WANT_ACK_MASK : 0);

View File

@@ -29,6 +29,9 @@ typedef struct {
* The bottom three bits of flags are use to store hop_limit when sent over the wire.
**/
uint8_t flags;
/** The channel hash - used as a hint for the decoder to limit which channels we consider */
uint8_t channel;
} PacketHeader;
/**
@@ -54,6 +57,8 @@ class RadioInterface
uint32_t shortPacketMsec;
protected:
bool disabled = false;
float bw = 125;
uint8_t sf = 9;
uint8_t cr = 7;
@@ -98,6 +103,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.
@@ -136,9 +144,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).
@@ -160,6 +181,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; }

View File

@@ -2,8 +2,8 @@
#include "MeshTypes.h"
#include "NodeDB.h"
#include "SPILock.h"
#include "mesh-pb-constants.h"
#include "error.h"
#include "mesh-pb-constants.h"
#include <configuration.h>
#include <pb_decode.h>
#include <pb_encode.h>
@@ -68,13 +68,12 @@ bool RadioLibInterface::canSendImmediately()
bool busyTx = sendingPacket != NULL;
bool busyRx = isReceiving && isActivelyReceiving();
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)){
if (busyTx && (millis() - lastTxStart > 60000)) {
DEBUG_MSG("Hardware Failure! busyTx for more than 60s\n");
recordCriticalError(CriticalErrorCode_TransmitFailed);
#ifndef NO_ESP32
@@ -94,6 +93,11 @@ 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);
@@ -110,7 +114,6 @@ 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.
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
@@ -119,7 +122,7 @@ ErrorCode RadioLibInterface::send(MeshPacket *p)
return res;
#else
packetPool.release(p);
return ERRNO_UNKNOWN;
return ERRNO_DISABLED;
#endif
}
@@ -133,17 +136,17 @@ bool RadioLibInterface::canSleep()
}
/** Attempt to cancel a previously sent packet. Returns true if a packet was found we could cancel */
bool RadioLibInterface::cancelSending(NodeNum from, PacketId id) {
bool RadioLibInterface::cancelSending(NodeNum from, PacketId id)
{
auto p = txQueue.remove(from, id);
if(p)
if (p)
packetPool.release(p); // free the packet we just removed
bool result = (p != NULL);
DEBUG_MSG("cancelSending id=0x%x, removed=%d", id, result);
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
@@ -241,13 +244,14 @@ void RadioLibInterface::handleReceiveInterrupt()
size_t length = iface->getPacketLength();
xmitMsec = getPacketTime(length);
airTime->logAirtime(RX_ALL_LOG, xmitMsec);
//airTime.logAirtime(RX_ALL_LOG, xmitMsec);
int state = iface->readData(radiobuf, length);
if (state != ERR_NONE) {
DEBUG_MSG("ignoring received packet due to error=%d\n", state);
rxBad++;
airTime->logAirtime(RX_ALL_LOG, xmitMsec);
} else {
// Skip the 4 headers that are at the beginning of the rxBuf
int32_t payloadLen = length - sizeof(PacketHeader);
@@ -257,6 +261,7 @@ void RadioLibInterface::handleReceiveInterrupt()
if (payloadLen < 0) {
DEBUG_MSG("ignoring received packet too short\n");
rxBad++;
airTime->logAirtime(RX_ALL_LOG, xmitMsec);
} else {
const PacketHeader *h = (PacketHeader *)radiobuf;
@@ -270,6 +275,7 @@ void RadioLibInterface::handleReceiveInterrupt()
mp->from = h->from;
mp->to = h->to;
mp->id = h->id;
mp->channel = h->channel;
assert(HOP_MAX <= PACKET_FLAGS_HOP_MASK); // If hopmax changes, carefully check this code
mp->hop_limit = h->flags & PACKET_FLAGS_HOP_MASK;
mp->want_ack = !!(h->flags & PACKET_FLAGS_WANT_ACK_MASK);
@@ -277,15 +283,14 @@ void RadioLibInterface::handleReceiveInterrupt()
addReceiveMetadata(mp);
mp->which_payloadVariant = MeshPacket_encrypted_tag; // Mark that the payload is still encrypted at this point
assert(((uint32_t) payloadLen) <= sizeof(mp->encrypted.bytes));
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);
//xmitMsec = getPacketTime(mp);
airTime->logAirtime(RX_LOG, xmitMsec);
//airTime.logAirtime(RX_LOG, xmitMsec);
deliverToReceiver(mp);
}
@@ -296,15 +301,20 @@ void RadioLibInterface::handleReceiveInterrupt()
void RadioLibInterface::startSend(MeshPacket *txp)
{
printPacket("Starting low level send", txp);
setStandby(); // Cancel any already in process receives
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);
}
}

View File

@@ -80,10 +80,13 @@ class RadioLibInterface : public RadioInterface, protected concurrency::Notified
protected:
/**
* FIXME, use a meshtastic sync word, but hashed with the Channel name. Currently picking the same default
* the RF95 used (0x14). Note: do not use 0x34 - that is reserved for lorawan
* We use a meshtastic sync word, but hashed with the Channel name. For releases before 1.2 we used 0x12 (or for very old loads 0x14)
* Note: do not use 0x34 - that is reserved for lorawan
*
* We now use 0x2b (so that someday we can possibly use NOT 2b - because that would be funny pun). We will be staying with this code
* for a long time.
*/
uint8_t syncWord = SX126X_SYNC_WORD_PRIVATE;
const uint8_t syncWord = 0x2b;
float currentLimit = 100; // FIXME

View File

@@ -1,4 +1,5 @@
#include "ReliableRouter.h"
#include "MeshPlugin.h"
#include "MeshTypes.h"
#include "configuration.h"
#include "mesh-pb-constants.h"
@@ -13,9 +14,10 @@ ErrorCode ReliableRouter::send(MeshPacket *p)
{
if (p->want_ack) {
// If someone asks for acks on broadcast, we need the hop limit to be at least one, so that first node that receives our
// message will rebroadcast
// message will rebroadcast. But asking for hop_limit 0 in that context means the client app has no preference on hop counts
// and we want this message to get through the whole mesh, so use the default.
if (p->to == NODENUM_BROADCAST && p->hop_limit == 0)
p->hop_limit = 1;
p->hop_limit = HOP_RELIABLE;
auto copy = packetPool.allocCopy(*p);
startRetransmission(copy);
@@ -26,15 +28,26 @@ ErrorCode ReliableRouter::send(MeshPacket *p)
bool ReliableRouter::shouldFilterReceived(const MeshPacket *p)
{
// Note: do not use getFrom() here, because we want to ignore messages sent from phone
if (p->to == NODENUM_BROADCAST && p->from == getNodeNum()) {
printPacket("Rx someone rebroadcasting for us", p);
// We are seeing someone rebroadcast one of our broadcast attempts.
// If this is the first time we saw this, cancel any retransmissions we have queued up and generate an internal ack for
// the original sending process.
if (stopRetransmission(p->from, p->id)) {
DEBUG_MSG("Someone is retransmitting for us, generate implicit ack\n");
sendAckNak(ErrorReason_NONE, p->from, p->id);
// FIXME - we might want to turn off this "optimization", it does save lots of airtime but it assumes that once we've heard one
// one adjacent node hear our packet that a) probably other adjacent nodes heard it and b) we can trust those nodes to reach
// our destination. Both of which might be incorrect.
auto key = GlobalPacketId(getFrom(p), p->id);
auto old = findPendingPacket(key);
if (old) {
DEBUG_MSG("generating implicit ack\n");
// NOTE: we do NOT check p->wantAck here because p is the INCOMING rebroadcast and that packet is not expected to be
// marked as wantAck
sendAckNak(Routing_Error_NONE, getFrom(p), p->id, old->packet->channel);
stopRetransmission(key);
}
}
@@ -53,35 +66,39 @@ bool ReliableRouter::shouldFilterReceived(const MeshPacket *p)
*
* Otherwise, let superclass handle it.
*/
void ReliableRouter::sniffReceived(const MeshPacket *p)
void ReliableRouter::sniffReceived(const MeshPacket *p, const Routing *c)
{
NodeNum ourNode = getNodeNum();
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(ErrorReason_NONE, p->from, p->id);
if (MeshPlugin::currentReply)
DEBUG_MSG("Someone else has replied to this message, no need for a 2nd ack\n");
else
sendAckNak(Routing_Error_NONE, getFrom(p), p->id, p->channel);
}
// If the payload is valid, look for ack/nak
// We consider an ack to be either a !routing packet with a request ID or a routing packet with !error
PacketId ackId = ((c && c->error_reason == Routing_Error_NONE) || !c) ? p->decoded.request_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;
// A nak is a routing packt that has an error code
PacketId nakId = (c && c->error_reason != Routing_Error_NONE) ? p->decoded.request_id : 0;
// We intentionally don't check wasSeenRecently, because it is harmless to delete non existent retransmission records
if (ackId || nakId) {
if (ackId) {
DEBUG_MSG("Received a ack=%d, stopping retransmissions\n", ackId);
DEBUG_MSG("Received a ack for 0x%x, stopping retransmissions\n", ackId);
stopRetransmission(p->to, ackId);
} else {
DEBUG_MSG("Received a nak=%d, stopping retransmissions\n", nakId);
DEBUG_MSG("Received a nak for 0x%x, stopping retransmissions\n", nakId);
stopRetransmission(p->to, nakId);
}
}
}
// handle the packet as normal
FloodingRouter::sniffReceived(p);
FloodingRouter::sniffReceived(p, c);
}
#define NUM_RETRANSMISSIONS 3
@@ -129,8 +146,9 @@ PendingPacket *ReliableRouter::startRetransmission(MeshPacket *p)
auto id = GlobalPacketId(p);
auto rec = PendingPacket(p);
stopRetransmission(getFrom(p), p->id);
setNextTx(&rec);
stopRetransmission(p->from, p->id);
pending[id] = rec;
return &pending[id];
@@ -150,18 +168,21 @@ int32_t ReliableRouter::doRetransmissions()
++nextIt; // we use this odd pattern because we might be deleting it...
auto &p = it->second;
bool stillValid = true; // assume we'll keep this record around
// FIXME, handle 51 day rolloever here!!!
if (p.nextTxMsec <= now) {
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,
DEBUG_MSG("Reliable send failed, returning a nak for fr=0x%x,to=0x%x,id=0x%x\n", p.packet->from, p.packet->to,
p.packet->id);
sendAckNak(ErrorReason_MAX_RETRANSMIT, p.packet->from, p.packet->id);
sendAckNak(Routing_Error_MAX_RETRANSMIT, getFrom(p.packet), p.packet->id, p.packet->channel);
// 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);
stillValid = false; // just deleted it
} else {
DEBUG_MSG("Sending reliable retransmission fr=0x%x,to=0x%x,id=%d, tries left=%d\n", p.packet->from, p.packet->to,
p.packet->id, p.numRetransmissions);
DEBUG_MSG("Sending reliable retransmission fr=0x%x,to=0x%x,id=0x%x, tries left=%d\n", p.packet->from,
p.packet->to, p.packet->id, p.numRetransmissions);
// Note: we call the superclass version because we don't want to have our version of send() add a new
// retransmission record
@@ -171,8 +192,10 @@ int32_t ReliableRouter::doRetransmissions()
--p.numRetransmissions;
setNextTx(&p);
}
} else {
// Not yet time
}
if (stillValid) {
// Update our desired sleep delay
int32_t t = p.nextTxMsec - now;
d = min(t, d);
@@ -180,4 +203,14 @@ int32_t ReliableRouter::doRetransmissions()
}
return d;
}
void ReliableRouter::setNextTx(PendingPacket *pending)
{
assert(iface);
auto d = iface->getRetransmissionMsec(pending->packet);
pending->nextTxMsec = millis() + d;
DEBUG_MSG("Setting next retransmission in %u msecs: ", d);
printPacket("", pending->packet);
setReceivedMessage(); // Run ASAP, so we can figure out our correct sleep time
}

View File

@@ -15,7 +15,7 @@ struct GlobalPacketId {
GlobalPacketId(const MeshPacket *p)
{
node = p->from;
node = getFrom(p);
id = p->id;
}
@@ -90,7 +90,7 @@ class ReliableRouter : public FloodingRouter
/**
* Look for acks/naks or someone retransmitting us
*/
virtual void sniffReceived(const MeshPacket *p);
virtual void sniffReceived(const MeshPacket *p, const Routing *c);
/**
* Try to find the pending packet record for this ID (or NULL if not found)
@@ -125,7 +125,5 @@ class ReliableRouter : public FloodingRouter
*/
int32_t doRetransmissions();
void setNextTx(PendingPacket *pending) {
assert(iface);
pending->nextTxMsec = millis() + iface->getRetransmissionMsec(pending->packet); }
void setNextTx(PendingPacket *pending);
};

View File

@@ -1,9 +1,11 @@
#include "Router.h"
#include "Channels.h"
#include "CryptoEngine.h"
#include "NodeDB.h"
#include "RTC.h"
#include "configuration.h"
#include "mesh-pb-constants.h"
#include <NodeDB.h>
#include "plugins/RoutingPlugin.h"
/**
* Router todo
@@ -80,7 +82,6 @@ PacketId generatePacketId()
i++;
PacketId id = (i % numPacketId) + 1; // return number between 1 and numPacketId (ie - never zero)
myNodeInfo.current_packet_id = id; // Kinda crufty - we keep updating this so the phone can see a current value
return id;
}
@@ -102,45 +103,38 @@ MeshPacket *Router::allocForSending()
/**
* Send an ack or a nak packet back towards whoever sent idFrom
*/
void Router::sendAckNak(ErrorReason err, NodeNum to, PacketId idFrom)
void Router::sendAckNak(Routing_Error err, NodeNum to, PacketId idFrom, ChannelIndex chIndex)
{
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
routingPlugin->sendAckNak(err, to, idFrom, chIndex);
}
void Router::abortSendAndNak(Routing_Error err, MeshPacket *p)
{
DEBUG_MSG("Error=%d, returning NAK and dropping packet.\n", err);
sendAckNak(Routing_Error_NO_INTERFACE, getFrom(p), p->id, p->channel);
packetPool.release(p);
}
void Router::setReceivedMessage()
{
setInterval(0); // Run ASAP, so we can figure out our correct sleep time
}
ErrorCode Router::sendLocal(MeshPacket *p)
{
// No need to deliver externally if the destination is the local node
if (p->to == nodeDB.getNodeNum()) {
printPacket("Enqueuing local", p);
fromRadioQueue.enqueue(p);
if (fromRadioQueue.enqueue(p, 0)) {
printPacket("Enqueued local", p);
setReceivedMessage();
} else {
printPacket("BUG! fromRadioQueue is full! Discarding!", p);
packetPool.release(p);
}
return ERRNO_OK;
} else if (!iface) {
// We must be sending to remote nodes also, fail if no interface found
// 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);
abortSendAndNak(Routing_Error_NO_INTERFACE, p);
return ERRNO_NO_INTERFACES;
} else {
@@ -154,6 +148,14 @@ ErrorCode Router::sendLocal(MeshPacket *p)
}
}
void printBytes(const char *label, const uint8_t *p, size_t numbytes)
{
DEBUG_MSG("%s: ", label);
for (size_t i = 0; i < numbytes; i++)
DEBUG_MSG("%02x ", p[i]);
DEBUG_MSG("\n");
}
/**
* Send a packet on a suitable interface. This routine will
* later free() the packet to pool. This routine is not allowed to stall.
@@ -163,14 +165,18 @@ ErrorCode Router::send(MeshPacket *p)
{
assert(p->to != nodeDB.getNodeNum()); // should have already been handled by sendLocal
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
// 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
// Never set the want_ack flag on broadcast packets sent over the air.
if (p->to == NODENUM_BROADCAST)
p->want_ack = false;
// Up until this point we might have been using 0 for the from address (if it started with the phone), but when we send over
// the lora we need to make sure we have replaced it with our local address
p->from = getFrom(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_payloadVariant == MeshPacket_encrypted_tag ||
@@ -180,10 +186,27 @@ ErrorCode Router::send(MeshPacket *p)
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);
// printPacket("pre encrypt", p); // portnum valid here
assert(numbytes <= MAX_RHPACKETLEN);
crypto->encrypt(p->from, p->id, numbytes, bytes);
size_t numbytes = pb_encode_to_bytes(bytes, sizeof(bytes), Data_fields, &p->decoded);
if (numbytes > MAX_RHPACKETLEN) {
abortSendAndNak(Routing_Error_TOO_LARGE, p);
return ERRNO_TOO_LARGE;
}
// printBytes("plaintext", bytes, numbytes);
auto hash = channels.setActiveByIndex(p->channel);
if (hash < 0) {
// No suitable channel could be found for sending
abortSendAndNak(Routing_Error_NO_CHANNEL, p);
return ERRNO_NO_CHANNEL;
}
// Now that we are encrypting the packet channel should be the hash (no longer the index)
p->channel = hash;
crypto->encrypt(getFrom(p), p->id, numbytes, bytes);
// Copy back into the packet and set the variant type
memcpy(p->encrypted.bytes, bytes, numbytes);
@@ -192,28 +215,20 @@ ErrorCode Router::send(MeshPacket *p)
}
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) {
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)
*/
void Router::sniffReceived(const MeshPacket *p)
void Router::sniffReceived(const MeshPacket *p, const Routing *c)
{
DEBUG_MSG("FIXME-update-db Sniffing packet\n");
// FIXME, update nodedb here for any packet that passes through us
@@ -226,23 +241,38 @@ bool Router::perhapsDecode(MeshPacket *p)
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.
// Try to decrypt the packet if we can
static uint8_t bytes[MAX_RHPACKETLEN];
memcpy(bytes, p->encrypted.bytes,
p->encrypted.size); // we have to copy into a scratch buffer, because these bytes are a union with the decoded protobuf
crypto->decrypt(p->from, p->id, p->encrypted.size, bytes);
// Try to find a channel that works with this hash
for (ChannelIndex chIndex = 0; chIndex < channels.getNumChannels(); chIndex++) {
// Try to use this hash/channel pair
if (channels.decryptForHash(chIndex, p->channel)) {
// Try to decrypt the packet if we can
static uint8_t bytes[MAX_RHPACKETLEN];
size_t rawSize = p->encrypted.size;
assert(rawSize <= sizeof(bytes));
memcpy(bytes, p->encrypted.bytes,
rawSize); // we have to copy into a scratch buffer, because these bytes are a union with the decoded protobuf
crypto->decrypt(p->from, p->id, rawSize, bytes);
// Take those raw bytes and convert them back into a well structured protobuf we can understand
if (!pb_decode_from_bytes(bytes, p->encrypted.size, SubPacket_fields, &p->decoded)) {
DEBUG_MSG("Invalid protobufs in received mesh packet!\n");
return false;
} else {
// parsing was successful
p->which_payloadVariant = MeshPacket_decoded_tag;
return true;
// printBytes("plaintext", bytes, p->encrypted.size);
// Take those raw bytes and convert them back into a well structured protobuf we can understand
memset(&p->decoded, 0, sizeof(p->decoded));
if (!pb_decode_from_bytes(bytes, rawSize, Data_fields, &p->decoded)) {
DEBUG_MSG("Invalid protobufs in received mesh packet (bad psk?)!\n");
} else if (p->decoded.portnum == PortNum_UNKNOWN_APP) {
DEBUG_MSG("Invalid portnum (bad psk?)!\n");
} else {
// parsing was successful
p->which_payloadVariant = MeshPacket_decoded_tag; // change type to decoded
p->channel = chIndex; // change to store the index instead of the hash
printPacket("decoded message", p);
return true;
}
}
}
DEBUG_MSG("No suitable channel found for decoding, hash was 0x%x!\n", p->channel);
return false;
}
NodeNum Router::getNodeNum()
@@ -260,15 +290,16 @@ void Router::handleReceived(MeshPacket *p)
p->rx_time = getValidTime(RTCQualityFromNet); // store the arrival timestamp for the phone
// Take those raw bytes and convert them back into a well structured protobuf we can understand
if (perhapsDecode(p)) {
bool decoded = perhapsDecode(p);
if (decoded) {
// parsing was successful, queue for our recipient
printPacket("handleReceived", p);
sniffReceived(p);
if (p->to == NODENUM_BROADCAST || p->to == getNodeNum()) {
printPacket("Delivering rx packet", p);
notifyPacketReceived.notifyObservers(p);
}
// call any promiscious plugins here, make a (non promisiocous) plugin for forwarding messages to phone api
// sniffReceived(p);
MeshPlugin::callPlugins(*p);
} else {
DEBUG_MSG("packet decoding failed\n");
}
}
@@ -280,7 +311,7 @@ void Router::perhapsHandleReceived(MeshPacket *p)
if (ignore)
DEBUG_MSG("Ignoring incoming message, 0x%x is in our ignore list\n", p->from);
else if (ignore |= shouldFilterReceived(p)) {
// DEBUG_MSG("Incoming message was filtered 0x%x\n", p->from);
DEBUG_MSG("Incoming message was filtered 0x%x\n", p->from);
}
// Note: we avoid calling shouldFilterReceived if we are supposed to ignore certain nodes - because some overrides might

View File

@@ -6,6 +6,7 @@
#include "PointerQueue.h"
#include "RadioInterface.h"
#include "concurrency/OSThread.h"
#include "Channels.h"
/**
* A mesh aware router that supports multiple interfaces.
@@ -21,10 +22,6 @@ class Router : protected concurrency::OSThread
RadioInterface *iface = NULL;
public:
/// Local services that want to see _every_ packet this node receives can observe this.
/// Observers should always return 0 and _copy_ any packets they want to keep for use later (this packet will be getting
/// freed)
Observable<const MeshPacket *> notifyPacketReceived;
/**
* Constructor
@@ -67,7 +64,14 @@ class Router : protected concurrency::OSThread
* @return our local nodenum */
NodeNum getNodeNum();
/** Wake up the router thread ASAP, because we just queued a message for it.
* FIXME, this is kinda a hack because we don't have a nice way yet to say 'wake us because we are 'blocked on this queue'
*/
void setReceivedMessage();
protected:
friend class RoutingPlugin;
/**
* Send a packet on a suitable interface. This routine will
* later free() the packet to pool. This routine is not allowed to stall.
@@ -79,6 +83,8 @@ class Router : protected concurrency::OSThread
/**
* Should this incoming filter be dropped?
*
* FIXME, move this into the new RoutingPlugin and do the filtering there using the regular plugin logic
*
* Called immedately on receiption, before any further processing.
* @return true to abandon the packet
@@ -89,7 +95,7 @@ class Router : protected concurrency::OSThread
* 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)
*/
virtual void sniffReceived(const MeshPacket *p);
virtual void sniffReceived(const MeshPacket *p, const Routing *c);
/**
* Remove any encryption and decode the protobufs inside this packet (if necessary).
@@ -101,7 +107,7 @@ class Router : protected concurrency::OSThread
/**
* Send an ack or a nak packet back towards whoever sent idFrom
*/
void sendAckNak(ErrorReason err, NodeNum to, PacketId idFrom);
void sendAckNak(Routing_Error err, NodeNum to, PacketId idFrom, ChannelIndex chIndex);
private:
/**
@@ -123,6 +129,9 @@ class Router : protected concurrency::OSThread
* Note: this method will free the provided packet.
*/
void handleReceived(MeshPacket *p);
/** Frees the provided packet, and generates a NAK indicating the speicifed error while sending */
void abortSendAndNak(Routing_Error err, MeshPacket *p);
};
extern Router *router;

View File

@@ -37,8 +37,8 @@ bool SX1262Interface::init()
#ifndef SX1262_E22
float tcxoVoltage = 0; // None - we use an XTAL
#else
float tcxoVoltage =
1.8; // E22 uses DIO3 to power tcxo per https://github.com/jgromes/RadioLib/issues/12#issuecomment-520695575
// Use DIO3 to power tcxo per https://github.com/jgromes/RadioLib/issues/12#issuecomment-520695575
float tcxoVoltage = 1.8;
#endif
bool useRegulatorLDO = false; // Seems to depend on the connection to pin 9/DCC_SW - if an inductor DCDC?
@@ -142,6 +142,7 @@ void SX1262Interface::addReceiveMetadata(MeshPacket *mp)
{
// DEBUG_MSG("PacketStatus %x\n", lora.getPacketStatus());
mp->rx_snr = lora.getSNR();
mp->rx_rssi = lround(lora.getRSSI());
}
/** We override to turn on transmitter power as needed.

View File

@@ -21,7 +21,7 @@ class SinglePortPlugin : public MeshPlugin
/**
* @return true if you want to receive the specified portnum
*/
virtual bool wantPortnum(PortNum p) { return p == ourPortNum; }
virtual bool wantPacket(const MeshPacket *p) { return p->decoded.portnum == ourPortNum; }
/**
* Return a mesh packet which has been preinited as a data packet with a particular port number.
@@ -32,8 +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_payloadVariant = SubPacket_data_tag;
p->decoded.data.portnum = ourPortNum;
p->decoded.portnum = ourPortNum;
return p;
}

View File

@@ -71,12 +71,18 @@ void StreamAPI::writeStream()
void StreamAPI::emitTxBuffer(size_t len)
{
if (len != 0) {
DEBUG_MSG("emit tx %d\n", len);
txBuf[0] = START1;
txBuf[1] = START2;
txBuf[2] = (len >> 8) & 0xff;
txBuf[3] = len & 0xff;
stream->write(txBuf, len + HEADER_LEN);
auto totalLen = len + HEADER_LEN;
stream->write(txBuf, totalLen);
/* for(size_t i = 0; i < totalLen; i++) {
stream->write(txBuf[i]);
// stream->flush();
} */
}
}

View File

@@ -31,7 +31,9 @@ template <class T> class TypedQueue
bool isEmpty() { return uxQueueMessagesWaiting(h) == 0; }
bool enqueue(T x, TickType_t maxWait = portMAX_DELAY)
/** euqueue a packet. Also, maxWait used to default to portMAX_DELAY, but we now want to callers to THINK about what blocking
* they want */
bool enqueue(T x, TickType_t maxWait)
{
if (reader) {
reader->setInterval(0);

View File

@@ -0,0 +1,12 @@
/* Automatically generated nanopb constant definitions */
/* Generated by nanopb-0.4.4 */
#include "admin.pb.h"
#if PB_PROTO_HEADER_VERSION != 40
#error Regenerate this file with the current version of nanopb generator.
#endif
PB_BIND(AdminMessage, AdminMessage, 2)

Some files were not shown because too many files have changed in this diff Show More