Compare commits

..

267 Commits

Author SHA1 Message Date
Kevin Hester
16f897d27c Merge pull request #547 from geeksville/udp
1.1.20
2020-12-10 13:32:59 +08:00
Kevin Hester
4cbf0a0730 oops - forgot to check in the new version file, broke CI 2020-12-10 13:19:00 +08:00
Kevin Hester
99c8df8e7d try to fix CI again 2020-12-10 13:17:43 +08:00
Kevin Hester
8e2e4f7e6a Merge remote-tracking branch 'root/master' into udp 2020-12-10 12:46:51 +08:00
Kevin Hester
45d72bd51b fix CI build - make projectdir relative 2020-12-10 12:44:35 +08:00
Jm Casler
781ed3eafd Merge pull request #544 from IZ1IVA/patch-1
Update radio-settings.md
2020-12-09 20:39:18 -08:00
Jm Casler
c66a0a37d8 Merge branch 'master' into patch-1 2020-12-09 20:25:17 -08:00
Jm Casler
92f2007207 Merge pull request #545 from IZ1IVA/patch-2
Update radio-settings.md
2020-12-09 20:24:32 -08:00
Kevin Hester
1f7b537d2d Merge remote-tracking branch 'root/master' into udp 2020-12-10 11:46:03 +08:00
Kevin Hester
cabeacfa94 1.1.21 2020-12-10 11:45:22 +08:00
Kevin Hester
df8b3ebbc7 always pull min_app_version from appload 2020-12-10 11:32:51 +08:00
Kevin Hester
b1c30f0650 Use a custom platformio py file to always set build version 2020-12-10 11:28:15 +08:00
IZ1IVA
194028f9fc Update radio-settings.md
- added ERP reference to LoRaWAN for NA
2020-12-09 11:16:31 +01:00
IZ1IVA
f49c8f4c43 Update radio-settings.md
- corrected dBM into dBm
- specified ERP reference
- added link to ERP in-depth article
2020-12-09 11:04:07 +01:00
Kevin Hester
b3b4c2c1c3 Merge pull request #543 from geeksville/udp
big set of changes from my weeks away
2020-12-08 22:46:26 -08:00
Kevin Hester
a0076eb394 better position debug output 2020-12-09 13:42:36 +08:00
Kevin Hester
a6a4fec4b9 Merge remote-tracking branch 'root/master' into udp 2020-12-09 12:15:32 +08:00
Kevin Hester
2d4657c8d4 Merge pull request #542 from IZ1IVA/patch-1
Update README.md
2020-12-08 20:14:43 -08:00
Kevin Hester
32b8e4f20a fix #536 allow fixed positions
meshtastic --setlat 32.7767 --setlon -96.7970 --setalt 1337
2020-12-09 12:05:15 +08:00
Kevin Hester
3753fef298 add debug_log_enabled 2020-12-09 11:56:41 +08:00
Kevin Hester
a4bb1937c1 Merge remote-tracking branch 'root/master' into udp 2020-12-09 11:21:59 +08:00
Kevin Hester
4bd22dd5db ignore our own msgs for gpio ctrl 2020-12-08 08:16:58 +08:00
Kevin Hester
79a24c200e use autogened protobuf init code 2020-12-07 10:27:31 +08:00
Kevin Hester
90060e84c0 WIP on GPIO example 2020-12-07 10:18:11 +08:00
Kevin Hester
8f5a1f19d3 add remote gpio control as an example plugin
https://github.com/meshtastic/Meshtastic-device/issues/182
2020-12-06 18:33:42 +08:00
Kevin Hester
3e0dc44210 move want_replies into new plugin system 2020-12-05 11:15:06 +08:00
Kevin Hester
91b99bd584 require apps to be 1.1.20 or later 2020-12-05 10:27:04 +08:00
Kevin Hester
b6e21bcbcd add SinglePortPlugin to simpilify api 2020-12-05 10:14:15 +08:00
Kevin Hester
ae7d3ee5ed move nodeinfo messages into new plugin system 2020-12-05 10:00:46 +08:00
Kevin Hester
f1179bd3ea positions now sent using the new API 2020-12-05 08:46:19 +08:00
Kevin Hester
9b24cc6dd6 update protobufs 2020-12-04 18:54:00 +08:00
IZ1IVA
20c5b98b2d Update README.md
Added "Related Groups" section
Added Meshtastic Italian Telegram group link
2020-12-03 12:27:44 +01:00
Kevin Hester
d3cb9bdd4a WIP moving positions to new system 2020-12-03 16:48:44 +08:00
Kevin Hester
7737123d0f begin moving position stuff into plugin 2020-11-28 18:10:31 +08:00
Kevin Hester
5138aff4b2 fix static initializer bug with mesh plugins 2020-11-28 13:25:03 +08:00
Kevin Hester
0b0d293a66 Move text message handling into the new plugin system 2020-11-28 12:10:19 +08:00
Kevin Hester
ddab4a0235 remove support for 8bit nodenums 2020-11-28 09:56:21 +08:00
Jm Casler
50615540ce Merge pull request #538 from meshtastic/dev-https
Merging in dev-https to master
2020-11-27 17:21:56 -08:00
Kevin Hester
f5e42b2533 update protos 2020-11-28 09:17:20 +08:00
Kevin Hester
9e9913101f Merge pull request #527 from geeksville/spiffs-bug496
Spiffs bug496
2020-11-27 17:11:34 -08:00
Kevin Hester
b1289b632a Merge remote-tracking branch 'root/master' into dev 2020-11-28 09:00:21 +08:00
Kevin Hester
c427c8abf9 oops - fix my crummy merge 2020-11-28 08:55:56 +08:00
Jm Casler
9170dc7738 Merge pull request #534 from mc-hamster/master
Fix for #533 - We will deinit wifi even if it were never initialized.
2020-11-27 16:19:03 -08:00
Kevin Hester
cc36e3a9a6 Merge branch 'dev-https' into spiffs-bug496 2020-11-27 16:15:14 -08:00
Jm Casler
7d4c77abfd Fix for #533 - deinitWifi() will de initialize the radio even if it was never initialized 2020-11-27 14:49:44 -08:00
Jm Casler
1ba91ec27f Merge pull request #26 from meshtastic/master
update my repo from master
2020-11-25 21:36:56 -08:00
Jm Casler
575c5b2193 Merge pull request #532 from meshtastic/mc-hamster-patch-2
wait_bluetooth_secs to 60 seconds.
2020-11-22 19:38:39 -08:00
Jm Casler
3473a1e323 wait_bluetooth_secs to 60 seconds. 2020-11-22 19:32:48 -08:00
Jm Casler
11a00e2977 Merge pull request #531 from mc-hamster/master
Add support for the power button #528
2020-11-22 19:19:35 -08:00
Jm Casler
817c99e09c trying to fix it again 2020-11-22 19:12:11 -08:00
Jm Casler
9801a62d2d Fix for CI on other boards. 2020-11-22 19:01:48 -08:00
Jm Casler
2bd40b7053 Add support for the power button #528 2020-11-22 18:50:14 -08:00
Jm Casler
dccc15946b Merge pull request #530 from mc-hamster/master
Removing stuff I accidently checked in for the duplexer
2020-11-22 16:50:45 -08:00
Jm Casler
3ab9d2a50e Removing stuff I accidently checked in for the duplexer 2020-11-22 16:24:22 -08:00
Jm Casler
776a978ea0 Merge pull request #529 from mc-hamster/master
Accidentally checked in my rf deduplixer
2020-11-22 15:44:19 -08:00
Jm Casler
f60922af34 Accidently checked in my rf deduplexer 2020-11-22 15:42:36 -08:00
Kevin Hester
cb34fd5eb9 Merge remote-tracking branch 'root/master' into dev 2020-11-20 08:59:54 +08:00
Kevin Hester
19d81347f2 1.1.9 (release for @mc-hamster only in android app alpha) 2020-11-20 07:42:02 +08:00
Kevin Hester
d7d13d637c clean up filesystem goo, add spiffs to install scripts, fix #496
@mc-hamster seems to work pretty good for me, so I'll send a PR to you
for the dev-http branch.

I'll push out an android alpha build later today (once the build is
complete).  Once this new device load is out in the field _future_
device builds will support updating spiffs from android. (i.e. device
loads older than 1.1.9 must be updated to 1.1.9 or later before spiffs
support is implemented on the device side - so some users might need to
update twice before the new spiffs contents will appear on their device)
2020-11-19 09:25:02 +08:00
Kevin Hester
df75cefeeb fix #525 - turn off (buggy) TinyGPS custom candidates support 2020-11-18 10:54:25 +08:00
Jm Casler
66f9dbec45 Merge pull request #524 from meshtastic/mc-hamster-patch-1
Lora Design Guide
2020-11-17 13:16:49 -08:00
Jm Casler
4679dd7c4d Lora Design Guide
Airtime calculation is in section 4.
2020-11-17 11:14:57 -08:00
Jm Casler
a02979d564 Merge branch 'externalAmp' into master 2020-11-16 19:54:06 -08:00
Kevin Hester
f2698bbf91 bug #496 - allow update clients to program SPIFFS region
also have build system properly build spiffs release builds
2020-11-16 08:56:32 +08:00
Kevin Hester
d045139945 return ble errors for invalid values 2020-11-16 07:57:32 +08:00
Kevin Hester
2c9c5991a0 add a script for testing release builds 2020-11-15 08:24:34 +08:00
Jm Casler
1b365fa0aa Merge pull request #24 from meshtastic/dev-https
Dev https
2020-11-13 21:40:00 -08:00
Jm Casler
71d1d4d8fa Merge pull request #521 from meshtastic/master
Update dev-https from master
2020-11-13 21:35:20 -08:00
Jm Casler
64df994a32 Merge pull request #520 from crossan007/dev-https
Only issue ContentType header once; correctly.
2020-11-13 20:13:31 -08:00
Charles Crossan
49a19e26d5 Only issue ContentType header once; correctly. 2020-11-13 22:07:52 -05:00
Kevin Hester
ef37f955c3 Merge remote-tracking branch 'root/master' into dev 2020-11-14 10:22:10 +08:00
Kevin Hester
ac50b9544b Merge pull request #519 from geeksville/bug513
Bug513
2020-11-14 10:21:44 +08:00
Kevin Hester
ccc1600bc9 remove stale fixme 2020-11-14 10:19:55 +08:00
Kevin Hester
7c220f8a39 Merge branch 'master' into bug513 2020-11-14 10:14:22 +08:00
Kevin Hester
1839f8f7ca fix #513 scale retransmission times based on true packet time on wire 2020-11-14 10:07:25 +08:00
Kevin Hester
48c461c50c Merge pull request #515 from geeksville/eink
nrf52 deep sleep support
2020-11-13 11:08:17 +08:00
Kevin Hester
f346b4f0f2 Merge branch 'eink' into bug513 2020-11-13 09:41:23 +08:00
Kevin Hester
5aab4f5c95 Merge branch 'master' into eink 2020-11-13 09:36:18 +08:00
Kevin Hester
d407db5ee1 add more deep sleep support for nrf52 boards 2020-11-13 09:33:59 +08:00
Kevin Hester
93afc71e2e Show "sleeping" on eink while in deep sleep 2020-11-13 07:49:01 +08:00
Kevin Hester
67e657f10f always print debug output to both serial and segger debug port
cc @mc-hamster, this  makes USE_SEGGER optional on nrf52 targets
2020-11-13 07:48:25 +08:00
Kevin Hester
619a48085a pinetab docs 2020-11-12 18:31:35 +08:00
Kevin Hester
68937d52fe bug #503 wip 2020-11-12 17:49:04 +08:00
Jm Casler
e33657eb75 Toggle GPIO2 to HIGH when transmitting a packet out the LORA radio. 2020-11-09 19:47:31 -08:00
Kevin Hester
21751da5a2 Merge pull request #512 from geeksville/dev
Dev
2020-11-07 04:19:36 -08:00
Kevin Hester
c2e8ac7173 1.1.8 2020-11-07 19:20:38 +08:00
Jm Casler
825001f313 Merge pull request #23 from mc-hamster/master
update
2020-11-06 23:12:50 -08:00
Jm Casler
576526576a Merge pull request #511 from mc-hamster/master
Web text editor #510
2020-11-06 23:11:09 -08:00
Kevin Hester
2fd5ce00ce fix build for esp32 targets 2020-11-07 15:09:05 +08:00
Jm Casler
4204c494ae fix poorly merged file 2020-11-06 22:41:26 -08:00
Jm Casler
84beae1001 Merge branch 'dev-https' into master 2020-11-06 22:29:03 -08:00
Jm Casler
951b4293c4 Set web cookie and web text editor. 2020-11-06 22:21:20 -08:00
Kevin Hester
952c216bf7 Merge remote-tracking branch 'root/master' into dev 2020-11-07 14:08:22 +08:00
Kevin Hester
ff4b03b8c1 Fix #509 was causing failure on non GPS equipped boards 2020-11-07 09:15:28 +08:00
Kevin Hester
c5903a790b update for portduino 2020-10-31 15:50:39 +08:00
Kevin Hester
bbc36f7b6f switch to my new SD 7.2.0 S113 based bootloader 2020-10-31 12:22:20 +08:00
Jm Casler
2f9ef463d8 Merge pull request #507 from meshtastic/master
update dev-https from master
2020-10-30 21:04:16 -07:00
Jm Casler
bea00569fd Merge pull request #22 from meshtastic/master
update my fork from master
2020-10-30 21:03:40 -07:00
Kevin Hester
d7368d5a51 begin deep sleep support for nrf52 2020-10-30 17:05:32 +08:00
Kevin Hester
47bbde3c60 fix #505 allow forced standby mode to not assert fail 2020-10-29 16:13:44 +08:00
Kevin Hester
04942a3570 fix #505 - device can reboot due to race condition in sending 2020-10-29 15:27:05 +08:00
Kevin Hester
62a8c968e8 Merge pull request #504 from geeksville/dev
Dev
2020-10-28 23:14:52 -07:00
Kevin Hester
b9a1cae72d 1.1.7 2020-10-29 13:27:52 +08:00
Kevin Hester
6b442784f3 Merge remote-tracking branch 'root/master' into dev 2020-10-29 13:26:49 +08:00
Kevin Hester
cfcb62bd18 Make region changes take effect immedately 2020-10-29 13:26:36 +08:00
Kevin Hester
f698883c02 add pinetab docs 2020-10-29 13:26:14 +08:00
Kevin Hester
4a5cef886e Merge pull request #450 from BeardyWalrus/master
Fix for #357 - Bluetooth pairing pin disable on devices with no display
2020-10-28 18:44:58 -07:00
Kevin Hester
f6ec129288 Merge branch 'master' into master 2020-10-28 18:38:27 -07:00
Kevin Hester
17763034a0 Merge pull request #503 from geeksville/ppr1
Ppr1
2020-10-27 22:26:18 -07:00
Kevin Hester
4ad562b9f4 The new 7.2.0 soft device works on nrf52833 2020-10-28 13:01:50 +08:00
Kevin Hester
6b838002d4 switch back to tbeam for merge to master 2020-10-28 09:51:30 +08:00
Kevin Hester
5e0d53a1e3 ppr1 todo updates 2020-10-28 09:48:28 +08:00
Jm Casler
b1dae3608e Merge pull request #502 from mc-hamster/master
Update to basic.js and add mime types.
2020-10-26 18:18:16 -07:00
Jm Casler
44aafd5b9c Update to basic.js and add mime types. 2020-10-26 18:17:33 -07:00
Kevin Hester
7597d5b3fd PPR1 - GPS serial now works 2020-10-25 17:07:54 +08:00
Kevin Hester
1a8891c33d ppr1: add crude version of charge controller driver 2020-10-24 18:40:47 +08:00
Kevin Hester
f0eeaf01d4 ppr1 lcd is 'good enough' for now 2020-10-24 09:49:14 +08:00
Kevin Hester
d4e95e95a6 fix long-press handling on alternate button 2020-10-24 08:44:54 +08:00
Kevin Hester
0767c8be03 PPR1 fix screen mirroring on LCD 2020-10-24 08:16:15 +08:00
Kevin Hester
18bbf3523e remove unused display code 2020-10-23 22:18:22 +08:00
Kevin Hester
b081a6da56 ppr1 st7567 lcd kinda works now 2020-10-23 18:00:43 +08:00
Kevin Hester
a102e49fdb PPR1 WIP - hacky code to make LCD talk on I2C. Works! 2020-10-23 17:10:48 +08:00
Kevin Hester
c078c08c3e Merge branch 'dev' into ppr1 2020-10-23 16:53:38 +08:00
Jm Casler
91756d1fec Merge pull request #501 from mc-hamster/master
Fixed typo. Updated js library. Update root file handler.
2020-10-22 18:46:26 -07:00
Jm Casler
5981831bc0 Fixed typo. Updated js library. Update root file handler.
Typo fixed. I placed the latest javascript library files into the static folder. Updated the root file handler to be able to serve both compressed and uncompressed files.
2020-10-22 18:43:54 -07:00
Jm Casler
00eed206cb Update typo, import from js library 2020-10-22 18:26:43 -07:00
Jm Casler
130d55aaaa Merge pull request #500 from mc-hamster/master
Updated dev-https from my fork.
2020-10-21 21:29:34 -07:00
Jm Casler
13ef48094d Merge pull request #21 from meshtastic/dev-https
Dev https to my fork
2020-10-21 21:20:55 -07:00
Jm Casler
529fd5a830 Merge pull request #499 from meshtastic/master
Update dev-https from master
2020-10-21 21:20:07 -07:00
Jm Casler
baa3d1dae4 Support for the SPIFFS 2020-10-21 20:57:44 -07:00
Kevin Hester
4dd50df810 Merge pull request #498 from geeksville/dev
1.1.6
2020-10-21 04:28:55 -07:00
Kevin Hester
14c4022c18 1.1.6 (and screen layout tweaks) 2020-10-21 19:18:03 +08:00
Kevin Hester
a5d7bacdbf Show current region on the boot screen 2020-10-21 17:27:13 +08:00
Kevin Hester
0b3c25f6d9 use correct code for "talking to phone" fixes OTA update while a router 2020-10-21 16:50:09 +08:00
Kevin Hester
ad7a474a52 update buildscript to generate universal/regionless roms 2020-10-21 12:48:04 +08:00
Kevin Hester
430186ec53 Merge remote-tracking branch 'root/master' into dev 2020-10-21 10:44:56 +08:00
Jm Casler
e9279919ae Remove uncompressed style.css. Combine SSID/PWD on same line and toggle between the two every 10 seconds. 2020-10-19 16:58:11 -07:00
Jm Casler
227c6fc27e Merge pull request #494 from meshtastic/dev-https
Added the x-protobuf-schema, initial integration of javascript library, tuning of the CPU throttle timer.
2020-10-18 22:03:46 -07:00
Jm Casler
a37844d7e5 Merge pull request #493 from mc-hamster/master
update case of meshhttpStatic.h
2020-10-18 21:55:25 -07:00
Jm Casler
ff20b29c3c update case of meshhttpStatic.h 2020-10-18 21:44:55 -07:00
Jm Casler
64c29c4a35 Merge pull request #490 from mc-hamster/master
Update filename, cpu clock timer, add more debug info on the web server startup
2020-10-18 21:41:34 -07:00
Jm Casler
d4df3f8a7e mDNS server http://meshtastic.local 2020-10-18 21:39:02 -07:00
Jm Casler
a16c3af30a Clarified debug message regardding the Web Server startup status. 2020-10-18 18:44:08 -07:00
Jm Casler
3061860dab Update sleep timer for the CPU clock 2020-10-18 18:30:19 -07:00
Jm Casler
2450d98b59 Merge branch 'master' of https://github.com/mc-hamster/Meshtastic-device 2020-10-18 18:08:03 -07:00
Jm Casler
a371592ad9 Added instructions for David 2020-10-18 18:07:44 -07:00
Jm Casler
f7aaf48ae9 Merge pull request #20 from meshtastic/dev-https
Update my branch from dev-https
2020-10-18 18:04:49 -07:00
Jm Casler
1fb604ebc8 Merge pull request #488 from mc-hamster/master
Update dev-https from my fork.
2020-10-18 16:44:01 -07:00
Jm Casler
df2733a3b5 readded nodeScriptScriptsJS. it got lost in the transition to the new webserver 2020-10-17 20:28:19 -07:00
Kevin Hester
8fd3cb1aac add crude charging detection for 'dumb' voltage based battery sensors 2020-10-18 09:44:29 +08:00
Kevin Hester
485c476f17 cleaner battery debug messages 2020-10-18 09:32:12 +08:00
Jm Casler
7dd4ce32d2 Moved style.css into meshhttpStatic.h
Also created /data to store static files before they go into meshhttpStatic.h
2020-10-17 17:33:29 -07:00
Jm Casler
7f12af73d4 Updated to be host and protocol agnostic 2020-10-17 12:00:21 -07:00
Jm Casler
63113d57b3 Initial integration of meshtastic.js and sample code 2020-10-17 11:30:59 -07:00
Jm Casler
32850ff39d Merge pull request #487 from mc-hamster/master
Some cleanup and implement the X-Protobuf-Schema header
2020-10-16 23:40:48 -07:00
Jm Casler
2901f773a4 Some cleanup and implement the X-Protobuf-Schema header 2020-10-16 23:33:50 -07:00
Kevin Hester
a7c54e4ad7 Merge remote-tracking branch 'root/master' into dev 2020-10-17 13:41:51 +08:00
Kevin Hester
e1f0e11cb8 ppr1 WIP DO NOT MERGE - will break other clients 2020-10-17 13:15:12 +08:00
Kevin Hester
c73ee98739 Merge branch 'master' into ppr1 2020-10-17 11:00:28 +08:00
Jm Casler
c2a1141dfa Merge pull request #485 from mc-hamster/master
update class case of HttpAPI
2020-10-16 19:40:32 -07:00
Jm Casler
5b4472ab56 fix case of HttpAPI class 2020-10-16 19:38:59 -07:00
Jm Casler
3262f732d8 Merge pull request #19 from meshtastic/master
update my branch from master
2020-10-16 19:31:59 -07:00
Jm Casler
cff21ca130 Merge pull request #484 from meshtastic/dev-https
Functional protobuf/rest interface.
2020-10-16 19:28:40 -07:00
Jm Casler
81ce04d3da Merge branch 'master' into dev-https 2020-10-16 19:21:06 -07:00
Jm Casler
f4d2b10840 Merge pull request #483 from mc-hamster/master
Update for interface with the JS client.
2020-10-16 19:18:31 -07:00
Jm Casler
2370cb8aac Merge pull request #482 from meshtastic/dev-https
Merge from dev-https to master.
2020-10-16 19:10:04 -07:00
Kevin Hester
59ec87f5b0 oops tbeam was inheriting from arduino, it should inherit from esp32 2020-10-17 09:58:34 +08:00
Jm Casler
0d9481b6ea add allow-headers 2020-10-16 18:00:28 -07:00
Jm Casler
8f0105ccd9 don't send content-type when options is set 2020-10-16 17:46:22 -07:00
Kevin Hester
05ca3c3d56 Update to work with my font size change 2020-10-17 08:44:04 +08:00
Jm Casler
ba549d8fcd Return OPTIONS when requested for toradio 2020-10-16 17:41:08 -07:00
Jm Casler
b9df2c00fa fromradio all option
requesting fromradio will return one protobuf
4:56
requesting fromradio?all=true will give you all of them.
2020-10-16 17:07:35 -07:00
Kevin Hester
d9dcb33576 Merge branch 'master' into dev-https 2020-10-16 17:05:06 -07:00
Jm Casler
f698231be7 Merge branch 'master' of https://github.com/mc-hamster/Meshtastic-device 2020-10-16 16:36:55 -07:00
Jm Casler
8414f4a6a3 Added access-control-allow headers 2020-10-16 16:36:52 -07:00
Kevin Hester
8505020be5 Merge branch 'dev' into ppr1 2020-10-16 17:03:30 +08:00
Kevin Hester
f3b93d55fb oops fix for esp32 2020-10-16 17:03:04 +08:00
Kevin Hester
9e0731a956 Merge branch 'dev' into ppr1 2020-10-16 17:01:42 +08:00
Kevin Hester
2b373048c6 fix battery voltage sensing on NRF52 boards 2020-10-16 17:00:27 +08:00
Kevin Hester
22f23bb07d Merge branch 'dev' into ppr1
# Conflicts:
#	src/gps/NMEAGPS.cpp
2020-10-16 15:59:55 +08:00
Kevin Hester
b32e3f1269 Merge pull request #479 from geeksville/dev
minor eink things
2020-10-15 23:44:15 -07:00
Kevin Hester
68ddb712f5 properly keep nrf52s from sleeping 2020-10-16 14:00:56 +08:00
Kevin Hester
2fb5cd8c1c work with more NMEA gps sentences 2020-10-16 14:00:17 +08:00
Kevin Hester
79aea8231f make screen positions less hardwired wrt font size 2020-10-16 11:22:07 +08:00
Kevin Hester
b0837c10c6 eink display improvements 2020-10-16 10:53:55 +08:00
Jm Casler
cd811951b1 Merge pull request #481 from mc-hamster/master
Update from mchamster to dev-https
2020-10-15 19:53:03 -07:00
Jm Casler
df2976dad0 Merge pull request #18 from meshtastic/dev-https
From Dev https to mc-hamster master
2020-10-15 19:40:49 -07:00
Jm Casler
4ccbe6ff71 Merge pull request #480 from meshtastic/master
Updating dev-https from master
2020-10-15 19:40:01 -07:00
Jm Casler
038ddb887f Be a little smarter with setting the CPU frequency
in the event we have HTTPS and HTTP requests within close periods, let the speed set by the HTTPS request take presidence.
2020-10-15 19:28:20 -07:00
Kevin Hester
9134faaed1 turn off segger debug in ttgo eink build 2020-10-15 16:11:40 +08:00
Kevin Hester
649a120fe0 make eink screen look nicer 2020-10-15 15:56:38 +08:00
Kevin Hester
4db0c4a563 Make screen code resolution agnostic. Look better on big TFT/eink screens 2020-10-15 15:12:27 +08:00
Kevin Hester
5f2f3c94b9 PPR1 and allow boards to set lower or higher sx1262 power limits 2020-10-15 13:47:10 +08:00
Jm Casler
3b2f5fa5e3 fromRadio now returns all the vailable protobufs. a request to http sets the cpu clock to 160mhz, https to 240mhz then will clock it back down after 2 minutes. 2020-10-14 20:24:19 -07:00
Jm Casler
97adb598b6 toRadio is functional 2020-10-14 16:17:12 -07:00
Kevin Hester
152ebf0dff Merge branch 'dev' into ppr1 2020-10-14 10:24:39 +08:00
Kevin Hester
5b1511c930 ppr1 make jlink scripts 2020-10-14 10:21:55 +08:00
Kevin Hester
7ef2cc8623 Merge pull request #478 from geeksville/dev
1.1.5
2020-10-13 18:11:15 -07:00
Kevin Hester
b41a32c6b6 1.1.5 2020-10-14 09:04:20 +08:00
Kevin Hester
1ebd7b0c3e make lightsleep default time 5 minutes, with 1 minute in for BLE syncing 2020-10-14 08:53:12 +08:00
Kevin Hester
5457541244 fix #477: sleep behavior was broken in app due to device bug since 1.1.1
+        // NOTE: The phone app needs to know the ls_secs value so it can properly expect sleep behavior.
+        // So even if we internally use 0 to represent 'use default' we still need to send the value we are
+        // using to the app (so that even old phone apps work with new device loads).
2020-10-14 08:45:29 +08:00
Kevin Hester
ca77d48b20 corvus2 gps still doesn't work WIP 2020-10-13 14:43:28 +08:00
Kevin Hester
965c2bda8d corvus2 board builds 2020-10-13 13:59:06 +08:00
Jm Casler
02e3438d5e Added a favicon.ico 2020-10-11 22:13:14 -07:00
Jm Casler
02b1ece6ac Update meshhttp.cpp 2020-10-11 21:30:14 -07:00
Jm Casler
9fdef366f7 Merge pull request #473 from mc-hamster/master
update dev-https from my fork
2020-10-11 21:20:50 -07:00
Jm Casler
284816229e Merge pull request #17 from meshtastic/master
update my fork from head
2020-10-11 21:19:39 -07:00
Jm Casler
10008d4eef fix merge conflict 2020-10-11 21:19:22 -07:00
Jm Casler
58cfd1317c Merge branch 'master' of https://github.com/mc-hamster/Meshtastic-device 2020-10-11 20:38:13 -07:00
Jm Casler
3d3f7869d4 Increase CPU frequency on HTTPS requests. Clock back down after a period of time. 2020-10-11 20:38:09 -07:00
Kevin Hester
ef325289eb fix a doc error 2020-10-12 11:09:53 +08:00
Kevin Hester
40c63c0615 Merge pull request #472 from geeksville/dev
1.1.4
2020-10-11 18:39:02 -07:00
Kevin Hester
a9de8b9bb3 oops - only read axp on boards that have it 2020-10-12 09:33:15 +08:00
Kevin Hester
66a7f896c8 1.1.4 2020-10-12 09:27:48 +08:00
Kevin Hester
45a36f5571 fix POWER state entry/exit based on loss of USB power (tx @mc-hamster) 2020-10-12 09:27:07 +08:00
Jm Casler
876d32c9ee Merge pull request #16 from geeksville/mc-master
fix build for linux (by moving esp32 specific lib to esp32 tree)
2020-10-11 18:17:20 -07:00
Kevin Hester
b9ce75b09c fix build for linux (by moving esp32 specific lib to esp32 tree) 2020-10-12 09:03:04 +08:00
Kevin Hester
62493efc40 Merge pull request #471 from geeksville/coroutine
fix my breakage
2020-10-11 17:42:26 -07:00
Kevin Hester
2848b76cc9 Merge branch 'master' into coroutine 2020-10-11 17:28:52 -07:00
Kevin Hester
ef8bea478d Merge branch 'coroutine' of https://github.com/geeksville/Meshtastic-esp32 into coroutine 2020-10-12 08:25:33 +08:00
Kevin Hester
a8e4bbbe65 fix my breaking of button press behavior 2020-10-12 08:25:17 +08:00
Kevin Hester
9a414d9c77 fix my breakage of screen waking 2020-10-12 08:13:32 +08:00
Jm Casler
48e6a60a07 Merge pull request #470 from mc-hamster/master
Merge pull request #15 from meshtastic/master
2020-10-10 22:30:34 -07:00
Jm Casler
ca48079545 Merge pull request #15 from meshtastic/master
updated my branch from head
2020-10-10 22:29:09 -07:00
Jm Casler
76b4be3b87 Merge pull request #469 from mc-hamster/master
Switched to esp32_https_server from the Espressif (Issue #452) and Enable RX LNA (#466)
2020-10-10 22:28:08 -07:00
Jm Casler
d39cc3d57b Checking if ESP32 for the frequency display 2020-10-10 22:06:56 -07:00
Jm Casler
b17a8d7a6a Removed powerExit -- it wasn't working 2020-10-10 21:54:27 -07:00
Kevin Hester
3d21794039 Merge pull request #465 from geeksville/coroutine
Lightweight thread refactoring
2020-10-10 18:32:15 -07:00
Kevin Hester
beac614e65 Merge branch 'master' into coroutine 2020-10-10 18:19:40 -07:00
Kevin Hester
87f2673fc4 Merge pull request #463 from geeksville/dev
fix #462 publish immediately on any GPS state change
2020-10-10 18:19:22 -07:00
Kevin Hester
999b292717 fixes for the posix port 2020-10-11 09:18:47 +08:00
Jm Casler
c16acb904e Merge branch 'master' of https://github.com/mc-hamster/Meshtastic-device 2020-10-10 17:59:46 -07:00
Jm Casler
5b777219be Enable the RX LNA #466
Enabling the RX LNA
2020-10-10 17:59:32 -07:00
Jm Casler
32ea11d2af Merge pull request #14 from meshtastic/master
Update from head
2020-10-10 17:23:13 -07:00
Kevin Hester
8330c3270e 1.1.3 2020-10-11 08:15:52 +08:00
Kevin Hester
0c8e0efed2 new threading finished- saves about 10mA for the high activity states 2020-10-11 08:12:53 +08:00
Kevin Hester
c44d8a0433 Merge remote-tracking branch 'root/master' into coroutine 2020-10-10 18:12:37 +08:00
Kevin Hester
49b4ed2a89 coroutine: kinda works now 2020-10-10 18:03:45 +08:00
Jm Casler
db8faa9faf added powerExit 2020-10-09 23:07:37 -07:00
Kevin Hester
4b9ea4f808 Merge branch 'dev' into coroutine 2020-10-10 09:22:23 +08:00
Kevin Hester
c3beca3e23 Merge branch 'master' into dev 2020-10-09 18:21:16 -07:00
Kevin Hester
95cb6b06e4 fix #462 publish immediately on any GPS state change
(don't wait until end of aquisition window)
2020-10-10 09:20:38 +08:00
Kevin Hester
c46a884558 concurrency wip 2020-10-10 08:28:00 +08:00
Kevin Hester
2044427e97 coroutines: wip compiles but does not link 2020-10-09 14:16:51 +08:00
Kevin Hester
514ebdf013 Merge pull request #460 from geeksville/dev
Dev
2020-10-08 19:09:12 -07:00
Kevin Hester
10f64590a9 Merge branch 'dev' into coroutine 2020-10-09 10:03:27 +08:00
Kevin Hester
4a70ba1f7a fix nodeinfo stored times (I think) for @lgoix 2020-10-09 10:01:13 +08:00
Kevin Hester
dd6a402ea0 coroutine: wip 2020-10-09 09:10:44 +08:00
Kevin Hester
bed7d8a619 threads: begin change to cooperative threading 2020-10-08 13:32:34 +08:00
Jm Casler
6d178ebc91 Added esp32_https_server to the meshtastic project 2020-10-07 22:18:46 -07:00
Jm Casler
f75a256631 Merge branch 'master' of https://github.com/mc-hamster/Meshtastic-device 2020-10-07 22:02:59 -07:00
Jm Casler
4f659b7563 Initial check in of HTTPS server for #452
This switches from the espressif web server to esp32_https_server. Both HTTPS and HTTP have been migrated. On board SSL key generation.
2020-10-07 22:02:53 -07:00
Kevin Hester
1b6e8e36d3 Merge remote-tracking branch 'root/master' 2020-10-08 11:14:28 +08:00
Kevin Hester
7a4b8cde11 keep rf95 max power at 20 dBm so users don't smoke their boards 2020-10-08 10:51:31 +08:00
Kevin Hester
113859e791 increase sx1272 max power
+#define MAX_POWER 27
 // if we use 20 we are limited to 1% duty cycle or hw might overheat.  For continuous operation set a limit of 17
+// In theory up to 27 dBm is possible, but the modules installed in most radios can cope with a max of 20.  So BIG WARNING
+// if you set power to something higher than 17 or 20 you might fry your board.
2020-10-08 09:57:59 +08:00
Jm Casler
dffcea1f4d Merge pull request #13 from meshtastic/master
Update my fork from head with 1.1 changes
2020-10-07 08:32:54 -07:00
BeardyWalrus
b4b1b24c84 always need double press, even if you dont have a screen 2020-10-05 20:13:04 -04:00
BeardyWalrus
4d7cd0a09d conditional on needing ESP32 bluetooth header loaded 2020-10-03 17:23:36 -04:00
BeardyWalrus
fed8e80ae4 Merge branch 'master' into master 2020-10-03 17:04:47 -04:00
BeardyWalrus
b38bcffafb Merge branch 'master' of https://github.com/BeardyWalrus/Meshtastic-device 2020-10-03 17:02:19 -04:00
BeardyWalrus
530432411e revised fix for #357
Now supports default password for devices with no display, and override with double press of user button
2020-10-03 17:02:17 -04:00
BeardyWalrus
f30c84012f Merge branch 'master' into master 2020-10-01 22:24:30 -04:00
BeardyWalrus
5150d15997 Merge branch 'master' into master 2020-09-29 19:57:01 -04:00
BeardyWalrus
7d6dbcfa3f Update BluetoothUtil.cpp
fix for #357
use presence of ssd1306 display to set display functionality for bluetooth security.
2020-09-29 19:51:39 -04:00
BeardyWalrus
26bafb4082 Merge pull request #3 from meshtastic/master
Update from upstream
2020-09-27 19:23:26 -04:00
BeardyWalrus
0b1a78c028 Merge pull request #2 from meshtastic/master
Pull to live
2020-06-14 16:06:06 -04:00
BeardyWalrus
b8e1b28958 Merge pull request #1 from meshtastic/master
Resync with Upstream
2020-05-21 20:36:06 -04:00
162 changed files with 5869 additions and 2421 deletions

View File

@@ -49,7 +49,8 @@
"string_view": "cpp",
"cassert": "cpp",
"iterator": "cpp",
"shared_mutex": "cpp"
"shared_mutex": "cpp",
"iostream": "cpp"
},
"cSpell.words": [
"Blox",

View File

@@ -2,18 +2,17 @@
set -e
source bin/version.sh
VERSION=`bin/buildinfo.py`
COUNTRIES="US EU433 EU865 CN JP ANZ KR"
#COUNTRIES=US
#COUNTRIES=CN
BOARDS_ESP32="tlora-v2 tlora-v1 tlora-v2-1-1.6 tbeam heltec tbeam0.7"
#BOARDS_ESP32=tbeam
# FIXME note nrf52840dk build is for some reason only generating a BIN file but not a HEX file nrf52840dk-geeksville is fine
BOARDS_NRF52="lora-relay-v1"
BOARDS="$BOARDS_ESP32 $BOARDS_NRF52"
#BOARDS=tbeam
OUTDIR=release/latest
@@ -22,22 +21,61 @@ ARCHIVEDIR=release/archive
rm -f $OUTDIR/firmware*
mkdir -p $OUTDIR/bins $OUTDIR/elfs
rm -f $OUTDIR/bins/*
mkdir -p $OUTDIR/bins
rm -r $OUTDIR/bins/*
mkdir -p $OUTDIR/bins/universal $OUTDIR/elfs/universal
# build the named environment and copy the bins to the release directory
function do_build {
echo "Building for $BOARD with $PLATFORMIO_BUILD_FLAGS"
function do_build() {
BOARD=$1
COUNTRY=$2
isNrf=$3
echo "Building $COUNTRY for $BOARD with $PLATFORMIO_BUILD_FLAGS"
rm -f .pio/build/$BOARD/firmware.*
# The shell vars the build tool expects to find
export HW_VERSION="1.0-$COUNTRY"
export APP_VERSION=$VERSION
export COUNTRY
# Are we building a universal/regionless rom?
if [ "x$COUNTRY" != "x" ]
then
export HW_VERSION="1.0-$COUNTRY"
export COUNTRY
basename=firmware-$BOARD-$COUNTRY-$VERSION
else
export HW_VERSION="1.0"
unset COUNTRY
basename=universal/firmware-$BOARD-$VERSION
fi
pio run --jobs 4 --environment $BOARD # -v
SRCELF=.pio/build/$BOARD/firmware.elf
cp $SRCELF $OUTDIR/elfs/firmware-$BOARD-$COUNTRY-$VERSION.elf
cp $SRCELF $OUTDIR/elfs/$basename.elf
if [ "$isNrf" = "false" ]
then
echo "Copying ESP32 bin file"
SRCBIN=.pio/build/$BOARD/firmware.bin
cp $SRCBIN $OUTDIR/bins/$basename.bin
else
echo "Generating NRF52 uf2 file"
SRCHEX=.pio/build/$BOARD/firmware.hex
bin/uf2conv.py $SRCHEX -c -o $OUTDIR/bins/$basename.uf2 -f 0xADA52840
fi
}
function do_boards() {
declare boards=$1
declare isNrf=$2
for board in $boards; do
for country in $COUNTRIES; do
do_build $board $country "$isNrf"
done
# Build universal
do_build $board "" "$isNrf"
done
}
# Make sure our submodules are current
@@ -46,26 +84,20 @@ git submodule update
# Important to pull latest version of libs into all device flavors, otherwise some devices might be stale
platformio lib update
for COUNTRY in $COUNTRIES; do
for BOARD in $BOARDS; do
do_build $BOARD
done
do_boards "$BOARDS_ESP32" "false"
do_boards "$BOARDS_NRF52" "true"
echo "Copying ESP32 bin files"
for BOARD in $BOARDS_ESP32; do
SRCBIN=.pio/build/$BOARD/firmware.bin
cp $SRCBIN $OUTDIR/bins/firmware-$BOARD-$COUNTRY-$VERSION.bin
done
echo "Generating NRF52 uf2 files"
for BOARD in $BOARDS_NRF52; do
SRCHEX=.pio/build/$BOARD/firmware.hex
bin/uf2conv.py $SRCHEX -c -o $OUTDIR/bins/firmware-$BOARD-$COUNTRY-$VERSION.uf2 -f 0xADA52840
done
done
echo "Building SPIFFS for ESP32 targets"
pio run --environment tbeam -t buildfs
cp .pio/build/tbeam/spiffs.bin $OUTDIR/bins/universal/spiffs-$VERSION.bin
# keep the bins in archive also
cp $OUTDIR/bins/firmware* $OUTDIR/elfs/firmware* $ARCHIVEDIR
cp $OUTDIR/bins/firmware* $OUTDIR/bins/universal/spiffs* $OUTDIR/elfs/firmware* $OUTDIR/bins/universal/firmware* $OUTDIR/elfs/universal/firmware* $ARCHIVEDIR
echo Updating android bins $OUTDIR/forandroid
rm -rf $OUTDIR/forandroid
mkdir -p $OUTDIR/forandroid
cp -a $OUTDIR/bins/universal/*.bin $OUTDIR/forandroid/
cat >$OUTDIR/curfirmwareversion.xml <<XML
<?xml version="1.0" encoding="utf-8"?>
@@ -79,7 +111,8 @@ Generated by bin/buildall.sh -->
</resources>
XML
echo Generating $ARCHIVEDIR/firmware-$VERSION.zip
rm -f $ARCHIVEDIR/firmware-$VERSION.zip
zip --junk-paths $ARCHIVEDIR/firmware-$VERSION.zip $OUTDIR/bins/firmware-*-$VERSION.* images/system-info.bin bin/device-install.sh bin/device-update.sh
zip --junk-paths $ARCHIVEDIR/firmware-$VERSION.zip $ARCHIVEDIR/spiffs-$VERSION.bin $OUTDIR/bins/firmware-*-$VERSION.* images/system-info.bin bin/device-install.sh bin/device-update.sh
echo BUILT ALL

11
bin/buildinfo.py Executable file
View File

@@ -0,0 +1,11 @@
#!/usr/bin/env python3
import configparser
config = configparser.RawConfigParser()
config.read('version.properties')
version = dict(config.items('VERSION'))
verStr = "{}.{}.{}".format(version["major"], version["minor"], version["build"])
print(f"{verStr}")

View File

@@ -1,5 +1,7 @@
#!/bin/sh
set -e
# Usage info
show_help() {
cat << EOF
@@ -36,6 +38,7 @@ if [ -f "${FILENAME}" ]; then
echo "Trying to flash ${FILENAME}, but first erasing and writing system information"
esptool.py --baud 921600 erase_flash
esptool.py --baud 921600 write_flash 0x1000 system-info.bin
esptool.py --baud 921600 write_flash 0x00390000 spiffs-*.bin
esptool.py --baud 921600 write_flash 0x10000 ${FILENAME}
else
echo "Invalid file: ${FILENAME}"

View File

@@ -2,7 +2,20 @@
set -e
# dependencies
# apt install srecord
BOOTDIR=/home/kevinh/development/meshtastic/Adafruit_nRF52_Bootloader
BOARD=othernet_ppr1
BOOTVER=0.3.2
BOOTNUM=128
BOOTSHA=gc01b9ea
SDCODE=s113
SDVER=7.2.0
PROJ=ppr1
# FIXME for nRF52840 use 0xff000, for nRF52833 use 0x7f000
BOOTSET=0x7f000
nrfjprog --eraseall -f nrf52
@@ -11,12 +24,12 @@ nrfjprog --eraseall -f nrf52
# first 4 bytes should be 0x01 to indicate valid app image
# second 4 bytes should be 0x00 to indicate no CRC required for image
echo "01 00 00 00 00 00 00 00" | xxd -r -p - >/tmp/bootconf.bin
srec_cat /tmp/bootconf.bin -binary -offset 0xff000 -output /tmp/bootconf.hex -intel
srec_cat /tmp/bootconf.bin -binary -offset $BOOTSET -output /tmp/bootconf.hex -intel
echo Generating merged hex file
mergehex -m $BOOTDIR/_build/build-ttgo_eink/ttgo_eink_bootloader-0.3.2-124-g69bd8eb-dirty_s140_6.1.1.hex .pio/build/eink/firmware.hex /tmp/bootconf.hex -o ttgo_eink_full.hex
echo Generating merged hex file from .pio/build/$PROJ/firmware.hex
mergehex -o ${BOARD}_full.hex -m $BOOTDIR/_build/build-$BOARD/${BOARD}_bootloader-$BOOTVER-$BOOTNUM-$BOOTSHA-dirty_${SDCODE}_$SDVER.hex .pio/build/$PROJ/firmware.hex /tmp/bootconf.hex
echo Telling bootloader app region is valid and telling CPU to run
nrfjprog --program ttgo_eink_full.hex -f nrf52 --reset
nrfjprog --program ${BOARD}_full.hex -f nrf52 --reset
# nrfjprog --readuicr /tmp/uicr.hex; objdump -s /tmp/uicr.hex | less

22
bin/install-eink.sh Executable file
View File

@@ -0,0 +1,22 @@
# You probably don't want to use this script, it programs a custom bootloader build onto a nrf52 board
set -e
BOOTDIR=/home/kevinh/development/meshtastic/Adafruit_nRF52_Bootloader
nrfjprog --eraseall -f nrf52
# this generates an intel hex file that can be programmed into a NRF52 to tell the adafruit bootloader that the current app image is valid
# Bootloader settings are at BOOTLOADER_SETTINGS (rw) : ORIGIN = 0xFF000, LENGTH = 0x1000
# first 4 bytes should be 0x01 to indicate valid app image
# second 4 bytes should be 0x00 to indicate no CRC required for image
echo "01 00 00 00 00 00 00 00" | xxd -r -p - >/tmp/bootconf.bin
srec_cat /tmp/bootconf.bin -binary -offset 0xff000 -output /tmp/bootconf.hex -intel
echo Generating merged hex file
mergehex -m $BOOTDIR/_build/build-ttgo_eink/ttgo_eink_bootloader-0.3.2-125-gf38f8f4-dirty_s140_6.1.1.hex .pio/build/eink/firmware.hex /tmp/bootconf.hex -o ttgo_eink_full.hex
echo Telling bootloader app region is valid and telling CPU to run
nrfjprog --program ttgo_eink_full.hex -f nrf52 --reset
# nrfjprog --readuicr /tmp/uicr.hex; objdump -s /tmp/uicr.hex | less

View File

@@ -1,3 +1,3 @@
JLinkGDBServerCLExe -if SWD -select USB -port 2331 -device NRF52840_XXAA
JLinkGDBServerCLExe -if SWD -select USB -port 2331 -device NRF52832_XXAA

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

@@ -0,0 +1,3 @@
JLinkGDBServerCLExe -if SWD -select USB -port 2331 -device NRF52833_XXAA

View File

@@ -1,3 +1,3 @@
JLinkGDBServerCLExe -if SWD -select USB -port 2331 -device NRF52832_XXAA
JLinkGDBServerCLExe -if SWD -select USB -port 2331 -device NRF52840_XXAA

21
bin/platformio-custom.py Normal file
View File

@@ -0,0 +1,21 @@
Import("projenv")
import configparser
prefsLoc = projenv["PROJECT_DIR"] + "/version.properties"
print(f"Preferences in {prefsLoc}")
try:
config = configparser.RawConfigParser()
config.read(prefsLoc)
version = dict(config.items('VERSION'))
verStr = "{}.{}.{}".format(version["major"], version["minor"], version["build"])
except:
print("Can't read preferences, using 0.0.0")
verStr = "0.0.0"
print(f"Using meshtastic platform-custom.py, firmare version {verStr}")
# General options that are passed to the C and C++ compilers
projenv.Append(CCFLAGS=[
f"-DAPP_VERSION={verStr}"
])

View File

@@ -0,0 +1,6 @@
set -e
source bin/version.sh
esptool.py --baud 921600 write_flash 0x10000 release/latest/bins/universal/firmware-tbeam-$VERSION.bin

View File

@@ -1,6 +1,12 @@
#!/bin/bash
set -e
echo "This script requires https://jpa.kapsi.fi/nanopb/download/ version 0.4.1"
# the nanopb tool seems to require that the .options file be in the current directory!
cd proto
../../nanopb-0.4.1-linux-x86/generator-bin/protoc --nanopb_out=-v:../src/mesh -I=../proto mesh.proto
../../nanopb-0.4.1-linux-x86/generator-bin/protoc --nanopb_out=-v:../src/mesh -I=../proto *.proto
echo "Regenerating protobuf documentation - if you see an error message"
echo "you can ignore it unless doing a new protobuf release to github."
bin/regen-docs.sh

View File

@@ -1,4 +1,4 @@
echo "Converting to uf2 for NRF52 Adafruit bootloader"
bin/uf2conv.py .pio/build/lora-relay-v1/firmware.hex -f 0xADA52840
bin/uf2conv.py .pio/build/lora-relay-v2/firmware.hex -f 0xADA52840
# cp flash.uf2 /media/kevinh/FTH*BOOT/

View File

@@ -1,3 +0,0 @@
export VERSION=1.1.2

2
bin/view-map.sh Executable file
View File

@@ -0,0 +1,2 @@
echo using amap tool to display memory map
amap .pio/build/output.map

View File

@@ -5,7 +5,7 @@
},
"core": "nRF5",
"cpu": "cortex-m4",
"extra_flags": "-DARDUINO_NRF52840_LORA_RELAY_V1 -DNRF52840_XXAA",
"extra_flags": "-DARDUINO_NRF52840_TTGO_EINK -DNRF52840_XXAA",
"f_cpu": "64000000L",
"hwids": [
[

46
boards/lora-relay-v2.json Normal file
View File

@@ -0,0 +1,46 @@
{
"build": {
"arduino": {
"ldscript": "nrf52840_s140_v6.ld"
},
"core": "nRF5",
"cpu": "cortex-m4",
"extra_flags": "-DARDUINO_NRF52840_LORA_RELAY_V2 -DNRF52840_XXAA",
"f_cpu": "64000000L",
"hwids": [["0x239A", "0x4406"]],
"usb_product": "LORA_RELAY",
"mcu": "nrf52840",
"variant": "lora_relay_v2",
"variants_dir": "variants",
"bsp": {
"name": "adafruit"
},
"softdevice": {
"sd_flags": "-DS140",
"sd_name": "s140",
"sd_version": "6.1.1",
"sd_fwid": "0x00B6"
},
"bootloader": {
"settings_addr": "0xFF000"
}
},
"connectivity": ["bluetooth"],
"debug": {
"jlink_device": "nRF52840_xxAA",
"onboard_tools": ["jlink"],
"svd_path": "nrf52840.svd"
},
"frameworks": ["arduino"],
"name": "Meshtastic Lora Relay V1 (Adafruit BSP)",
"upload": {
"maximum_ram_size": 248832,
"maximum_size": 815104,
"require_upload_port": true,
"speed": 115200,
"protocol": "jlink",
"protocols": ["jlink", "nrfjprog", "stlink"]
},
"url": "https://github.com/BigCorvus/SX1262-LoRa-BLE-Relay",
"vendor": "BigCorvus"
}

View File

@@ -1,7 +1,7 @@
{
"build": {
"arduino": {
"ldscript": "nrf52840_s140_v6.ld"
"ldscript": "nrf52840_s113_v7.ld"
},
"core": "nRF5",
"cpu": "cortex-m4",
@@ -16,9 +16,9 @@
"name": "adafruit"
},
"softdevice": {
"sd_flags": "-DS140",
"sd_name": "s140",
"sd_version": "6.1.1",
"sd_flags": "-DS113",
"sd_name": "s113",
"sd_version": "7.2.0",
"sd_fwid": "0x00B6"
},
"bootloader": {

46
boards/ppr1.json Normal file
View File

@@ -0,0 +1,46 @@
{
"build": {
"arduino": {
"ldscript": "nrf52833_s113_v7.ld"
},
"core": "nRF5",
"cpu": "cortex-m4",
"extra_flags": "-DARDUINO_NRF52833_PPR -DNRF52833_XXAA",
"f_cpu": "64000000L",
"hwids": [["0x239A", "0x4406"]],
"usb_product": "PPR",
"mcu": "nrf52833",
"variant": "ppr",
"variants_dir": "variants",
"bsp": {
"name": "adafruit"
},
"softdevice": {
"sd_flags": "-DS113",
"sd_name": "s113",
"sd_version": "7.2.0",
"sd_fwid": "0x00b6"
},
"bootloader": {
"settings_addr": "0xFF000"
}
},
"connectivity": ["bluetooth"],
"debug": {
"jlink_device": "nRF52833_xxAA",
"onboard_tools": ["jlink"],
"svd_path": "nrf52833.svd"
},
"frameworks": ["arduino"],
"name": "Meshtastic PPR1 (Adafruit BSP)",
"upload": {
"maximum_ram_size": 248832,
"maximum_size": 815104,
"require_upload_port": true,
"speed": 115200,
"protocol": "jlink",
"protocols": ["jlink", "nrfjprog", "stlink"]
},
"url": "https://meshtastic.org/",
"vendor": "Othernet"
}

43
data/static/basic.js Normal file
View File

@@ -0,0 +1,43 @@
var meshtasticClient;
var connectionOne;
// Important: the connect action must be called from a user interaction (e.g. button press), otherwise the browsers won't allow the connect
function connect() {
// Create new connection
var httpconn = new meshtasticjs.IHTTPConnection();
// Set connection params
let sslActive;
if (window.location.protocol === 'https:') {
sslActive = true;
} else {
sslActive = false;
}
let deviceIp = window.location.hostname; // Your devices IP here
// Add event listeners that get called when a new packet is received / state of device changes
httpconn.addEventListener('fromRadio', function (packet) { console.log(packet) });
// Connect to the device async, then send a text message
httpconn.connect(deviceIp, sslActive)
.then(result => {
alert('device has been configured')
// This gets called when the connection has been established
// -> send a message over the mesh network. If no recipient node is provided, it gets sent as a broadcast
return httpconn.sendText('meshtastic is awesome');
})
.then(result => {
// This gets called when the message has been sucessfully sent
console.log('Message sent!');
})
.catch(error => { console.log(error); });
}

18
data/static/index.html Normal file
View File

@@ -0,0 +1,18 @@
<!doctype html>
<html class="no-js" lang="">
<head>
<meta charset="utf-8">
<title></title>
<script src="/static/meshtastic.js"></script>
<script src="/static/basic.js"></script>
</head>
<body>
<button id="connect_button" onclick="connect()">Connect to Meshtastic device</button>
</body>
</html>

Binary file not shown.

View File

@@ -39,6 +39,10 @@ This software is 100% open source and developed by a group of hobbyist experimen
For an detailed walk-through aimed at beginners, we recommend [meshtastic.letstalkthis.com](https://meshtastic.letstalkthis.com/).
### Related Groups
Telegram group for **Italy**-based users [t.me/meshtastic_italia](http://t.me/meshtastic_italia) (Italian language, unofficial).
# Updates
Note: Updates are happening almost daily, only major updates are listed below. For more details see our forum.

Binary file not shown.

Binary file not shown.

Binary file not shown.

File diff suppressed because one or more lines are too long

Binary file not shown.

After

Width:  |  Height:  |  Size: 272 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 417 KiB

View File

@@ -8,7 +8,7 @@ See [this site](https://www.rfwireless-world.com/Tutorials/LoRa-channels-list.ht
## LoRaWAN Europe Frequency Band
The maximum power allowed is +14dBM.
The maximum power allowed is +14dBm ERP (Effective Radiated Power, see [this site](https://en.wikipedia.org/wiki/Effective_radiated_power) for more information).
### 433 MHz
@@ -24,7 +24,7 @@ Channel zero starts at 865.20 MHz
LoRaWAN defines 64, 125 kHz channels from 902.3 to 914.9 MHz increments.
The maximum output power for North America is +30 dBM.
The maximum output power for North America is +30 dBm ERP.
The band is from 902 to 928 MHz. It mentions channel number and its respective channel frequency. All the 13 channels are separated by 2.16 MHz with respect to the adjacent channels.
Channel zero starts at 903.08 MHz center frequency.
Channel zero starts at 903.08 MHz center frequency.

View File

@@ -2,31 +2,90 @@
You probably don't care about this section - skip to the next one.
Threading tasks:
For app cleanup:
- Use https://github.com/ivanseidel/ArduinoThread? rather than full coroutines
- clean up main loop()
- check that we are mostly asleep, show which thread is causing us to wake
-
- use tickless idle on nrf52, and sleep X msec or until an interrupt occurs or the cooperative scheduling changes. https://devzone.nordicsemi.com/f/nordic-q-a/12363/nrf52-freertos-power-consumption-tickless-idle
- BAD IDEA: use vTaskDelay and https://www.freertos.org/xTaskAbortDelay.html if scheduling changes. (define INCLUDE_xTaskAbortDelay on ESP32 and NRF52 - seems impossible to find?)
- GOOD IDEA: use xSemaphoreTake to take a semaphore using a timeout. Expect semaphore to not be set, but set it to indicate scheduling has changed.
* DONE make device build always have a valid version
* DONE do fixed position bug https://github.com/meshtastic/Meshtastic-device/issues/536
* check build guide
* generate autodocs
* write devapi user guide
* DONE update android code: https://developer.android.com/topic/libraries/view-binding/migration
* only do wantReplies once per packet type, if we change network settings force it again
* make gpio watch work, use thread and setup
* make hello world example service
* make python ping command
* make python gpio read a bit cleaner
* DONE have python tool check max packet size before sending to device
* DONE if request was sent reliably, send reply reliably
* DONE require a recent python api to talk to these new device loads
* DONE require a recent android app to talk to these new device loads
* DONE fix handleIncomingPosition
* DONE move want_replies handling into plugins
* DONE on android for received positions handle either old or new positions / user messages
* on android side send old or new positions as needed / user messages
* test python side handle new position/user messages
* DONE make a gpio example. --gpiowrb 4 1, --gpiord 0x444, --gpiowatch 0x3ff
* DONE fix position sending to use new plugin
* DONE Add SinglePortNumPlugin - as the new most useful baseclass
* DONE move positions into regular data packets (use new app framework)
* DONE move user info into regular data packets (use new app framework)
* DONE test that positions, text messages and user info still work
* DONE test that position, text messages and user info work properly with new android app and old device code
* fix the RTC drift bug
* move python ping functionality into device, reply with rxsnr info
* use channels for gpio security https://github.com/meshtastic/Meshtastic-device/issues/104
Nimble tasks:
For high speed/lots of devices/short range tasks:
- readerror.txt stress test bug
- started RPA long test, jul 22 6pm
- implement nimble software update api
- update to latest bins, test OTA again (measure times) and then checkin bins
- do alpha release
- When guessing numhops for sending: if I've heard from many local (0 hop neighbors) decrease hopcount by 2 rather than 1.
This should nicely help 'router' nodes do the right thing when long range, or if there are many local nodes for short range.
- fix timeouts/delays to be based on packet length at current radio settings
* update protocol description per cyclomies email thread
* update faq with antennas https://meshtastic.discourse.group/t/range-test-ideas-requested/738/2
* update faq on recommended android version and phones
* add help link inside the app, reference a page on the wiki
* turn on amazon reviews support
* add a tablet layout (with map next to messages) in the android app
# Old docs to merge
MESH RADIO PROTOCOL
Old TODO notes on the mesh radio protocol, merge into real docs someday...
for each named group we have a pre-shared key known by all group members and
wrapped around the device. you can only be in one group at a time (FIXME?!) To
join the group we read a qr code with the preshared key and ParamsCodeEnum. that
gets sent via bluetooth to the device. ParamsCodeEnum maps to a set of various
radio params (regulatory region, center freq, SF, bandwidth, bitrate, power
etc...) so all members of the mesh can have their radios set the same way.
once in that group, we can talk between 254 node numbers.
to get our node number (and announce our presence in the channel) we pick a
random node number and broadcast as that node with WANT-NODENUM(my globally
unique name). If anyone on the channel has seen someone _else_ using that name
within the last 24 hrs(?) they reply with DENY-NODENUM. Note: we might receive
multiple denies. Note: this allows others to speak up for some other node that
might be saving battery right now. Any time we hear from another node (for any
message type), we add that node number to the unpickable list. To dramatically
decrease the odds a node number we request is already used by someone. If no one
denies within TBD seconds, we assume that we have that node number. As long as
we keep talking to folks at least once every 24 hrs, others should remember we
have it.
Once we have a node number we can broadcast POSITION-UPDATE(my globally unique
name, lat, lon, alt, amt battery remaining). All receivers will use this to a)
update the mapping of who is at what node nums, b) the time of last rx, c)
position. If we haven't heard from that node in a while we reply to that node
(only) with our current POSITION_UPDATE state - so that node (presumably just
rejoined the network) can build a map of all participants.
We will periodically broadcast POSITION-UPDATE as needed based on distance moved
or a periodic minimum heartbeat.
If user wants to send a text they can SEND_TEXT(dest user, short text message).
Dest user is a node number, or 0xff for broadcast.
# Medium priority
Items to complete before 1.0.

View File

@@ -42,7 +42,6 @@ Expected sequence for initial download:
- Read a RadioConfig from "radio" - used to get the channel and radio settings
- Read a User from "user" - to get the username for this node
- Read a MyNodeInfo from "mynode" to get information about this local device
- Write an empty record to "nodeinfo" to restart the nodeinfo reading state machine
- Read a series of NodeInfo packets to build the phone's copy of the current NodeDB for the mesh
- Read a endConfig packet that indicates that the entire state you need has been sent.
- Read a series of MeshPackets until it returns empty to get any messages that arrived for this node while the phone was away
@@ -112,6 +111,7 @@ Characteristics
| e272ebac-d463-4b98-bc84-5cc1a39ee517 | write | data, variable sized, recommended 512 bytes, write one for each block of file |
| 4826129c-c22a-43a3-b066-ce8f0d5bacc6 | write | crc32, write last - writing this will complete the OTA operation, now you can read result |
| 5e134862-7411-4424-ac4a-210937432c77 | read,notify | result code, readable but will notify when the OTA operation completes |
| 5e134862-7411-4424-ac4a-210937432c67 | write | sets the region for programming, currently only 0 (app) or 100 (spiffs) are defined, if not set app is assumed |
| GATT_UUID_SW_VERSION_STR/0x2a28 | read | We also implement these standard GATT entries because SW update probably needs them: |
| GATT_UUID_MANU_NAME/0x2a29 | read | |
| GATT_UUID_HW_VERSION_STR/0x2a27 | read | |

View File

@@ -5,7 +5,27 @@
## RAK815
TODO:
### PPR1 TODO
* V_BK for the GPS should probably be supplied from something always on
* use S113 soft device 7.2.0
* properly test charge controller config and read battery/charge status
* fix bluetooth
* fix LCD max contrast (currently too high, needs to be about 40?)
* save brightness settings in flash
* make ST7567Wire driver less ugly, move OLED stuff into a common class treee
* add LCD power save mode for lcd per page 31 of datasheet
* add LCD power off sequence per datasheet to lcd driver
* leave LCD screen on most of the time (because it needs little power)
### general nrf52 TODO:
- turn off transitions on eink screens
- change update interval on eink from 1/sec frames to one frame every 5 mins
- enter SDS state at correct time (to protect battery or loss of phone contact)
- show screen on eink when we enter SDS state (with app info and say sleeping)
- require button press to pair
- shrink soft device RAM usage
- get nrf52832 working again (currently OOM)

View File

@@ -2,18 +2,25 @@
These are **preliminary** notes on support for Meshtastic in the Pinetab.
A RF95 is connected via a CS341 USB-SPI chip.
A RF95 is connected via a CH341 USB-SPI chip.
Pin assignments:
CS0 from RF95 goes to CS0 on CS341
DIO0 from RF95 goes to INT on CS341
RST from RF95 goes to RST on CS341
CS0 from RF95 goes to CS0 on CH341
DIO0 from RF95 goes to INT on CH341
RST from RF95 goes to RST on CH341
This linux driver claims to provide USB-SPI support: https://github.com/gschorcht/spi-ch341-usb
Notes here on using that driver: https://www.linuxquestions.org/questions/linux-hardware-18/ch341-usb-to-spi-adaptor-driver-doesn%27t-work-4175622736/
Or if **absolutely** necessary could bitbang: https://www.cnx-software.com/2018/02/16/wch-ch341-usb-to-serial-chip-gets-linux-driver-to-control-gpios-over-usb/
## Portduino tasks
* How to access spi devices via ioctl (spidev): https://www.raspberrypi.org/documentation/hardware/raspberrypi/spi/README.md#:~:text=Troubleshooting-,Overview,bus)%2C%20UARTs%2C%20etc.
* access gpio via libgpiod?
* Use dkms to distribute driver?
* echo 100 > /sys/module/spi_ch341_usb/parameters/poll_period
## Task list
* Port meshtastic to build (under platformio) for a poxix target. spec: no screen, no gpios, sim network interface, posix threads, posix semaphores & queues, IO to the console only

View File

@@ -38,7 +38,7 @@ From lower to higher power consumption.
- full on (ON) - Everything is on, can eventually timeout and lower to a lower power state
onEntry: setBluetoothOn(true), screen.setOn(true)
onExit: screen.setOn(false)
onExit: screen->setOn(false)
- has power (POWER) - Screen is on, device doesn't sleep, bluetooth on, will stay in this state as long as we have power
onEntry: setBluetooth off, screen on
@@ -50,7 +50,7 @@ From lower to higher power consumption.
- At cold boot: The initial state (after setup() has run) is DARK
- While in DARK: if we receive EVENT_BOOT, transition to ON (and show the bootscreen). This event will be sent if we detect we woke due to reset (as opposed to deep sleep)
- While in LS: Once every position_broadcast_secs (default 15 mins) - the unit will wake into DARK mode and broadcast a "networkPing" (our position) and stay alive for wait_bluetooth_secs (default 30 seconds). This allows other nodes to have a record of our last known position if we go away and allows a paired phone to hear from us and download messages.
- While in LS: Once every position_broadcast_secs (default 15 mins) - the unit will wake into DARK mode and broadcast a "networkPing" (our position) and stay alive for wait_bluetooth_secs (default 60 seconds). This allows other nodes to have a record of our last known position if we go away and allows a paired phone to hear from us and download messages.
- While in LS: Every send*owner_interval (defaults to 4, i.e. one hour), when we wake to send our position we \_also* broadcast our owner. This lets new nodes on the network find out about us or correct duplicate node number assignments.
- While in LS/NB/DARK: If the user presses a button (EVENT_PRESS) we go to full ON mode for screen_on_secs (default 30 seconds). Multiple presses keeps resetting this timeout
- While in LS/NB/DARK: If we receive new text messages (EVENT_RECEIVED_TEXT_MSG), we go to full ON mode for screen_on_secs (same as if user pressed a button)

View File

@@ -19,23 +19,25 @@ default_envs = tbeam # lora-relay-v1 # nrf52840dk-geeksville # linux # or if you
; The following environment variables must be set in the shell if you'd like to override them.
; They are used in this ini file as systenv.VARNAME, so in your shell do export "VARNAME=fish"
; COUNTRY (default US), i.e. "export COUNTRY=EU865"
; APP_VERSION (default emptystring)
; HW_VERSION (default emptystring)
[env]
; note: APP_VERSION now comes from bin/version.json
extra_scripts = bin/platformio-custom.py
; note: we add src to our include search path so that lmic_project_config can override
; FIXME: fix lib/BluetoothOTA dependency back on src/ so we can remove -Isrc
build_flags = -Wno-missing-field-initializers -Isrc -Isrc/mesh -Isrc/gps -Ilib/nanopb/include -Wl,-Map,.pio/build/output.map
-DHW_VERSION_${sysenv.COUNTRY}
-DAPP_VERSION=${sysenv.APP_VERSION}
-DHW_VERSION=${sysenv.HW_VERSION}
-DUSE_THREAD_NAMES
-DTINYGPSPLUS_OPTION_NO_CUSTOM_FIELDS
; leave this commented out to avoid breaking Windows
;upload_port = /dev/ttyUSB0
;monitor_port = /dev/ttyUSB0
; geeksville: I think setting this should not be required - it breaks linux
;upload_port = /dev/cu.SLAB_USBtoUART
;monitor_port = /dev/cu.SLAB_USBtoUART
@@ -59,15 +61,16 @@ debug_tool = jlink
lib_deps =
https://github.com/meshtastic/esp8266-oled-ssd1306.git ; ESP8266_SSD1306
1260 ; OneButton library for non-blocking button debounce
https://github.com/geeksville/OneButton.git ; OneButton library for non-blocking button debounce
1202 ; CRC32, explicitly needed because dependency is missing in the ble ota update lib
https://github.com/meshtastic/arduino-fsm.git
https://github.com/meshtastic/arduino-fsm.git#2f106146071fc7bc620e1e8d4b88dc4e0266ce39
https://github.com/meshtastic/SparkFun_Ublox_Arduino_Library.git#31015a55e630a2df77d9d714669c621a5bf355ad
https://github.com/meshtastic/RadioLib.git#8657380241bce681c33aab46598bbf13b11f876c
https://github.com/meshtastic/TinyGPSPlus.git
https://github.com/meshtastic/TinyGPSPlus.git#9c1d584d2469523381e077b0b9c1bf868d6c0206
https://github.com/meshtastic/AXP202X_Library.git#8404abb6d4b486748636bc6ad72d2a47baaf5460
Wire ; explicitly needed here because the AXP202 library forgets to add it
SPI
https://github.com/geeksville/ArduinoThread.git#333ffd09b596977c217ba25da4258f588b462ac6
; Common settings for conventional (non Portduino) Ardino targets
[arduino_base]
@@ -93,6 +96,9 @@ build_flags =
${arduino_base.build_flags} -Wall -Wextra -Isrc/esp32 -Isrc/esp32-mfix-esp32-psram-cache-issue -lnimble -std=c++11
-DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG -DCORE_DEBUG_LEVEL=ARDUHAL_LOG_LEVEL_DEBUG
-DAXP_DEBUG_PORT=Serial
lib_deps =
${arduino_base.lib_deps}
https://github.com/meshtastic/esp32_https_server.git
# Hmm - this doesn't work yet
# board_build.ldscript = linker/esp32.extram.bss.ld
lib_ignore = segger_rtt
@@ -115,7 +121,7 @@ board_build.partitions = partition-table.csv
extends = esp32_base
board = ttgo-t-beam
lib_deps =
${arduino_base.lib_deps}
${esp32_base.lib_deps}
build_flags =
${esp32_base.build_flags} -D TBEAM_V10
@@ -232,6 +238,15 @@ lib_deps =
${arduino_base.lib_deps}
UC1701
; The PPR board
[env:ppr1]
extends = nrf52_base
board = ppr1
build_flags = ${nrf52_base.build_flags} -Ivariants/ppr1
src_filter = ${nrf52_base.src_filter} +<../variants/ppr1>
lib_deps =
${arduino_base.lib_deps}
; Prototype eink/nrf52840/sx1262 device
[env:eink]
extends = nrf52_base
@@ -267,7 +282,30 @@ lib_deps =
${arduino_base.lib_deps}
SparkFun BQ27441 LiPo Fuel Gauge Arduino Library
TFT_eSPI
# Adafruit ST7735 and ST7789 Library
; The https://github.com/BigCorvus/LoRa-BLE-Relay-v2 board by @BigCorvus
[env:lora-relay-v2]
extends = nrf52_base
board = lora-relay-v2
# 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/lora_relay_v2
-DUSER_SETUP_LOADED
-DTFT_WIDTH=80
-DTFT_HEIGHT=160
-DST7735_GREENTAB160x80
-DST7735_DRIVER
-DTFT_CS=ST7735_CS
-DTFT_DC=ST7735_RS
-DTFT_RST=ST7735_RESET
-DSPI_FREQUENCY=27000000
-DTFT_WR=ST7735_SDA
-DTFT_SCLK=ST7735_SCK
src_filter = ${nrf52_base.src_filter} +<../variants/lora_relay_v2>
lib_deps =
${arduino_base.lib_deps}
SparkFun BQ27441 LiPo Fuel Gauge Arduino Library
TFT_eSPI
; The Portduino based sim environment on top of linux
[env:linux]

2
proto

Submodule proto updated: a0b8d88896...ebd18145ca

21
src/FSCommon.cpp Normal file
View File

@@ -0,0 +1,21 @@
#include "FSCommon.h"
void fsInit()
{
#ifdef FS
if (!FSBegin())
{
DEBUG_MSG("ERROR filesystem mount Failed\n");
assert(0); // FIXME - report failure to phone
}
DEBUG_MSG("Filesystem files:\n");
File dir = FS.open("/");
File f = dir.openNextFile();
while (f) {
DEBUG_MSG(" %s\n", f.name());
f.close();
f = dir.openNextFile();
}
#endif
}

29
src/FSCommon.h Normal file
View File

@@ -0,0 +1,29 @@
#pragma once
#include "configuration.h"
// Cross platform filesystem API
#ifdef PORTDUINO
// Portduino version
#include "PortduinoFS.h"
#define FS PortduinoFS
#define FSBegin() true
#define FILE_O_WRITE "w"
#define FILE_O_READ "r"
#elif !defined(NO_ESP32)
// ESP32 version
#include "SPIFFS.h"
#define FS SPIFFS
#define FSBegin() FS.begin(true)
#define FILE_O_WRITE "w"
#define FILE_O_READ "r"
#else
// NRF52 version
#include "InternalFileSystem.h"
#define FS InternalFS
#define FSBegin() FS.begin()
using namespace Adafruit_LittleFS_Namespace;
#endif
void fsInit();

View File

@@ -18,6 +18,21 @@ Power *power;
using namespace meshtastic;
#if defined(NRF52_SERIES)
/*
* Internal Reference is +/-0.6V, with an adjustable gain of 1/6, 1/5, 1/4,
* 1/3, 1/2 or 1, meaning 3.6, 3.0, 2.4, 1.8, 1.2 or 0.6V for the ADC levels.
*
* External Reference is VDD/4, with an adjustable gain of 1, 2 or 4, meaning
* VDD/4, VDD/2 or VDD for the ADC levels.
*
* Default settings are internal reference with 1/6 gain (GND..3.6V ADC range)
*/
#define AREF_VOLTAGE 3.6
#else
#define AREF_VOLTAGE 3.3
#endif
/**
* If this board has a battery level sensor, set this to a valid implementation
*/
@@ -37,10 +52,13 @@ class AnalogBatteryLevel : public HasBatteryLevel
{
float v = getBattVoltage() / 1000;
if (v < 2.1)
if (v < noBatVolt)
return -1; // If voltage is super low assume no battery installed
return 100 * (v - 3.27) / (4.2 - 3.27);
if (v > chargingVolt)
return 0; // While charging we can't report % full on the battery
return 100 * (v - emptyVolt) / (fullVolt - emptyVolt);
}
/**
@@ -48,9 +66,11 @@ class AnalogBatteryLevel : public HasBatteryLevel
*/
virtual float getBattVoltage()
{
// Tested ttgo eink nrf52 board and the reported value is perfect
// DEBUG_MSG("raw val %u", raw);
return
#ifdef BATTERY_PIN
1000.0 * analogRead(BATTERY_PIN) * 2.0 * (3.3 / 1024.0);
1000.0 * 2.0 * (AREF_VOLTAGE / 1024.0) * analogRead(BATTERY_PIN);
#else
NAN;
#endif
@@ -59,17 +79,40 @@ class AnalogBatteryLevel : public HasBatteryLevel
/**
* return true if there is a battery installed in this unit
*/
virtual bool isBatteryConnect() { return getBattVoltage() != -1; }
virtual bool isBatteryConnect() { return getBattPercentage() != -1; }
/// If we see a battery voltage higher than physics allows - assume charger is pumping
/// in power
virtual bool isVBUSPlug() { return getBattVoltage() > chargingVolt; }
/// Assume charging if we have a battery and external power is connected.
/// we can't be smart enough to say 'full'?
virtual bool isChargeing() { return isBatteryConnect() && isVBUSPlug(); }
private:
/// If we see a battery voltage higher than physics allows - assume charger is pumping
/// in power
const float fullVolt = 4.2, emptyVolt = 3.27, chargingVolt = 4.3, noBatVolt = 2.1;
} analogLevel;
Power::Power() : OSThread("Power") {}
bool Power::analogInit()
{
#ifdef BATTERY_PIN
DEBUG_MSG("Using analog input for battery level\n");
// disable any internal pullups
pinMode(BATTERY_PIN, INPUT);
#ifndef NO_ESP32
// ESP32 needs special analog stuff
adcAttachPin(BATTERY_PIN);
#endif
#ifdef NRF52_SERIES
analogReference(AR_INTERNAL); // 3.6V
#endif
// adcStart(BATTERY_PIN);
analogReadResolution(10); // Default of 12 is not very linear. Recommended to use 10 or 11 depending on needed resolution.
batteryLevel = &analogLevel;
@@ -86,14 +129,19 @@ bool Power::setup()
if (!found) {
found = analogInit();
}
if (found) {
concurrency::PeriodicTask::setup(); // We don't start our periodic task unless we actually found the device
setPeriod(1);
}
enabled = found;
return found;
}
void Power::shutdown()
{
#ifdef TBEAM_V10
DEBUG_MSG("Shutting down\n");
axp.shutdown();
#endif
}
/// Reads power status to powerStatus singleton.
//
// TODO(girts): move this and other axp stuff to power.h/power.cpp.
@@ -122,7 +170,8 @@ void Power::readPowerStatus()
const PowerStatus powerStatus =
PowerStatus(hasBattery ? OptTrue : OptFalse, batteryLevel->isVBUSPlug() ? OptTrue : OptFalse,
batteryLevel->isChargeing() ? OptTrue : OptFalse, batteryVoltageMv, batteryChargePercent);
DEBUG_MSG("Read power stat %d\n", powerStatus.getHasUSB());
DEBUG_MSG("Battery: usbPower=%d, isCharging=%d, batMv=%d, batPct=%d\n", powerStatus.getHasUSB(),
powerStatus.getIsCharging(), powerStatus.getBatteryVoltageMv(), powerStatus.getBatteryChargePercent());
newStatus.notifyObservers(&powerStatus);
// If we have a battery at all and it is less than 10% full, force deep sleep
@@ -135,13 +184,47 @@ void Power::readPowerStatus()
}
}
void Power::doTask()
int32_t Power::runOnce()
{
readPowerStatus();
#ifdef TBEAM_V10
// WE no longer use the IRQ line to wake the CPU (due to false wakes from sleep), but we do poll
// the IRQ status by reading the registers over I2C
axp.readIRQ();
if (axp.isVbusRemoveIRQ()) {
DEBUG_MSG("USB unplugged\n");
powerFSM.trigger(EVENT_POWER_DISCONNECTED);
}
if (axp.isVbusPlugInIRQ()) {
DEBUG_MSG("USB plugged In\n");
powerFSM.trigger(EVENT_POWER_CONNECTED);
}
/*
Other things we could check if we cared...
if (axp.isChargingIRQ()) {
DEBUG_MSG("Battery start charging\n");
}
if (axp.isChargingDoneIRQ()) {
DEBUG_MSG("Battery fully charged\n");
}
if (axp.isBattPlugInIRQ()) {
DEBUG_MSG("Battery inserted\n");
}
if (axp.isBattRemoveIRQ()) {
DEBUG_MSG("Battery removed\n");
}
if (axp.isPEKShortPressIRQ()) {
DEBUG_MSG("PEK short button press\n");
}
*/
axp.clearIRQ();
#endif
// Only read once every 20 seconds once the power status for the app has been initialized
if (statusHandler && statusHandler->isInitialized())
setPeriod(1000 * 20);
return (statusHandler && statusHandler->isInitialized()) ? (1000 * 20) : RUN_SAME;
}
/**
@@ -208,8 +291,7 @@ bool Power::axp192Init()
// no battery also it could cause inadvertent waking from light sleep just because the battery filled
// we don't look for AXP202_BATT_REMOVED_IRQ because it occurs repeatedly while no battery installed
// we don't look at AXP202_VBUS_REMOVED_IRQ because we don't have anything hooked to vbus
axp.enableIRQ(AXP202_BATT_CONNECT_IRQ | AXP202_VBUS_CONNECT_IRQ | AXP202_PEK_SHORTPRESS_IRQ,
1);
axp.enableIRQ(AXP202_BATT_CONNECT_IRQ | AXP202_VBUS_CONNECT_IRQ | AXP202_PEK_SHORTPRESS_IRQ, 1);
axp.clearIRQ();
#endif
@@ -226,43 +308,3 @@ bool Power::axp192Init()
return false;
#endif
}
void Power::loop()
{
#ifdef PMU_IRQ
if (pmu_irq) {
pmu_irq = false;
axp.readIRQ();
DEBUG_MSG("pmu irq!\n");
if (axp.isChargingIRQ()) {
DEBUG_MSG("Battery start charging\n");
}
if (axp.isChargingDoneIRQ()) {
DEBUG_MSG("Battery fully charged\n");
}
if (axp.isVbusRemoveIRQ()) {
DEBUG_MSG("USB unplugged\n");
powerFSM.trigger(EVENT_POWER_DISCONNECTED);
}
if (axp.isVbusPlugInIRQ()) {
DEBUG_MSG("USB plugged In\n");
powerFSM.trigger(EVENT_POWER_CONNECTED);
}
if (axp.isBattPlugInIRQ()) {
DEBUG_MSG("Battery inserted\n");
}
if (axp.isBattRemoveIRQ()) {
DEBUG_MSG("Battery removed\n");
}
if (axp.isPEKShortPressIRQ()) {
DEBUG_MSG("PEK short button press\n");
}
readPowerStatus();
axp.clearIRQ();
}
#endif
}

View File

@@ -22,7 +22,7 @@ static uint32_t secsSlept;
static void lsEnter()
{
DEBUG_MSG("lsEnter begin, ls_secs=%u\n", getPref_ls_secs());
screen.setOn(false);
screen->setOn(false);
secsSlept = 0; // How long have we been sleeping this time
DEBUG_MSG("lsEnter end\n");
@@ -102,7 +102,7 @@ static void lsExit()
static void nbEnter()
{
screen.setOn(false);
screen->setOn(false);
setBluetoothEnable(false);
// FIXME - check if we already have packets for phone and immediately trigger EVENT_PACKETS_FOR_PHONE
@@ -111,24 +111,33 @@ static void nbEnter()
static void darkEnter()
{
setBluetoothEnable(true);
screen.setOn(false);
screen->setOn(false);
}
static void serialEnter()
{
setBluetoothEnable(false);
screen.setOn(true);
screen->setOn(true);
screen->print("Using API...\n");
}
static void powerEnter()
{
screen.setOn(true);
screen->setOn(true);
setBluetoothEnable(true);
screen->print("Powered...\n");
}
static void powerExit()
{
screen->setOn(true);
setBluetoothEnable(true);
screen->print("Unpowered...\n");
}
static void onEnter()
{
screen.setOn(true);
screen->setOn(true);
setBluetoothEnable(true);
static uint32_t lastPingMs;
@@ -140,11 +149,12 @@ static void onEnter()
service.sendNetworkPing(displayedNodeNum, true); // Refresh the currently displayed node
lastPingMs = now;
}
}
static void screenPress()
{
screen.onPress();
screen->onPress();
}
static void bootEnter() {}
@@ -156,7 +166,7 @@ State stateDARK(darkEnter, NULL, NULL, "DARK");
State stateSERIAL(serialEnter, NULL, NULL, "SERIAL");
State stateBOOT(bootEnter, NULL, NULL, "BOOT");
State stateON(onEnter, NULL, NULL, "ON");
State statePOWER(powerEnter, NULL, NULL, "POWER");
State statePOWER(powerEnter, NULL, powerExit, "POWER");
Fsm powerFSM(&stateBOOT);
void PowerFSM_setup()
@@ -235,20 +245,24 @@ void PowerFSM_setup()
powerFSM.add_timed_transition(&stateON, &stateDARK, getPref_screen_on_secs() * 1000, NULL, "Screen-on timeout");
powerFSM.add_timed_transition(&stateDARK, &stateNB, getPref_phone_timeout_secs() * 1000, NULL, "Phone timeout");
// On most boards we use light-sleep to be our main state, but on NRF52 we just stay in DARK
State *lowPowerState = &stateLS;
#ifndef NRF52_SERIES
// We never enter light-sleep state on NRF52 (because the CPU uses so little power normally)
powerFSM.add_timed_transition(&stateNB, &stateLS, getPref_min_wake_secs() * 1000, NULL, "Min wake timeout");
// We never enter light-sleep or NB states on NRF52 (because the CPU uses so little power normally)
powerFSM.add_timed_transition(&stateDARK, &stateNB, getPref_phone_timeout_secs() * 1000, NULL, "Phone timeout");
powerFSM.add_timed_transition(&stateNB, &stateLS, getPref_min_wake_secs() * 1000, NULL, "Min wake timeout");
powerFSM.add_timed_transition(&stateDARK, &stateLS, getPref_wait_bluetooth_secs() * 1000, NULL, "Bluetooth timeout");
#else
lowPowerState = &stateDARK;
#endif
auto meshSds = getPref_mesh_sds_timeout_secs();
if (meshSds != UINT32_MAX)
powerFSM.add_timed_transition(&stateLS, &stateSDS, meshSds * 1000, NULL, "mesh timeout");
powerFSM.add_timed_transition(lowPowerState, &stateSDS, meshSds * 1000, NULL, "mesh timeout");
// removing for now, because some users don't even have phones
// powerFSM.add_timed_transition(&stateLS, &stateSDS, getPref_phone_sds_timeout_sec() * 1000, NULL, "phone
// powerFSM.add_timed_transition(lowPowerState, &stateSDS, getPref_phone_sds_timeout_sec() * 1000, NULL, "phone
// timeout");
powerFSM.run_machine(); // run one interation of the state machine, so we run our on enter tasks for the initial DARK state

View File

@@ -20,5 +20,6 @@
#define EVENT_POWER_DISCONNECTED 14
extern Fsm powerFSM;
extern State statePOWER, stateSERIAL;
void PowerFSM_setup();

View File

@@ -82,7 +82,7 @@ class PowerStatus : public Status
isCharging = newStatus->isCharging;
}
if (isDirty) {
DEBUG_MSG("Battery %dmV %d%%\n", batteryVoltageMv, batteryChargePercent);
// DEBUG_MSG("Battery %dmV %d%%\n", batteryVoltageMv, batteryChargePercent);
onNewStatus.notifyObservers(this);
}
return 0;

View File

@@ -1,4 +1,5 @@
#include "RedirectablePrint.h"
#include "configuration.h"
#include <assert.h>
/**
@@ -10,4 +11,14 @@ void RedirectablePrint::setDestination(Print *_dest)
{
assert(_dest);
dest = _dest;
}
size_t RedirectablePrint::write(uint8_t c)
{
#ifdef SEGGER_STDOUT_CH
SEGGER_RTT_PutCharSkip(SEGGER_STDOUT_CH, c);
#endif
dest->write(c);
return 1; // We always claim one was written, rather than trusting what the serial port said (which could be zero)
}

View File

@@ -19,7 +19,7 @@ class RedirectablePrint : public Print
*/
void setDestination(Print *dest);
virtual size_t write(uint8_t c) { return dest->write(c); }
virtual size_t write(uint8_t c);
};
class NoopPrint : public Print

View File

@@ -1,6 +1,7 @@
#include "SerialConsole.h"
#include "PowerFSM.h"
#include "configuration.h"
#include "NodeDB.h"
#include <Arduino.h>
#define Port Serial
@@ -28,7 +29,8 @@ void SerialConsole::init()
void SerialConsole::handleToRadio(const uint8_t *buf, size_t len)
{
// Turn off debug serial printing once the API is activated, because other threads could print and corrupt packets
setDestination(&noopPrint);
if(!radioConfig.preferences.debug_log_enabled)
setDestination(&noopPrint);
canWrite = true;
StreamAPI::handleToRadio(buf, len);

View File

@@ -1,44 +0,0 @@
#pragma once
#include "WorkerThread.h"
namespace concurrency {
/**
* @brief A worker thread that waits on a freertos notification
*/
class BaseNotifiedWorkerThread : public WorkerThread
{
public:
/**
* Notify this thread so it can run
*/
virtual void notify(uint32_t v = 0, eNotifyAction action = eNoAction) = 0;
/**
* Notify from an ISR
*
* This must be inline or IRAM_ATTR on ESP32
*/
virtual void notifyFromISR(BaseType_t *highPriWoken, uint32_t v = 0, eNotifyAction action = eNoAction) { notify(v, action); }
protected:
/**
* The notification that was most recently used to wake the thread. Read from loop()
*/
uint32_t notification = 0;
/**
* What notification bits should be cleared just after we read and return them in notification?
*
* Defaults to clear all of them.
*/
uint32_t clearOnRead = UINT32_MAX;
/**
* A method that should block execution - either waiting ona queue/mutex or a "task notification"
*/
virtual void block() = 0;
};
} // namespace concurrency

View File

@@ -1,12 +0,0 @@
#include "Thread.h"
#include <assert.h>
namespace concurrency
{
void BaseThread::callRun(void *_this)
{
((BaseThread *)_this)->doRun();
}
} // namespace concurrency

View File

@@ -1,47 +0,0 @@
#pragma once
#include <cstdlib>
#include <stdint.h>
#include "freertosinc.h"
namespace concurrency
{
/**
* @brief Base threading
*/
class BaseThread
{
protected:
/**
* set this to true to ask thread to cleanly exit asap
*/
volatile bool wantExit = false;
public:
virtual void start(const char *name, size_t stackSize = 1024, uint32_t priority = tskIDLE_PRIORITY) = 0;
virtual ~BaseThread() {}
// uint32_t getStackHighwaterMark() { return uxTaskGetStackHighWaterMark(taskHandle); }
protected:
/**
* The method that will be called when start is called.
*/
virtual void doRun() = 0;
/**
* All thread run methods must periodically call serviceWatchdog, or the system will declare them hung and panic.
*
* this only applies after startWatchdog() has been called. If you need to sleep for a long time call stopWatchdog()
*/
virtual void serviceWatchdog() {}
virtual void startWatchdog() {}
virtual void stopWatchdog() {}
static void callRun(void *_this);
};
} // namespace concurrency

View File

@@ -0,0 +1,39 @@
#include "concurrency/BinarySemaphoreFreeRTOS.h"
#include "configuration.h"
#ifdef HAS_FREE_RTOS
namespace concurrency
{
BinarySemaphoreFreeRTOS::BinarySemaphoreFreeRTOS()
{
semaphore = xSemaphoreCreateBinary();
}
BinarySemaphoreFreeRTOS::~BinarySemaphoreFreeRTOS()
{
vSemaphoreDelete(semaphore);
}
/**
* Returns false if we were interrupted
*/
bool BinarySemaphoreFreeRTOS::take(uint32_t msec)
{
return xSemaphoreTake(semaphore, pdMS_TO_TICKS(msec));
}
void BinarySemaphoreFreeRTOS::give()
{
xSemaphoreGive(semaphore);
}
IRAM_ATTR void BinarySemaphoreFreeRTOS::giveFromISR(BaseType_t *pxHigherPriorityTaskWoken)
{
xSemaphoreGiveFromISR(semaphore, pxHigherPriorityTaskWoken);
}
} // namespace concurrency
#endif

View File

@@ -0,0 +1,31 @@
#pragma once
#include "configuration.h"
#include "../freertosinc.h"
namespace concurrency
{
#ifdef HAS_FREE_RTOS
class BinarySemaphoreFreeRTOS
{
SemaphoreHandle_t semaphore;
public:
BinarySemaphoreFreeRTOS();
~BinarySemaphoreFreeRTOS();
/**
* Returns false if we timed out
*/
bool take(uint32_t msec);
void give();
void giveFromISR(BaseType_t *pxHigherPriorityTaskWoken);
};
#endif
}

View File

@@ -0,0 +1,36 @@
#include "concurrency/BinarySemaphorePosix.h"
#include "configuration.h"
#ifndef HAS_FREE_RTOS
namespace concurrency
{
BinarySemaphorePosix::BinarySemaphorePosix()
{
}
BinarySemaphorePosix::~BinarySemaphorePosix()
{
}
/**
* Returns false if we timed out
*/
bool BinarySemaphorePosix::take(uint32_t msec)
{
delay(msec); // FIXME
return false;
}
void BinarySemaphorePosix::give()
{
}
IRAM_ATTR void BinarySemaphorePosix::giveFromISR(BaseType_t *pxHigherPriorityTaskWoken)
{
}
} // namespace concurrency
#endif

View File

@@ -0,0 +1,31 @@
#pragma once
#include "configuration.h"
#include "../freertosinc.h"
namespace concurrency
{
#ifndef HAS_FREE_RTOS
class BinarySemaphorePosix
{
// SemaphoreHandle_t semaphore;
public:
BinarySemaphorePosix();
~BinarySemaphorePosix();
/**
* Returns false if we timed out
*/
bool take(uint32_t msec);
void give();
void giveFromISR(BaseType_t *pxHigherPriorityTaskWoken);
};
#endif
}

View File

@@ -1,23 +0,0 @@
#include "NotifiedWorkerThread.h"
#ifdef HAS_FREE_RTOS
namespace concurrency {
/**
* Notify this thread so it can run
*/
void FreeRtosNotifiedWorkerThread::notify(uint32_t v, eNotifyAction action)
{
xTaskNotify(taskHandle, v, action);
}
void FreeRtosNotifiedWorkerThread::block()
{
xTaskNotifyWait(0, // don't clear notification on entry
clearOnRead, &notification, portMAX_DELAY); // Wait forever
}
} // namespace concurrency
#endif

View File

@@ -1,40 +0,0 @@
#pragma once
#include "BaseNotifiedWorkerThread.h"
#ifdef HAS_FREE_RTOS
namespace concurrency {
/**
* @brief A worker thread that waits on a freertos notification
*/
class FreeRtosNotifiedWorkerThread : public BaseNotifiedWorkerThread
{
public:
/**
* Notify this thread so it can run
*/
void notify(uint32_t v = 0, eNotifyAction action = eNoAction);
/**
* Notify from an ISR
*
* This must be inline or IRAM_ATTR on ESP32
*/
inline void notifyFromISR(BaseType_t *highPriWoken, uint32_t v = 0, eNotifyAction action = eNoAction)
{
xTaskNotifyFromISR(taskHandle, v, action, highPriWoken);
}
protected:
/**
* A method that should block execution - either waiting ona queue/mutex or a "task notification"
*/
virtual void block();
};
} // namespace concurrency
#endif

View File

@@ -1,45 +0,0 @@
#include "FreeRtosThread.h"
#ifdef HAS_FREE_RTOS
#include <assert.h>
#ifdef ARDUINO_ARCH_ESP32
#include "esp_task_wdt.h"
#endif
namespace concurrency
{
void FreeRtosThread::start(const char *name, size_t stackSize, uint32_t priority)
{
auto r = xTaskCreate(callRun, name, stackSize, this, priority, &taskHandle);
assert(r == pdPASS);
}
void FreeRtosThread::serviceWatchdog()
{
#ifdef ARDUINO_ARCH_ESP32
esp_task_wdt_reset();
#endif
}
void FreeRtosThread::startWatchdog()
{
#ifdef ARDUINO_ARCH_ESP32
auto r = esp_task_wdt_add(taskHandle);
assert(r == ESP_OK);
#endif
}
void FreeRtosThread::stopWatchdog()
{
#ifdef ARDUINO_ARCH_ESP32
auto r = esp_task_wdt_delete(taskHandle);
assert(r == ESP_OK);
#endif
}
} // namespace concurrency
#endif

View File

@@ -1,44 +0,0 @@
#pragma once
#include "BaseThread.h"
#include "freertosinc.h"
#ifdef HAS_FREE_RTOS
namespace concurrency
{
/**
* @brief Base threading
*/
class FreeRtosThread : public BaseThread
{
protected:
TaskHandle_t taskHandle = NULL;
public:
void start(const char *name, size_t stackSize = 1024, uint32_t priority = tskIDLE_PRIORITY);
virtual ~FreeRtosThread() { vTaskDelete(taskHandle); }
// uint32_t getStackHighwaterMark() { return uxTaskGetStackHighWaterMark(taskHandle); }
protected:
/**
* The method that will be called when start is called.
*/
virtual void doRun() = 0;
/**
* All thread run methods must periodically call serviceWatchdog, or the system will declare them hung and panic.
*
* this only applies after startWatchdog() has been called. If you need to sleep for a long time call stopWatchdog()
*/
void serviceWatchdog();
void startWatchdog();
void stopWatchdog();
};
} // namespace concurrency
#endif

View File

@@ -0,0 +1,35 @@
#include "concurrency/InterruptableDelay.h"
#include "configuration.h"
namespace concurrency
{
InterruptableDelay::InterruptableDelay() {}
InterruptableDelay::~InterruptableDelay() {}
/**
* Returns false if we were interrupted
*/
bool InterruptableDelay::delay(uint32_t msec)
{
// DEBUG_MSG("delay %u ", msec);
// sem take will return false if we timed out (i.e. were not interrupted)
bool r = semaphore.take(msec);
// DEBUG_MSG("interrupt=%d\n", r);
return !r;
}
void InterruptableDelay::interrupt()
{
semaphore.give();
}
IRAM_ATTR void InterruptableDelay::interruptFromISR(BaseType_t *pxHigherPriorityTaskWoken)
{
semaphore.giveFromISR(pxHigherPriorityTaskWoken);
}
} // namespace concurrency

View File

@@ -0,0 +1,42 @@
#pragma once
#include "../freertosinc.h"
#ifdef HAS_FREE_RTOS
#include "concurrency/BinarySemaphoreFreeRTOS.h"
#define BinarySemaphore BinarySemaphoreFreeRTOS
#else
#include "concurrency/BinarySemaphorePosix.h"
#define BinarySemaphore BinarySemaphorePosix
#endif
namespace concurrency
{
/**
* An object that provides delay(msec) like functionality, but can be interrupted by calling interrupt().
*
* Useful for they top level loop() delay call to keep the CPU powered down until our next scheduled event or some external event.
*
* This is implmented for FreeRTOS but should be easy to port to other operating systems.
*/
class InterruptableDelay
{
BinarySemaphore semaphore;
public:
InterruptableDelay();
~InterruptableDelay();
/**
* Returns false if we were interrupted
*/
bool delay(uint32_t msec);
void interrupt();
void interruptFromISR(BaseType_t *pxHigherPriorityTaskWoken);
};
} // namespace concurrency

View File

@@ -0,0 +1,85 @@
#include "NotifiedWorkerThread.h"
#include "configuration.h"
#include <assert.h>
namespace concurrency
{
static bool debugNotification;
/**
* Notify this thread so it can run
*/
bool NotifiedWorkerThread::notify(uint32_t v, bool overwrite)
{
bool r = notifyCommon(v, overwrite);
if (r)
mainDelay.interrupt();
return r;
}
/**
* Notify this thread so it can run
*/
IRAM_ATTR bool NotifiedWorkerThread::notifyCommon(uint32_t v, bool overwrite)
{
if (overwrite || notification == 0) {
enabled = true;
setInterval(0); // Run ASAP
notification = v;
if (debugNotification)
DEBUG_MSG("setting notification %d\n", v);
return true;
} else {
if (debugNotification)
DEBUG_MSG("dropping notification %d\n", v);
return false;
}
}
/**
* Notify from an ISR
*
* This must be inline or IRAM_ATTR on ESP32
*/
IRAM_ATTR bool NotifiedWorkerThread::notifyFromISR(BaseType_t *highPriWoken, uint32_t v, bool overwrite)
{
bool r = notifyCommon(v, overwrite);
if (r)
mainDelay.interruptFromISR(highPriWoken);
return r;
}
/**
* Schedule a notification to fire in delay msecs
*/
bool NotifiedWorkerThread::notifyLater(uint32_t delay, uint32_t v, bool overwrite)
{
bool didIt = notify(v, overwrite);
if (didIt) { // If we didn't already have something queued, override the delay to be larger
setIntervalFromNow(delay); // a new version of setInterval relative to the current time
if (debugNotification)
DEBUG_MSG("delaying notification %u\n", delay);
}
return didIt;
}
int32_t NotifiedWorkerThread::runOnce()
{
auto n = notification;
enabled = false; // Only run once per notification
notification = 0; // clear notification
if (n) {
onNotify(n);
}
return RUN_SAME;
}
} // namespace concurrency

View File

@@ -1,17 +1,50 @@
#pragma once
#include "FreeRtosNotifiedWorkerThread.h"
#include "PosixNotifiedWorkerThread.h"
#include "OSThread.h"
namespace concurrency
{
#ifdef HAS_FREE_RTOS
typedef FreeRtosNotifiedWorkerThread NotifiedWorkerThread;
#endif
/**
* @brief A worker thread that waits on a freertos notification
*/
class NotifiedWorkerThread : public OSThread
{
/**
* The notification that was most recently used to wake the thread. Read from runOnce()
*/
uint32_t notification = 0;
#ifdef __unix__
typedef PosixNotifiedWorkerThread NotifiedWorkerThread;
#endif
public:
NotifiedWorkerThread(const char *name) : OSThread(name) {}
/**
* Notify this thread so it can run
*/
bool notify(uint32_t v, bool overwrite);
/**
* Notify from an ISR
*
* This must be inline or IRAM_ATTR on ESP32
*/
bool notifyFromISR(BaseType_t *highPriWoken, uint32_t v, bool overwrite);
/**
* Schedule a notification to fire in delay msecs
*/
bool notifyLater(uint32_t delay, uint32_t v, bool overwrite);
protected:
virtual void onNotify(uint32_t notification) = 0;
virtual int32_t runOnce();
private:
/**
* Notify this thread so it can run
*/
bool notifyCommon(uint32_t v, bool overwrite);
};
} // namespace concurrency

View File

@@ -0,0 +1,79 @@
#include "OSThread.h"
#include "configuration.h"
#include <assert.h>
namespace concurrency
{
/// Show debugging info for disabled threads
bool OSThread::showDisabled;
/// Show debugging info for threads when we run them
bool OSThread::showRun = false;
/// Show debugging info for threads we decide not to run;
bool OSThread::showWaiting = false;
ThreadController mainController, timerController;
InterruptableDelay mainDelay;
void OSThread::setup()
{
mainController.ThreadName = "mainController";
timerController.ThreadName = "timerController";
}
OSThread::OSThread(const char *_name, uint32_t period, ThreadController *_controller)
: Thread(NULL, period), controller(_controller)
{
ThreadName = _name;
if (controller)
controller->add(this);
}
OSThread::~OSThread()
{
if (controller)
controller->remove(this);
}
/**
* Wait a specified number msecs starting from the current time (rather than the last time we were run)
*/
void OSThread::setIntervalFromNow(unsigned long _interval)
{
// Save interval
interval = _interval;
// Cache the next run based on the last_run
_cached_next_run = millis() + interval;
}
bool OSThread::shouldRun(unsigned long time)
{
bool r = Thread::shouldRun(time);
if (showRun && r)
DEBUG_MSG("Thread %s: run\n", ThreadName.c_str());
if (showWaiting && enabled && !r)
DEBUG_MSG("Thread %s: wait %lu\n", ThreadName.c_str(), interval);
if (showDisabled && !enabled)
DEBUG_MSG("Thread %s: disabled\n", ThreadName.c_str());
return r;
}
void OSThread::run()
{
auto newDelay = runOnce();
runned();
if (newDelay >= 0)
setInterval(newDelay);
}
} // namespace concurrency

View File

@@ -0,0 +1,70 @@
#pragma once
#include <cstdlib>
#include <stdint.h>
#include "Thread.h"
#include "ThreadController.h"
#include "concurrency/InterruptableDelay.h"
namespace concurrency
{
extern ThreadController mainController, timerController;
extern InterruptableDelay mainDelay;
#define RUN_SAME -1
/**
* @brief Base threading
*
* This is a pseudo threading layer that is super easy to port, well suited to our slow network and very ram & power efficient.
*
* TODO FIXME @geeksville
*
* move more things into OSThreads
* remove lock/lockguard
*
* move typedQueue into concurrency
* remove freertos from typedqueue
*/
class OSThread : public Thread
{
ThreadController *controller;
/// Show debugging info for disabled threads
static bool showDisabled;
/// Show debugging info for threads when we run them
static bool showRun;
/// Show debugging info for threads we decide not to run;
static bool showWaiting;
public:
OSThread(const char *name, uint32_t period = 0, ThreadController *controller = &mainController);
virtual ~OSThread();
virtual bool shouldRun(unsigned long time);
static void setup();
/**
* Wait a specified number msecs starting from the current time (rather than the last time we were run)
*/
void setIntervalFromNow(unsigned long _interval);
protected:
/**
* The method that will be called each time our thread gets a chance to run
*
* Returns desired period for next invocation (or RUN_SAME for no change)
*/
virtual int32_t runOnce() = 0;
// Do not override this
virtual void run();
};
} // namespace concurrency

View File

@@ -1,26 +1,24 @@
#pragma once
#include "PeriodicTask.h"
#include "concurrency/OSThread.h"
namespace concurrency {
namespace concurrency
{
/**
* @brief Periodically invoke a callback. This just provides C-style callback conventions
* @brief Periodically invoke a callback. This just provides C-style callback conventions
* rather than a virtual function - FIXME, remove?
*/
class Periodic : public PeriodicTask
class Periodic : public OSThread
{
uint32_t (*callback)();
int32_t (*callback)();
public:
// callback returns the period for the next callback invocation (or 0 if we should no longer be called)
Periodic(uint32_t (*_callback)()) : callback(_callback) {}
Periodic(const char *name, int32_t (*_callback)()) : OSThread(name), callback(_callback) {}
protected:
void doTask() {
uint32_t p = callback();
setPeriod(p);
}
int32_t runOnce() { return callback(); }
};
} // namespace concurrency

View File

@@ -1,34 +0,0 @@
#include "PeriodicScheduler.h"
#include "PeriodicTask.h"
#include "LockGuard.h"
namespace concurrency {
/// call this from loop
void PeriodicScheduler::loop()
{
LockGuard lg(&lock);
uint32_t now = millis();
for (auto t : tasks) {
if (t->period && (now - t->lastMsec) >= t->period) {
t->doTask();
t->lastMsec = now;
}
}
}
void PeriodicScheduler::schedule(PeriodicTask *t)
{
LockGuard lg(&lock);
tasks.insert(t);
}
void PeriodicScheduler::unschedule(PeriodicTask *t)
{
LockGuard lg(&lock);
tasks.erase(t);
}
} // namespace concurrency

View File

@@ -1,40 +0,0 @@
#pragma once
#include "Lock.h"
#include <cstdint>
#include <unordered_set>
namespace concurrency {
class PeriodicTask;
/**
* @brief Runs all PeriodicTasks in the system. Currently called from main loop()
* but eventually should be its own thread blocked on a freertos timer.
*/
class PeriodicScheduler
{
friend class PeriodicTask;
/**
* This really should be some form of heap, and when the period gets changed on a task it should get
* rescheduled in that heap. Currently it is just a dumb array and everytime we run loop() we check
* _every_ tasks. If it was a heap we'd only have to check the first task.
*/
std::unordered_set<PeriodicTask *> tasks;
// Protects the above variables.
Lock lock;
public:
/// Run any next tasks which are due for execution
void loop();
private:
void schedule(PeriodicTask *t);
void unschedule(PeriodicTask *t);
};
extern PeriodicScheduler periodicScheduler;
} // namespace concurrency

View File

@@ -1,16 +0,0 @@
#include "PeriodicTask.h"
#include "Periodic.h"
#include "LockGuard.h"
namespace concurrency {
PeriodicScheduler periodicScheduler;
PeriodicTask::PeriodicTask(uint32_t initialPeriod) : period(initialPeriod) {}
void PeriodicTask::setup()
{
periodicScheduler.schedule(this);
}
} // namespace concurrency

View File

@@ -1,56 +0,0 @@
#pragma once
#include <Arduino.h>
#include "PeriodicScheduler.h"
namespace concurrency {
/**
* @brief A base class for tasks that want their doTask() method invoked periodically
*
* @todo currently just syntatic sugar for polling in loop (you must call .loop), but eventually
* generalize with the freertos scheduler so we can save lots of power by having everything either in
* something like this or triggered off of an irq.
*/
class PeriodicTask
{
friend class PeriodicScheduler;
uint32_t lastMsec = 0;
uint32_t period = 1; // call soon after creation
public:
virtual ~PeriodicTask() { periodicScheduler.unschedule(this); }
/**
* Constructor (will schedule with the global PeriodicScheduler)
*/
PeriodicTask(uint32_t initialPeriod = 1);
/**
* MUST be be called once at startup (but after threading is running - i.e. not from a constructor)
*/
void setup();
/**
* Set a new period in msecs (can be called from doTask or elsewhere and the scheduler will cope)
* While zero this task is disabled and will not run
*/
void setPeriod(uint32_t p)
{
lastMsec = millis(); // reset starting from now
period = p;
}
uint32_t getPeriod() const { return period; }
/**
* Syntatic sugar for suspending tasks
*/
void disable() { setPeriod(0); }
protected:
virtual void doTask() = 0;
};
} // namespace concurrency

View File

@@ -1,19 +0,0 @@
#include "PosixNotifiedWorkerThread.h"
#ifdef __unix__
#include <Utility.h>
using namespace concurrency;
/**
* Notify this thread so it can run
*/
void PosixNotifiedWorkerThread::notify(uint32_t v, eNotifyAction action) NOT_IMPLEMENTED("notify");
/**
* A method that should block execution - either waiting ona queue/mutex or a "task notification"
*/
void PosixNotifiedWorkerThread::block() NOT_IMPLEMENTED("block");
#endif

View File

@@ -1,26 +0,0 @@
#pragma once
#include "BaseNotifiedWorkerThread.h"
namespace concurrency {
/**
* @brief A worker thread that waits on a freertos notification
*/
class PosixNotifiedWorkerThread : public BaseNotifiedWorkerThread
{
public:
/**
* Notify this thread so it can run
*/
void notify(uint32_t v = 0, eNotifyAction action = eNoAction);
protected:
/**
* A method that should block execution - either waiting ona queue/mutex or a "task notification"
*/
virtual void block();
};
} // namespace concurrency

View File

@@ -1,33 +0,0 @@
#pragma once
#include "BaseThread.h"
#ifdef __unix__
namespace concurrency
{
/**
* @brief Base threading
*/
class PosixThread : public BaseThread
{
protected:
public:
void start(const char *name, size_t stackSize = 1024, uint32_t priority = tskIDLE_PRIORITY) {}
virtual ~PosixThread() {}
// uint32_t getStackHighwaterMark() { return uxTaskGetStackHighWaterMark(taskHandle); }
protected:
/**
* The method that will be called when start is called.
*/
virtual void doRun() = 0;
};
} // namespace concurrency
#endif

View File

@@ -1,17 +0,0 @@
#pragma once
#include "FreeRtosThread.h"
#include "PosixThread.h"
namespace concurrency
{
#ifdef HAS_FREE_RTOS
typedef FreeRtosThread Thread;
#endif
#ifdef __unix__
typedef PosixThread Thread;
#endif
} // namespace concurrency

View File

@@ -1,31 +0,0 @@
#include "WorkerThread.h"
namespace concurrency {
void WorkerThread::doRun()
{
startWatchdog();
while (!wantExit) {
stopWatchdog();
block();
startWatchdog();
// no need - startWatchdog is guaranteed to give us one full watchdog interval
// serviceWatchdog(); // Let our loop worker have one full watchdog interval (at least) to run
#ifdef DEBUG_STACK
static uint32_t lastPrint = 0;
if (millis() - lastPrint > 10 * 1000L) {
lastPrint = millis();
meshtastic::printThreadInfo("net");
}
#endif
loop();
}
stopWatchdog();
}
} // namespace concurrency

View File

@@ -1,29 +0,0 @@
#pragma once
#include "Thread.h"
namespace concurrency {
/**
* @brief This wraps threading (FreeRTOS for now) with a blocking API intended for efficiently converting
* old-school arduino loop() code. Use as a mixin base class for the classes you want to convert.
*
* @link https://www.freertos.org/RTOS_Task_Notification_As_Mailbox.html
*/
class WorkerThread : public Thread
{
protected:
/**
* A method that should block execution - either waiting ona queue/mutex or a "task notification"
*/
virtual void block() = 0;
virtual void loop() = 0;
/**
* The method that will be called when start is called.
*/
virtual void doRun();
};
} // namespace concurrency

View File

@@ -31,9 +31,12 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
// If app version is not specified we assume we are not being invoked by the build script
#ifndef APP_VERSION
#error APP_VERSION, HW_VERSION, and HW_VERSION_countryname must be set by the build environment
//#define APP_VERSION 0.0.0 // this def normally comes from build-all.sh
//#define HW_VERSION 1.0 - US // normally comes from build-all.sh and contains the region code
#error APP_VERSION must be set by the build environment
#endif
// If app version is not specified we assume we are not being invoked by the build script
#ifndef HW_VERSION
#error HW_VERSION, and HW_VERSION_countryname must be set by the build environment
#endif
// -----------------------------------------------------------------------------
@@ -139,6 +142,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
// -----------------------------------------------------------------------------
#define SSD1306_ADDRESS 0x3C
#define ST7567_ADDRESS 0x3F
// The SH1106 controller is almost, but not quite, the same as SSD1306
// Define this if you know you have that controller or your "SSD1306" misbehaves.
@@ -146,7 +150,10 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
// Flip the screen upside down by default as it makes more sense on T-BEAM
// devices. Comment this out to not rotate screen 180 degrees.
#define FLIP_SCREEN_VERTICALLY
#define SCREEN_FLIP_VERTICALLY
// Define if screen should be mirrored left to right
// #define SCREEN_MIRROR
// -----------------------------------------------------------------------------
// GPS
@@ -400,8 +407,11 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
// Always include the SEGGER code on NRF52 - because useful for debugging
#include "SEGGER_RTT.h"
// The channel we send stdout data to
#define SEGGER_STDOUT_CH 0
// Debug printing to segger console
#define SEGGER_MSG(...) SEGGER_RTT_printf(0, __VA_ARGS__)
#define SEGGER_MSG(...) SEGGER_RTT_printf(SEGGER_STDOUT_CH, __VA_ARGS__)
// If we are not on a NRF52840 (which has built in USB-ACM serial support) and we don't have serial pins hooked up, then we MUST
// use SEGGER for debug output
@@ -430,3 +440,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#define GPS_POWER_CTRL_CH 3
#define LORA_POWER_CTRL_CH 2
// Default Bluetooth PIN
#define defaultBLEPin 123456

View File

@@ -6,6 +6,7 @@
#include "RadioLibInterface.h"
#include "configuration.h"
#include "nimble/BluetoothUtil.h"
#include "NodeDB.h"
#include <CRC32.h>
#include <Update.h>
@@ -16,6 +17,8 @@ static CRC32 crc;
static uint32_t rebootAtMsec = 0; // If not zero we will reboot at this time (used to reboot shortly after the update completes)
static uint32_t updateExpectedSize, updateActualSize;
static uint8_t update_result;
static uint8_t update_region;
static concurrency::Lock *updateLock;
@@ -32,8 +35,8 @@ int update_size_callback(uint16_t conn_handle, uint16_t attr_handle, struct ble_
crc.reset();
if (Update.isRunning())
Update.abort();
bool canBegin = Update.begin(updateExpectedSize);
DEBUG_MSG("Setting update size %u, result %d\n", updateExpectedSize, canBegin);
bool canBegin = Update.begin(updateExpectedSize, update_region);
DEBUG_MSG("Setting region %d update size %u, result %d\n", update_region, updateExpectedSize, canBegin);
if (!canBegin) {
// Indicate failure by forcing the size to 0 (client will read it back)
updateExpectedSize = 0;
@@ -72,13 +75,11 @@ int update_data_callback(uint16_t conn_handle, uint16_t attr_handle, struct ble_
crc.update(data, len);
Update.write(data, len);
updateActualSize += len;
powerFSM.trigger(EVENT_RECEIVED_TEXT_MSG); // Not exactly correct, but we want to force the device to not sleep now
powerFSM.trigger(EVENT_CONTACT_FROM_PHONE);
return 0;
}
static uint8_t update_result;
/// Handle writes to crc32
int update_crc32_callback(uint16_t conn_handle, uint16_t attr_handle, struct ble_gatt_access_ctxt *ctxt, void *arg)
{
@@ -100,8 +101,14 @@ int update_crc32_callback(uint16_t conn_handle, uint16_t attr_handle, struct ble
result = 0xe0; // FIXME, use real error codes
} else {
if (Update.end()) {
DEBUG_MSG("OTA done, rebooting in 5 seconds!\n");
rebootAtMsec = millis() + 5000;
if (update_region == U_SPIFFS) {
DEBUG_MSG("SPIFFS updated!\n");
nodeDB.saveToDisk(); // Since we just wiped spiffs, we need to save our current state
}
else {
DEBUG_MSG("Appload updated, rebooting in 5 seconds!\n");
rebootAtMsec = millis() + 5000;
}
} else {
DEBUG_MSG("Error Occurred. Error #: %d\n", Update.getError());
}
@@ -125,6 +132,11 @@ int update_result_callback(uint16_t conn_handle, uint16_t attr_handle, struct bl
return chr_readwrite8(&update_result, sizeof(update_result), ctxt);
}
int update_region_callback(uint16_t conn_handle, uint16_t attr_handle, struct ble_gatt_access_ctxt *ctxt, void *arg)
{
return chr_readwrite8(&update_region, sizeof(update_region), ctxt);
}
void bluetoothRebootCheck()
{
if (rebootAtMsec && millis() > rebootAtMsec) {

View File

@@ -14,10 +14,11 @@ int update_size_callback(uint16_t conn_handle, uint16_t attr_handle, struct ble_
int update_data_callback(uint16_t conn_handle, uint16_t attr_handle, struct ble_gatt_access_ctxt *ctxt, void *arg);
int update_result_callback(uint16_t conn_handle, uint16_t attr_handle, struct ble_gatt_access_ctxt *ctxt, void *arg);
int update_crc32_callback(uint16_t conn_handle, uint16_t attr_handle, struct ble_gatt_access_ctxt *ctxt, void *arg);
int update_region_callback(uint16_t conn_handle, uint16_t attr_handle, struct ble_gatt_access_ctxt *ctxt, void *arg);
extern const struct ble_gatt_svc_def gatt_update_svcs[];
extern const ble_uuid128_t update_result_uuid;
extern const ble_uuid128_t update_result_uuid, update_region_uuid;
extern int16_t updateResultHandle;

View File

@@ -23,6 +23,10 @@ const ble_uuid128_t update_crc32_uuid =
const ble_uuid128_t update_result_uuid =
BLE_UUID128_INIT(0x77, 0x2c, 0x43, 0x37, 0x09, 0x21, 0x4a, 0xac, 0x24, 0x44, 0x11, 0x74, 0x62, 0x48, 0x13, 0x5e);
// "5e134862-7411-4424-ac4a-210937432c67" write
const ble_uuid128_t update_region_uuid =
BLE_UUID128_INIT(0x67, 0x2c, 0x43, 0x37, 0x09, 0x21, 0x4a, 0xac, 0x24, 0x44, 0x11, 0x74, 0x62, 0x48, 0x13, 0x5e);
const struct ble_gatt_svc_def gatt_update_svcs[] = {
{
/*** Service: Security test. */
@@ -47,9 +51,14 @@ const struct ble_gatt_svc_def gatt_update_svcs[] = {
},
{
.uuid = &update_result_uuid.u,
.access_cb = update_size_callback,
.access_cb = update_result_callback,
.flags = BLE_GATT_CHR_F_READ | BLE_GATT_CHR_F_READ_AUTHEN | BLE_GATT_CHR_F_NOTIFY,
},
{
.uuid = &update_region_uuid.u,
.access_cb = update_region_callback,
.flags = BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_WRITE_AUTHEN,
},
{
0, /* No more characteristics in this service. */
}},

View File

@@ -40,7 +40,7 @@ void WiFiServerAPI::loop()
#define MESHTASTIC_PORTNUM 4403
WiFiServerPort::WiFiServerPort() : WiFiServer(MESHTASTIC_PORTNUM) {}
WiFiServerPort::WiFiServerPort() : WiFiServer(MESHTASTIC_PORTNUM), concurrency::OSThread("ApiServer") {}
void WiFiServerPort::init()
{
@@ -48,7 +48,7 @@ void WiFiServerPort::init()
begin();
}
void WiFiServerPort::loop()
int32_t WiFiServerPort::runOnce()
{
auto client = available();
if (client) {
@@ -59,7 +59,10 @@ void WiFiServerPort::loop()
openAPI = new WiFiServerAPI(client);
}
if (openAPI)
if (openAPI) {
// Allow idle processing so the API can read from its incoming stream
openAPI->loop();
return 0; // run fast while our API server is running
} else
return 100; // only check occasionally for incoming connections
}

View File

@@ -1,6 +1,7 @@
#pragma once
#include "StreamAPI.h"
#include "concurrency/OSThread.h"
#include <WiFi.h>
/**
@@ -27,7 +28,7 @@ class WiFiServerAPI : public StreamAPI
/**
* Listens for incoming connections and does accepts and creates instances of WiFiServerAPI as needed
*/
class WiFiServerPort : public WiFiServer
class WiFiServerPort : public WiFiServer, private concurrency::OSThread
{
/** The currently open port
*
@@ -41,5 +42,5 @@ class WiFiServerPort : public WiFiServer
void init();
void loop();
int32_t runOnce();
};

View File

@@ -9,6 +9,7 @@
#include "utils.h"
#include <nvs.h>
#include <nvs_flash.h>
#include <driver/rtc_io.h>
void getMacAddr(uint8_t *dmac)
{
@@ -86,4 +87,58 @@ void esp32Loop()
// for debug printing
// radio.radioIf.canSleep();
}
void cpuDeepSleep(uint64_t msecToWake)
{
/*
Some ESP32 IOs have internal pullups or pulldowns, which are enabled by default.
If an external circuit drives this pin in deep sleep mode, current consumption may
increase due to current flowing through these pullups and pulldowns.
To isolate a pin, preventing extra current draw, call rtc_gpio_isolate() function.
For example, on ESP32-WROVER module, GPIO12 is pulled up externally.
GPIO12 also has an internal pulldown in the ESP32 chip. This means that in deep sleep,
some current will flow through these external and internal resistors, increasing deep
sleep current above the minimal possible value.
Note: we don't isolate pins that are used for the LORA, LED, i2c, spi or the wake button
*/
static const uint8_t rtcGpios[] = {/* 0, */ 2,
/* 4, */
#ifndef USE_JTAG
13,
/* 14, */ /* 15, */
#endif
/* 25, */ 26, /* 27, */
32, 33, 34, 35,
36, 37
/* 38, 39 */};
for (int i = 0; i < sizeof(rtcGpios); i++)
rtc_gpio_isolate((gpio_num_t)rtcGpios[i]);
// FIXME, disable internal rtc pullups/pulldowns on the non isolated pins. for inputs that we aren't using
// to detect wake and in normal operation the external part drives them hard.
// We want RTC peripherals to stay on
esp_sleep_pd_config(ESP_PD_DOMAIN_RTC_PERIPH, ESP_PD_OPTION_ON);
#ifdef BUTTON_PIN
// Only GPIOs which are have RTC functionality can be used in this bit map: 0,2,4,12-15,25-27,32-39.
uint64_t gpioMask = (1ULL << BUTTON_PIN);
#ifdef BUTTON_NEED_PULLUP
gpio_pullup_en((gpio_num_t)BUTTON_PIN);
#endif
// Not needed because both of the current boards have external pullups
// FIXME change polarity in hw so we can wake on ANY_HIGH instead - that would allow us to use all three buttons (instead of
// just the first) gpio_pullup_en((gpio_num_t)BUTTON_PIN);
esp_sleep_enable_ext1_wakeup(gpioMask, ESP_EXT1_WAKEUP_ALL_LOW);
#endif
esp_sleep_enable_timer_wakeup(msecToWake * 1000ULL); // call expects usecs
esp_deep_sleep_start(); // TBD mA sleep current (battery)
}

View File

@@ -65,9 +65,8 @@ void Air530GPS::sendCommand(const char *cmd) {
}
void Air530GPS::sleep() {
NMEAGPS::sleep();
#ifdef PIN_GPS_WAKE
digitalWrite(PIN_GPS_WAKE, 0);
pinMode(PIN_GPS_WAKE, OUTPUT);
sendCommand("$PGKC105,4");
#endif
}
@@ -76,10 +75,7 @@ void Air530GPS::sleep() {
void Air530GPS::wake()
{
#if 1
#ifdef PIN_GPS_WAKE
digitalWrite(PIN_GPS_WAKE, 1);
pinMode(PIN_GPS_WAKE, OUTPUT);
#endif
NMEAGPS::wake();
#else
// For power testing - keep GPS sleeping forever
sleep();

View File

@@ -10,7 +10,7 @@
#ifdef GPS_RX_PIN
HardwareSerial _serial_gps_real(GPS_SERIAL_NUM);
HardwareSerial *GPS::_serial_gps = &_serial_gps_real;
#elif defined(NRF52840_XXAA)
#elif defined(NRF52840_XXAA) || defined(NRF52833_XXAA)
// Assume NRF52840
HardwareSerial *GPS::_serial_gps = &Serial1;
#else
@@ -25,17 +25,92 @@ uint8_t GPS::i2cAddress = 0;
GPS *gps;
/// Multiple GPS instances might use the same serial port (in sequence), but we can
/// only init that port once.
static bool didSerialInit;
bool GPS::setupGPS()
{
if (_serial_gps && !didSerialInit) {
didSerialInit = true;
#ifdef GPS_RX_PIN
_serial_gps->begin(GPS_BAUDRATE, SERIAL_8N1, GPS_RX_PIN, GPS_TX_PIN);
#else
_serial_gps->begin(GPS_BAUDRATE);
#endif
#ifndef NO_ESP32
_serial_gps->setRxBufferSize(2048); // the default is 256
#endif
}
return true;
}
bool GPS::setup()
{
// Master power for the GPS
#ifdef PIN_GPS_EN
digitalWrite(PIN_GPS_EN, PIN_GPS_EN);
pinMode(PIN_GPS_EN, OUTPUT);
#endif
#ifdef PIN_GPS_RESET
digitalWrite(PIN_GPS_RESET, 1); // assert for 10ms
pinMode(PIN_GPS_RESET, OUTPUT);
delay(10);
digitalWrite(PIN_GPS_RESET, 0);
#endif
setAwake(true); // Wake GPS power before doing any init
bool ok = setupGPS();
if (ok)
if (ok) {
notifySleepObserver.observe(&notifySleep);
notifyDeepSleepObserver.observe(&notifyDeepSleep);
}
return ok;
}
// Allow defining the polarity of the WAKE output. default is active high
#ifndef GPS_WAKE_ACTIVE
#define GPS_WAKE_ACTIVE 1
#endif
void GPS::wake()
{
#ifdef PIN_GPS_WAKE
digitalWrite(PIN_GPS_WAKE, GPS_WAKE_ACTIVE);
pinMode(PIN_GPS_WAKE, OUTPUT);
#endif
}
void GPS::sleep() {
#ifdef PIN_GPS_WAKE
digitalWrite(PIN_GPS_WAKE, GPS_WAKE_ACTIVE ? 0 : 1);
pinMode(PIN_GPS_WAKE, OUTPUT);
#endif
}
/// Record that we have a GPS
void GPS::setConnected()
{
if (!hasGPS) {
hasGPS = true;
shouldPublish = true;
}
}
void GPS::setNumSatellites(uint8_t n)
{
if (n != numSatellites) {
numSatellites = n;
shouldPublish = true;
}
}
/**
* Switch the GPS into a mode where we are actively looking for a lock, or alternatively switch GPS into a low power mode
*
@@ -114,19 +189,23 @@ uint32_t GPS::getSleepTime() const
void GPS::publishUpdate()
{
DEBUG_MSG("publishing GPS lock=%d\n", hasLock());
if (shouldPublish) {
shouldPublish = false;
// Notify any status instances that are observing us
const meshtastic::GPSStatus status =
meshtastic::GPSStatus(hasLock(), isConnected, latitude, longitude, altitude, dop, heading, numSatellites);
newStatus.notifyObservers(&status);
DEBUG_MSG("publishing GPS lock=%d\n", hasLock());
// Notify any status instances that are observing us
const meshtastic::GPSStatus status =
meshtastic::GPSStatus(hasLock(), isConnected(), latitude, longitude, altitude, dop, heading, numSatellites);
newStatus.notifyObservers(&status);
}
}
void GPS::loop()
int32_t GPS::runOnce()
{
if (whileIdle()) {
// if we have received valid NMEA claim we are connected
isConnected = true;
setConnected();
}
// If we are overdue for an update, turn on the GPS and at least publish the current status
@@ -147,8 +226,17 @@ void GPS::loop()
}
// If we've already set time from the GPS, no need to ask the GPS
bool gotTime = (getRTCQuality() >= RTCQualityGPS) || lookForTime();
bool gotTime = (getRTCQuality() >= RTCQualityGPS);
if (!gotTime && lookForTime()) { // Note: we count on this && short-circuiting and not resetting the RTC time
gotTime = true;
shouldPublish = true;
}
bool gotLoc = lookForLocation();
if (gotLoc && !hasValidLocation) { // declare that we have location ASAP
hasValidLocation = true;
shouldPublish = true;
}
// We've been awake too long - force sleep
auto wakeTime = getWakeTime();
@@ -158,8 +246,6 @@ void GPS::loop()
// or if we got a time and we are in GpsOpTimeOnly mode
// DEBUG_MSG("gotLoc %d, tooLong %d, gotTime %d\n", gotLoc, tooLong, gotTime);
if ((gotLoc && gotTime) || tooLong || (gotTime && getGpsOp() == GpsOperation_GpsOpTimeOnly)) {
if (gotLoc)
hasValidLocation = true;
if (tooLong) {
// we didn't get a location during this ack window, therefore declare loss of lock
@@ -167,9 +253,16 @@ void GPS::loop()
}
setAwake(false);
publishUpdate(); // publish our update for this just finished acquisition window
shouldPublish = true; // publish our update for this just finished acquisition window
}
}
// If state has changed do a publish
publishUpdate();
// 9600bps is approx 1 byte per msec, so considering our buffer size we never need to wake more often than 200ms
// if not awake we can run super infrquently (once every 5 secs?) to see if we need to wake.
return isAwake ? 100 : 5000;
}
void GPS::forceWake(bool on)
@@ -190,7 +283,19 @@ void GPS::forceWake(bool on)
/// Prepare the GPS for the cpu entering deep or light sleep, expect to be gone for at least 100s of msecs
int GPS::prepareSleep(void *unused)
{
DEBUG_MSG("GPS prepare sleep!\n");
forceWake(false);
return 0;
}
/// Prepare the GPS for the cpu entering deep or light sleep, expect to be gone for at least 100s of msecs
int GPS::prepareDeepSleep(void *unused)
{
DEBUG_MSG("GPS deep sleep!\n");
// For deep sleep we also want abandon any lock attempts (because we want minimum power)
setAwake(false);
return 0;
}

View File

@@ -2,6 +2,7 @@
#include "GPSStatus.h"
#include "Observer.h"
#include "concurrency/OSThread.h"
// Generate a string representation of DOP
const char *getDOPString(uint32_t dop);
@@ -11,7 +12,7 @@ const char *getDOPString(uint32_t dop);
*
* When new data is available it will notify observers.
*/
class GPS
class GPS : private concurrency::OSThread
{
private:
uint32_t lastWakeStartMsec = 0, lastSleepStartMsec = 0, lastWhileActiveMsec = 0;
@@ -22,9 +23,15 @@ class GPS
bool wakeAllowed = true; // false if gps must be forced to sleep regardless of what time it is
CallbackObserver<GPS, void *> notifySleepObserver = CallbackObserver<GPS, void *>(this, &GPS::prepareSleep);
bool shouldPublish = false; // If we've changed GPS state, this will force a publish the next loop()
bool hasGPS = false; // Do we have a GPS we are talking to
uint8_t numSatellites = 0;
CallbackObserver<GPS, void *> notifySleepObserver = CallbackObserver<GPS, void *>(this, &GPS::prepareSleep);
CallbackObserver<GPS, void *> notifyDeepSleepObserver = CallbackObserver<GPS, void *>(this, &GPS::prepareDeepSleep);
protected:
public:
/** If !NULL we will use this serial port to construct our GPS */
static HardwareSerial *_serial_gps;
@@ -37,9 +44,8 @@ class GPS
uint32_t dop = 0; // Diminution of position; PDOP where possible (UBlox), HDOP otherwise (TinyGPS) in 10^2 units (needs
// scaling before use)
uint32_t heading = 0; // Heading of motion, in degrees * 10^-5
uint32_t numSatellites = 0;
bool isConnected = false; // Do we have a GPS we are talking to
GPS() : concurrency::OSThread("GPS") {}
virtual ~GPS() {} // FIXME, we really should unregister our sleep observer
@@ -51,11 +57,12 @@ class GPS
*/
virtual bool setup();
virtual void loop();
/// Returns ture if we have acquired GPS lock.
bool hasLock() const { return hasValidLocation; }
/// Return true if we are connected to a GPS
bool isConnected() const { return hasGPS; }
/**
* Restart our lock attempt - try to get and broadcast a GPS reading ASAP
* called after the CPU wakes from light-sleep state
@@ -66,13 +73,13 @@ class GPS
protected:
/// Do gps chipset specific init, return true for success
virtual bool setupGPS() = 0;
virtual bool setupGPS();
/// If possible force the GPS into sleep/low power mode
virtual void sleep() {}
virtual void sleep();
/// wake the GPS into normal operation mode
virtual void wake() {}
virtual void wake();
/** Subclasses should look for serial rx characters here and feed it to their GPS parser
*
@@ -99,11 +106,20 @@ class GPS
*/
virtual bool lookForLocation() = 0;
/// Record that we have a GPS
void setConnected();
void setNumSatellites(uint8_t n);
private:
/// Prepare the GPS for the cpu entering deep or light sleep, expect to be gone for at least 100s of msecs
/// always returns 0 to indicate okay to sleep
int prepareSleep(void *unused);
/// Prepare the GPS for the cpu entering deep sleep, expect to be gone for at least 100s of msecs
/// always returns 0 to indicate okay to sleep
int prepareDeepSleep(void *unused);
/**
* Switch the GPS into a mode where we are actively looking for a lock, or alternatively switch GPS into a low power mode
*
@@ -125,6 +141,8 @@ class GPS
* Tell users we have new GPS readings
*/
void publishUpdate();
virtual int32_t runOnce();
};
extern GPS *gps;

View File

@@ -1,6 +1,6 @@
#include "NMEAGPS.h"
#include "configuration.h"
#include "RTC.h"
#include "configuration.h"
static int32_t toDegInt(RawDegrees d)
{
@@ -13,6 +13,8 @@ static int32_t toDegInt(RawDegrees d)
bool NMEAGPS::setupGPS()
{
GPS::setupGPS();
#ifdef PIN_GPS_PPS
// pulse per second
// FIXME - move into shared GPS code
@@ -32,7 +34,7 @@ bool NMEAGPS::lookForTime()
{
auto ti = reader.time;
auto d = reader.date;
if (ti.isUpdated() && ti.isValid() && d.isValid()) {
if (ti.isValid() && d.isValid()) { // Note: we don't check for updated, because we'll only be called if needed
/* Convert to unix time
The Unix epoch (or Unix time or POSIX time or Unix timestamp) is the number of seconds that have elapsed since January 1, 1970
(midnight UTC/GMT), not counting leap seconds (in ISO 8601: 1970-01-01T00:00:00Z).
@@ -65,27 +67,27 @@ bool NMEAGPS::lookForLocation()
// uint8_t fixtype = reader.fixQuality();
// hasValidLocation = ((fixtype >= 1) && (fixtype <= 5));
if (reader.satellites.isUpdated()) {
setNumSatellites(reader.satellites.value());
}
// Diminution of precision (an accuracy metric) is reported in 10^2 units, so we need to scale down when we use it
if (reader.hdop.isUpdated()) {
dop = reader.hdop.value();
}
if (reader.course.isUpdated()) {
heading = reader.course.value() * 1e3; // Scale the heading (in degrees * 10^-2) to match the expected degrees * 10^-5
}
if (reader.altitude.isUpdated())
altitude = reader.altitude.meters();
if (reader.location.isUpdated()) {
if (reader.altitude.isValid())
altitude = reader.altitude.meters();
if (reader.location.isValid()) {
auto loc = reader.location.value();
latitude = toDegInt(loc.lat);
longitude = toDegInt(loc.lng);
foundLocation = true;
}
// Diminution of precision (an accuracy metric) is reported in 10^2 units, so we need to scale down when we use it
if (reader.hdop.isValid()) {
dop = reader.hdop.value();
}
if (reader.course.isValid()) {
heading = reader.course.value() * 1e3; // Scale the heading (in degrees * 10^-2) to match the expected degrees * 10^-5
}
if (reader.satellites.isValid()) {
numSatellites = reader.satellites.value();
}
auto loc = reader.location.value();
latitude = toDegInt(loc.lat);
longitude = toDegInt(loc.lng);
foundLocation = true;
// expect gps pos lat=37.520825, lon=-122.309162, alt=158
DEBUG_MSG("new NMEA GPS pos lat=%f, lon=%f, alt=%d, hdop=%g, heading=%f\n", latitude * 1e-7, longitude * 1e-7, altitude,

View File

@@ -1,6 +1,5 @@
#pragma once
#include "../concurrency/PeriodicTask.h"
#include "GPS.h"
#include "Observer.h"
#include "TinyGPS++.h"

View File

@@ -71,7 +71,7 @@ uint32_t getTime()
return ((millis() - timeStartMsec) / 1000) + zeroOffsetSecs;
}
uint32_t getValidTime()
uint32_t getValidTime(RTCQuality minQuality)
{
return (currentQuality >= RTCQualityFromNet) ? getTime() : 0;
return (currentQuality >= minQuality) ? getTime() : 0;
}

View File

@@ -25,6 +25,6 @@ bool perhapsSetRTC(RTCQuality q, struct tm &t);
uint32_t getTime();
/// Return time since 1970 in secs. If quality is RTCQualityNone return zero
uint32_t getValidTime();
uint32_t getValidTime(RTCQuality minQuality);
void readFromRTC();

View File

@@ -8,34 +8,28 @@ UBloxGPS::UBloxGPS() {}
bool UBloxGPS::tryConnect()
{
isConnected = false;
bool c = false;
if (_serial_gps)
isConnected = ublox.begin(*_serial_gps);
c = ublox.begin(*_serial_gps);
if (!isConnected && i2cAddress) {
if (!c && i2cAddress) {
extern bool neo6M; // Super skanky - if we are talking to the device i2c we assume it is a neo7 on a RAK815, which
// supports the newer API
neo6M = true;
isConnected = ublox.begin(Wire, i2cAddress);
c = ublox.begin(Wire, i2cAddress);
}
return isConnected;
if (c)
setConnected();
return c;
}
bool UBloxGPS::setupGPS()
{
if (_serial_gps) {
#ifdef GPS_RX_PIN
_serial_gps->begin(GPS_BAUDRATE, SERIAL_8N1, GPS_RX_PIN, GPS_TX_PIN);
#else
_serial_gps->begin(GPS_BAUDRATE);
#endif
#ifndef NO_ESP32
_serial_gps->setRxBufferSize(2048); // the default is 256
#endif
}
GPS::setupGPS();
// uncomment to see debug info
// ublox.enableDebugging(Serial);
@@ -45,7 +39,7 @@ bool UBloxGPS::setupGPS()
for (int i = 0; (i < 3) && !tryConnect(); i++)
delay(500);
if (isConnected) {
if (isConnected()) {
DEBUG_MSG("Connected to UBLOX GPS successfully\n");
if (!setUBXMode())
@@ -106,8 +100,8 @@ bool UBloxGPS::factoryReset()
for (int i = 0; (i < 3) && !tryConnect(); i++)
delay(500);
DEBUG_MSG("GPS Factory reset success=%d\n", isConnected);
if (isConnected)
DEBUG_MSG("GPS Factory reset success=%d\n", isConnected());
if (isConnected())
ok = setUBXMode();
return ok;
@@ -127,7 +121,7 @@ void UBloxGPS::whileActive()
// Update fixtype
if (ublox.moduleQueried.fixType) {
fixType = ublox.getFixType(0);
DEBUG_MSG("GPS fix type %d, numSats %d\n", fixType, numSatellites);
// DEBUG_MSG("GPS fix type %d, numSats %d\n", fixType, numSatellites);
}
}
@@ -170,7 +164,7 @@ bool UBloxGPS::lookForLocation()
bool foundLocation = false;
if (ublox.moduleQueried.SIV)
numSatellites = ublox.getSIV(0);
setNumSatellites(ublox.getSIV(0));
if (ublox.moduleQueried.pDOP)
dop = ublox.getPDOP(0); // PDOP (an accuracy metric) is reported in 10^2 units so we have to scale down when we use it
@@ -189,8 +183,7 @@ bool UBloxGPS::lookForLocation()
// bogus lat lon is reported as 0 or 0 (can be bogus just for one)
// Also: apparently when the GPS is initially reporting lock it can output a bogus latitude > 90 deg!
foundLocation =
(latitude != 0) && (longitude != 0) && (latitude <= 900000000 && latitude >= -900000000) && (numSatellites > 0);
foundLocation = (latitude != 0) && (longitude != 0) && (latitude <= 900000000 && latitude >= -900000000);
}
}

View File

@@ -46,15 +46,18 @@ void updateDisplay(uint8_t *blackFrame = framePtr)
EInkDisplay::EInkDisplay(uint8_t address, int sda, int scl)
{
setGeometry(GEOMETRY_128_64); // FIXME - currently we lie and claim 128x64 because I'm not yet sure other resolutions will
// work ie GEOMETRY_RAWMODE
setGeometry(GEOMETRY_RAWMODE, EPD_WIDTH, EPD_HEIGHT);
// setGeometry(GEOMETRY_RAWMODE, 128, 64); // old resolution
// setGeometry(GEOMETRY_128_64); // We originally used this because I wasn't sure if rawmode worked - it does
}
// FIXME quick hack to limit drawing to a very slow rate
uint32_t lastDrawMsec;
// Write the buffer to the display memory
void EInkDisplay::display(void)
/**
* Force a display update if we haven't drawn within the specified msecLimit
*/
bool EInkDisplay::forceDisplay(uint32_t msecLimit)
{
// No need to grab this lock because we are on our own SPI bus
// concurrency::LockGuard g(spiLock);
@@ -62,16 +65,16 @@ void EInkDisplay::display(void)
uint32_t now = millis();
uint32_t sinceLast = now - lastDrawMsec;
if (framePtr && (sinceLast > 60 * 1000 || lastDrawMsec == 0)) {
if (framePtr && (sinceLast > msecLimit || lastDrawMsec == 0)) {
lastDrawMsec = now;
// FIXME - only draw bits have changed (use backbuf similar to the other displays)
// tft.drawBitmap(0, 0, buffer, 128, 64, TFT_YELLOW, TFT_BLACK);
for (uint8_t y = 0; y < SCREEN_HEIGHT; y++) {
for (uint8_t x = 0; x < SCREEN_WIDTH; x++) {
for (uint8_t y = 0; y < displayHeight; y++) {
for (uint8_t x = 0; x < displayWidth; x++) {
// get src pixel in the page based ordering the OLED lib uses FIXME, super inefficent
auto b = buffer[x + (y / 8) * SCREEN_WIDTH];
auto b = buffer[x + (y / 8) * displayWidth];
auto isset = b & (1 << (y & 7));
frame.drawPixel(x, y, isset ? INK : PAPER);
}
@@ -83,11 +86,25 @@ void EInkDisplay::display(void)
updateDisplay(); // Send image to display and refresh
DEBUG_MSG("done\n");
// Put screen to sleep to save power
// Put screen to sleep to save power
ePaper.Sleep();
return true;
} else {
// DEBUG_MSG("Skipping eink display\n");
return false;
}
}
// Write the buffer to the display memory
void EInkDisplay::display(void)
{
// We don't allow regular 'dumb' display() calls to draw on eink until we've shown
// at least one forceDisplay() keyframe. This prevents flashing when we should the critical
// bootscreen (that we want to look nice)
if (lastDrawMsec)
forceDisplay(slowUpdateMsec); // Show the first screen a few seconds after boot, then slower
}
// Send a command to the display (low level function)
void EInkDisplay::sendCommand(uint8_t com)
{

View File

@@ -14,15 +14,26 @@
*/
class EInkDisplay : public OLEDDisplay
{
/// How often should we update the display
/// thereafter we do once per 5 minutes
uint32_t slowUpdateMsec = 5 * 60 * 1000;
public:
/* constructor
FIXME - the parameters are not used, just a temporary hack to keep working like the old displays
*/
EInkDisplay(uint8_t address, int sda, int scl);
// Write the buffer to the display memory
// Write the buffer to the display memory (for eink we only do this occasionally)
virtual void display(void);
/**
* Force a display update if we haven't drawn within the specified msecLimit
*
* @return true if we did draw the screen
*/
bool forceDisplay(uint32_t msecLimit = 1000);
protected:
// the header size of the buffer used, e.g. for the SPI command header
virtual int getBufferOffset(void) { return 0; }
@@ -33,3 +44,5 @@ class EInkDisplay : public OLEDDisplay
// Connect to the display
virtual bool connect();
};

View File

@@ -32,6 +32,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#include "main.h"
#include "mesh-pb-constants.h"
#include "meshwifi/meshwifi.h"
#include "plugins/TextMessagePlugin.h"
#include "target_specific.h"
#include "utils.h"
@@ -58,39 +59,92 @@ static char ourId[5];
static bool heartbeat = false;
#endif
static void drawBootScreen(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y)
static uint16_t displayWidth, displayHeight;
#define SCREEN_WIDTH displayWidth
#define SCREEN_HEIGHT displayHeight
#ifdef HAS_EINK
// The screen is bigger so use bigger fonts
#define FONT_SMALL ArialMT_Plain_16
#define FONT_MEDIUM ArialMT_Plain_24
#define FONT_LARGE ArialMT_Plain_24
#else
#define FONT_SMALL ArialMT_Plain_10
#define FONT_MEDIUM ArialMT_Plain_16
#define FONT_LARGE ArialMT_Plain_24
#endif
#define fontHeight(font) ((font)[1] + 1) // height is position 1
#define FONT_HEIGHT_SMALL fontHeight(FONT_SMALL)
#define FONT_HEIGHT_MEDIUM fontHeight(FONT_MEDIUM)
#define getStringCenteredX(s) ((SCREEN_WIDTH - display->getStringWidth(s)) / 2)
#ifndef SCREEN_TRANSITION_MSECS
#define SCREEN_TRANSITION_MSECS 300
#endif
/**
* Draw the icon with extra info printed around the corners
*/
static void drawIconScreen(const char *upperMsg, OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y)
{
// draw an xbm image.
// Please note that everything that should be transitioned
// needs to be drawn relative to x and y
display->drawXbm(x + 32, y, icon_width, icon_height, (const uint8_t *)icon_bits);
display->setFont(ArialMT_Plain_16);
display->setTextAlignment(TEXT_ALIGN_CENTER);
display->drawString(64 + x, SCREEN_HEIGHT - FONT_HEIGHT_16, "meshtastic.org");
display->setFont(ArialMT_Plain_10);
const char *region = xstr(HW_VERSION);
if (*region && region[3] == '-') // Skip past 1.0- in the 1.0-EU865 string
region += 4;
// draw centered icon left to right and centered above the one line of app text
display->drawXbm(x + (SCREEN_WIDTH - icon_width) / 2, y + (SCREEN_HEIGHT - FONT_HEIGHT_MEDIUM - icon_height) / 2 + 2,
icon_width, icon_height, (const uint8_t *)icon_bits);
display->setFont(FONT_MEDIUM);
display->setTextAlignment(TEXT_ALIGN_LEFT);
const char *title = "meshtastic.org";
display->drawString(x + getStringCenteredX(title), y + SCREEN_HEIGHT - FONT_HEIGHT_MEDIUM, title);
display->setFont(FONT_SMALL);
// Draw region in upper left
if (upperMsg)
display->drawString(x + 0, y + 0, upperMsg);
// Draw version in upper right
char buf[16];
snprintf(buf, sizeof(buf), "%s",
xstr(APP_VERSION)); // Note: we don't bother printing region or now, it makes the string too long
display->drawString(SCREEN_WIDTH - 20, 0, buf);
display->drawString(x + SCREEN_WIDTH - display->getStringWidth(buf), y + 0, buf);
screen->forceDisplay();
// FIXME - draw serial # somewhere?
}
static void drawBootScreen(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y)
{
// Draw region in upper left
const char *region = myRegion ? myRegion->name : NULL;
drawIconScreen(region, display, state, x, y);
}
/// Used on eink displays while in deep sleep
static void drawSleepScreen(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y)
{
drawIconScreen("Sleeping...", display, state, x, y);
}
static void drawFrameBluetooth(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y)
{
display->setTextAlignment(TEXT_ALIGN_CENTER);
display->setFont(ArialMT_Plain_16);
display->setFont(FONT_MEDIUM);
display->drawString(64 + x, y, "Bluetooth");
display->setFont(ArialMT_Plain_10);
display->drawString(64 + x, FONT_HEIGHT + y + 2, "Enter this code");
display->setFont(FONT_SMALL);
display->drawString(64 + x, FONT_HEIGHT_SMALL + y + 2, "Enter this code");
display->setFont(ArialMT_Plain_24);
display->setFont(FONT_LARGE);
display->drawString(64 + x, 26 + y, btPIN);
display->setFont(ArialMT_Plain_10);
display->setFont(FONT_SMALL);
char buf[30];
const char *name = "Name: ";
strcpy(buf, name);
@@ -112,10 +166,10 @@ static void drawTextMessageFrame(OLEDDisplay *display, OLEDDisplayUiState *state
// with the third parameter you can define the width after which words will
// be wrapped. Currently only spaces and "-" are allowed for wrapping
display->setTextAlignment(TEXT_ALIGN_LEFT);
display->setFont(ArialMT_Plain_16);
display->setFont(FONT_MEDIUM);
String sender = (node && node->has_user) ? node->user.short_name : "???";
display->drawString(0 + x, 0 + y, sender);
display->setFont(ArialMT_Plain_10);
display->setFont(FONT_SMALL);
// the max length of this buffer is much longer than we can possibly print
static char tempBuf[96];
@@ -135,8 +189,8 @@ static void drawColumns(OLEDDisplay *display, int16_t x, int16_t y, const char *
int xo = x, yo = y;
while (*f) {
display->drawString(xo, yo, *f);
yo += FONT_HEIGHT;
if (yo > SCREEN_HEIGHT - FONT_HEIGHT) {
yo += FONT_HEIGHT_SMALL;
if (yo > SCREEN_HEIGHT - FONT_HEIGHT_SMALL) {
xo += SCREEN_WIDTH / 2;
yo = 0;
}
@@ -162,14 +216,14 @@ static void drawColumns(OLEDDisplay *display, int16_t x, int16_t y, const char *
// Wrap to next row, if needed.
if (++col >= COLUMNS) {
xo = x;
yo += FONT_HEIGHT;
yo += FONT_HEIGHT_SMALL;
col = 0;
}
f++;
}
if (col != 0) {
// Include last incomplete line in our total.
yo += FONT_HEIGHT;
yo += FONT_HEIGHT_SMALL;
}
return yo;
@@ -236,7 +290,7 @@ static void drawGPS(OLEDDisplay *display, int16_t x, int16_t y, const GPSStatus
display->drawFastImage(x + 24, y, 8, 8, imgSatellite);
// Draw the number of satellites
sprintf(satsString, "%d", gps->getNumSatellites());
sprintf(satsString, "%lu", gps->getNumSatellites());
display->drawString(x + 34, y - 2, satsString);
}
}
@@ -476,7 +530,7 @@ static void drawNodeInfo(OLEDDisplay *display, OLEDDisplayUiState *state, int16_
NodeInfo *node = nodeDB.getNodeByIndex(nodeIndex);
display->setFont(ArialMT_Plain_10);
display->setFont(FONT_SMALL);
// The coordinates define the left starting point of the text
display->setTextAlignment(TEXT_ALIGN_LEFT);
@@ -489,11 +543,11 @@ static void drawNodeInfo(OLEDDisplay *display, OLEDDisplayUiState *state, int16_
uint32_t agoSecs = sinceLastSeen(node);
static char lastStr[20];
if (agoSecs < 120) // last 2 mins?
snprintf(lastStr, sizeof(lastStr), "%u seconds ago", agoSecs);
snprintf(lastStr, sizeof(lastStr), "%lu seconds ago", agoSecs);
else if (agoSecs < 120 * 60) // last 2 hrs
snprintf(lastStr, sizeof(lastStr), "%u minutes ago", agoSecs / 60);
snprintf(lastStr, sizeof(lastStr), "%lu minutes ago", agoSecs / 60);
else
snprintf(lastStr, sizeof(lastStr), "%u hours ago", agoSecs / 60 / 60);
snprintf(lastStr, sizeof(lastStr), "%lu hours ago", agoSecs / 60 / 60);
static char distStr[20];
strcpy(distStr, "? km"); // might not have location data
@@ -531,7 +585,7 @@ static void drawNodeInfo(OLEDDisplay *display, OLEDDisplayUiState *state, int16_
// direction to node is unknown so display question mark
// Debug info for gps lock errors
// DEBUG_MSG("ourNode %d, ourPos %d, theirPos %d\n", !!ourNode, ourNode && hasPosition(ourNode), hasPosition(node));
display->drawString(compassX - FONT_HEIGHT / 4, compassY - FONT_HEIGHT / 2, "?");
display->drawString(compassX - FONT_HEIGHT_SMALL / 4, compassY - FONT_HEIGHT_SMALL / 2, "?");
display->drawCircle(compassX, compassY, COMPASS_DIAM / 2);
// Must be after distStr is populated
@@ -561,7 +615,25 @@ void _screen_header()
}
#endif
Screen::Screen(uint8_t address, int sda, int scl) : cmdQueue(32), dispdev(address, sda, scl), ui(&dispdev) {}
Screen::Screen(uint8_t address, int sda, int scl) : OSThread("Screen"), cmdQueue(32), dispdev(address, sda, scl), ui(&dispdev)
{
cmdQueue.setReader(this);
}
/**
* Prepare the display for the unit going to the lowest power mode possible. Most screens will just
* poweroff, but eink screens will show a "I'm sleeping" graphic, possibly with a QR code
*/
void Screen::doDeepSleep()
{
#ifdef HAS_EINK
static FrameCallback sleepFrames[] = {drawSleepScreen};
static const int sleepFrameCount = sizeof(sleepFrames) / sizeof(sleepFrames[0]);
ui.setFrames(sleepFrames, sleepFrameCount);
ui.update();
#endif
setOn(false);
}
void Screen::handleSetOn(bool on)
{
@@ -573,9 +645,12 @@ void Screen::handleSetOn(bool on)
DEBUG_MSG("Turning on screen\n");
dispdev.displayOn();
dispdev.displayOn();
enabled = true;
setInterval(0); // Draw ASAP
} else {
DEBUG_MSG("Turning off screen\n");
dispdev.displayOff();
enabled = false;
}
screenOn = on;
}
@@ -583,17 +658,21 @@ void Screen::handleSetOn(bool on)
void Screen::setup()
{
concurrency::PeriodicTask::setup();
// We don't set useDisplay until setup() is called, because some boards have a declaration of this object but the device
// is never found when probing i2c and therefore we don't call setup and never want to do (invalid) accesses to this device.
useDisplay = true;
dispdev.resetOrientation();
// I think this is not needed - redundant with ui.init
// dispdev.resetOrientation();
// Initialising the UI will init the display too.
ui.init();
ui.setTimePerTransition(300); // msecs
displayWidth = dispdev.width();
displayHeight = dispdev.height();
ui.setTimePerTransition(SCREEN_TRANSITION_MSECS);
ui.setIndicatorPosition(BOTTOM);
// Defines where the first frame is located in the bar.
ui.setIndicatorDirection(LEFT_RIGHT);
@@ -619,7 +698,9 @@ void Screen::setup()
// Set up a log buffer with 3 lines, 32 chars each.
dispdev.setLogBuffer(3, 32);
#ifdef FLIP_SCREEN_VERTICALLY
#ifdef SCREEN_MIRROR
dispdev.mirrorScreen();
#elif defined(SCREEN_FLIP_VERTICALLY)
dispdev.flipScreenVertically();
#endif
@@ -640,16 +721,36 @@ void Screen::setup()
powerStatusObserver.observe(&powerStatus->onNewStatus);
gpsStatusObserver.observe(&gpsStatus->onNewStatus);
nodeStatusObserver.observe(&nodeStatus->onNewStatus);
textMessageObserver.observe(&textMessagePlugin);
}
void Screen::doTask()
void Screen::forceDisplay()
{
// Nasty hack to force epaper updates for 'key' frames. FIXME, cleanup.
#ifdef HAS_EINK
dispdev.forceDisplay();
#endif
}
int32_t Screen::runOnce()
{
// If we don't have a screen, don't ever spend any CPU for us.
if (!useDisplay) {
setPeriod(0);
return;
enabled = false;
return RUN_SAME;
}
// Show boot screen for first 3 seconds, then switch to normal operation.
static bool showingBootScreen = true;
if (showingBootScreen && (millis() > 5000)) {
DEBUG_MSG("Done with boot screen...\n");
stopBootScreen();
showingBootScreen = false;
}
// Update the screen last, after we've figured out what to show.
debug_info()->setChannelNameStatus(getChannelName());
// Process incoming commands.
for (;;) {
ScreenCmd cmd;
@@ -684,10 +785,14 @@ void Screen::doTask()
if (!screenOn) { // If we didn't just wake and the screen is still off, then
// stop updating until it is on again
setPeriod(0);
return;
enabled = false;
return 0;
}
// this must be before the frameState == FIXED check, because we always
// want to draw at least one FIXED frame before doing forceDisplay
ui.update();
// Switch to a low framerate (to save CPU) when we are not in transition
// but we should only call setTargetFPS when framestate changes, because
// otherwise that breaks animations.
@@ -696,6 +801,7 @@ void Screen::doTask()
DEBUG_MSG("Setting idle framerate\n");
targetFramerate = IDLE_FRAMERATE;
ui.setTargetFPS(targetFramerate);
forceDisplay();
}
// While showing the bootscreen or Bluetooth pair screen all of our
@@ -704,14 +810,12 @@ void Screen::doTask()
// standard screen loop handling here
}
ui.update();
// DEBUG_MSG("want fps %d, fixed=%d\n", targetFramerate,
// ui.getUiState()->frameState); If we are scrolling we need to be called
// soon, otherwise just 1 fps (to save CPU) We also ask to be called twice
// as fast as we really need so that any rounding errors still result with
// the correct framerate
setPeriod(1000 / targetFramerate);
return (1000 / targetFramerate);
}
void Screen::drawDebugInfoTrampoline(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y)
@@ -772,6 +876,8 @@ void Screen::setFrames()
prevFrame = -1; // Force drawNodeInfo to pick a new node (because our list
// just changed)
setFastFramerate(); // Draw ASAP
}
void Screen::handleStartBluetoothPinScreen(uint32_t pin)
@@ -781,16 +887,17 @@ void Screen::handleStartBluetoothPinScreen(uint32_t pin)
static FrameCallback btFrames[] = {drawFrameBluetooth};
snprintf(btPIN, sizeof(btPIN), "%06u", pin);
snprintf(btPIN, sizeof(btPIN), "%06lu", pin);
ui.disableAllIndicators();
ui.setFrames(btFrames, 1);
setFastFramerate();
}
void Screen::handlePrint(const char *text)
{
DEBUG_MSG("Screen: %s", text);
if (!useDisplay)
if (!useDisplay || !showingNormalScreen)
return;
dispdev.print(text);
@@ -801,22 +908,31 @@ void Screen::handleOnPress()
// If screen was off, just wake it, otherwise advance to next frame
// If we are in a transition, the press must have bounced, drop it.
if (ui.getUiState()->frameState == FIXED) {
setPeriod(1); // redraw ASAP
ui.nextFrame();
DEBUG_MSG("Setting fast framerate\n");
// We are about to start a transition so speed up fps
targetFramerate = TRANSITION_FRAMERATE;
ui.setTargetFPS(targetFramerate);
setFastFramerate();
}
}
#ifndef SCREEN_TRANSITION_FRAMERATE
#define SCREEN_TRANSITION_FRAMERATE 30 // fps
#endif
void Screen::setFastFramerate()
{
DEBUG_MSG("Setting fast framerate\n");
// We are about to start a transition so speed up fps
targetFramerate = SCREEN_TRANSITION_FRAMERATE;
ui.setTargetFPS(targetFramerate);
setInterval(0); // redraw ASAP
}
void DebugInfo::drawFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y)
{
displayedNodeNum = 0; // Not currently showing a node pane
display->setFont(ArialMT_Plain_10);
display->setFont(FONT_SMALL);
// The coordinates define the left starting point of the text
display->setTextAlignment(TEXT_ALIGN_LEFT);
@@ -838,13 +954,13 @@ void DebugInfo::drawFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16
drawGPS(display, x + (SCREEN_WIDTH * 0.63), y + 2, gpsStatus);
// Draw the channel name
display->drawString(x, y + FONT_HEIGHT, channelStr);
display->drawString(x, y + FONT_HEIGHT_SMALL, channelStr);
// Draw our hardware ID to assist with bluetooth pairing
display->drawFastImage(x + SCREEN_WIDTH - (10) - display->getStringWidth(ourId), y + 2 + FONT_HEIGHT, 8, 8, imgInfo);
display->drawString(x + SCREEN_WIDTH - display->getStringWidth(ourId), y + FONT_HEIGHT, ourId);
display->drawFastImage(x + SCREEN_WIDTH - (10) - display->getStringWidth(ourId), y + 2 + FONT_HEIGHT_SMALL, 8, 8, imgInfo);
display->drawString(x + SCREEN_WIDTH - display->getStringWidth(ourId), y + FONT_HEIGHT_SMALL, ourId);
// Draw any log messages
display->drawLogBuffer(x, y + (FONT_HEIGHT * 2));
display->drawLogBuffer(x, y + (FONT_HEIGHT_SMALL * 2));
/* Display a heartbeat pixel that blinks every time the frame is redrawn */
#ifdef SHOW_REDRAWS
@@ -863,7 +979,7 @@ void DebugInfo::drawFrameWiFi(OLEDDisplay *display, OLEDDisplayUiState *state, i
displayedNodeNum = 0; // Not currently showing a node pane
display->setFont(ArialMT_Plain_10);
display->setFont(FONT_SMALL);
// The coordinates define the left starting point of the text
display->setTextAlignment(TEXT_ALIGN_LEFT);
@@ -894,86 +1010,90 @@ void DebugInfo::drawFrameWiFi(OLEDDisplay *display, OLEDDisplayUiState *state, i
if (WiFi.status() == WL_CONNECTED) {
if (radioConfig.preferences.wifi_ap_mode) {
display->drawString(x, y + FONT_HEIGHT * 1, "IP: " + String(WiFi.softAPIP().toString().c_str()));
display->drawString(x, y + FONT_HEIGHT_SMALL * 1, "IP: " + String(WiFi.softAPIP().toString().c_str()));
} else {
display->drawString(x, y + FONT_HEIGHT * 1, "IP: " + String(WiFi.localIP().toString().c_str()));
display->drawString(x, y + FONT_HEIGHT_SMALL * 1, "IP: " + String(WiFi.localIP().toString().c_str()));
}
} else if (WiFi.status() == WL_NO_SSID_AVAIL) {
display->drawString(x, y + FONT_HEIGHT * 1, "SSID Not Found");
display->drawString(x, y + FONT_HEIGHT_SMALL * 1, "SSID Not Found");
} else if (WiFi.status() == WL_CONNECTION_LOST) {
display->drawString(x, y + FONT_HEIGHT * 1, "Connection Lost");
display->drawString(x, y + FONT_HEIGHT_SMALL * 1, "Connection Lost");
} else if (WiFi.status() == WL_CONNECT_FAILED) {
display->drawString(x, y + FONT_HEIGHT * 1, "Connection Failed");
display->drawString(x, y + FONT_HEIGHT_SMALL * 1, "Connection Failed");
//} else if (WiFi.status() == WL_DISCONNECTED) {
// display->drawString(x, y + FONT_HEIGHT * 1, "Disconnected");
// display->drawString(x, y + FONT_HEIGHT_SMALL * 1, "Disconnected");
} else if (WiFi.status() == WL_IDLE_STATUS) {
display->drawString(x, y + FONT_HEIGHT * 1, "Idle ... Reconnecting");
display->drawString(x, y + FONT_HEIGHT_SMALL * 1, "Idle ... Reconnecting");
} else {
// Codes:
// https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-guides/wifi.html#wi-fi-reason-code
if (getWifiDisconnectReason() == 2) {
display->drawString(x, y + FONT_HEIGHT * 1, "Authentication Invalid");
display->drawString(x, y + FONT_HEIGHT_SMALL * 1, "Authentication Invalid");
} else if (getWifiDisconnectReason() == 3) {
display->drawString(x, y + FONT_HEIGHT * 1, "De-authenticated");
display->drawString(x, y + FONT_HEIGHT_SMALL * 1, "De-authenticated");
} else if (getWifiDisconnectReason() == 4) {
display->drawString(x, y + FONT_HEIGHT * 1, "Disassociated Expired");
display->drawString(x, y + FONT_HEIGHT_SMALL * 1, "Disassociated Expired");
} else if (getWifiDisconnectReason() == 5) {
display->drawString(x, y + FONT_HEIGHT * 1, "AP - Too Many Clients");
display->drawString(x, y + FONT_HEIGHT_SMALL * 1, "AP - Too Many Clients");
} else if (getWifiDisconnectReason() == 6) {
display->drawString(x, y + FONT_HEIGHT * 1, "NOT_AUTHED");
display->drawString(x, y + FONT_HEIGHT_SMALL * 1, "NOT_AUTHED");
} else if (getWifiDisconnectReason() == 7) {
display->drawString(x, y + FONT_HEIGHT * 1, "NOT_ASSOCED");
display->drawString(x, y + FONT_HEIGHT_SMALL * 1, "NOT_ASSOCED");
} else if (getWifiDisconnectReason() == 8) {
display->drawString(x, y + FONT_HEIGHT * 1, "Disassociated");
display->drawString(x, y + FONT_HEIGHT_SMALL * 1, "Disassociated");
} else if (getWifiDisconnectReason() == 9) {
display->drawString(x, y + FONT_HEIGHT * 1, "ASSOC_NOT_AUTHED");
display->drawString(x, y + FONT_HEIGHT_SMALL * 1, "ASSOC_NOT_AUTHED");
} else if (getWifiDisconnectReason() == 10) {
display->drawString(x, y + FONT_HEIGHT * 1, "DISASSOC_PWRCAP_BAD");
display->drawString(x, y + FONT_HEIGHT_SMALL * 1, "DISASSOC_PWRCAP_BAD");
} else if (getWifiDisconnectReason() == 11) {
display->drawString(x, y + FONT_HEIGHT * 1, "DISASSOC_SUPCHAN_BAD");
display->drawString(x, y + FONT_HEIGHT_SMALL * 1, "DISASSOC_SUPCHAN_BAD");
} else if (getWifiDisconnectReason() == 13) {
display->drawString(x, y + FONT_HEIGHT * 1, "IE_INVALID");
display->drawString(x, y + FONT_HEIGHT_SMALL * 1, "IE_INVALID");
} else if (getWifiDisconnectReason() == 14) {
display->drawString(x, y + FONT_HEIGHT * 1, "MIC_FAILURE");
display->drawString(x, y + FONT_HEIGHT_SMALL * 1, "MIC_FAILURE");
} else if (getWifiDisconnectReason() == 15) {
display->drawString(x, y + FONT_HEIGHT * 1, "AP Handshake Timeout");
display->drawString(x, y + FONT_HEIGHT_SMALL * 1, "AP Handshake Timeout");
} else if (getWifiDisconnectReason() == 16) {
display->drawString(x, y + FONT_HEIGHT * 1, "GROUP_KEY_UPDATE_TIMEOUT");
display->drawString(x, y + FONT_HEIGHT_SMALL * 1, "GROUP_KEY_UPDATE_TIMEOUT");
} else if (getWifiDisconnectReason() == 17) {
display->drawString(x, y + FONT_HEIGHT * 1, "IE_IN_4WAY_DIFFERS");
display->drawString(x, y + FONT_HEIGHT_SMALL * 1, "IE_IN_4WAY_DIFFERS");
} else if (getWifiDisconnectReason() == 18) {
display->drawString(x, y + FONT_HEIGHT * 1, "Invalid Group Cipher");
display->drawString(x, y + FONT_HEIGHT_SMALL * 1, "Invalid Group Cipher");
} else if (getWifiDisconnectReason() == 19) {
display->drawString(x, y + FONT_HEIGHT * 1, "Invalid Pairwise Cipher");
display->drawString(x, y + FONT_HEIGHT_SMALL * 1, "Invalid Pairwise Cipher");
} else if (getWifiDisconnectReason() == 20) {
display->drawString(x, y + FONT_HEIGHT * 1, "AKMP_INVALID");
display->drawString(x, y + FONT_HEIGHT_SMALL * 1, "AKMP_INVALID");
} else if (getWifiDisconnectReason() == 21) {
display->drawString(x, y + FONT_HEIGHT * 1, "UNSUPP_RSN_IE_VERSION");
display->drawString(x, y + FONT_HEIGHT_SMALL * 1, "UNSUPP_RSN_IE_VERSION");
} else if (getWifiDisconnectReason() == 22) {
display->drawString(x, y + FONT_HEIGHT * 1, "INVALID_RSN_IE_CAP");
display->drawString(x, y + FONT_HEIGHT_SMALL * 1, "INVALID_RSN_IE_CAP");
} else if (getWifiDisconnectReason() == 23) {
display->drawString(x, y + FONT_HEIGHT * 1, "802_1X_AUTH_FAILED");
display->drawString(x, y + FONT_HEIGHT_SMALL * 1, "802_1X_AUTH_FAILED");
} else if (getWifiDisconnectReason() == 24) {
display->drawString(x, y + FONT_HEIGHT * 1, "CIPHER_SUITE_REJECTED");
display->drawString(x, y + FONT_HEIGHT_SMALL * 1, "CIPHER_SUITE_REJECTED");
} else if (getWifiDisconnectReason() == 200) {
display->drawString(x, y + FONT_HEIGHT * 1, "BEACON_TIMEOUT");
display->drawString(x, y + FONT_HEIGHT_SMALL * 1, "BEACON_TIMEOUT");
} else if (getWifiDisconnectReason() == 201) {
display->drawString(x, y + FONT_HEIGHT * 1, "AP Not Found");
display->drawString(x, y + FONT_HEIGHT_SMALL * 1, "AP Not Found");
} else if (getWifiDisconnectReason() == 202) {
display->drawString(x, y + FONT_HEIGHT * 1, "AUTH_FAIL");
display->drawString(x, y + FONT_HEIGHT_SMALL * 1, "AUTH_FAIL");
} else if (getWifiDisconnectReason() == 203) {
display->drawString(x, y + FONT_HEIGHT * 1, "ASSOC_FAIL");
display->drawString(x, y + FONT_HEIGHT_SMALL * 1, "ASSOC_FAIL");
} else if (getWifiDisconnectReason() == 204) {
display->drawString(x, y + FONT_HEIGHT * 1, "HANDSHAKE_TIMEOUT");
display->drawString(x, y + FONT_HEIGHT_SMALL * 1, "HANDSHAKE_TIMEOUT");
} else if (getWifiDisconnectReason() == 205) {
display->drawString(x, y + FONT_HEIGHT * 1, "Connection Failed");
display->drawString(x, y + FONT_HEIGHT_SMALL * 1, "Connection Failed");
} else {
display->drawString(x, y + FONT_HEIGHT * 1, "Unknown Status");
display->drawString(x, y + FONT_HEIGHT_SMALL * 1, "Unknown Status");
}
}
display->drawString(x, y + FONT_HEIGHT * 2, "SSID: " + String(wifiName));
display->drawString(x, y + FONT_HEIGHT * 3, "PWD: " + String(wifiPsw));
if ((millis() / 10000) % 2) {
display->drawString(x, y + FONT_HEIGHT_SMALL * 2, "SSID: " + String(wifiName));
} else {
display->drawString(x, y + FONT_HEIGHT_SMALL * 2, "PWD: " + String(wifiPsw));
}
display->drawString(x, y + FONT_HEIGHT_SMALL * 3, "http://meshtastic.local");
/* Display a heartbeat pixel that blinks every time the frame is redrawn */
#ifdef SHOW_REDRAWS
@@ -988,7 +1108,7 @@ void DebugInfo::drawFrameSettings(OLEDDisplay *display, OLEDDisplayUiState *stat
{
displayedNodeNum = 0; // Not currently showing a node pane
display->setFont(ArialMT_Plain_10);
display->setFont(FONT_SMALL);
// The coordinates define the left starting point of the text
display->setTextAlignment(TEXT_ALIGN_LEFT);
@@ -1022,15 +1142,21 @@ void DebugInfo::drawFrameSettings(OLEDDisplay *display, OLEDDisplayUiState *stat
minutes %= 60;
hours %= 24;
display->drawString(x, y + FONT_HEIGHT * 1,
display->drawString(x, y + FONT_HEIGHT_SMALL * 1,
String(days) + "d " + (hours < 10 ? "0" : "") + String(hours) + ":" + (minutes < 10 ? "0" : "") +
String(minutes) + ":" + (seconds < 10 ? "0" : "") + String(seconds));
#ifndef NO_ESP32
// Show CPU Frequency.
display->drawString(x + SCREEN_WIDTH - display->getStringWidth("CPU " + String(getCpuFrequencyMhz()) + "MHz"),
y + FONT_HEIGHT_SMALL * 1, "CPU " + String(getCpuFrequencyMhz()) + "MHz");
#endif
// Line 3
drawGPSAltitude(display, x, y + FONT_HEIGHT * 2, gpsStatus);
drawGPSAltitude(display, x, y + FONT_HEIGHT_SMALL * 2, gpsStatus);
// Line 4
drawGPScoordinates(display, x, y + FONT_HEIGHT * 3, gpsStatus);
drawGPScoordinates(display, x, y + FONT_HEIGHT_SMALL * 3, gpsStatus);
/* Display a heartbeat pixel that blinks every time the frame is redrawn */
#ifdef SHOW_REDRAWS
@@ -1059,16 +1185,24 @@ int Screen::handleStatusUpdate(const meshtastic::Status *arg)
// DEBUG_MSG("Screen got status update %d\n", arg->getStatusType());
switch (arg->getStatusType()) {
case STATUS_TYPE_NODE:
if (nodeDB.updateTextMessage || nodeStatus->getLastNumTotal() != nodeStatus->getNumTotal()) {
setFrames(); // Regen the list of screens
prevFrame = -1; // Force a GUI update
setPeriod(1); // Update the screen right away
if (showingNormalScreen &&
nodeStatus->getLastNumTotal() != nodeStatus->getNumTotal()) {
setFrames(); // Regen the list of screens
}
nodeDB.updateGUI = false;
nodeDB.updateTextMessage = false;
break;
}
return 0;
}
int Screen::handleTextMessage(const MeshPacket *arg)
{
if (showingNormalScreen) {
setFrames(); // Regen the list of screens (will show new text message)
}
return 0;
}
} // namespace graphics

View File

@@ -6,6 +6,8 @@
#ifdef USE_SH1106
#include <SH1106Wire.h>
#elif defined(USE_ST7567)
#include <ST7567Wire.h>
#else
#include <SSD1306Wire.h>
#endif
@@ -15,10 +17,15 @@
#include "TypedQueue.h"
#include "commands.h"
#include "concurrency/LockGuard.h"
#include "concurrency/PeriodicTask.h"
#include "concurrency/OSThread.h"
#include "power.h"
#include <string>
// 0 to 255, though particular variants might define different defaults
#ifndef BRIGHTNESS_DEFAULT
#define BRIGHTNESS_DEFAULT 150
#endif
namespace graphics
{
@@ -62,7 +69,7 @@ class DebugInfo
* multiple times simultaneously. All state-changing calls are queued and executed
* when the main loop calls us.
*/
class Screen : public concurrency::PeriodicTask
class Screen : public concurrency::OSThread
{
CallbackObserver<Screen, const meshtastic::Status *> powerStatusObserver =
CallbackObserver<Screen, const meshtastic::Status *>(this, &Screen::handleStatusUpdate);
@@ -70,6 +77,8 @@ class Screen : public concurrency::PeriodicTask
CallbackObserver<Screen, const meshtastic::Status *>(this, &Screen::handleStatusUpdate);
CallbackObserver<Screen, const meshtastic::Status *> nodeStatusObserver =
CallbackObserver<Screen, const meshtastic::Status *>(this, &Screen::handleStatusUpdate);
CallbackObserver<Screen, const MeshPacket *> textMessageObserver =
CallbackObserver<Screen, const MeshPacket *>(this, &Screen::handleTextMessage);
public:
Screen(uint8_t address, int sda = -1, int scl = -1);
@@ -92,12 +101,18 @@ class Screen : public concurrency::PeriodicTask
enqueueCmd(ScreenCmd{.cmd = on ? Cmd::SET_ON : Cmd::SET_OFF});
}
/**
* Prepare the display for the unit going to the lowest power mode possible. Most screens will just
* poweroff, but eink screens will show a "I'm sleeping" graphic, possibly with a QR code
*/
void doDeepSleep();
/// Handles a button press.
void onPress() { enqueueCmd(ScreenCmd{.cmd = Cmd::ON_PRESS}); }
// Implementation to Adjust Brightness
void adjustBrightness();
uint8_t brightness = 150;
uint8_t brightness = BRIGHTNESS_DEFAULT;
/// Starts showing the Bluetooth PIN screen.
//
@@ -179,12 +194,16 @@ class Screen : public concurrency::PeriodicTask
DebugInfo *debug_info() { return &debugInfo; }
int handleStatusUpdate(const meshtastic::Status *arg);
int handleTextMessage(const MeshPacket *arg);
/// Used to force (super slow) eink displays to draw critical frames
void forceDisplay();
protected:
/// Updates the UI.
//
// Called periodically from the main loop.
void doTask() final;
int32_t runOnce() final;
private:
struct ScreenCmd {
@@ -202,7 +221,7 @@ class Screen : public concurrency::PeriodicTask
return true; // claim success if our display is not in use
else {
bool success = cmdQueue.enqueue(cmd, 0);
setPeriod(1); // handle ASAP
enabled = true; // handle ASAP (we are the registered reader for cmdQueue, but might have been disabled)
return success;
}
}
@@ -216,6 +235,9 @@ class Screen : public concurrency::PeriodicTask
/// Rebuilds our list of frames (screens) to default ones.
void setFrames();
/// Try to start drawing ASAP
void setFastFramerate();
/// Called when debug screen is to be drawn, calls through to debugInfo.drawFrame.
static void drawDebugInfoTrampoline(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y);
@@ -244,6 +266,8 @@ class Screen : public concurrency::PeriodicTask
EInkDisplay dispdev;
#elif defined(USE_SH1106)
SH1106Wire dispdev;
#elif defined(USE_ST7567)
ST7567Wire dispdev;
#else
SSD1306Wire dispdev;
#endif

View File

@@ -11,8 +11,7 @@ static TFT_eSPI tft = TFT_eSPI(); // Invoke library, pins defined in User_Setup.
TFTDisplay::TFTDisplay(uint8_t address, int sda, int scl)
{
setGeometry(GEOMETRY_128_64); // FIXME - currently we lie and claim 128x64 because I'm not yet sure other resolutions will
// work ie GEOMETRY_RAWMODE
setGeometry(GEOMETRY_RAWMODE, 160, 80);
}
// Write the buffer to the display memory
@@ -22,11 +21,11 @@ void TFTDisplay::display(void)
// FIXME - only draw bits have changed (use backbuf similar to the other displays)
// tft.drawBitmap(0, 0, buffer, 128, 64, TFT_YELLOW, TFT_BLACK);
for (uint8_t y = 0; y < SCREEN_HEIGHT; y++) {
for (uint8_t x = 0; x < SCREEN_WIDTH; x++) {
for (uint8_t y = 0; y < displayHeight; y++) {
for (uint8_t x = 0; x < displayWidth; x++) {
// get src pixel in the page based ordering the OLED lib uses FIXME, super inefficent
auto b = buffer[x + (y / 8) * SCREEN_WIDTH];
auto b = buffer[x + (y / 8) * displayWidth];
auto isset = b & (1 << (y & 7));
tft.drawPixel(x, y, isset ? TFT_WHITE : TFT_BLACK);
}

View File

@@ -2,12 +2,7 @@
#include "fonts.h"
#define FONT_HEIGHT 14 // actually 13 for "Arial 10" but want a little extra space
#define FONT_HEIGHT_16 (ArialMT_Plain_16[1] + 1)
// This means the *visible* area (sh1106 can address 132, but shows 128 for example)
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
#define TRANSITION_FRAMERATE 30 // fps
#define IDLE_FRAMERATE 1 // in fps
#define COMPASS_DIAM 44

View File

@@ -1,25 +1,3 @@
/*
Main module
# Modified by Kyle T. Gabriel to fix issue with incorrect GPS data for TTNMapper
Copyright (C) 2018 by Xose Pérez <xose dot perez at gmail dot com>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "Air530GPS.h"
#include "MeshRadio.h"
@@ -27,15 +5,17 @@
#include "NodeDB.h"
#include "PowerFSM.h"
#include "UBloxGPS.h"
#include "concurrency/Periodic.h"
#include "configuration.h"
#include "error.h"
#include "power.h"
// #include "rom/rtc.h"
#include "DSRRouter.h"
// #include "debug.h"
#include "FSCommon.h"
#include "RTC.h"
#include "SPILock.h"
#include "concurrency/OSThread.h"
#include "concurrency/Periodic.h"
#include "graphics/Screen.h"
#include "main.h"
#include "meshwifi/meshhttp.h"
@@ -57,8 +37,10 @@
#include "variant.h"
#endif
using namespace concurrency;
// We always create a screen object, but we only init it if we find the hardware
graphics::Screen screen(SSD1306_ADDRESS);
graphics::Screen *screen;
// Global power status
meshtastic::PowerStatus *powerStatus = new meshtastic::PowerStatus();
@@ -69,11 +51,12 @@ meshtastic::GPSStatus *gpsStatus = new meshtastic::GPSStatus();
// Global Node status
meshtastic::NodeStatus *nodeStatus = new meshtastic::NodeStatus();
bool ssd1306_found;
/// The I2C address of our display (if found)
uint8_t screen_found;
bool axp192_found;
DSRRouter realRouter;
Router &router = realRouter; // Users of router don't care what sort of subclass implements that API
Router *router = NULL; // Users of router don't care what sort of subclass implements that API
// -----------------------------------------------------------------------------
// Application
@@ -92,9 +75,13 @@ void scanI2Cdevice(void)
nDevices++;
if (addr == SSD1306_ADDRESS) {
ssd1306_found = true;
screen_found = addr;
DEBUG_MSG("ssd1306 display found\n");
}
if (addr == ST7567_ADDRESS) {
screen_found = addr;
DEBUG_MSG("st7567 display found\n");
}
#ifdef AXP192_SLAVE_ADDRESS
if (addr == AXP192_SLAVE_ADDRESS) {
axp192_found = true;
@@ -123,7 +110,7 @@ const char *getDeviceName()
return name;
}
static uint32_t ledBlinker()
static int32_t ledBlinker()
{
static bool ledOn;
ledOn ^= 1;
@@ -131,31 +118,164 @@ static uint32_t ledBlinker()
setLed(ledOn);
// have a very sparse duty cycle of LED being on, unless charging, then blink 0.5Hz square wave rate to indicate that
return powerStatus->getIsCharging() ? 1000 : (ledOn ? 2 : 1000);
return powerStatus->getIsCharging() ? 1000 : (ledOn ? 1 : 1000);
}
concurrency::Periodic ledPeriodic(ledBlinker);
/// Wrapper to convert our powerFSM stuff into a 'thread'
class PowerFSMThread : public OSThread
{
public:
// callback returns the period for the next callback invocation (or 0 if we should no longer be called)
PowerFSMThread() : OSThread("PowerFSM") {}
protected:
int32_t runOnce()
{
powerFSM.run_machine();
/// If we are in power state we force the CPU to wake every 10ms to check for serial characters (we don't yet wake
/// cpu for serial rx - FIXME)
auto state = powerFSM.getState();
canSleep = (state != &statePOWER) && (state != &stateSERIAL);
return 10;
}
};
/**
* Watch a GPIO and if we get an IRQ, wake the main thread.
* Use to add wake on button press
*/
void wakeOnIrq(int irq, int mode)
{
attachInterrupt(
irq,
[] {
BaseType_t higherWake = 0;
mainDelay.interruptFromISR(&higherWake);
},
FALLING);
}
class ButtonThread : public OSThread
{
// Prepare for button presses
#ifdef BUTTON_PIN
OneButton userButton;
OneButton userButton;
#endif
#ifdef BUTTON_PIN_ALT
OneButton userButtonAlt;
OneButton userButtonAlt;
#endif
void userButtonPressed()
{
powerFSM.trigger(EVENT_PRESS);
}
void userButtonPressedLong()
{
screen.adjustBrightness();
}
public:
static uint32_t longPressTime;
// callback returns the period for the next callback invocation (or 0 if we should no longer be called)
ButtonThread() : OSThread("Button")
{
#ifdef BUTTON_PIN
userButton = OneButton(BUTTON_PIN, true, true);
#ifdef INPUT_PULLUP_SENSE
// Some platforms (nrf52) have a SENSE variant which allows wake from sleep - override what OneButton did
pinMode(BUTTON_PIN, INPUT_PULLUP_SENSE);
#endif
userButton.attachClick(userButtonPressed);
userButton.attachDuringLongPress(userButtonPressedLong);
userButton.attachDoubleClick(userButtonDoublePressed);
userButton.attachLongPressStart(userButtonPressedLongStart);
userButton.attachLongPressStop(userButtonPressedLongStop);
wakeOnIrq(BUTTON_PIN, FALLING);
#endif
#ifdef BUTTON_PIN_ALT
userButtonAlt = OneButton(BUTTON_PIN_ALT, true, true);
#ifdef INPUT_PULLUP_SENSE
// Some platforms (nrf52) have a SENSE variant which allows wake from sleep - override what OneButton did
pinMode(BUTTON_PIN_ALT, INPUT_PULLUP_SENSE);
#endif
userButtonAlt.attachClick(userButtonPressed);
userButtonAlt.attachDuringLongPress(userButtonPressedLong);
userButtonAlt.attachDoubleClick(userButtonDoublePressed);
userButtonAlt.attachLongPressStart(userButtonPressedLongStart);
userButtonAlt.attachLongPressStop(userButtonPressedLongStop);
wakeOnIrq(BUTTON_PIN_ALT, FALLING);
#endif
}
protected:
/// If the button is pressed we suppress CPU sleep until release
int32_t runOnce()
{
canSleep = true; // Assume we should not keep the board awake
#ifdef BUTTON_PIN
userButton.tick();
canSleep &= userButton.isIdle();
#endif
#ifdef BUTTON_PIN_ALT
userButtonAlt.tick();
canSleep &= userButtonAlt.isIdle();
#endif
// if (!canSleep) DEBUG_MSG("Supressing sleep!\n");
// else DEBUG_MSG("sleep ok\n");
return 5;
}
private:
static void userButtonPressed()
{
// DEBUG_MSG("press!\n");
powerFSM.trigger(EVENT_PRESS);
}
static void userButtonPressedLong()
{
// DEBUG_MSG("Long press!\n");
screen->adjustBrightness();
// If user button is held down for 10 seconds, shutdown the device.
if (millis() - longPressTime > 10 * 1000) {
#ifdef TBEAM_V10
if (axp192_found == true) {
power->shutdown();
}
#endif
} else {
//DEBUG_MSG("Long press %u\n", (millis() - longPressTime));
}
}
static void userButtonDoublePressed()
{
#ifndef NO_ESP32
disablePin();
#endif
}
static void userButtonPressedLongStart()
{
DEBUG_MSG("Long press start!\n");
longPressTime = millis();
}
static void userButtonPressedLongStop()
{
DEBUG_MSG("Long press stop!\n");
longPressTime = 0;
}
};
static Periodic *ledPeriodic;
static OSThread *powerFSMthread, *buttonThread;
uint32_t ButtonThread::longPressTime = 0;
RadioInterface *rIf = NULL;
void setup()
{
#ifdef SEGGER_STDOUT_CH
SEGGER_RTT_ConfigUpBuffer(SEGGER_STDOUT_CH, NULL, NULL, 1024, SEGGER_RTT_MODE_NO_BLOCK_TRIM);
#endif
#ifdef USE_SEGGER
SEGGER_RTT_ConfigUpBuffer(0, NULL, NULL, 0, SEGGER_RTT_MODE_NO_BLOCK_TRIM);
#endif
@@ -177,41 +297,46 @@ void setup()
digitalWrite(RESET_OLED, 1);
#endif
OSThread::setup();
ledPeriodic = new Periodic("Blink", ledBlinker);
fsInit();
router = new DSRRouter();
#ifdef I2C_SDA
Wire.begin(I2C_SDA, I2C_SCL);
#else
Wire.begin();
#endif
// i2c still busted on new board
#ifndef ARDUINO_NRF52840_PPR
scanI2Cdevice();
#ifdef PIN_LCD_RESET
// FIXME - move this someplace better, LCD is at address 0x3F
pinMode(PIN_LCD_RESET, OUTPUT);
digitalWrite(PIN_LCD_RESET, 0);
delay(1);
digitalWrite(PIN_LCD_RESET, 1);
delay(1);
#endif
scanI2Cdevice();
// Buttons & LED
#ifdef BUTTON_PIN
userButton = OneButton(BUTTON_PIN, true, true);
userButton.attachClick(userButtonPressed);
userButton.attachDuringLongPress(userButtonPressedLong);
#endif
#ifdef BUTTON_PIN_ALT
userButtonAlt = OneButton(BUTTON_PIN_ALT, true, true);
userButtonAlt.attachClick(userButtonPressed);
userButton.attachDuringLongPress(userButtonPressedLong);
#endif
buttonThread = new ButtonThread();
#ifdef LED_PIN
pinMode(LED_PIN, OUTPUT);
digitalWrite(LED_PIN, 1 ^ LED_INVERTED); // turn on for now
#endif
ledPeriodic.setup();
// Hello
DEBUG_MSG("Meshtastic swver=%s, hwver=%s\n", optstr(APP_VERSION), optstr(HW_VERSION));
#ifndef NO_ESP32
// Don't init display if we don't have one or we are waking headless due to a timer event
if (wakeCause == ESP_SLEEP_WAKEUP_TIMER)
ssd1306_found = false; // forget we even have the hardware
screen_found = 0; // forget we even have the hardware
esp32Setup();
#endif
@@ -237,54 +362,58 @@ void setup()
#endif
// Initialize the screen first so we can show the logo while we start up everything else.
#if defined(ST7735_CS) || defined(HAS_EINK)
screen.setup();
#else
if (ssd1306_found)
screen.setup();
#endif
screen.print("Started...\n");
screen = new graphics::Screen(screen_found);
readFromRTC(); // read the main CPU RTC at first (in case we can't get GPS time)
// If we know we have a L80 GPS, don't try UBLOX
#ifndef L80_RESET
// If we don't have bidirectional comms, we can't even try talking to UBLOX
UBloxGPS *ublox = NULL;
#ifdef GPS_TX_PIN
// Init GPS - first try ublox
auto ublox = new UBloxGPS();
ublox = new UBloxGPS();
gps = ublox;
if (!gps->setup()) {
DEBUG_MSG("ERROR: No UBLOX GPS found\n");
delete ublox;
gps = ublox = NULL;
}
#endif
if (GPS::_serial_gps) {
// Some boards might have only the TX line from the GPS connected, in that case, we can't configure it at all. Just
// assume NMEA at 9600 baud.
// dumb NMEA access only work for serial GPSes)
DEBUG_MSG("Hoping that NMEA might work\n");
if (!gps && GPS::_serial_gps) {
// Some boards might have only the TX line from the GPS connected, in that case, we can't configure it at all. Just
// assume NMEA at 9600 baud.
// dumb NMEA access only work for serial GPSes)
DEBUG_MSG("Hoping that NMEA might work\n");
#ifdef HAS_AIR530_GPS
gps = new Air530GPS();
gps = new Air530GPS();
#else
gps = new NMEAGPS();
gps = new NMEAGPS();
#endif
gps->setup();
}
gps->setup();
}
#else
gps = new NMEAGPS();
gps->setup();
#endif
if (gps)
gpsStatus->observe(&gps->newStatus);
else
DEBUG_MSG("Warning: No GPS found - running without GPS\n");
nodeStatus->observe(&nodeDB.newStatus);
service.init();
// Don't call screen setup until after nodedb is setup (because we need
// the current region name)
#if defined(ST7735_CS) || defined(HAS_EINK)
screen->setup();
#else
if (screen_found)
screen->setup();
#endif
screen->print("Started...\n");
// We have now loaded our saved preferences from flash
// ONCE we will factory reset the GPS for bug #327
@@ -342,10 +471,11 @@ void setup()
if (!rIf)
recordCriticalError(ErrNoRadio);
else
router.addInterface(rIf);
router->addInterface(rIf);
// This must be _after_ service.init because we need our preferences loaded from flash to have proper timeout values
PowerFSM_setup(); // we will transition to ON in a couple of seconds, FIXME, only do this for cold boots, not waking from SDS
powerFSMthread = new PowerFSMThread();
// setBluetoothEnable(false); we now don't start bluetooth until we enter the proper state
setCPUFast(false); // 80MHz is fine for our slow peripherals
@@ -368,21 +498,12 @@ uint32_t axpDebugRead()
return 30 * 1000;
}
concurrency::Periodic axpDebugOutput(axpDebugRead);
Periodic axpDebugOutput(axpDebugRead);
axpDebugOutput.setup();
#endif
void loop()
{
uint32_t msecstosleep = 1000 * 30; // How long can we sleep before we again need to service the main loop?
if (gps)
gps->loop(); // FIXME, remove from main, instead block on read
router.loop();
powerFSM.run_machine();
service.loop();
concurrency::periodicScheduler.loop();
// axpDebugOutput.loop();
#ifdef DEBUG_PORT
@@ -394,29 +515,10 @@ void loop()
#ifndef NO_ESP32
esp32Loop();
#endif
#ifdef TBEAM_V10
power->loop();
#endif
#ifdef BUTTON_PIN
userButton.tick();
#endif
#ifdef BUTTON_PIN_ALT
userButtonAlt.tick();
#endif
loopWifi();
// For debugging
// if (rIf) ((RadioLibInterface *)rIf)->isActivelyReceiving();
// Show boot screen for first 3 seconds, then switch to normal operation.
static bool showingBootScreen = true;
if (showingBootScreen && (millis() > 3000)) {
screen.stopBootScreen();
showingBootScreen = false;
}
#ifdef DEBUG_STACK
static uint32_t lastPrint = 0;
if (millis() - lastPrint > 10 * 1000L) {
@@ -425,19 +527,18 @@ void loop()
}
#endif
// Update the screen last, after we've figured out what to show.
screen.debug_info()->setChannelNameStatus(getChannelName());
// No GPS lock yet, let the OS put the main CPU in low power mode for 100ms (or until another interrupt comes in)
// i.e. don't just keep spinning in loop as fast as we can.
// DEBUG_MSG("msecs %d\n", msecstosleep);
// FIXME - until button press handling is done by interrupt (see polling above) we can't sleep very long at all or buttons
// feel slow
msecstosleep = 10; // FIXME, stop early if something happens and sleep much longer
// TODO: This should go into a thread handled by FreeRTOS.
handleWebResponse();
delay(msecstosleep);
service.loop();
long delayMsec = mainController.runOrDelay();
/* if (mainController.nextThread && delayMsec)
DEBUG_MSG("Next %s in %ld\n", mainController.nextThread->ThreadName.c_str(),
mainController.nextThread->tillRun(millis())); */
// We want to sleep as long as possible here - because it saves power
mainDelay.delay(delayMsec);
// if (didWake) DEBUG_MSG("wake!\n");
}

View File

@@ -1,28 +1,23 @@
#pragma once
#include "graphics/Screen.h"
#include "PowerStatus.h"
#include "GPSStatus.h"
#include "NodeStatus.h"
#include "PowerStatus.h"
#include "graphics/Screen.h"
extern bool axp192_found;
extern bool ssd1306_found;
extern bool isCharging;
extern bool isUSBPowered;
// Global Screen singleton.
extern graphics::Screen screen;
//extern Observable<meshtastic::PowerStatus> newPowerStatus; //TODO: move this to main-esp32.cpp somehow or a helper class
extern graphics::Screen *screen;
// extern Observable<meshtastic::PowerStatus> newPowerStatus; //TODO: move this to main-esp32.cpp somehow or a helper class
//extern meshtastic::PowerStatus *powerStatus;
//extern meshtastic::GPSStatus *gpsStatus;
//extern meshtastic::NodeStatusHandler *nodeStatusHandler;
// extern meshtastic::PowerStatus *powerStatus;
// extern meshtastic::GPSStatus *gpsStatus;
// extern meshtastic::NodeStatusHandler *nodeStatusHandler;
// Return a human readable string of the form "Meshtastic_ab13"
const char *getDeviceName();
void nrf52Setup(), esp32Setup(), nrf52Loop(), esp32Loop();

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