Compare commits

...

192 Commits

Author SHA1 Message Date
Kevin Hester
7ff1f3a759 Merge pull request #684 from geeksville/dev
Dev
2021-02-12 10:04:48 +08:00
Kevin Hester
bbc8fc0269 fix build for nrf52 2021-02-12 09:56:42 +08:00
Kevin Hester
9d81511153 fix warning 2021-02-12 09:56:35 +08:00
Kevin Hester
16d63bd0ce 1.1.46 2021-02-12 09:42:04 +08:00
Kevin Hester
f2b7ff2b79 an optimization for #681 - make want_ack packets higher priority 2021-02-12 09:40:20 +08:00
Kevin Hester
bc8453283f Merge remote-tracking branch 'root/master' into dev 2021-02-12 09:10:48 +08:00
Kevin Hester
2ff5046dcd for #681, add MeshPacketQueue (a priority queue) to ensure acks get sent quickly 2021-02-12 09:08:49 +08:00
Kevin Hester
917090856f partial for #681: Never let stale nodeinfos/positions stay in xmit queue 2021-02-11 18:19:43 +08:00
Kevin Hester
b45d633a34 Merge pull request #680 from geeksville/dev
Dev
2021-02-10 16:33:02 +08:00
Kevin Hester
fdfe62edf0 updates to work with new protobuf names 2021-02-10 16:18:41 +08:00
Kevin Hester
8e8170b667 Merge remote-tracking branch 'root/master' into dev 2021-02-08 18:03:35 +08:00
Kevin Hester
2fa38c7dc4 update protos 2021-02-08 17:57:45 +08:00
Kevin Hester
58bb7169a0 use nodenum as the default nodeid 2021-02-08 10:15:02 +08:00
Kevin Hester
f7beec4728 Merge pull request #678 from geeksville/dev
misc bugs
2021-02-07 11:00:09 +08:00
Kevin Hester
ccf3450864 make CI build steps more atomic for easier reporting 2021-02-07 10:49:14 +08:00
Kevin Hester
86553a4fc9 add libs needed for linux build on CI server 2021-02-07 10:44:31 +08:00
Kevin Hester
55349ea570 1.1.42 2021-02-07 10:27:04 +08:00
Kevin Hester
486b03e985 bug 677. Don't assert fail for missing interfaces, instead return
nak packet to clients.
2021-02-07 10:26:11 +08:00
Kevin Hester
ccb232b6ac make route errors a more general 'message delivery failure' reason
in support of https://github.com/meshtastic/Meshtastic-device/issues/677
2021-02-07 09:46:35 +08:00
Kevin Hester
e7af338c31 add trademark 2021-02-04 15:46:57 +08:00
Kevin Hester
9069e5b33e no longer need to set num jobs, because my computer is better 2021-02-04 11:51:12 +08:00
Jm Casler
399fbc5d65 Merge pull request #665 from mc-hamster/master
#664 - Blink the LED when we enter disablePin() and #654 - ExternalNotificationPlugin
2021-01-30 17:28:28 -08:00
Jm Casler
48b38ed94b Update documentation 2021-01-30 17:21:54 -08:00
Jm Casler
c0444ef16f Merge pull request #64 from meshtastic/master
Update from meshtastic main
2021-01-30 17:20:56 -08:00
Kevin Hester
1719a8e764 Merge pull request #667 from geeksville/dev
kevins misc fixes
2021-01-31 09:18:05 +08:00
Jm Casler
092af0f9f9 Update to doc to add info on the bell character 2021-01-30 09:50:19 -08:00
Jm Casler
133a7ff166 Added more notes. 2021-01-30 09:46:26 -08:00
Jm Casler
5df08410e7 Create SerialPlugin.md 2021-01-30 09:39:40 -08:00
Jm
9f9787bc03 Merge branch 'master' of https://github.com/mc-hamster/Meshtastic-device 2021-01-30 09:36:28 -08:00
Jm
7129a19f35 #654 Add reference to documentation 2021-01-30 09:36:17 -08:00
Jm Casler
f45ffc8773 Rename documentation 2021-01-30 09:32:44 -08:00
Jm Casler
3162f74945 Create External Notification documentation. 2021-01-30 09:32:04 -08:00
Jm
6cef3e41e7 Update comments and use radioConfig 2021-01-30 09:17:40 -08:00
Kevin Hester
c0e2ec8dec 1.1.34 2021-01-30 21:23:03 +08:00
Jm
aee81c8dcd #654 - Add comments on what the config does 2021-01-29 19:35:03 -08:00
Jm
9e736ab0d7 #654 Fix gpio initilization 2021-01-28 23:16:10 -08:00
Jm
85752b0fc7 #654 - Fixed problem with class namespaces. 2021-01-28 23:02:00 -08:00
Kevin Hester
c6f34c59b4 Update to logo by @thepoweroftwo 2021-01-29 13:26:08 +08:00
Kevin Hester
7f07725840 make improperly sized AES128/256 keys non fatal, instead pad with zeros
Fixes rebooting the board if someone specifies an invalid key
2021-01-29 10:15:48 +08:00
Kevin Hester
c81d090464 someone added storerequest app? 2021-01-29 10:14:46 +08:00
Jm Casler
c524732849 #654 - Non blocking call to toggle the output. 2021-01-27 21:35:07 -08:00
Jm Casler
5e303f8a1f #654 - Work in progress. Needs testing, non-blocking alert and integration with configuration 2021-01-27 21:20:18 -08:00
Jm
2246564279 #654 - Small changes. 2021-01-27 20:06:39 -08:00
Jm
eff0c1fe89 #654 - Partial work for the LED/Speaker.
Framework is done. Just need to blink a few things and update protobufs.
2021-01-27 19:18:16 -08:00
Jm Casler
ad322476d2 Merge branch 'master' into master 2021-01-27 18:56:48 -08:00
Jm
2561742683 #664 - Blink the LED when we enter disablePin() 2021-01-27 18:56:09 -08:00
Kevin Hester
fa9e31fe03 Merge pull request #663 from geeksville/eink
back in the saddle - reading github and slack next
2021-01-28 10:38:46 +08:00
Kevin Hester
3ac5b045c4 Merge remote-tracking branch 'root/master' into eink 2021-01-28 10:31:56 +08:00
Kevin Hester
6a593e01e1 notes on eink1.0 2021-01-28 10:30:24 +08:00
Kevin Hester
6f6dd2291e fix typo 2021-01-28 10:30:00 +08:00
Kevin Hester
2b4ddc07f5 Merge pull request #651 from android606/log-tx-failure
Set critical error and reboot when radio fails to generate Tx IRQ - fixes #138
2021-01-27 18:05:56 +08:00
Kevin Hester
63c650c33e Merge branch 'master' into log-tx-failure 2021-01-27 17:54:06 +08:00
Kevin Hester
dc29161f37 Merge pull request #659 from IZ1IVA/patch-4
Update radio-settings.md
2021-01-27 17:52:27 +08:00
Jm Casler
8a6fdafc79 Merge branch 'master' into patch-4 2021-01-26 22:29:41 -08:00
Jm Casler
ea40bd991c Merge pull request #660 from mc-hamster/master
Update version.properties to 1.1.33
2021-01-25 17:12:32 -08:00
Jm Casler
e19dd46f0f Merge branch 'master' into master 2021-01-25 17:05:52 -08:00
Jm
532b06c280 Update version.properties to 1.1.33 2021-01-25 17:01:47 -08:00
IZ1IVA
a8480d1eaf Update radio-settings.md
Added data-rates
2021-01-25 16:11:24 +01:00
Jm Casler
0cf7aaffff Merge pull request #658 from sachaw/master
Another missing header
2021-01-24 18:06:14 -08:00
Sacha Weatherstone
e2e1819ef1 Merge branch 'master' of https://github.com/sachaw/meshtastic-device 2021-01-24 10:49:23 +11:00
Sacha Weatherstone
31b89e2932 Another header required 2021-01-24 10:48:48 +11:00
Jm Casler
a021ff7eb8 Merge pull request #656 from sachaw/master
fix cors for API requsts & fix spiffs url
2021-01-22 23:36:27 -08:00
Jm Casler
bb5d0fac90 Merge branch 'master' into master 2021-01-22 23:26:53 -08:00
Jm Casler
df5ed64514 Merge pull request #655 from mc-hamster/master
Fix for #650 - build-all.sh will fail
2021-01-22 23:26:39 -08:00
Sacha Weatherstone
9db5f9ff67 fix cors for API requsts & fix spiffs url 2021-01-23 17:42:15 +11:00
Jm
ca83a78e13 Fix for #650 - build-all.sh will fail 2021-01-22 19:50:12 -08:00
Jm Casler
13eef9a309 Merge pull request #653 from mc-hamster/master
Update comments in SerialPlugin
2021-01-20 21:42:12 -08:00
Jm Casler
2a8ac2c0c6 Merge branch 'master' into master 2021-01-20 19:04:46 -08:00
Jm Casler
c97342db99 Merge pull request #63 from mc-hamster/serial
Update comments on SerialPlugin
2021-01-20 19:03:44 -08:00
Jm Casler
d7b2a0ed79 Merge pull request #62 from mc-hamster/master
update serial from master
2021-01-20 19:02:58 -08:00
Jm
af0a1b5db5 Update comments of SerialPlugin 2021-01-20 19:02:08 -08:00
Jm Casler
9cf030d587 Merge pull request #652 from mc-hamster/master
#649 More webserver refactoring
2021-01-19 21:52:56 -08:00
Jm
c04d70d5e5 Merge branch 'master' of https://github.com/mc-hamster/Meshtastic-device 2021-01-19 21:38:30 -08:00
Jm
2a47819fd6 #649 More webserver refactoring 2021-01-19 21:38:17 -08:00
Jm Casler
4516c8f9b5 Merge pull request #61 from meshtastic/master
Merge pull request #648 from mc-hamster/master
2021-01-19 21:27:24 -08:00
Jm
e4fdf26dc7 #649 - First pass on the refactoring of the webserver 2021-01-19 21:26:23 -08:00
Andrew Mark
dd511588a2 Oops, let's only try to reboot ESP32 when there's an ESP32 2021-01-19 20:13:19 -08:00
Andrew Mark
79dad8ec8c Set critical error and reboot when radio fails to generate tx IRQ 2021-01-19 18:21:54 -08:00
Jm Casler
39d14fedc2 Merge pull request #648 from mc-hamster/master
#647 - Fix for admin mode being forced on boards without hardware pullup
2021-01-18 10:56:13 -08:00
Jm Casler
1da38fc748 Merge branch 'master' into master 2021-01-18 10:44:38 -08:00
Jm
b5f50efdcd #647 - Fix for admin mode being forced on boards without hardware pullup 2021-01-18 10:43:15 -08:00
Jm Casler
046e691d4e Merge pull request #645 from mc-hamster/master
#635 Added web_request_count and fixed printf of psram
2021-01-17 17:26:56 -08:00
Jm Casler
e72531b090 Merge pull request #60 from mc-hamster/master
Update Serial
2021-01-17 15:41:21 -08:00
Jm
81e320c9cf Merge branch 'master' of https://github.com/mc-hamster/Meshtastic-device 2021-01-17 15:40:27 -08:00
Jm
fa8cc74141 Update to Serial Plugin to make it easy to override the device configuration 2021-01-17 15:40:25 -08:00
Jm Casler
c7d9ff7cc0 Merge branch 'master' into master 2021-01-17 10:36:54 -08:00
Jm
8704a9d08f Merge branch 'master' of https://github.com/mc-hamster/Meshtastic-device 2021-01-17 10:30:40 -08:00
Jm
c0d27e2ce9 #635 Added web_request_count and fixed printf of psram 2021-01-17 10:30:34 -08:00
Jm Casler
84b9028ecb Merge pull request #642 from mc-hamster/master
#635 - Added memory usage statistics
2021-01-17 04:52:03 -08:00
Jm Casler
4fda7098c0 Merge branch 'master' into master 2021-01-17 04:44:42 -08:00
Jm
8e8264efb0 #635 - Added memory usage statistics 2021-01-17 00:29:29 -08:00
Jm Casler
54e780a6ca Merge pull request #641 from mc-hamster/master
Update generated protobuf; Update serial plugin; #615 - Configure charge current
2021-01-17 00:18:33 -08:00
Jm
125eb2b784 Fix for build fail on NRF 2021-01-17 00:11:26 -08:00
Jm Casler
6ea9cdc83b Merge pull request #59 from mc-hamster/master
Update serial plugin with new protobufs
2021-01-17 00:03:13 -08:00
Jm
c0711fde69 #615 Allow rate for tbeam battery charger to be configurable 2021-01-16 23:10:08 -08:00
Jm
20b8d2c4a5 Set the port to PortNum_SERIAL_APP 2021-01-16 22:41:33 -08:00
Jm
73ae151971 Added serialplugin_timeout to SerialPlugin 2021-01-16 22:39:28 -08:00
Jm Casler
f4806c9dd7 Merge branch 'serial' into master 2021-01-16 22:35:56 -08:00
Jm
79532210e8 SerialPlugin - Update comments with better instructions 2021-01-16 22:31:56 -08:00
Jm
d7f26493a5 Update to SerialPlugin to take advantage of the configs 2021-01-16 22:27:33 -08:00
Jm
b9d025dd58 Missed adding the proto file 2021-01-16 20:53:45 -08:00
Jm
f435086a5a Update generated protobufs 2021-01-16 20:50:58 -08:00
Jm
3dcdf372d7 add remote update 2021-01-16 20:12:31 -08:00
Jm
cd84f2867c Updated submodule proto 2021-01-16 20:00:37 -08:00
Jm
cafe00e463 Update of serialplugin 2021-01-16 19:40:47 -08:00
Kevin Hester
fd9ffbbb88 fix charging indication for dumb battery sensors 2021-01-16 13:05:33 +08:00
Kevin Hester
d1be7cf142 improve hardfault handler 2021-01-16 12:55:51 +08:00
Kevin Hester
d1f0be215b eink leds seem a bit busted 2021-01-16 11:42:06 +08:00
Kevin Hester
3a2c17998e turn off nrf52 ble while debugging 2021-01-16 11:41:50 +08:00
Kevin Hester
a0dd051511 turn off eink backlight 2021-01-16 11:41:28 +08:00
Kevin Hester
4faff3ec6f cope with bogus NMEA gps 2021-01-16 11:41:18 +08:00
Kevin Hester
f110225173 Update variant file and qspi flash programming settings for new ttgoeink
Note: bin/qspi-flash-test.sh contains a script you can use for basic
bench programming and testing of the serial spi flash over SWD
2021-01-16 10:34:46 +08:00
Kevin Hester
2684257e7e update protobufs 2021-01-16 09:59:43 +08:00
Kevin Hester
51fb1021df Merge remote-tracking branch 'root/master' into eink
# Conflicts:
#	platformio.ini
2021-01-16 09:59:08 +08:00
Kevin Hester
51d0d0d6c5 begin new ttgo eink board 2021-01-16 09:57:55 +08:00
Kevin Hester
047df76373 move rev 0.1 of the ttgo eink board to eink0.1 2021-01-16 09:55:06 +08:00
Jm
6da4e30215 Merge branch 'serial' of https://github.com/mc-hamster/Meshtastic-device into serial 2021-01-14 22:38:53 -08:00
Jm
dbf0569e29 Update comments for serial pins recommended by @ryguy 2021-01-14 22:36:51 -08:00
Jm Casler
18220b88b3 Merge pull request #58 from mc-hamster/master
update serial from master
2021-01-14 22:36:17 -08:00
Jm Casler
665da2fb00 Merge pull request #57 from meshtastic/master
Merge pull request #633 from mc-hamster/master
2021-01-14 22:34:01 -08:00
Jm Casler
57ffe6622d Merge pull request #56 from mc-hamster/master
update serial from master
2021-01-14 22:25:20 -08:00
Jm Casler
485fec9649 Merge pull request #633 from mc-hamster/master
Fix for #632 (move web server to osthread) and #611 (move airtime to osthread)
2021-01-14 22:06:18 -08:00
Jm
bd85736226 SerialPlugin - Exclude working on non esp32 platforms. 2021-01-14 21:59:26 -08:00
Jm
4ec8986934 Fixed switching to uint32 from 16 for airtime in json report 2021-01-14 21:45:41 -08:00
Jm Casler
b963216764 Merge pull request #55 from mc-hamster/serial
Update for SerialPlugin and Airtime
2021-01-14 21:43:28 -08:00
Jm Casler
813fd95bc8 Merge pull request #54 from geeksville/mc-master
fix @mc-hamster build to work on linux/nrf52
2021-01-14 21:33:27 -08:00
Kevin Hester
3598c91c29 fix @mc-hamster build to work on linux/nrf52 2021-01-15 13:30:40 +08:00
Jm
507cd1dd20 #639 - Move from counting seconds to milliseconds 2021-01-14 18:40:18 -08:00
Jm
e39506824d Added more comments 2021-01-14 18:08:23 -08:00
Jm
f68a31ab28 serialplugin - ability to configure ACK 2021-01-13 23:21:55 -08:00
Jm
b1181deb58 serialplugin - Added my todo list 2021-01-13 23:02:13 -08:00
Jm
89b32dd7ee Fix comments in serial plugin 2021-01-13 22:51:36 -08:00
Jm
c54e87f9a2 Update SerialPlugin.cpp
Added documentation.
2021-01-13 22:50:02 -08:00
Jm
eee7e1de57 Update to serial plugin 2021-01-13 22:39:11 -08:00
Jm
3c60df1565 Partial work on the SerialPlugin 2021-01-13 20:22:59 -08:00
Jm Casler
a827017bd2 Merge pull request #53 from crossan007/master
add power statistics for #635
2021-01-11 17:54:27 -08:00
Charles Crossan
95c502c658 fix typo 2021-01-11 20:30:02 -05:00
Charles Crossan
0f573901d5 didn't need power.h 2021-01-11 20:28:09 -05:00
Charles Crossan
fdc9bf5783 add power statistics for #635 2021-01-11 20:25:02 -05:00
Jm Casler
37e0f9a325 Merge pull request #52 from mc-hamster/osthread
maybe this will fix the linux build?
2021-01-09 19:32:40 -08:00
Jm
0c06d8db3c maybe this will fix the linux build? 2021-01-09 19:31:16 -08:00
Jm Casler
0be4bbb369 Merge pull request #51 from mc-hamster/osthread
Does this fix the linux build?
2021-01-09 19:17:45 -08:00
Jm
f02ab88393 Does this fix the linux build? 2021-01-09 19:15:10 -08:00
Jm Casler
c9d4de8808 Merge pull request #50 from mc-hamster/osthread
fixed in linux now?
2021-01-09 19:07:16 -08:00
Jm
adb912b665 fixed in linux now? 2021-01-09 19:06:34 -08:00
Jm Casler
3f5da1e03e Merge pull request #49 from mc-hamster/osthread
another linux fix?
2021-01-09 18:37:04 -08:00
Jm
0a40d920e3 Update BluetoothUtil.cpp
yet another linux fix?
2021-01-09 18:36:23 -08:00
Jm Casler
39311f1e40 Merge pull request #48 from mc-hamster/osthread
is linux fixed now?
2021-01-09 18:27:32 -08:00
Jm
9cd24a5646 is linux fixed now? 2021-01-09 18:27:03 -08:00
Jm Casler
1c0efde315 Merge pull request #47 from mc-hamster/osthread
is linux fixed now?
2021-01-09 18:09:57 -08:00
Jm
c82905bbdd is linux fixed now? 2021-01-09 18:09:16 -08:00
Jm Casler
275eace968 Merge pull request #46 from mc-hamster/osthread
more linux fixes
2021-01-09 17:52:09 -08:00
Jm
5688c8b81e more linux build fixes 2021-01-09 17:50:58 -08:00
Jm
8b2798abd5 Added a few commented out default_envs 2021-01-09 17:44:20 -08:00
Jm Casler
6d977923b6 Merge pull request #45 from mc-hamster/osthread
more fixes for linux build
2021-01-09 17:34:34 -08:00
Jm
52dacaed37 more fixes for linux build 2021-01-09 17:30:36 -08:00
Jm Casler
7a381eaea1 Merge pull request #44 from mc-hamster/osthread
Fix for breaking the linux build
2021-01-09 13:47:43 -08:00
Jm
69391e186b Fix for breaking the linux build 2021-01-09 13:47:10 -08:00
Jm Casler
06f8beaa17 Merge pull request #43 from mc-hamster/osthread
fix for building on NRF platform.
2021-01-08 23:00:36 -08:00
Jm
3798f4ca5b fix for building on NRF platform. 2021-01-08 22:59:37 -08:00
Jm Casler
4fd243a6e4 Merge pull request #42 from mc-hamster/osthread
Moving web server and airtime to osthread model. Moved web server to mesh/wifi
2021-01-08 22:29:45 -08:00
Jm
d458f673be Web server is now treaded and moved to mesh/wifi/* 2021-01-08 22:25:44 -08:00
Jm
cfcb00b943 that's enough for tonight. web server is in its own thread, needs to be further optimized but it works enough. next is to refactor. 2021-01-08 20:43:51 -08:00
Jm
977e47d109 partial work 2021-01-08 20:06:11 -08:00
Jm Casler
cfeb40f36d Merge pull request #41 from mc-hamster/master
Updated osthread branch from master
2021-01-08 19:57:19 -08:00
Jm Casler
4fcc3ac1de Merge pull request #40 from meshtastic/master
Updated my master from head
2021-01-08 19:54:57 -08:00
Kevin Hester
f4afa6931b Merge pull request #630 from geeksville/dev
Dev
2021-01-08 13:38:23 +08:00
Kevin Hester
71be71d63d add note about how to send messages thanks @rw-w for the question 2021-01-08 13:31:28 +08:00
Kevin Hester
de9f7e6c39 update protos 2021-01-08 13:21:14 +08:00
Kevin Hester
7c8db2b501 Merge remote-tracking branch 'root/master' into dev
# Conflicts:
#	docs/software/plugin-api.md
2021-01-08 13:20:28 +08:00
Kevin Hester
cd653f9434 Merge pull request #629 from ry-w/patch-1
Update plugin-api.md
2021-01-08 13:19:03 +08:00
Kevin Hester
74bc05936d Merge remote-tracking branch 'root/master' into dev 2021-01-08 13:16:27 +08:00
Kevin Hester
7aacfd66ef add assertIsSetup() and use it from OSThread constructor
fixes nasty bug @mc-hamster discovered with plugin order of operations
2021-01-08 13:15:49 +08:00
Kevin Hester
3636b87db0 formatting 2021-01-08 11:52:43 +08:00
ry-w
d6bd328576 Update plugin-api.md
Couple of clarifications, and fixes to minor errors.
2021-01-06 14:24:51 -08:00
Jm
0af5b225c4 intermediate 2021-01-05 23:32:33 -08:00
Jm
f7dcef39ce intermediate work 2021-01-05 23:21:14 -08:00
Kevin Hester
07042178d2 Merge pull request #627 from mc-hamster/master
Include instrusctions for real time backtrace decoding.
2021-01-06 14:55:46 +08:00
Kevin Hester
243878f2a0 Merge branch 'master' into master 2021-01-06 14:39:57 +08:00
Kevin Hester
d3f8a76cce Merge pull request #625 from geeksville/dev
fix #624 - update battery level and current time on mynodeinfo
2021-01-06 14:39:42 +08:00
Kevin Hester
20131a51a2 Merge branch 'master' into dev 2021-01-06 14:31:11 +08:00
Kevin Hester
1c9a369774 Merge branch 'master' into master 2021-01-06 13:26:30 +08:00
Kevin Hester
dcb426f58f Merge pull request #628 from IZ1IVA/patch-3
Update radio-settings.md
2021-01-06 13:23:53 +08:00
Jm Casler
35bcb5297a Merge pull request #38 from mc-hamster/master
update from master to osthread
2021-01-05 19:04:15 -08:00
Jm
84e3d7c276 Test fixes for Tobiases 2021-01-05 18:57:03 -08:00
IZ1IVA
9b03f0ac8e Update radio-settings.md
Fixed frequency table non showing
2021-01-05 12:13:18 +01:00
Jm Casler
eb402809e2 Merge branch 'master' into master 2021-01-04 22:16:07 -08:00
Jm
e9c9e40624 Include instrusctions for real time backtrace decoding. 2021-01-04 22:15:19 -08:00
Kevin Hester
01eed97b91 Merge branch 'master' into dev 2021-01-04 10:00:38 +08:00
Kevin Hester
94a47dba7d fix #624 - update battery level and current time on mynodeinfo 2021-01-04 09:59:53 +08:00
Jm
8295b88d96 Checking in work so I don't lose it. Nothing's broke with the build. 2021-01-01 21:20:34 -08:00
Jm
925829dc58 Partial work to migrate to OSThread model 2021-01-01 12:31:46 -08:00
91 changed files with 2776 additions and 954 deletions

View File

@@ -22,5 +22,15 @@ jobs:
- name: Install extra python tools - name: Install extra python tools
run: | run: |
pip install -U adafruit-nrfutil pip install -U adafruit-nrfutil
- name: Build - name: Install libs needed for linux build
run: platformio run -e tbeam -e heltec -e lora-relay-v1 -e linux run: |
sudo apt install -y libpsocksxx-dev
- name: Build for tbeam
run: platformio run -e tbeam
- name: Build for heltec
run: platformio run -e heltec
- name: Build for lora-relay-v1
run: platformio run -e lora-relay-v1
- name: Build for linux
run: platformio run -e linux

3
.gitmodules vendored
View File

@@ -4,3 +4,6 @@
[submodule "sdk-nrfxlib"] [submodule "sdk-nrfxlib"]
path = sdk-nrfxlib path = sdk-nrfxlib
url = https://github.com/nrfconnect/sdk-nrfxlib.git url = https://github.com/nrfconnect/sdk-nrfxlib.git
[submodule "design"]
path = design
url = https://github.com/meshtastic/meshtastic-design.git

View File

@@ -4,7 +4,7 @@ This is the device side code for the [meshtastic.org](https://www.meshtastic.org
![Continuous Integration](https://github.com/meshtastic/Meshtastic-esp32/workflows/Continuous%20Integration/badge.svg) ![Continuous Integration](https://github.com/meshtastic/Meshtastic-esp32/workflows/Continuous%20Integration/badge.svg)
Meshtastic is a project that lets you use Meshtastic® is a project that lets you use
inexpensive GPS mesh radios as an extensible, super long battery life mesh GPS communicator. These radios are great for hiking, skiing, paragliding - inexpensive GPS mesh radios as an extensible, super long battery life mesh GPS communicator. These radios are great for hiking, skiing, paragliding -
essentially any hobby where you don't have reliable internet access. Each member of your private mesh can always see the location and distance of all other essentially any hobby where you don't have reliable internet access. Each member of your private mesh can always see the location and distance of all other
members and any text messages sent to your group chat. members and any text messages sent to your group chat.

View File

@@ -14,8 +14,6 @@ BOARDS_ESP32="tlora-v2 tlora-v1 tlora-v2-1-1.6 tbeam heltec tbeam0.7"
# FIXME note nrf52840dk build is for some reason only generating a BIN file but not a HEX file nrf52840dk-geeksville is fine # FIXME note nrf52840dk build is for some reason only generating a BIN file but not a HEX file nrf52840dk-geeksville is fine
BOARDS_NRF52="lora-relay-v1" BOARDS_NRF52="lora-relay-v1"
NUM_JOBS=2
OUTDIR=release/latest OUTDIR=release/latest
# We keep all old builds (and their map files in the archive dir) # We keep all old builds (and their map files in the archive dir)
@@ -51,7 +49,7 @@ function do_build() {
basename=universal/firmware-$BOARD-$VERSION basename=universal/firmware-$BOARD-$VERSION
fi fi
pio run --jobs $NUM_JOBS --environment $BOARD # -v pio run --environment $BOARD # -v
SRCELF=.pio/build/$BOARD/firmware.elf SRCELF=.pio/build/$BOARD/firmware.elf
cp $SRCELF $OUTDIR/elfs/$basename.elf cp $SRCELF $OUTDIR/elfs/$basename.elf

19
bin/gen-images.sh Executable file
View File

@@ -0,0 +1,19 @@
set -e
# regen the design bins first
cd design
bin/generate-pngs.sh
cd ..
# assumes 50 wide, 28 high
convert design/logo/png/Mesh_Logo_Black_Small.png -background white -alpha Background src/graphics/img/icon.xbm
inkscape --batch-process -o images/compass.png -w 48 -h 48 images/location_searching-24px.svg
convert compass.png -background white -alpha Background src/graphics/img/compass.xbm
inkscape --batch-process -o images/face.png -w 13 -h 13 images/face-24px.svg
inkscape --batch-process -o images/pin.png -w 13 -h 13 images/room-24px.svg
convert pin.png -background white -alpha Background src/graphics/img/pin.xbm

View File

@@ -1,6 +1,6 @@
# You probably don't need this - it is a basic test of the serial flash on the TTGO eink board # You probably don't need this - it is a basic test of the serial flash on the TTGO eink board
nrfjprog -qspiini nrf52/ttgo_eink_qpsi.ini --qspieraseall nrfjprog --qspiini nrf52/ttgo_eink_qpsi.ini --qspieraseall
nrfjprog --qspiini nrf52/ttgo_eink_qpsi.ini --memwr 0x12000000 --val 0xdeadbeef --verify nrfjprog --qspiini nrf52/ttgo_eink_qpsi.ini --memwr 0x12000000 --val 0xdeadbeef --verify
nrfjprog --qspiini nrf52/ttgo_eink_qpsi.ini --readqspi spi.hex nrfjprog --qspiini nrf52/ttgo_eink_qpsi.ini --readqspi spi.hex
objdump -s spi.hex | less objdump -s spi.hex | less

61
boards/eink0.1.json Normal file
View File

@@ -0,0 +1,61 @@
{
"build": {
"arduino": {
"ldscript": "nrf52840_s140_v6.ld"
},
"core": "nRF5",
"cpu": "cortex-m4",
"extra_flags": "-DARDUINO_NRF52840_TTGO_EINK -DNRF52840_XXAA",
"f_cpu": "64000000L",
"hwids": [
[
"0x239A",
"0x4405"
]
],
"usb_product": "TTGO_eink",
"mcu": "nrf52840",
"variant": "eink0.1",
"variants_dir": "variants",
"bsp": {
"name": "adafruit"
},
"softdevice": {
"sd_flags": "-DS140",
"sd_name": "s140",
"sd_version": "6.1.1",
"sd_fwid": "0x00B6"
},
"bootloader": {
"settings_addr": "0xFF000"
}
},
"connectivity": [
"bluetooth"
],
"debug": {
"jlink_device": "nRF52840_xxAA",
"onboard_tools": [
"jlink"
],
"svd_path": "nrf52840.svd"
},
"frameworks": [
"arduino"
],
"name": "TTGO eink (Adafruit BSP)",
"upload": {
"maximum_ram_size": 248832,
"maximum_size": 815104,
"require_upload_port": true,
"speed": 115200,
"protocol": "jlink",
"protocols": [
"jlink",
"nrfjprog",
"stlink"
]
},
"url": "FIXME",
"vendor": "TTGO"
}

View File

Before

Width:  |  Height:  |  Size: 532 B

After

Width:  |  Height:  |  Size: 532 B

View File

Before

Width:  |  Height:  |  Size: 442 B

After

Width:  |  Height:  |  Size: 442 B

View File

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

1
design Submodule

Submodule design added at 73ba05ceef

View File

@@ -1,13 +1,13 @@
# What is Meshtastic? # What is Meshtastic?
Meshtastic is a project that lets you use Meshtastic® is a project that lets you use
inexpensive (\$30 ish) GPS radios as an extensible, long battery life, secure, mesh GPS communicator. These radios are great for hiking, skiing, paragliding - essentially any hobby where you don't have reliable internet access. Each member of your private mesh can always see the location and distance of all other members and any text messages sent to your group chat. inexpensive (\$30 ish) GPS radios as an extensible, long battery life, secure, mesh GPS communicator. These radios are great for hiking, skiing, paragliding - essentially any hobby where you don't have reliable internet access. Each member of your private mesh can always see the location and distance of all other members and any text messages sent to your group chat.
The radios automatically create a mesh to forward packets as needed, so everyone in the group can receive messages from even the furthest member. The radios will optionally work with your phone, but no phone is required. The radios automatically create a mesh to forward packets as needed, so everyone in the group can receive messages from even the furthest member. The radios will optionally work with your phone, but no phone is required.
Note: Questions after reading this? See our new [forum](https://meshtastic.discourse.group/). Note: Questions after reading this? See our new [forum](https://meshtastic.discourse.group/).
### Uses ## Uses
- Outdoor sports where cellular coverage is limited. (Hiking, Skiing, Boating, Paragliding, Gliders etc..) - Outdoor sports where cellular coverage is limited. (Hiking, Skiing, Boating, Paragliding, Gliders etc..)
- Applications where closed source GPS communicators just won't cut it (it is easy to add features for glider pilots etc...) - Applications where closed source GPS communicators just won't cut it (it is easy to add features for glider pilots etc...)
@@ -17,7 +17,7 @@ Note: Questions after reading this? See our new [forum](https://meshtastic.disco
[![Youtube video demo](desk-video-screenshot.png)](https://www.youtube.com/watch?v=WlNbMbVZlHI "Meshtastic early demo") [![Youtube video demo](desk-video-screenshot.png)](https://www.youtube.com/watch?v=WlNbMbVZlHI "Meshtastic early demo")
### Features ## Features
Not all of these features are fully implemented yet - see **important** disclaimers below. But they should be in by the time we decide to call this project beta (three months?) Not all of these features are fully implemented yet - see **important** disclaimers below. But they should be in by the time we decide to call this project beta (three months?)

View File

@@ -2,7 +2,7 @@
We use the same channel maps as LoRaWAN (though this is not LoRaWAN). We use the same channel maps as LoRaWAN (though this is not LoRaWAN).
![freq table](images/LoRa-Frequency-Bands.jpg) ![freq table](/images/LoRa-Frequency-Bands.jpg)
See [this site](https://www.rfwireless-world.com/Tutorials/LoRa-channels-list.html) for more information. See [this site](https://www.rfwireless-world.com/Tutorials/LoRa-channels-list.html) for more information.
@@ -28,3 +28,14 @@ The maximum output power for North America is +30 dBm ERP.
The band is from 902 to 928 MHz. It mentions channel number and its respective channel frequency. All the 13 channels are separated by 2.16 MHz with respect to the adjacent channels. The band is from 902 to 928 MHz. It mentions channel number and its respective channel frequency. All the 13 channels are separated by 2.16 MHz with respect to the adjacent channels.
Channel zero starts at 903.08 MHz center frequency. 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:
| 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) |

View File

@@ -2,6 +2,27 @@
You probably don't care about this section - skip to the next one. You probably don't care about this section - skip to the next one.
eink:
* new battery level sensing
* measure current draw
* DONE: fix backlight
* USB is busted because of power enable mode?
* OHH BME280! THAT IS GREAT!
* make new screen work, ask for datasheet
* say I think you could ship this
* leds seem busted
* usb doesn't stay connected
* check GPS works
* check GPS fast locking
* send email about variants & faster flash programming - https://github.com/geeksville/Meshtastic-esp32/commit/f110225173a77326aac029321cdb6491bfa640f6
* send PR for bootloader
* fix nrf52 time/date
* send new master bin file
* send email about low power mode problems
* support new flash chip in appload, possibly use low power mode
* swbug! stuck busy tx occurred!
For app cleanup: For app cleanup:
* use structured logging to kep logs in ram. Also send logs as packets to api clients * use structured logging to kep logs in ram. Also send logs as packets to api clients

View File

@@ -39,9 +39,20 @@ cd Meshtastic-device
## Decoding stack traces ## Decoding stack traces
### Option 1
If you get a crash, you can decode the addresses from the `Backtrace:` line: If you get a crash, you can decode the addresses from the `Backtrace:` line:
1. Save the `Backtrace: 0x....` line to a file, e.g., `backtrace.txt`. 1. Save the `Backtrace: 0x....` line to a file, e.g., `backtrace.txt`.
2. Run `bin/exception_decoder.py backtrace.txt` (this uses symbols from the 2. Run `bin/exception_decoder.py backtrace.txt` (this uses symbols from the
last `firmware.elf`, so you must be running the same binary that's still in last `firmware.elf`, so you must be running the same binary that's still in
your `.pio/build` directory). your `.pio/build` directory).
### Option 2
You can run the exception decoder to monitor the serial output and decode backtraces in real time.
1. From within PlatformIO, open a new terminal.
2. At the the terminal, enter:
`pio device monitor --port /dev/cu.SLAB_USBtoUART -f esp32_exception_decoder`
Replace the value of port with the location of your serial port.

View File

@@ -13,7 +13,7 @@ the project developers are not cryptography experts. Therefore we ask two things
Based on comments from reviewers (see below), here's some tips for usage of these radios. So you can know the level of protection offered: Based on comments from reviewers (see below), here's some tips for usage of these radios. So you can know the level of protection offered:
* It is pretty likely that the AES256 security is implemented 'correctly' and an observer will not be able to decode your messages. * It is pretty likely that the AES256 security is implemented 'correctly' and an observer will not be able to decode your messages.
* Warning: If an attacker is able to get one of the radios in their position, they could either a) extract the channel key from that device or b) use that radio to listen to new communications. * Warning: If an attacker is able to get one of the radios in their posession, they could either a) extract the channel key from that device or b) use that radio to listen to new communications.
* Warning: If an attacker is able to get the "Channel QR code/URL" that you share with others - that attacker could then be able to read any messages sent on the channel (either tomorrow or in the past - if they kept a raw copy of those broadcast packets) * Warning: If an attacker is able to get the "Channel QR code/URL" that you share with others - that attacker could then be able to read any messages sent on the channel (either tomorrow or in the past - if they kept a raw copy of those broadcast packets)
Possible future areas of work (if there is enough interest - post in our [forum](https://meshtastic.discourse.group) if you want this): Possible future areas of work (if there is enough interest - post in our [forum](https://meshtastic.discourse.group) if you want this):

View File

@@ -15,10 +15,10 @@ Packets can be sent/received either as raw binary structures or as [Protobufs](h
The relevant bits of the class heirarchy are as follows The relevant bits of the class heirarchy are as follows
* [MeshPlugin](/src/mesh/MeshPlugin.h) (in src/mesh/MeshPlugin.h) - you probably don't want to use this baseclass directly * [MeshPlugin](/src/mesh/MeshPlugin.h) (in src/mesh/MeshPlugin.h) - you probably don't want to use this baseclass directly
* [SinglePortPlugin](/src/mesh/SinglePortPlugin.h) (in src/mesh/SinglePortPlugin.h) - for plugins that receive from a single port number (the normal case) * [SinglePortPlugin](/src/mesh/SinglePortPlugin.h) (in src/mesh/SinglePortPlugin.h) - for plugins that send/receive from a single port number (the normal case)
* [ProtobufPlugin](/src/mesh/ProtobufPlugin.h) (in src/mesh/ProtobufPlugin.h) - for plugins that are sending/receiving a single particular Protobuf type. Inherit from this if you are using protocol buffers in your plugin. * [ProtobufPlugin](/src/mesh/ProtobufPlugin.h) (in src/mesh/ProtobufPlugin.h) - for plugins that send/receive a single particular Protobuf type. Inherit from this if you are using protocol buffers in your plugin.
You will typically want to inherit from either SinglePortPlugin (if you are just sending raw bytes) or ProtobufPlugin (if you are sending protobufs). You'll implement your own handleReceived/handleReceivedProtobuf - probably based on the example code. You will typically want to inherit from either SinglePortPlugin (if you are just sending/receiving raw bytes) or ProtobufPlugin (if you are sending/receiving protobufs). You'll implement your own handleReceived/handleReceivedProtobuf - probably based on the example code.
If your plugin needs to perform any operations at startup you can override and implement the setup() method to run your code. If your plugin needs to perform any operations at startup you can override and implement the setup() method to run your code.
@@ -45,25 +45,30 @@ A number of [key services](/src/plugins) are implemented using the plugin API, t
The easiest way to get started is: The easiest way to get started is:
* [Build and install](build-instructions.md) the standard codebase from github. * [Build and install](build-instructions.md) the standard codebase from github.
* Copy [src/plugins/ReplyPlugin.*](/src/plugins/ReplyPlugin.cpp) into src/plugins/YourPlugin.*. Then change the port number from REPLY_APP to PRIVATE_APP. * Copy [src/plugins/ReplyPlugin.*](/src/plugins/ReplyPlugin.cpp) into src/plugins/YourPlugin.*. Then change the port number from *PortNum_REPLY_APP* to *PortNum_PRIVATE_APP*.
* Edit plugins/Plugins.cpp:setupPlugins() to add a call to create an instance of your plugin (see comment at head of that function)
* Rebuild with your new messaging goodness and install on the device * Rebuild with your new messaging goodness and install on the device
* Use the [meshtastic commandline tool](https://github.com/meshtastic/Meshtastic-python) to send a packet to your board "meshtastic --dest 1234 --ping" * Use the [meshtastic commandline tool](https://github.com/meshtastic/Meshtastic-python) to send a packet to your board, for example "*meshtastic --dest 1234 --sendping*", where *1234* is another mesh node to send the ping to.
## Threading ## Threading
It is very common that you would like your plugin to be invoked periodically. It is very common that you would like your plugin to be invoked periodically.
We use a crude/basic cooperative threading system to allow this on any of our supported platforms. Simply inherit from OSThread and implement runOnce(). See the OSThread [documentation](/src/concurrency/OSThread.h) for more details. For an example consumer of this API see RemoteHardwarePlugin::runOnce. We use a crude/basic cooperative threading system to allow this on any of our supported platforms. Simply inherit from OSThread and implement runOnce(). See the OSThread [documentation](/src/concurrency/OSThread.h) for more details. For an example consumer of this API see RemoteHardwarePlugin::runOnce.
## Sending messages
If you would like to proactively send messages (rather than just responding to them), just call service.sendToMesh(). For an example of this see [NodeInfoPlugin::sendOurNodeInfo(...)](/src/plugins/NodeInfoPlugin.cpp).
## Picking a port number ## Picking a port number
For any new 'apps' that run on the device or via sister apps on phones/PCs they should pick and use a unique 'portnum' for their application. For any new 'apps' that run on the device or via sister apps on phones/PCs they should pick and use a unique 'portnum' for their application.
If you are making a new app using meshtastic, please send in a pull request to add your 'portnum' to [the master list](https://github.com/meshtastic/Meshtastic-protobufs/blob/master/portnums.proto). PortNums should be assigned in the following range: If you are making a new app using meshtastic, please send in a pull request to add your 'portnum' to [the master list](https://github.com/meshtastic/Meshtastic-protobufs/blob/master/portnums.proto). PortNums should be assigned in the following range:
* 0-63 Core Meshtastic use, do not use for third party apps * **0-63** Core Meshtastic use; do not use for third party apps
* 64-127 Registered 3rd party apps, send in a pull request that adds a new entry to portnums.proto to register your application * **64-127** Registered 3rd party apps. Send in a pull request that adds a new entry to portnums.proto to register your application
* 256-511 Use one of these portnums for your private applications that you don't want to register publically * **256-511** Use one of these portnums for your private applications that you don't want to register publically
* 1024-66559 Are reserved for use by IP tunneling (see FIXME for more information) * **1024-66559** Are reserved for use by IP tunneling (see *FIXME* for more information)
All other values are reserved. All other values are reserved.

View File

@@ -0,0 +1,89 @@
# About
The ExternalNotification Plugin will allow you to connect a speaker, LED or other device to notify you when a message has been received from the mesh network.
# Configuration
These are the settings that can be configured.
ext_notification_plugin_enabled
Is the plugin enabled?
0 = Disabled (Default)
1 = Enabled
ext_notification_plugin_active
Is your external circuit triggered when our GPIO is low or high?
0 = Active Low (Default)
1 = Active High
ext_notification_plugin_alert_message
Do you want to be notified on an incoming message?
0 = Disabled (Default)
1 = Alert when a text message comes
ext_notification_plugin_alert_bell
Do you want to be notified on an incoming bell?
0 = Disabled (Default)
1 = Alert when the bell character is received
ext_notification_plugin_output
What GPIO is your external circuit attached?
GPIO of the output. (Default = 13)
ext_notification_plugin_output_ms
How long do you want us to trigger your external circuit?
Amount of time in ms for the alert. Default is 1000.
# Usage Notes
For basic usage, start with:
ext_notification_plugin_enabled = 1
ext_notification_plugin_alert_message = 1
Depending on how your external cirtcuit configured is configured, you may need to set the active state to true.
ext_notification_plugin_active = 1
## Alert Types
We support being alerted on two events:
1) Incoming Text Message
2) Incoming Text Message that contains the ascii bell character. At present, only the Python API can send an ascii bell character, but more support may be added in the future.
### Bell Character
The bell character is ASCII 0x07. Include 0x07 anywhere in the text message and with ext_notification_plugin_alert_bell enabled, we will issue an external notification.
# External Hardware
Be mindful of the max current sink and source of the esp32 GPIO. The easiest devices to interface with would be either an LED or Active Buzzer.
Ideas for external hardware:
* LED
* Active Buzzer
* Flame thrower
* Strobe Light
* Siren
# Known Problems
* This won't directly support an passive (normal) speaker as it does not generate any audio wave forms.
* This currently only supports the esp32. Other targets may be possible, I just don't have to test with.
* This plugin only monitors text messages. We won't trigger on any other packet types.
# Need more help?
Go to the Meshtastic Discourse Group if you have any questions or to share how you have used this.
https://meshtastic.discourse.group

View File

@@ -0,0 +1,40 @@
# About
A simple interface to send messages over the mesh network by sending strings
over a serial port.
Default is to use RX GPIO 16 and TX GPIO 17.
# Basic Usage:
1) Enable the plugin by setting serialplugin_enabled to 1.
2) Set the pins (serialplugin_rxd / serialplugin_rxd) for your preferred RX and TX GPIO pins.
On tbeam, recommend to use:
RXD 35
TXD 15
3) Set serialplugin_timeout to the amount of time to wait before we consider
your packet as "done".
4) (Optional) In SerialPlugin.h set the port to PortNum_TEXT_MESSAGE_APP if you want to
send messages to/from the general text message channel.
5) Connect to your device over the serial interface at 38400 8N1.
6) Send a packet up to 240 bytes in length. This will get relayed over the mesh network.
7) (Optional) Set serialplugin_echo to 1 and any message you send out will be echoed back
to your device.
# TODO (in this order):
* Define a verbose RX mode to report on mesh and packet infomration.
- This won't happen any time soon.
# Known Problems
* Until the plugin is initilized by the startup sequence, the TX pin is in a floating
state. Device connected to that pin may see this as "noise".
* Will not work on NRF and the Linux device targets.
# Need help?
Need help with this plugin? Post your question on the Meshtastic Discourse:
https://meshtastic.discourse.group

View File

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

View File

@@ -1,11 +0,0 @@
# using height of 50 to have 14 pixels beneath icon for text
inkscape -z -e icon.png -w 50 -h 50 icon-24px.svg
convert icon.png -background white -alpha Background ../src/icon.xbm
inkscape -z -e compass.png -w 48 -h 48 location_searching-24px.svg
convert compass.png -background white -alpha Background ../src/compass.xbm
inkscape -z -e face.png -w 13 -h 13 face-24px.svg
inkscape -z -e pin.png -w 13 -h 13 room-24px.svg
convert pin.png -background white -alpha Background ../src/pin.xbm

View File

@@ -8,10 +8,10 @@
MemSize = 0x200000 MemSize = 0x200000
; Define the desired ReadMode. Valid options are FASTREAD, READ2O, READ2IO, READ4O and READ4IO ; Define the desired ReadMode. Valid options are FASTREAD, READ2O, READ2IO, READ4O and READ4IO
ReadMode = READ2IO ReadMode = READ4IO
; Define the desired WriteMode. Valid options are PP, PP2O, PP4O and PP4IO ; Define the desired WriteMode. Valid options are PP, PP2O, PP4O and PP4IO
WriteMode = PP WriteMode = PP4IO
; Define the desired AddressMode. Valid options are BIT24 and BIT32 ; Define the desired AddressMode. Valid options are BIT24 and BIT32
AddressMode = BIT24 AddressMode = BIT24
@@ -38,12 +38,10 @@ DIO0Pin = 12
DIO0Port = 1 DIO0Port = 1
DIO1Pin = 13 DIO1Pin = 13
DIO1Port = 1 DIO1Port = 1
DIO2Pin = 7
;These two pins are not connected, but we must name something DIO2Port = 0
DIO2Pin = 3
DIO2Port = 1
DIO3Pin = 5 DIO3Pin = 5
DIO3Port = 1 DIO3Port = 0
; Define the Index of the Write In Progress (WIP) bit in the status register. Valid options are in the range of 0 to 7. ; Define the Index of the Write In Progress (WIP) bit in the status register. Valid options are in the range of 0 to 7.
WIPIndex = 0 WIPIndex = 0
@@ -57,13 +55,8 @@ PPSize = PAGE256
; Numbers can be given in decimal, hex (starting with either 0x or 0X) and binary (starting with either 0b or 0B) formats. ; Numbers can be given in decimal, hex (starting with either 0x or 0X) and binary (starting with either 0b or 0B) formats.
; The custom instructions will be executed in the order found. ; The custom instructions will be executed in the order found.
; This example includes two commands, first a WREN (WRite ENable) and then a WRSR (WRite Satus Register) enabling the Quad Operation and the High Performance
; mode for the MX25R6435F memory present in the nRF52840 DK.
;InitializationCustomInstruction = 0x06
;InitializationCustomInstruction = 0x01, [0x40, 0, 0x2]
; For MX25R1635F on TTGO board, only two data lines are connected ; For MX25R1635F on TTGO board, only two data lines are connected
; This example includes two commands, first a WREN (WRite ENable) and then a WRSR (WRite Satus Register) disabling Quad Operation and the High Performance ; This example includes two commands, first a WREN (WRite ENable) and then a WRSR (WRite Satus Register) enabling Quad Operation and the High Performance
; mode. For normal operation you might want low power mode instead. ; mode. For normal operation you might want low power mode instead.
InitializationCustomInstruction = 0x06 InitializationCustomInstruction = 0x06
InitializationCustomInstruction = 0x01, [0x00, 0, 0x2] InitializationCustomInstruction = 0x01, [0x40, 0, 0x2]

View File

@@ -0,0 +1,69 @@
; nrfjprog.exe configuration file.
; Note: QSPI flash is mapped into memory at address 0x12000000
[DEFAULT_CONFIGURATION]
; Define the capacity of the flash memory device in bytes. Set to 0 if no external memory device is present in your board.
; MX25R1635F is 16Mbit/2Mbyte
MemSize = 0x200000
; Define the desired ReadMode. Valid options are FASTREAD, READ2O, READ2IO, READ4O and READ4IO
ReadMode = READ2IO
; Define the desired WriteMode. Valid options are PP, PP2O, PP4O and PP4IO
WriteMode = PP
; Define the desired AddressMode. Valid options are BIT24 and BIT32
AddressMode = BIT24
; Define the desired Frequency. Valid options are M2, M4, M8, M16 and M32
Frequency = M16
; Define the desired SPI mode. Valid options are MODE0 and MODE3
SpiMode = MODE0
; Define the desired SckDelay. Valid options are in the range 0 to 255
SckDelay = 0x80
; Define the desired IO level for DIO2 and DIO3 during a custom instruction. Valid options are LEVEL_HIGH and LEVEL_LOW
CustomInstructionIO2Level = LEVEL_LOW
CustomInstructionIO3Level = LEVEL_HIGH
; Define the assigned pins for the QSPI peripheral. Valid options are those existing in your device
CSNPin = 15
CSNPort = 1
SCKPin = 14
SCKPort = 1
DIO0Pin = 12
DIO0Port = 1
DIO1Pin = 13
DIO1Port = 1
;These two pins are not connected, but we must name something
DIO2Pin = 3
DIO2Port = 1
DIO3Pin = 5
DIO3Port = 1
; Define the Index of the Write In Progress (WIP) bit in the status register. Valid options are in the range of 0 to 7.
WIPIndex = 0
; Define page size for commands. Valid sizes are PAGE256 and PAGE512.
PPSize = PAGE256
; Custom instructions to send to the external memory after initialization. Format is instruction code plus data to send in between optional brakets.
; These instructions will be executed each time the qspi peripheral is initiated by nrfjprog.
; To improve execution speed on consecutive interations with QSPI, you can run nrfjprog once with custom initialization, and then comment out the lines below.
; Numbers can be given in decimal, hex (starting with either 0x or 0X) and binary (starting with either 0b or 0B) formats.
; The custom instructions will be executed in the order found.
; This example includes two commands, first a WREN (WRite ENable) and then a WRSR (WRite Satus Register) enabling the Quad Operation and the High Performance
; mode for the MX25R6435F memory present in the nRF52840 DK.
;InitializationCustomInstruction = 0x06
;InitializationCustomInstruction = 0x01, [0x40, 0, 0x2]
; For MX25R1635F on TTGO board, only two data lines are connected
; This example includes two commands, first a WREN (WRite ENable) and then a WRSR (WRite Satus Register) disabling Quad Operation and the High Performance
; mode. For normal operation you might want low power mode instead.
InitializationCustomInstruction = 0x06
InitializationCustomInstruction = 0x01, [0x00, 0, 0x2]

View File

@@ -9,8 +9,13 @@
; https://docs.platformio.org/page/projectconf.html ; https://docs.platformio.org/page/projectconf.html
[platformio] [platformio]
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 = tbeam
;default_envs = heltec # lora-relay-v1 # nrf52840dk-geeksville # linux # or if you'd like to change the default to something like lora-relay-v1 put that here ;default_envs = tbeam0.7
;default_envs = heltec
;default_envs = tlora-v1
;default_envs = tlora-v2
;default_envs = lora-relay-v1 # nrf board
;default_envs = linux # lora-relay-v1 # nrf52840dk-geeksville # linux # or if you'd like to change the default to something like lora-relay-v1 put that here
[common] [common]
; common is not currently used ; common is not currently used
@@ -180,10 +185,10 @@ build_type = debug ; I'm debugging with ICE a lot now
build_flags = build_flags =
${arduino_base.build_flags} -Wno-unused-variable ${arduino_base.build_flags} -Wno-unused-variable
-Isrc/nrf52 -Isrc/nrf52
-Isdk-nrfxlib/crypto/nrf_oberon/include -Lsdk-nrfxlib/crypto/nrf_oberon/lib/cortex-m4/hard-float/ -lliboberon_3.0.3 -Isdk-nrfxlib/crypto/nrf_oberon/include -Lsdk-nrfxlib/crypto/nrf_oberon/lib/cortex-m4/hard-float/ -lliboberon_3.0.7
;-DCFG_DEBUG=3 ;-DCFG_DEBUG=3
src_filter = src_filter =
${arduino_base.src_filter} -<esp32/> -<nimble/> -<meshwifi/> -<mesh/wifi/> ${arduino_base.src_filter} -<esp32/> -<nimble/> -<mesh/wifi/> -<mesh/http/>
lib_ignore = lib_ignore =
BluetoothOTA BluetoothOTA
monitor_port = /dev/ttyACM1 monitor_port = /dev/ttyACM1
@@ -247,7 +252,7 @@ src_filter = ${nrf52_base.src_filter} +<../variants/ppr1>
lib_deps = lib_deps =
${arduino_base.lib_deps} ${arduino_base.lib_deps}
; Prototype eink/nrf52840/sx1262 device ; First prototype eink/nrf52840/sx1262 device
[env:eink] [env:eink]
extends = nrf52_base extends = nrf52_base
board = eink board = eink
@@ -261,6 +266,20 @@ lib_deps =
https://github.com/geeksville/EPD_Libraries.git https://github.com/geeksville/EPD_Libraries.git
TFT_eSPI TFT_eSPI
; First prototype eink/nrf52840/sx1262 device
[env:eink0.1]
extends = nrf52_base
board = eink0.1
# add our variants files to the include and src paths
# define build flags for the TFT_eSPI library
build_flags = ${nrf52_base.build_flags} -Ivariants/eink0.1
-DBUSY_PIN=3 -DRST_PIN=2 -DDC_PIN=28 -DCS_PIN=30
src_filter = ${nrf52_base.src_filter} +<../variants/eink0.1>
lib_deps =
${arduino_base.lib_deps}
https://github.com/geeksville/EPD_Libraries.git
TFT_eSPI
; The https://github.com/BigCorvus/SX1262-LoRa-BLE-Relay board by @BigCorvus ; The https://github.com/BigCorvus/SX1262-LoRa-BLE-Relay board by @BigCorvus
[env:lora-relay-v1] [env:lora-relay-v1]
extends = nrf52_base extends = nrf52_base
@@ -310,7 +329,7 @@ lib_deps =
; The Portduino based sim environment on top of linux ; The Portduino based sim environment on top of linux
[env:linux] [env:linux]
platform = https://github.com/geeksville/platform-portduino.git platform = https://github.com/geeksville/platform-portduino.git
src_filter = ${env.src_filter} -<esp32/> -<nimble/> -<nrf52/> -<meshwifi/> src_filter = ${env.src_filter} -<esp32/> -<nimble/> -<nrf52/> -<mesh/http/>
build_flags = ${arduino_base.build_flags} -O0 build_flags = ${arduino_base.build_flags} -O0
framework = arduino framework = arduino
board = linux_x86_64 board = linux_x86_64

2
proto

Submodule proto updated: dfe7bc1217...b1aed06442

View File

@@ -1,4 +1,5 @@
#include "power.h" #include "power.h"
#include "NodeDB.h"
#include "PowerFSM.h" #include "PowerFSM.h"
#include "main.h" #include "main.h"
#include "sleep.h" #include "sleep.h"
@@ -83,7 +84,7 @@ class AnalogBatteryLevel : public HasBatteryLevel
/// If we see a battery voltage higher than physics allows - assume charger is pumping /// If we see a battery voltage higher than physics allows - assume charger is pumping
/// in power /// in power
virtual bool isVBUSPlug() { return getBattVoltage() > chargingVolt; } virtual bool isVBUSPlug() { return getBattVoltage() > 1000 * chargingVolt; }
/// Assume charging if we have a battery and external power is connected. /// Assume charging if we have a battery and external power is connected.
/// we can't be smart enough to say 'full'? /// we can't be smart enough to say 'full'?
@@ -268,8 +269,41 @@ bool Power::axp192Init()
DEBUG_MSG("DCDC3: %s\n", axp.isDCDC3Enable() ? "ENABLE" : "DISABLE"); DEBUG_MSG("DCDC3: %s\n", axp.isDCDC3Enable() ? "ENABLE" : "DISABLE");
DEBUG_MSG("Exten: %s\n", axp.isExtenEnable() ? "ENABLE" : "DISABLE"); DEBUG_MSG("Exten: %s\n", axp.isExtenEnable() ? "ENABLE" : "DISABLE");
//axp.setChargeControlCur(AXP1XX_CHARGE_CUR_1320MA); // actual limit (in HW) on the tbeam is 450mA if (radioConfig.preferences.charge_current == ChargeCurrent_MAUnset) {
axp.setChargeControlCur(AXP1XX_CHARGE_CUR_450MA); // There's no HW limit on the tbeam. Setting to 450mz to be a good neighbor on the usb bus. axp.setChargeControlCur(AXP1XX_CHARGE_CUR_450MA);
} else if (radioConfig.preferences.charge_current == ChargeCurrent_MA100) {
axp.setChargeControlCur(AXP1XX_CHARGE_CUR_100MA);
} else if (radioConfig.preferences.charge_current == ChargeCurrent_MA190) {
axp.setChargeControlCur(AXP1XX_CHARGE_CUR_190MA);
} else if (radioConfig.preferences.charge_current == ChargeCurrent_MA280) {
axp.setChargeControlCur(AXP1XX_CHARGE_CUR_280MA);
} else if (radioConfig.preferences.charge_current == ChargeCurrent_MA360) {
axp.setChargeControlCur(AXP1XX_CHARGE_CUR_360MA);
} else if (radioConfig.preferences.charge_current == ChargeCurrent_MA450) {
axp.setChargeControlCur(AXP1XX_CHARGE_CUR_450MA);
} else if (radioConfig.preferences.charge_current == ChargeCurrent_MA550) {
axp.setChargeControlCur(AXP1XX_CHARGE_CUR_550MA);
} else if (radioConfig.preferences.charge_current == ChargeCurrent_MA630) {
axp.setChargeControlCur(AXP1XX_CHARGE_CUR_630MA);
} else if (radioConfig.preferences.charge_current == ChargeCurrent_MA700) {
axp.setChargeControlCur(AXP1XX_CHARGE_CUR_700MA);
} else if (radioConfig.preferences.charge_current == ChargeCurrent_MA780) {
axp.setChargeControlCur(AXP1XX_CHARGE_CUR_780MA);
} else if (radioConfig.preferences.charge_current == ChargeCurrent_MA880) {
axp.setChargeControlCur(AXP1XX_CHARGE_CUR_880MA);
} else if (radioConfig.preferences.charge_current == ChargeCurrent_MA960) {
axp.setChargeControlCur(AXP1XX_CHARGE_CUR_960MA);
} else if (radioConfig.preferences.charge_current == ChargeCurrent_MA1000) {
axp.setChargeControlCur(AXP1XX_CHARGE_CUR_1000MA);
} else if (radioConfig.preferences.charge_current == ChargeCurrent_MA1080) {
axp.setChargeControlCur(AXP1XX_CHARGE_CUR_1080MA);
} else if (radioConfig.preferences.charge_current == ChargeCurrent_MA1160) {
axp.setChargeControlCur(AXP1XX_CHARGE_CUR_1160MA);
} else if (radioConfig.preferences.charge_current == ChargeCurrent_MA1240) {
axp.setChargeControlCur(AXP1XX_CHARGE_CUR_1240MA);
} else if (radioConfig.preferences.charge_current == ChargeCurrent_MA1320) {
axp.setChargeControlCur(AXP1XX_CHARGE_CUR_1320MA);
}
#if 0 #if 0

View File

@@ -11,7 +11,7 @@ namespace meshtastic
*/ */
enum OptionalBool { OptFalse = 0, OptTrue = 1, OptUnknown = 2 }; enum OptionalBool { OptFalse = 0, OptTrue = 1, OptUnknown = 2 };
/// Describes the state of the GPS system. /// Describes the state of the Power system.
class PowerStatus : public Status class PowerStatus : public Status
{ {

View File

@@ -3,33 +3,35 @@
#define periodsToLog 48 #define periodsToLog 48
// A reminder that there are 3600 seconds in an hour so I don't have AirTime *airTime;
// to keep googling it.
// This can be changed to a smaller number to speed up testing.
//
uint32_t secondsPerPeriod = 3600; uint32_t secondsPerPeriod = 3600;
uint32_t lastMillis = 0; uint32_t lastMillis = 0;
uint32_t secSinceBoot = 0; uint32_t secSinceBoot = 0;
// AirTime at;
// Don't read out of this directly. Use the helper functions. // Don't read out of this directly. Use the helper functions.
struct airtimeStruct { struct airtimeStruct {
uint16_t periodTX[periodsToLog]; uint32_t periodTX[periodsToLog]; // AirTime transmitted
uint16_t periodRX[periodsToLog]; uint32_t periodRX[periodsToLog]; // AirTime received and repeated (Only valid mesh packets)
uint16_t periodRX_ALL[periodsToLog]; uint32_t periodRX_ALL[periodsToLog]; // AirTime received regardless of valid mesh packet. Could include noise.
uint8_t lastPeriodIndex; uint8_t lastPeriodIndex;
} airtimes; } airtimes;
void logAirtime(reportTypes reportType, uint32_t airtime_ms) void AirTime::logAirtime(reportTypes reportType, uint32_t airtime_ms)
{ {
if (reportType == TX_LOG) { if (reportType == TX_LOG) {
airtimes.periodTX[0] = airtimes.periodTX[0] + round(airtime_ms / 1000); DEBUG_MSG("AirTime - Packet transmitted : %ums\n", airtime_ms);
airtimes.periodTX[0] = airtimes.periodTX[0] + airtime_ms;
} else if (reportType == RX_LOG) { } else if (reportType == RX_LOG) {
airtimes.periodRX[0] = airtimes.periodRX[0] + round(airtime_ms / 1000); DEBUG_MSG("AirTime - Packet received : %ums\n", airtime_ms);
airtimes.periodRX[0] = airtimes.periodRX[0] + airtime_ms;
} else if (reportType == RX_ALL_LOG) { } else if (reportType == RX_ALL_LOG) {
airtimes.periodRX_ALL[0] = airtimes.periodRX_ALL[0] + round(airtime_ms / 1000); DEBUG_MSG("AirTime - Packet received (noise?) : %ums\n", airtime_ms);
airtimes.periodRX_ALL[0] = airtimes.periodRX_ALL[0] + airtime_ms;
} else { } else {
// Unknown report type DEBUG_MSG("AirTime - Unknown report time. This should never happen!!\n");
} }
} }
@@ -38,29 +40,27 @@ uint8_t currentPeriodIndex()
return ((getSecondsSinceBoot() / secondsPerPeriod) % periodsToLog); return ((getSecondsSinceBoot() / secondsPerPeriod) % periodsToLog);
} }
void airtimeCalculator() void airtimeRotatePeriod()
{ {
if (millis() - lastMillis > 1000) {
lastMillis = millis();
secSinceBoot++;
if (airtimes.lastPeriodIndex != currentPeriodIndex()) {
for (int i = periodsToLog - 2; i >= 0; --i) {
airtimes.periodTX[i + 1] = airtimes.periodTX[i];
airtimes.periodRX[i + 1] = airtimes.periodRX[i];
airtimes.periodRX_ALL[i + 1] = airtimes.periodRX_ALL[i];
}
airtimes.periodTX[0] = 0;
airtimes.periodRX[0] = 0;
airtimes.periodRX_ALL[0] = 0;
airtimes.lastPeriodIndex = currentPeriodIndex(); if (airtimes.lastPeriodIndex != currentPeriodIndex()) {
DEBUG_MSG("Rotating airtimes to a new period = %u\n", currentPeriodIndex());
for (int i = periodsToLog - 2; i >= 0; --i) {
airtimes.periodTX[i + 1] = airtimes.periodTX[i];
airtimes.periodRX[i + 1] = airtimes.periodRX[i];
airtimes.periodRX_ALL[i + 1] = airtimes.periodRX_ALL[i];
} }
airtimes.periodTX[0] = 0;
airtimes.periodRX[0] = 0;
airtimes.periodRX_ALL[0] = 0;
airtimes.lastPeriodIndex = currentPeriodIndex();
} }
} }
uint16_t *airtimeReport(reportTypes reportType) uint32_t *airtimeReport(reportTypes reportType)
{ {
// currentHourIndexReset();
if (reportType == TX_LOG) { if (reportType == TX_LOG) {
return airtimes.periodTX; return airtimes.periodTX;
@@ -86,3 +86,22 @@ uint32_t getSecondsSinceBoot()
{ {
return secSinceBoot; return secSinceBoot;
} }
AirTime::AirTime() : concurrency::OSThread("AirTime") {}
int32_t AirTime::runOnce()
{
//DEBUG_MSG("AirTime::runOnce()\n");
airtimeRotatePeriod();
secSinceBoot++;
/*
This actually doesn't need to be run once per second but we currently use it for the
secSinceBoot counter.
If we have a better counter of how long the device has been online (and not millis())
then we can change this to something less frequent. Maybe once ever 5 seconds?
*/
return (1000 * 1);
}

View File

@@ -1,5 +1,6 @@
#pragma once #pragma once
#include "concurrency/OSThread.h"
#include "configuration.h" #include "configuration.h"
#include <Arduino.h> #include <Arduino.h>
#include <functional> #include <functional>
@@ -26,13 +27,28 @@ enum reportTypes { TX_LOG, RX_LOG, RX_ALL_LOG };
void logAirtime(reportTypes reportType, uint32_t airtime_ms); void logAirtime(reportTypes reportType, uint32_t airtime_ms);
void airtimeCalculator(); void airtimeRotatePeriod();
uint8_t currentPeriodIndex(); uint8_t currentPeriodIndex();
uint8_t getPeriodsToLog(); uint8_t getPeriodsToLog();
uint32_t getSecondsSinceBoot(); uint32_t getSecondsSinceBoot();
uint16_t *airtimeReport(reportTypes reportType); uint32_t *airtimeReport(reportTypes reportType);
uint32_t getSecondsPerPeriod(); uint32_t getSecondsPerPeriod();
class AirTime : private concurrency::OSThread
{
public:
AirTime();
void logAirtime(reportTypes reportType, uint32_t airtime_ms);
protected:
virtual int32_t runOnce();
};
extern AirTime *airTime;

View File

@@ -28,6 +28,8 @@ void OSThread::setup()
OSThread::OSThread(const char *_name, uint32_t period, ThreadController *_controller) OSThread::OSThread(const char *_name, uint32_t period, ThreadController *_controller)
: Thread(NULL, period), controller(_controller) : Thread(NULL, period), controller(_controller)
{ {
assertIsSetup();
ThreadName = _name; ThreadName = _name;
if (controller) if (controller)
@@ -81,4 +83,36 @@ void OSThread::run()
currentThread = NULL; currentThread = NULL;
} }
/**
* This flag is set **only** when setup() starts, to provide a way for us to check for sloppy static constructor calls.
* Call assertIsSetup() to force a crash if someone tries to create an instance too early.
*
* it is super important to never allocate those object statically. instead, you should explicitly
* new them at a point where you are guaranteed that other objects that this instance
* depends on have already been created.
*
* in particular, for OSThread that means "all instances must be declared via new() in setup() or later" -
* this makes it guaranteed that the global mainController is fully constructed first.
*/
bool hasBeenSetup;
void assertIsSetup()
{
/**
* Dear developer comrade - If this assert fails() that means you need to fix the following:
*
* This flag is set **only** when setup() starts, to provide a way for us to check for sloppy static constructor calls.
* Call assertIsSetup() to force a crash if someone tries to create an instance too early.
*
* it is super important to never allocate those object statically. instead, you should explicitly
* new them at a point where you are guaranteed that other objects that this instance
* depends on have already been created.
*
* in particular, for OSThread that means "all instances must be declared via new() in setup() or later" -
* this makes it guaranteed that the global mainController is fully constructed first.
*/
assert(hasBeenSetup);
}
} // namespace concurrency } // namespace concurrency

View File

@@ -42,7 +42,6 @@ class OSThread : public Thread
static bool showWaiting; static bool showWaiting;
public: public:
/// For debug printing only (might be null) /// For debug printing only (might be null)
static const OSThread *currentThread; static const OSThread *currentThread;
@@ -71,4 +70,19 @@ class OSThread : public Thread
virtual void run(); virtual void run();
}; };
/**
* This flag is set **only** when setup() starts, to provide a way for us to check for sloppy static constructor calls.
* Call assertIsSetup() to force a crash if someone tries to create an instance too early.
*
* it is super important to never allocate those object statically. instead, you should explicitly
* new them at a point where you are guaranteed that other objects that this instance
* depends on have already been created.
*
* in particular, for OSThread that means "all instances must be declared via new() in setup() or later" -
* this makes it guaranteed that the global mainController is fully constructed first.
*/
extern bool hasBeenSetup;
void assertIsSetup();
} // namespace concurrency } // namespace concurrency

View File

@@ -47,6 +47,8 @@ The Unix epoch (or Unix time or POSIX time or Unix timestamp) is the number of s
t.tm_mon = d.month() - 1; t.tm_mon = d.month() - 1;
t.tm_year = d.year() - 1900; t.tm_year = d.year() - 1900;
t.tm_isdst = false; t.tm_isdst = false;
DEBUG_MSG("NMEA GPS time %d\n", t.tm_sec);
perhapsSetRTC(RTCQualityGPS, t); perhapsSetRTC(RTCQualityGPS, t);
return true; return true;
@@ -87,11 +89,17 @@ bool NMEAGPS::lookForLocation()
auto loc = reader.location.value(); auto loc = reader.location.value();
latitude = toDegInt(loc.lat); latitude = toDegInt(loc.lat);
longitude = toDegInt(loc.lng); longitude = toDegInt(loc.lng);
foundLocation = true;
// expect gps pos lat=37.520825, lon=-122.309162, alt=158 // Some GPSes (Air530) seem to send a zero longitude when the current fix is bogus
DEBUG_MSG("new NMEA GPS pos lat=%f, lon=%f, alt=%d, hdop=%g, heading=%f\n", latitude * 1e-7, longitude * 1e-7, altitude, if(longitude == 0)
dop * 1e-2, heading * 1e-5); DEBUG_MSG("Ignoring bogus NMEA position\n");
else {
foundLocation = true;
// expect gps pos lat=37.520825, lon=-122.309162, alt=158
DEBUG_MSG("new NMEA GPS pos lat=%f, lon=%f, alt=%d, hdop=%g, heading=%f\n", latitude * 1e-7, longitude * 1e-7, altitude,
dop * 1e-2, heading * 1e-5);
}
} }
return foundLocation; return foundLocation;

View File

@@ -4,7 +4,6 @@
#include "EInkDisplay.h" #include "EInkDisplay.h"
#include "SPILock.h" #include "SPILock.h"
#include "epd1in54.h" // Screen specific library #include "epd1in54.h" // Screen specific library
#include "graphics/configs.h"
#include <SPI.h> #include <SPI.h>
#include <TFT_eSPI.h> // Graphics library and Sprite class #include <TFT_eSPI.h> // Graphics library and Sprite class
@@ -123,7 +122,8 @@ bool EInkDisplay::connect()
#endif #endif
#ifdef PIN_EINK_EN #ifdef PIN_EINK_EN
digitalWrite(PIN_EINK_EN, HIGH); // backlight power, HIGH is backlight on, LOW is off
digitalWrite(PIN_EINK_EN, LOW);
pinMode(PIN_EINK_EN, OUTPUT); pinMode(PIN_EINK_EN, OUTPUT);
#endif #endif

View File

@@ -31,11 +31,14 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#include "graphics/images.h" #include "graphics/images.h"
#include "main.h" #include "main.h"
#include "mesh-pb-constants.h" #include "mesh-pb-constants.h"
#include "meshwifi/meshwifi.h"
#include "plugins/TextMessagePlugin.h" #include "plugins/TextMessagePlugin.h"
#include "target_specific.h" #include "target_specific.h"
#include "utils.h" #include "utils.h"
#ifndef NO_ESP32
#include "mesh/http/WiFiAPClient.h"
#endif
using namespace meshtastic; /** @todo remove */ using namespace meshtastic; /** @todo remove */
namespace graphics namespace graphics
@@ -198,7 +201,7 @@ static void drawTextMessageFrame(OLEDDisplay *display, OLEDDisplayUiState *state
// the max length of this buffer is much longer than we can possibly print // the max length of this buffer is much longer than we can possibly print
static char tempBuf[96]; static char tempBuf[96];
assert(mp.decoded.which_payload == SubPacket_data_tag); assert(mp.decoded.which_payloadVariant == SubPacket_data_tag);
snprintf(tempBuf, sizeof(tempBuf), " %s", mp.decoded.data.payload.bytes); snprintf(tempBuf, sizeof(tempBuf), " %s", mp.decoded.data.payload.bytes);
display->drawStringMaxWidth(4 + x, 10 + y, SCREEN_WIDTH - (6 + x), tempBuf); display->drawStringMaxWidth(4 + x, 10 + y, SCREEN_WIDTH - (6 + x), tempBuf);
@@ -746,7 +749,7 @@ void Screen::setup()
powerStatusObserver.observe(&powerStatus->onNewStatus); powerStatusObserver.observe(&powerStatus->onNewStatus);
gpsStatusObserver.observe(&gpsStatus->onNewStatus); gpsStatusObserver.observe(&gpsStatus->onNewStatus);
nodeStatusObserver.observe(&nodeStatus->onNewStatus); nodeStatusObserver.observe(&nodeStatus->onNewStatus);
textMessageObserver.observe(&textMessagePlugin); textMessageObserver.observe(textMessagePlugin);
} }
void Screen::forceDisplay() void Screen::forceDisplay()
@@ -895,10 +898,12 @@ void Screen::setFrames()
// call a method on debugInfoScreen object (for more details) // call a method on debugInfoScreen object (for more details)
normalFrames[numframes++] = &Screen::drawDebugInfoSettingsTrampoline; normalFrames[numframes++] = &Screen::drawDebugInfoSettingsTrampoline;
#ifndef NO_ESP32
if (isWifiAvailable()) { if (isWifiAvailable()) {
// call a method on debugInfoScreen object (for more details) // call a method on debugInfoScreen object (for more details)
normalFrames[numframes++] = &Screen::drawDebugInfoWiFiTrampoline; normalFrames[numframes++] = &Screen::drawDebugInfoWiFiTrampoline;
} }
#endif
ui.setFrames(normalFrames, numframes); ui.setFrames(normalFrames, numframes);
ui.enableAllIndicators(); ui.enableAllIndicators();

View File

@@ -1,33 +1,20 @@
#define icon_width 50 #define icon_width 50
#define icon_height 50 #define icon_height 28
static char icon_bits[] = { static char icon_bits[] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x03, 0x00, 0x03,
0x00, 0x00, 0x00, 0x00, 0xFC, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x80, 0xFF, 0x00, 0x00, 0x00, 0x80, 0x07, 0xC0, 0x07, 0x00, 0x00, 0x00, 0xC0, 0x1F,
0xFF, 0x07, 0x00, 0x00, 0x00, 0xE0, 0xFF, 0xFF, 0x1F, 0x00, 0x00, 0x00, 0xC0, 0x0F, 0x00, 0x00, 0x00, 0xE0, 0x0F, 0xE0, 0x0F, 0x00, 0x00, 0x00,
0xF0, 0xFF, 0xFF, 0x3F, 0x00, 0x00, 0x00, 0xFC, 0xFF, 0xFF, 0xFF, 0x00, 0xE0, 0x0F, 0xF0, 0x1F, 0x00, 0x00, 0x00, 0xF0, 0x07, 0xF0, 0x3F, 0x00,
0x00, 0x00, 0xFE, 0x0F, 0xC0, 0xFF, 0x01, 0x00, 0x00, 0xFF, 0x03, 0x00, 0x00, 0x00, 0xF8, 0x03, 0xF8, 0x7F, 0x00, 0x00, 0x00, 0xF8, 0x03, 0xFC,
0xFF, 0x03, 0x00, 0x80, 0xFF, 0x00, 0x00, 0xFC, 0x07, 0x00, 0xC0, 0x3F, 0x7F, 0x00, 0x00, 0x00, 0xFC, 0x01, 0xFC, 0xFE, 0x00, 0x00, 0x00, 0xFE,
0xE0, 0x1F, 0xF0, 0x0F, 0x00, 0xC0, 0x1F, 0xFC, 0xFF, 0xE0, 0x0F, 0x00, 0x00, 0xFE, 0xFC, 0x01, 0x00, 0x00, 0xFE, 0x00, 0x7F, 0xFC, 0x01, 0x00,
0xE0, 0x0F, 0xFF, 0xFF, 0xC3, 0x1F, 0x00, 0xF0, 0x87, 0xFF, 0xFF, 0x87, 0x00, 0x7F, 0x00, 0x3F, 0xF8, 0x03, 0x00, 0x80, 0x3F, 0x80, 0x3F, 0xF0,
0x3F, 0x00, 0xF0, 0xC3, 0xFF, 0xFF, 0x0F, 0x3F, 0x00, 0xF8, 0xE3, 0x7F, 0x07, 0x00, 0x80, 0x3F, 0xC0, 0x1F, 0xF0, 0x07, 0x00, 0xC0, 0x1F, 0xC0,
0xF8, 0x1F, 0x7F, 0x00, 0xF8, 0xF1, 0x0F, 0xC0, 0x3F, 0x7E, 0x00, 0xF8, 0x0F, 0xE0, 0x0F, 0x00, 0xE0, 0x0F, 0xE0, 0x0F, 0xC0, 0x1F, 0x00, 0xE0,
0xF1, 0x07, 0x80, 0x3F, 0x7E, 0x00, 0xFC, 0xF8, 0x03, 0x00, 0x7F, 0xFC, 0x0F, 0xF0, 0x07, 0x80, 0x1F, 0x00, 0xF0, 0x07, 0xF8, 0x03, 0x80, 0x3F,
0x00, 0xFC, 0xF8, 0x81, 0x07, 0x7E, 0xFC, 0x00, 0x7C, 0xF8, 0xE0, 0x1F, 0x00, 0xF8, 0x03, 0xF8, 0x03, 0x00, 0x7F, 0x00, 0xFC, 0x03, 0xFC, 0x01,
0x7C, 0xF8, 0x00, 0x7C, 0xFC, 0xF0, 0x3F, 0xFC, 0xF8, 0x00, 0x7C, 0xFC, 0x00, 0x7E, 0x00, 0xFC, 0x01, 0xFE, 0x00, 0x00, 0xFE, 0x00, 0xFE, 0x00,
0xF0, 0x3F, 0xFC, 0xF8, 0x00, 0x7C, 0x7C, 0xF8, 0x7F, 0xF8, 0xF8, 0x00, 0xFE, 0x00, 0x00, 0xFC, 0x01, 0x7E, 0x00, 0x7F, 0x00, 0x00, 0xF8, 0x01,
0x7C, 0x7C, 0xF8, 0x7F, 0xF8, 0xF8, 0x00, 0x00, 0x00, 0xF8, 0x7F, 0x00, 0x7E, 0x00, 0x3E, 0x00, 0x00, 0xF8, 0x01, 0x38, 0x00, 0x3C, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0xF8, 0x7F, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0x70, 0x00, 0x10, 0x00, 0x10, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00,
0x3F, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0x3F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, };
0x00, 0xE0, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0x0F, 0x00, 0x00,
0x00, 0x00, 0x00, 0xC0, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0x0F,
0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00,
0xC0, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0x0F, 0x00, 0x00, 0x00,
0x00, 0x00, 0xC0, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0x1F, 0x00,
0x00, 0x00, 0x00, 0x00, 0xF0, 0x3F, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF8,
0x7F, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFC, 0xFF, 0x00, 0x00, 0x00, 0x00,
0x00, 0xFE, 0xFF, 0x01, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFC, 0x03, 0x00,
0x00, 0x00, 0x80, 0x7F, 0xF8, 0x07, 0x00, 0x00, 0x00, 0x00, 0x3F, 0xF0,
0x03, 0x00, 0x00, 0x00, 0x00, 0x1E, 0xE0, 0x01, 0x00, 0x00, 0x00, 0x00,
0x0C, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, };

View File

@@ -19,8 +19,7 @@
#include "concurrency/Periodic.h" #include "concurrency/Periodic.h"
#include "graphics/Screen.h" #include "graphics/Screen.h"
#include "main.h" #include "main.h"
#include "meshwifi/meshhttp.h" #include "plugins/Plugins.h"
#include "meshwifi/meshwifi.h"
#include "sleep.h" #include "sleep.h"
#include "target_specific.h" #include "target_specific.h"
#include <OneButton.h> #include <OneButton.h>
@@ -28,6 +27,8 @@
// #include <driver/rtc_io.h> // #include <driver/rtc_io.h>
#ifndef NO_ESP32 #ifndef NO_ESP32
#include "mesh/http/WebServer.h"
#include "mesh/http/WiFiAPClient.h"
#include "nimble/BluetoothUtil.h" #include "nimble/BluetoothUtil.h"
#endif #endif
@@ -275,6 +276,8 @@ RadioInterface *rIf = NULL;
void setup() void setup()
{ {
concurrency::hasBeenSetup = true;
#ifdef SEGGER_STDOUT_CH #ifdef SEGGER_STDOUT_CH
SEGGER_RTT_ConfigUpBuffer(SEGGER_STDOUT_CH, NULL, NULL, 1024, SEGGER_RTT_MODE_NO_BLOCK_TRIM); SEGGER_RTT_ConfigUpBuffer(SEGGER_STDOUT_CH, NULL, NULL, 1024, SEGGER_RTT_MODE_NO_BLOCK_TRIM);
#endif #endif
@@ -300,17 +303,20 @@ void setup()
digitalWrite(RESET_OLED, 1); digitalWrite(RESET_OLED, 1);
#endif #endif
#ifdef BUTTON_PIN
#ifndef NO_ESP32
// If BUTTON_PIN is held down during the startup process, // If BUTTON_PIN is held down during the startup process,
// force the device to go into a SoftAP mode. // force the device to go into a SoftAP mode.
bool forceSoftAP = 0; bool forceSoftAP = 0;
#ifdef BUTTON_PIN
#ifndef NO_ESP32
pinMode(BUTTON_PIN, INPUT); pinMode(BUTTON_PIN, INPUT);
#ifdef BUTTON_NEED_PULLUP
gpio_pullup_en((gpio_num_t)BUTTON_PIN);
#endif
// BUTTON_PIN is pulled high by a 12k resistor. // BUTTON_PIN is pulled high by a 12k resistor.
if (!digitalRead(BUTTON_PIN)) { if (!digitalRead(BUTTON_PIN)) {
forceSoftAP = 1; forceSoftAP = 1;
DEBUG_MSG("-------------------- Setting forceSoftAP = 1\n"); DEBUG_MSG("Setting forceSoftAP = 1\n");
} }
#endif #endif
@@ -390,15 +396,15 @@ void setup()
readFromRTC(); // read the main CPU RTC at first (in case we can't get GPS time) readFromRTC(); // read the main CPU RTC at first (in case we can't get GPS time)
#ifdef GENIEBLOCKS #ifdef GENIEBLOCKS
//gps setup // gps setup
pinMode (GPS_RESET_N, OUTPUT); pinMode(GPS_RESET_N, OUTPUT);
pinMode(GPS_EXTINT, OUTPUT); pinMode(GPS_EXTINT, OUTPUT);
digitalWrite(GPS_RESET_N, HIGH); digitalWrite(GPS_RESET_N, HIGH);
digitalWrite(GPS_EXTINT, LOW); digitalWrite(GPS_EXTINT, LOW);
//battery setup // battery setup
// If we want to read battery level, we need to set BATTERY_EN_PIN pin to low. // If we want to read battery level, we need to set BATTERY_EN_PIN pin to low.
// ToDo: For low power consumption after read battery level, set that pin to high. // ToDo: For low power consumption after read battery level, set that pin to high.
pinMode (BATTERY_EN_PIN, OUTPUT); pinMode(BATTERY_EN_PIN, OUTPUT);
digitalWrite(BATTERY_EN_PIN, LOW); digitalWrite(BATTERY_EN_PIN, LOW);
#endif #endif
@@ -439,14 +445,17 @@ void setup()
service.init(); service.init();
// Now that the mesh service is created, create any plugins
setupPlugins();
// Do this after service.init (because that clears error_code) // Do this after service.init (because that clears error_code)
#ifdef AXP192_SLAVE_ADDRESS #ifdef AXP192_SLAVE_ADDRESS
if(!axp192_found) if (!axp192_found)
recordCriticalError(CriticalErrorCode_NoAXP192); // Record a hardware fault for missing hardware recordCriticalError(CriticalErrorCode_NoAXP192); // Record a hardware fault for missing hardware
#endif #endif
// Don't call screen setup until after nodedb is setup (because we need // Don't call screen setup until after nodedb is setup (because we need
// the current region name) // the current region name)
#if defined(ST7735_CS) || defined(HAS_EINK) #if defined(ST7735_CS) || defined(HAS_EINK)
screen->setup(); screen->setup();
#else #else
@@ -481,6 +490,8 @@ void setup()
DEBUG_MSG("Warning: Failed to find RF95 radio\n"); DEBUG_MSG("Warning: Failed to find RF95 radio\n");
delete rIf; delete rIf;
rIf = NULL; rIf = NULL;
} else {
DEBUG_MSG("Radio init succeeded, using RF95 radio\n");
} }
} }
#endif #endif
@@ -492,6 +503,8 @@ void setup()
DEBUG_MSG("Warning: Failed to find SX1262 radio\n"); DEBUG_MSG("Warning: Failed to find SX1262 radio\n");
delete rIf; delete rIf;
rIf = NULL; rIf = NULL;
} else {
DEBUG_MSG("Radio init succeeded, using SX1262 radio\n");
} }
} }
#endif #endif
@@ -503,13 +516,23 @@ void setup()
DEBUG_MSG("Warning: Failed to find simulated radio\n"); DEBUG_MSG("Warning: Failed to find simulated radio\n");
delete rIf; delete rIf;
rIf = NULL; rIf = NULL;
} else {
DEBUG_MSG("Using SIMULATED radio!\n");
} }
} }
#endif #endif
#ifndef NO_ESP32
// Initialize Wifi // Initialize Wifi
initWifi(forceSoftAP); initWifi(forceSoftAP);
// Start web server thread.
webServerThread = new WebServerThread();
#endif
// Start airtime logger thread.
airTime = new AirTime();
if (!rIf) if (!rIf)
recordCriticalError(CriticalErrorCode_NoRadio); recordCriticalError(CriticalErrorCode_NoRadio);
else else
@@ -570,7 +593,7 @@ void loop()
#endif #endif
// TODO: This should go into a thread handled by FreeRTOS. // TODO: This should go into a thread handled by FreeRTOS.
handleWebResponse(); // handleWebResponse();
service.loop(); service.loop();
@@ -583,7 +606,4 @@ void loop()
// We want to sleep as long as possible here - because it saves power // We want to sleep as long as possible here - because it saves power
mainDelay.delay(delayMsec); mainDelay.delay(delayMsec);
// if (didWake) DEBUG_MSG("wake!\n"); // if (didWake) DEBUG_MSG("wake!\n");
// Handles cleanup for the airtime calculator.
airtimeCalculator();
} }

View File

@@ -72,7 +72,7 @@ void DSRRouter::sniffReceived(const MeshPacket *p)
addRoute(p->from, p->from, 0); // We are adjacent with zero hops addRoute(p->from, p->from, 0); // We are adjacent with zero hops
} }
switch (p->decoded.which_payload) { switch (p->decoded.which_payloadVariant) {
case SubPacket_route_request_tag: case SubPacket_route_request_tag:
// Handle route discovery packets (will be a broadcast message) // Handle route discovery packets (will be a broadcast message)
// FIXME - always start request with the senders nodenum // FIXME - always start request with the senders nodenum
@@ -105,7 +105,7 @@ void DSRRouter::sniffReceived(const MeshPacket *p)
// packets until ack arrives) // 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... // 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; break;
case SubPacket_route_error_tag: case SubPacket_error_reason_tag:
removeRoute(p->decoded.dest); removeRoute(p->decoded.dest);
// FIXME: if any pending packets were waiting on this route, delete them // FIXME: if any pending packets were waiting on this route, delete them
@@ -131,7 +131,7 @@ void DSRRouter::sniffReceived(const MeshPacket *p)
assert(p->decoded.source); // I think this is guaranteed by now assert(p->decoded.source); // I think this is guaranteed by now
// FIXME - what if the current packet _is_ a route error packet? // FIXME - what if the current packet _is_ a route error packet?
sendRouteError(p, RouteError_NO_ROUTE); sendRouteError(p, ErrorReason_NO_ROUTE);
} }
// FIXME, stop local processing of this packet // FIXME, stop local processing of this packet
@@ -139,13 +139,13 @@ void DSRRouter::sniffReceived(const MeshPacket *p)
// handle naks - convert them to route error packets // handle naks - convert them to route error packets
// All naks are generated locally, because we failed resending the packet too many times // All naks are generated locally, because we failed resending the packet too many times
PacketId nakId = p->decoded.which_ack == SubPacket_fail_id_tag ? p->decoded.ack.fail_id : 0; PacketId nakId = p->decoded.which_ackVariant == SubPacket_fail_id_tag ? p->decoded.ackVariant.fail_id : 0;
if (nakId) { if (nakId) {
auto pending = findPendingPacket(p->to, 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 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 removeRoute(pending->packet->decoded.dest); // We no longer have a route to the specified node
sendRouteError(p, RouteError_GOT_NAK); sendRouteError(p, ErrorReason_GOT_NAK);
} }
} }
} }
@@ -230,7 +230,7 @@ void DSRRouter::sendNextHop(NodeNum n, const MeshPacket *p)
/** /**
* Send a route error packet towards whoever originally sent this message * Send a route error packet towards whoever originally sent this message
*/ */
void DSRRouter::sendRouteError(const MeshPacket *p, RouteError err) void DSRRouter::sendRouteError(const MeshPacket *p, ErrorReason err)
{ {
DEBUG_MSG("FIXME not implemented sendRouteError\n"); DEBUG_MSG("FIXME not implemented sendRouteError\n");
} }

View File

@@ -70,7 +70,7 @@ class DSRRouter : public ReliableRouter
/** /**
* Send a route error packet towards whoever originally sent this message * Send a route error packet towards whoever originally sent this message
*/ */
void sendRouteError(const MeshPacket *p, RouteError err); void sendRouteError(const MeshPacket *p, ErrorReason err);
/** make a copy of p, start discovery, but only if we don't /** 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 * already a discovery in progress for that node number. Caller has already scheduled this message for retransmission

View File

@@ -0,0 +1,91 @@
#include "MeshPacketQueue.h"
#include <algorithm>
/// @return the priority of the specified packet
inline uint32_t getPriority(MeshPacket *p)
{
auto pri = p->priority;
return pri;
}
/// @return "true" if "p1" is ordered before "p2"
bool CompareMeshPacket::operator()(MeshPacket *p1, MeshPacket *p2)
{
assert(p1 && p2);
auto p1p = getPriority(p1), p2p = getPriority(p2);
// If priorities differ, use that
// for equal priorities, order by id (older packets have higher priority - this will briefly be wrong when IDs roll over but
// no big deal)
return (p1p != p2p) ? (p1p < p2p) // prefer bigger priorities
: (p1->id >= p2->id); // prefer smaller packet ids
}
MeshPacketQueue::MeshPacketQueue(size_t _maxLen) : maxLen(_maxLen) {}
/** Some clients might not properly set priority, therefore we fix it here.
*/
void fixPriority(MeshPacket *p)
{
// We might receive acks from other nodes (and since generated remotely, they won't have priority assigned. Check for that
// and fix it
if (p->priority == MeshPacket_Priority_UNSET) {
// if acks give high priority
// if a reliable message give a bit higher default priority
p->priority = p->decoded.which_ackVariant ? MeshPacket_Priority_ACK :
(p->want_ack ? MeshPacket_Priority_RELIABLE : MeshPacket_Priority_DEFAULT);
}
}
/** enqueue a packet, return false if full */
bool MeshPacketQueue::enqueue(MeshPacket *p)
{
fixPriority(p);
// fixme if there is something lower priority in the queue that can be deleted to make space, delete that instead
if (size() >= maxLen)
return false;
else {
push(p);
return true;
}
}
MeshPacket *MeshPacketQueue::dequeue()
{
if (empty())
return NULL;
else {
auto p = top();
pop(); // remove the first item
return p;
}
}
// this is kinda yucky, but I'm not sure if all arduino c++ compilers support closuers. And we only have one
// thread that can run at a time - so safe
static NodeNum findFrom;
static PacketId findId;
static bool isMyPacket(MeshPacket *p)
{
return p->id == findId && p->from == findFrom;
}
/** Attempt to find and remove a packet from this queue. Returns true the packet which was removed from the queue */
MeshPacket *MeshPacketQueue::remove(NodeNum from, PacketId id)
{
findFrom = from;
findId = id;
auto it = std::find_if(this->c.begin(), this->c.end(), isMyPacket);
if (it != this->c.end()) {
auto p = *it;
this->c.erase(it);
std::make_heap(this->c.begin(), this->c.end(), this->comp);
return p;
} else {
return NULL;
}
}

View File

@@ -0,0 +1,33 @@
#pragma once
#include "MeshTypes.h"
#include <assert.h>
#include <queue>
// this is an strucure which implements the
// operator overloading
struct CompareMeshPacket {
bool operator()(MeshPacket *p1, MeshPacket *p2);
};
/**
* A priority queue of packets.
*
*/
class MeshPacketQueue : public std::priority_queue<MeshPacket *, std::vector<MeshPacket *>, CompareMeshPacket>
{
size_t maxLen;
public:
MeshPacketQueue(size_t _maxLen);
/** enqueue a packet, return false if full */
bool enqueue(MeshPacket *p);
// bool isEmpty();
MeshPacket *dequeue();
/** Attempt to find and remove a packet from this queue. Returns true the packet which was removed from the queue */
MeshPacket *remove(NodeNum from, PacketId id);
};

View File

@@ -60,7 +60,8 @@ static int32_t sendOwnerCb()
currentGeneration = radioGeneration; currentGeneration = radioGeneration;
DEBUG_MSG("Sending our nodeinfo to mesh (wantReplies=%d)\n", requestReplies); DEBUG_MSG("Sending our nodeinfo to mesh (wantReplies=%d)\n", requestReplies);
nodeInfoPlugin.sendOurNodeInfo(NODENUM_BROADCAST, requestReplies); // Send our info (don't request replies) assert(nodeInfoPlugin);
nodeInfoPlugin->sendOurNodeInfo(NODENUM_BROADCAST, requestReplies); // Send our info (don't request replies)
return getPref_send_owner_interval() * getPref_position_broadcast_secs() * 1000; return getPref_send_owner_interval() * getPref_position_broadcast_secs() * 1000;
} }
@@ -134,7 +135,8 @@ bool MeshService::reloadConfig()
/// The owner User record just got updated, update our node DB and broadcast the info into the mesh /// The owner User record just got updated, update our node DB and broadcast the info into the mesh
void MeshService::reloadOwner() void MeshService::reloadOwner()
{ {
nodeInfoPlugin.sendOurNodeInfo(); assert(nodeInfoPlugin);
nodeInfoPlugin->sendOurNodeInfo();
nodeDB.saveToDisk(); nodeDB.saveToDisk();
} }
@@ -167,6 +169,11 @@ void MeshService::handleToRadio(MeshPacket &p)
} }
} }
/** Attempt to cancel a previously sent packet from this _local_ node. Returns true if a packet was found we could cancel */
bool MeshService::cancelSending(PacketId id) {
return router->cancelSending(nodeDB.getNodeNum(), id);
}
void MeshService::sendToMesh(MeshPacket *p) void MeshService::sendToMesh(MeshPacket *p)
{ {
nodeDB.updateFrom(*p); // update our local DB for this packet (because phone might have sent position packets etc...) nodeDB.updateFrom(*p); // update our local DB for this packet (because phone might have sent position packets etc...)
@@ -174,7 +181,7 @@ void MeshService::sendToMesh(MeshPacket *p)
// Strip out any time information before sending packets to other nodes - to keep the wire size small (and because other // Strip out any time information before sending packets to other nodes - to keep the wire size small (and because other
// nodes shouldn't trust it anyways) Note: we allow a device with a local GPS to include the time, so that gpsless // nodes shouldn't trust it anyways) Note: we allow a device with a local GPS to include the time, so that gpsless
// devices can get time. // devices can get time.
if (p->which_payload == MeshPacket_decoded_tag && p->decoded.which_payload == SubPacket_position_tag && if (p->which_payloadVariant == MeshPacket_decoded_tag && p->decoded.which_payloadVariant == SubPacket_position_tag &&
p->decoded.position.time) { p->decoded.position.time) {
if (getRTCQuality() < RTCQualityGPS) { if (getRTCQuality() < RTCQualityGPS) {
DEBUG_MSG("Stripping time %u from position send\n", p->decoded.position.time); DEBUG_MSG("Stripping time %u from position send\n", p->decoded.position.time);
@@ -193,17 +200,40 @@ void MeshService::sendNetworkPing(NodeNum dest, bool wantReplies)
assert(node); assert(node);
DEBUG_MSG("Sending network ping to 0x%x, with position=%d, wantReplies=%d\n", dest, node->has_position, wantReplies); 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) if (node->has_position)
positionPlugin.sendOurPosition(dest, wantReplies); positionPlugin->sendOurPosition(dest, wantReplies);
else else
nodeInfoPlugin.sendOurNodeInfo(dest, wantReplies); nodeInfoPlugin->sendOurNodeInfo(dest, wantReplies);
}
NodeInfo *MeshService::refreshMyNodeInfo() {
NodeInfo *node = nodeDB.getNode(nodeDB.getNodeNum());
assert(node);
// We might not have a position yet for our local node, in that case, at least try to send the time
if(!node->has_position) {
memset(&node->position, 0, sizeof(node->position));
node->has_position = true;
}
Position &position = node->position;
// Update our local node info with our position (even if we don't decide to update anyone else)
position.time = getValidTime(RTCQualityGPS); // This nodedb timestamp might be stale, so update it if our clock is valid.
position.battery_level = powerStatus->getBatteryChargePercent();
updateBatteryLevel(position.battery_level);
return node;
} }
int MeshService::onGPSChanged(const meshtastic::GPSStatus *unused) int MeshService::onGPSChanged(const meshtastic::GPSStatus *unused)
{ {
// Update our local node info with our position (even if we don't decide to update anyone else) // Update our local node info with our position (even if we don't decide to update anyone else)
NodeInfo *node = refreshMyNodeInfo();
Position pos = Position_init_default; Position pos = node->position;
if (gps->hasLock()) { if (gps->hasLock()) {
if (gps->altitude != 0) if (gps->altitude != 0)
@@ -214,21 +244,16 @@ int MeshService::onGPSChanged(const meshtastic::GPSStatus *unused)
else { else {
// The GPS has lost lock, if we are fixed position we should just keep using // The GPS has lost lock, if we are fixed position we should just keep using
// the old position // the old position
if(radioConfig.preferences.fixed_position) { if(!radioConfig.preferences.fixed_position) {
NodeInfo *node = nodeDB.getNode(nodeDB.getNodeNum());
assert(node);
assert(node->has_position);
pos = node->position;
DEBUG_MSG("WARNING: Using fixed position\n"); DEBUG_MSG("WARNING: Using fixed position\n");
} else {
// throw away old position
pos.latitude_i = 0;
pos.longitude_i = 0;
pos.altitude = 0;
} }
} }
pos.time = getValidTime(RTCQualityGPS);
// Include our current battery voltage in our position announcement
pos.battery_level = powerStatus->getBatteryChargePercent();
updateBatteryLevel(pos.battery_level);
DEBUG_MSG("got gps notify time=%u, lat=%d, bat=%d\n", pos.latitude_i, pos.time, pos.battery_level); DEBUG_MSG("got gps notify time=%u, lat=%d, bat=%d\n", pos.latitude_i, pos.time, pos.battery_level);
// Update our current position in the local DB // Update our current position in the local DB
@@ -247,7 +272,8 @@ int MeshService::onGPSChanged(const meshtastic::GPSStatus *unused)
currentGeneration = radioGeneration; currentGeneration = radioGeneration;
DEBUG_MSG("Sending position to mesh (wantReplies=%d)\n", requestReplies); DEBUG_MSG("Sending position to mesh (wantReplies=%d)\n", requestReplies);
positionPlugin.sendOurPosition(NODENUM_BROADCAST, requestReplies); assert(positionPlugin);
positionPlugin->sendOurPosition(NODENUM_BROADCAST, requestReplies);
} }
return 0; return 0;

View File

@@ -79,6 +79,12 @@ class MeshService
/// cache /// cache
void sendToMesh(MeshPacket *p); void sendToMesh(MeshPacket *p);
/** Attempt to cancel a previously sent packet from this _local_ node. Returns true if a packet was found we could cancel */
bool cancelSending(PacketId id);
/// Pull the latest power and time info into my nodeinfo
NodeInfo *refreshMyNodeInfo();
private: private:
/// Called when our gps position has changed - updates nodedb and sends Location message out into the mesh /// Called when our gps position has changed - updates nodedb and sends Location message out into the mesh

View File

@@ -5,7 +5,9 @@
#include "FS.h" #include "FS.h"
#include "CryptoEngine.h" #include "CryptoEngine.h"
#include "FSCommon.h"
#include "GPS.h" #include "GPS.h"
#include "main.h"
#include "MeshRadio.h" #include "MeshRadio.h"
#include "NodeDB.h" #include "NodeDB.h"
#include "PacketHistory.h" #include "PacketHistory.h"
@@ -15,11 +17,13 @@
#include "configuration.h" #include "configuration.h"
#include "error.h" #include "error.h"
#include "mesh-pb-constants.h" #include "mesh-pb-constants.h"
#include "meshwifi/meshwifi.h"
#include "FSCommon.h"
#include <pb_decode.h> #include <pb_decode.h>
#include <pb_encode.h> #include <pb_encode.h>
#ifndef NO_ESP32
#include "mesh/http/WiFiAPClient.h"
#endif
NodeDB nodeDB; NodeDB nodeDB;
// we have plenty of ram so statically alloc this tempbuf (for now) // we have plenty of ram so statically alloc this tempbuf (for now)
@@ -41,8 +45,6 @@ DeviceState versions used to be defined in the .proto file but really only this
#define DEVICESTATE_CUR_VER 11 #define DEVICESTATE_CUR_VER 11
#define DEVICESTATE_MIN_VER DEVICESTATE_CUR_VER #define DEVICESTATE_MIN_VER DEVICESTATE_CUR_VER
// FIXME - move this somewhere else // FIXME - move this somewhere else
extern void getMacAddr(uint8_t *dmac); extern void getMacAddr(uint8_t *dmac);
@@ -90,7 +92,7 @@ const char *getChannelName()
static char buf[32]; static char buf[32];
char suffix; char suffix;
if(channelSettings.psk.size != 1) { if (channelSettings.psk.size != 1) {
// We have a standard PSK, so generate a letter based hash. // We have a standard PSK, so generate a letter based hash.
uint8_t code = 0; uint8_t code = 0;
for (int i = 0; i < activePSKSize; i++) for (int i = 0; i < activePSKSize; i++)
@@ -140,43 +142,55 @@ bool NodeDB::resetRadioConfig()
} }
// Convert the old string "Default" to our new short representation // Convert the old string "Default" to our new short representation
if(strcmp(channelSettings.name, "Default") == 0) if (strcmp(channelSettings.name, "Default") == 0)
*channelSettings.name = '\0'; *channelSettings.name = '\0';
// Convert the short "" representation for Default into a usable string // Convert the short "" representation for Default into a usable string
channelName = channelSettings.name; channelName = channelSettings.name;
if(!*channelName) { // emptystring if (!*channelName) { // emptystring
// Per mesh.proto spec, if bandwidth is specified we must ignore modemConfig enum, we assume that in that case // 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 // the app fucked up and forgot to set channelSettings.name
channelName = "Unset";
if(channelSettings.bandwidth == 0) switch(channelSettings.modem_config) { if (channelSettings.bandwidth != 0)
channelName = "Unset";
else
switch (channelSettings.modem_config) {
case ChannelSettings_ModemConfig_Bw125Cr45Sf128: case ChannelSettings_ModemConfig_Bw125Cr45Sf128:
channelName = "Medium"; break; channelName = "Medium";
break;
case ChannelSettings_ModemConfig_Bw500Cr45Sf128: case ChannelSettings_ModemConfig_Bw500Cr45Sf128:
channelName = "ShortFast"; break; channelName = "ShortFast";
break;
case ChannelSettings_ModemConfig_Bw31_25Cr48Sf512: case ChannelSettings_ModemConfig_Bw31_25Cr48Sf512:
channelName = "LongAlt"; break; channelName = "LongAlt";
break;
case ChannelSettings_ModemConfig_Bw125Cr48Sf4096: case ChannelSettings_ModemConfig_Bw125Cr48Sf4096:
channelName = "LongSlow"; break; channelName = "LongSlow";
break;
default: default:
channelName = "Invalid"; break; channelName = "Invalid";
} break;
}
} }
// Convert any old usage of the defaultpsk into our new short representation. // Convert any old usage of the defaultpsk into our new short representation.
if(channelSettings.psk.size == sizeof(defaultpsk) && if (channelSettings.psk.size == sizeof(defaultpsk) &&
memcmp(channelSettings.psk.bytes, defaultpsk, sizeof(defaultpsk)) == 0) { memcmp(channelSettings.psk.bytes, defaultpsk, sizeof(defaultpsk)) == 0) {
*channelSettings.psk.bytes = 1; *channelSettings.psk.bytes = 1;
channelSettings.psk.size = 1; channelSettings.psk.size = 1;
} }
// Convert the short single byte variants of psk into variant that can be used more generally memset(activePSK, 0, sizeof(activePSK)); // In case the user provided a short key, we want to pad the rest with zeros
memcpy(activePSK, channelSettings.psk.bytes, channelSettings.psk.size); memcpy(activePSK, channelSettings.psk.bytes, channelSettings.psk.size);
activePSKSize = channelSettings.psk.size; activePSKSize = channelSettings.psk.size;
if(activePSKSize == 1) { if(activePSKSize == 0)
DEBUG_MSG("Warning: User disabled encryption\n");
else if (activePSKSize == 1) {
// Convert the short single byte variants of psk into variant that can be used more generally
uint8_t pskIndex = activePSK[0]; uint8_t pskIndex = activePSK[0];
DEBUG_MSG("Expanding short PSK #%d\n", pskIndex); DEBUG_MSG("Expanding short PSK #%d\n", pskIndex);
if(pskIndex == 0) if (pskIndex == 0)
activePSKSize = 0; // Turn off encryption activePSKSize = 0; // Turn off encryption
else { else {
memcpy(activePSK, defaultpsk, sizeof(defaultpsk)); memcpy(activePSK, defaultpsk, sizeof(defaultpsk));
@@ -185,6 +199,16 @@ bool NodeDB::resetRadioConfig()
uint8_t *last = activePSK + sizeof(defaultpsk) - 1; uint8_t *last = activePSK + sizeof(defaultpsk) - 1;
*last = *last + pskIndex - 1; // index of 1 means no change vs defaultPSK *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 // Tell our crypto engine about the psk
@@ -241,16 +265,15 @@ void NodeDB::installDefaultDeviceState()
// Init our blank owner info to reasonable defaults // Init our blank owner info to reasonable defaults
getMacAddr(ourMacAddr); getMacAddr(ourMacAddr);
sprintf(owner.id, "!%02x%02x%02x%02x%02x%02x", ourMacAddr[0], ourMacAddr[1], ourMacAddr[2], ourMacAddr[3], ourMacAddr[4],
ourMacAddr[5]);
memcpy(owner.macaddr, ourMacAddr, sizeof(owner.macaddr));
// Set default owner name // Set default owner name
pickNewNodeNum(); // Note: we will repick later, just in case the settings are corrupted, but we need a valid pickNewNodeNum(); // based on macaddr now
// owner.short_name now
sprintf(owner.long_name, "Unknown %02x%02x", ourMacAddr[4], ourMacAddr[5]); sprintf(owner.long_name, "Unknown %02x%02x", ourMacAddr[4], ourMacAddr[5]);
sprintf(owner.short_name, "?%02X", (unsigned)(myNodeInfo.my_node_num & 0xff)); sprintf(owner.short_name, "?%02X", (unsigned)(myNodeInfo.my_node_num & 0xff));
sprintf(owner.id, "!%08x", getNodeNum()); // Default node ID now based on nodenum
memcpy(owner.macaddr, ourMacAddr, sizeof(owner.macaddr));
// Restore region if possible // Restore region if possible
if (oldRegionCode != RegionCode_Unset) if (oldRegionCode != RegionCode_Unset)
radioConfig.preferences.region = oldRegionCode; radioConfig.preferences.region = oldRegionCode;
@@ -271,7 +294,8 @@ void NodeDB::init()
myNodeInfo.node_num_bits = sizeof(NodeNum) * 8; myNodeInfo.node_num_bits = sizeof(NodeNum) * 8;
myNodeInfo.packet_id_bits = sizeof(PacketId) * 8; myNodeInfo.packet_id_bits = sizeof(PacketId) * 8;
myNodeInfo.error_code = CriticalErrorCode_None; // For the error code, only show values from this boot (discard value from flash) myNodeInfo.error_code =
CriticalErrorCode_None; // For the error code, only show values from this boot (discard value from flash)
myNodeInfo.error_address = 0; myNodeInfo.error_address = 0;
// likewise - we always want the app requirements to come from the running appload // likewise - we always want the app requirements to come from the running appload
@@ -501,7 +525,7 @@ void NodeDB::updateUser(uint32_t nodeId, const User &p)
/// we updateGUI and updateGUIforNode if we think our this change is big enough for a redraw /// we updateGUI and updateGUIforNode if we think our this change is big enough for a redraw
void NodeDB::updateFrom(const MeshPacket &mp) void NodeDB::updateFrom(const MeshPacket &mp)
{ {
if (mp.which_payload == MeshPacket_decoded_tag) { if (mp.which_payloadVariant == MeshPacket_decoded_tag) {
const SubPacket &p = mp.decoded; const SubPacket &p = mp.decoded;
DEBUG_MSG("Update DB node 0x%x, rx_time=%u\n", mp.from, mp.rx_time); DEBUG_MSG("Update DB node 0x%x, rx_time=%u\n", mp.from, mp.rx_time);
@@ -514,7 +538,7 @@ void NodeDB::updateFrom(const MeshPacket &mp)
info->snr = mp.rx_snr; // keep the most recent SNR we received for this node. info->snr = mp.rx_snr; // keep the most recent SNR we received for this node.
switch (p.which_payload) { switch (p.which_payloadVariant) {
case SubPacket_position_tag: { case SubPacket_position_tag: {
// handle a legacy position packet // handle a legacy position packet
DEBUG_MSG("WARNING: Processing a (deprecated) position packet from %d\n", mp.from); DEBUG_MSG("WARNING: Processing a (deprecated) position packet from %d\n", mp.from);
@@ -573,8 +597,14 @@ NodeInfo *NodeDB::getOrCreateNode(NodeNum n)
/// Record an error that should be reported via analytics /// Record an error that should be reported via analytics
void recordCriticalError(CriticalErrorCode code, uint32_t address) void recordCriticalError(CriticalErrorCode code, uint32_t address)
{ {
DEBUG_MSG("NOTE! Recording critical error %d, address=%x\n", code, address); // Print error to screen and serial port
String lcd = String("Critical error ") + code + "!\n";
screen->print(lcd.c_str());
DEBUG_MSG("NOTE! Recording critical error %d, address=%lx\n", code, address);
// Record error to DB
myNodeInfo.error_code = code; myNodeInfo.error_code = code;
myNodeInfo.error_address = address; myNodeInfo.error_address = address;
myNodeInfo.error_count++; myNodeInfo.error_count++;
} }

View File

@@ -59,15 +59,15 @@ void PhoneAPI::handleToRadio(const uint8_t *buf, size_t bufLength)
// return (lastContactMsec != 0) && // return (lastContactMsec != 0) &&
if (pb_decode_from_bytes(buf, bufLength, ToRadio_fields, &toRadioScratch)) { if (pb_decode_from_bytes(buf, bufLength, ToRadio_fields, &toRadioScratch)) {
switch (toRadioScratch.which_variant) { switch (toRadioScratch.which_payloadVariant) {
case ToRadio_packet_tag: { case ToRadio_packet_tag: {
MeshPacket &p = toRadioScratch.variant.packet; MeshPacket &p = toRadioScratch.packet;
printPacket("PACKET FROM PHONE", &p); printPacket("PACKET FROM PHONE", &p);
service.handleToRadio(p); service.handleToRadio(p);
break; break;
} }
case ToRadio_want_config_id_tag: case ToRadio_want_config_id_tag:
config_nonce = toRadioScratch.variant.want_config_id; config_nonce = toRadioScratch.want_config_id;
DEBUG_MSG("Client wants config, nonce=%u\n", config_nonce); DEBUG_MSG("Client wants config, nonce=%u\n", config_nonce);
state = STATE_SEND_MY_INFO; state = STATE_SEND_MY_INFO;
@@ -79,12 +79,12 @@ void PhoneAPI::handleToRadio(const uint8_t *buf, size_t bufLength)
case ToRadio_set_owner_tag: case ToRadio_set_owner_tag:
DEBUG_MSG("Client is setting owner\n"); DEBUG_MSG("Client is setting owner\n");
handleSetOwner(toRadioScratch.variant.set_owner); handleSetOwner(toRadioScratch.set_owner);
break; break;
case ToRadio_set_radio_tag: case ToRadio_set_radio_tag:
DEBUG_MSG("Client is setting radio\n"); DEBUG_MSG("Client is setting radio\n");
handleSetRadio(toRadioScratch.variant.set_radio); handleSetRadio(toRadioScratch.set_radio);
break; break;
default: default:
@@ -131,20 +131,22 @@ size_t PhoneAPI::getFromRadio(uint8_t *buf)
myNodeInfo.has_gps = (radioConfig.preferences.location_share == LocationSharing_LocDisabled) myNodeInfo.has_gps = (radioConfig.preferences.location_share == LocationSharing_LocDisabled)
? true ? true
: (gps && gps->isConnected()); // Update with latest GPS connect info : (gps && gps->isConnected()); // Update with latest GPS connect info
fromRadioScratch.which_variant = FromRadio_my_info_tag; fromRadioScratch.which_payloadVariant = FromRadio_my_info_tag;
fromRadioScratch.variant.my_info = myNodeInfo; fromRadioScratch.my_info = myNodeInfo;
state = STATE_SEND_RADIO; state = STATE_SEND_RADIO;
service.refreshMyNodeInfo(); // Update my NodeInfo because the client will be asking for it soon.
break; break;
case STATE_SEND_RADIO: case STATE_SEND_RADIO:
fromRadioScratch.which_variant = FromRadio_radio_tag; fromRadioScratch.which_payloadVariant = FromRadio_radio_tag;
fromRadioScratch.variant.radio = radioConfig; fromRadioScratch.radio = radioConfig;
// NOTE: The phone app needs to know the ls_secs value so it can properly expect sleep behavior. // 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 // 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). // using to the app (so that even old phone apps work with new device loads).
fromRadioScratch.variant.radio.preferences.ls_secs = getPref_ls_secs(); fromRadioScratch.radio.preferences.ls_secs = getPref_ls_secs();
state = STATE_SEND_NODEINFO; state = STATE_SEND_NODEINFO;
break; break;
@@ -156,8 +158,8 @@ size_t PhoneAPI::getFromRadio(uint8_t *buf)
if (info) { if (info) {
DEBUG_MSG("Sending nodeinfo: num=0x%x, lastseen=%u, id=%s, name=%s\n", info->num, info->position.time, info->user.id, DEBUG_MSG("Sending nodeinfo: num=0x%x, lastseen=%u, id=%s, name=%s\n", info->num, info->position.time, info->user.id,
info->user.long_name); info->user.long_name);
fromRadioScratch.which_variant = FromRadio_node_info_tag; fromRadioScratch.which_payloadVariant = FromRadio_node_info_tag;
fromRadioScratch.variant.node_info = *info; fromRadioScratch.node_info = *info;
// Stay in current state until done sending nodeinfos // Stay in current state until done sending nodeinfos
} else { } else {
DEBUG_MSG("Done sending nodeinfos\n"); DEBUG_MSG("Done sending nodeinfos\n");
@@ -169,8 +171,8 @@ size_t PhoneAPI::getFromRadio(uint8_t *buf)
} }
case STATE_SEND_COMPLETE_ID: case STATE_SEND_COMPLETE_ID:
fromRadioScratch.which_variant = FromRadio_config_complete_id_tag; fromRadioScratch.which_payloadVariant = FromRadio_config_complete_id_tag;
fromRadioScratch.variant.config_complete_id = config_nonce; fromRadioScratch.config_complete_id = config_nonce;
config_nonce = 0; config_nonce = 0;
state = STATE_SEND_PACKETS; state = STATE_SEND_PACKETS;
break; break;
@@ -183,8 +185,8 @@ size_t PhoneAPI::getFromRadio(uint8_t *buf)
printPacket("phone downloaded packet", packetForPhone); printPacket("phone downloaded packet", packetForPhone);
// Encapsulate as a FromRadio packet // Encapsulate as a FromRadio packet
fromRadioScratch.which_variant = FromRadio_packet_tag; fromRadioScratch.which_payloadVariant = FromRadio_packet_tag;
fromRadioScratch.variant.packet = *packetForPhone; fromRadioScratch.packet = *packetForPhone;
service.releaseToPool(packetForPhone); // we just copied the bytes, so don't need this buffer anymore service.releaseToPool(packetForPhone); // we just copied the bytes, so don't need this buffer anymore
packetForPhone = NULL; packetForPhone = NULL;
@@ -196,9 +198,9 @@ size_t PhoneAPI::getFromRadio(uint8_t *buf)
} }
// Do we have a message from the mesh? // Do we have a message from the mesh?
if (fromRadioScratch.which_variant != 0) { if (fromRadioScratch.which_payloadVariant != 0) {
// Encapsulate as a FromRadio packet // Encapsulate as a FromRadio packet
DEBUG_MSG("encoding toPhone packet to phone variant=%d", fromRadioScratch.which_variant); DEBUG_MSG("encoding toPhone packet to phone variant=%d", fromRadioScratch.which_payloadVariant);
size_t numbytes = pb_encode_to_bytes(buf, FromRadio_size, FromRadio_fields, &fromRadioScratch); size_t numbytes = pb_encode_to_bytes(buf, FromRadio_size, FromRadio_fields, &fromRadioScratch);
DEBUG_MSG(", %d bytes\n", numbytes); DEBUG_MSG(", %d bytes\n", numbytes);
return numbytes; return numbytes;

View File

@@ -83,7 +83,7 @@ uint32_t RadioInterface::getPacketTime(uint32_t pl)
uint32_t RadioInterface::getPacketTime(MeshPacket *p) uint32_t RadioInterface::getPacketTime(MeshPacket *p)
{ {
assert(p->which_payload == MeshPacket_encrypted_tag); // It should have already been encoded by now assert(p->which_payloadVariant == MeshPacket_encrypted_tag); // It should have already been encoded by now
uint32_t pl = p->encrypted.size + sizeof(PacketHeader); uint32_t pl = p->encrypted.size + sizeof(PacketHeader);
return getPacketTime(pl); return getPacketTime(pl);
@@ -119,9 +119,9 @@ void printPacket(const char *prefix, const MeshPacket *p)
{ {
DEBUG_MSG("%s (id=0x%08x Fr0x%02x To0x%02x, WantAck%d, HopLim%d", prefix, p->id, p->from & 0xff, p->to & 0xff, p->want_ack, 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); p->hop_limit);
if (p->which_payload == MeshPacket_decoded_tag) { if (p->which_payloadVariant == MeshPacket_decoded_tag) {
auto &s = p->decoded; auto &s = p->decoded;
switch (s.which_payload) { switch (s.which_payloadVariant) {
case SubPacket_data_tag: case SubPacket_data_tag:
DEBUG_MSG(" Portnum=%d", s.data.portnum); DEBUG_MSG(" Portnum=%d", s.data.portnum);
break; break;
@@ -135,7 +135,7 @@ void printPacket(const char *prefix, const MeshPacket *p)
DEBUG_MSG(" Payload:None"); DEBUG_MSG(" Payload:None");
break; break;
default: default:
DEBUG_MSG(" Payload:%d", s.which_payload); DEBUG_MSG(" Payload:%d", s.which_payloadVariant);
break; break;
} }
if (s.want_response) if (s.want_response)
@@ -147,10 +147,10 @@ void printPacket(const char *prefix, const MeshPacket *p)
if (s.dest != 0) if (s.dest != 0)
DEBUG_MSG(" dest=%08x", s.dest); DEBUG_MSG(" dest=%08x", s.dest);
if (s.which_ack == SubPacket_success_id_tag) if (s.which_ackVariant == SubPacket_success_id_tag)
DEBUG_MSG(" successId=%08x", s.ack.success_id); DEBUG_MSG(" successId=%08x", s.ackVariant.success_id);
else if (s.which_ack == SubPacket_fail_id_tag) else if (s.which_ackVariant == SubPacket_fail_id_tag)
DEBUG_MSG(" failId=%08x", s.ack.fail_id); DEBUG_MSG(" failId=%08x", s.ackVariant.fail_id);
} else { } else {
DEBUG_MSG(" encrypted"); DEBUG_MSG(" encrypted");
} }
@@ -161,6 +161,9 @@ void printPacket(const char *prefix, const MeshPacket *p)
if (p->rx_snr != 0.0) { if (p->rx_snr != 0.0) {
DEBUG_MSG(" rxSNR=%g", p->rx_snr); DEBUG_MSG(" rxSNR=%g", p->rx_snr);
} }
if(p->priority != 0)
DEBUG_MSG(" priority=%d", p->priority);
DEBUG_MSG(")\n"); DEBUG_MSG(")\n");
} }
@@ -315,7 +318,7 @@ size_t RadioInterface::beginSending(MeshPacket *p)
assert(!sendingPacket); assert(!sendingPacket);
// DEBUG_MSG("sending queued packet on mesh (txGood=%d,rxGood=%d,rxBad=%d)\n", rf95.txGood(), rf95.rxGood(), rf95.rxBad()); // DEBUG_MSG("sending queued packet on mesh (txGood=%d,rxGood=%d,rxBad=%d)\n", rf95.txGood(), rf95.rxGood(), rf95.rxBad());
assert(p->which_payload == MeshPacket_encrypted_tag); // It should have already been encoded by now assert(p->which_payloadVariant == MeshPacket_encrypted_tag); // It should have already been encoded by now
lastTxStart = millis(); lastTxStart = millis();

View File

@@ -105,6 +105,9 @@ class RadioInterface
*/ */
virtual ErrorCode send(MeshPacket *p) = 0; virtual ErrorCode send(MeshPacket *p) = 0;
/** Attempt to cancel a previously sent packet. Returns true if a packet was found we could cancel */
virtual bool cancelSending(NodeNum from, PacketId id) { return false; }
// methods from radiohead // methods from radiohead
/// Initialise the Driver transport hardware and software. /// Initialise the Driver transport hardware and software.

View File

@@ -3,6 +3,7 @@
#include "NodeDB.h" #include "NodeDB.h"
#include "SPILock.h" #include "SPILock.h"
#include "mesh-pb-constants.h" #include "mesh-pb-constants.h"
#include "error.h"
#include <configuration.h> #include <configuration.h>
#include <pb_decode.h> #include <pb_decode.h>
#include <pb_encode.h> #include <pb_encode.h>
@@ -67,9 +68,20 @@ bool RadioLibInterface::canSendImmediately()
bool busyTx = sendingPacket != NULL; bool busyTx = sendingPacket != NULL;
bool busyRx = isReceiving && isActivelyReceiving(); bool busyRx = isReceiving && isActivelyReceiving();
if (busyTx || busyRx) { if (busyTx || busyRx) {
if (busyTx) if (busyTx)
DEBUG_MSG("Can not send yet, busyTx\n"); DEBUG_MSG("Can not send yet, busyTx\n");
// If we've been trying to send the same packet more than one minute and we haven't gotten a
// TX IRQ from the radio, the radio is probably broken.
if (busyTx && (millis() - lastTxStart > 60000)){
DEBUG_MSG("Hardware Failure! busyTx for more than 60s\n");
recordCriticalError(CriticalErrorCode_TransmitFailed);
#ifndef NO_ESP32
if (busyTx && (millis() - lastTxStart > 65000)) // After 5s more, reboot
ESP.restart();
#endif
}
if (busyRx) if (busyRx)
DEBUG_MSG("Can not send yet, busyRx\n"); DEBUG_MSG("Can not send yet, busyRx\n");
return false; return false;
@@ -88,7 +100,7 @@ ErrorCode RadioLibInterface::send(MeshPacket *p)
uint32_t xmitMsec = getPacketTime(p); uint32_t xmitMsec = getPacketTime(p);
DEBUG_MSG("txGood=%d,rxGood=%d,rxBad=%d\n", txGood, rxGood, rxBad); DEBUG_MSG("txGood=%d,rxGood=%d,rxBad=%d\n", txGood, rxGood, rxBad);
ErrorCode res = txQueue.enqueue(p, 0) ? ERRNO_OK : ERRNO_UNKNOWN; ErrorCode res = txQueue.enqueue(p) ? ERRNO_OK : ERRNO_UNKNOWN;
if (res != ERRNO_OK) { // we weren't able to queue it, so we must drop it to prevent leaks if (res != ERRNO_OK) { // we weren't able to queue it, so we must drop it to prevent leaks
packetPool.release(p); packetPool.release(p);
@@ -97,7 +109,8 @@ ErrorCode RadioLibInterface::send(MeshPacket *p)
// Count the packet toward our TX airtime utilization. // Count the packet toward our TX airtime utilization.
// We only count it if it can be added to the TX queue. // We only count it if it can be added to the TX queue.
logAirtime(TX_LOG, xmitMsec); airTime->logAirtime(TX_LOG, xmitMsec);
//airTime.logAirtime(TX_LOG, xmitMsec);
// We want all sending/receiving to be done by our daemon thread, We use a delay here because this packet might have been sent // 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 // 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
@@ -112,13 +125,25 @@ ErrorCode RadioLibInterface::send(MeshPacket *p)
bool RadioLibInterface::canSleep() bool RadioLibInterface::canSleep()
{ {
bool res = txQueue.isEmpty(); bool res = txQueue.empty();
if (!res) // only print debug messages if we are vetoing sleep if (!res) // only print debug messages if we are vetoing sleep
DEBUG_MSG("radio wait to sleep, txEmpty=%d\n", res); DEBUG_MSG("radio wait to sleep, txEmpty=%d\n", res);
return res; return res;
} }
/** Attempt to cancel a previously sent packet. Returns true if a packet was found we could cancel */
bool RadioLibInterface::cancelSending(NodeNum from, PacketId id) {
auto p = txQueue.remove(from, id);
if(p)
packetPool.release(p); // free the packet we just removed
bool result = (p != NULL);
DEBUG_MSG("cancelSending id=0x%x, removed=%d", id, result);
return result;
}
/** radio helper thread callback. /** radio helper thread callback.
We never immediately transmit after any operation (either rx or tx). Instead we should start receiving and We never immediately transmit after any operation (either rx or tx). Instead we should start receiving and
@@ -152,12 +177,12 @@ void RadioLibInterface::onNotify(uint32_t notification)
// If we are not currently in receive mode, then restart the timer and try again later (this can happen if the main thread // If we are not currently in receive mode, then restart the timer and try again later (this can happen if the main thread
// has placed the unit into standby) FIXME, how will this work if the chipset is in sleep mode? // has placed the unit into standby) FIXME, how will this work if the chipset is in sleep mode?
if (!txQueue.isEmpty()) { if (!txQueue.empty()) {
if (!canSendImmediately()) { if (!canSendImmediately()) {
startTransmitTimer(); // try again in a little while startTransmitTimer(); // try again in a little while
} else { } else {
// Send any outgoing packets we have ready // Send any outgoing packets we have ready
MeshPacket *txp = txQueue.dequeuePtr(0); MeshPacket *txp = txQueue.dequeue();
assert(txp); assert(txp);
startSend(txp); startSend(txp);
} }
@@ -173,7 +198,7 @@ void RadioLibInterface::onNotify(uint32_t notification)
void RadioLibInterface::startTransmitTimer(bool withDelay) void RadioLibInterface::startTransmitTimer(bool withDelay)
{ {
// If we have work to do and the timer wasn't already scheduled, schedule it now // If we have work to do and the timer wasn't already scheduled, schedule it now
if (!txQueue.isEmpty()) { if (!txQueue.empty()) {
uint32_t delay = !withDelay ? 1 : getTxDelayMsec(); uint32_t delay = !withDelay ? 1 : getTxDelayMsec();
// DEBUG_MSG("xmit timer %d\n", delay); // DEBUG_MSG("xmit timer %d\n", delay);
notifyLater(delay, TRANSMIT_DELAY_COMPLETED, false); // This will implicitly enable notifyLater(delay, TRANSMIT_DELAY_COMPLETED, false); // This will implicitly enable
@@ -216,7 +241,8 @@ void RadioLibInterface::handleReceiveInterrupt()
size_t length = iface->getPacketLength(); size_t length = iface->getPacketLength();
xmitMsec = getPacketTime(length); xmitMsec = getPacketTime(length);
logAirtime(RX_ALL_LOG, xmitMsec); airTime->logAirtime(RX_ALL_LOG, xmitMsec);
//airTime.logAirtime(RX_ALL_LOG, xmitMsec);
int state = iface->readData(radiobuf, length); int state = iface->readData(radiobuf, length);
if (state != ERR_NONE) { if (state != ERR_NONE) {
@@ -250,7 +276,7 @@ void RadioLibInterface::handleReceiveInterrupt()
addReceiveMetadata(mp); addReceiveMetadata(mp);
mp->which_payload = MeshPacket_encrypted_tag; // Mark that the payload is still encrypted at this point 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); memcpy(mp->encrypted.bytes, payload, payloadLen);
mp->encrypted.size = payloadLen; mp->encrypted.size = payloadLen;
@@ -258,7 +284,8 @@ void RadioLibInterface::handleReceiveInterrupt()
printPacket("Lora RX", mp); printPacket("Lora RX", mp);
xmitMsec = getPacketTime(mp); xmitMsec = getPacketTime(mp);
logAirtime(RX_LOG, xmitMsec); airTime->logAirtime(RX_LOG, xmitMsec);
//airTime.logAirtime(RX_LOG, xmitMsec);
deliverToReceiver(mp); deliverToReceiver(mp);
} }

View File

@@ -2,6 +2,7 @@
#include "../concurrency/OSThread.h" #include "../concurrency/OSThread.h"
#include "RadioInterface.h" #include "RadioInterface.h"
#include "MeshPacketQueue.h"
#ifdef CubeCell_BoardPlus #ifdef CubeCell_BoardPlus
#define RADIOLIB_SOFTWARE_SERIAL_UNSUPPORTED #define RADIOLIB_SOFTWARE_SERIAL_UNSUPPORTED
@@ -74,7 +75,7 @@ class RadioLibInterface : public RadioInterface, protected concurrency::Notified
*/ */
uint32_t rxBad = 0, rxGood = 0, txGood = 0; uint32_t rxBad = 0, rxGood = 0, txGood = 0;
PointerQueue<MeshPacket> txQueue = PointerQueue<MeshPacket>(MAX_TX_QUEUE); MeshPacketQueue txQueue = MeshPacketQueue(MAX_TX_QUEUE);
protected: protected:
@@ -136,6 +137,9 @@ class RadioLibInterface : public RadioInterface, protected concurrency::Notified
*/ */
virtual bool isActivelyReceiving() = 0; virtual bool isActivelyReceiving() = 0;
/** Attempt to cancel a previously sent packet. Returns true if a packet was found we could cancel */
virtual bool cancelSending(NodeNum from, PacketId id);
private: private:
/** if we have something waiting to send, start a short random timer so we can come check for collision before actually doing /** if we have something waiting to send, start a short random timer so we can come check for collision before actually doing
* the transmit * the transmit

View File

@@ -34,7 +34,7 @@ bool ReliableRouter::shouldFilterReceived(const MeshPacket *p)
// the original sending process. // the original sending process.
if (stopRetransmission(p->from, p->id)) { if (stopRetransmission(p->from, p->id)) {
DEBUG_MSG("Someone is retransmitting for us, generate implicit ack\n"); DEBUG_MSG("Someone is retransmitting for us, generate implicit ack\n");
sendAckNak(true, p->from, p->id); sendAckNak(ErrorReason_NONE, p->from, p->id);
} }
} }
@@ -60,13 +60,13 @@ void ReliableRouter::sniffReceived(const MeshPacket *p)
if (p->to == ourNode) { // ignore ack/nak/want_ack packets that are not address to us (we only handle 0 hop reliability 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) // - not DSR routing)
if (p->want_ack) { if (p->want_ack) {
sendAckNak(true, p->from, p->id); sendAckNak(ErrorReason_NONE, p->from, p->id);
} }
// If the payload is valid, look for ack/nak // If the payload is valid, look for ack/nak
PacketId ackId = p->decoded.which_ack == SubPacket_success_id_tag ? p->decoded.ack.success_id : 0; PacketId ackId = p->decoded.which_ackVariant == SubPacket_success_id_tag ? p->decoded.ackVariant.success_id : 0;
PacketId nakId = p->decoded.which_ack == SubPacket_fail_id_tag ? p->decoded.ack.fail_id : 0; PacketId nakId = p->decoded.which_ackVariant == SubPacket_fail_id_tag ? p->decoded.ackVariant.fail_id : 0;
// We intentionally don't check wasSeenRecently, because it is harmless to delete non existent retransmission records // We intentionally don't check wasSeenRecently, because it is harmless to delete non existent retransmission records
if (ackId || nakId) { if (ackId || nakId) {
@@ -84,27 +84,6 @@ void ReliableRouter::sniffReceived(const MeshPacket *p)
FloodingRouter::sniffReceived(p); FloodingRouter::sniffReceived(p);
} }
/**
* Send an ack or a nak packet back towards whoever sent idFrom
*/
void ReliableRouter::sendAckNak(bool isAck, NodeNum to, PacketId idFrom)
{
auto p = allocForSending();
p->hop_limit = 0; // Assume just immediate neighbors for now
p->to = to;
DEBUG_MSG("Sending an ack=0x%x,to=0x%x,idFrom=0x%x,id=0x%x\n", isAck, to, idFrom, p->id);
if (isAck) {
p->decoded.ack.success_id = idFrom;
p->decoded.which_ack = SubPacket_success_id_tag;
} else {
p->decoded.ack.fail_id = idFrom;
p->decoded.which_ack = SubPacket_fail_id_tag;
}
sendLocal(p); // we sometimes send directly to the local node
}
#define NUM_RETRANSMISSIONS 3 #define NUM_RETRANSMISSIONS 3
PendingPacket::PendingPacket(MeshPacket *p) PendingPacket::PendingPacket(MeshPacket *p)
@@ -176,7 +155,7 @@ int32_t ReliableRouter::doRetransmissions()
if (p.numRetransmissions == 0) { 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 fr=0x%x,to=0x%x,id=%d\n", p.packet->from, p.packet->to,
p.packet->id); p.packet->id);
sendAckNak(false, p.packet->from, p.packet->id); sendAckNak(ErrorReason_MAX_RETRANSMIT, p.packet->from, p.packet->id);
// Note: we don't stop retransmission here, instead the Nak packet gets processed in sniffReceived - which // 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 // allows the DSR version to still be able to look at the PendingPacket
stopRetransmission(it->first); stopRetransmission(it->first);

View File

@@ -109,10 +109,6 @@ class ReliableRouter : public FloodingRouter
PendingPacket *startRetransmission(MeshPacket *p); PendingPacket *startRetransmission(MeshPacket *p);
private: private:
/**
* Send an ack or a nak packet back towards whoever sent idFrom
*/
void sendAckNak(bool isAck, NodeNum to, PacketId idFrom);
/** /**
* Stop any retransmissions we are doing of the specified node/packet ID pair * Stop any retransmissions we are doing of the specified node/packet ID pair

View File

@@ -88,7 +88,7 @@ MeshPacket *Router::allocForSending()
{ {
MeshPacket *p = packetPool.allocZeroed(); MeshPacket *p = packetPool.allocZeroed();
p->which_payload = MeshPacket_decoded_tag; // Assume payload is decoded at start. p->which_payloadVariant = MeshPacket_decoded_tag; // Assume payload is decoded at start.
p->from = nodeDB.getNodeNum(); p->from = nodeDB.getNodeNum();
p->to = NODENUM_BROADCAST; p->to = NODENUM_BROADCAST;
p->hop_limit = HOP_RELIABLE; p->hop_limit = HOP_RELIABLE;
@@ -99,6 +99,34 @@ MeshPacket *Router::allocForSending()
return p; return p;
} }
/**
* Send an ack or a nak packet back towards whoever sent idFrom
*/
void Router::sendAckNak(ErrorReason err, NodeNum to, PacketId idFrom)
{
auto p = allocForSending();
p->hop_limit = 0; // Assume just immediate neighbors for now
p->to = to;
DEBUG_MSG("Sending an err=%d,to=0x%x,idFrom=0x%x,id=0x%x\n", err, to, idFrom, p->id);
if (!err) {
p->decoded.ackVariant.success_id = idFrom;
p->decoded.which_ackVariant = SubPacket_success_id_tag;
} else {
p->decoded.ackVariant.fail_id = idFrom;
p->decoded.which_ackVariant = SubPacket_fail_id_tag;
// Also send back the error reason
p->decoded.which_payloadVariant = SubPacket_error_reason_tag;
p->decoded.error_reason = err;
}
p->priority = MeshPacket_Priority_ACK;
sendLocal(p); // we sometimes send directly to the local node
}
ErrorCode Router::sendLocal(MeshPacket *p) ErrorCode Router::sendLocal(MeshPacket *p)
{ {
// No need to deliver externally if the destination is the local node // No need to deliver externally if the destination is the local node
@@ -106,15 +134,24 @@ ErrorCode Router::sendLocal(MeshPacket *p)
printPacket("Enqueuing local", p); printPacket("Enqueuing local", p);
fromRadioQueue.enqueue(p); fromRadioQueue.enqueue(p);
return ERRNO_OK; return ERRNO_OK;
} } else if (!iface) {
// We must be sending to remote nodes also, fail if no interface found
// If we are sending a broadcast, we also treat it as if we just received it ourself // ERROR! no radio found, report failure back to the client and drop the packet
// this allows local apps (and PCs) to see broadcasts sourced locally DEBUG_MSG("Error: No interface, returning NAK and dropping packet.\n");
if (p->to == NODENUM_BROADCAST) { sendAckNak(ErrorReason_NO_INTERFACE, p->from, p->id);
handleReceived(p); packetPool.release(p);
}
return send(p); return ERRNO_NO_INTERFACES;
} else {
// If we are sending a broadcast, we also treat it as if we just received it ourself
// this allows local apps (and PCs) to see broadcasts sourced locally
if (p->to == NODENUM_BROADCAST) {
handleReceived(p);
}
return send(p);
}
} }
/** /**
@@ -126,7 +163,7 @@ ErrorCode Router::send(MeshPacket *p)
{ {
assert(p->to != nodeDB.getNodeNum()); // should have already been handled by sendLocal assert(p->to != nodeDB.getNodeNum()); // should have already been handled by sendLocal
PacketId nakId = p->decoded.which_ack == SubPacket_fail_id_tag ? p->decoded.ack.fail_id : 0; PacketId nakId = p->decoded.which_ackVariant == SubPacket_fail_id_tag ? p->decoded.ackVariant.fail_id : 0;
assert( assert(
!nakId); // I don't think we ever send 0hop naks over the wire (other than to the phone), test that assumption with assert !nakId); // I don't think we ever send 0hop naks over the wire (other than to the phone), test that assumption with assert
@@ -136,11 +173,11 @@ ErrorCode Router::send(MeshPacket *p)
// If the packet hasn't yet been encrypted, do so now (it might already be encrypted if we are just forwarding it) // If the packet hasn't yet been encrypted, do so now (it might already be encrypted if we are just forwarding it)
assert(p->which_payload == MeshPacket_encrypted_tag || assert(p->which_payloadVariant == MeshPacket_encrypted_tag ||
p->which_payload == MeshPacket_decoded_tag); // I _think_ all packets should have a payload by now p->which_payloadVariant == MeshPacket_decoded_tag); // I _think_ all packets should have a payload by now
// First convert from protobufs to raw bytes // First convert from protobufs to raw bytes
if (p->which_payload == MeshPacket_decoded_tag) { if (p->which_payloadVariant == MeshPacket_decoded_tag) {
static uint8_t bytes[MAX_RHPACKETLEN]; // we have to use a scratch buffer because a union 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); size_t numbytes = pb_encode_to_bytes(bytes, sizeof(bytes), SubPacket_fields, &p->decoded);
@@ -151,19 +188,27 @@ ErrorCode Router::send(MeshPacket *p)
// Copy back into the packet and set the variant type // Copy back into the packet and set the variant type
memcpy(p->encrypted.bytes, bytes, numbytes); memcpy(p->encrypted.bytes, bytes, numbytes);
p->encrypted.size = numbytes; p->encrypted.size = numbytes;
p->which_payload = MeshPacket_encrypted_tag; p->which_payloadVariant = MeshPacket_encrypted_tag;
} }
if (iface) { assert(iface); // This should have been detected already in sendLocal (or we just received a packet from outside)
// DEBUG_MSG("Sending packet via interface fr=0x%x,to=0x%x,id=%d\n", p->from, p->to, p->id); // if (iface) {
return iface->send(p); // DEBUG_MSG("Sending packet via interface fr=0x%x,to=0x%x,id=%d\n", p->from, p->to, p->id);
} else { 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); DEBUG_MSG("Dropping packet - no interfaces - fr=0x%x,to=0x%x,id=%d\n", p->from, p->to, p->id);
packetPool.release(p); packetPool.release(p);
return ERRNO_NO_INTERFACES; return ERRNO_NO_INTERFACES;
} } */
} }
/** Attempt to cancel a previously sent packet. Returns true if a packet was found we could cancel */
bool Router::cancelSending(NodeNum from, PacketId id) {
return iface ? iface->cancelSending(from, id) : false;
}
/** /**
* Every (non duplicate) packet this node receives will be passed through this method. This allows subclasses to * 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) * update routing tables etc... based on what we overhear (even for messages not destined to our node)
@@ -176,10 +221,10 @@ void Router::sniffReceived(const MeshPacket *p)
bool Router::perhapsDecode(MeshPacket *p) bool Router::perhapsDecode(MeshPacket *p)
{ {
if (p->which_payload == MeshPacket_decoded_tag) if (p->which_payloadVariant == MeshPacket_decoded_tag)
return true; // If packet was already decoded just return return true; // If packet was already decoded just return
assert(p->which_payload == MeshPacket_encrypted_tag); assert(p->which_payloadVariant == MeshPacket_encrypted_tag);
// FIXME - someday don't send routing packets encrypted. That would allow us to route for other channels without // FIXME - someday don't send routing packets encrypted. That would allow us to route for other channels without
// being able to decrypt their data. // being able to decrypt their data.
@@ -195,7 +240,7 @@ bool Router::perhapsDecode(MeshPacket *p)
return false; return false;
} else { } else {
// parsing was successful // parsing was successful
p->which_payload = MeshPacket_decoded_tag; p->which_payloadVariant = MeshPacket_decoded_tag;
return true; return true;
} }
} }

View File

@@ -48,13 +48,19 @@ class Router : protected concurrency::OSThread
virtual int32_t runOnce(); virtual int32_t runOnce();
/** /**
* Works like send, but if we are sending to the local node, we directly put the message in the receive queue * Works like send, but if we are sending to the local node, we directly put the message in the receive queue.
* This is the primary method used for sending packets, because it handles both the remote and local cases.
* *
* NOTE: This method will free the provided packet (even if we return an error code) * NOTE: This method will free the provided packet (even if we return an error code)
*/ */
ErrorCode sendLocal(MeshPacket *p); ErrorCode sendLocal(MeshPacket *p);
/// Allocate and return a meshpacket which defaults as send to broadcast from the current node. /** Attempt to cancel a previously sent packet. Returns true if a packet was found we could cancel */
bool cancelSending(NodeNum from, PacketId id);
/** Allocate and return a meshpacket which defaults as send to broadcast from the current node.
* The returned packet is guaranteed to have a unique packet ID already assigned
*/
MeshPacket *allocForSending(); MeshPacket *allocForSending();
/** /**
@@ -92,6 +98,11 @@ class Router : protected concurrency::OSThread
*/ */
bool perhapsDecode(MeshPacket *p); bool perhapsDecode(MeshPacket *p);
/**
* Send an ack or a nak packet back towards whoever sent idFrom
*/
void sendAckNak(ErrorReason err, NodeNum to, PacketId idFrom);
private: private:
/** /**
* Called from loop() * Called from loop()

View File

@@ -32,7 +32,7 @@ class SinglePortPlugin : public MeshPlugin
{ {
// Update our local node info with our position (even if we don't decide to update anyone else) // Update our local node info with our position (even if we don't decide to update anyone else)
MeshPacket *p = router->allocForSending(); MeshPacket *p = router->allocForSending();
p->decoded.which_payload = SubPacket_data_tag; p->decoded.which_payloadVariant = SubPacket_data_tag;
p->decoded.data.portnum = ourPortNum; p->decoded.data.portnum = ourPortNum;
return p; return p;

View File

@@ -84,8 +84,8 @@ void StreamAPI::emitRebooted()
{ {
// In case we send a FromRadio packet // In case we send a FromRadio packet
memset(&fromRadioScratch, 0, sizeof(fromRadioScratch)); memset(&fromRadioScratch, 0, sizeof(fromRadioScratch));
fromRadioScratch.which_variant = FromRadio_rebooted_tag; fromRadioScratch.which_payloadVariant = FromRadio_rebooted_tag;
fromRadioScratch.variant.rebooted = true; fromRadioScratch.rebooted = true;
DEBUG_MSG("Emitting reboot packet for serial shell\n"); DEBUG_MSG("Emitting reboot packet for serial shell\n");
emitTxBuffer(pb_encode_to_bytes(txBuf + HEADER_LEN, FromRadio_size, FromRadio_fields, &fromRadioScratch)); emitTxBuffer(pb_encode_to_bytes(txBuf + HEADER_LEN, FromRadio_size, FromRadio_fields, &fromRadioScratch));

View File

@@ -80,7 +80,7 @@ extern const pb_msgdesc_t DeviceState_msg;
#define DeviceState_fields &DeviceState_msg #define DeviceState_fields &DeviceState_msg
/* Maximum encoded size of messages (where known) */ /* Maximum encoded size of messages (where known) */
#define DeviceState_size 6176 #define DeviceState_size 6266
#ifdef __cplusplus #ifdef __cplusplus
} /* extern "C" */ } /* extern "C" */

View File

@@ -57,3 +57,5 @@ PB_BIND(ToRadio, ToRadio, 2)

View File

@@ -11,12 +11,14 @@
#endif #endif
/* Enum definitions */ /* Enum definitions */
typedef enum _RouteError { typedef enum _ErrorReason {
RouteError_NONE = 0, ErrorReason_NONE = 0,
RouteError_NO_ROUTE = 1, ErrorReason_NO_ROUTE = 1,
RouteError_GOT_NAK = 2, ErrorReason_GOT_NAK = 2,
RouteError_TIMEOUT = 3 ErrorReason_TIMEOUT = 3,
} RouteError; ErrorReason_NO_INTERFACE = 4,
ErrorReason_MAX_RETRANSMIT = 5
} ErrorReason;
typedef enum _Constants { typedef enum _Constants {
Constants_Unused = 0, Constants_Unused = 0,
@@ -35,8 +37,29 @@ typedef enum _RegionCode {
RegionCode_TW = 8 RegionCode_TW = 8
} RegionCode; } RegionCode;
typedef enum _ChargeCurrent {
ChargeCurrent_MAUnset = 0,
ChargeCurrent_MA100 = 1,
ChargeCurrent_MA190 = 2,
ChargeCurrent_MA280 = 3,
ChargeCurrent_MA360 = 4,
ChargeCurrent_MA450 = 5,
ChargeCurrent_MA550 = 6,
ChargeCurrent_MA630 = 7,
ChargeCurrent_MA700 = 8,
ChargeCurrent_MA780 = 9,
ChargeCurrent_MA880 = 10,
ChargeCurrent_MA960 = 11,
ChargeCurrent_MA1000 = 12,
ChargeCurrent_MA1080 = 13,
ChargeCurrent_MA1160 = 14,
ChargeCurrent_MA1240 = 15,
ChargeCurrent_MA1320 = 16
} ChargeCurrent;
typedef enum _GpsOperation { typedef enum _GpsOperation {
GpsOperation_GpsOpUnset = 0, GpsOperation_GpsOpUnset = 0,
GpsOperation_GpsOpStationary = 1,
GpsOperation_GpsOpMobile = 2, GpsOperation_GpsOpMobile = 2,
GpsOperation_GpsOpTimeOnly = 3, GpsOperation_GpsOpTimeOnly = 3,
GpsOperation_GpsOpDisabled = 4 GpsOperation_GpsOpDisabled = 4
@@ -56,9 +79,20 @@ typedef enum _CriticalErrorCode {
CriticalErrorCode_Unspecified = 4, CriticalErrorCode_Unspecified = 4,
CriticalErrorCode_UBloxInitFailed = 5, CriticalErrorCode_UBloxInitFailed = 5,
CriticalErrorCode_NoAXP192 = 6, CriticalErrorCode_NoAXP192 = 6,
CriticalErrorCode_InvalidRadioSetting = 7 CriticalErrorCode_InvalidRadioSetting = 7,
CriticalErrorCode_TransmitFailed = 8
} CriticalErrorCode; } CriticalErrorCode;
typedef enum _MeshPacket_Priority {
MeshPacket_Priority_UNSET = 0,
MeshPacket_Priority_MIN = 1,
MeshPacket_Priority_BACKGROUND = 10,
MeshPacket_Priority_DEFAULT = 64,
MeshPacket_Priority_RELIABLE = 70,
MeshPacket_Priority_ACK = 120,
MeshPacket_Priority_MAX = 127
} MeshPacket_Priority;
typedef enum _ChannelSettings_ModemConfig { typedef enum _ChannelSettings_ModemConfig {
ChannelSettings_ModemConfig_Bw125Cr45Sf128 = 0, ChannelSettings_ModemConfig_Bw125Cr45Sf128 = 0,
ChannelSettings_ModemConfig_Bw500Cr45Sf128 = 1, ChannelSettings_ModemConfig_Bw500Cr45Sf128 = 1,
@@ -145,6 +179,7 @@ typedef struct _RadioConfig_UserPreferences {
char wifi_password[64]; char wifi_password[64];
bool wifi_ap_mode; bool wifi_ap_mode;
RegionCode region; RegionCode region;
ChargeCurrent charge_current;
LocationSharing location_share; LocationSharing location_share;
GpsOperation gps_operation; GpsOperation gps_operation;
uint32_t gps_update_interval; uint32_t gps_update_interval;
@@ -156,6 +191,23 @@ typedef struct _RadioConfig_UserPreferences {
bool debug_log_enabled; bool debug_log_enabled;
pb_size_t ignore_incoming_count; pb_size_t ignore_incoming_count;
uint32_t ignore_incoming[3]; uint32_t ignore_incoming[3];
bool serialplugin_enabled;
bool serialplugin_echo;
uint32_t serialplugin_rxd;
uint32_t serialplugin_txd;
uint32_t serialplugin_timeout;
uint32_t serialplugin_mode;
bool ext_notification_plugin_enabled;
uint32_t ext_notification_plugin_output_ms;
uint32_t ext_notification_plugin_output;
bool ext_notification_plugin_active;
bool ext_notification_plugin_alert_message;
bool ext_notification_plugin_alert_bell;
bool range_test_plugin_enabled;
uint32_t range_test_plugin_sender;
bool range_test_plugin_save;
bool store_forward_plugin_enabled;
uint32_t store_forward_plugin_records;
} RadioConfig_UserPreferences; } RadioConfig_UserPreferences;
typedef struct _RouteDiscovery { typedef struct _RouteDiscovery {
@@ -188,23 +240,23 @@ typedef struct _RadioConfig {
} RadioConfig; } RadioConfig;
typedef struct _SubPacket { typedef struct _SubPacket {
pb_size_t which_payload; pb_size_t which_payloadVariant;
union { union {
Position position; Position position;
Data data; Data data;
User user; User user;
RouteDiscovery route_request; RouteDiscovery route_request;
RouteDiscovery route_reply; RouteDiscovery route_reply;
RouteError route_error; ErrorReason error_reason;
}; };
uint32_t original_id; uint32_t original_id;
bool want_response; bool want_response;
uint32_t dest; uint32_t dest;
pb_size_t which_ack; pb_size_t which_ackVariant;
union { union {
uint32_t success_id; uint32_t success_id;
uint32_t fail_id; uint32_t fail_id;
} ack; } ackVariant;
uint32_t source; uint32_t source;
} SubPacket; } SubPacket;
@@ -212,7 +264,7 @@ typedef PB_BYTES_ARRAY_T(256) MeshPacket_encrypted_t;
typedef struct _MeshPacket { typedef struct _MeshPacket {
uint32_t from; uint32_t from;
uint32_t to; uint32_t to;
pb_size_t which_payload; pb_size_t which_payloadVariant;
union { union {
SubPacket decoded; SubPacket decoded;
MeshPacket_encrypted_t encrypted; MeshPacket_encrypted_t encrypted;
@@ -223,11 +275,12 @@ typedef struct _MeshPacket {
uint32_t rx_time; uint32_t rx_time;
uint32_t hop_limit; uint32_t hop_limit;
bool want_ack; bool want_ack;
MeshPacket_Priority priority;
} MeshPacket; } MeshPacket;
typedef struct _FromRadio { typedef struct _FromRadio {
uint32_t num; uint32_t num;
pb_size_t which_variant; pb_size_t which_payloadVariant;
union { union {
MeshPacket packet; MeshPacket packet;
MyNodeInfo my_info; MyNodeInfo my_info;
@@ -237,25 +290,25 @@ typedef struct _FromRadio {
uint32_t config_complete_id; uint32_t config_complete_id;
bool rebooted; bool rebooted;
ChannelSettings channel; ChannelSettings channel;
} variant; };
} FromRadio; } FromRadio;
typedef struct _ToRadio { typedef struct _ToRadio {
pb_size_t which_variant; pb_size_t which_payloadVariant;
union { union {
MeshPacket packet; MeshPacket packet;
uint32_t want_config_id; uint32_t want_config_id;
RadioConfig set_radio; RadioConfig set_radio;
User set_owner; User set_owner;
ChannelSettings set_channel; ChannelSettings set_channel;
} variant; };
} ToRadio; } ToRadio;
/* Helper constants for enums */ /* Helper constants for enums */
#define _RouteError_MIN RouteError_NONE #define _ErrorReason_MIN ErrorReason_NONE
#define _RouteError_MAX RouteError_TIMEOUT #define _ErrorReason_MAX ErrorReason_MAX_RETRANSMIT
#define _RouteError_ARRAYSIZE ((RouteError)(RouteError_TIMEOUT+1)) #define _ErrorReason_ARRAYSIZE ((ErrorReason)(ErrorReason_MAX_RETRANSMIT+1))
#define _Constants_MIN Constants_Unused #define _Constants_MIN Constants_Unused
#define _Constants_MAX Constants_DATA_PAYLOAD_LEN #define _Constants_MAX Constants_DATA_PAYLOAD_LEN
@@ -265,6 +318,10 @@ typedef struct _ToRadio {
#define _RegionCode_MAX RegionCode_TW #define _RegionCode_MAX RegionCode_TW
#define _RegionCode_ARRAYSIZE ((RegionCode)(RegionCode_TW+1)) #define _RegionCode_ARRAYSIZE ((RegionCode)(RegionCode_TW+1))
#define _ChargeCurrent_MIN ChargeCurrent_MAUnset
#define _ChargeCurrent_MAX ChargeCurrent_MA1320
#define _ChargeCurrent_ARRAYSIZE ((ChargeCurrent)(ChargeCurrent_MA1320+1))
#define _GpsOperation_MIN GpsOperation_GpsOpUnset #define _GpsOperation_MIN GpsOperation_GpsOpUnset
#define _GpsOperation_MAX GpsOperation_GpsOpDisabled #define _GpsOperation_MAX GpsOperation_GpsOpDisabled
#define _GpsOperation_ARRAYSIZE ((GpsOperation)(GpsOperation_GpsOpDisabled+1)) #define _GpsOperation_ARRAYSIZE ((GpsOperation)(GpsOperation_GpsOpDisabled+1))
@@ -274,8 +331,12 @@ typedef struct _ToRadio {
#define _LocationSharing_ARRAYSIZE ((LocationSharing)(LocationSharing_LocDisabled+1)) #define _LocationSharing_ARRAYSIZE ((LocationSharing)(LocationSharing_LocDisabled+1))
#define _CriticalErrorCode_MIN CriticalErrorCode_None #define _CriticalErrorCode_MIN CriticalErrorCode_None
#define _CriticalErrorCode_MAX CriticalErrorCode_InvalidRadioSetting #define _CriticalErrorCode_MAX CriticalErrorCode_TransmitFailed
#define _CriticalErrorCode_ARRAYSIZE ((CriticalErrorCode)(CriticalErrorCode_InvalidRadioSetting+1)) #define _CriticalErrorCode_ARRAYSIZE ((CriticalErrorCode)(CriticalErrorCode_TransmitFailed+1))
#define _MeshPacket_Priority_MIN MeshPacket_Priority_UNSET
#define _MeshPacket_Priority_MAX MeshPacket_Priority_MAX
#define _MeshPacket_Priority_ARRAYSIZE ((MeshPacket_Priority)(MeshPacket_Priority_MAX+1))
#define _ChannelSettings_ModemConfig_MIN ChannelSettings_ModemConfig_Bw125Cr45Sf128 #define _ChannelSettings_ModemConfig_MIN ChannelSettings_ModemConfig_Bw125Cr45Sf128
#define _ChannelSettings_ModemConfig_MAX ChannelSettings_ModemConfig_Bw125Cr48Sf4096 #define _ChannelSettings_ModemConfig_MAX ChannelSettings_ModemConfig_Bw125Cr48Sf4096
@@ -296,10 +357,10 @@ extern "C" {
#define User_init_default {"", "", "", {0}} #define User_init_default {"", "", "", {0}}
#define RouteDiscovery_init_default {0, {0, 0, 0, 0, 0, 0, 0, 0}} #define RouteDiscovery_init_default {0, {0, 0, 0, 0, 0, 0, 0, 0}}
#define SubPacket_init_default {0, {Position_init_default}, 0, 0, 0, 0, {0}, 0} #define SubPacket_init_default {0, {Position_init_default}, 0, 0, 0, 0, {0}, 0}
#define MeshPacket_init_default {0, 0, 0, {SubPacket_init_default}, 0, 0, 0, 0, 0, 0} #define MeshPacket_init_default {0, 0, 0, {SubPacket_init_default}, 0, 0, 0, 0, 0, 0, _MeshPacket_Priority_MIN}
#define ChannelSettings_init_default {0, _ChannelSettings_ModemConfig_MIN, {0, {0}}, "", 0, 0, 0, 0, 0, 0, 0} #define ChannelSettings_init_default {0, _ChannelSettings_ModemConfig_MIN, {0, {0}}, "", 0, 0, 0, 0, 0, 0, 0}
#define RadioConfig_init_default {false, RadioConfig_UserPreferences_init_default, false, ChannelSettings_init_default} #define RadioConfig_init_default {false, RadioConfig_UserPreferences_init_default, false, ChannelSettings_init_default}
#define RadioConfig_UserPreferences_init_default {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, "", "", 0, _RegionCode_MIN, _LocationSharing_MIN, _GpsOperation_MIN, 0, 0, 0, 0, 0, 0, 0, 0, {0, 0, 0}} #define RadioConfig_UserPreferences_init_default {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, "", "", 0, _RegionCode_MIN, _ChargeCurrent_MIN, _LocationSharing_MIN, _GpsOperation_MIN, 0, 0, 0, 0, 0, 0, 0, 0, {0, 0, 0}, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
#define NodeInfo_init_default {0, false, User_init_default, false, Position_init_default, 0, 0} #define NodeInfo_init_default {0, false, User_init_default, false, Position_init_default, 0, 0}
#define MyNodeInfo_init_default {0, 0, 0, "", "", "", _CriticalErrorCode_MIN, 0, 0, 0, 0, 0, 0, 0} #define MyNodeInfo_init_default {0, 0, 0, "", "", "", _CriticalErrorCode_MIN, 0, 0, 0, 0, 0, 0, 0}
#define LogRecord_init_default {"", 0, "", _LogRecord_Level_MIN} #define LogRecord_init_default {"", 0, "", _LogRecord_Level_MIN}
@@ -310,10 +371,10 @@ extern "C" {
#define User_init_zero {"", "", "", {0}} #define User_init_zero {"", "", "", {0}}
#define RouteDiscovery_init_zero {0, {0, 0, 0, 0, 0, 0, 0, 0}} #define RouteDiscovery_init_zero {0, {0, 0, 0, 0, 0, 0, 0, 0}}
#define SubPacket_init_zero {0, {Position_init_zero}, 0, 0, 0, 0, {0}, 0} #define SubPacket_init_zero {0, {Position_init_zero}, 0, 0, 0, 0, {0}, 0}
#define MeshPacket_init_zero {0, 0, 0, {SubPacket_init_zero}, 0, 0, 0, 0, 0, 0} #define MeshPacket_init_zero {0, 0, 0, {SubPacket_init_zero}, 0, 0, 0, 0, 0, 0, _MeshPacket_Priority_MIN}
#define ChannelSettings_init_zero {0, _ChannelSettings_ModemConfig_MIN, {0, {0}}, "", 0, 0, 0, 0, 0, 0, 0} #define ChannelSettings_init_zero {0, _ChannelSettings_ModemConfig_MIN, {0, {0}}, "", 0, 0, 0, 0, 0, 0, 0}
#define RadioConfig_init_zero {false, RadioConfig_UserPreferences_init_zero, false, ChannelSettings_init_zero} #define RadioConfig_init_zero {false, RadioConfig_UserPreferences_init_zero, false, ChannelSettings_init_zero}
#define RadioConfig_UserPreferences_init_zero {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, "", "", 0, _RegionCode_MIN, _LocationSharing_MIN, _GpsOperation_MIN, 0, 0, 0, 0, 0, 0, 0, 0, {0, 0, 0}} #define RadioConfig_UserPreferences_init_zero {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, "", "", 0, _RegionCode_MIN, _ChargeCurrent_MIN, _LocationSharing_MIN, _GpsOperation_MIN, 0, 0, 0, 0, 0, 0, 0, 0, {0, 0, 0}, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
#define NodeInfo_init_zero {0, false, User_init_zero, false, Position_init_zero, 0, 0} #define NodeInfo_init_zero {0, false, User_init_zero, false, Position_init_zero, 0, 0}
#define MyNodeInfo_init_zero {0, 0, 0, "", "", "", _CriticalErrorCode_MIN, 0, 0, 0, 0, 0, 0, 0} #define MyNodeInfo_init_zero {0, 0, 0, "", "", "", _CriticalErrorCode_MIN, 0, 0, 0, 0, 0, 0, 0}
#define LogRecord_init_zero {"", 0, "", _LogRecord_Level_MIN} #define LogRecord_init_zero {"", 0, "", _LogRecord_Level_MIN}
@@ -371,6 +432,7 @@ extern "C" {
#define RadioConfig_UserPreferences_wifi_password_tag 13 #define RadioConfig_UserPreferences_wifi_password_tag 13
#define RadioConfig_UserPreferences_wifi_ap_mode_tag 14 #define RadioConfig_UserPreferences_wifi_ap_mode_tag 14
#define RadioConfig_UserPreferences_region_tag 15 #define RadioConfig_UserPreferences_region_tag 15
#define RadioConfig_UserPreferences_charge_current_tag 16
#define RadioConfig_UserPreferences_location_share_tag 32 #define RadioConfig_UserPreferences_location_share_tag 32
#define RadioConfig_UserPreferences_gps_operation_tag 33 #define RadioConfig_UserPreferences_gps_operation_tag 33
#define RadioConfig_UserPreferences_gps_update_interval_tag 34 #define RadioConfig_UserPreferences_gps_update_interval_tag 34
@@ -381,6 +443,23 @@ extern "C" {
#define RadioConfig_UserPreferences_factory_reset_tag 100 #define RadioConfig_UserPreferences_factory_reset_tag 100
#define RadioConfig_UserPreferences_debug_log_enabled_tag 101 #define RadioConfig_UserPreferences_debug_log_enabled_tag 101
#define RadioConfig_UserPreferences_ignore_incoming_tag 103 #define RadioConfig_UserPreferences_ignore_incoming_tag 103
#define RadioConfig_UserPreferences_serialplugin_enabled_tag 120
#define RadioConfig_UserPreferences_serialplugin_echo_tag 121
#define RadioConfig_UserPreferences_serialplugin_rxd_tag 122
#define RadioConfig_UserPreferences_serialplugin_txd_tag 123
#define RadioConfig_UserPreferences_serialplugin_timeout_tag 124
#define RadioConfig_UserPreferences_serialplugin_mode_tag 125
#define RadioConfig_UserPreferences_ext_notification_plugin_enabled_tag 126
#define RadioConfig_UserPreferences_ext_notification_plugin_output_ms_tag 127
#define RadioConfig_UserPreferences_ext_notification_plugin_output_tag 128
#define RadioConfig_UserPreferences_ext_notification_plugin_active_tag 129
#define RadioConfig_UserPreferences_ext_notification_plugin_alert_message_tag 130
#define RadioConfig_UserPreferences_ext_notification_plugin_alert_bell_tag 131
#define RadioConfig_UserPreferences_range_test_plugin_enabled_tag 132
#define RadioConfig_UserPreferences_range_test_plugin_sender_tag 133
#define RadioConfig_UserPreferences_range_test_plugin_save_tag 134
#define RadioConfig_UserPreferences_store_forward_plugin_enabled_tag 136
#define RadioConfig_UserPreferences_store_forward_plugin_records_tag 137
#define RouteDiscovery_route_tag 2 #define RouteDiscovery_route_tag 2
#define User_id_tag 1 #define User_id_tag 1
#define User_long_name_tag 2 #define User_long_name_tag 2
@@ -398,7 +477,7 @@ extern "C" {
#define SubPacket_user_tag 4 #define SubPacket_user_tag 4
#define SubPacket_route_request_tag 6 #define SubPacket_route_request_tag 6
#define SubPacket_route_reply_tag 7 #define SubPacket_route_reply_tag 7
#define SubPacket_route_error_tag 13 #define SubPacket_error_reason_tag 13
#define SubPacket_original_id_tag 2 #define SubPacket_original_id_tag 2
#define SubPacket_want_response_tag 5 #define SubPacket_want_response_tag 5
#define SubPacket_dest_tag 9 #define SubPacket_dest_tag 9
@@ -415,6 +494,7 @@ extern "C" {
#define MeshPacket_rx_time_tag 9 #define MeshPacket_rx_time_tag 9
#define MeshPacket_hop_limit_tag 10 #define MeshPacket_hop_limit_tag 10
#define MeshPacket_want_ack_tag 11 #define MeshPacket_want_ack_tag 11
#define MeshPacket_priority_tag 12
#define FromRadio_num_tag 1 #define FromRadio_num_tag 1
#define FromRadio_packet_tag 2 #define FromRadio_packet_tag 2
#define FromRadio_my_info_tag 3 #define FromRadio_my_info_tag 3
@@ -460,40 +540,41 @@ X(a, STATIC, REPEATED, INT32, route, 2)
#define RouteDiscovery_DEFAULT NULL #define RouteDiscovery_DEFAULT NULL
#define SubPacket_FIELDLIST(X, a) \ #define SubPacket_FIELDLIST(X, a) \
X(a, STATIC, ONEOF, MESSAGE, (payload,position,position), 1) \ X(a, STATIC, ONEOF, MESSAGE, (payloadVariant,position,position), 1) \
X(a, STATIC, ONEOF, MESSAGE, (payload,data,data), 3) \ X(a, STATIC, ONEOF, MESSAGE, (payloadVariant,data,data), 3) \
X(a, STATIC, ONEOF, MESSAGE, (payload,user,user), 4) \ X(a, STATIC, ONEOF, MESSAGE, (payloadVariant,user,user), 4) \
X(a, STATIC, ONEOF, MESSAGE, (payload,route_request,route_request), 6) \ X(a, STATIC, ONEOF, MESSAGE, (payloadVariant,route_request,route_request), 6) \
X(a, STATIC, ONEOF, MESSAGE, (payload,route_reply,route_reply), 7) \ X(a, STATIC, ONEOF, MESSAGE, (payloadVariant,route_reply,route_reply), 7) \
X(a, STATIC, ONEOF, UENUM, (payload,route_error,route_error), 13) \ X(a, STATIC, ONEOF, UENUM, (payloadVariant,error_reason,error_reason), 13) \
X(a, STATIC, SINGULAR, UINT32, original_id, 2) \ X(a, STATIC, SINGULAR, UINT32, original_id, 2) \
X(a, STATIC, SINGULAR, BOOL, want_response, 5) \ X(a, STATIC, SINGULAR, BOOL, want_response, 5) \
X(a, STATIC, SINGULAR, UINT32, dest, 9) \ X(a, STATIC, SINGULAR, UINT32, dest, 9) \
X(a, STATIC, ONEOF, UINT32, (ack,success_id,ack.success_id), 10) \ X(a, STATIC, ONEOF, UINT32, (ackVariant,success_id,ackVariant.success_id), 10) \
X(a, STATIC, ONEOF, UINT32, (ack,fail_id,ack.fail_id), 11) \ X(a, STATIC, ONEOF, UINT32, (ackVariant,fail_id,ackVariant.fail_id), 11) \
X(a, STATIC, SINGULAR, UINT32, source, 12) X(a, STATIC, SINGULAR, UINT32, source, 12)
#define SubPacket_CALLBACK NULL #define SubPacket_CALLBACK NULL
#define SubPacket_DEFAULT NULL #define SubPacket_DEFAULT NULL
#define SubPacket_payload_position_MSGTYPE Position #define SubPacket_payloadVariant_position_MSGTYPE Position
#define SubPacket_payload_data_MSGTYPE Data #define SubPacket_payloadVariant_data_MSGTYPE Data
#define SubPacket_payload_user_MSGTYPE User #define SubPacket_payloadVariant_user_MSGTYPE User
#define SubPacket_payload_route_request_MSGTYPE RouteDiscovery #define SubPacket_payloadVariant_route_request_MSGTYPE RouteDiscovery
#define SubPacket_payload_route_reply_MSGTYPE RouteDiscovery #define SubPacket_payloadVariant_route_reply_MSGTYPE RouteDiscovery
#define MeshPacket_FIELDLIST(X, a) \ #define MeshPacket_FIELDLIST(X, a) \
X(a, STATIC, SINGULAR, UINT32, from, 1) \ X(a, STATIC, SINGULAR, UINT32, from, 1) \
X(a, STATIC, SINGULAR, UINT32, to, 2) \ X(a, STATIC, SINGULAR, UINT32, to, 2) \
X(a, STATIC, ONEOF, MESSAGE, (payload,decoded,decoded), 3) \ X(a, STATIC, ONEOF, MESSAGE, (payloadVariant,decoded,decoded), 3) \
X(a, STATIC, ONEOF, BYTES, (payload,encrypted,encrypted), 8) \ X(a, STATIC, ONEOF, BYTES, (payloadVariant,encrypted,encrypted), 8) \
X(a, STATIC, SINGULAR, UINT32, channel_index, 4) \ X(a, STATIC, SINGULAR, UINT32, channel_index, 4) \
X(a, STATIC, SINGULAR, UINT32, id, 6) \ X(a, STATIC, SINGULAR, UINT32, id, 6) \
X(a, STATIC, SINGULAR, FLOAT, rx_snr, 7) \ X(a, STATIC, SINGULAR, FLOAT, rx_snr, 7) \
X(a, STATIC, SINGULAR, FIXED32, rx_time, 9) \ X(a, STATIC, SINGULAR, FIXED32, rx_time, 9) \
X(a, STATIC, SINGULAR, UINT32, hop_limit, 10) \ X(a, STATIC, SINGULAR, UINT32, hop_limit, 10) \
X(a, STATIC, SINGULAR, BOOL, want_ack, 11) X(a, STATIC, SINGULAR, BOOL, want_ack, 11) \
X(a, STATIC, SINGULAR, UENUM, priority, 12)
#define MeshPacket_CALLBACK NULL #define MeshPacket_CALLBACK NULL
#define MeshPacket_DEFAULT NULL #define MeshPacket_DEFAULT NULL
#define MeshPacket_payload_decoded_MSGTYPE SubPacket #define MeshPacket_payloadVariant_decoded_MSGTYPE SubPacket
#define ChannelSettings_FIELDLIST(X, a) \ #define ChannelSettings_FIELDLIST(X, a) \
X(a, STATIC, SINGULAR, INT32, tx_power, 1) \ X(a, STATIC, SINGULAR, INT32, tx_power, 1) \
@@ -533,6 +614,7 @@ X(a, STATIC, SINGULAR, STRING, wifi_ssid, 12) \
X(a, STATIC, SINGULAR, STRING, wifi_password, 13) \ X(a, STATIC, SINGULAR, STRING, wifi_password, 13) \
X(a, STATIC, SINGULAR, BOOL, wifi_ap_mode, 14) \ X(a, STATIC, SINGULAR, BOOL, wifi_ap_mode, 14) \
X(a, STATIC, SINGULAR, UENUM, region, 15) \ X(a, STATIC, SINGULAR, UENUM, region, 15) \
X(a, STATIC, SINGULAR, UENUM, charge_current, 16) \
X(a, STATIC, SINGULAR, UENUM, location_share, 32) \ X(a, STATIC, SINGULAR, UENUM, location_share, 32) \
X(a, STATIC, SINGULAR, UENUM, gps_operation, 33) \ X(a, STATIC, SINGULAR, UENUM, gps_operation, 33) \
X(a, STATIC, SINGULAR, UINT32, gps_update_interval, 34) \ X(a, STATIC, SINGULAR, UINT32, gps_update_interval, 34) \
@@ -542,7 +624,24 @@ X(a, STATIC, SINGULAR, BOOL, is_low_power, 38) \
X(a, STATIC, SINGULAR, BOOL, fixed_position, 39) \ X(a, STATIC, SINGULAR, BOOL, fixed_position, 39) \
X(a, STATIC, SINGULAR, BOOL, factory_reset, 100) \ X(a, STATIC, SINGULAR, BOOL, factory_reset, 100) \
X(a, STATIC, SINGULAR, BOOL, debug_log_enabled, 101) \ X(a, STATIC, SINGULAR, BOOL, debug_log_enabled, 101) \
X(a, STATIC, REPEATED, UINT32, ignore_incoming, 103) X(a, STATIC, REPEATED, UINT32, ignore_incoming, 103) \
X(a, STATIC, SINGULAR, BOOL, serialplugin_enabled, 120) \
X(a, STATIC, SINGULAR, BOOL, serialplugin_echo, 121) \
X(a, STATIC, SINGULAR, UINT32, serialplugin_rxd, 122) \
X(a, STATIC, SINGULAR, UINT32, serialplugin_txd, 123) \
X(a, STATIC, SINGULAR, UINT32, serialplugin_timeout, 124) \
X(a, STATIC, SINGULAR, UINT32, serialplugin_mode, 125) \
X(a, STATIC, SINGULAR, BOOL, ext_notification_plugin_enabled, 126) \
X(a, STATIC, SINGULAR, UINT32, ext_notification_plugin_output_ms, 127) \
X(a, STATIC, SINGULAR, UINT32, ext_notification_plugin_output, 128) \
X(a, STATIC, SINGULAR, BOOL, ext_notification_plugin_active, 129) \
X(a, STATIC, SINGULAR, BOOL, ext_notification_plugin_alert_message, 130) \
X(a, STATIC, SINGULAR, BOOL, ext_notification_plugin_alert_bell, 131) \
X(a, STATIC, SINGULAR, BOOL, range_test_plugin_enabled, 132) \
X(a, STATIC, SINGULAR, UINT32, range_test_plugin_sender, 133) \
X(a, STATIC, SINGULAR, BOOL, range_test_plugin_save, 134) \
X(a, STATIC, SINGULAR, BOOL, store_forward_plugin_enabled, 136) \
X(a, STATIC, SINGULAR, UINT32, store_forward_plugin_records, 137)
#define RadioConfig_UserPreferences_CALLBACK NULL #define RadioConfig_UserPreferences_CALLBACK NULL
#define RadioConfig_UserPreferences_DEFAULT NULL #define RadioConfig_UserPreferences_DEFAULT NULL
@@ -585,35 +684,35 @@ X(a, STATIC, SINGULAR, UENUM, level, 4)
#define FromRadio_FIELDLIST(X, a) \ #define FromRadio_FIELDLIST(X, a) \
X(a, STATIC, SINGULAR, UINT32, num, 1) \ X(a, STATIC, SINGULAR, UINT32, num, 1) \
X(a, STATIC, ONEOF, MESSAGE, (variant,packet,variant.packet), 2) \ X(a, STATIC, ONEOF, MESSAGE, (payloadVariant,packet,packet), 2) \
X(a, STATIC, ONEOF, MESSAGE, (variant,my_info,variant.my_info), 3) \ X(a, STATIC, ONEOF, MESSAGE, (payloadVariant,my_info,my_info), 3) \
X(a, STATIC, ONEOF, MESSAGE, (variant,node_info,variant.node_info), 4) \ X(a, STATIC, ONEOF, MESSAGE, (payloadVariant,node_info,node_info), 4) \
X(a, STATIC, ONEOF, MESSAGE, (variant,radio,variant.radio), 6) \ X(a, STATIC, ONEOF, MESSAGE, (payloadVariant,radio,radio), 6) \
X(a, STATIC, ONEOF, MESSAGE, (variant,log_record,variant.log_record), 7) \ X(a, STATIC, ONEOF, MESSAGE, (payloadVariant,log_record,log_record), 7) \
X(a, STATIC, ONEOF, UINT32, (variant,config_complete_id,variant.config_complete_id), 8) \ X(a, STATIC, ONEOF, UINT32, (payloadVariant,config_complete_id,config_complete_id), 8) \
X(a, STATIC, ONEOF, BOOL, (variant,rebooted,variant.rebooted), 9) \ X(a, STATIC, ONEOF, BOOL, (payloadVariant,rebooted,rebooted), 9) \
X(a, STATIC, ONEOF, MESSAGE, (variant,channel,variant.channel), 10) X(a, STATIC, ONEOF, MESSAGE, (payloadVariant,channel,channel), 10)
#define FromRadio_CALLBACK NULL #define FromRadio_CALLBACK NULL
#define FromRadio_DEFAULT NULL #define FromRadio_DEFAULT NULL
#define FromRadio_variant_packet_MSGTYPE MeshPacket #define FromRadio_payloadVariant_packet_MSGTYPE MeshPacket
#define FromRadio_variant_my_info_MSGTYPE MyNodeInfo #define FromRadio_payloadVariant_my_info_MSGTYPE MyNodeInfo
#define FromRadio_variant_node_info_MSGTYPE NodeInfo #define FromRadio_payloadVariant_node_info_MSGTYPE NodeInfo
#define FromRadio_variant_radio_MSGTYPE RadioConfig #define FromRadio_payloadVariant_radio_MSGTYPE RadioConfig
#define FromRadio_variant_log_record_MSGTYPE LogRecord #define FromRadio_payloadVariant_log_record_MSGTYPE LogRecord
#define FromRadio_variant_channel_MSGTYPE ChannelSettings #define FromRadio_payloadVariant_channel_MSGTYPE ChannelSettings
#define ToRadio_FIELDLIST(X, a) \ #define ToRadio_FIELDLIST(X, a) \
X(a, STATIC, ONEOF, MESSAGE, (variant,packet,variant.packet), 1) \ X(a, STATIC, ONEOF, MESSAGE, (payloadVariant,packet,packet), 1) \
X(a, STATIC, ONEOF, UINT32, (variant,want_config_id,variant.want_config_id), 100) \ X(a, STATIC, ONEOF, UINT32, (payloadVariant,want_config_id,want_config_id), 100) \
X(a, STATIC, ONEOF, MESSAGE, (variant,set_radio,variant.set_radio), 101) \ X(a, STATIC, ONEOF, MESSAGE, (payloadVariant,set_radio,set_radio), 101) \
X(a, STATIC, ONEOF, MESSAGE, (variant,set_owner,variant.set_owner), 102) \ X(a, STATIC, ONEOF, MESSAGE, (payloadVariant,set_owner,set_owner), 102) \
X(a, STATIC, ONEOF, MESSAGE, (variant,set_channel,variant.set_channel), 103) X(a, STATIC, ONEOF, MESSAGE, (payloadVariant,set_channel,set_channel), 103)
#define ToRadio_CALLBACK NULL #define ToRadio_CALLBACK NULL
#define ToRadio_DEFAULT NULL #define ToRadio_DEFAULT NULL
#define ToRadio_variant_packet_MSGTYPE MeshPacket #define ToRadio_payloadVariant_packet_MSGTYPE MeshPacket
#define ToRadio_variant_set_radio_MSGTYPE RadioConfig #define ToRadio_payloadVariant_set_radio_MSGTYPE RadioConfig
#define ToRadio_variant_set_owner_MSGTYPE User #define ToRadio_payloadVariant_set_owner_MSGTYPE User
#define ToRadio_variant_set_channel_MSGTYPE ChannelSettings #define ToRadio_payloadVariant_set_channel_MSGTYPE ChannelSettings
extern const pb_msgdesc_t Position_msg; extern const pb_msgdesc_t Position_msg;
extern const pb_msgdesc_t Data_msg; extern const pb_msgdesc_t Data_msg;
@@ -652,15 +751,15 @@ extern const pb_msgdesc_t ToRadio_msg;
#define User_size 72 #define User_size 72
#define RouteDiscovery_size 88 #define RouteDiscovery_size 88
#define SubPacket_size 275 #define SubPacket_size 275
#define MeshPacket_size 320 #define MeshPacket_size 322
#define ChannelSettings_size 95 #define ChannelSettings_size 95
#define RadioConfig_size 319 #define RadioConfig_size 405
#define RadioConfig_UserPreferences_size 219 #define RadioConfig_UserPreferences_size 305
#define NodeInfo_size 132 #define NodeInfo_size 132
#define MyNodeInfo_size 106 #define MyNodeInfo_size 106
#define LogRecord_size 81 #define LogRecord_size 81
#define FromRadio_size 329 #define FromRadio_size 414
#define ToRadio_size 323 #define ToRadio_size 409
#ifdef __cplusplus #ifdef __cplusplus
} /* extern "C" */ } /* extern "C" */

View File

@@ -18,13 +18,17 @@ typedef enum _PortNum {
PortNum_NODEINFO_APP = 4, PortNum_NODEINFO_APP = 4,
PortNum_REPLY_APP = 32, PortNum_REPLY_APP = 32,
PortNum_IP_TUNNEL_APP = 33, PortNum_IP_TUNNEL_APP = 33,
PortNum_PRIVATE_APP = 256 PortNum_SERIAL_APP = 64,
PortNum_STORE_FORWARD_APP = 65,
PortNum_RANGE_TEST_APP = 66,
PortNum_PRIVATE_APP = 256,
PortNum_ATAK_FORWARDER = 257
} PortNum; } PortNum;
/* Helper constants for enums */ /* Helper constants for enums */
#define _PortNum_MIN PortNum_UNKNOWN_APP #define _PortNum_MIN PortNum_UNKNOWN_APP
#define _PortNum_MAX PortNum_PRIVATE_APP #define _PortNum_MAX PortNum_ATAK_FORWARDER
#define _PortNum_ARRAYSIZE ((PortNum)(PortNum_PRIVATE_APP+1)) #define _PortNum_ARRAYSIZE ((PortNum)(PortNum_ATAK_FORWARDER+1))
#ifdef __cplusplus #ifdef __cplusplus

View File

@@ -1,23 +1,20 @@
#include "meshwifi/meshhttp.h"
#include "NodeDB.h" #include "NodeDB.h"
#include "PowerFSM.h" #include "PowerFSM.h"
#include "airtime.h" #include "airtime.h"
#include "configuration.h"
#include "esp_task_wdt.h"
#include "main.h" #include "main.h"
#include "meshhttpStatic.h" #include "mesh/http/ContentHelper.h"
#include "meshwifi/meshwifi.h" #include "mesh/http/ContentStatic.h"
#include "mesh/http/WiFiAPClient.h"
#include "power.h"
#include "sleep.h" #include "sleep.h"
#include <HTTPBodyParser.hpp> #include <HTTPBodyParser.hpp>
#include <HTTPMultipartBodyParser.hpp> #include <HTTPMultipartBodyParser.hpp>
#include <HTTPURLEncodedBodyParser.hpp> #include <HTTPURLEncodedBodyParser.hpp>
#include <SPIFFS.h> #include <SPIFFS.h>
#include <WebServer.h>
#include <WiFi.h>
// Persistant Data Storage #ifndef NO_ESP32
#include <Preferences.h> #include "esp_task_wdt.h"
Preferences prefs; #endif
/* /*
Including the esp32_https_server library will trigger a compile time error. I've Including the esp32_https_server library will trigger a compile time error. I've
@@ -42,40 +39,7 @@ Preferences prefs;
// The HTTPS Server comes in a separate namespace. For easier use, include it here. // The HTTPS Server comes in a separate namespace. For easier use, include it here.
using namespace httpsserver; using namespace httpsserver;
SSLCert *cert; #include "mesh/http/ContentHandler.h"
HTTPSServer *secureServer;
HTTPServer *insecureServer;
// Our API to handle messages to and from the radio.
HttpAPI webAPI;
// Declare some handler functions for the various URLs on the server
void handleAPIv1FromRadio(HTTPRequest *req, HTTPResponse *res);
void handleAPIv1ToRadio(HTTPRequest *req, HTTPResponse *res);
void handleStyleCSS(HTTPRequest *req, HTTPResponse *res);
void handleHotspot(HTTPRequest *req, HTTPResponse *res);
void handleFavicon(HTTPRequest *req, HTTPResponse *res);
void handleRoot(HTTPRequest *req, HTTPResponse *res);
void handleStaticBrowse(HTTPRequest *req, HTTPResponse *res);
void handleStaticPost(HTTPRequest *req, HTTPResponse *res);
void handleStatic(HTTPRequest *req, HTTPResponse *res);
void handleRestart(HTTPRequest *req, HTTPResponse *res);
void handle404(HTTPRequest *req, HTTPResponse *res);
void handleFormUpload(HTTPRequest *req, HTTPResponse *res);
void handleScanNetworks(HTTPRequest *req, HTTPResponse *res);
void handleSpiffsBrowseStatic(HTTPRequest *req, HTTPResponse *res);
void handleSpiffsDeleteStatic(HTTPRequest *req, HTTPResponse *res);
void handleBlinkLED(HTTPRequest *req, HTTPResponse *res);
void handleReport(HTTPRequest *req, HTTPResponse *res);
void middlewareSpeedUp240(HTTPRequest *req, HTTPResponse *res, std::function<void()> next);
void middlewareSpeedUp160(HTTPRequest *req, HTTPResponse *res, std::function<void()> next);
void middlewareSession(HTTPRequest *req, HTTPResponse *res, std::function<void()> next);
bool isWebServerReady = 0;
bool isCertReady = 0;
uint32_t timeSpeedUp = 0;
// We need to specify some content-type mapping, so the resources get delivered with the // We need to specify some content-type mapping, so the resources get delivered with the
// right content type and are displayed correctly in the browser // right content type and are displayed correctly in the browser
@@ -86,161 +50,32 @@ char contentTypes[][2][32] = {{".txt", "text/plain"}, {".html", "text/html"}
{".css", "text/css"}, {".ico", "image/vnd.microsoft.icon"}, {".css", "text/css"}, {".ico", "image/vnd.microsoft.icon"},
{".svg", "image/svg+xml"}, {"", ""}}; {".svg", "image/svg+xml"}, {"", ""}};
void handleWebResponse() // Our API to handle messages to and from the radio.
HttpAPI webAPI;
uint32_t numberOfRequests = 0;
uint32_t timeSpeedUp = 0;
uint32_t getTimeSpeedUp()
{ {
if (isWifiAvailable() == 0) { return timeSpeedUp;
return;
}
if (isWebServerReady) {
// We're going to handle the DNS responder here so it
// will be ignored by the NRF boards.
handleDNSResponse();
secureServer->loop();
insecureServer->loop();
}
/*
Slow down the CPU if we have not received a request within the last few
seconds.
*/
if (millis() - timeSpeedUp >= (25 * 1000)) {
setCpuFrequencyMhz(80);
timeSpeedUp = millis();
}
} }
void taskCreateCert(void *parameter) void setTimeSpeedUp()
{ {
timeSpeedUp = millis();
prefs.begin("MeshtasticHTTPS", false);
// Delete the saved certs
if (0) {
DEBUG_MSG("Deleting any saved SSL keys ...\n");
// prefs.clear();
prefs.remove("PK");
prefs.remove("cert");
}
size_t pkLen = prefs.getBytesLength("PK");
size_t certLen = prefs.getBytesLength("cert");
DEBUG_MSG("Checking if we have a previously saved SSL Certificate.\n");
if (pkLen && certLen) {
DEBUG_MSG("Existing SSL Certificate found!\n");
} else {
DEBUG_MSG("Creating the certificate. This may take a while. Please wait...\n");
yield();
cert = new SSLCert();
yield();
int createCertResult = createSelfSignedCert(*cert, KEYSIZE_2048, "CN=meshtastic.local,O=Meshtastic,C=US",
"20190101000000", "20300101000000");
yield();
if (createCertResult != 0) {
DEBUG_MSG("Creating the certificate failed\n");
// Serial.printf("Creating the certificate failed. Error Code = 0x%02X, check SSLCert.hpp for details",
// createCertResult);
// while (true)
// delay(500);
} else {
DEBUG_MSG("Creating the certificate was successful\n");
DEBUG_MSG("Created Private Key: %d Bytes\n", cert->getPKLength());
// for (int i = 0; i < cert->getPKLength(); i++)
// Serial.print(cert->getPKData()[i], HEX);
// Serial.println();
DEBUG_MSG("Created Certificate: %d Bytes\n", cert->getCertLength());
// for (int i = 0; i < cert->getCertLength(); i++)
// Serial.print(cert->getCertData()[i], HEX);
// Serial.println();
prefs.putBytes("PK", (uint8_t *)cert->getPKData(), cert->getPKLength());
prefs.putBytes("cert", (uint8_t *)cert->getCertData(), cert->getCertLength());
}
}
isCertReady = 1;
vTaskDelete(NULL);
} }
void createSSLCert() void registerHandlers(HTTPServer *insecureServer, HTTPSServer *secureServer)
{ {
if (isWifiAvailable() == 0) {
return;
}
// Create a new process just to handle creating the cert.
// This is a workaround for Bug: https://github.com/fhessel/esp32_https_server/issues/48
// jm@casler.org (Oct 2020)
xTaskCreate(taskCreateCert, /* Task function. */
"createCert", /* String with name of task. */
16384, /* Stack size in bytes. */
NULL, /* Parameter passed as input of the task */
16, /* Priority of the task. */
NULL); /* Task handle. */
DEBUG_MSG("Waiting for SSL Cert to be generated.\n");
while (!isCertReady) {
DEBUG_MSG(".");
delay(1000);
yield();
esp_task_wdt_reset();
}
DEBUG_MSG("SSL Cert Ready!\n");
}
void initWebServer()
{
DEBUG_MSG("Initializing Web Server ...\n");
prefs.begin("MeshtasticHTTPS", false);
size_t pkLen = prefs.getBytesLength("PK");
size_t certLen = prefs.getBytesLength("cert");
DEBUG_MSG("Checking if we have a previously saved SSL Certificate.\n");
if (pkLen && certLen) {
uint8_t *pkBuffer = new uint8_t[pkLen];
prefs.getBytes("PK", pkBuffer, pkLen);
uint8_t *certBuffer = new uint8_t[certLen];
prefs.getBytes("cert", certBuffer, certLen);
cert = new SSLCert(certBuffer, certLen, pkBuffer, pkLen);
DEBUG_MSG("Retrieved Private Key: %d Bytes\n", cert->getPKLength());
// DEBUG_MSG("Retrieved Private Key: " + String(cert->getPKLength()) + " Bytes");
// for (int i = 0; i < cert->getPKLength(); i++)
// Serial.print(cert->getPKData()[i], HEX);
// Serial.println();
DEBUG_MSG("Retrieved Certificate: %d Bytes\n", cert->getCertLength());
// for (int i = 0; i < cert->getCertLength(); i++)
// Serial.print(cert->getCertData()[i], HEX);
// Serial.println();
} else {
DEBUG_MSG("Web Server started without SSL keys! How did this happen?\n");
}
// We can now use the new certificate to setup our server as usual.
secureServer = new HTTPSServer(cert);
insecureServer = new HTTPServer();
// For every resource available on the server, we need to create a ResourceNode // For every resource available on the server, we need to create a ResourceNode
// The ResourceNode links URL and HTTP method to a handler function // The ResourceNode links URL and HTTP method to a handler function
ResourceNode *nodeAPIv1ToRadioOptions = new ResourceNode("/api/v1/toradio", "OPTIONS", &handleAPIv1ToRadio); ResourceNode *nodeAPIv1ToRadioOptions = new ResourceNode("/api/v1/toradio", "OPTIONS", &handleAPIv1ToRadio);
ResourceNode *nodeAPIv1ToRadio = new ResourceNode("/api/v1/toradio", "PUT", &handleAPIv1ToRadio); ResourceNode *nodeAPIv1ToRadio = new ResourceNode("/api/v1/toradio", "PUT", &handleAPIv1ToRadio);
ResourceNode *nodeAPIv1FromRadio = new ResourceNode("/api/v1/fromradio", "GET", &handleAPIv1FromRadio); ResourceNode *nodeAPIv1FromRadio = new ResourceNode("/api/v1/fromradio", "GET", &handleAPIv1FromRadio);
ResourceNode *nodeHotspot = new ResourceNode("/hotspot-detect.html", "GET", &handleHotspot); ResourceNode *nodeHotspot = new ResourceNode("/hotspot-detect.html", "GET", &handleHotspot);
ResourceNode *nodeFavicon = new ResourceNode("/favicon.ico", "GET", &handleFavicon); ResourceNode *nodeFavicon = new ResourceNode("/favicon.ico", "GET", &handleFavicon);
ResourceNode *nodeRoot = new ResourceNode("/", "GET", &handleRoot); ResourceNode *nodeRoot = new ResourceNode("/", "GET", &handleRoot);
@@ -253,7 +88,7 @@ void initWebServer()
ResourceNode *nodeJsonScanNetworks = new ResourceNode("/json/scanNetworks", "GET", &handleScanNetworks); ResourceNode *nodeJsonScanNetworks = new ResourceNode("/json/scanNetworks", "GET", &handleScanNetworks);
ResourceNode *nodeJsonBlinkLED = new ResourceNode("/json/blink", "POST", &handleBlinkLED); ResourceNode *nodeJsonBlinkLED = new ResourceNode("/json/blink", "POST", &handleBlinkLED);
ResourceNode *nodeJsonReport = new ResourceNode("/json/report", "GET", &handleReport); ResourceNode *nodeJsonReport = new ResourceNode("/json/report", "GET", &handleReport);
ResourceNode *nodeJsonSpiffsBrowseStatic = new ResourceNode("/json/spiffs/browse/static/", "GET", &handleSpiffsBrowseStatic); ResourceNode *nodeJsonSpiffsBrowseStatic = new ResourceNode("/json/spiffs/browse/static", "GET", &handleSpiffsBrowseStatic);
ResourceNode *nodeJsonDelete = new ResourceNode("/json/spiffs/delete/static", "DELETE", &handleSpiffsDeleteStatic); ResourceNode *nodeJsonDelete = new ResourceNode("/json/spiffs/delete/static", "DELETE", &handleSpiffsDeleteStatic);
// Secure nodes // Secure nodes
@@ -297,16 +132,6 @@ void initWebServer()
insecureServer->setDefaultNode(node404); insecureServer->setDefaultNode(node404);
insecureServer->addMiddleware(&middlewareSpeedUp160); insecureServer->addMiddleware(&middlewareSpeedUp160);
DEBUG_MSG("Starting Web Servers...\n");
secureServer->start();
insecureServer->start();
if (secureServer->isRunning() && insecureServer->isRunning()) {
DEBUG_MSG("HTTP and HTTPS Web Servers Ready! :-) \n");
isWebServerReady = 1;
} else {
DEBUG_MSG("HTTP and HTTPS Web Servers Failed! ;-( \n");
}
} }
void middlewareSpeedUp240(HTTPRequest *req, HTTPResponse *res, std::function<void()> next) void middlewareSpeedUp240(HTTPRequest *req, HTTPResponse *res, std::function<void()> next)
@@ -319,7 +144,9 @@ void middlewareSpeedUp240(HTTPRequest *req, HTTPResponse *res, std::function<voi
powerFSM.trigger(EVENT_CONTACT_FROM_PHONE); powerFSM.trigger(EVENT_CONTACT_FROM_PHONE);
setCpuFrequencyMhz(240); setCpuFrequencyMhz(240);
timeSpeedUp = millis(); setTimeSpeedUp();
numberOfRequests++;
} }
void middlewareSpeedUp160(HTTPRequest *req, HTTPResponse *res, std::function<void()> next) void middlewareSpeedUp160(HTTPRequest *req, HTTPResponse *res, std::function<void()> next)
@@ -337,7 +164,108 @@ void middlewareSpeedUp160(HTTPRequest *req, HTTPResponse *res, std::function<voi
if (getCpuFrequencyMhz() != 240) { if (getCpuFrequencyMhz() != 240) {
setCpuFrequencyMhz(160); setCpuFrequencyMhz(160);
} }
timeSpeedUp = millis(); setTimeSpeedUp();
numberOfRequests++;
}
void handleAPIv1FromRadio(HTTPRequest *req, HTTPResponse *res)
{
DEBUG_MSG("+++++++++++++++ webAPI handleAPIv1FromRadio\n");
/*
For documentation, see:
https://github.com/meshtastic/Meshtastic-device/wiki/HTTP-REST-API-discussion
https://github.com/meshtastic/Meshtastic-device/blob/master/docs/software/device-api.md
Example:
http://10.10.30.198/api/v1/fromradio
*/
// Get access to the parameters
ResourceParameters *params = req->getParams();
// std::string paramAll = "all";
std::string valueAll;
// Status code is 200 OK by default.
res->setHeader("Content-Type", "application/x-protobuf");
res->setHeader("Access-Control-Allow-Origin", "*");
res->setHeader("Access-Control-Allow-Methods", "GET");
res->setHeader("X-Protobuf-Schema", "https://raw.githubusercontent.com/meshtastic/Meshtastic-protobufs/master/mesh.proto");
uint8_t txBuf[MAX_STREAM_BUF_SIZE];
uint32_t len = 1;
if (params->getQueryParameter("all", valueAll)) {
// If all is ture, return all the buffers we have available
// to us at this point in time.
if (valueAll == "true") {
while (len) {
len = webAPI.getFromRadio(txBuf);
res->write(txBuf, len);
}
// Otherwise, just return one protobuf
} else {
len = webAPI.getFromRadio(txBuf);
res->write(txBuf, len);
}
// the param "all" was not spcified. Return just one protobuf
} else {
len = webAPI.getFromRadio(txBuf);
res->write(txBuf, len);
}
DEBUG_MSG("--------------- webAPI handleAPIv1FromRadio, len %d\n", len);
}
void handleAPIv1ToRadio(HTTPRequest *req, HTTPResponse *res)
{
DEBUG_MSG("+++++++++++++++ webAPI handleAPIv1ToRadio\n");
/*
For documentation, see:
https://github.com/meshtastic/Meshtastic-device/wiki/HTTP-REST-API-discussion
https://github.com/meshtastic/Meshtastic-device/blob/master/docs/software/device-api.md
Example:
http://10.10.30.198/api/v1/toradio
*/
// Status code is 200 OK by default.
res->setHeader("Content-Type", "application/x-protobuf");
res->setHeader("Access-Control-Allow-Headers", "Content-Type");
res->setHeader("Access-Control-Allow-Origin", "*");
res->setHeader("Access-Control-Allow-Methods", "PUT, OPTIONS");
res->setHeader("X-Protobuf-Schema", "https://raw.githubusercontent.com/meshtastic/Meshtastic-protobufs/master/mesh.proto");
if (req->getMethod() == "OPTIONS") {
res->setStatusCode(204); // Success with no content
res->print("");
return;
}
byte buffer[MAX_TO_FROM_RADIO_SIZE];
size_t s = req->readBytes(buffer, MAX_TO_FROM_RADIO_SIZE);
DEBUG_MSG("Received %d bytes from PUT request\n", s);
webAPI.handleToRadio(buffer, s);
res->write(buffer, s);
DEBUG_MSG("--------------- webAPI handleAPIv1ToRadio\n");
}
void handleFavicon(HTTPRequest *req, HTTPResponse *res)
{
// Set Content-Type
res->setHeader("Content-Type", "image/vnd.microsoft.icon");
// Write data from header file
res->write(FAVICON_DATA, FAVICON_LENGTH);
} }
void handleStaticPost(HTTPRequest *req, HTTPResponse *res) void handleStaticPost(HTTPRequest *req, HTTPResponse *res)
@@ -414,7 +342,8 @@ void handleSpiffsBrowseStatic(HTTPRequest *req, HTTPResponse *res)
// jm // jm
res->setHeader("Content-Type", "application/json"); res->setHeader("Content-Type", "application/json");
// res->setHeader("Content-Type", "text/html"); res->setHeader("Access-Control-Allow-Origin", "*");
res->setHeader("Access-Control-Allow-Methods", "GET");
File root = SPIFFS.open("/"); File root = SPIFFS.open("/");
@@ -469,6 +398,8 @@ void handleSpiffsDeleteStatic(HTTPRequest *req, HTTPResponse *res)
std::string paramValDelete; std::string paramValDelete;
res->setHeader("Content-Type", "application/json"); res->setHeader("Content-Type", "application/json");
res->setHeader("Access-Control-Allow-Origin", "*");
res->setHeader("Access-Control-Allow-Methods", "DELETE");
if (params->getQueryParameter("delete", paramValDelete)) { if (params->getQueryParameter("delete", paramValDelete)) {
std::string pathDelete = "/" + paramValDelete; std::string pathDelete = "/" + paramValDelete;
if (SPIFFS.remove(pathDelete.c_str())) { if (SPIFFS.remove(pathDelete.c_str())) {
@@ -487,6 +418,67 @@ void handleSpiffsDeleteStatic(HTTPRequest *req, HTTPResponse *res)
} }
} }
/*
To convert text to c strings:
https://tomeko.net/online_tools/cpp_text_escape.php?lang=en
*/
void handleRoot(HTTPRequest *req, HTTPResponse *res)
{
res->setHeader("Content-Type", "text/html");
res->setHeader("Set-Cookie",
"mt_session=" + httpsserver::intToString(random(1, 9999999)) + "; Expires=Wed, 20 Apr 2049 4:20:00 PST");
std::string cookie = req->getHeader("Cookie");
// String cookieString = cookie.c_str();
// uint8_t nameIndex = cookieString.indexOf("mt_session");
// DEBUG_MSG(cookie.c_str());
std::string filename = "/static/index.html";
std::string filenameGzip = "/static/index.html.gz";
if (!SPIFFS.exists(filename.c_str()) && !SPIFFS.exists(filenameGzip.c_str())) {
// Send "404 Not Found" as response, as the file doesn't seem to exist
res->setStatusCode(404);
res->setStatusText("Not found");
res->println("404 Not Found");
res->printf("<p>File not found: %s</p>\n", filename.c_str());
res->printf("<p></p>\n");
res->printf("<p>You have gotten this error because the filesystem for the web server has not been loaded.</p>\n");
res->printf("<p>Please review the 'Common Problems' section of the <a "
"href=https://github.com/meshtastic/Meshtastic-device/wiki/"
"How-to-use-the-Meshtastic-Web-Interface-over-WiFi>web interface</a> documentation.</p>\n");
return;
}
// Try to open the file from SPIFFS
File file;
if (SPIFFS.exists(filename.c_str())) {
file = SPIFFS.open(filename.c_str());
if (!file.available()) {
DEBUG_MSG("File not available - %s\n", filename.c_str());
}
} else if (SPIFFS.exists(filenameGzip.c_str())) {
file = SPIFFS.open(filenameGzip.c_str());
res->setHeader("Content-Encoding", "gzip");
if (!file.available()) {
DEBUG_MSG("File not available\n");
}
}
// Read the file from SPIFFS and write it to the HTTP response body
size_t length = 0;
do {
char buffer[256];
length = file.read((uint8_t *)buffer, 256);
std::string bufferString(buffer, length);
res->write((uint8_t *)bufferString.c_str(), bufferString.size());
} while (length > 0);
}
void handleStaticBrowse(HTTPRequest *req, HTTPResponse *res) void handleStaticBrowse(HTTPRequest *req, HTTPResponse *res)
{ {
// Get access to the parameters // Get access to the parameters
@@ -494,6 +486,9 @@ void handleStaticBrowse(HTTPRequest *req, HTTPResponse *res)
std::string paramValDelete; std::string paramValDelete;
std::string paramValEdit; std::string paramValEdit;
DEBUG_MSG("Static Browse - Disabling keep-alive\n");
res->setHeader("Connection", "close");
// Set a default content type // Set a default content type
res->setHeader("Content-Type", "text/html"); res->setHeader("Content-Type", "text/html");
@@ -700,6 +695,11 @@ void handleStatic(HTTPRequest *req, HTTPResponse *res)
void handleFormUpload(HTTPRequest *req, HTTPResponse *res) void handleFormUpload(HTTPRequest *req, HTTPResponse *res)
{ {
DEBUG_MSG("Form Upload - Disabling keep-alive\n");
res->setHeader("Connection", "close");
DEBUG_MSG("Form Upload - Set frequency to 240mhz\n");
// The upload process is very CPU intensive. Let's speed things up a bit. // The upload process is very CPU intensive. Let's speed things up a bit.
setCpuFrequencyMhz(240); setCpuFrequencyMhz(240);
@@ -708,6 +708,7 @@ void handleFormUpload(HTTPRequest *req, HTTPResponse *res)
// Then we select the body parser based on the encoding. // Then we select the body parser based on the encoding.
// Actually we do this only for documentary purposes, we know the form is going // Actually we do this only for documentary purposes, we know the form is going
// to be multipart/form-data. // to be multipart/form-data.
DEBUG_MSG("Form Upload - Creating body parser reference\n");
HTTPBodyParser *parser; HTTPBodyParser *parser;
std::string contentType = req->getHeader("Content-Type"); std::string contentType = req->getHeader("Content-Type");
@@ -723,6 +724,7 @@ void handleFormUpload(HTTPRequest *req, HTTPResponse *res)
// Now, we can decide based on the content type: // Now, we can decide based on the content type:
if (contentType == "multipart/form-data") { if (contentType == "multipart/form-data") {
DEBUG_MSG("Form Upload - multipart/form-data\n");
parser = new HTTPMultipartBodyParser(req); parser = new HTTPMultipartBodyParser(req);
} else { } else {
Serial.printf("Unknown POST Content-Type: %s\n", contentType.c_str()); Serial.printf("Unknown POST Content-Type: %s\n", contentType.c_str());
@@ -757,21 +759,21 @@ void handleFormUpload(HTTPRequest *req, HTTPResponse *res)
// Double check that it is what we expect // Double check that it is what we expect
if (name != "file") { if (name != "file") {
DEBUG_MSG("Skipping unexpected field"); DEBUG_MSG("Skipping unexpected field\n");
res->println("<p>No file found.</p>"); res->println("<p>No file found.</p>");
return; return;
} }
// Double check that it is what we expect // Double check that it is what we expect
if (filename == "") { if (filename == "") {
DEBUG_MSG("Skipping unexpected field"); DEBUG_MSG("Skipping unexpected field\n");
res->println("<p>No file found.</p>"); res->println("<p>No file found.</p>");
return; return;
} }
// SPIFFS limits the total lenth of a path + file to 31 characters. // SPIFFS limits the total lenth of a path + file to 31 characters.
if (filename.length() + 8 > 31) { if (filename.length() + 8 > 31) {
DEBUG_MSG("Uploaded filename too long!"); DEBUG_MSG("Uploaded filename too long!\n");
res->println("<p>Uploaded filename too long! Limit of 23 characters.</p>"); res->println("<p>Uploaded filename too long! Limit of 23 characters.</p>");
delete parser; delete parser;
return; return;
@@ -806,10 +808,10 @@ void handleFormUpload(HTTPRequest *req, HTTPResponse *res)
return; return;
} }
//if (readLength) { // if (readLength) {
file.write(buf, readLength); file.write(buf, readLength);
fileLength += readLength; fileLength += readLength;
DEBUG_MSG("File Length %i\n", fileLength); DEBUG_MSG("File Length %i\n", fileLength);
//} //}
} }
// enableLoopWDT(); // enableLoopWDT();
@@ -824,6 +826,118 @@ void handleFormUpload(HTTPRequest *req, HTTPResponse *res)
delete parser; delete parser;
} }
void handleReport(HTTPRequest *req, HTTPResponse *res)
{
ResourceParameters *params = req->getParams();
std::string content;
if (!params->getQueryParameter("content", content)) {
content = "json";
}
if (content == "json") {
res->setHeader("Content-Type", "application/json");
res->setHeader("Access-Control-Allow-Origin", "*");
res->setHeader("Access-Control-Allow-Methods", "GET");
} else {
res->setHeader("Content-Type", "text/html");
res->println("<pre>");
}
res->println("{");
res->println("\"data\": {");
res->println("\"airtime\": {");
uint32_t *logArray;
res->print("\"tx_log\": [");
logArray = airtimeReport(TX_LOG);
for (int i = 0; i < getPeriodsToLog(); i++) {
uint32_t tmp;
tmp = *(logArray + i);
res->printf("%d", tmp);
if (i != getPeriodsToLog() - 1) {
res->print(", ");
}
}
res->println("],");
res->print("\"rx_log\": [");
logArray = airtimeReport(RX_LOG);
for (int i = 0; i < getPeriodsToLog(); i++) {
uint32_t tmp;
tmp = *(logArray + i);
res->printf("%d", tmp);
if (i != getPeriodsToLog() - 1) {
res->print(", ");
}
}
res->println("],");
res->print("\"rx_all_log\": [");
logArray = airtimeReport(RX_ALL_LOG);
for (int i = 0; i < getPeriodsToLog(); i++) {
uint32_t tmp;
tmp = *(logArray + i);
res->printf("%d", tmp);
if (i != getPeriodsToLog() - 1) {
res->print(", ");
}
}
res->println("],");
res->printf("\"seconds_since_boot\": %u,\n", getSecondsSinceBoot());
res->printf("\"seconds_per_period\": %u,\n", getSecondsPerPeriod());
res->printf("\"periods_to_log\": %u\n", getPeriodsToLog());
res->println("},");
res->println("\"wifi\": {");
res->printf("\"web_request_count\": %d,\n", numberOfRequests);
res->println("\"rssi\": " + String(WiFi.RSSI()) + ",");
if (radioConfig.preferences.wifi_ap_mode || isSoftAPForced()) {
res->println("\"ip\": \"" + String(WiFi.softAPIP().toString().c_str()) + "\"");
} else {
res->println("\"ip\": \"" + String(WiFi.localIP().toString().c_str()) + "\"");
}
res->println("},");
res->println("\"memory\": {");
res->printf("\"heap_total\": %d,\n", ESP.getHeapSize());
res->printf("\"heap_free\": %d,\n", ESP.getFreeHeap());
res->printf("\"psram_total\": %d,\n", ESP.getPsramSize());
res->printf("\"psram_free\": %d,\n", ESP.getFreePsram());
res->println("\"spiffs_total\" : " + String(SPIFFS.totalBytes()) + ",");
res->println("\"spiffs_used\" : " + String(SPIFFS.usedBytes()) + ",");
res->println("\"spiffs_free\" : " + String(SPIFFS.totalBytes() - SPIFFS.usedBytes()));
res->println("},");
res->println("\"power\": {");
res->printf("\"battery_percent\": %u,\n", powerStatus->getBatteryChargePercent());
res->printf("\"battery_voltage_mv\": %u,\n", powerStatus->getBatteryVoltageMv());
res->printf("\"has_battery\": %s,\n", BoolToString(powerStatus->getHasBattery()));
res->printf("\"has_usb\": %s,\n", BoolToString(powerStatus->getHasUSB()));
res->printf("\"is_charging\": %s\n", BoolToString(powerStatus->getIsCharging()));
res->println("}");
res->println("},");
res->println("\"status\": \"ok\"");
res->println("}");
}
// --------
void handle404(HTTPRequest *req, HTTPResponse *res) void handle404(HTTPRequest *req, HTTPResponse *res)
{ {
@@ -861,165 +975,18 @@ void handleHotspot(HTTPRequest *req, HTTPResponse *res)
// Status code is 200 OK by default. // Status code is 200 OK by default.
// We want to deliver a simple HTML page, so we send a corresponding content type: // We want to deliver a simple HTML page, so we send a corresponding content type:
res->setHeader("Content-Type", "text/html"); res->setHeader("Content-Type", "text/html");
res->setHeader("Access-Control-Allow-Origin", "*");
res->setHeader("Access-Control-Allow-Methods", "GET");
// res->println("<!DOCTYPE html>"); // res->println("<!DOCTYPE html>");
res->println("<meta http-equiv=\"refresh\" content=\"0;url=/\" />\n"); res->println("<meta http-equiv=\"refresh\" content=\"0;url=/\" />\n");
} }
void handleAPIv1FromRadio(HTTPRequest *req, HTTPResponse *res)
{
DEBUG_MSG("+++++++++++++++ webAPI handleAPIv1FromRadio\n");
/*
For documentation, see:
https://github.com/meshtastic/Meshtastic-device/wiki/HTTP-REST-API-discussion
https://github.com/meshtastic/Meshtastic-device/blob/master/docs/software/device-api.md
Example:
http://10.10.30.198/api/v1/fromradio
*/
// Get access to the parameters
ResourceParameters *params = req->getParams();
// std::string paramAll = "all";
std::string valueAll;
// Status code is 200 OK by default.
res->setHeader("Content-Type", "application/x-protobuf");
res->setHeader("Access-Control-Allow-Origin", "*");
res->setHeader("Access-Control-Allow-Methods", "PUT, GET");
res->setHeader("X-Protobuf-Schema", "https://raw.githubusercontent.com/meshtastic/Meshtastic-protobufs/master/mesh.proto");
uint8_t txBuf[MAX_STREAM_BUF_SIZE];
uint32_t len = 1;
if (params->getQueryParameter("all", valueAll)) {
// If all is ture, return all the buffers we have available
// to us at this point in time.
if (valueAll == "true") {
while (len) {
len = webAPI.getFromRadio(txBuf);
res->write(txBuf, len);
}
// Otherwise, just return one protobuf
} else {
len = webAPI.getFromRadio(txBuf);
res->write(txBuf, len);
}
// the param "all" was not spcified. Return just one protobuf
} else {
len = webAPI.getFromRadio(txBuf);
res->write(txBuf, len);
}
DEBUG_MSG("--------------- webAPI handleAPIv1FromRadio, len %d\n", len);
}
void handleAPIv1ToRadio(HTTPRequest *req, HTTPResponse *res)
{
DEBUG_MSG("+++++++++++++++ webAPI handleAPIv1ToRadio\n");
/*
For documentation, see:
https://github.com/meshtastic/Meshtastic-device/wiki/HTTP-REST-API-discussion
https://github.com/meshtastic/Meshtastic-device/blob/master/docs/software/device-api.md
Example:
http://10.10.30.198/api/v1/toradio
*/
// Status code is 200 OK by default.
res->setHeader("Content-Type", "application/x-protobuf");
res->setHeader("Access-Control-Allow-Headers", "Content-Type");
res->setHeader("Access-Control-Allow-Origin", "*");
res->setHeader("Access-Control-Allow-Methods", "PUT, OPTIONS");
res->setHeader("X-Protobuf-Schema", "https://raw.githubusercontent.com/meshtastic/Meshtastic-protobufs/master/mesh.proto");
if (req->getMethod() == "OPTIONS") {
res->setStatusCode(204); // Success with no content
res->print("");
return;
}
byte buffer[MAX_TO_FROM_RADIO_SIZE];
size_t s = req->readBytes(buffer, MAX_TO_FROM_RADIO_SIZE);
DEBUG_MSG("Received %d bytes from PUT request\n", s);
webAPI.handleToRadio(buffer, s);
res->write(buffer, s);
DEBUG_MSG("--------------- webAPI handleAPIv1ToRadio\n");
}
/*
To convert text to c strings:
https://tomeko.net/online_tools/cpp_text_escape.php?lang=en
*/
void handleRoot(HTTPRequest *req, HTTPResponse *res)
{
res->setHeader("Content-Type", "text/html");
res->setHeader("Set-Cookie",
"mt_session=" + httpsserver::intToString(random(1, 9999999)) + "; Expires=Wed, 20 Apr 2049 4:20:00 PST");
std::string cookie = req->getHeader("Cookie");
// String cookieString = cookie.c_str();
// uint8_t nameIndex = cookieString.indexOf("mt_session");
// DEBUG_MSG(cookie.c_str());
std::string filename = "/static/index.html";
std::string filenameGzip = "/static/index.html.gz";
if (!SPIFFS.exists(filename.c_str()) && !SPIFFS.exists(filenameGzip.c_str())) {
// Send "404 Not Found" as response, as the file doesn't seem to exist
res->setStatusCode(404);
res->setStatusText("Not found");
res->println("404 Not Found");
res->printf("<p>File not found: %s</p>\n", filename.c_str());
res->printf("<p></p>\n");
res->printf("<p>You have gotten this error because the filesystem for the web server has not been loaded.</p>\n");
res->printf("<p>Please review the 'Common Problems' section of the <a "
"href=https://github.com/meshtastic/Meshtastic-device/wiki/How-to-use-the-Meshtastic-Web-Interface-over-WiFi>web interface</a> documentation.</p>\n");
return;
}
// Try to open the file from SPIFFS
File file;
if (SPIFFS.exists(filename.c_str())) {
file = SPIFFS.open(filename.c_str());
if (!file.available()) {
DEBUG_MSG("File not available - %s\n", filename.c_str());
}
} else if (SPIFFS.exists(filenameGzip.c_str())) {
file = SPIFFS.open(filenameGzip.c_str());
res->setHeader("Content-Encoding", "gzip");
if (!file.available()) {
DEBUG_MSG("File not available\n");
}
}
// Read the file from SPIFFS and write it to the HTTP response body
size_t length = 0;
do {
char buffer[256];
length = file.read((uint8_t *)buffer, 256);
std::string bufferString(buffer, length);
res->write((uint8_t *)bufferString.c_str(), bufferString.size());
} while (length > 0);
}
void handleRestart(HTTPRequest *req, HTTPResponse *res) void handleRestart(HTTPRequest *req, HTTPResponse *res)
{ {
res->setHeader("Content-Type", "text/html"); res->setHeader("Content-Type", "text/html");
res->setHeader("Access-Control-Allow-Origin", "*");
res->setHeader("Access-Control-Allow-Methods", "GET");
DEBUG_MSG("***** Restarted on HTTP(s) Request *****\n"); DEBUG_MSG("***** Restarted on HTTP(s) Request *****\n");
res->println("Restarting"); res->println("Restarting");
@@ -1030,6 +997,8 @@ void handleRestart(HTTPRequest *req, HTTPResponse *res)
void handleBlinkLED(HTTPRequest *req, HTTPResponse *res) void handleBlinkLED(HTTPRequest *req, HTTPResponse *res)
{ {
res->setHeader("Content-Type", "application/json"); res->setHeader("Content-Type", "application/json");
res->setHeader("Access-Control-Allow-Origin", "*");
res->setHeader("Access-Control-Allow-Methods", "POST");
ResourceParameters *params = req->getParams(); ResourceParameters *params = req->getParams();
std::string blink_target; std::string blink_target;
@@ -1058,100 +1027,11 @@ void handleBlinkLED(HTTPRequest *req, HTTPResponse *res)
res->println("}"); res->println("}");
} }
void handleReport(HTTPRequest *req, HTTPResponse *res)
{
ResourceParameters *params = req->getParams();
std::string content;
if (!params->getQueryParameter("content", content)) {
content = "json";
}
if (content == "json") {
res->setHeader("Content-Type", "application/json");
} else {
res->setHeader("Content-Type", "text/html");
res->println("<pre>");
}
res->println("{");
res->println("\"data\": {");
res->println("\"airtime\": {");
uint16_t *logArray;
res->print("\"tx_log\": [");
logArray = airtimeReport(TX_LOG);
for (int i = 0; i < getPeriodsToLog(); i++) {
uint16_t tmp;
tmp = *(logArray + i);
res->printf("%d", tmp);
if (i != getPeriodsToLog() - 1) {
res->print(", ");
}
}
res->println("],");
res->print("\"rx_log\": [");
logArray = airtimeReport(RX_LOG);
for (int i = 0; i < getPeriodsToLog(); i++) {
uint16_t tmp;
tmp = *(logArray + i);
res->printf("%d", tmp);
if (i != getPeriodsToLog() - 1) {
res->print(", ");
}
}
res->println("],");
res->print("\"rx_all_log\": [");
logArray = airtimeReport(RX_ALL_LOG);
for (int i = 0; i < getPeriodsToLog(); i++) {
uint16_t tmp;
tmp = *(logArray + i);
res->printf("%d", tmp);
if (i != getPeriodsToLog() - 1) {
res->print(", ");
}
}
res->println("],");
res->printf("\"seconds_since_boot\": %u,\n", getSecondsSinceBoot());
res->printf("\"seconds_per_period\": %u,\n", getSecondsPerPeriod());
res->printf("\"periods_to_log\": %u\n", getPeriodsToLog());
res->println("},");
res->println("\"wifi\": {");
res->println("\"rssi\": " + String(WiFi.RSSI()) + ",");
if (radioConfig.preferences.wifi_ap_mode || isSoftAPForced()) {
res->println("\"ip\": \"" + String(WiFi.softAPIP().toString().c_str()) + "\"");
} else {
res->println("\"ip\": \"" + String(WiFi.localIP().toString().c_str()) + "\"");
}
res->println("},");
res->println("\"test\": 123");
res->println("},");
res->println("\"status\": \"ok\"");
res->println("}");
}
void handleScanNetworks(HTTPRequest *req, HTTPResponse *res) void handleScanNetworks(HTTPRequest *req, HTTPResponse *res)
{ {
res->setHeader("Content-Type", "application/json"); res->setHeader("Content-Type", "application/json");
res->setHeader("Access-Control-Allow-Origin", "*");
res->setHeader("Access-Control-Allow-Methods", "GET");
// res->setHeader("Content-Type", "text/html"); // res->setHeader("Content-Type", "text/html");
int n = WiFi.scanNetworks(); int n = WiFi.scanNetworks();
@@ -1191,22 +1071,3 @@ void handleScanNetworks(HTTPRequest *req, HTTPResponse *res)
res->println("\"status\": \"ok\""); res->println("\"status\": \"ok\"");
res->println("}"); res->println("}");
} }
void handleFavicon(HTTPRequest *req, HTTPResponse *res)
{
// Set Content-Type
res->setHeader("Content-Type", "image/vnd.microsoft.icon");
// Write data from header file
res->write(FAVICON_DATA, FAVICON_LENGTH);
}
void replaceAll(std::string &str, const std::string &from, const std::string &to)
{
if (from.empty())
return;
size_t start_pos = 0;
while ((start_pos = str.find(from, start_pos)) != std::string::npos) {
str.replace(start_pos, from.length(), to);
start_pos += to.length(); // In case 'to' contains 'from', like replacing 'x' with 'yx'
}
}

View File

@@ -0,0 +1,46 @@
#pragma once
void registerHandlers(HTTPServer *insecureServer, HTTPSServer *secureServer);
// Declare some handler functions for the various URLs on the server
void handleAPIv1FromRadio(HTTPRequest *req, HTTPResponse *res);
void handleAPIv1ToRadio(HTTPRequest *req, HTTPResponse *res);
void handleStyleCSS(HTTPRequest *req, HTTPResponse *res);
void handleHotspot(HTTPRequest *req, HTTPResponse *res);
void handleRoot(HTTPRequest *req, HTTPResponse *res);
void handleStaticBrowse(HTTPRequest *req, HTTPResponse *res);
void handleStaticPost(HTTPRequest *req, HTTPResponse *res);
void handleStatic(HTTPRequest *req, HTTPResponse *res);
void handleRestart(HTTPRequest *req, HTTPResponse *res);
void handle404(HTTPRequest *req, HTTPResponse *res);
void handleFormUpload(HTTPRequest *req, HTTPResponse *res);
void handleScanNetworks(HTTPRequest *req, HTTPResponse *res);
void handleSpiffsBrowseStatic(HTTPRequest *req, HTTPResponse *res);
void handleSpiffsDeleteStatic(HTTPRequest *req, HTTPResponse *res);
void handleBlinkLED(HTTPRequest *req, HTTPResponse *res);
void handleReport(HTTPRequest *req, HTTPResponse *res);
void handleFavicon(HTTPRequest *req, HTTPResponse *res);
void middlewareSpeedUp240(HTTPRequest *req, HTTPResponse *res, std::function<void()> next);
void middlewareSpeedUp160(HTTPRequest *req, HTTPResponse *res, std::function<void()> next);
void middlewareSession(HTTPRequest *req, HTTPResponse *res, std::function<void()> next);
uint32_t getTimeSpeedUp();
void setTimeSpeedUp();
// Interface to the PhoneAPI to access the protobufs with messages
class HttpAPI : public PhoneAPI
{
public:
// Nothing here yet
private:
// Nothing here yet
protected:
// Nothing here yet
};

View File

@@ -0,0 +1,14 @@
#include "mesh/http/ContentHelper.h"
//#include <Arduino.h>
//#include "main.h"
void replaceAll(std::string &str, const std::string &from, const std::string &to)
{
if (from.empty())
return;
size_t start_pos = 0;
while ((start_pos = str.find(from, start_pos)) != std::string::npos) {
str.replace(start_pos, from.length(), to);
start_pos += to.length(); // In case 'to' contains 'from', like replacing 'x' with 'yx'
}
}

View File

@@ -0,0 +1,8 @@
#include <Arduino.h>
#include <functional>
#define BoolToString(x) ((x) ? "true" : "false")
void replaceAll(std::string &str, const std::string &from, const std::string &to);

View File

@@ -2,14 +2,7 @@
#include <functional> #include <functional>
/* /*
Steps: This file contains static content.
- Compress the .js file to .js.gz
- Convert to hex:
http://tomeko.net/online_tools/file_to_hex.php?lang=en
- Paste into the array
- Note the filesize of your .gz file and write the file
size into the length int.
*/ */
// Length of the binary data // Length of the binary data

231
src/mesh/http/WebServer.cpp Normal file
View File

@@ -0,0 +1,231 @@
#include "mesh/http/WebServer.h"
#include "NodeDB.h"
#include "mesh/http/WiFiAPClient.h"
#include <HTTPBodyParser.hpp>
#include <HTTPMultipartBodyParser.hpp>
#include <HTTPURLEncodedBodyParser.hpp>
#include <WebServer.h>
#include <WiFi.h>
#ifndef NO_ESP32
#include "esp_task_wdt.h"
#endif
// Persistant Data Storage
#include <Preferences.h>
Preferences prefs;
/*
Including the esp32_https_server library will trigger a compile time error. I've
tracked it down to a reoccurrance of this bug:
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=57824
The work around is described here:
https://forums.xilinx.com/t5/Embedded-Development-Tools/Error-with-Standard-Libaries-in-Zynq/td-p/450032
Long story short is we need "#undef str" before including the esp32_https_server.
- Jm Casler (jm@casler.org) Oct 2020
*/
#undef str
// Includes for the https server
// https://github.com/fhessel/esp32_https_server
#include <HTTPRequest.hpp>
#include <HTTPResponse.hpp>
#include <HTTPSServer.hpp>
#include <HTTPServer.hpp>
#include <SSLCert.hpp>
// The HTTPS Server comes in a separate namespace. For easier use, include it here.
using namespace httpsserver;
#include "mesh/http/ContentHandler.h"
SSLCert *cert;
HTTPSServer *secureServer;
HTTPServer *insecureServer;
bool isWebServerReady = 0;
bool isCertReady = 0;
void handleWebResponse()
{
if (isWifiAvailable() == 0) {
return;
}
if (isWebServerReady) {
// We're going to handle the DNS responder here so it
// will be ignored by the NRF boards.
handleDNSResponse();
secureServer->loop();
insecureServer->loop();
}
/*
Slow down the CPU if we have not received a request within the last few
seconds.
*/
if (millis() - getTimeSpeedUp() >= (25 * 1000)) {
setCpuFrequencyMhz(80);
setTimeSpeedUp();
}
}
void taskCreateCert(void *parameter)
{
prefs.begin("MeshtasticHTTPS", false);
// Delete the saved certs
if (0) {
DEBUG_MSG("Deleting any saved SSL keys ...\n");
// prefs.clear();
prefs.remove("PK");
prefs.remove("cert");
}
size_t pkLen = prefs.getBytesLength("PK");
size_t certLen = prefs.getBytesLength("cert");
DEBUG_MSG("Checking if we have a previously saved SSL Certificate.\n");
if (pkLen && certLen) {
DEBUG_MSG("Existing SSL Certificate found!\n");
} else {
DEBUG_MSG("Creating the certificate. This may take a while. Please wait...\n");
yield();
cert = new SSLCert();
yield();
int createCertResult = createSelfSignedCert(*cert, KEYSIZE_2048, "CN=meshtastic.local,O=Meshtastic,C=US",
"20190101000000", "20300101000000");
yield();
if (createCertResult != 0) {
DEBUG_MSG("Creating the certificate failed\n");
// Serial.printf("Creating the certificate failed. Error Code = 0x%02X, check SSLCert.hpp for details",
// createCertResult);
// while (true)
// delay(500);
} else {
DEBUG_MSG("Creating the certificate was successful\n");
DEBUG_MSG("Created Private Key: %d Bytes\n", cert->getPKLength());
// for (int i = 0; i < cert->getPKLength(); i++)
// Serial.print(cert->getPKData()[i], HEX);
// Serial.println();
DEBUG_MSG("Created Certificate: %d Bytes\n", cert->getCertLength());
// for (int i = 0; i < cert->getCertLength(); i++)
// Serial.print(cert->getCertData()[i], HEX);
// Serial.println();
prefs.putBytes("PK", (uint8_t *)cert->getPKData(), cert->getPKLength());
prefs.putBytes("cert", (uint8_t *)cert->getCertData(), cert->getCertLength());
}
}
isCertReady = 1;
vTaskDelete(NULL);
}
void createSSLCert()
{
if (isWifiAvailable() == 0) {
return;
}
// Create a new process just to handle creating the cert.
// This is a workaround for Bug: https://github.com/fhessel/esp32_https_server/issues/48
// jm@casler.org (Oct 2020)
xTaskCreate(taskCreateCert, /* Task function. */
"createCert", /* String with name of task. */
16384, /* Stack size in bytes. */
NULL, /* Parameter passed as input of the task */
16, /* Priority of the task. */
NULL); /* Task handle. */
DEBUG_MSG("Waiting for SSL Cert to be generated.\n");
while (!isCertReady) {
DEBUG_MSG(".");
delay(1000);
yield();
esp_task_wdt_reset();
}
DEBUG_MSG("SSL Cert Ready!\n");
}
WebServerThread *webServerThread;
WebServerThread::WebServerThread() : concurrency::OSThread("WebServerThread") {}
int32_t WebServerThread::runOnce()
{
// DEBUG_MSG("WebServerThread::runOnce()\n");
handleWebResponse();
// Loop every 5ms.
return (5);
}
void initWebServer()
{
DEBUG_MSG("Initializing Web Server ...\n");
prefs.begin("MeshtasticHTTPS", false);
size_t pkLen = prefs.getBytesLength("PK");
size_t certLen = prefs.getBytesLength("cert");
DEBUG_MSG("Checking if we have a previously saved SSL Certificate.\n");
if (pkLen && certLen) {
uint8_t *pkBuffer = new uint8_t[pkLen];
prefs.getBytes("PK", pkBuffer, pkLen);
uint8_t *certBuffer = new uint8_t[certLen];
prefs.getBytes("cert", certBuffer, certLen);
cert = new SSLCert(certBuffer, certLen, pkBuffer, pkLen);
DEBUG_MSG("Retrieved Private Key: %d Bytes\n", cert->getPKLength());
// DEBUG_MSG("Retrieved Private Key: " + String(cert->getPKLength()) + " Bytes");
// for (int i = 0; i < cert->getPKLength(); i++)
// Serial.print(cert->getPKData()[i], HEX);
// Serial.println();
DEBUG_MSG("Retrieved Certificate: %d Bytes\n", cert->getCertLength());
// for (int i = 0; i < cert->getCertLength(); i++)
// Serial.print(cert->getCertData()[i], HEX);
// Serial.println();
} else {
DEBUG_MSG("Web Server started without SSL keys! How did this happen?\n");
}
// We can now use the new certificate to setup our server as usual.
secureServer = new HTTPSServer(cert);
insecureServer = new HTTPServer();
registerHandlers(insecureServer, secureServer);
DEBUG_MSG("Starting Web Servers...\n");
secureServer->start();
insecureServer->start();
if (secureServer->isRunning() && insecureServer->isRunning()) {
DEBUG_MSG("HTTP and HTTPS Web Servers Ready! :-) \n");
isWebServerReady = 1;
} else {
DEBUG_MSG("HTTP and HTTPS Web Servers Failed! ;-( \n");
}
}

26
src/mesh/http/WebServer.h Normal file
View File

@@ -0,0 +1,26 @@
#pragma once
#include "PhoneAPI.h"
#include "concurrency/OSThread.h"
#include <Arduino.h>
#include <functional>
void initWebServer();
void createSSLCert();
void handleWebResponse();
class WebServerThread : private concurrency::OSThread
{
public:
WebServerThread();
protected:
virtual int32_t runOnce();
};
extern WebServerThread *webServerThread;

View File

@@ -1,9 +1,9 @@
#include "meshwifi.h" #include "mesh/http/WiFiAPClient.h"
#include "NodeDB.h" #include "NodeDB.h"
#include "mesh/wifi/WiFiServerAPI.h"
#include "configuration.h" #include "configuration.h"
#include "main.h" #include "main.h"
#include "meshwifi/meshhttp.h" #include "mesh/http/WebServer.h"
#include "mesh/wifi/WiFiServerAPI.h"
#include "target_specific.h" #include "target_specific.h"
#include <DNSServer.h> #include <DNSServer.h>
#include <ESPmDNS.h> #include <ESPmDNS.h>

View File

@@ -1,38 +0,0 @@
#pragma once
#include "PhoneAPI.h"
#include <Arduino.h>
#include <functional>
void initWebServer();
void createSSLCert();
void handleNotFound();
void handleWebResponse();
void handleJSONChatHistory();
void notifyWebUI();
void handleHotspot();
void handleStyleCSS();
void handleRoot();
void handleScriptsScriptJS();
void handleJSONChatHistoryDummy();
void replaceAll(std::string& str, const std::string& from, const std::string& to);
class HttpAPI : public PhoneAPI
{
public:
// Nothing here yet
private:
// Nothing here yet
protected:
// Nothing here yet
};

View File

@@ -7,13 +7,17 @@
#include "esp_bt.h" #include "esp_bt.h"
#include "host/util/util.h" #include "host/util/util.h"
#include "main.h" #include "main.h"
#include "meshwifi/meshwifi.h"
#include "nimble/NimbleDefs.h" #include "nimble/NimbleDefs.h"
#include "services/gap/ble_svc_gap.h" #include "services/gap/ble_svc_gap.h"
#include "services/gatt/ble_svc_gatt.h" #include "services/gatt/ble_svc_gatt.h"
#include "sleep.h"
#include <Arduino.h> #include <Arduino.h>
#include <WiFi.h> #include <WiFi.h>
#ifndef NO_ESP32
#include "mesh/http/WiFiAPClient.h"
#endif
static bool pinShowing; static bool pinShowing;
static uint32_t doublepressed; static uint32_t doublepressed;
@@ -223,13 +227,11 @@ static int gap_event(struct ble_gap_event *event, void *arg)
if (event->passkey.params.action == BLE_SM_IOACT_DISP) { if (event->passkey.params.action == BLE_SM_IOACT_DISP) {
pkey.action = event->passkey.params.action; pkey.action = event->passkey.params.action;
DEBUG_MSG("dp: %d now:%d\n",doublepressed, now); DEBUG_MSG("dp: %d now:%d\n", doublepressed, now);
if (doublepressed > 0 && (doublepressed + (30*1000)) > now) if (doublepressed > 0 && (doublepressed + (30 * 1000)) > now) {
{
DEBUG_MSG("User has overridden passkey or no display available\n"); DEBUG_MSG("User has overridden passkey or no display available\n");
pkey.passkey = defaultBLEPin; pkey.passkey = defaultBLEPin;
} } else {
else {
DEBUG_MSG("Using random passkey\n"); DEBUG_MSG("Using random passkey\n");
pkey.passkey = random( pkey.passkey = random(
100000, 999999); // This is the passkey to be entered on peer - we pick a number >100,000 to ensure 6 digits 100000, 999999); // This is the passkey to be entered on peer - we pick a number >100,000 to ensure 6 digits
@@ -392,7 +394,6 @@ void gatt_svr_register_cb(struct ble_gatt_register_ctxt *ctxt, void *arg)
} }
} }
/** /**
* A helper function that implements simple read and write handling for a uint32_t * A helper function that implements simple read and write handling for a uint32_t
* *
@@ -446,8 +447,7 @@ int chr_readwrite8(uint8_t *v, size_t vlen, struct ble_gatt_access_ctxt *ctxt)
if (len < vlen) { if (len < vlen) {
DEBUG_MSG("Error: wrongsized write\n"); DEBUG_MSG("Error: wrongsized write\n");
return BLE_ATT_ERR_UNLIKELY; return BLE_ATT_ERR_UNLIKELY;
} } else {
else {
DEBUG_MSG("BLE writing bytes\n"); DEBUG_MSG("BLE writing bytes\n");
} }
} else { } else {
@@ -462,6 +462,20 @@ void disablePin()
{ {
DEBUG_MSG("User Override, disabling bluetooth pin requirement\n"); DEBUG_MSG("User Override, disabling bluetooth pin requirement\n");
// keep track of when it was pressed, so we know it was within X seconds // keep track of when it was pressed, so we know it was within X seconds
// Flash the LED
setLed(true);
delay(100);
setLed(false);
delay(100);
setLed(true);
delay(100);
setLed(false);
delay(100);
setLed(true);
delay(100);
setLed(false);
doublepressed = millis(); doublepressed = millis();
} }
@@ -545,7 +559,9 @@ void setBluetoothEnable(bool on)
if (firstTime) { if (firstTime) {
firstTime = 0; firstTime = 0;
} else { } else {
#ifndef NO_ESP32
initWifi(0); initWifi(0);
#endif
} }
} else { } else {
@@ -557,7 +573,9 @@ void setBluetoothEnable(bool on)
*/ */
// shutdown wifi // shutdown wifi
#ifndef NO_ESP32
deinitWifi(); deinitWifi();
#endif
// We have to totally teardown our bluetooth objects to prevent leaks // We have to totally teardown our bluetooth objects to prevent leaks
deinitBLE(); deinitBLE();

View File

@@ -16,8 +16,14 @@ static void printUsageErrorMsg(uint32_t cfsr)
cfsr >>= SCB_CFSR_USGFAULTSR_Pos; // right shift to lsb cfsr >>= SCB_CFSR_USGFAULTSR_Pos; // right shift to lsb
if ((cfsr & (1 << 9)) != 0) if ((cfsr & (1 << 9)) != 0)
FAULT_MSG("Divide by zero\n"); FAULT_MSG("Divide by zero\n");
if ((cfsr & (1 << 8)) != 0) else if ((cfsr & (1 << 8)) != 0)
FAULT_MSG("Unaligned\n"); FAULT_MSG("Unaligned\n");
else if ((cfsr & (1 << 1)) != 0)
FAULT_MSG("Invalid state\n");
else if ((cfsr & (1 << 0)) != 0)
FAULT_MSG("Invalid instruction\n");
else
FAULT_MSG("FIXME add to printUsageErrorMsg!\n");
} }
static void printBusErrorMsg(uint32_t cfsr) static void printBusErrorMsg(uint32_t cfsr)
@@ -71,8 +77,9 @@ extern "C" void HardFault_Impl(uint32_t stack[])
FAULT_MSG("Done with fault report - Waiting to reboot\n"); FAULT_MSG("Done with fault report - Waiting to reboot\n");
asm volatile("bkpt #01"); // Enter the debugger if one is connected asm volatile("bkpt #01"); // Enter the debugger if one is connected
while (1)
; // Don't spin, so that the debugger will let the user step to next instruction
// while (1) ;
} }
extern "C" void HardFault_Handler(void) extern "C" void HardFault_Handler(void)

View File

@@ -51,7 +51,7 @@ void getMacAddr(uint8_t *dmac)
NRF52Bluetooth *nrf52Bluetooth; NRF52Bluetooth *nrf52Bluetooth;
static bool bleOn = false; static bool bleOn = false;
static const bool enableBle = true; // Set to false for easier debugging static const bool enableBle = false; // Set to false for easier debugging
void setBluetoothEnable(bool on) void setBluetoothEnable(bool on)
{ {

View File

@@ -1,5 +1,9 @@
#include "meshwifi/meshhttp.h" //#include "mesh/wifi/WebServer.h"
#include "meshwifi/meshwifi.h" #include "configuration.h"
#ifndef NO_ESP32
//#include "mesh/wifi/WiFiAPClient.h"
void initWifi(bool forceSoftAP) {} void initWifi(bool forceSoftAP) {}
@@ -10,7 +14,4 @@ bool isWifiAvailable()
return false; return false;
} }
void handleWebResponse() {} #endif
/// Perform idle loop processing required by the wifi layer
void loopWifi() {}

View File

@@ -0,0 +1,176 @@
#include "ExternalNotificationPlugin.h"
#include "MeshService.h"
#include "NodeDB.h"
#include "RTC.h"
#include "Router.h"
#include "configuration.h"
#include <Arduino.h>
#include <assert.h>
/*
Documentation:
https://github.com/mc-hamster/Meshtastic-device/blob/master/docs/software/plugins/ExternalNotificationPlugin.md
This plugin supports:
https://github.com/meshtastic/Meshtastic-device/issues/654
Quick reference:
radioConfig.preferences.ext_notification_plugin_enabled
0 = Disabled (Default)
1 = Enabled
radioConfig.preferences.ext_notification_plugin_active
0 = Active Low (Default)
1 = Active High
radioConfig.preferences.ext_notification_plugin_alert_message
0 = Disabled (Default)
1 = Alert when a text message comes
radioConfig.preferences.ext_notification_plugin_alert_bell
0 = Disabled (Default)
1 = Alert when the bell character is received
radioConfig.preferences.ext_notification_plugin_output
GPIO of the output. (Default = 13)
radioConfig.preferences.ext_notification_plugin_output_ms
Amount of time in ms for the alert. Default is 1000.
*/
// Default configurations
#define EXT_NOTIFICATION_PLUGIN_OUTPUT 13
#define EXT_NOTIFICATION_PLUGIN_OUTPUT_MS 1000
#define ASCII_BELL 0x07
ExternalNotificationPlugin *externalNotificationPlugin;
ExternalNotificationPluginRadio *externalNotificationPluginRadio;
ExternalNotificationPlugin::ExternalNotificationPlugin() : concurrency::OSThread("ExternalNotificationPlugin") {}
bool externalCurrentState = 0;
uint32_t externalTurnedOn = 0;
int32_t ExternalNotificationPlugin::runOnce()
{
#ifndef NO_ESP32
/*
Uncomment the preferences below if you want to use the plugin
without having to configure it from the PythonAPI or WebUI.
*/
// radioConfig.preferences.ext_notification_plugin_enabled = 1;
// radioConfig.preferences.ext_notification_plugin_alert_message = 1;
// radioConfig.preferences.ext_notification_plugin_active = 1;
// radioConfig.preferences.ext_notification_plugin_alert_bell = 1;
// radioConfig.preferences.ext_notification_plugin_output_ms = 1000;
// radioConfig.preferences.ext_notification_plugin_output = 13;
if (radioConfig.preferences.ext_notification_plugin_enabled) {
if (firstTime) {
DEBUG_MSG("Initializing External Notification Plugin\n");
// Set the direction of a pin
pinMode((radioConfig.preferences.ext_notification_plugin_output
? radioConfig.preferences.ext_notification_plugin_output
: EXT_NOTIFICATION_PLUGIN_OUTPUT),
OUTPUT);
// Turn off the pin
setExternalOff();
externalNotificationPluginRadio = new ExternalNotificationPluginRadio();
firstTime = 0;
} else {
if (externalCurrentState) {
// If the output is turned on, turn it back off after the given period of time.
if (externalTurnedOn + (radioConfig.preferences.ext_notification_plugin_output_ms
? radioConfig.preferences.ext_notification_plugin_output_ms
: EXT_NOTIFICATION_PLUGIN_OUTPUT_MS) <
millis()) {
DEBUG_MSG("Turning off external notification\n");
setExternalOff();
}
}
}
return (25);
} else {
DEBUG_MSG("External Notification Plugin Disabled\n");
return (INT32_MAX);
}
#endif
}
void ExternalNotificationPlugin::setExternalOn()
{
externalCurrentState = 1;
externalTurnedOn = millis();
digitalWrite((radioConfig.preferences.ext_notification_plugin_output ? radioConfig.preferences.ext_notification_plugin_output
: EXT_NOTIFICATION_PLUGIN_OUTPUT),
(radioConfig.preferences.ext_notification_plugin_active ? true : false));
}
void ExternalNotificationPlugin::setExternalOff()
{
externalCurrentState = 0;
digitalWrite((radioConfig.preferences.ext_notification_plugin_output ? radioConfig.preferences.ext_notification_plugin_output
: EXT_NOTIFICATION_PLUGIN_OUTPUT),
(radioConfig.preferences.ext_notification_plugin_active ? false : true));
}
// --------
bool ExternalNotificationPluginRadio::handleReceived(const MeshPacket &mp)
{
#ifndef NO_ESP32
if (radioConfig.preferences.ext_notification_plugin_enabled) {
auto &p = mp.decoded.data;
if (mp.from != nodeDB.getNodeNum()) {
// TODO: This may be a problem if messages are sent in unicide, but I'm not sure if it will.
// Need to know if and how this could be a problem.
if (radioConfig.preferences.ext_notification_plugin_alert_bell) {
DEBUG_MSG("externalNotificationPlugin - Notification Bell\n");
for (int i = 0; i < p.payload.size; i++) {
if (p.payload.bytes[i] == ASCII_BELL) {
externalNotificationPlugin->setExternalOn();
}
}
}
if (radioConfig.preferences.ext_notification_plugin_alert_message) {
DEBUG_MSG("externalNotificationPlugin - Notification Plugin\n");
externalNotificationPlugin->setExternalOn();
}
}
} else {
DEBUG_MSG("External Notification Plugin Disabled\n");
}
#endif
return true; // Let others look at this message also if they want
}

View File

@@ -0,0 +1,47 @@
#pragma once
#include "SinglePortPlugin.h"
#include "concurrency/OSThread.h"
#include "configuration.h"
#include <Arduino.h>
#include <functional>
class ExternalNotificationPlugin : private concurrency::OSThread
{
bool firstTime = 1;
public:
ExternalNotificationPlugin();
void setExternalOn();
void setExternalOff();
void getExternal();
protected:
virtual int32_t runOnce();
};
extern ExternalNotificationPlugin *externalNotificationPlugin;
/*
* Radio interface for ExternalNotificationPlugin
*
*/
class ExternalNotificationPluginRadio : public SinglePortPlugin
{
public:
ExternalNotificationPluginRadio() : SinglePortPlugin("ExternalNotificationPluginRadio", PortNum_TEXT_MESSAGE_APP) {}
protected:
//virtual MeshPacket *allocReply();
/** Called to handle a particular incoming message
@return true if you've guaranteed you've handled this message and no other handlers should be considered for it
*/
virtual bool handleReceived(const MeshPacket &mp);
};
extern ExternalNotificationPluginRadio *externalNotificationPluginRadio;

View File

@@ -6,7 +6,7 @@
#include "configuration.h" #include "configuration.h"
#include "main.h" #include "main.h"
NodeInfoPlugin nodeInfoPlugin; NodeInfoPlugin *nodeInfoPlugin;
bool NodeInfoPlugin::handleReceivedProtobuf(const MeshPacket &mp, const User &p) bool NodeInfoPlugin::handleReceivedProtobuf(const MeshPacket &mp, const User &p)
{ {
@@ -28,9 +28,15 @@ bool NodeInfoPlugin::handleReceivedProtobuf(const MeshPacket &mp, const User &p)
void NodeInfoPlugin::sendOurNodeInfo(NodeNum dest, bool wantReplies) void NodeInfoPlugin::sendOurNodeInfo(NodeNum dest, bool wantReplies)
{ {
// cancel any not yet sent (now stale) position packets
if(prevPacketId) // if we wrap around to zero, we'll simply fail to cancel in that rare case (no big deal)
service.cancelSending(prevPacketId);
MeshPacket *p = allocReply(); MeshPacket *p = allocReply();
p->to = dest; p->to = dest;
p->decoded.want_response = wantReplies; p->decoded.want_response = wantReplies;
p->priority = MeshPacket_Priority_BACKGROUND;
prevPacketId = p->id;
service.sendToMesh(p); service.sendToMesh(p);
} }

View File

@@ -6,6 +6,9 @@
*/ */
class NodeInfoPlugin : public ProtobufPlugin<User> class NodeInfoPlugin : public ProtobufPlugin<User>
{ {
/// The id of the last packet we sent, to allow us to cancel it if we make something fresher
PacketId prevPacketId = 0;
public: public:
/** Constructor /** Constructor
* name is for debugging output * name is for debugging output
@@ -29,4 +32,4 @@ class NodeInfoPlugin : public ProtobufPlugin<User>
virtual MeshPacket *allocReply(); virtual MeshPacket *allocReply();
}; };
extern NodeInfoPlugin nodeInfoPlugin; extern NodeInfoPlugin *nodeInfoPlugin;

30
src/plugins/Plugins.cpp Normal file
View File

@@ -0,0 +1,30 @@
#include "plugins/ExternalNotificationPlugin.h"
#include "plugins/NodeInfoPlugin.h"
#include "plugins/PositionPlugin.h"
#include "plugins/RemoteHardwarePlugin.h"
#include "plugins/ReplyPlugin.h"
#include "plugins/SerialPlugin.h"
#include "plugins/TextMessagePlugin.h"
/**
* Create plugin instances here. If you are adding a new plugin, you must 'new' it here (or somewhere else)
*/
void setupPlugins()
{
nodeInfoPlugin = new NodeInfoPlugin();
positionPlugin = new PositionPlugin();
textMessagePlugin = new TextMessagePlugin();
// Note: if the rest of meshtastic doesn't need to explicitly use your plugin, you do not need to assign the instance
// to a global variable.
new RemoteHardwarePlugin();
new ReplyPlugin();
#ifndef NO_ESP32
// Only run on an esp32 based device.
new SerialPlugin(); // Maintained by MC Hamster (Jm Casler) jm@casler.org
new ExternalNotificationPlugin(); // Maintained by MC Hamster (Jm Casler) jm@casler.org
#endif
}

6
src/plugins/Plugins.h Normal file
View File

@@ -0,0 +1,6 @@
#pragma once
/**
* Create plugin instances here. If you are adding a new plugin, you must 'new' it here (or somewhere else)
*/
void setupPlugins();

View File

@@ -5,7 +5,7 @@
#include "Router.h" #include "Router.h"
#include "configuration.h" #include "configuration.h"
PositionPlugin positionPlugin; PositionPlugin *positionPlugin;
bool PositionPlugin::handleReceivedProtobuf(const MeshPacket &mp, const Position &p) bool PositionPlugin::handleReceivedProtobuf(const MeshPacket &mp, const Position &p)
{ {
@@ -29,28 +29,23 @@ bool PositionPlugin::handleReceivedProtobuf(const MeshPacket &mp, const Position
MeshPacket *PositionPlugin::allocReply() MeshPacket *PositionPlugin::allocReply()
{ {
NodeInfo *node = nodeDB.getNode(nodeDB.getNodeNum()); NodeInfo *node = service.refreshMyNodeInfo(); // should guarantee there is now a position
assert(node); assert(node->has_position);
// We might not have a position yet for our local node, in that case, at least try to send the time return allocDataProtobuf(node->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.
return allocDataProtobuf(position);
} }
void PositionPlugin::sendOurPosition(NodeNum dest, bool wantReplies) void PositionPlugin::sendOurPosition(NodeNum dest, bool wantReplies)
{ {
// cancel any not yet sent (now stale) position packets
if(prevPacketId) // if we wrap around to zero, we'll simply fail to cancel in that rare case (no big deal)
service.cancelSending(prevPacketId);
MeshPacket *p = allocReply(); MeshPacket *p = allocReply();
p->to = dest; p->to = dest;
p->decoded.want_response = wantReplies; p->decoded.want_response = wantReplies;
p->priority = MeshPacket_Priority_BACKGROUND;
prevPacketId = p->id;
service.sendToMesh(p); service.sendToMesh(p);
} }

View File

@@ -6,6 +6,9 @@
*/ */
class PositionPlugin : public ProtobufPlugin<Position> class PositionPlugin : public ProtobufPlugin<Position>
{ {
/// The id of the last packet we sent, to allow us to cancel it if we make something fresher
PacketId prevPacketId = 0;
public: public:
/** Constructor /** Constructor
* name is for debugging output * name is for debugging output
@@ -30,4 +33,4 @@ class PositionPlugin : public ProtobufPlugin<Position>
virtual MeshPacket *allocReply(); virtual MeshPacket *allocReply();
}; };
extern PositionPlugin positionPlugin; extern PositionPlugin *positionPlugin;

View File

@@ -6,8 +6,6 @@
#include "configuration.h" #include "configuration.h"
#include "main.h" #include "main.h"
RemoteHardwarePlugin remoteHardwarePlugin;
#define NUM_GPIOS 64 #define NUM_GPIOS 64
// Because (FIXME) we currently don't tell API clients status on sent messages // Because (FIXME) we currently don't tell API clients status on sent messages

View File

@@ -5,9 +5,6 @@
#include <assert.h> #include <assert.h>
// Create an a static instance of our plugin - this registers with the plugin system
ReplyPlugin replyPlugin;
MeshPacket *ReplyPlugin::allocReply() MeshPacket *ReplyPlugin::allocReply()
{ {
assert(currentRequest); // should always be !NULL assert(currentRequest); // should always be !NULL

View File

@@ -0,0 +1,194 @@
#include "SerialPlugin.h"
#include "MeshService.h"
#include "NodeDB.h"
#include "RTC.h"
#include "Router.h"
#include "configuration.h"
#include <Arduino.h>
#include <assert.h>
/*
SerialPlugin
A simple interface to send messages over the mesh network by sending strings
over a serial port.
Default is to use RX GPIO 16 and TX GPIO 17.
Need help with this plugin? Post your question on the Meshtastic Discourse:
https://meshtastic.discourse.group
Basic Usage:
1) Enable the plugin by setting serialplugin_enabled to 1.
2) Set the pins (serialplugin_rxd / serialplugin_rxd) for your preferred RX and TX GPIO pins.
On tbeam, recommend to use:
RXD 35
TXD 15
3) Set serialplugin_timeout to the amount of time to wait before we consider
your packet as "done".
4) (Optional) In SerialPlugin.h set the port to PortNum_TEXT_MESSAGE_APP if you want to
send messages to/from the general text message channel.
5) Connect to your device over the serial interface at 38400 8N1.
6) Send a packet up to 240 bytes in length. This will get relayed over the mesh network.
7) (Optional) Set serialplugin_echo to 1 and any message you send out will be echoed back
to your device.
TODO (in this order):
* Define a verbose RX mode to report on mesh and packet infomration.
- This won't happen any time soon.
KNOWN PROBLEMS
* Until the plugin is initilized by the startup sequence, the TX pin is in a floating
state. Device connected to that pin may see this as "noise".
* Will not work on NRF and the Linux device targets.
*/
#define RXD2 16
#define TXD2 17
#define SERIALPLUGIN_RX_BUFFER 128
#define SERIALPLUGIN_STRING_MAX Constants_DATA_PAYLOAD_LEN
#define SERIALPLUGIN_TIMEOUT 250
#define SERIALPLUGIN_BAUD 38400
#define SERIALPLUGIN_ACK 1
SerialPlugin *serialPlugin;
SerialPluginRadio *serialPluginRadio;
SerialPlugin::SerialPlugin() : concurrency::OSThread("SerialPlugin") {}
char serialStringChar[Constants_DATA_PAYLOAD_LEN];
int32_t SerialPlugin::runOnce()
{
#ifndef NO_ESP32
/*
Uncomment the preferences below if you want to use the plugin
without having to configure it from the PythonAPI or WebUI.
*/
// radioConfig.preferences.serialplugin_enabled = 1;
// radioConfig.preferences.serialplugin_rxd = 35;
// radioConfig.preferences.serialplugin_txd = 15;
// radioConfig.preferences.serialplugin_timeout = 1000;
// radioConfig.preferences.serialplugin_echo = 1;
if (radioConfig.preferences.serialplugin_enabled) {
if (firstTime) {
// Interface with the serial peripheral from in here.
DEBUG_MSG("Initializing serial peripheral interface\n");
if (radioConfig.preferences.serialplugin_rxd && radioConfig.preferences.serialplugin_txd) {
Serial2.begin(SERIALPLUGIN_BAUD, SERIAL_8N1, radioConfig.preferences.serialplugin_rxd,
radioConfig.preferences.serialplugin_txd);
} else {
Serial2.begin(SERIALPLUGIN_BAUD, SERIAL_8N1, RXD2, TXD2);
}
if (radioConfig.preferences.serialplugin_timeout) {
Serial2.setTimeout(
radioConfig.preferences.serialplugin_timeout); // Number of MS to wait to set the timeout for the string.
} else {
Serial2.setTimeout(SERIALPLUGIN_TIMEOUT); // Number of MS to wait to set the timeout for the string.
}
Serial2.setRxBufferSize(SERIALPLUGIN_RX_BUFFER);
serialPluginRadio = new SerialPluginRadio();
firstTime = 0;
} else {
String serialString;
while (Serial2.available()) {
serialString = Serial2.readString();
serialString.toCharArray(serialStringChar, Constants_DATA_PAYLOAD_LEN);
serialPluginRadio->sendPayload();
DEBUG_MSG("Received: %s\n", serialStringChar);
}
}
return (10);
} else {
DEBUG_MSG("Serial Plugin Disabled\n");
return (INT32_MAX);
}
#endif
}
MeshPacket *SerialPluginRadio::allocReply()
{
auto reply = allocDataPacket(); // Allocate a packet for sending
return reply;
}
void SerialPluginRadio::sendPayload(NodeNum dest, bool wantReplies)
{
MeshPacket *p = allocReply();
p->to = dest;
p->decoded.want_response = wantReplies;
p->want_ack = SERIALPLUGIN_ACK;
p->decoded.data.payload.size = strlen(serialStringChar); // You must specify how many bytes are in the reply
memcpy(p->decoded.data.payload.bytes, serialStringChar, p->decoded.data.payload.size);
service.sendToMesh(p);
}
bool SerialPluginRadio::handleReceived(const MeshPacket &mp)
{
#ifndef NO_ESP32
if (radioConfig.preferences.serialplugin_enabled) {
auto &p = mp.decoded.data;
// DEBUG_MSG("Received text msg self=0x%0x, from=0x%0x, to=0x%0x, id=%d, msg=%.*s\n",
// nodeDB.getNodeNum(), mp.from, mp.to, mp.id, p.payload.size, p.payload.bytes);
if (mp.from == nodeDB.getNodeNum()) {
/*
* If radioConfig.preferences.serialplugin_echo is true, then echo the packets that are sent out back to the TX
* of the serial interface.
*/
if (radioConfig.preferences.serialplugin_echo) {
// For some reason, we get the packet back twice when we send out of the radio.
// TODO: need to find out why.
if (lastRxID != mp.id) {
lastRxID = mp.id;
// DEBUG_MSG("* * Message came this device\n");
// Serial2.println("* * Message came this device");
Serial2.printf("%s", p.payload.bytes);
}
}
} else {
// DEBUG_MSG("* * Message came from the mesh\n");
// Serial2.println("* * Message came from the mesh");
Serial2.printf("%s", p.payload.bytes);
}
} else {
DEBUG_MSG("Serial Plugin Disabled\n");
}
#endif
return true; // Let others look at this message also if they want
}

View File

@@ -0,0 +1,54 @@
#pragma once
#include "SinglePortPlugin.h"
#include "concurrency/OSThread.h"
#include "configuration.h"
#include <Arduino.h>
#include <functional>
class SerialPlugin : private concurrency::OSThread
{
bool firstTime = 1;
public:
SerialPlugin();
protected:
virtual int32_t runOnce();
};
extern SerialPlugin *serialPlugin;
/*
* Radio interface for SerialPlugin
*
*/
class SerialPluginRadio : public SinglePortPlugin
{
uint32_t lastRxID;
public:
/*
TODO: Switch this to PortNum_SERIAL_APP once the change is able to be merged back here
from the main code.
*/
// SerialPluginRadio() : SinglePortPlugin("SerialPluginRadio", PortNum_TEXT_MESSAGE_APP) {}
SerialPluginRadio() : SinglePortPlugin("SerialPluginRadio", PortNum_SERIAL_APP) {}
/**
* Send our payload into the mesh
*/
void sendPayload(NodeNum dest = NODENUM_BROADCAST, bool wantReplies = false);
protected:
virtual MeshPacket *allocReply();
/** Called to handle a particular incoming message
@return true if you've guaranteed you've handled this message and no other handlers should be considered for it
*/
virtual bool handleReceived(const MeshPacket &mp);
};
extern SerialPluginRadio *serialPluginRadio;

View File

@@ -3,7 +3,7 @@
#include "NodeDB.h" #include "NodeDB.h"
#include "PowerFSM.h" #include "PowerFSM.h"
TextMessagePlugin textMessagePlugin; TextMessagePlugin *textMessagePlugin;
bool TextMessagePlugin::handleReceived(const MeshPacket &mp) bool TextMessagePlugin::handleReceived(const MeshPacket &mp)
{ {

View File

@@ -22,4 +22,4 @@ class TextMessagePlugin : public SinglePortPlugin, public Observable<const MeshP
virtual bool handleReceived(const MeshPacket &mp); virtual bool handleReceived(const MeshPacket &mp);
}; };
extern TextMessagePlugin textMessagePlugin; extern TextMessagePlugin *textMessagePlugin;

View File

@@ -16,8 +16,8 @@
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/ */
#ifndef _VARIANT_TTGO_EINK_V1_ #ifndef _VARIANT_TTGO_EINK_V1_0_
#define _VARIANT_TTGO_EINK_V1_ #define _VARIANT_TTGO_EINK_V1_0_
/** Master clock frequency */ /** Master clock frequency */
#define VARIANT_MCK (64000000ul) #define VARIANT_MCK (64000000ul)
@@ -99,9 +99,9 @@ extern "C" {
#define NUM_ANALOG_OUTPUTS (0) #define NUM_ANALOG_OUTPUTS (0)
// LEDs // LEDs
#define PIN_LED1 (0 + 13) // green (but red on my prototype) #define PIN_LED1 (0 + 13) // red (confirmed on 1.0 board)
#define PIN_LED2 (0 + 15) // blue (but red on my prototype) #define PIN_LED2 (0 + 14) // blue (seems busted!)
#define PIN_LED3 (0 + 14) // red (not functional on my prototype) #define PIN_LED3 (0 + 15) // green (seems busted!)
#define LED_RED PIN_LED3 #define LED_RED PIN_LED3
#define LED_GREEN PIN_LED1 #define LED_GREEN PIN_LED1
@@ -149,7 +149,7 @@ No longer populated on PCB
*/ */
#define WIRE_INTERFACES_COUNT 1 #define WIRE_INTERFACES_COUNT 1
#define PIN_WIRE_SDA (26) // Not connected on board? #define PIN_WIRE_SDA (26)
#define PIN_WIRE_SCL (27) #define PIN_WIRE_SCL (27)
/* touch sensor, active high */ /* touch sensor, active high */
@@ -167,8 +167,8 @@ External serial flash WP25R1635FZUIL0
#define PIN_QSPI_CS (32 + 15) #define PIN_QSPI_CS (32 + 15)
#define PIN_QSPI_IO0 (32 + 12) // MOSI if using two bit interface #define PIN_QSPI_IO0 (32 + 12) // MOSI if using two bit interface
#define PIN_QSPI_IO1 (32 + 13) // MISO if using two bit interface #define PIN_QSPI_IO1 (32 + 13) // MISO if using two bit interface
//#define PIN_QSPI_IO2 22 // WP if using two bit interface (i.e. not used) #define PIN_QSPI_IO2 (0 + 7) // WP if using two bit interface (i.e. not used)
//#define PIN_QSPI_IO3 23 // HOLD if using two bit interface (i.e. not used) #define PIN_QSPI_IO3 (0 + 5) // HOLD if using two bit interface (i.e. not used)
// On-board QSPI Flash // On-board QSPI Flash
#define EXTERNAL_FLASH_DEVICES MX25R1635F #define EXTERNAL_FLASH_DEVICES MX25R1635F
@@ -196,7 +196,7 @@ External serial flash WP25R1635FZUIL0
* eink display pins * eink display pins
*/ */
#define PIN_EINK_EN (32 + 11) #define PIN_EINK_EN (32 + 11) // Note: this is really just backlight power
#define PIN_EINK_CS (0 + 30) #define PIN_EINK_CS (0 + 30)
#define PIN_EINK_BUSY (0 + 3) #define PIN_EINK_BUSY (0 + 3)
#define PIN_EINK_DC (0 + 28) #define PIN_EINK_DC (0 + 28)
@@ -223,7 +223,8 @@ External serial flash WP25R1635FZUIL0
*/ */
#define PIN_GPS_WAKE (32 + 2) // An output to wake GPS, low means allow sleep, high means force wake #define PIN_GPS_WAKE (32 + 2) // An output to wake GPS, low means allow sleep, high means force wake
#define PIN_GPS_PPS (32 + 4) // Pulse per second input from the GPS // Seems to be missing on this new board
// #define PIN_GPS_PPS (32 + 4) // Pulse per second input from the GPS
#define PIN_GPS_TX (32 + 9) // This is for bits going TOWARDS the CPU #define PIN_GPS_TX (32 + 9) // This is for bits going TOWARDS the CPU
#define PIN_GPS_RX (32 + 8) // This is for bits going TOWARDS the GPS #define PIN_GPS_RX (32 + 8) // This is for bits going TOWARDS the GPS
@@ -242,6 +243,8 @@ External serial flash WP25R1635FZUIL0
#define PIN_SPI_MOSI (0 + 22) #define PIN_SPI_MOSI (0 + 22)
#define PIN_SPI_SCK (0 + 19) #define PIN_SPI_SCK (0 + 19)
#define PIN_PWR_EN (0 + 6)
// To debug via the segger JLINK console rather than the CDC-ACM serial device // To debug via the segger JLINK console rather than the CDC-ACM serial device
// #define USE_SEGGER // #define USE_SEGGER

View File

@@ -0,0 +1,44 @@
/*
Copyright (c) 2014-2015 Arduino LLC. All right reserved.
Copyright (c) 2016 Sandeep Mistry All right reserved.
Copyright (c) 2018, Adafruit Industries (adafruit.com)
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library 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 Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "variant.h"
#include "nrf.h"
#include "wiring_constants.h"
#include "wiring_digital.h"
const uint32_t g_ADigitalPinMap[] = {
// P0 - pins 0 and 1 are hardwired for xtal and should never be enabled
0xff, 0xff, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
// P1
32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47};
void initVariant()
{
// LED1 & LED2
pinMode(PIN_LED1, OUTPUT);
ledOff(PIN_LED1);
pinMode(PIN_LED2, OUTPUT);
ledOff(PIN_LED2);
pinMode(PIN_LED3, OUTPUT);
ledOff(PIN_LED3);
}

256
variants/eink0.1/variant.h Normal file
View File

@@ -0,0 +1,256 @@
/*
Copyright (c) 2014-2015 Arduino LLC. All right reserved.
Copyright (c) 2016 Sandeep Mistry All right reserved.
Copyright (c) 2018, Adafruit Industries (adafruit.com)
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library 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 Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef _VARIANT_TTGO_EINK_V0_1_
#define _VARIANT_TTGO_EINK_V0_1_
/** Master clock frequency */
#define VARIANT_MCK (64000000ul)
#define USE_LFXO // Board uses 32khz crystal for LF
/*
@geeksville eink TODO:
soonish:
DONE hook cdc acm device to debug output
DONE fix bootloader to use two buttons - remove bootloader hacks
DONE get second button working in app load
DONE use tp_ser_io as a button, it goes high when pressed unify eink display classes
fix display width and height
clean up eink drawing to not have the nasty timeout hack
measure current draws
DONE put eink to sleep when we think the screen is off
enable gps sleep mode
turn off txco on lora?
make screen.adjustBrightness() a nop on eink screens
later:
enable flash on qspi.
fix floating point SEGGER printf on nrf52 - see "new NMEA GPS pos"
add factory/power on self test
feedback to give:
* bootloader is finished
* the capacitive touch sensor works, though I'm not sure what use you are intending for it
* remove ipx connector for nfc, instead use two caps and loop traces on the back of the board as an antenna?
* the i2c RTC seems to talk fine on the i2c bus. However, I'm not sure of the utility of that part. Instead I'd be in favor of
the following:
* move BAT1 to power the GPS VBACKUP instead per page 6 of the Air530 datasheet. And remove the i2c RTC entirely.
* remove the cp2014 chip.
* I've made the serial flash chip work, but if you do a new spin of the board I recommend:
connect pin 3 and pin 7 of U4 to spare GPIOs on the processor (instead of their current connections),
This would allow using 4 bit wide interface mode to the serial flash - doubling the transfer speed! see example here:
https://infocenter.nordicsemi.com/topic/ug_nrf52840_dk/UG/nrf52840_DK/hw_external_memory.html?cp=4_0_4_7_4
Once again - I'm glad you added that external flash chip.
* Power measurements
When powered by 4V battery
CPU on, lora radio RX mode, bluetooth enabled, GPS trying to lock. total draw 43mA
CPU on, lora radio RX mode, bluetooth enabled, GPS super low power sleep mode. Total draw 20mA
CPU on, lora radio TX mode, bluetooth enabled, GPS super low power sleep mode. Total draw 132mA
Note: power consumption while connected via BLE to a phone almost identical.
Note: eink display for all tests was in sleep mode most of the time. Current draw during the brief periods while the eink was being drawn was not
measured (but it was low).
Note: Turning off EINK PWR_ON produces no noticeable power savings over just putting the eink display into sleep mode.
*/
/*----------------------------------------------------------------------------
* Headers
*----------------------------------------------------------------------------*/
#include "WVariant.h"
#ifdef __cplusplus
extern "C" {
#endif // __cplusplus
// Number of pins defined in PinDescription array
#define PINS_COUNT (48)
#define NUM_DIGITAL_PINS (48)
#define NUM_ANALOG_INPUTS (1)
#define NUM_ANALOG_OUTPUTS (0)
// LEDs
#define PIN_LED1 (0 + 13) // green (but red on my prototype)
#define PIN_LED2 (0 + 15) // blue (but red on my prototype)
#define PIN_LED3 (0 + 14) // red (not functional on my prototype)
#define LED_RED PIN_LED3
#define LED_GREEN PIN_LED1
#define LED_BLUE PIN_LED2
#define LED_BUILTIN LED_GREEN
#define LED_CONN PIN_BLUE
#define LED_STATE_ON 0 // State when LED is lit
#define LED_INVERTED 1
/*
* Buttons
*/
#define PIN_BUTTON1 (32 + 10)
#define PIN_BUTTON2 (0 + 18) // 0.18 is labeled on the board as RESET but we configure it in the bootloader as a regular GPIO
/*
* Analog pins
*/
#define PIN_A0 (4) // Battery ADC
#define BATTERY_PIN PIN_A0
static const uint8_t A0 = PIN_A0;
#define ADC_RESOLUTION 14
#define PIN_NFC1 (9)
#define PIN_NFC2 (10)
/*
* Serial interfaces
*/
/*
No longer populated on PCB
*/
//#define PIN_SERIAL2_RX (0 + 6)
//#define PIN_SERIAL2_TX (0 + 8)
// #define PIN_SERIAL2_EN (0 + 17)
/**
Wire Interfaces
*/
#define WIRE_INTERFACES_COUNT 1
#define PIN_WIRE_SDA (26) // Not connected on board?
#define PIN_WIRE_SCL (27)
/* touch sensor, active high */
#define TP_SER_IO (0 + 11)
#define PIN_RTC_INT (0 + 16) // Interrupt from the PCF8563 RTC
/*
External serial flash WP25R1635FZUIL0
*/
// QSPI Pins
#define PIN_QSPI_SCK (32 + 14)
#define PIN_QSPI_CS (32 + 15)
#define PIN_QSPI_IO0 (32 + 12) // MOSI if using two bit interface
#define PIN_QSPI_IO1 (32 + 13) // MISO if using two bit interface
//#define PIN_QSPI_IO2 22 // WP if using two bit interface (i.e. not used)
//#define PIN_QSPI_IO3 23 // HOLD if using two bit interface (i.e. not used)
// On-board QSPI Flash
#define EXTERNAL_FLASH_DEVICES MX25R1635F
#define EXTERNAL_FLASH_USE_QSPI
/*
* Lora radio
*/
#define SX1262_CS (0 + 24) // FIXME - we really should define LORA_CS instead
#define SX1262_DIO1 (0 + 20)
// Note DIO2 is attached internally to the module to an analog switch for TX/RX switching
#define SX1262_DIO3 \
(0 + 21) // This is used as an *output* from the sx1262 and connected internally to power the tcxo, do not drive from the main
// CPU?
#define SX1262_BUSY (0 + 17)
#define SX1262_RESET (0 + 25)
#define SX1262_E22 // Not really an E22 but TTGO seems to be trying to clone that
// Internally the TTGO module hooks the SX1262-DIO2 in to control the TX/RX switch (which is the default for the sx1262interface
// code)
// #define LORA_DISABLE_SENDING // Define this to disable transmission for testing (power testing etc...)
/*
* eink display pins
*/
#define PIN_EINK_EN (32 + 11)
#define PIN_EINK_CS (0 + 30)
#define PIN_EINK_BUSY (0 + 3)
#define PIN_EINK_DC (0 + 28)
#define PIN_EINK_RES (0 + 2)
#define PIN_EINK_SCLK (0 + 31)
#define PIN_EINK_MOSI (0 + 29) // also called SDI
// Controls power for the eink display - Board power is enabled either by VBUS from USB or the CPU asserting PWR_ON
// FIXME - I think this is actually just the board power enable - it enables power to the CPU also
#define PIN_EINK_PWR_ON (0 + 12)
#define HAS_EINK
// No screen wipes on eink
#define SCREEN_TRANSITION_MSECS 0
#define PIN_SPI1_MISO \
(32 + 7) // FIXME not really needed, but for now the SPI code requires something to be defined, pick an used GPIO
#define PIN_SPI1_MOSI PIN_EINK_MOSI
#define PIN_SPI1_SCK PIN_EINK_SCLK
/*
* Air530 GPS pins
*/
#define PIN_GPS_WAKE (32 + 2) // An output to wake GPS, low means allow sleep, high means force wake
#define PIN_GPS_PPS (32 + 4) // Pulse per second input from the GPS
#define PIN_GPS_TX (32 + 9) // This is for bits going TOWARDS the CPU
#define PIN_GPS_RX (32 + 8) // This is for bits going TOWARDS the GPS
#define HAS_AIR530_GPS
#define PIN_SERIAL1_RX PIN_GPS_TX
#define PIN_SERIAL1_TX PIN_GPS_RX
/*
* SPI Interfaces
*/
#define SPI_INTERFACES_COUNT 2
// For LORA, spi 0
#define PIN_SPI_MISO (0 + 23)
#define PIN_SPI_MOSI (0 + 22)
#define PIN_SPI_SCK (0 + 19)
// To debug via the segger JLINK console rather than the CDC-ACM serial device
// #define USE_SEGGER
#ifdef __cplusplus
}
#endif
/*----------------------------------------------------------------------------
* Arduino objects - C++ only
*----------------------------------------------------------------------------*/
#endif

View File

@@ -1,4 +1,4 @@
[VERSION] [VERSION]
major = 1 major = 1
minor = 1 minor = 1
build = 32 build = 46