Compare commits

...

267 Commits
0.9.3 ... 1.1.2

Author SHA1 Message Date
Kevin Hester
a6b82ccfd9 Merge pull request #457 from geeksville/master
fixes based on alpha tester report
2020-10-07 16:55:41 -07:00
Kevin Hester
e8b8ec69f1 1.1.2 2020-10-08 07:48:14 +08:00
Kevin Hester
023f1c24fb RTC: add notion of 'quality' for different time sources
Allow use of mesh based time until a GPS time arrives
2020-10-08 07:46:20 +08:00
Kevin Hester
f00d07baa3 RTC: pull rtc code into own file for cleanup 2020-10-08 07:28:57 +08:00
Kevin Hester
62c228b986 gps: don't stop lock attempts just because the main cpu is sleeping 2020-10-08 06:23:53 +08:00
Kevin Hester
1a3cc40c7e sx1262 better to check for header because preamble might never result in irq 2020-10-08 06:23:05 +08:00
Kevin Hester
bdcd5c3981 allow reporting # sats before we ahve a fix 2020-10-08 06:22:25 +08:00
Kevin Hester
fc82e872d6 don't require gps to have lock before we'll trust GPS time 2020-10-08 05:23:52 +08:00
Kevin Hester
b47c54b5b6 keep lora radio totally unpowered when in deep-sleep 2020-10-07 17:52:44 +08:00
Kevin Hester
c0c83ad389 If we are not supposed to share locations make sure phone doesn't either 2020-10-07 17:46:25 +08:00
Kevin Hester
23aecbdc38 Merge pull request #456 from geeksville/power
major cleanup of GPS code and changes to keep GPS asleep
2020-10-06 23:26:39 -07:00
Kevin Hester
eca7242a1f fix NRF52 build 2020-10-07 14:00:59 +08:00
Kevin Hester
ef899425b8 1.1.1 2020-10-07 13:46:39 +08:00
Kevin Hester
269f90c510 Force GPS power to zero when in deep sleep 2020-10-07 13:44:17 +08:00
Kevin Hester
7a5832ab8a SX1262: fix serious bug with detecting if we have a rx packet in progress
Could cause hangs on the way into sleep (and enormous power consumption).
Instead of checking for rx packet length (which only changes at completion)
check if we've received preamble bits but haven't yet received a completed
packet interrupt.

notes:


wait to sleep loop problem
radio wait to sleep, txEmpty=0
radio wait to sleep, txEmpty=0
radio wait to sleep, txEmpty=0
radio wait to sleep, txEmpty=0
radio wait to sleep, txEmpty=0
radio wait to sleep, txEmpty=0
Can not send yet, busyRx
radio wait to sleep, txEmpty=0
radio wait to sleep, txEmpty=0
radio wait to sleep, txEmpty=0
radio wait to sleep, txEmpty=0
radio wait to sleep, txEmpty=0
radio wait to sleep, txEmpty=0
radio wait to sleep, txEmpty=0
radio wait to sleep, txEmpty=0

vs normal run
radio wait to sleep, txEmpty=0
radio wait to sleep, txEmpty=0
radio wait to sleep, txEmpty=0
radio wait to sleep, txEmpty=0
radio wait to sleep, txEmpty=0
radio wait to sleep, txEmpty=0
radio wait to sleep, txEmpty=0
radio wait to sleep, txEmpty=0
radio wait to sleep, txEmpty=0
radio wait to sleep, txEmpty=0
radio wait to sleep, txEmpty=0
radio wait to sleep, txEmpty=0
radio wait to sleep, txEmpty=0
radio wait to sleep, txEmpty=0
radio wait to sleep, txEmpty=0
Starting low level send (id=0x53fe1dd0 Fr0xe5 To0xff, WantAck0, HopLim3 encrypted)
Completed sending (id=0x53fe1dd0 Fr0xe5 To0xff, WantAck0, HopLim3 encrypted)
2020-10-07 13:43:51 +08:00
Kevin Hester
044cc26340 #376 use power off command instead of killing gps power per manual 9.5 2020-10-07 11:44:30 +08:00
Kevin Hester
4ccd03623f bug #376: disable the "wake on serial" feature, because it causes
bogus wakes on TBEAMS because the USB->SERIAL chip pulls the RX input
to ground.  This feature is no longer needed because in !isRouter
nodes we force the node to never sleep anyways when on USB power.

    // this doesn't work on TBEAMs when the USB is depowered (causes bogus interrupts)
    // So we disable this "wake on serial" feature - because now when a TBEAM (only) has power connected it
    // never tries to go to sleep if the user is using the API
    // gpio_wakeup_enable((gpio_num_t)SERIAL0_RX_GPIO, GPIO_INTR_LOW_LEVEL);
2020-10-06 14:24:08 +08:00
Kevin Hester
7854a22fbf bug #376 - stop using pmu IRQ - we don't need it and it causes LS wakes 2020-10-06 12:45:19 +08:00
Kevin Hester
943d5cb08d bug #376 we were not staying in light sleep as long as intended 2020-10-06 11:48:53 +08:00
Kevin Hester
7480eb1826 Change to use zeros for timeout values that are 'default' 2020-10-06 09:43:00 +08:00
Kevin Hester
c32c97c389 TBEAM 1.1 has an extra controllable LED - blink that also 2020-10-06 08:20:06 +08:00
Kevin Hester
ef146fc0b5 bug #376 - wip time only mode now works 2020-10-06 06:27:46 +08:00
Kevin Hester
f6861a8fe2 bug #376 wip - we now minimize comms to gps to save power 2020-10-06 06:07:30 +08:00
Kevin Hester
736642455f bug #376 wip - we now respect the new gps_operating_mode pref. 2020-10-06 05:34:56 +08:00
Kevin Hester
3c1c11e439 bug #376 wip - we now kill gps power when it is supposed to be asleep 2020-10-05 15:29:26 +08:00
Kevin Hester
b072eec4ac wip for #376 2020-10-05 14:43:44 +08:00
Kevin Hester
ff9b49ddaa add lora32 schematic
Signed-off-by: Kevin Hester <kevinh@geeksville.com>
2020-10-05 11:03:30 +08:00
Kevin Hester
b8863c8a07 Merge remote-tracking branch 'root/master' into power 2020-10-05 10:56:50 +08:00
Jm Casler
71cdbb1a73 Merge pull request #454 from mc-hamster/master
Update readme to link to the US aliexpress listing
2020-10-01 20:39:38 -07:00
Jm Casler
200aa27cc0 Merge branch 'master' of https://github.com/mc-hamster/Meshtastic-device 2020-10-01 20:08:01 -07:00
Jm Casler
f7752e4f9d Merge pull request #12 from meshtastic/master
Update my repo from head
2020-10-01 20:07:07 -07:00
Jm Casler
7f0e8a8d6b Update readme to link to the US aliexpress listing for T-Beam V1.1 w/ NEO-M8N /w SX1262 2020-10-01 20:04:12 -07:00
Jm Casler
1f6877606f Merge pull request #453 from ikstream/update_readme
Fix update/install instructions
2020-10-01 17:32:55 -07:00
geeksville
1907873831 gps wip for #376 2020-10-01 10:04:04 -07:00
geeksville
bacc6caf04 wip gps power fixes #376 2020-10-01 09:17:43 -07:00
geeksville
56d4250197 Merge remote-tracking branch 'root/master' into power 2020-10-01 07:51:24 -07:00
geeksville
d66cede7fc Merge branch 'eink' into power 2020-10-01 07:51:01 -07:00
Stefan Venz
f7ffd196e3 Fix update/install instructions 2020-10-01 11:22:41 +02:00
geeksville
3a638090a2 update protos for #376 2020-09-30 07:47:16 -07:00
geeksville
4342ae74fb Merge remote-tracking branch 'root/master' 2020-09-30 07:24:38 -07:00
Kevin Hester
5a7962896d Merge pull request #449 from geeksville/eink
pulling in some common GPS fixes, so I can use them on ESP32
2020-09-29 14:21:00 -07:00
Kevin Hester
cfb9a600e4 Merge branch 'master' into eink 2020-09-29 14:14:10 -07:00
geeksville
7f3217d69e update image build script 2020-09-29 14:12:26 -07:00
Jm Casler
d94be0f534 Merge pull request #448 from mc-hamster/master
Add frequency information to debug output (#447) and cleanup of screen.cpp and meshwifi.cpp
2020-09-29 01:09:55 -07:00
Jm Casler
17a3e6e975 Merge branch 'master' of https://github.com/mc-hamster/Meshtastic-device 2020-09-29 00:59:31 -07:00
Jm Casler
423cbc2c6d Added frequency information to debug output (#447) and cleanup of screen.cpp and meshwifi.cpp 2020-09-29 00:59:26 -07:00
geeksville
124a82888d add power testing notes for eink 2020-09-28 17:38:36 -07:00
geeksville
fec7a6bf17 add air530 gps sleep support 2020-09-28 17:04:19 -07:00
geeksville
bc50b39a3b put eink screen to sleep to save power 2020-09-28 16:08:52 -07:00
geeksville
158e3edbe7 eink generate full image 2020-09-28 15:21:54 -07:00
geeksville
116fe6d109 eink bootloader finished 2020-09-28 15:18:32 -07:00
geeksville
6a4ef7e1d1 eink board serial flash seems to work 2020-09-28 14:10:33 -07:00
geeksville
a0fd83428f eink use RESET button as regular button instead 2020-09-28 13:10:27 -07:00
geeksville
e5d4fbb164 fix pins per email eink 2020-09-28 13:03:54 -07:00
Jm Casler
154dd3990c Merge pull request #11 from meshtastic/master
Merge pull request #446 from mc-hamster/master
2020-09-27 09:34:27 -07:00
Jm Casler
78fe41710b Merge pull request #446 from mc-hamster/master
Give more details of why wifi is disconnected & Set device hostname with hardwire ID #445
2020-09-27 01:04:01 -07:00
Jm Casler
1f38404e60 Merge branch 'master' into master 2020-09-27 00:58:12 -07:00
Jm Casler
848760e5bf Set device hostname with hardwire ID #445 2020-09-27 00:55:41 -07:00
Jm Casler
5ebac0cd54 Merge branch 'master' of https://github.com/mc-hamster/Meshtastic-device 2020-09-26 23:38:22 -07:00
Jm Casler
9b4079317b Give more details of why wifi is disconnected 2020-09-26 23:37:58 -07:00
Jm Casler
0f64332f93 Merge pull request #444 from mc-hamster/master
Display gps altitude on display #443
2020-09-26 18:50:00 -07:00
Jm Casler
fd62edbcab Merge branch 'master' into master 2020-09-26 18:44:53 -07:00
Kevin Hester
46abb9ae3f Merge pull request #441 from geeksville/eink
add preiminary support for TTGO eink board
2020-09-26 18:39:35 -07:00
Jm Casler
b5361ef89f Display gps altitude on display #443 2020-09-26 18:37:51 -07:00
geeksville
bf808f57fe add air530 manuals 2020-09-26 18:25:10 -07:00
geeksville
648589ed16 translate important parts of the Air530 datasheet to english 2020-09-26 18:16:32 -07:00
geeksville
28ec0e310d make bat voltage sensing work on eink 2020-09-26 18:13:16 -07:00
geeksville
956d9e96f2 Merge branch 'eink' of https://github.com/geeksville/Meshtastic-esp32 into eink 2020-09-26 13:49:54 -07:00
geeksville
266ba03bb7 route debug output back to the CDC-ACM device instead of JLINK 2020-09-26 13:49:22 -07:00
Kevin Hester
04c54840f4 Merge branch 'master' into eink 2020-09-26 12:58:13 -07:00
geeksville
db33200468 remove more stale eink code 2020-09-26 12:51:05 -07:00
geeksville
d7fbcf89bf cleanup todo eink now ready to merge 2020-09-26 12:36:11 -07:00
geeksville
a8b1bc735a remove debugging code eink gps now works 2020-09-26 12:12:50 -07:00
geeksville
ba8c640d6e eink leds kinda work now 2020-09-26 10:53:02 -07:00
geeksville
d88d2780f4 eink display now kinda works 2020-09-26 09:40:48 -07:00
geeksville
b0bbf95b03 LORA now works on the eink board (new schematic from ttgo) 2020-09-26 07:25:32 -07:00
geeksville
7ca150bf07 personal notes about threading 2020-09-26 06:50:54 -07:00
Jm Casler
9c7aa02db8 Merge pull request #440 from mc-hamster/master
Merging from my fork into master for some cleanup, more code comments, reduce compile warnings and move the "Mode" text up a line.
2020-09-26 00:20:27 -07:00
Jm Casler
6f444ed4b5 Accidently left #include "nimble/BluetoothUtil.h" in screen 2020-09-26 00:03:23 -07:00
Jm Casler
28119bf1bf Fix for #439 and some cleanup 2020-09-26 00:01:02 -07:00
Jm Casler
7fdb5b594d Merge pull request #10 from meshtastic/master
update my fork from head
2020-09-25 16:40:01 -07:00
Kevin Hester
6013fceb10 Merge pull request #438 from geeksville/master
fix TCP API server and get ready to end "region specific" firmware builds
2020-09-25 16:27:29 -07:00
geeksville
2996c7c8e2 Make tcp API now work. Sample usage and caveats below:
Sample usage:

First configure device to use @mc-hamster's new wifi stuff:
meshtastic --set wifi_ssid mylanname --set wifi_password mylanpassword

Then reboot the device (so wifi starts up).

(assuming device was assigned addr 192.168.81.45)
meshtastic --info --host 192.168.81.45
(See the usual device info you previously had to get over USB)

Caveats:

* Currently we are limiting to one active TCP connection open at once, if
you open a new session the old one is closed automatically
* There are no access controls/authentication needed to open a TCP
connection to the device
* Currently main.cpp is kinda dumb about how we should schedule work and
we rely on too many helper loop() functions.  Very soon in my queue
(related to all the other cleanup) is to add a basic notion of coroutines,
so that we can get away from freertos threads and this old school arduino
loop function.  Once that cleanup happens we can the a) have much lower
battery consumption (always) and b) super fast response for all operations.
2020-09-25 16:18:30 -07:00
geeksville
d1c3078698 shrink guard for channel settings lock 2020-09-25 12:52:26 -07:00
geeksville
3e5f81bf2a move region into userpreferences, to end region specific firmwares 2020-09-25 12:52:08 -07:00
geeksville
772d045166 more eink notes 2020-09-25 10:41:27 -07:00
geeksville
1a064a4666 Merge remote-tracking branch 'root/master' 2020-09-25 09:19:58 -07:00
Kevin Hester
e81c89dcae Merge pull request #436 from meshtastic/dev-wifi
Initial Merge of WiFi Soft AP, WiFi Client, HTTP Server, DNS Server, Captive Portal and Apple Captive Network Assistant (CNA)
2020-09-25 09:18:44 -07:00
geeksville
1f36139e99 remove Signal 2020-09-25 09:14:44 -07:00
geeksville
6f77244af3 eink wip notes 2020-09-25 09:14:00 -07:00
Jm Casler
05351ce3e4 Merge branch 'master' into dev-wifi 2020-09-24 17:21:16 -07:00
geeksville
a79aff4778 WIP - I think some pins are wrong, waiting for new schematic 2020-09-24 16:36:07 -07:00
geeksville
86d6f88787 Merge remote-tracking branch 'root/master' into eink 2020-09-24 15:13:08 -07:00
geeksville
5fbeaee0b0 eink kinda builds 2020-09-24 14:36:43 -07:00
Jm Casler
fed4dfd410 Merge pull request #435 from mc-hamster/master
Update dev-wifi from my fork
2020-09-24 11:15:47 -07:00
Jm Casler
0d3b8bdb22 Note not to file bugs 2020-09-24 11:07:30 -07:00
Kevin Hester
3c4f56f3bf Merge pull request #434 from geeksville/dev-wifi
Add stubs so that portduino can build with wifi. cc @mc-hamster
2020-09-24 10:51:39 -07:00
geeksville
d3c00584a2 oops - the NRF52 build rules are suppose to inherit from arduino_base. 2020-09-24 10:36:37 -07:00
Jm Casler
ce7fa65595 David's latest changes to the HTML UI 2020-09-23 19:04:12 -07:00
geeksville
9c0a0ad220 Add stubs so that portduino can build with wifi. cc @mc-hamster 2020-09-23 17:22:17 -07:00
Kevin Hester
22d0ef36b8 Merge pull request #431 from KenVanHoeylandt/patch-1
Fix for broken link to device API docs
2020-09-23 17:12:52 -07:00
Ken Van Hoeylandt
829c5f493c Fix for broken link to device API docs
bluetooth-api.md -> device-api.md
2020-09-23 20:16:21 +02:00
Jm Casler
2d8eb8e205 Merge pull request #9 from meshtastic/dev-wifi
from dev-wifi to my fork
2020-09-22 22:09:06 -07:00
Jm Casler
52a3927585 Merge branch 'master' into dev-wifi 2020-09-22 22:00:55 -07:00
Jm Casler
b39f6c96bd Merge remote-tracking branch 'origin/dev-wifi' 2020-09-22 22:00:07 -07:00
Jm Casler
a96c2e0eac Merge pull request #428 from mc-hamster/master
Initial check-in of David's UI
2020-09-22 21:10:48 -07:00
Jm Casler
10b24c0269 Initial check-in of David's UI 2020-09-22 21:01:31 -07:00
Jm Casler
4140883684 Merge pull request #426 from comgram/kr
Add Korean Frequency
2020-09-22 05:46:18 -07:00
geeksville
3352fae64c fix #371 allow button while using API. also don't let tbeams sleep if they have USB power 2020-09-21 12:41:39 -07:00
comgram
55cb0c52ee Add Korean Frequency 2020-09-21 16:10:20 +09:00
Jm Casler
626d82614c Merge pull request #425 from mc-hamster/master
Fix for - dev-wifi: Detailed error reporting of reason why WiFi didn't connect #424
2020-09-19 22:05:57 -07:00
Jm Casler
d890068acb dev-wifi: Detailed error reporting of reason why WiFi didn't connect #424 2020-09-19 21:58:21 -07:00
Jm Casler
468ad39a94 Merge pull request #7 from meshtastic/dev-wifi
Bringing in the API changes
2020-09-19 17:27:29 -07:00
Jm Casler
5cbe06c2b0 Merge pull request #419 from geeksville/dev-wifi
Add API server on port 4403 (kinda a WIP, seems to work but I haven't
2020-09-19 17:25:24 -07:00
Jm Casler
3412ecfe7b Merge pull request #6 from meshtastic/dev-wifi
Pulling from meshtastic dev-wifi to my work area
2020-09-19 17:21:56 -07:00
Jm Casler
adb16a334c Merge pull request #423 from mc-hamster/master
Add more detail on the connection status on the screen #422
2020-09-19 17:20:35 -07:00
Jm Casler
377f0bda5d Add more detail on the connection status on the screen #422 2020-09-19 17:15:03 -07:00
Jm Casler
51ab853658 Merge pull request #421 from mc-hamster/master
Fix for Bug #420 : Wifi in station mode sometimes enters loops of repeatedly joining
2020-09-19 16:46:05 -07:00
Jm Casler
464a42258f Fix for "Wifi in station mode sometimes enters loops of repeatedly joining... #420"
Fix for Wifi in station mode sometimes enters loops of repeatedly joining... #420
2020-09-19 16:38:59 -07:00
geeksville
9e9c50e6d8 Add API server on port 4403 (kinda a WIP, seems to work but I haven't
finished the python client code)
2020-09-19 12:54:49 -07:00
Jm Casler
945f726b65 Merge branch 'master' of https://github.com/mc-hamster/Meshtastic-device 2020-09-19 12:50:46 -07:00
Jm Casler
7c44daf8f4 pushing my chances to personal branch so i can get the changes from the laptop 2020-09-19 12:50:43 -07:00
Jm Casler
c57a9a8613 Update from my laptop 2020-09-19 11:24:55 -07:00
geeksville
3c0429deee Add new factory_reset preferences option clients can set 2020-09-19 11:19:42 -07:00
Kevin Hester
5d8f541e70 Merge pull request #418 from geeksville/master
1.1.0
2020-09-19 10:33:48 -07:00
geeksville
bd126b866c 1.1.0 2020-09-19 10:32:57 -07:00
Kevin Hester
036a1991b8 Merge pull request #403 from r51n/anz
Add Australia/New Zealand channels and build target
2020-09-19 07:48:17 -07:00
Jm Casler
6e3b22c624 Stub for a handler of the root (/) of the web server with a html table and form for chat 2020-09-18 20:42:35 -07:00
Jm Casler
82fbedbf41 Auto formatting of meshwifi.cpp 2020-09-18 18:51:42 -07:00
Jm Casler
0929b86d62 Merge pull request #411 from mc-hamster/master
Moved handleDNSResponse from main into handleWebResponse and used the auto format
2020-09-18 18:36:40 -07:00
Jm Casler
65fc1cf4a6 Moved handleDNSResponse into handleWebResponse and used the autoformatter 2020-09-18 18:29:16 -07:00
Jm Casler
6f753799fd Merge pull request #410 from mc-hamster/master
Completed Software AP and basic Captive Portal
2020-09-18 18:20:27 -07:00
Jm Casler
4d72afebe6 Soft AP and basic captive portal done. 2020-09-18 18:16:58 -07:00
Jm Casler
b1643e6036 Merge pull request #5 from meshtastic/dev-wifi
Dev wifi
2020-09-18 18:06:13 -07:00
Jm Casler
362d8cb831 Merge branch 'master' into dev-wifi 2020-09-18 18:02:56 -07:00
Jm Casler
b203c95dd1 changes for soft ap + captive portal 2020-09-18 15:33:03 -07:00
Kevin Hester
8d4672964c Merge pull request #409 from geeksville/dev-wifi
Make wifi optional and exclude from nrf52 builds
2020-09-18 13:38:00 -07:00
geeksville
6e5e5822aa Make wifi optional and exclude from nrf52 builds 2020-09-18 10:48:39 -07:00
Kevin Hester
5fb0bf2575 Merge branch 'master' into anz 2020-09-18 10:19:43 -07:00
Kevin Hester
9af2045dc1 Merge pull request #404 from geeksville/master
Merge portduino/sim beginnings into mainline
2020-09-18 10:18:33 -07:00
Kevin Hester
9624cc3798 Merge pull request #405 from mc-hamster/master
Initial Check-in of wifi changes into dev-wifi
2020-09-18 10:18:12 -07:00
geeksville
3541228c1f update to my latest radiolib 2020-09-17 11:41:34 -07:00
geeksville
cc95361fdc Merge remote-tracking branch 'root/master' 2020-09-17 11:24:42 -07:00
geeksville
9b1d1ad0a5 add linux/portduino to the CI test build
Signed-off-by: geeksville <kevinh@geeksville.com>
2020-09-17 11:23:46 -07:00
geeksville
7e467f1466 Merge branch 'portduino' 2020-09-17 11:20:50 -07:00
geeksville
d3e28e3e2c Merge branch 'dev' 2020-09-17 11:20:41 -07:00
r51n
1a1a0fbfbe add ANZ to build list 2020-09-17 12:04:38 +00:00
r51n
91305c2c84 add AU/NZ channel definitions 2020-09-17 12:02:38 +00:00
Jm Casler
48dd6d388d Merge branch 'master' of https://github.com/mc-hamster/Meshtastic-device 2020-09-16 23:16:19 -07:00
Jm Casler
64710a6a04 renamed reconnectWiFi to initWifi 2020-09-16 23:16:11 -07:00
Jm Casler
c83ff03d66 Merge pull request #4 from meshtastic/master
Updating form head
2020-09-16 22:51:19 -07:00
Jm Casler
73b47a78aa Clean up and added comments about the esp32 sdk bug 2020-09-16 22:31:07 -07:00
Jm Casler
493b25f23e Final checkin of WiFi and basic HTTP server 2020-09-16 20:15:00 -07:00
Kevin Hester
004f1f625b Merge pull request #398 from geeksville/master
misc atomic bug fixes - see commits
2020-09-16 10:10:00 -07:00
geeksville
fc20f658e6 Fix #362 by @a-f-G-U-C - I was mispelling NMEA ;-) 2020-09-16 09:22:03 -07:00
geeksville
8e988cc926 fix #397 from @a-f-G-U-C - bogus GPS positions during locking could be reported
btw - from my read of the NMEA, the lowest value that means 'has a valid
position' is 1 not 2.  But I only know this because you pointed me at
it ;-)

Thanks!
2020-09-16 09:18:44 -07:00
geeksville
8c240b59f6 fix #393. immediately save node db to disk if user changes name 2020-09-16 09:08:35 -07:00
geeksville
f847e30a3c Merge remote-tracking branch 'root/master' 2020-09-16 09:04:27 -07:00
geeksville
7050ae4ba1 Merge branch 'master' into portduino 2020-09-15 21:14:42 -07:00
geeksville
3e64d8439d Merge branch 'master' into dev 2020-09-15 21:13:24 -07:00
geeksville
435c955acd make hash prototype a bit stricter 2020-09-15 21:05:57 -07:00
Jm Casler
c656a95a84 Merge pull request #3 from meshtastic/master
Update from meshtastic main
2020-09-15 20:34:30 -07:00
Jm Casler
27ad8472c1 remove ssid info from configuration.h 2020-09-15 20:24:58 -07:00
Jm Casler
3fcd4a61aa commenting out the strcpy to manually set the wifi info. 2020-09-15 20:24:03 -07:00
geeksville
c6d93d1a28 fix #346 limit tx power in japan 2020-09-15 18:54:50 -07:00
Kevin Hester
b0c82dcb5b Merge pull request #394 from geeksville/master
misc bug fixes
2020-09-15 18:19:58 -07:00
geeksville
7d4058f49d let users set 20 dBm if they wish 2020-09-15 18:00:21 -07:00
geeksville
31fc8fafec make default power level radio chipset specific 2020-09-15 17:55:33 -07:00
geeksville
313cee9a3f update webpage 2020-09-15 17:54:55 -07:00
Jm Casler
066d9d48a4 New method to deinit the wifi stack. 2020-09-14 20:27:49 -07:00
geeksville
16de4a0d2e Merge remote-tracking branch 'root/master' 2020-09-14 09:25:31 -07:00
geeksville
621fcb598e Declare 1.0 2020-09-14 09:25:14 -07:00
Jm Casler
b2a6a4000b Merge pull request #2 from meshtastic/master
Updating from head
2020-09-14 00:05:21 -07:00
Jm Casler
f5c939fb10 Fix to turn WiFi back on after the bluetooth radio is reenabled. 2020-09-13 22:22:49 -07:00
Jm Casler
e508306395 Refactoring to break out HTTP from WiFi 2020-09-13 16:58:36 -07:00
Kevin Hester
cc6b500029 Merge pull request #388 from geeksville/master
1.0.0
2020-09-13 12:11:32 -07:00
geeksville
887b1b5dcd Merge remote-tracking branch 'root/master' 2020-09-13 11:58:31 -07:00
geeksville
62e333b235 1.0.0 2020-09-13 11:53:31 -07:00
Jm Casler
d859700497 Merge branch 'master' into master 2020-09-12 21:55:01 -07:00
Jm Casler
f129b458ad Initial Checkin for WiFi and HTTP Server Framework 2020-09-12 21:43:41 -07:00
Kevin Hester
bf68ad7cf5 Merge pull request #382 from geeksville/master
0.9.7
2020-09-12 10:31:19 -07:00
geeksville
6d9bdbb628 0.9.7 2020-09-12 10:21:44 -07:00
Kevin Hester
6782c2d3d1 Merge pull request #380 from mc-hamster/master
Fix for screen crashing after GPS lock.
2020-09-12 10:20:13 -07:00
Jm Casler
b6f71ca1db Update screen.cpp for for crash when gps gets lock
Update screen.cpp for for crash when gps gets a lock
2020-09-11 22:17:45 -07:00
geeksville
92df77f228 Merge remote-tracking branch 'root/master' into dev 2020-09-11 18:15:13 -07:00
Jm Casler
caeea41867 Merge pull request #1 from meshtastic/master
Merge from original repo
2020-09-11 17:59:02 -07:00
Kevin Hester
bc17dd1a0f Merge pull request #378 from geeksville/master
0.9.6
2020-09-11 13:41:09 -07:00
geeksville
2a3175470b merge remote-tracking branch 'root/master' 2020-09-11 13:31:28 -07:00
geeksville
8ca6bbfb78 0.9.6 2020-09-11 13:30:44 -07:00
Kevin Hester
9777762052 Merge pull request #368 from geeksville/master
misc bug fixes
2020-09-11 11:42:51 -07:00
geeksville
da01f0ab7f Merge remote-tracking branch 'mine/master' 2020-09-10 10:11:53 -07:00
geeksville
1cd81208c0 Merge remote-tracking branch 'root/master' 2020-09-10 10:11:15 -07:00
Kevin Hester
2394075d94 Merge branch 'master' into master 2020-09-10 10:11:04 -07:00
geeksville
de7b9877f9 remove auto-inserted whitespace that might confuse platformio 2020-09-10 09:51:53 -07:00
geeksville
ee27c15c2c likely fix for bug #373. fix #339. great gps fixes from @a-f-G-U-C
fixes described in bug #376
2020-09-10 09:25:10 -07:00
geeksville
076f8bd77b fix #370 by pulling in my bugfix to ESP32-Nimble 2020-09-09 14:15:43 -07:00
Kevin Hester
288363b3a6 Merge pull request #369 from gkelly/fix-no-button
Fix compilation without a BUTTON_PIN definition
2020-09-08 12:49:24 -07:00
Garret Kelly
48256d6e9e Fix compilation without a BUTTON_PIN definition 2020-09-08 15:22:49 -04:00
geeksville
c007302564 fix #363 gps altitude - based on tip by @a-f-G-U-C 2020-09-08 10:28:53 -07:00
geeksville
0a9f7147f3 probably fix #341 enable internal pullup on lora-v2 button 2020-09-08 10:25:37 -07:00
geeksville
46ba36511a Merge branch 'portduino' into dev
# Conflicts:
#	platformio.ini
2020-09-07 16:31:17 -07:00
Kevin Hester
170f0693c6 Merge pull request #366 from mc-hamster/master
New screen to fix issue #352
2020-09-07 15:31:16 -07:00
Kevin Hester
d900509fbc Merge branch 'master' into master 2020-09-07 15:26:35 -07:00
Kevin Hester
8018c27dcd Merge pull request #367 from geeksville/master
test fixing CI build
2020-09-07 15:26:24 -07:00
geeksville
8df5ac9d3f pull in my fixed nrf52 platform 2020-09-07 15:22:00 -07:00
geeksville
c3196f47ef Merge remote-tracking branch 'root/master' 2020-09-07 15:13:09 -07:00
geeksville
81a49d4e3c Merge branch 'post1' 2020-09-07 15:12:14 -07:00
Kevin Hester
4f32c36db8 Merge branch 'master' into master 2020-09-07 15:04:01 -07:00
Kevin Hester
5db0bb3368 Merge pull request #361 from drewsed/add-new-cases-to-both-README
Added a new 3D printable case to \README.md and updated \docs\README.md fixes #360
2020-09-07 15:02:55 -07:00
geeksville
f3d38d84c9 fix nrf52 build 2020-09-07 13:03:37 -07:00
geeksville
f8bb6bbcb4 try to bang on SPI from simulator 2020-09-06 16:32:13 -07:00
geeksville
e0d5b9dce1 use simradio on portduino 2020-09-06 16:09:07 -07:00
geeksville
94e4b30125 add crude sim getmacaddr 2020-09-06 16:07:32 -07:00
geeksville
2a067e7f6b make gps optional. Portduino almost works in sim! 2020-09-06 14:45:43 -07:00
geeksville
97ab07e05c WIP 2020-09-06 10:09:40 -07:00
geeksville
b8d39845cf portduino now links! 2020-09-06 09:24:08 -07:00
Jm Casler
cbea36a151 Merge branch 'master' into master 2020-09-05 22:53:03 -07:00
Jm Casler
49dea6d6bd Update Screen.cpp
Added commnets for concepts for future TODO work.
2020-09-05 22:36:57 -07:00
Jm Casler
7d4c6c7086 Update Screen.cpp
- Changed "No Satellite Lock" to "No GPS Lock"
- Fixed bug when there's no battery. Now shows "USB"
- Tested on tlora-v1
2020-09-05 14:41:00 -07:00
geeksville
fefd3d78f3 Portduino WIP now compiles but does not link 2020-09-05 12:34:48 -07:00
Jm Casler
f41a77c46d Initial add of status screen
Initial add of the status screen.

TODO:
- Get status of the BT Radio
- Display something on line 3
2020-09-05 09:30:18 -07:00
geeksville
6a475d8288 WIP 2020-09-04 17:23:17 -07:00
geeksville
c629b94333 portduino WIP 2020-09-04 15:03:22 -07:00
drewsed
5423f4e06c Added a new 3D printable case to \README.md and updated \docs\README.md 2020-09-04 10:41:22 +02:00
Kevin Hester
88f8bbe21e Merge pull request #358 from Dafeman/master
Update build instructions
2020-09-02 14:36:13 -07:00
Dafeman
eda4862f0d Update build-instructions.md 2020-09-03 08:34:52 +12:00
Dafeman
7ca752cd32 Merge pull request #5 from meshtastic/master
Merge latest
2020-09-03 08:14:37 +12:00
Kevin Hester
c5ffebc498 Merge pull request #355 from geeksville/master
stop using post1 for now (it was a dumb idea)
2020-09-01 16:33:17 -07:00
geeksville
baa12aa5b3 Merge branch 'master' into post1 2020-09-01 08:54:39 -07:00
geeksville
ccf3522ada Merge remote-tracking branch 'root/master' 2020-09-01 08:53:31 -07:00
geeksville
d14cf5aa94 Merge remote-tracking branch 'root/post1' 2020-09-01 08:53:26 -07:00
geeksville
810429b54f Merge branch 'master' into post1 2020-08-31 09:10:24 -07:00
Kevin Hester
915427c964 Merge pull request #350 from geeksville/master
misc bugfix
2020-08-30 14:43:48 -07:00
geeksville
347484baaf 0.9.5 2020-08-30 14:38:23 -07:00
geeksville
bb6913a56a Merge remote-tracking branch 'root/master' 2020-08-30 13:48:16 -07:00
geeksville
6cdaf8c600 fix #349 channel suffix letter didn't match between device and phone 2020-08-30 12:38:15 -07:00
geeksville
c370eb4a88 allow restarting failed upgrades (fixes an android autobug report) 2020-08-30 12:21:05 -07:00
geeksville
8dc4492ba3 add taiwan frequencies 2020-08-30 12:20:43 -07:00
geeksville
901cc536ef less logspam 2020-08-29 15:17:32 -07:00
Kevin Hester
41c2732e4f Merge pull request #347 from geeksville/post1
Post1
2020-08-29 13:13:40 -07:00
Kevin Hester
2d12a363db Merge pull request #348 from geeksville/master
finish (ish) corvus board
2020-08-29 13:13:25 -07:00
geeksville
28455f0056 Merge branch 'master' into post1 2020-08-29 12:47:27 -07:00
geeksville
5125126aec Merge branch 'corvus' 2020-08-29 12:47:01 -07:00
geeksville
f1ca1ee3c0 okay basic support for @bigcorvus board is done 2020-08-29 12:46:42 -07:00
geeksville
dffb6c2f06 If display is on on the @BigCorvus board and we xmit the board browns out? 2020-08-28 17:38:23 -07:00
geeksville
7f214ffbb0 TFT kinda correct now - but slow because of bit banging 2020-08-28 15:33:33 -07:00
geeksville
e049eac38a TFT display kinda draws stuff (badly) 2020-08-28 15:06:52 -07:00
geeksville
338445d175 basic init of the Corvus TFT display works 2020-08-28 14:24:22 -07:00
Kevin Hester
8eb492d356 Merge pull request #345 from geeksville/post1
update from master
2020-08-27 14:56:28 -07:00
Kevin Hester
a5341d766e Merge pull request #344 from geeksville/master
lora32 again
2020-08-27 14:56:18 -07:00
geeksville
c78142b235 Merge remote-tracking branch 'root/post1' into post1 2020-08-27 14:50:54 -07:00
geeksville
9ebaa2b962 Merge remote-tracking branch 'root/master' 2020-08-27 14:49:52 -07:00
geeksville
79498580b1 If LORA32 battery voltage is super low, assume no battery installed 2020-08-27 14:46:59 -07:00
Kevin Hester
41901aed97 Merge pull request #342 from geeksville/master
ttgo lora32 fixes
2020-08-26 14:06:25 -07:00
geeksville
2729a513ab Merge remote-tracking branch 'root/master' 2020-08-26 14:00:43 -07:00
geeksville
af046e7dbd bug #322 - ttgo lora32 deep sleep
oh - I think I found the problem (probably)!  we were isolating gpio12 (which isn't used on other boards) to save power during sleep.  gpio12 is the button for this board. @thomslik would you mind pulling this commit and seeing if it works better?
2020-08-26 14:00:10 -07:00
Kevin Hester
0caf534b65 Merge pull request #338 from geeksville/master
fix heltec battery display
2020-08-25 13:06:32 -07:00
geeksville
f650222e94 Merge remote-tracking branch 'root/master' 2020-08-25 13:01:19 -07:00
geeksville
5c40378805 fix #336 don't send battery status on nodes without batteries 2020-08-25 13:00:55 -07:00
geeksville
780b7e3628 don't show battery status on boards that can't sense that #336 2020-08-25 12:48:47 -07:00
geeksville
83ae3c7714 doc rename 2020-08-25 12:48:19 -07:00
Kevin Hester
25ebb9adb8 Merge pull request #337 from geeksville/master
fix screen redrawing
2020-08-25 12:14:07 -07:00
geeksville
7f6a0e7ddc Merge remote-tracking branch 'root/master' 2020-08-25 12:08:38 -07:00
geeksville
f62e6793c5 Fix #333 screens were redrawing when they should not
thanks @drewsed for the clear report - made it easy to repro/fix
due to refactoring/cleanup a couple of weeks back
2020-08-25 12:08:18 -07:00
geeksville
babd57ecde add hw docs 2020-08-25 12:06:55 -07:00
geeksville
de196810a2 add debugging for SNR values on SX1262 2020-08-25 12:06:36 -07:00
Kevin Hester
83726086a9 Merge pull request #329 from geeksville/post1
Post1
2020-08-22 09:13:55 -07:00
116 changed files with 43294 additions and 1366 deletions

View File

@@ -23,4 +23,4 @@ jobs:
run: |
pip install -U adafruit-nrfutil
- name: Build
run: platformio run -e tbeam -e heltec -e lora-relay-v1
run: platformio run -e tbeam -e heltec -e lora-relay-v1 -e linux

View File

@@ -48,13 +48,17 @@
"optional": "cpp",
"string_view": "cpp",
"cassert": "cpp",
"iterator": "cpp"
"iterator": "cpp",
"shared_mutex": "cpp"
},
"cSpell.words": [
"Blox",
"EINK",
"HFSR",
"Meshtastic",
"NEMAGPS",
"NMEAGPS",
"RDEF",
"Ublox",
"bkpt",
"cfsr",

View File

@@ -25,11 +25,14 @@ We currently support three models of radios.
- TTGO T-Beam (usually the recommended choice)
- [T-Beam V1.1 w/ NEO-6M - special Meshtastic version](https://www.aliexpress.com/item/4001178678568.html) (Includes built-in OLED display and they have **preinstalled** the meshtastic software)
- [T-Beam V1.1 w/ NEO-M8N](https://www.aliexpress.com/item/33047631119.html) (slightly better GPS)
- [T-Beam V1.1 w/ NEO-M8N /w SX1262](https://www.aliexpress.com/item/4001287221970.html) (slightly better GPS + LoRa)
- board labels "TTGO T22_V1.1 20191212"
- [T-Beam V0.7 w/ NEO-6M](https://www.aliexpress.com/item/4000574335430.html) (will work but **you must use the tbeam0.7 firmware ** - but the T-Beam V1.0 or later are better!)
- board labels "TTGO T22_V07 20180711"
- 3D printable cases
- [T-Beam V0](https://www.thingiverse.com/thing:3773717)
- [T-Beam V1](https://www.thingiverse.com/thing:3830711)
- [T-Beam V1 (SMA-antenna)](https://www.thingiverse.com/thing:3830711)
- [T-Beam V1 (IPEX-antenna)](https://www.thingiverse.com/thing:4587297)
- Laser-cut cases
- [T-Beam V1](https://www.thingiverse.com/thing:4552771)
@@ -100,10 +103,10 @@ Hard resetting via RTS pin...
```
5. cd into the directory where the release zip file was expanded.
6. Install the correct firmware for your board with `device-install.sh firmware-_board_-_country_.bin`.
- Example: `./device-install.sh firmware-HELTEC-US-0.0.3.bin`.
7. To update run `device-update.sh firmware-_board_-_country_.bin`
- Example: `./device-update.sh firmware-HELTEC-US-0.0.3.bin`.
6. Install the correct firmware for your board with `device-install.sh -f firmware-_board_-_country_.bin`.
- Example: `./device-install.sh -f firmware-HELTEC-US-0.0.3.bin`.
7. To update run `device-update.sh -f firmware-_board_-_country_.bin`
- Example: `./device-update.sh -f firmware-HELTEC-US-0.0.3.bin`.
Note: If you have previously installed meshtastic, you don't need to run this full script instead just run `esptool.py --baud 921600 write_flash 0x10000 firmware-_board_-_country_-_version_.bin`. This will be faster, also all of your current preferences will be preserved.

View File

@@ -4,7 +4,7 @@ set -e
source bin/version.sh
COUNTRIES="US EU433 EU865 CN JP"
COUNTRIES="US EU433 EU865 CN JP ANZ KR"
#COUNTRIES=US
#COUNTRIES=CN

22
bin/install-bootloader.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-124-g69bd8eb-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

6
bin/qspi-flash-test.sh Executable file
View File

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

View File

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

61
boards/eink.json Normal file
View File

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

View File

@@ -43,7 +43,8 @@ For an detailed walk-through aimed at beginners, we recommend [meshtastic.letsta
Note: Updates are happening almost daily, only major updates are listed below. For more details see our forum.
- 06/24/2020 - 0.7.x Now with over 1000 android users, over 600 people using the radios and translated into 13 languages. Fairly stable and we are working through bugs to get to 1.0.
- 09/14/2020 - 1.0.0 Now with over 1700 android users, over 2000 nodes and translated into 15 languages. This project will always be a "beta" experiment, but now quite usable. We are currently selecting 1.1 features in our discussion forum.
- 06/24/2020 - 0.7.x Now with over 1000 android users, over 600 people using the radios and translated into 22 languages. Fairly stable and we are working through bugs to get to 1.0.
- 06/04/2020 - 0.6.7 Beta releases of both the application and the device code are released. Features are fairly solid now with a sizable number of users.
- 04/28/2020 - 0.6.0 [Python API](https://pypi.org/project/meshtastic/) released. Makes it easy to use meshtastic devices as "zero config / just works" mesh transport adapters for other projects.
- 04/20/2020 - 0.4.3 Pretty solid now both for the android app and the device code. Many people have donated translations and code. Probably going to call it a beta soon.
@@ -76,11 +77,15 @@ Make sure to buy the frequency range which is legal for your country. For the US
Instructions for installing prebuilt firmware can be found [here](https://github.com/meshtastic/Meshtastic-esp32/blob/master/README.md).
For a nice printable cases:
For a nice looking cases:
1. TTGO T-Beam V0 see this [design](https://www.thingiverse.com/thing:3773717) by [bsiege](https://www.thingiverse.com/bsiege).
2. TTGO T_Beam V1 see this [design](https://www.thingiverse.com/thing:3830711) by [rwanrooy](https://www.thingiverse.com/rwanrooy) or this [remix](https://www.thingiverse.com/thing:3949330) by [8ung](https://www.thingiverse.com/8ung)
3. Heltec Lora32 see this [design](https://www.thingiverse.com/thing:3125854) by [ornotermes](https://www.thingiverse.com/ornotermes).
- 3D printable cases
1. TTGO T-Beam V0 see this [design](https://www.thingiverse.com/thing:3773717) by [bsiege](https://www.thingiverse.com/bsiege).
2. TTGO T_Beam V1 (SMA) see this [design](https://www.thingiverse.com/thing:3830711) by [rwanrooy](https://www.thingiverse.com/rwanrooy) or this [remix](https://www.thingiverse.com/thing:3949330) by [8ung](https://www.thingiverse.com/8ung)
3. TTGO T_Beam V1 (IPEX) see this [design](https://www.thingiverse.com/thing:4587297) by [drewsed](https://www.thingiverse.com/drewsed)
4. Heltec Lora32 see this [design](https://www.thingiverse.com/thing:3125854) by [ornotermes](https://www.thingiverse.com/ornotermes).
- Laser-cut cases
1. TTGO T_Beam V1 (SMA) see this [design](https://www.thingiverse.com/thing:4552771) by [jefish](https://www.thingiverse.com/jefish)
# IMPORTANT DISCLAIMERS AND FAQ

View File

@@ -1,7 +1,7 @@
theme: jekyll-theme-cayman
title: Meshtastic
description: An opensource hiking, pilot, skiing, Signal-App-extending GPS mesh communicator
description: An opensource hiking, pilot, skiing, secure GPS mesh communicator
google_analytics: G-DRZ5H5EXHV
include: [".well-known"]

Binary file not shown.

BIN
docs/hardware/T-SX1262.pdf Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

File diff suppressed because one or more lines are too long

View File

@@ -2,6 +2,16 @@
You probably don't care about this section - skip to the next one.
Threading tasks:
- 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.
Nimble tasks:
- readerror.txt stress test bug

View File

@@ -4,13 +4,19 @@ This project uses the simple PlatformIO build system. PlatformIO is an extension
## GUI
1. Purchase a suitable [radio](https://github.com/meshtastic/Meshtastic-device/wiki/Hardware-Information).
2. Install [PlatformIO](https://platformio.org/platformio-ide).
3. Click the PlatformIO icon on the side bar. ![platformio icon](https://user-images.githubusercontent.com/47490997/89482668-77c7ea00-d7ee-11ea-8785-5faf8ff99800.png)
4. Under `Quick Access, Miscellaneous, Clone Git Project` enter the URL of the Meshtastic repo found [here](https://github.com/meshtastic/Meshtastic-device). ![image](https://user-images.githubusercontent.com/47490997/89483047-4c91ca80-d7ef-11ea-91f4-1d53d4e8acd9.png)
5. Select a file location to save the repo.
6. Once loaded, open the `platformio.ini` file.
7. At the line `default_envs` you can change it to the board type you are building for ie. `tlora-v2, tlora-v1, tlora-v2-1-1.6, tbeam, heltec, tbeam0.7` (boards are listed further down in the file).
8. Click the PlatformIO icon on the side bar. Under `Project Tasks` you can now build or upload.
2. Install [Python](https://www.python.org/downloads/).
3. Install [Git](https://git-scm.com/downloads).
4. Reboot your computer.
5. Install [PlatformIO](https://platformio.org/platformio-ide).
6. Click the PlatformIO icon on the side bar. ![platformio icon](https://user-images.githubusercontent.com/47490997/89482668-77c7ea00-d7ee-11ea-8785-5faf8ff99800.png)
7. Under `Quick Access, Miscellaneous, Clone Git Project` enter the URL of the Meshtastic repo found [here](https://github.com/meshtastic/Meshtastic-device). ![image](https://user-images.githubusercontent.com/47490997/89483047-4c91ca80-d7ef-11ea-91f4-1d53d4e8acd9.png)
8. Select a file location to save the repo.
9. Once loaded, open the `platformio.ini` file.
10. At the line `default_envs` you can change it to the board type you are building for ie. `tlora-v2, tlora-v1, tlora-v2-1-1.6, tbeam, heltec, tbeam0.7` (boards are listed further down in the file).
11. The hardware can be configured for different countries by adding a definition to the `configuration.h` file. `#define HW_VERSION_US` or `HW_VERSION_EU433, HW_VERSION_EU865, HW_VERSION_CN, HW_VERSION_JP`. Other country settings can be found in `MeshRadio.h`. The default is `HW_VERSION_US`.
12. Click the PlatformIO icon on the side bar. Under `Project Tasks` you can now build or upload.
Note - To get a clean build you may have to delete the auto-generated file `./.vscode/c_cpp_properties.json`, close and re-open Visual Studio and WAIT until the file is auto-generated before compiling again.
## Command Line
1. Purchase a suitable [radio](https://github.com/meshtastic/Meshtastic-device/wiki/Hardware-Information).

View File

@@ -0,0 +1,17 @@
You probably don't care about this ugly file of personal notes ;-)
for taiwan region:
bin/run.sh --set region 8
time only mode
./bin/run.sh --set gps_operation 3
ublox parsing failure
record power measurements and update spreadsheet
have loop methods return allowable sleep time (from their perspective)
increase main cpu sleep time
warn people about crummy gps antennas - add to faq

View File

@@ -196,7 +196,7 @@ Nice ideas worth considering someday...
- DONE neg 7 error code from receive
- DONE remove unused sx1262 lib from github
- at boot we are starting our message IDs at 1, rather we should start them at a random number. also, seed random based on timer. this could be the cause of our first message not seen bug.
- add a NEMA based GPS driver to test GPS
- add a NMEA based GPS driver to test GPS
- DONE use "variants" to get all gpio bindings
- DONE plug in correct variants for the real board
- turn on DFU assistance in the appload using the nordic DFU helper lib call

View File

@@ -32,11 +32,15 @@ From lower to higher power consumption.
onEntry: setBluetoothOn(true)
onExit:
- full on (ON) - Everything is on
- serial API usage (SERIAL) - Screen is on, device doesn't sleep, bluetooth off
onEntry: setBluetooth off, screen on
onExit:
- 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)
- serial API usage (SERIAL) - Screen is on, device doesn't sleep, bluetooth off
- 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
onExit:
@@ -56,9 +60,11 @@ From lower to higher power consumption.
- While in NB/DARK/ON: If we receive EVENT_NODEDB_UPDATED we transition to ON (so the new screen can be shown)
- While in DARK: While the phone talks to us over BLE (EVENT_CONTACT_FROM_PHONE) reset any sleep timers and stay in DARK (needed for bluetooth sw update and nice user experience if the user is reading/replying to texts)
- while in LS/NB/DARK: if SERIAL_CONNECTED, go to serial
- while in any state: if we have AC power, go to POWER
### events that decrease cpu activity
- While in POWER: if lose AC go to ON
- While in SERIAL: if SERIAL_DISCONNECTED, go to NB
- While in ON: If PRESS event occurs, reset screen_on_secs timer and tell the screen to handle the pess
- While in ON: If it has been more than screen_on_secs since a press, lower to DARK

View File

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

69
nrf52/ttgo_eink_qpsi.ini Normal file
View File

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

View File

@@ -9,7 +9,8 @@
; https://docs.platformio.org/page/projectconf.html
[platformio]
default_envs = tbeam # lora-relay-v1
default_envs = tbeam # lora-relay-v1 # nrf52840dk-geeksville # linux # or if you'd like to change the default to something like lora-relay-v1 put that here
;default_envs = heltec # lora-relay-v1 # nrf52840dk-geeksville # linux # or if you'd like to change the default to something like lora-relay-v1 put that here
[common]
; common is not currently used
@@ -23,15 +24,9 @@ default_envs = tbeam # lora-relay-v1
[env]
framework = arduino
; customize the partition table
; http://docs.platformio.org/en/latest/platforms/espressif32.html#partition-tables
board_build.partitions = partition-table.csv
; note: we add src to our include search path so that lmic_project_config can override
; FIXME: fix lib/BluetoothOTA dependency back on src/ so we can remove -Isrc
build_flags = -Wno-missing-field-initializers -Isrc -Isrc/mesh -Isrc/gps -Ilib/nanopb/include -Os -Wl,-Map,.pio/build/output.map
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}
@@ -40,6 +35,10 @@ build_flags = -Wno-missing-field-initializers -Isrc -Isrc/mesh -Isrc/gps -Ilib/n
;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
; the default is esptool
; upload_protocol = esp-prog
@@ -60,32 +59,49 @@ debug_tool = jlink
lib_deps =
https://github.com/meshtastic/esp8266-oled-ssd1306.git ; ESP8266_SSD1306
SPI
1260 ; OneButton library for non-blocking button debounce
1202 ; CRC32, explicitly needed because dependency is missing in the ble ota update lib
Wire ; explicitly needed here because the AXP202 library forgets to add it
https://github.com/meshtastic/arduino-fsm.git
https://github.com/meshtastic/SparkFun_Ublox_Arduino_Library.git
https://github.com/meshtastic/RadioLib.git#d6b12f7eb0a06bd2414c79b437b25d377e3f603f
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/AXP202X_Library.git#8404abb6d4b486748636bc6ad72d2a47baaf5460
Wire ; explicitly needed here because the AXP202 library forgets to add it
SPI
; Common settings for conventional (non Portduino) Ardino targets
[arduino_base]
framework = arduino
lib_deps =
${env.lib_deps}
build_flags = ${env.build_flags} -Os
src_filter = ${env.src_filter} -<portduino/>
; Common settings for ESP targes, mixin with extends = esp32_base
[esp32_base]
extends = arduino_base
platform = espressif32
src_filter =
${env.src_filter} -<nrf52/>
${arduino_base.src_filter} -<nrf52/>
upload_speed = 921600
debug_init_break = tbreak setup
build_flags =
${env.build_flags} -Wall -Wextra -Isrc/esp32 -mfix-esp32-psram-cache-issue -lnimble -std=c++11
${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
# Hmm - this doesn't work yet
# board_build.ldscript = linker/esp32.extram.bss.ld
lib_ignore = segger_rtt
platform_packages =
framework-arduinoespressif32 @ https://github.com/meshtastic/arduino-esp32.git#1adba3f11ca8406ac0a704d151697b572058b53d
framework-arduinoespressif32@https://github.com/meshtastic/arduino-esp32.git#2814f110aa618429bdd9a0a2d6a93c55f29f87a6
; customize the partition table
; http://docs.platformio.org/en/latest/platforms/espressif32.html#partition-tables
board_build.partitions = partition-table.csv
; not needed included in ttgo-t-beam board file
; also to use PSRAM https://docs.platformio.org/en/latest/platforms/espressif32.html#external-ram-psram
@@ -94,12 +110,12 @@ platform_packages =
; -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG -DCORE_DEBUG_LEVEL=ARDUHAL_LOG_LEVEL_DEBUG
; The 1.0 release of the TBEAM board
; The 1.0 release of the TBEAM board
[env:tbeam]
extends = esp32_base
board = ttgo-t-beam
lib_deps =
${env.lib_deps}
${arduino_base.lib_deps}
build_flags =
${esp32_base.build_flags} -D TBEAM_V10
@@ -142,25 +158,26 @@ build_flags =
platform = https://github.com/HelTecAutomation/platform-asrmicro650x.git ; we use top-of-tree because stable version has too many bugs - asrmicro650x
board = cubecell_board_plus
; FIXME, bug in cubecell arduino - they are supposed to set ARDUINO
build_flags = ${env.build_flags} -DARDUINO=100 -Isrc/cubecell
build_flags = ${arduino_base.build_flags} -DARDUINO=100 -Isrc/cubecell
src_filter =
${env.src_filter} -<esp32/> -<nrf52/>
${arduino_base.src_filter} -<esp32/> -<nrf52/>
; Common settings for NRF52 based targets
[nrf52_base]
; Instead of the standard nordicnrf52 platform, we use our fork which has our added variant files
; platform = nordicnrf52
platform = https://github.com/meshtastic/platform-nordicnrf52.git#62d185fe61b6c84c554046106529b4fd8f155e2c
platform = https://github.com/meshtastic/platform-nordicnrf52.git#1a2639a6b0f79b5df66bea3e3089f0d5285fdc63
extends = arduino_base
debug_tool = jlink
build_type = debug ; I'm debugging with ICE a lot now
; note: liboberon provides the AES256 implementation for NRF52 (though not using the hardware acceleration of the NRF52840 - FIXME)
build_flags =
${env.build_flags} -Wno-unused-variable
${arduino_base.build_flags} -Wno-unused-variable
-Isrc/nrf52
-Isdk-nrfxlib/crypto/nrf_oberon/include -Lsdk-nrfxlib/crypto/nrf_oberon/lib/cortex-m4/hard-float/ -lliboberon_3.0.3
;-DCFG_DEBUG=3
src_filter =
${env.src_filter} -<esp32/> -<nimble/>
${arduino_base.src_filter} -<esp32/> -<nimble/> -<meshwifi/>
lib_ignore =
BluetoothOTA
monitor_port = /dev/ttyACM1
@@ -212,22 +229,50 @@ monitor_speed = 115200
extends = nrf52_base
board = ppr
lib_deps =
${env.lib_deps}
${arduino_base.lib_deps}
UC1701
; Prototype eink/nrf52840/sx1262 device
[env:eink]
extends = nrf52_base
board = eink
# 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/eink
-DBUSY_PIN=3 -DRST_PIN=2 -DDC_PIN=28 -DCS_PIN=30
src_filter = ${nrf52_base.src_filter} +<../variants/eink>
lib_deps =
${arduino_base.lib_deps}
https://github.com/geeksville/EPD_Libraries.git
TFT_eSPI
; The https://github.com/BigCorvus/SX1262-LoRa-BLE-Relay board by @BigCorvus
[env:lora-relay-v1]
extends = nrf52_base
board = lora-relay-v1
# 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_v1
-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
src_filter = ${nrf52_base.src_filter} +<../variants/lora_relay_v1>
lib_deps =
${env.lib_deps}
${arduino_base.lib_deps}
SparkFun BQ27441 LiPo Fuel Gauge Arduino Library
TFT_eSPI
# Adafruit ST7735 and ST7789 Library
; The Portduino based sim environment on top of linux
[env:linux]
platform = https://github.com/geeksville/platform-portduino.git
src_filter = ${env.src_filter} -<esp32/> -<nimble/> -<nrf52/> -<meshwifi/>
build_flags = ${arduino_base.build_flags} -O0
framework = arduino
board = linux_x86_64

2
proto

Submodule proto updated: 3caee2e5b9...a0b8d88896

View File

@@ -1,126 +1,98 @@
#pragma once
#include <Arduino.h>
#include "Status.h"
#include "configuration.h"
#include <Arduino.h>
namespace meshtastic {
namespace meshtastic
{
/// Describes the state of the GPS system.
class GPSStatus : public Status
/// Describes the state of the GPS system.
class GPSStatus : public Status
{
private:
CallbackObserver<GPSStatus, const GPSStatus *> statusObserver =
CallbackObserver<GPSStatus, const GPSStatus *>(this, &GPSStatus::updateStatus);
bool hasLock = false; // default to false, until we complete our first read
bool isConnected = false; // Do we have a GPS we are talking to
int32_t latitude = 0, longitude = 0; // as an int mult by 1e-7 to get value as double
int32_t altitude = 0;
uint32_t dop = 0; // Diminution of position; PDOP where possible (UBlox), HDOP otherwise (TinyGPS) in 10^2 units (needs
// scaling before use)
uint32_t heading = 0;
uint32_t numSatellites = 0;
public:
GPSStatus() { statusType = STATUS_TYPE_GPS; }
GPSStatus(bool hasLock, bool isConnected, int32_t latitude, int32_t longitude, int32_t altitude, uint32_t dop,
uint32_t heading, uint32_t numSatellites)
: Status()
{
this->hasLock = hasLock;
this->isConnected = isConnected;
this->latitude = latitude;
this->longitude = longitude;
this->altitude = altitude;
this->dop = dop;
this->heading = heading;
this->numSatellites = numSatellites;
}
GPSStatus(const GPSStatus &);
GPSStatus &operator=(const GPSStatus &);
private:
CallbackObserver<GPSStatus, const GPSStatus *> statusObserver = CallbackObserver<GPSStatus, const GPSStatus *>(this, &GPSStatus::updateStatus);
void observe(Observable<const GPSStatus *> *source) { statusObserver.observe(source); }
bool hasLock = false; // default to false, until we complete our first read
bool isConnected = false; // Do we have a GPS we are talking to
int32_t latitude = 0, longitude = 0; // as an int mult by 1e-7 to get value as double
int32_t altitude = 0;
uint32_t dop = 0; // Diminution of position; PDOP where possible (UBlox), HDOP otherwise (TinyGPS) in 10^2 units (needs scaling before use)
uint32_t heading = 0;
uint32_t numSatellites = 0;
bool getHasLock() const { return hasLock; }
public:
bool getIsConnected() const { return isConnected; }
GPSStatus() {
statusType = STATUS_TYPE_GPS;
}
GPSStatus( bool hasLock, bool isConnected, int32_t latitude, int32_t longitude, int32_t altitude, uint32_t dop, uint32_t heading, uint32_t numSatellites ) : Status()
int32_t getLatitude() const { return latitude; }
int32_t getLongitude() const { return longitude; }
int32_t getAltitude() const { return altitude; }
uint32_t getDOP() const { return dop; }
uint32_t getHeading() const { return heading; }
uint32_t getNumSatellites() const { return numSatellites; }
bool matches(const GPSStatus *newStatus) const
{
return (newStatus->hasLock != hasLock || newStatus->isConnected != isConnected || newStatus->latitude != latitude ||
newStatus->longitude != longitude || newStatus->altitude != altitude || newStatus->dop != dop ||
newStatus->heading != heading || newStatus->numSatellites != numSatellites);
}
int updateStatus(const GPSStatus *newStatus)
{
// Only update the status if values have actually changed
bool isDirty;
{
this->hasLock = hasLock;
this->isConnected = isConnected;
this->latitude = latitude;
this->longitude = longitude;
this->altitude = altitude;
this->dop = dop;
this->heading = heading;
this->numSatellites = numSatellites;
isDirty = matches(newStatus);
initialized = true;
hasLock = newStatus->hasLock;
isConnected = newStatus->isConnected;
latitude = newStatus->latitude;
longitude = newStatus->longitude;
altitude = newStatus->altitude;
dop = newStatus->dop;
heading = newStatus->heading;
numSatellites = newStatus->numSatellites;
}
GPSStatus(const GPSStatus &);
GPSStatus &operator=(const GPSStatus &);
void observe(Observable<const GPSStatus *> *source)
{
statusObserver.observe(source);
if (isDirty) {
if (hasLock)
DEBUG_MSG("New GPS pos lat=%f, lon=%f, alt=%d, pdop=%f, heading=%f, sats=%d\n", latitude * 1e-7, longitude * 1e-7,
altitude, dop * 1e-2, heading * 1e-5, numSatellites);
else
DEBUG_MSG("No GPS lock\n");
onNewStatus.notifyObservers(this);
}
return 0;
}
};
bool getHasLock() const
{
return hasLock;
}
bool getIsConnected() const
{
return isConnected;
}
int32_t getLatitude() const
{
return latitude;
}
int32_t getLongitude() const
{
return longitude;
}
int32_t getAltitude() const
{
return altitude;
}
uint32_t getDOP() const
{
return dop;
}
uint32_t getHeading() const
{
return heading;
}
uint32_t getNumSatellites() const
{
return numSatellites;
}
bool matches(const GPSStatus *newStatus) const
{
return (
newStatus->hasLock != hasLock ||
newStatus->isConnected != isConnected ||
newStatus->latitude != latitude ||
newStatus->longitude != longitude ||
newStatus->altitude != altitude ||
newStatus->dop != dop ||
newStatus->heading != heading ||
newStatus->numSatellites != numSatellites
);
}
int updateStatus(const GPSStatus *newStatus) {
// Only update the status if values have actually changed
bool isDirty;
{
isDirty = matches(newStatus);
initialized = true;
hasLock = newStatus->hasLock;
isConnected = newStatus->isConnected;
latitude = newStatus->latitude;
longitude = newStatus->longitude;
altitude = newStatus->altitude;
dop = newStatus->dop;
heading = newStatus->heading;
numSatellites = newStatus->numSatellites;
}
if(isDirty) {
DEBUG_MSG("New GPS pos lat=%f, lon=%f, alt=%d, pdop=%f, heading=%f, sats=%d\n", latitude * 1e-7, longitude * 1e-7, altitude, dop * 1e-2, heading * 1e-5, numSatellites);
onNewStatus.notifyObservers(this);
}
return 0;
}
};
}
} // namespace meshtastic
extern meshtastic::GPSStatus *gpsStatus;

View File

@@ -16,6 +16,8 @@ bool pmu_irq = false;
Power *power;
using namespace meshtastic;
/**
* If this board has a battery level sensor, set this to a valid implementation
*/
@@ -36,7 +38,7 @@ class AnalogBatteryLevel : public HasBatteryLevel
float v = getBattVoltage() / 1000;
if (v < 2.1)
return -1;
return -1; // If voltage is super low assume no battery installed
return 100 * (v - 3.27) / (4.2 - 3.27);
}
@@ -57,14 +59,17 @@ class AnalogBatteryLevel : public HasBatteryLevel
/**
* return true if there is a battery installed in this unit
*/
virtual bool isBatteryConnect() { return true; }
virtual bool isBatteryConnect() { return getBattVoltage() != -1; }
} analogLevel;
bool Power::analogInit()
{
#ifdef BATTERY_PIN
DEBUG_MSG("Using analog input for battery level\n");
#ifndef NO_ESP32
// ESP32 needs special analog stuff
adcAttachPin(BATTERY_PIN);
#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;
@@ -97,7 +102,7 @@ void Power::readPowerStatus()
if (batteryLevel) {
bool hasBattery = batteryLevel->isBatteryConnect();
int batteryVoltageMv = 0;
uint8_t batteryChargePercent = 0;
int8_t batteryChargePercent = 0;
if (hasBattery) {
batteryVoltageMv = batteryLevel->getBattVoltage();
// If the AXP192 returns a valid battery percentage, use it
@@ -114,13 +119,19 @@ void Power::readPowerStatus()
}
// Notify any status instances that are observing us
const meshtastic::PowerStatus powerStatus = meshtastic::PowerStatus(
hasBattery, batteryLevel->isVBUSPlug(), batteryLevel->isChargeing(), batteryVoltageMv, batteryChargePercent);
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());
newStatus.notifyObservers(&powerStatus);
// If we have a battery at all and it is less than 10% full, force deep sleep
if (powerStatus.getHasBattery() && !powerStatus.getHasUSB() && batteryLevel->getBattVoltage() < MIN_BAT_MILLIVOLTS)
powerFSM.trigger(EVENT_LOW_BATTERY);
} else {
// No power sensing on this board - tell everyone else we have no idea what is happening
const PowerStatus powerStatus = PowerStatus(OptUnknown, OptUnknown, OptUnknown, -1, -1);
newStatus.notifyObservers(&powerStatus);
}
}
@@ -161,7 +172,7 @@ bool Power::axp192Init()
DEBUG_MSG("----------------------------------------\n");
axp.setPowerOutPut(AXP192_LDO2, AXP202_ON); // LORA radio
axp.setPowerOutPut(AXP192_LDO3, AXP202_ON); // GPS main power
// axp.setPowerOutPut(AXP192_LDO3, AXP202_ON); // GPS main power - now turned on in setGpsPower
axp.setPowerOutPut(AXP192_DCDC2, AXP202_ON);
axp.setPowerOutPut(AXP192_EXTEN, AXP202_ON);
axp.setPowerOutPut(AXP192_DCDC1, AXP202_ON);
@@ -193,8 +204,11 @@ bool Power::axp192Init()
PMU_IRQ, [] { pmu_irq = true; }, FALLING);
axp.adc1Enable(AXP202_BATT_CUR_ADC1, 1);
axp.enableIRQ(AXP202_BATT_REMOVED_IRQ | AXP202_BATT_CONNECT_IRQ | AXP202_CHARGING_FINISHED_IRQ | AXP202_CHARGING_IRQ |
AXP202_VBUS_REMOVED_IRQ | AXP202_VBUS_CONNECT_IRQ | AXP202_PEK_SHORTPRESS_IRQ,
// we do not look for AXP202_CHARGING_FINISHED_IRQ & AXP202_CHARGING_IRQ because it occurs repeatedly while there is
// 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.clearIRQ();
@@ -230,9 +244,11 @@ void Power::loop()
}
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");

View File

@@ -4,25 +4,15 @@
#include "MeshService.h"
#include "NodeDB.h"
#include "configuration.h"
#include "main.h"
#include "graphics/Screen.h"
#include "main.h"
#include "sleep.h"
#include "target_specific.h"
#include "timing.h"
static void sdsEnter()
{
/*
// Don't deepsleep if we have USB power or if the user as pressed a button recently
// !isUSBPowered <- doesn't work yet because the axp192 isn't letting the battery fully charge when we are awake - FIXME
if (timing::millis() - lastPressMs > radioConfig.preferences.mesh_sds_timeout_secs)
{
doDeepSleep(radioConfig.preferences.sds_secs);
}
*/
doDeepSleep(radioConfig.preferences.sds_secs * 1000LL);
// FIXME - make sure GPS and LORA radio are off first - because we want close to zero current draw
doDeepSleep(getPref_sds_secs() * 1000LL);
}
#include "error.h"
@@ -31,7 +21,7 @@ static uint32_t secsSlept;
static void lsEnter()
{
DEBUG_MSG("lsEnter begin, ls_secs=%u\n", radioConfig.preferences.ls_secs);
DEBUG_MSG("lsEnter begin, ls_secs=%u\n", getPref_ls_secs());
screen.setOn(false);
secsSlept = 0; // How long have we been sleeping this time
@@ -40,13 +30,13 @@ static void lsEnter()
static void lsIdle()
{
// DEBUG_MSG("lsIdle begin ls_secs=%u\n", radioConfig.preferences.ls_secs);
// DEBUG_MSG("lsIdle begin ls_secs=%u\n", getPref_ls_secs());
#ifndef NO_ESP32
esp_sleep_source_t wakeCause = ESP_SLEEP_WAKEUP_UNDEFINED;
// Do we have more sleeping to do?
if (secsSlept < radioConfig.preferences.ls_secs) {
if (secsSlept < getPref_ls_secs()) {
// Briefly come out of sleep long enough to blink the led once every few seconds
uint32_t sleepTime = 30;
@@ -55,7 +45,8 @@ static void lsIdle()
setLed(false); // Never leave led on while in light sleep
wakeCause = doLightSleep(sleepTime * 1000LL);
if (wakeCause == ESP_SLEEP_WAKEUP_TIMER) {
switch (wakeCause) {
case ESP_SLEEP_WAKEUP_TIMER:
// Normal case: timer expired, we should just go back to sleep ASAP
setLed(true); // briefly turn on led
@@ -63,12 +54,15 @@ static void lsIdle()
secsSlept += sleepTime;
// DEBUG_MSG("sleeping, flash led!\n");
}
if (wakeCause == ESP_SLEEP_WAKEUP_UART) {
break;
case ESP_SLEEP_WAKEUP_UART:
// Not currently used (because uart triggers in hw have problems)
powerFSM.trigger(EVENT_SERIAL_CONNECTED);
} else {
// We woke for some other reason (button press, uart, device interrupt)
break;
default:
// We woke for some other reason (button press, device interrupt)
// uint64_t status = esp_sleep_get_ext1_wakeup_status();
DEBUG_MSG("wakeCause %d\n", wakeCause);
@@ -82,8 +76,10 @@ static void lsIdle()
powerFSM.trigger(EVENT_PRESS);
} else {
// Otherwise let the NB state handle the IRQ (and that state will handle stuff like IRQs etc)
// we lie and say "wake timer" because the interrupt will be handled by the regular IRQ code
powerFSM.trigger(EVENT_WAKE_TIMER);
}
break;
}
} else {
// Someone says we can't sleep now, so just save some power by sleeping the CPU for 100ms or so
@@ -101,7 +97,7 @@ static void lsIdle()
static void lsExit()
{
// setGPSPower(true); // restore GPS power
gps->startLock();
gps->forceWake(true);
}
static void nbEnter()
@@ -124,6 +120,12 @@ static void serialEnter()
screen.setOn(true);
}
static void powerEnter()
{
screen.setOn(true);
setBluetoothEnable(true);
}
static void onEnter()
{
screen.setOn(true);
@@ -131,7 +133,7 @@ static void onEnter()
static uint32_t lastPingMs;
uint32_t now = timing::millis();
uint32_t now = millis();
if (now - lastPingMs > 30 * 1000) { // if more than a minute since our last press, ask other nodes to update their state
if (displayedNodeNum)
@@ -140,8 +142,6 @@ static void onEnter()
}
}
static void wakeForPing() {}
static void screenPress()
{
screen.onPress();
@@ -156,16 +156,25 @@ 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");
Fsm powerFSM(&stateBOOT);
void PowerFSM_setup()
{
powerFSM.add_timed_transition(&stateBOOT, &stateON, 3 * 1000, NULL, "boot timeout");
// If we are not a router and we already have AC power go to POWER state after init, otherwise go to ON
// We assume routers might be powered all the time, but from a low current (solar) source
bool isLowPower = radioConfig.preferences.is_low_power;
bool hasPower = !isLowPower && powerStatus && powerStatus->getHasUSB();
bool isRouter = radioConfig.preferences.is_router;
DEBUG_MSG("PowerFSM init, USB power=%d\n", hasPower);
powerFSM.add_timed_transition(&stateBOOT, hasPower ? &statePOWER : &stateON, 3 * 1000, NULL, "boot timeout");
powerFSM.add_transition(&stateLS, &stateDARK, EVENT_WAKE_TIMER, wakeForPing, "Wake timer");
// wake timer expired or a packet arrived
// if we are a router node, we go to NB (no need for bluetooth) otherwise we go to DARK (so we can send message to phone)
powerFSM.add_transition(&stateLS, isRouter ? &stateNB : &stateDARK, EVENT_WAKE_TIMER, NULL, "Wake timer");
// Note we don't really use this transition, because when we wake from light sleep we _always_ transition to NB and then it
// handles things powerFSM.add_transition(&stateLS, &stateNB, EVENT_RECEIVED_PACKET, NULL, "Received packet");
// Note we don't really use this transition, because when we wake from light sleep we _always_ transition to NB or dark and
// then it handles things powerFSM.add_transition(&stateLS, &stateNB, EVENT_RECEIVED_PACKET, NULL, "Received packet");
powerFSM.add_transition(&stateNB, &stateNB, EVENT_RECEIVED_PACKET, NULL, "Received packet, resetting win wake");
@@ -173,7 +182,10 @@ void PowerFSM_setup()
powerFSM.add_transition(&stateLS, &stateON, EVENT_PRESS, NULL, "Press");
powerFSM.add_transition(&stateNB, &stateON, EVENT_PRESS, NULL, "Press");
powerFSM.add_transition(&stateDARK, &stateON, EVENT_PRESS, NULL, "Press");
powerFSM.add_transition(&statePOWER, &statePOWER, EVENT_PRESS, screenPress, "Press");
powerFSM.add_transition(&stateON, &stateON, EVENT_PRESS, screenPress, "Press"); // reenter On to restart our timers
powerFSM.add_transition(&stateSERIAL, &stateSERIAL, EVENT_PRESS, screenPress,
"Press"); // Allow button to work while in serial API
// Handle critically low power battery by forcing deep sleep
powerFSM.add_transition(&stateBOOT, &stateSDS, EVENT_LOW_BATTERY, NULL, "LowBat");
@@ -186,42 +198,57 @@ void PowerFSM_setup()
powerFSM.add_transition(&stateDARK, &stateON, EVENT_BLUETOOTH_PAIR, NULL, "Bluetooth pairing");
powerFSM.add_transition(&stateON, &stateON, EVENT_BLUETOOTH_PAIR, NULL, "Bluetooth pairing");
powerFSM.add_transition(&stateNB, &stateON, EVENT_NODEDB_UPDATED, NULL, "NodeDB update");
powerFSM.add_transition(&stateDARK, &stateON, EVENT_NODEDB_UPDATED, NULL, "NodeDB update");
powerFSM.add_transition(&stateON, &stateON, EVENT_NODEDB_UPDATED, NULL, "NodeDB update");
// if we are a router we don't turn the screen on for these things
if (!isRouter) {
// show the latest node when we get a new node db update
powerFSM.add_transition(&stateNB, &stateON, EVENT_NODEDB_UPDATED, NULL, "NodeDB update");
powerFSM.add_transition(&stateDARK, &stateON, EVENT_NODEDB_UPDATED, NULL, "NodeDB update");
powerFSM.add_transition(&stateON, &stateON, EVENT_NODEDB_UPDATED, NULL, "NodeDB update");
powerFSM.add_transition(&stateLS, &stateON, EVENT_RECEIVED_TEXT_MSG, NULL, "Received text");
powerFSM.add_transition(&stateNB, &stateON, EVENT_RECEIVED_TEXT_MSG, NULL, "Received text");
powerFSM.add_transition(&stateDARK, &stateON, EVENT_RECEIVED_TEXT_MSG, NULL, "Received text");
powerFSM.add_transition(&stateON, &stateON, EVENT_RECEIVED_TEXT_MSG, NULL, "Received text"); // restarts the sleep timer
// Show the received text message
powerFSM.add_transition(&stateLS, &stateON, EVENT_RECEIVED_TEXT_MSG, NULL, "Received text");
powerFSM.add_transition(&stateNB, &stateON, EVENT_RECEIVED_TEXT_MSG, NULL, "Received text");
powerFSM.add_transition(&stateDARK, &stateON, EVENT_RECEIVED_TEXT_MSG, NULL, "Received text");
powerFSM.add_transition(&stateON, &stateON, EVENT_RECEIVED_TEXT_MSG, NULL, "Received text"); // restarts the sleep timer
}
powerFSM.add_transition(&stateLS, &stateSERIAL, EVENT_SERIAL_CONNECTED, NULL, "serial API");
powerFSM.add_transition(&stateNB, &stateSERIAL, EVENT_SERIAL_CONNECTED, NULL, "serial API");
powerFSM.add_transition(&stateDARK, &stateSERIAL, EVENT_SERIAL_CONNECTED, NULL, "serial API");
powerFSM.add_transition(&stateON, &stateSERIAL, EVENT_SERIAL_CONNECTED, NULL, "serial API");
if (!isLowPower) {
powerFSM.add_transition(&stateLS, &statePOWER, EVENT_POWER_CONNECTED, NULL, "power connect");
powerFSM.add_transition(&stateNB, &statePOWER, EVENT_POWER_CONNECTED, NULL, "power connect");
powerFSM.add_transition(&stateDARK, &statePOWER, EVENT_POWER_CONNECTED, NULL, "power connect");
powerFSM.add_transition(&stateON, &statePOWER, EVENT_POWER_CONNECTED, NULL, "power connect");
}
powerFSM.add_transition(&statePOWER, &stateON, EVENT_POWER_DISCONNECTED, NULL, "power disconnected");
powerFSM.add_transition(&stateSERIAL, &stateON, EVENT_POWER_DISCONNECTED, NULL, "power disconnected");
powerFSM.add_transition(&stateSERIAL, &stateNB, EVENT_SERIAL_DISCONNECTED, NULL, "serial disconnect");
powerFSM.add_transition(&stateDARK, &stateDARK, EVENT_CONTACT_FROM_PHONE, NULL, "Contact from phone");
powerFSM.add_transition(&stateNB, &stateDARK, EVENT_PACKET_FOR_PHONE, NULL, "Packet for phone");
powerFSM.add_timed_transition(&stateON, &stateDARK, radioConfig.preferences.screen_on_secs * 1000, NULL, "Screen-on timeout");
powerFSM.add_timed_transition(&stateON, &stateDARK, getPref_screen_on_secs() * 1000, NULL, "Screen-on timeout");
powerFSM.add_timed_transition(&stateDARK, &stateNB, radioConfig.preferences.phone_timeout_secs * 1000, NULL, "Phone timeout");
powerFSM.add_timed_transition(&stateDARK, &stateNB, getPref_phone_timeout_secs() * 1000, NULL, "Phone timeout");
#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, radioConfig.preferences.min_wake_secs * 1000, NULL, "Min wake timeout");
powerFSM.add_timed_transition(&stateNB, &stateLS, getPref_min_wake_secs() * 1000, NULL, "Min wake timeout");
powerFSM.add_timed_transition(&stateDARK, &stateLS, radioConfig.preferences.wait_bluetooth_secs * 1000, NULL,
"Bluetooth timeout");
powerFSM.add_timed_transition(&stateDARK, &stateLS, getPref_wait_bluetooth_secs() * 1000, NULL, "Bluetooth timeout");
#endif
powerFSM.add_timed_transition(&stateLS, &stateSDS, radioConfig.preferences.mesh_sds_timeout_secs * 1000, NULL,
"mesh timeout");
auto meshSds = getPref_mesh_sds_timeout_secs();
if (meshSds != UINT32_MAX)
powerFSM.add_timed_transition(&stateLS, &stateSDS, meshSds * 1000, NULL, "mesh timeout");
// removing for now, because some users don't even have phones
// powerFSM.add_timed_transition(&stateLS, &stateSDS, radioConfig.preferences.phone_sds_timeout_sec * 1000, NULL, "phone
// powerFSM.add_timed_transition(&stateLS, &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

@@ -13,9 +13,11 @@
#define EVENT_BLUETOOTH_PAIR 7
#define EVENT_NODEDB_UPDATED 8 // NodeDB has a big enough change that we think you should turn on the screen
#define EVENT_CONTACT_FROM_PHONE 9 // the phone just talked to us over bluetooth
#define EVENT_LOW_BATTERY 10 // Battery is critically low, go to sleep
#define EVENT_LOW_BATTERY 10 // Battery is critically low, go to sleep
#define EVENT_SERIAL_CONNECTED 11
#define EVENT_SERIAL_DISCONNECTED 12
#define EVENT_POWER_CONNECTED 13
#define EVENT_POWER_DISCONNECTED 14
extern Fsm powerFSM;

View File

@@ -1,103 +1,94 @@
#pragma once
#include <Arduino.h>
#include "Status.h"
#include "configuration.h"
#include <Arduino.h>
namespace meshtastic {
namespace meshtastic
{
/// Describes the state of the GPS system.
class PowerStatus : public Status
/**
* A boolean where we have a third state of Unknown
*/
enum OptionalBool { OptFalse = 0, OptTrue = 1, OptUnknown = 2 };
/// Describes the state of the GPS system.
class PowerStatus : public Status
{
private:
CallbackObserver<PowerStatus, const PowerStatus *> statusObserver =
CallbackObserver<PowerStatus, const PowerStatus *>(this, &PowerStatus::updateStatus);
/// Whether we have a battery connected
OptionalBool hasBattery = OptUnknown;
/// Battery voltage in mV, valid if haveBattery is true
int batteryVoltageMv = 0;
/// Battery charge percentage, either read directly or estimated
int8_t batteryChargePercent = 0;
/// Whether USB is connected
OptionalBool hasUSB = OptUnknown;
/// Whether we are charging the battery
OptionalBool isCharging = OptUnknown;
public:
PowerStatus() { statusType = STATUS_TYPE_POWER; }
PowerStatus(OptionalBool hasBattery, OptionalBool hasUSB, OptionalBool isCharging, int batteryVoltageMv = -1,
int8_t batteryChargePercent = 0)
: Status()
{
this->hasBattery = hasBattery;
this->hasUSB = hasUSB;
this->isCharging = isCharging;
this->batteryVoltageMv = batteryVoltageMv;
this->batteryChargePercent = batteryChargePercent;
}
PowerStatus(const PowerStatus &);
PowerStatus &operator=(const PowerStatus &);
private:
CallbackObserver<PowerStatus, const PowerStatus *> statusObserver = CallbackObserver<PowerStatus, const PowerStatus *>(this, &PowerStatus::updateStatus);
void observe(Observable<const PowerStatus *> *source) { statusObserver.observe(source); }
/// Whether we have a battery connected
bool hasBattery;
/// Battery voltage in mV, valid if haveBattery is true
int batteryVoltageMv;
/// Battery charge percentage, either read directly or estimated
uint8_t batteryChargePercent;
/// Whether USB is connected
bool hasUSB;
/// Whether we are charging the battery
bool isCharging;
bool getHasBattery() const { return hasBattery == OptTrue; }
public:
bool getHasUSB() const { return hasUSB == OptTrue; }
PowerStatus() {
statusType = STATUS_TYPE_POWER;
}
PowerStatus( bool hasBattery, bool hasUSB, bool isCharging, int batteryVoltageMv, uint8_t batteryChargePercent ) : Status()
/// Can we even know if this board has USB power or not
bool knowsUSB() const { return hasUSB != OptUnknown; }
bool getIsCharging() const { return isCharging == OptTrue; }
int getBatteryVoltageMv() const { return batteryVoltageMv; }
/**
* Note: 0% battery means 'unknown/this board doesn't have a battery installed'
*/
uint8_t getBatteryChargePercent() const { return getHasBattery() ? batteryChargePercent : 0; }
bool matches(const PowerStatus *newStatus) const
{
return (newStatus->getHasBattery() != hasBattery || newStatus->getHasUSB() != hasUSB ||
newStatus->getBatteryVoltageMv() != batteryVoltageMv);
}
int updateStatus(const PowerStatus *newStatus)
{
// Only update the status if values have actually changed
bool isDirty;
{
this->hasBattery = hasBattery;
this->hasUSB = hasUSB;
this->isCharging = isCharging;
this->batteryVoltageMv = batteryVoltageMv;
this->batteryChargePercent = batteryChargePercent;
isDirty = matches(newStatus);
initialized = true;
hasBattery = newStatus->hasBattery;
batteryVoltageMv = newStatus->getBatteryVoltageMv();
batteryChargePercent = newStatus->getBatteryChargePercent();
hasUSB = newStatus->hasUSB;
isCharging = newStatus->isCharging;
}
PowerStatus(const PowerStatus &);
PowerStatus &operator=(const PowerStatus &);
void observe(Observable<const PowerStatus *> *source)
{
statusObserver.observe(source);
if (isDirty) {
DEBUG_MSG("Battery %dmV %d%%\n", batteryVoltageMv, batteryChargePercent);
onNewStatus.notifyObservers(this);
}
return 0;
}
};
bool getHasBattery() const
{
return hasBattery;
}
bool getHasUSB() const
{
return hasUSB;
}
bool getIsCharging() const
{
return isCharging;
}
int getBatteryVoltageMv() const
{
return batteryVoltageMv;
}
uint8_t getBatteryChargePercent() const
{
return batteryChargePercent;
}
bool matches(const PowerStatus *newStatus) const
{
return (
newStatus->getHasBattery() != hasBattery ||
newStatus->getHasUSB() != hasUSB ||
newStatus->getBatteryVoltageMv() != batteryVoltageMv
);
}
int updateStatus(const PowerStatus *newStatus) {
// Only update the status if values have actually changed
bool isDirty;
{
isDirty = matches(newStatus);
initialized = true;
hasBattery = newStatus->getHasBattery();
batteryVoltageMv = newStatus->getBatteryVoltageMv();
batteryChargePercent = newStatus->getBatteryChargePercent();
hasUSB = newStatus->getHasUSB();
isCharging = newStatus->getIsCharging();
}
if(isDirty) {
DEBUG_MSG("Battery %dmV %d%%\n", batteryVoltageMv, batteryChargePercent);
onNewStatus.notifyObservers(this);
}
return 0;
}
};
}
} // namespace meshtastic
extern meshtastic::PowerStatus *powerStatus;

11
src/SPILock.cpp Normal file
View File

@@ -0,0 +1,11 @@
#include "SPILock.h"
#include <Arduino.h>
#include <assert.h>
concurrency::Lock *spiLock;
void initSPI()
{
assert(!spiLock);
spiLock = new concurrency::Lock();
}

12
src/SPILock.h Normal file
View File

@@ -0,0 +1,12 @@
#pragma once
#include "../concurrency/LockGuard.h"
/**
* Used to provide mutual exclusion for access to the SPI bus. Usage:
* concurrency::LockGuard g(spiLock);
*/
extern concurrency::Lock *spiLock;
/** Setup SPI access and create the spiLock lock. */
void initSPI();

View File

@@ -40,6 +40,8 @@ void SerialConsole::onConnectionChanged(bool connected)
if (connected) { // To prevent user confusion, turn off bluetooth while using the serial port api
powerFSM.trigger(EVENT_SERIAL_CONNECTED);
} else {
// FIXME, we get no notice of serial going away, we should instead automatically generate this event if we haven't
// received a packet in a while
powerFSM.trigger(EVENT_SERIAL_DISCONNECTED);
}
}

View File

@@ -1,339 +0,0 @@
// This file is used to set TFT/eInk preferences for the TFT.cpp driver
#include <variant.h>
// USER DEFINED SETTINGS
// Set driver type, fonts to be loaded, pins used and SPI control method etc
//
// See the User_Setup_Select.h file if you wish to be able to define multiple
// setups and then easily select which setup file is used by the compiler.
//
// If this file is edited correctly then all the library example sketches should
// run without the need to make any more changes for a particular hardware setup!
// Note that some sketches are designed for a particular TFT pixel width/height
// ##################################################################################
//
// Section 1. Call up the right driver file and any options for it
//
// ##################################################################################
// Define STM32 to invoke optimised processor support (only for STM32)
//#define STM32
// Defining the STM32 board allows the library to optimise the performance
// for UNO compatible "MCUfriend" style shields
//#define NUCLEO_64_TFT
//#define NUCLEO_144_TFT
// STM32 8 bit parallel only:
// If STN32 Port A or B pins 0-7 are used for 8 bit parallel data bus bits 0-7
// then this will improve rendering performance by a factor of ~8x
//#define STM_PORTA_DATA_BUS
//#define STM_PORTA_DATA_BUS
// Tell the library to use 8 bit parallel mode (otherwise SPI is assumed)
//#define TFT_PARALLEL_8_BIT
// Display type - only define if RPi display
//#define RPI_DISPLAY_TYPE // 20MHz maximum SPI
// Only define one driver, the other ones must be commented out
//#define ILI9341_DRIVER
#define ST7735_DRIVER // Define additional parameters below for this display
//#define ILI9163_DRIVER // Define additional parameters below for this display
//#define S6D02A1_DRIVER
//#define RPI_ILI9486_DRIVER // 20MHz maximum SPI
//#define HX8357D_DRIVER
//#define ILI9481_DRIVER
//#define ILI9486_DRIVER
//#define ILI9488_DRIVER // WARNING: Do not connect ILI9488 display SDO to MISO if other devices share the SPI bus (TFT SDO does NOT tristate when CS is high)
//#define ST7789_DRIVER // Full configuration option, define additional parameters below for this display
//#define ST7789_2_DRIVER // Minimal configuration option, define additional parameters below for this display
//#define R61581_DRIVER
//#define RM68140_DRIVER
//#define ST7796_DRIVER
//#define SSD1963_480_DRIVER // Untested
//#define SSD1963_800_DRIVER // Untested
//#define SSD1963_800ALT_DRIVER // Untested
// Some displays support SPI reads via the MISO pin, other displays have a single
// bi-directional SDA pin and the library will try to read this via the MOSI line.
// To use the SDA line for reading data from the TFT uncomment the following line:
// #define TFT_SDA_READ // This option is for ESP32 ONLY, tested with ST7789 display only
// For ST7789 and ILI9341 ONLY, define the colour order IF the blue and red are swapped on your display
// Try ONE option at a time to find the correct colour order for your display
// #define TFT_RGB_ORDER TFT_RGB // Colour order Red-Green-Blue
// #define TFT_RGB_ORDER TFT_BGR // Colour order Blue-Green-Red
// For M5Stack ESP32 module with integrated ILI9341 display ONLY, remove // in line below
// #define M5STACK
// For ST7789, ST7735 and ILI9163 ONLY, define the pixel width and height in portrait orientation
#define TFT_WIDTH 80
// #define TFT_WIDTH 128
// #define TFT_WIDTH 240 // ST7789 240 x 240 and 240 x 320
#define TFT_HEIGHT 160
// #define TFT_HEIGHT 128
// #define TFT_HEIGHT 240 // ST7789 240 x 240
// #define TFT_HEIGHT 320 // ST7789 240 x 320
// For ST7735 ONLY, define the type of display, originally this was based on the
// colour of the tab on the screen protector film but this is not always true, so try
// out the different options below if the screen does not display graphics correctly,
// e.g. colours wrong, mirror images, or tray pixels at the edges.
// Comment out ALL BUT ONE of these options for a ST7735 display driver, save this
// this User_Setup file, then rebuild and upload the sketch to the board again:
// #define ST7735_INITB
#define ST7735_GREENTAB
// #define ST7735_GREENTAB2
// #define ST7735_GREENTAB3
// #define ST7735_GREENTAB128 // For 128 x 128 display
// #define ST7735_GREENTAB160x80 // For 160 x 80 display (BGR, inverted, 26 offset)
// #define ST7735_REDTAB
// #define ST7735_BLACKTAB
// #define ST7735_REDTAB160x80 // For 160 x 80 display with 24 pixel offset
// If colours are inverted (white shows as black) then uncomment one of the next
// 2 lines try both options, one of the options should correct the inversion.
// #define TFT_INVERSION_ON
// #define TFT_INVERSION_OFF
// ##################################################################################
//
// Section 2. Define the pins that are used to interface with the display here
//
// ##################################################################################
// If a backlight control signal is available then define the TFT_BL pin in Section 2
// below. The backlight will be turned ON when tft.begin() is called, but the library
// needs to know if the LEDs are ON with the pin HIGH or LOW. If the LEDs are to be
// driven with a PWM signal or turned OFF/ON then this must be handled by the user
// sketch. e.g. with digitalWrite(TFT_BL, LOW);
#define TFT_BL ST7735_BACKLIGHT_EN // LED back-light control pin
#define TFT_BACKLIGHT_ON HIGH // Level to turn ON back-light (HIGH or LOW)
// We must use hardware SPI, a minimum of 3 GPIO pins is needed.
// Typical setup for ESP8266 NodeMCU ESP-12 is :
//
// Display SDO/MISO to NodeMCU pin D6 (or leave disconnected if not reading TFT)
// Display LED to NodeMCU pin VIN (or 5V, see below)
// Display SCK to NodeMCU pin D5
// Display SDI/MOSI to NodeMCU pin D7
// Display DC (RS/AO)to NodeMCU pin D3
// Display RESET to NodeMCU pin D4 (or RST, see below)
// Display CS to NodeMCU pin D8 (or GND, see below)
// Display GND to NodeMCU pin GND (0V)
// Display VCC to NodeMCU 5V or 3.3V
//
// The TFT RESET pin can be connected to the NodeMCU RST pin or 3.3V to free up a control pin
//
// The DC (Data Command) pin may be labeled AO or RS (Register Select)
//
// With some displays such as the ILI9341 the TFT CS pin can be connected to GND if no more
// SPI devices (e.g. an SD Card) are connected, in this case comment out the #define TFT_CS
// line below so it is NOT defined. Other displays such at the ST7735 require the TFT CS pin
// to be toggled during setup, so in these cases the TFT_CS line must be defined and connected.
//
// The NodeMCU D0 pin can be used for RST
//
//
// Note: only some versions of the NodeMCU provide the USB 5V on the VIN pin
// If 5V is not available at a pin you can use 3.3V but backlight brightness
// will be lower.
// ###### EDIT THE PIN NUMBERS IN THE LINES FOLLOWING TO SUIT YOUR ESP8266 SETUP ######
// For NodeMCU - use pin numbers in the form PIN_Dx where Dx is the NodeMCU pin designation
#define TFT_CS ST7735_CS // Chip select control pin D8
#define TFT_DC ST7735_RS // Data Command control pin
#define TFT_RST ST7735_RESET // Reset pin (could connect to NodeMCU RST, see next line)
//#define TFT_RST -1 // Set TFT_RST to -1 if the display RESET is connected to NodeMCU RST or 3.3V
//#define TFT_BL PIN_D1 // LED back-light (only for ST7789 with backlight control pin)
//#define TOUCH_CS PIN_D2 // Chip select pin (T_CS) of touch screen
//#define TFT_WR PIN_D2 // Write strobe for modified Raspberry Pi TFT only
// ###### FOR ESP8266 OVERLAP MODE EDIT THE PIN NUMBERS IN THE FOLLOWING LINES ######
// Overlap mode shares the ESP8266 FLASH SPI bus with the TFT so has a performance impact
// but saves pins for other functions. It is best not to connect MISO as some displays
// do not tristate that line wjen chip select is high!
// On NodeMCU 1.0 SD0=MISO, SD1=MOSI, CLK=SCLK to connect to TFT in overlap mode
// On NodeMCU V3 S0 =MISO, S1 =MOSI, S2 =SCLK
// In ESP8266 overlap mode the following must be defined
//#define TFT_SPI_OVERLAP
// In ESP8266 overlap mode the TFT chip select MUST connect to pin D3
//#define TFT_CS PIN_D3
//#define TFT_DC PIN_D5 // Data Command control pin
//#define TFT_RST PIN_D4 // Reset pin (could connect to NodeMCU RST, see next line)
//#define TFT_RST -1 // Set TFT_RST to -1 if the display RESET is connected to NodeMCU RST or 3.3V
// ###### EDIT THE PIN NUMBERS IN THE LINES FOLLOWING TO SUIT YOUR ESP32 SETUP ######
// For ESP32 Dev board (only tested with ILI9341 display)
// The hardware SPI can be mapped to any pins
//#define TFT_MISO 19
//#define TFT_MOSI 23
//#define TFT_SCLK 18
//#define TFT_CS 15 // Chip select control pin
//#define TFT_DC 2 // Data Command control pin
//#define TFT_RST 4 // Reset pin (could connect to RST pin)
//#define TFT_RST -1 // Set TFT_RST to -1 if display RESET is connected to ESP32 board RST
//#define TOUCH_CS 21 // Chip select pin (T_CS) of touch screen
//#define TFT_WR 22 // Write strobe for modified Raspberry Pi TFT only
// For the M5Stack module use these #define lines
//#define TFT_MISO 19
//#define TFT_MOSI 23
//#define TFT_SCLK 18
//#define TFT_CS 14 // Chip select control pin
//#define TFT_DC 27 // Data Command control pin
//#define TFT_RST 33 // Reset pin (could connect to Arduino RESET pin)
//#define TFT_BL 32 // LED back-light (required for M5Stack)
// ###### EDIT THE PINs BELOW TO SUIT YOUR ESP32 PARALLEL TFT SETUP ######
// The library supports 8 bit parallel TFTs with the ESP32, the pin
// selection below is compatible with ESP32 boards in UNO format.
// Wemos D32 boards need to be modified, see diagram in Tools folder.
// Only ILI9481 and ILI9341 based displays have been tested!
// Parallel bus is only supported for the STM32 and ESP32
// Example below is for ESP32 Parallel interface with UNO displays
// Tell the library to use 8 bit parallel mode (otherwise SPI is assumed)
//#define TFT_PARALLEL_8_BIT
// The ESP32 and TFT the pins used for testing are:
//#define TFT_CS 33 // Chip select control pin (library pulls permanently low
//#define TFT_DC 15 // Data Command control pin - must use a pin in the range 0-31
//#define TFT_RST 32 // Reset pin, toggles on startup
//#define TFT_WR 4 // Write strobe control pin - must use a pin in the range 0-31
//#define TFT_RD 2 // Read strobe control pin
//#define TFT_D0 12 // Must use pins in the range 0-31 for the data bus
//#define TFT_D1 13 // so a single register write sets/clears all bits.
//#define TFT_D2 26 // Pins can be randomly assigned, this does not affect
//#define TFT_D3 25 // TFT screen update performance.
//#define TFT_D4 17
//#define TFT_D5 16
//#define TFT_D6 27
//#define TFT_D7 14
// ###### EDIT THE PINs BELOW TO SUIT YOUR STM32 SPI TFT SETUP ######
// The TFT can be connected to SPI port 1 or 2
//#define TFT_SPI_PORT 1 // SPI port 1 maximum clock rate is 55MHz
//#define TFT_MOSI PA7
//#define TFT_MISO PA6
//#define TFT_SCLK PA5
//#define TFT_SPI_PORT 2 // SPI port 2 maximum clock rate is 27MHz
//#define TFT_MOSI PB15
//#define TFT_MISO PB14
//#define TFT_SCLK PB13
// Can use Ardiuno pin references, arbitrary allocation, TFT_eSPI controls chip select
//#define TFT_CS D5 // Chip select control pin to TFT CS
//#define TFT_DC D6 // Data Command control pin to TFT DC (may be labelled RS = Register Select)
//#define TFT_RST D7 // Reset pin to TFT RST (or RESET)
// OR alternatively, we can use STM32 port reference names PXnn
//#define TFT_CS PE11 // Nucleo-F767ZI equivalent of D5
//#define TFT_DC PE9 // Nucleo-F767ZI equivalent of D6
//#define TFT_RST PF13 // Nucleo-F767ZI equivalent of D7
//#define TFT_RST -1 // Set TFT_RST to -1 if the display RESET is connected to processor reset
// Use an Arduino pin for initial testing as connecting to processor reset
// may not work (pulse too short at power up?)
// ##################################################################################
//
// Section 3. Define the fonts that are to be used here
//
// ##################################################################################
// Comment out the #defines below with // to stop that font being loaded
// The ESP8366 and ESP32 have plenty of memory so commenting out fonts is not
// normally necessary. If all fonts are loaded the extra FLASH space required is
// about 17Kbytes. To save FLASH space only enable the fonts you need!
//#define LOAD_GLCD // Font 1. Original Adafruit 8 pixel font needs ~1820 bytes in FLASH
//#define LOAD_FONT2 // Font 2. Small 16 pixel high font, needs ~3534 bytes in FLASH, 96 characters
//#define LOAD_FONT4 // Font 4. Medium 26 pixel high font, needs ~5848 bytes in FLASH, 96 characters
//#define LOAD_FONT6 // Font 6. Large 48 pixel font, needs ~2666 bytes in FLASH, only characters 1234567890:-.apm
//#define LOAD_FONT7 // Font 7. 7 segment 48 pixel font, needs ~2438 bytes in FLASH, only characters 1234567890:-.
//#define LOAD_FONT8 // Font 8. Large 75 pixel font needs ~3256 bytes in FLASH, only characters 1234567890:-.
//#define LOAD_FONT8N // Font 8. Alternative to Font 8 above, slightly narrower, so 3 digits fit a 160 pixel TFT
//#define LOAD_GFXFF // FreeFonts. Include access to the 48 Adafruit_GFX free fonts FF1 to FF48 and custom fonts
// Comment out the #define below to stop the SPIFFS filing system and smooth font code being loaded
// this will save ~20kbytes of FLASH
//#define SMOOTH_FONT
// ##################################################################################
//
// Section 4. Other options
//
// ##################################################################################
// Define the SPI clock frequency, this affects the graphics rendering speed. Too
// fast and the TFT driver will not keep up and display corruption appears.
// With an ILI9341 display 40MHz works OK, 80MHz sometimes fails
// With a ST7735 display more than 27MHz may not work (spurious pixels and lines)
// With an ILI9163 display 27 MHz works OK.
// #define SPI_FREQUENCY 1000000
// #define SPI_FREQUENCY 5000000
// #define SPI_FREQUENCY 10000000
// #define SPI_FREQUENCY 20000000
#define SPI_FREQUENCY 27000000
// #define SPI_FREQUENCY 40000000
// #define SPI_FREQUENCY 55000000 // STM32 SPI1 only (SPI2 maximum is 27MHz)
// #define SPI_FREQUENCY 80000000
// Optional reduced SPI frequency for reading TFT
#define SPI_READ_FREQUENCY 20000000
// The XPT2046 requires a lower SPI clock rate of 2.5MHz so we define that here:
#define SPI_TOUCH_FREQUENCY 2500000
// The ESP32 has 2 free SPI ports i.e. VSPI and HSPI, the VSPI is the default.
// If the VSPI port is in use and pins are not accessible (e.g. TTGO T-Beam)
// then uncomment the following line:
//#define USE_HSPI_PORT
// Comment out the following #define if "SPI Transactions" do not need to be
// supported. When commented out the code size will be smaller and sketches will
// run slightly faster, so leave it commented out unless you need it!
// Transaction support is needed to work with SD library but not needed with TFT_SdFat
// Transaction support is required if other SPI devices are connected.
// Transactions are automatically enabled by the library for an ESP32 (to use HAL mutex)
// so changing it here has no effect
// #define SUPPORT_TRANSACTIONS

View File

@@ -0,0 +1,44 @@
#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

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

View File

@@ -0,0 +1,47 @@
#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

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

View File

@@ -0,0 +1,40 @@
#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,5 +1,7 @@
#include "Thread.h"
#include "timing.h"
#include "FreeRtosThread.h"
#ifdef HAS_FREE_RTOS
#include <assert.h>
#ifdef ARDUINO_ARCH_ESP32
@@ -9,25 +11,20 @@
namespace concurrency
{
void Thread::start(const char *name, size_t stackSize, uint32_t priority)
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 Thread::callRun(void *_this)
{
((Thread *)_this)->doRun();
}
void Thread::serviceWatchdog()
void FreeRtosThread::serviceWatchdog()
{
#ifdef ARDUINO_ARCH_ESP32
esp_task_wdt_reset();
#endif
}
void Thread::startWatchdog()
void FreeRtosThread::startWatchdog()
{
#ifdef ARDUINO_ARCH_ESP32
auto r = esp_task_wdt_add(taskHandle);
@@ -35,7 +32,7 @@ void Thread::startWatchdog()
#endif
}
void Thread::stopWatchdog()
void FreeRtosThread::stopWatchdog()
{
#ifdef ARDUINO_ARCH_ESP32
auto r = esp_task_wdt_delete(taskHandle);
@@ -44,3 +41,5 @@ void Thread::stopWatchdog()
}
} // namespace concurrency
#endif

View File

@@ -0,0 +1,44 @@
#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

@@ -1,8 +1,10 @@
#include "Lock.h"
#include <cassert>
namespace concurrency {
namespace concurrency
{
#ifdef HAS_FREE_RTOS
Lock::Lock()
{
handle = xSemaphoreCreateBinary();
@@ -19,5 +21,12 @@ void Lock::unlock()
{
assert(xSemaphoreGive(handle));
}
#else
Lock::Lock() {}
void Lock::lock() {}
void Lock::unlock() {}
#endif
} // namespace concurrency

View File

@@ -2,7 +2,8 @@
#include "../freertosinc.h"
namespace concurrency {
namespace concurrency
{
/**
* @brief Simple wrapper around FreeRTOS API for implementing a mutex lock
@@ -26,8 +27,9 @@ class Lock
void unlock();
private:
#ifdef HAS_FREE_RTOS
SemaphoreHandle_t handle;
#endif
};
} // namespace concurrency

View File

@@ -1,47 +1,17 @@
#pragma once
#include "WorkerThread.h"
#include "FreeRtosNotifiedWorkerThread.h"
#include "PosixNotifiedWorkerThread.h"
namespace concurrency {
/**
* @brief A worker thread that waits on a freertos notification
*/
class NotifiedWorkerThread : public WorkerThread
namespace concurrency
{
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);
}
#ifdef HAS_FREE_RTOS
typedef FreeRtosNotifiedWorkerThread NotifiedWorkerThread;
#endif
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();
};
#ifdef __unix__
typedef PosixNotifiedWorkerThread NotifiedWorkerThread;
#endif
} // namespace concurrency

View File

@@ -1,7 +1,6 @@
#include "PeriodicScheduler.h"
#include "PeriodicTask.h"
#include "LockGuard.h"
#include "../timing.h"
namespace concurrency {
@@ -10,7 +9,7 @@ void PeriodicScheduler::loop()
{
LockGuard lg(&lock);
uint32_t now = timing::millis();
uint32_t now = millis();
for (auto t : tasks) {
if (t->period && (now - t->lastMsec) >= t->period) {

View File

@@ -1,7 +1,7 @@
#pragma once
#include <Arduino.h>
#include "PeriodicScheduler.h"
#include "timing.h"
namespace concurrency {
@@ -38,7 +38,7 @@ class PeriodicTask
*/
void setPeriod(uint32_t p)
{
lastMsec = timing::millis(); // reset starting from now
lastMsec = millis(); // reset starting from now
period = p;
}

View File

@@ -0,0 +1,19 @@
#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

@@ -0,0 +1,26 @@
#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

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

View File

@@ -1,5 +1,4 @@
#include "WorkerThread.h"
#include "timing.h"
namespace concurrency {
@@ -17,8 +16,8 @@ void WorkerThread::doRun()
#ifdef DEBUG_STACK
static uint32_t lastPrint = 0;
if (timing::millis() - lastPrint > 10 * 1000L) {
lastPrint = timing::millis();
if (millis() - lastPrint > 10 * 1000L) {
lastPrint = millis();
meshtastic::printThreadInfo("net");
}
#endif

View File

@@ -55,24 +55,21 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
/// Convert a preprocessor name into a quoted string and if that string is empty use "unset"
#define optstr(s) (xstr(s)[0] ? xstr(s) : "unset")
#ifdef NRF52_SERIES // All of the NRF52 targets are configured using variant.h, so this section shouldn't need to be
// board specific
#ifdef PORTDUINO
#define NO_ESP32 // Don't use ESP32 libs (mainly bluetooth)
#elif defined(NRF52_SERIES) // All of the NRF52 targets are configured using variant.h, so this section shouldn't need to be
// board specific
//
// Standard definitions for NRF52 targets
//
// Nop definition for these attributes - not used on NRF52
#define EXT_RAM_ATTR
#define IRAM_ATTR
#define NO_ESP32 // Don't use ESP32 libs (mainly bluetooth)
// We bind to the GPS using variant.h instead for this platform (Serial1)
// FIXME, not yet ready for NRF52
#define RTC_DATA_ATTR
#define LED_PIN PIN_LED1 // LED1 on nrf52840-DK
// If the variant filed defines as standard button
@@ -80,6 +77,10 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#define BUTTON_PIN PIN_BUTTON1
#endif
#ifdef PIN_BUTTON2
#define BUTTON_PIN_ALT PIN_BUTTON2
#endif
// FIXME, use variant.h defs for all of this!!! (even on the ESP32 targets)
#elif defined(CubeCell_BoardPlus)
@@ -89,9 +90,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#define NO_ESP32 // Don't use ESP32 libs (mainly bluetooth)
// FIXME, not yet ready for NRF52
#define RTC_DATA_ATTR
#define LED_PIN -1 // FIXME totally bogus
#define BUTTON_PIN -1
@@ -101,6 +99,8 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
// Standard definitions for ESP32 targets
//
#define HAS_WIFI
#define GPS_SERIAL_NUM 1
#define GPS_RX_PIN 34
#ifdef USE_JTAG
@@ -123,6 +123,17 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#endif
//
// Standard definitions for !ESP32 targets
//
#ifdef NO_ESP32
// Nop definition for these attributes - not used on NRF52
#define EXT_RAM_ATTR
#define IRAM_ATTR
#define RTC_DATA_ATTR
#endif
// -----------------------------------------------------------------------------
// OLED
// -----------------------------------------------------------------------------
@@ -137,9 +148,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
// devices. Comment this out to not rotate screen 180 degrees.
#define FLIP_SCREEN_VERTICALLY
// DEBUG LED
#define LED_INVERTED 0 // define as 1 if LED is active low (on)
// -----------------------------------------------------------------------------
// GPS
// -----------------------------------------------------------------------------
@@ -158,6 +166,9 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#define BUTTON_PIN 38 // The middle button GPIO on the T-Beam
#define BUTTON_PIN_ALT 13 // Alternate GPIO for an external button if needed
#define LED_INVERTED 1
#define LED_PIN 4 // Newer tbeams (1.1) have an extra led on GPIO4
// TTGO uses a common pinout for their SX1262 vs RF95 modules - both can be enabled and we will probe at runtime for RF95 and if
// not found then probe for SX1262
#define USE_RF95
@@ -179,8 +190,9 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
// code)
#endif
// Leave undefined to disable our PMU IRQ handler
#define PMU_IRQ 35
// Leave undefined to disable our PMU IRQ handler. DO NOT ENABLE THIS because the pmuirq can cause sperious interrupts
// and waking from light sleep
// #define PMU_IRQ 35
#define AXP192_SLAVE_ADDRESS 0x34
#elif defined(TBEAM_V07)
@@ -195,8 +207,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#define BUTTON_PIN 39
#define BATTERY_PIN 35 // A battery voltage measurement pin, voltage divider connected here to measure battery voltage
#define USE_RF95
#define USE_RF95
#define LORA_DIO0 26 // a No connect on the SX1262 module
#define LORA_RESET 23
@@ -255,6 +265,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
// #define VEXT_ENABLE 21 // active low, powers the oled display and the lora antenna boost
#define LED_PIN 2 // If defined we will blink this LED
#define BUTTON_PIN 0 // If defined, this will be used for user button presses
#define BUTTON_NEED_PULLUP
#define USE_RF95
#define LORA_DIO0 26 // a No connect on the SX1262 module
@@ -281,6 +292,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#define BUTTON_PIN \
0 // If defined, this will be used for user button presses, if your board doesn't have a physical switch, you can wire one
// between this pin and ground
#define BUTTON_NEED_PULLUP
#define USE_RF95
#define LORA_DIO0 26 // a No connect on the SX1262 module
@@ -309,6 +321,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#define BUTTON_PIN \
12 // If defined, this will be used for user button presses, if your board doesn't have a physical switch, you can wire one
// between this pin and ground
#define BUTTON_NEED_PULLUP
#define USE_RF95
#define LORA_DIO0 26 // a No connect on the SX1262 module
@@ -335,6 +348,29 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#define HW_VENDOR "nrf52unknown" // FIXME - unknown nrf52 board
#elif PORTDUINO
#define HW_VENDOR "portduino"
#define USE_SIM_RADIO
#define USE_RF95
#define LORA_DIO0 26 // a No connect on the SX1262 module
#define LORA_RESET 23
#define LORA_DIO1 33 // Not really used
#define LORA_DIO2 32 // Not really used
// Fake SPI device selections
#define RF95_SCK 5
#define RF95_MISO 19
#define RF95_MOSI 27
#define RF95_NSS 18
#endif
// DEBUG LED
#ifndef LED_INVERTED
#define LED_INVERTED 0 // define as 1 if LED is active low (on)
#endif
#ifdef USE_RF95

View File

@@ -1,7 +1,6 @@
#include <Arduino.h>
#include "../concurrency/LockGuard.h"
#include "../timing.h"
#include "BluetoothSoftwareUpdate.h"
#include "PowerFSM.h"
#include "RadioLibInterface.h"
@@ -31,6 +30,8 @@ int update_size_callback(uint16_t conn_handle, uint16_t attr_handle, struct ble_
if (ctxt->op == BLE_GATT_ACCESS_OP_WRITE_CHR && updateExpectedSize != 0) {
updateActualSize = 0;
crc.reset();
if (Update.isRunning())
Update.abort();
bool canBegin = Update.begin(updateExpectedSize);
DEBUG_MSG("Setting update size %u, result %d\n", updateExpectedSize, canBegin);
if (!canBegin) {
@@ -100,7 +101,7 @@ int update_crc32_callback(uint16_t conn_handle, uint16_t attr_handle, struct ble
} else {
if (Update.end()) {
DEBUG_MSG("OTA done, rebooting in 5 seconds!\n");
rebootAtMsec = timing::millis() + 5000;
rebootAtMsec = millis() + 5000;
} else {
DEBUG_MSG("Error Occurred. Error #: %d\n", Update.getError());
}
@@ -126,7 +127,7 @@ int update_result_callback(uint16_t conn_handle, uint16_t attr_handle, struct bl
void bluetoothRebootCheck()
{
if (rebootAtMsec && timing::millis() > rebootAtMsec) {
if (rebootAtMsec && millis() > rebootAtMsec) {
DEBUG_MSG("Rebooting for update\n");
ESP.restart();
}

View File

@@ -33,7 +33,7 @@ void WiFiServerAPI::loop()
if (client.connected()) {
StreamAPI::loop();
} else {
DEBUG_MSG("Client dropped connection, closing UDP server\n");
DEBUG_MSG("Client dropped connection, closing TCP server\n");
delete this;
}
}
@@ -44,7 +44,7 @@ WiFiServerPort::WiFiServerPort() : WiFiServer(MESHTASTIC_PORTNUM) {}
void WiFiServerPort::init()
{
DEBUG_MSG("Listening on TCP port %d\n", MESHTASTIC_PORTNUM);
DEBUG_MSG("API server sistening on TCP port %d\n", MESHTASTIC_PORTNUM);
begin();
}
@@ -52,6 +52,14 @@ void WiFiServerPort::loop()
{
auto client = available();
if (client) {
new WiFiServerAPI(client);
// Close any previous connection (see FIXME in header file)
if (openAPI)
delete openAPI;
openAPI = new WiFiServerAPI(client);
}
if (openAPI)
// Allow idle processing so the API can read from its incoming stream
openAPI->loop();
}

View File

@@ -29,6 +29,13 @@ class WiFiServerAPI : public StreamAPI
*/
class WiFiServerPort : public WiFiServer
{
/** The currently open port
*
* FIXME: We currently only allow one open TCP connection at a time, because we depend on the loop() call in this class to
* delegate to the worker. Once coroutines are implemented we can relax this restriction.
*/
WiFiServerAPI *openAPI = NULL;
public:
WiFiServerPort();

View File

@@ -3,22 +3,32 @@
// The FreeRTOS includes are in a different directory on ESP32 and I can't figure out how to make that work with platformio gcc
// options so this is my quick hack to make things work
#ifdef ARDUINO_ARCH_ESP32
#if defined(ARDUINO_ARCH_ESP32)
#define HAS_FREE_RTOS
#include <freertos/FreeRTOS.h>
#include <freertos/queue.h>
#include <freertos/semphr.h>
#include <freertos/task.h>
#else
// not yet supported on cubecell
#ifndef CubeCell_BoardPlus
#endif
#if defined(ARDUINO_NRF52_ADAFRUIT)
#define HAS_FREE_RTOS
#include <FreeRTOS.h>
#include <queue.h>
#include <semphr.h>
#include <task.h>
#endif
#ifdef HAS_FREE_RTOS
// Include real FreeRTOS defs above
#else
// Include placeholder fake FreeRTOS defs
#include <Arduino.h>
typedef uint32_t TickType_t;
@@ -26,5 +36,12 @@ typedef uint32_t BaseType_t;
#define portMAX_DELAY UINT32_MAX
#define tskIDLE_PRIORITY 0
#define configMAX_PRIORITIES 10 // Highest priority level
// Don't do anything on non free rtos platforms when done with the ISR
#define portYIELD_FROM_ISR(x)
enum eNotifyAction { eNoAction, eSetValueWithoutOverwrite, eSetValueWithOverwrite };
#endif
#endif

87
src/gps/Air530GPS.cpp Normal file
View File

@@ -0,0 +1,87 @@
#include "Air530GPS.h"
#include <assert.h>
/*
Helpful translations from the Air530 GPS datasheet
Sat acquision mode
捕获电流值@3.3v 42.6 mA
sat tracking mode
跟踪电流值@3.3v 36.7 mA
Low power mode
低功耗模式@3.3V 0.85 mA
(发送指令:$PGKC051,0)
Super low power mode
超低功耗模式@3.3V 31 uA
(发送指令:$PGKC105,4)
To exit sleep use WAKE pin
Commands to enter sleep
6、Command: 105
进入周期性低功耗模式
Arguments:
Arg1: “0”,正常运行模式 (normal mode)
“1”,周期超低功耗跟踪模式,需要拉高 WAKE 来唤醒 (periodic low power tracking mode - keeps sat positions, use wake to wake up)
“2”,周期低功耗模式 (periodic low power mode)
“4”,直接进入超低功耗跟踪模式,需要拉高 WAKE 来唤醒 (super low power consumption mode immediately, need WAKE to resume)
“8”,自动低功耗模式,可以通过串口唤醒 (automatic low power mode, wake by sending characters to serial port)
“9”, 自动超低功耗跟踪模式,需要拉高 WAKE 来唤醒 (automatic low power tracking when possible, need wake pin to resume)
(Arg 2 & 3 only valid if Arg1 is "1" or "2")
Arg2:运行时间(毫秒),在 Arg1 为 1、2 的周期模式下,此参数起作用
ON time in msecs
Arg3:睡眠时间(毫秒),在 Arg1 为 1、2 的周期模式下,此参数起作用
Sleep time in msecs
Example:
$PGKC105,8*3F<CR><LF>
This will set automatic low power mode with waking when we send chars to the serial port. Possibly do this as soon as we get a
new location. When we wake again in a minute we send a character to wake up.
*/
void Air530GPS::sendCommand(const char *cmd) {
uint8_t sum = 0;
// Skip the $
assert(cmd[0] == '$');
const char *p = cmd + 1;
while(*p)
sum ^= *p++;
assert(_serial_gps);
_serial_gps->write(cmd);
_serial_gps->printf("*%02x\r\n", sum);
// DEBUG_MSG("xsum %02x\n", sum);
}
void Air530GPS::sleep() {
#ifdef PIN_GPS_WAKE
digitalWrite(PIN_GPS_WAKE, 0);
pinMode(PIN_GPS_WAKE, OUTPUT);
sendCommand("$PGKC105,4");
#endif
}
/// wake the GPS into normal operation mode
void Air530GPS::wake()
{
#if 1
#ifdef PIN_GPS_WAKE
digitalWrite(PIN_GPS_WAKE, 1);
pinMode(PIN_GPS_WAKE, OUTPUT);
#endif
#else
// For power testing - keep GPS sleeping forever
sleep();
#endif
}

22
src/gps/Air530GPS.h Normal file
View File

@@ -0,0 +1,22 @@
#pragma once
#include "NMEAGPS.h"
/**
* A gps class thatreads from a NMEA GPS stream (and FIXME - eventually keeps the gps powered down except when reading)
*
* When new data is available it will notify observers.
*/
class Air530GPS : public NMEAGPS
{
protected:
/// If possible force the GPS into sleep/low power mode
virtual void sleep();
/// wake the GPS into normal operation mode
virtual void wake();
private:
/// Send a NMEA cmd with checksum
void sendCommand(const char *str);
};

View File

@@ -1,9 +1,10 @@
#include "GPS.h"
#include "NodeDB.h"
#include "RTC.h"
#include "configuration.h"
#include "timing.h"
#include "sleep.h"
#include <assert.h>
#include <time.h>
// If we have a serial GPS port it will not be null
#ifdef GPS_RX_PIN
@@ -22,67 +23,174 @@ uint8_t GPS::i2cAddress = GPS_I2C_ADDRESS;
uint8_t GPS::i2cAddress = 0;
#endif
bool timeSetFromGPS; // We try to set our time from GPS each time we wake from sleep
GPS *gps;
// stuff that really should be in in the instance instead...
static uint32_t
timeStartMsec; // Once we have a GPS lock, this is where we hold the initial msec clock that corresponds to that time
static uint64_t zeroOffsetSecs; // GPS based time in secs since 1970 - only updated once on initial lock
void readFromRTC()
bool GPS::setup()
{
struct timeval tv; /* btw settimeofday() is helpfull here too*/
setAwake(true); // Wake GPS power before doing any init
bool ok = setupGPS();
if (!gettimeofday(&tv, NULL)) {
uint32_t now = timing::millis();
if (ok)
notifySleepObserver.observe(&notifySleep);
DEBUG_MSG("Read RTC time as %ld (cur millis %u) valid=%d\n", tv.tv_sec, now, timeSetFromGPS);
timeStartMsec = now;
zeroOffsetSecs = tv.tv_sec;
return ok;
}
/**
* Switch the GPS into a mode where we are actively looking for a lock, or alternatively switch GPS into a low power mode
*
* calls sleep/wake
*/
void GPS::setAwake(bool on)
{
if (!wakeAllowed && on) {
DEBUG_MSG("Inhibiting because !wakeAllowed\n");
on = false;
}
if (isAwake != on) {
DEBUG_MSG("WANT GPS=%d\n", on);
if (on) {
lastWakeStartMsec = millis();
wake();
} else {
lastSleepStartMsec = millis();
sleep();
}
isAwake = on;
}
}
/// If we haven't yet set our RTC this boot, set it from a GPS derived time
void perhapsSetRTC(const struct timeval *tv)
GpsOperation GPS::getGpsOp() const
{
if (!timeSetFromGPS) {
timeSetFromGPS = true;
DEBUG_MSG("Setting RTC %ld secs\n", tv->tv_sec);
#ifndef NO_ESP32
settimeofday(tv, NULL);
#else
DEBUG_MSG("ERROR TIME SETTING NOT IMPLEMENTED!\n");
#endif
readFromRTC();
auto op = radioConfig.preferences.gps_operation;
if (op == GpsOperation_GpsOpUnset)
op = (radioConfig.preferences.location_share == LocationSharing_LocDisabled) ? GpsOperation_GpsOpTimeOnly
: GpsOperation_GpsOpMobile;
return op;
}
/** Get how long we should stay looking for each aquisition in msecs
*/
uint32_t GPS::getWakeTime() const
{
uint32_t t = radioConfig.preferences.gps_attempt_time;
if (t == UINT32_MAX)
return t; // already maxint
if (t == 0)
t = 15 * 60; // Allow up to 5 mins for each attempt (probably will be much less if we can find sats)
t *= 1000; // msecs
return t;
}
/** Get how long we should sleep between aqusition attempts in msecs
*/
uint32_t GPS::getSleepTime() const
{
uint32_t t = radioConfig.preferences.gps_update_interval;
auto op = getGpsOp();
bool gotTime = (getRTCQuality() >= RTCQualityGPS);
if ((gotTime && op == GpsOperation_GpsOpTimeOnly) || (op == GpsOperation_GpsOpDisabled))
t = UINT32_MAX; // Sleep forever now
if (t == UINT32_MAX)
return t; // already maxint
if (t == 0)
t = 2 * 60; // 2 mins
t *= 1000;
return t;
}
void GPS::publishUpdate()
{
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()
{
if (whileIdle()) {
// if we have received valid NMEA claim we are connected
isConnected = true;
}
// If we are overdue for an update, turn on the GPS and at least publish the current status
uint32_t now = millis();
auto sleepTime = getSleepTime();
if (!isAwake && sleepTime != UINT32_MAX && (now - lastSleepStartMsec) > sleepTime) {
// We now want to be awake - so wake up the GPS
setAwake(true);
}
// While we are awake
if (isAwake) {
// DEBUG_MSG("looking for location\n");
if ((now - lastWhileActiveMsec) > 5000) {
lastWhileActiveMsec = now;
whileActive();
}
// If we've already set time from the GPS, no need to ask the GPS
bool gotTime = (getRTCQuality() >= RTCQualityGPS) || lookForTime();
bool gotLoc = lookForLocation();
// We've been awake too long - force sleep
auto wakeTime = getWakeTime();
bool tooLong = wakeTime != UINT32_MAX && (now - lastWakeStartMsec) > wakeTime;
// Once we get a location we no longer desperately want an update
// 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
hasValidLocation = false;
}
setAwake(false);
publishUpdate(); // publish our update for this just finished acquisition window
}
}
}
void perhapsSetRTC(struct tm &t)
void GPS::forceWake(bool on)
{
/* 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).
*/
time_t res = mktime(&t);
struct timeval tv;
tv.tv_sec = res;
tv.tv_usec = 0; // time.centisecond() * (10 / 1000);
if (on) {
DEBUG_MSG("Allowing GPS lock\n");
// lastSleepStartMsec = 0; // Force an update ASAP
wakeAllowed = true;
} else {
wakeAllowed = false;
// DEBUG_MSG("Got time from GPS month=%d, year=%d, unixtime=%ld\n", t.tm_mon, t.tm_year, tv.tv_sec);
if (t.tm_year < 0 || t.tm_year >= 300)
DEBUG_MSG("Ignoring invalid GPS month=%d, year=%d, unixtime=%ld\n", t.tm_mon, t.tm_year, tv.tv_sec);
else
perhapsSetRTC(&tv);
// Note: if the gps was already awake, we DO NOT shut it down, because we want to allow it to complete its lock
// attempt even if we are in light sleep. Once the attempt succeeds (or times out) we'll then shut it down.
// setAwake(false);
}
}
uint32_t getTime()
/// 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)
{
return ((timing::millis() - timeStartMsec) / 1000) + zeroOffsetSecs;
}
forceWake(false);
uint32_t getValidTime()
{
return timeSetFromGPS ? getTime() : 0;
return 0;
}

View File

@@ -1,25 +1,11 @@
#pragma once
#include "../concurrency/PeriodicTask.h"
#include "GPSStatus.h"
#include "Observer.h"
#include "sys/time.h"
/// If we haven't yet set our RTC this boot, set it from a GPS derived time
void perhapsSetRTC(const struct timeval *tv);
void perhapsSetRTC(struct tm &t);
// Generate a string representation of DOP
const char *getDOPString(uint32_t dop);
/// Return time since 1970 in secs. Until we have a GPS lock we will be returning time based at zero
uint32_t getTime();
/// Return time since 1970 in secs. If we don't have a GPS lock return zero
uint32_t getValidTime();
void readFromRTC();
/**
* A gps class that only reads from the GPS periodically (and FIXME - eventually keeps the gps powered down except when reading)
*
@@ -27,9 +13,18 @@ void readFromRTC();
*/
class GPS
{
protected:
private:
uint32_t lastWakeStartMsec = 0, lastSleepStartMsec = 0, lastWhileActiveMsec = 0;
bool hasValidLocation = false; // default to false, until we complete our first read
bool isAwake = false; // true if we want a location right now
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);
protected:
public:
/** If !NULL we will use this serial port to construct our GPS */
static HardwareSerial *_serial_gps;
@@ -46,7 +41,7 @@ class GPS
bool isConnected = false; // Do we have a GPS we are talking to
virtual ~GPS() {}
virtual ~GPS() {} // FIXME, we really should unregister our sleep observer
/** We will notify this observable anytime GPS state has changed meaningfully */
Observable<const meshtastic::GPSStatus *> newStatus;
@@ -54,18 +49,82 @@ class GPS
/**
* Returns true if we succeeded
*/
virtual bool setup() { return true; }
virtual bool setup();
/// A loop callback for subclasses that need it. FIXME, instead just block on serial reads
virtual void loop() {}
virtual void loop();
/// Returns ture if we have acquired GPS lock.
bool hasLock() const { return hasValidLocation; }
/**
* Restart our lock attempt - try to get and broadcast a GPS reading ASAP
* called after the CPU wakes from light-sleep state */
virtual void startLock() {}
* called after the CPU wakes from light-sleep state
*
* Or set to false, to disallow any sort of waking
* */
void forceWake(bool on);
protected:
/// Do gps chipset specific init, return true for success
virtual bool setupGPS() = 0;
/// If possible force the GPS into sleep/low power mode
virtual void sleep() {}
/// wake the GPS into normal operation mode
virtual void wake() {}
/** Subclasses should look for serial rx characters here and feed it to their GPS parser
*
* Return true if we received a valid message from the GPS
*/
virtual bool whileIdle() = 0;
/** Idle processing while GPS is looking for lock, called once per secondish */
virtual void whileActive() {}
/**
* Perform any processing that should be done only while the GPS is awake and looking for a fix.
* Override this method to check for new locations
*
* @return true if we've acquired a time
*/
virtual bool lookForTime() = 0;
/**
* Perform any processing that should be done only while the GPS is awake and looking for a fix.
* Override this method to check for new locations
*
* @return true if we've acquired a new location
*/
virtual bool lookForLocation() = 0;
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);
/**
* Switch the GPS into a mode where we are actively looking for a lock, or alternatively switch GPS into a low power mode
*
* calls sleep/wake
*/
void setAwake(bool on);
/** Get how long we should stay looking for each aquisition
*/
uint32_t getWakeTime() const;
/** Get how long we should sleep between aqusition attempts
*/
uint32_t getSleepTime() const;
GpsOperation getGpsOp() const;
/**
* Tell users we have new GPS readings
*/
void publishUpdate();
};
extern GPS *gps;

View File

@@ -1,75 +0,0 @@
#include "NEMAGPS.h"
#include "configuration.h"
#include "timing.h"
static int32_t toDegInt(RawDegrees d)
{
int32_t degMult = 10000000; // 1e7
int32_t r = d.deg * degMult + d.billionths / 100;
if (d.negative)
r *= -1;
return r;
}
void NEMAGPS::loop()
{
while (_serial_gps->available() > 0) {
int c = _serial_gps->read();
// Serial.write(c);
reader.encode(c);
}
uint32_t now = timing::millis();
if ((now - lastUpdateMsec) > 20 * 1000) { // Ugly hack for now - limit update checks to once every 20 secs (but still consume
// serial chars at whatever rate)
lastUpdateMsec = now;
auto ti = reader.time;
auto d = reader.date;
if (ti.isUpdated() && ti.isValid() && d.isValid()) {
/* 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).
*/
struct tm t;
t.tm_sec = ti.second();
t.tm_min = ti.minute();
t.tm_hour = ti.hour();
t.tm_mday = d.day();
t.tm_mon = d.month() - 1;
t.tm_year = d.year() - 1900;
t.tm_isdst = false;
perhapsSetRTC(t);
isConnected = true; // we seem to have a real GPS (but not necessarily a lock)
}
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);
}
// 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();
}
// expect gps pos lat=37.520825, lon=-122.309162, alt=158
DEBUG_MSG("new NEMA GPS pos lat=%f, lon=%f, alt=%d, hdop=%f, heading=%f\n", latitude * 1e-7, longitude * 1e-7, altitude, dop * 1e-2, heading * 1e-5);
}
// Notify any status instances that are observing us
const meshtastic::GPSStatus status = meshtastic::GPSStatus(hasLock(), isConnected, latitude, longitude, altitude, dop, heading, numSatellites);
newStatus.notifyObservers(&status);
}
}

View File

@@ -1,21 +0,0 @@
#pragma once
#include "GPS.h"
#include "Observer.h"
#include "../concurrency/PeriodicTask.h"
#include "TinyGPS++.h"
/**
* A gps class thatreads from a NEMA GPS stream (and FIXME - eventually keeps the gps powered down except when reading)
*
* When new data is available it will notify observers.
*/
class NEMAGPS : public GPS
{
TinyGPSPlus reader;
uint32_t lastUpdateMsec = 0;
public:
virtual void loop();
};

110
src/gps/NMEAGPS.cpp Normal file
View File

@@ -0,0 +1,110 @@
#include "NMEAGPS.h"
#include "configuration.h"
#include "RTC.h"
static int32_t toDegInt(RawDegrees d)
{
int32_t degMult = 10000000; // 1e7
int32_t r = d.deg * degMult + d.billionths / 100;
if (d.negative)
r *= -1;
return r;
}
bool NMEAGPS::setupGPS()
{
#ifdef PIN_GPS_PPS
// pulse per second
// FIXME - move into shared GPS code
pinMode(PIN_GPS_PPS, INPUT);
#endif
return true;
}
/**
* Perform any processing that should be done only while the GPS is awake and looking for a fix.
* Override this method to check for new locations
*
* @return true if we've acquired a new location
*/
bool NMEAGPS::lookForTime()
{
auto ti = reader.time;
auto d = reader.date;
if (ti.isUpdated() && ti.isValid() && d.isValid()) {
/* 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).
*/
struct tm t;
t.tm_sec = ti.second();
t.tm_min = ti.minute();
t.tm_hour = ti.hour();
t.tm_mday = d.day();
t.tm_mon = d.month() - 1;
t.tm_year = d.year() - 1900;
t.tm_isdst = false;
perhapsSetRTC(RTCQualityGPS, t);
return true;
} else
return false;
}
/**
* Perform any processing that should be done only while the GPS is awake and looking for a fix.
* Override this method to check for new locations
*
* @return true if we've acquired a new location
*/
bool NMEAGPS::lookForLocation()
{
bool foundLocation = false;
// uint8_t fixtype = reader.fixQuality();
// hasValidLocation = ((fixtype >= 1) && (fixtype <= 5));
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();
}
// expect gps pos lat=37.520825, lon=-122.309162, alt=158
DEBUG_MSG("new NMEA GPS pos lat=%f, lon=%f, alt=%d, hdop=%g, heading=%f\n", latitude * 1e-7, longitude * 1e-7, altitude,
dop * 1e-2, heading * 1e-5);
}
return foundLocation;
}
bool NMEAGPS::whileIdle()
{
bool isValid = false;
// First consume any chars that have piled up at the receiver
while (_serial_gps->available() > 0) {
int c = _serial_gps->read();
// DEBUG_MSG("%c", c);
isValid |= reader.encode(c);
}
return isValid;
}

42
src/gps/NMEAGPS.h Normal file
View File

@@ -0,0 +1,42 @@
#pragma once
#include "../concurrency/PeriodicTask.h"
#include "GPS.h"
#include "Observer.h"
#include "TinyGPS++.h"
/**
* A gps class thatreads from a NMEA GPS stream (and FIXME - eventually keeps the gps powered down except when reading)
*
* When new data is available it will notify observers.
*/
class NMEAGPS : public GPS
{
TinyGPSPlus reader;
public:
virtual bool setupGPS();
protected:
/** Subclasses should look for serial rx characters here and feed it to their GPS parser
*
* Return true if we received a valid message from the GPS
*/
virtual bool whileIdle();
/**
* Perform any processing that should be done only while the GPS is awake and looking for a fix.
* Override this method to check for new locations
*
* @return true if we've acquired a time
*/
virtual bool lookForTime();
/**
* Perform any processing that should be done only while the GPS is awake and looking for a fix.
* Override this method to check for new locations
*
* @return true if we've acquired a new location
*/
virtual bool lookForLocation();
};

77
src/gps/RTC.cpp Normal file
View File

@@ -0,0 +1,77 @@
#include "RTC.h"
#include "configuration.h"
#include <sys/time.h>
#include <time.h>
static RTCQuality currentQuality = RTCQualityNone;
RTCQuality getRTCQuality()
{
return currentQuality;
}
// stuff that really should be in in the instance instead...
static uint32_t
timeStartMsec; // Once we have a GPS lock, this is where we hold the initial msec clock that corresponds to that time
static uint64_t zeroOffsetSecs; // GPS based time in secs since 1970 - only updated once on initial lock
void readFromRTC()
{
struct timeval tv; /* btw settimeofday() is helpfull here too*/
if (!gettimeofday(&tv, NULL)) {
uint32_t now = millis();
DEBUG_MSG("Read RTC time as %ld (cur millis %u) quality=%d\n", tv.tv_sec, now, currentQuality);
timeStartMsec = now;
zeroOffsetSecs = tv.tv_sec;
}
}
/// If we haven't yet set our RTC this boot, set it from a GPS derived time
bool perhapsSetRTC(RTCQuality q, const struct timeval *tv)
{
if (q > currentQuality) {
currentQuality = q;
DEBUG_MSG("Setting RTC %ld secs\n", tv->tv_sec);
#ifndef NO_ESP32
settimeofday(tv, NULL);
#else
DEBUG_MSG("ERROR TIME SETTING NOT IMPLEMENTED!\n");
#endif
readFromRTC();
return true;
} else {
return false;
}
}
bool perhapsSetRTC(RTCQuality q, struct tm &t)
{
/* 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).
*/
time_t res = mktime(&t);
struct timeval tv;
tv.tv_sec = res;
tv.tv_usec = 0; // time.centisecond() * (10 / 1000);
// DEBUG_MSG("Got time from GPS month=%d, year=%d, unixtime=%ld\n", t.tm_mon, t.tm_year, tv.tv_sec);
if (t.tm_year < 0 || t.tm_year >= 300) {
// DEBUG_MSG("Ignoring invalid GPS month=%d, year=%d, unixtime=%ld\n", t.tm_mon, t.tm_year, tv.tv_sec);
return false;
} else {
return perhapsSetRTC(q, &tv);
}
}
uint32_t getTime()
{
return ((millis() - timeStartMsec) / 1000) + zeroOffsetSecs;
}
uint32_t getValidTime()
{
return (currentQuality >= RTCQualityFromNet) ? getTime() : 0;
}

30
src/gps/RTC.h Normal file
View File

@@ -0,0 +1,30 @@
#pragma once
#include "configuration.h"
#include "sys/time.h"
#include <Arduino.h>
enum RTCQuality {
/// We haven't had our RTC set yet
RTCQualityNone = 0,
/// Some other node gave us a time we can use
RTCQualityFromNet = 1,
/// Our time is based on our own GPS
RTCQualityGPS = 2
};
RTCQuality getRTCQuality();
/// If we haven't yet set our RTC this boot, set it from a GPS derived time
bool perhapsSetRTC(RTCQuality q, const struct timeval *tv);
bool perhapsSetRTC(RTCQuality q, struct tm &t);
/// Return time since 1970 in secs. While quality is RTCQualityNone we will be returning time based at zero
uint32_t getTime();
/// Return time since 1970 in secs. If quality is RTCQualityNone return zero
uint32_t getValidTime();
void readFromRTC();

View File

@@ -1,12 +1,10 @@
#include "UBloxGPS.h"
#include "RTC.h"
#include "error.h"
#include "sleep.h"
#include <assert.h>
UBloxGPS::UBloxGPS() : concurrency::PeriodicTask()
{
notifySleepObserver.observe(&notifySleep);
}
UBloxGPS::UBloxGPS() {}
bool UBloxGPS::tryConnect()
{
@@ -26,7 +24,7 @@ bool UBloxGPS::tryConnect()
return isConnected;
}
bool UBloxGPS::setup()
bool UBloxGPS::setupGPS()
{
if (_serial_gps) {
#ifdef GPS_RX_PIN
@@ -34,20 +32,18 @@ bool UBloxGPS::setup()
#else
_serial_gps->begin(GPS_BAUDRATE);
#endif
// _serial_gps.setRxBufferSize(1024); // the default is 256
#ifndef NO_ESP32
_serial_gps->setRxBufferSize(2048); // the default is 256
#endif
}
#ifdef GPS_POWER_EN
pinMode(GPS_POWER_EN, OUTPUT);
digitalWrite(GPS_POWER_EN, 1);
delay(200); // Give time for the GPS to startup after we gave power
#endif
// uncomment to see debug info
// ublox.enableDebugging(Serial);
// try a second time, the ublox lib serial parsing is buggy?
if (!tryConnect())
tryConnect();
// see https://github.com/meshtastic/Meshtastic-device/issues/376
for (int i = 0; (i < 3) && !tryConnect(); i++)
delay(500);
if (isConnected) {
DEBUG_MSG("Connected to UBLOX GPS successfully\n");
@@ -55,8 +51,6 @@ bool UBloxGPS::setup()
if (!setUBXMode())
recordCriticalError(UBloxInitFailed); // Don't halt the boot if saving the config fails, but do report the bug
concurrency::PeriodicTask::setup(); // We don't start our periodic task unless we actually found the device
return true;
} else {
return false;
@@ -81,8 +75,11 @@ bool UBloxGPS::setUBXMode()
// assert(ok);
// ok = ublox.setDynamicModel(DYN_MODEL_BIKE); // probably PEDESTRIAN but just in case assume bike speeds
// assert(ok);
if (!ublox.powerSaveMode(true, 2000)) // use power save mode, the default timeout (1100ms seems a bit too tight)
return false;
// per https://github.com/meshtastic/Meshtastic-device/issues/376 powerSaveMode might not work with the marginal
// TTGO antennas
// if (!ublox.powerSaveMode(true, 2000)) // use power save mode, the default timeout (1100ms seems a bit too tight)
// return false;
if (!ublox.saveConfiguration(3000))
return false;
@@ -99,15 +96,15 @@ bool UBloxGPS::factoryReset()
{
bool ok = false;
// It is useful to force back into factory defaults (9600baud, NEMA to test the behavior of boards that don't have
// It is useful to force back into factory defaults (9600baud, NMEA to test the behavior of boards that don't have
// GPS_TX connected)
ublox.factoryReset();
delay(5000);
tryConnect(); // sets isConnected
// try a second time, the ublox lib serial parsing is buggy?
if (!tryConnect())
tryConnect();
for (int i = 0; (i < 3) && !tryConnect(); i++)
delay(500);
DEBUG_MSG("GPS Factory reset success=%d\n", isConnected);
if (isConnected)
@@ -116,89 +113,112 @@ bool UBloxGPS::factoryReset()
return ok;
}
/// Prepare the GPS for the cpu entering deep or light sleep, expect to be gone for at least 100s of msecs
int UBloxGPS::prepareSleep(void *unused)
/** Idle processing while GPS is looking for lock */
void UBloxGPS::whileActive()
{
if (isConnected)
ublox.powerOff();
ublox.getT(maxWait()); // ask for new time data - hopefully ready when we come back
return 0;
// Ask for a new position fix - hopefully it will have results ready by next time
// the order here is important, because we only check for has latitude when reading
ublox.getSIV(maxWait());
ublox.getPDOP(maxWait());
ublox.getP(maxWait());
// Update fixtype
if (ublox.moduleQueried.fixType) {
fixType = ublox.getFixType(0);
DEBUG_MSG("GPS fix type %d, numSats %d\n", fixType, numSatellites);
}
}
void UBloxGPS::doTask()
/**
* Perform any processing that should be done only while the GPS is awake and looking for a fix.
* Override this method to check for new locations
*
* @return true if we've acquired a new location
*/
bool UBloxGPS::lookForTime()
{
if (isConnected) {
// Consume all characters that have arrived
uint8_t fixtype = 3; // If we are only using the RX pin, assume we have a 3d fix
// if using i2c or serial look too see if any chars are ready
ublox.checkUblox(); // See if new data is available. Process bytes as they come in.
// If we don't have a fix (a quick check), don't try waiting for a solution)
// Hmmm my fix type reading returns zeros for fix, which doesn't seem correct, because it is still sptting out positions
// turn off for now
uint16_t maxWait = i2cAddress ? 300 : 0; // If using i2c we must poll with wait
fixtype = ublox.getFixType(maxWait);
DEBUG_MSG("GPS fix type %d\n", fixtype);
// DEBUG_MSG("sec %d\n", ublox.getSecond());
// DEBUG_MSG("lat %d\n", ublox.getLatitude());
// any fix that has time
if (ublox.getT(maxWait)) {
/* Convert to unix time
The Unix epoch (or Unix time or POSIX time or Unix timestamp) is the number of seconds that have elapsed since January
1, 1970 (midnight UTC/GMT), not counting leap seconds (in ISO 8601: 1970-01-01T00:00:00Z).
*/
struct tm t;
t.tm_sec = ublox.getSecond(0);
t.tm_min = ublox.getMinute(0);
t.tm_hour = ublox.getHour(0);
t.tm_mday = ublox.getDay(0);
t.tm_mon = ublox.getMonth(0) - 1;
t.tm_year = ublox.getYear(0) - 1900;
t.tm_isdst = false;
perhapsSetRTC(t);
}
latitude = ublox.getLatitude(0);
longitude = ublox.getLongitude(0);
altitude = ublox.getAltitude(0) / 1000; // in mm convert to meters
dop = ublox.getPDOP(0); // PDOP (an accuracy metric) is reported in 10^2 units so we have to scale down when we use it
heading = ublox.getHeading(0);
numSatellites = ublox.getSIV(0);
// bogus lat lon is reported as 0 or 0 (can be bogus just for one)
// Also: apparently when the GPS is initially reporting lock it can output a bogus latitude > 90 deg!
hasValidLocation =
(latitude != 0) && (longitude != 0) && (latitude <= 900000000 && latitude >= -900000000) && (numSatellites > 0);
// we only notify if position has changed due to a new fix
if ((fixtype >= 3 && fixtype <= 4) && ublox.getP(maxWait)) // rd fixes only
{
if (hasValidLocation) {
wantNewLocation = false;
// ublox.powerOff();
}
} else // we didn't get a location update, go back to sleep and hope the characters show up
wantNewLocation = true;
// Notify any status instances that are observing us
const meshtastic::GPSStatus status =
meshtastic::GPSStatus(hasLock(), isConnected, latitude, longitude, altitude, dop, heading, numSatellites);
newStatus.notifyObservers(&status);
if (ublox.moduleQueried.gpsSecond) {
/* 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).
*/
struct tm t;
t.tm_sec = ublox.getSecond(0);
t.tm_min = ublox.getMinute(0);
t.tm_hour = ublox.getHour(0);
t.tm_mday = ublox.getDay(0);
t.tm_mon = ublox.getMonth(0) - 1;
t.tm_year = ublox.getYear(0) - 1900;
t.tm_isdst = false;
perhapsSetRTC(RTCQualityGPS, t);
return true;
}
// Once we have sent a location once we only poll the GPS rarely, otherwise check back every 10s until we have something
// over the serial
setPeriod(hasValidLocation && !wantNewLocation ? 30 * 1000 : 10 * 1000);
return false;
}
void UBloxGPS::startLock()
/**
* Perform any processing that should be done only while the GPS is awake and looking for a fix.
* Override this method to check for new locations
*
* @return true if we've acquired a new location
*/
bool UBloxGPS::lookForLocation()
{
DEBUG_MSG("Looking for GPS lock\n");
wantNewLocation = true;
setPeriod(1);
bool foundLocation = false;
if (ublox.moduleQueried.SIV)
numSatellites = 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
// we only notify if position has changed due to a new fix
if ((fixType >= 3 && fixType <= 4)) {
if (ublox.moduleQueried.latitude) // rd fixes only
{
latitude = ublox.getLatitude(0);
longitude = ublox.getLongitude(0);
altitude = ublox.getAltitudeMSL(0) / 1000; // in mm convert to meters
// Note: heading is only currently implmented in the ublox for the 8m chipset - therefore
// don't read it here - it will generate an ignored getPVT command on the 6ms
// heading = ublox.getHeading(0);
// bogus lat lon is reported as 0 or 0 (can be bogus just for one)
// Also: apparently when the GPS is initially reporting lock it can output a bogus latitude > 90 deg!
foundLocation =
(latitude != 0) && (longitude != 0) && (latitude <= 900000000 && latitude >= -900000000) && (numSatellites > 0);
}
}
return foundLocation;
}
bool UBloxGPS::whileIdle()
{
// if using i2c or serial look too see if any chars are ready
return ublox.checkUblox(); // See if new data is available. Process bytes as they come in.
}
/// If possible force the GPS into sleep/low power mode
/// Note: ublox doesn't need a wake method, because as soon as we send chars to the GPS it will wake up
void UBloxGPS::sleep()
{
// Tell GPS to power down until we send it characters on serial port (we leave vcc connected)
ublox.powerOff();
// setGPSPower(false);
}
void UBloxGPS::wake()
{
fixType = 0; // assume we hace no fix yet
setGPSPower(true);
// Note: no delay needed because now we leave gps power on always and instead use ublox.powerOff()
// Give time for the GPS to boot
// delay(200);
}

View File

@@ -1,6 +1,5 @@
#pragma once
#include "../concurrency/PeriodicTask.h"
#include "GPS.h"
#include "Observer.h"
#include "SparkFun_Ublox_Arduino_Library.h"
@@ -10,29 +9,14 @@
*
* When new data is available it will notify observers.
*/
class UBloxGPS : public GPS, public concurrency::PeriodicTask
class UBloxGPS : public GPS
{
SFE_UBLOX_GPS ublox;
bool wantNewLocation = true;
CallbackObserver<UBloxGPS, void *> notifySleepObserver = CallbackObserver<UBloxGPS, void *>(this, &UBloxGPS::prepareSleep);
uint8_t fixType = 0;
public:
UBloxGPS();
/**
* Returns true if we succeeded
*/
virtual bool setup();
virtual void doTask();
/**
* Restart our lock attempt - try to get and broadcast a GPS reading ASAP
* called after the CPU wakes from light-sleep state */
virtual void startLock();
/**
* Reset our GPS back to factory settings
*
@@ -40,15 +24,48 @@ class UBloxGPS : public GPS, public concurrency::PeriodicTask
*/
bool factoryReset();
private:
/// Prepare the GPS for the cpu entering deep or light sleep, expect to be gone for at least 100s of msecs
/// always returns 0 to indicate okay to sleep
int prepareSleep(void *unused);
protected:
/**
* Returns true if we succeeded
*/
virtual bool setupGPS();
/** Subclasses should look for serial rx characters here and feed it to their GPS parser
*
* Return true if we received a valid message from the GPS
*/
virtual bool whileIdle();
/** Idle processing while GPS is looking for lock */
virtual void whileActive();
/**
* Perform any processing that should be done only while the GPS is awake and looking for a fix.
* Override this method to check for new locations
*
* @return true if we've acquired a time
*/
virtual bool lookForTime();
/**
* Perform any processing that should be done only while the GPS is awake and looking for a fix.
* Override this method to check for new locations
*
* @return true if we've acquired a new location
*/
virtual bool lookForLocation();
/// If possible force the GPS into sleep/low power mode
virtual void sleep();
virtual void wake();
private:
/// Attempt to connect to our GPS, returns false if no gps is present
bool tryConnect();
/// Switch to our desired operating mode and save the settings to flash
/// returns true for success
bool setUBXMode();
uint16_t maxWait() const { return i2cAddress ? 300 : 0; /*If using i2c we must poll with wait */ }
};

View File

@@ -0,0 +1,135 @@
#include "configuration.h"
#ifdef HAS_EINK
#include "EInkDisplay.h"
#include "SPILock.h"
#include "epd1in54.h" // Screen specific library
#include "graphics/configs.h"
#include <SPI.h>
#include <TFT_eSPI.h> // Graphics library and Sprite class
Epd ePaper; // Create an instance ePaper
TFT_eSPI glc = TFT_eSPI(); // Invoke the graphics library class
TFT_eSprite frame = TFT_eSprite(&glc); // Invoke the Sprite class for the image frame buffer
uint8_t *framePtr; // Pointer for the black frame buffer
#define COLORED 0
#define UNCOLORED 1
#define INK COLORED // Black ink
#define PAPER UNCOLORED // 'paper' background colour
//------------------------------------------------------------------------------------
// Update display - different displays have different function names in the default
// Waveshare libraries :-(
//------------------------------------------------------------------------------------
#if defined(EPD1IN54B_H) || defined(EPD1IN54C_H) || defined(EPD2IN13B_H) || defined(EPD2IN7B_H) || defined(EPD2IN9B_H) || \
defined(EPD4IN2_H)
void updateDisplay(uint8_t *blackFrame = blackFramePtr, uint8_t *redFrame = redFramePtr)
{
ePaper.DisplayFrame(blackFrame, redFrame); // Update 3 colour display
#else
void updateDisplay(uint8_t *blackFrame = framePtr)
{
#if defined(EPD2IN7_H) || defined(EPD4IN2_H)
ePaper.DisplayFrame(blackFrame); // Update 2 color display
#elif defined(EPD1IN54_H) || defined(EPD2IN13_H) || defined(EPD2IN9_H)
ePaper.SetFrameMemory(blackFrame); // Update 2 colour display
ePaper.DisplayFrame();
#else
#error "Selected ePaper library is not supported"
#endif
#endif
}
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
}
// 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)
{
// No need to grab this lock because we are on our own SPI bus
// concurrency::LockGuard g(spiLock);
uint32_t now = millis();
uint32_t sinceLast = now - lastDrawMsec;
if (framePtr && (sinceLast > 60 * 1000 || 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++) {
// get src pixel in the page based ordering the OLED lib uses FIXME, super inefficent
auto b = buffer[x + (y / 8) * SCREEN_WIDTH];
auto isset = b & (1 << (y & 7));
frame.drawPixel(x, y, isset ? INK : PAPER);
}
}
ePaper.Reset(); // wake the screen from sleep
DEBUG_MSG("Updating eink... ");
updateDisplay(); // Send image to display and refresh
DEBUG_MSG("done\n");
// Put screen to sleep to save power
ePaper.Sleep();
}
}
// Send a command to the display (low level function)
void EInkDisplay::sendCommand(uint8_t com)
{
(void)com;
// Drop all commands to device (we just update the buffer)
}
// Connect to the display
bool EInkDisplay::connect()
{
DEBUG_MSG("Doing EInk init\n");
#ifdef PIN_EINK_PWR_ON
digitalWrite(PIN_EINK_PWR_ON, HIGH); // If we need to assert a pin to power external peripherals
pinMode(PIN_EINK_PWR_ON, OUTPUT);
#endif
#ifdef PIN_EINK_EN
digitalWrite(PIN_EINK_EN, HIGH);
pinMode(PIN_EINK_EN, OUTPUT);
#endif
// Initialise the ePaper library
// FIXME - figure out how to use lut_partial_update
if (ePaper.Init(lut_full_update) != 0) {
DEBUG_MSG("ePaper init failed\n");
return false;
} else {
frame.setColorDepth(1); // Must set the bits per pixel to 1 for ePaper displays
// Set bit depth BEFORE creating Sprite, default is 16!
// Create a frame buffer in RAM of defined size and save the pointer to it
// RAM needed is about (EPD_WIDTH * EPD_HEIGHT)/8 , ~5000 bytes for 200 x 200 pixels
// Note: always create the Sprite before setting the Sprite rotation
framePtr = (uint8_t *)frame.createSprite(EPD_WIDTH, EPD_HEIGHT);
frame.fillSprite(PAPER); // Fill frame with white
/* frame.drawLine(0, 0, frame.width() - 1, frame.height() - 1, INK);
frame.drawLine(0, frame.height() - 1, frame.width() - 1, 0, INK);
updateDisplay(); */
return true;
}
}
#endif

View File

@@ -0,0 +1,35 @@
#pragma once
#include <OLEDDisplay.h>
/**
* An adapter class that allows using the TFT_eSPI library as if it was an OLEDDisplay implementation.
*
* Remaining TODO:
* optimize display() to only draw changed pixels (see other OLED subclasses for examples)
* implement displayOn/displayOff to turn off the TFT device (and backlight)
* Use the fast NRF52 SPI API rather than the slow standard arduino version
*
* turn radio back on - currently with both on spi bus is fucked? or are we leaving chip select asserted?
*/
class EInkDisplay : public OLEDDisplay
{
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
virtual void display(void);
protected:
// the header size of the buffer used, e.g. for the SPI command header
virtual int getBufferOffset(void) { return 0; }
// Send a command to the display (low level function)
virtual void sendCommand(uint8_t com);
// Connect to the display
virtual bool connect();
};

View File

@@ -31,6 +31,8 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#include "graphics/images.h"
#include "main.h"
#include "mesh-pb-constants.h"
#include "meshwifi/meshwifi.h"
#include "target_specific.h"
#include "utils.h"
using namespace meshtastic; /** @todo remove */
@@ -239,6 +241,39 @@ static void drawGPS(OLEDDisplay *display, int16_t x, int16_t y, const GPSStatus
}
}
static void drawGPSAltitude(OLEDDisplay *display, int16_t x, int16_t y, const GPSStatus *gps)
{
String displayLine = "";
if (!gps->getIsConnected()) {
// displayLine = "No GPS Module";
// display->drawString(x + (SCREEN_WIDTH - (display->getStringWidth(displayLine))) / 2, y, displayLine);
} else if (!gps->getHasLock()) {
// displayLine = "No GPS Lock";
// display->drawString(x + (SCREEN_WIDTH - (display->getStringWidth(displayLine))) / 2, y, displayLine);
} else {
displayLine = "Altitude: " + String(gps->getAltitude()) + "m";
display->drawString(x + (SCREEN_WIDTH - (display->getStringWidth(displayLine))) / 2, y, displayLine);
}
}
// Draw GPS status coordinates
static void drawGPScoordinates(OLEDDisplay *display, int16_t x, int16_t y, const GPSStatus *gps)
{
String displayLine = "";
if (!gps->getIsConnected()) {
displayLine = "No GPS Module";
display->drawString(x + (SCREEN_WIDTH - (display->getStringWidth(displayLine))) / 2, y, displayLine);
} else if (!gps->getHasLock()) {
displayLine = "No GPS Lock";
display->drawString(x + (SCREEN_WIDTH - (display->getStringWidth(displayLine))) / 2, y, displayLine);
} else {
char coordinateLine[22];
sprintf(coordinateLine, "%f %f", gps->getLatitude() * 1e-7, gps->getLongitude() * 1e-7);
display->drawString(x + (SCREEN_WIDTH - (display->getStringWidth(coordinateLine))) / 2, y, coordinateLine);
}
}
/// Ported from my old java code, returns distance in meters along the globe
/// surface (by magic?)
static float latLongToMeter(double lat_a, double lng_a, double lat_b, double lng_b)
@@ -685,6 +720,18 @@ void Screen::drawDebugInfoTrampoline(OLEDDisplay *display, OLEDDisplayUiState *s
screen->debugInfo.drawFrame(display, state, x, y);
}
void Screen::drawDebugInfoSettingsTrampoline(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y)
{
Screen *screen = reinterpret_cast<Screen *>(state->userData);
screen->debugInfo.drawFrameSettings(display, state, x, y);
}
void Screen::drawDebugInfoWiFiTrampoline(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y)
{
Screen *screen = reinterpret_cast<Screen *>(state->userData);
screen->debugInfo.drawFrameWiFi(display, state, x, y);
}
// restore our regular frame list
void Screen::setFrames()
{
@@ -712,6 +759,14 @@ void Screen::setFrames()
// call a method on debugInfo object.
normalFrames[numframes++] = &Screen::drawDebugInfoTrampoline;
// call a method on debugInfoScreen object (for more details)
normalFrames[numframes++] = &Screen::drawDebugInfoSettingsTrampoline;
if (isWifiAvailable()) {
// call a method on debugInfoScreen object (for more details)
normalFrames[numframes++] = &Screen::drawDebugInfoWiFiTrampoline;
}
ui.setFrames(normalFrames, numframes);
ui.enableAllIndicators();
@@ -770,18 +825,18 @@ void DebugInfo::drawFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16
{
concurrency::LockGuard guard(&lock);
snprintf(channelStr, sizeof(channelStr), "%s", channelName.c_str());
// Display power status
if (powerStatus->getHasBattery())
drawBattery(display, x, y + 2, imgBattery, powerStatus);
else
display->drawFastImage(x, y + 2, 16, 8, powerStatus->getHasUSB() ? imgUSB : imgPower);
// Display nodes status
drawNodes(display, x + (SCREEN_WIDTH * 0.25), y + 2, nodeStatus);
// Display GPS status
drawGPS(display, x + (SCREEN_WIDTH * 0.63), y + 2, gpsStatus);
}
// Display power status
if (powerStatus->getHasBattery())
drawBattery(display, x, y + 2, imgBattery, powerStatus);
else if (powerStatus->knowsUSB())
display->drawFastImage(x, y + 2, 16, 8, powerStatus->getHasUSB() ? imgUSB : imgPower);
// Display nodes status
drawNodes(display, x + (SCREEN_WIDTH * 0.25), y + 2, nodeStatus);
// Display GPS status
drawGPS(display, x + (SCREEN_WIDTH * 0.63), y + 2, gpsStatus);
// Draw the channel name
display->drawString(x, y + FONT_HEIGHT, channelStr);
// Draw our hardware ID to assist with bluetooth pairing
@@ -799,6 +854,191 @@ void DebugInfo::drawFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16
#endif
}
// Jm
void DebugInfo::drawFrameWiFi(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y)
{
#ifdef HAS_WIFI
const char *wifiName = radioConfig.preferences.wifi_ssid;
const char *wifiPsw = radioConfig.preferences.wifi_password;
displayedNodeNum = 0; // Not currently showing a node pane
display->setFont(ArialMT_Plain_10);
// The coordinates define the left starting point of the text
display->setTextAlignment(TEXT_ALIGN_LEFT);
if (radioConfig.preferences.wifi_ap_mode) {
display->drawString(x, y, String("WiFi: Software AP"));
} else if (WiFi.status() != WL_CONNECTED) {
display->drawString(x, y, String("WiFi: Not Connected"));
} else {
display->drawString(x, y, String("WiFi: Connected"));
display->drawString(x + SCREEN_WIDTH - display->getStringWidth("RSSI " + String(WiFi.RSSI())), y,
"RSSI " + String(WiFi.RSSI()));
}
/*
- WL_CONNECTED: assigned when connected to a WiFi network;
- WL_NO_SSID_AVAIL: assigned when no SSID are available;
- WL_CONNECT_FAILED: assigned when the connection fails for all the attempts;
- WL_CONNECTION_LOST: assigned when the connection is lost;
- WL_DISCONNECTED: assigned when disconnected from a network;
- WL_IDLE_STATUS: it is a temporary status assigned when WiFi.begin() is called and remains active until the number of
attempts expires (resulting in WL_CONNECT_FAILED) or a connection is established (resulting in WL_CONNECTED);
- WL_SCAN_COMPLETED: assigned when the scan networks is completed;
- WL_NO_SHIELD: assigned when no WiFi shield is present;
*/
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()));
} else {
display->drawString(x, y + FONT_HEIGHT * 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");
} else if (WiFi.status() == WL_CONNECTION_LOST) {
display->drawString(x, y + FONT_HEIGHT * 1, "Connection Lost");
} else if (WiFi.status() == WL_CONNECT_FAILED) {
display->drawString(x, y + FONT_HEIGHT * 1, "Connection Failed");
//} else if (WiFi.status() == WL_DISCONNECTED) {
// display->drawString(x, y + FONT_HEIGHT * 1, "Disconnected");
} else if (WiFi.status() == WL_IDLE_STATUS) {
display->drawString(x, y + FONT_HEIGHT * 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");
} else if (getWifiDisconnectReason() == 3) {
display->drawString(x, y + FONT_HEIGHT * 1, "De-authenticated");
} else if (getWifiDisconnectReason() == 4) {
display->drawString(x, y + FONT_HEIGHT * 1, "Disassociated Expired");
} else if (getWifiDisconnectReason() == 5) {
display->drawString(x, y + FONT_HEIGHT * 1, "AP - Too Many Clients");
} else if (getWifiDisconnectReason() == 6) {
display->drawString(x, y + FONT_HEIGHT * 1, "NOT_AUTHED");
} else if (getWifiDisconnectReason() == 7) {
display->drawString(x, y + FONT_HEIGHT * 1, "NOT_ASSOCED");
} else if (getWifiDisconnectReason() == 8) {
display->drawString(x, y + FONT_HEIGHT * 1, "Disassociated");
} else if (getWifiDisconnectReason() == 9) {
display->drawString(x, y + FONT_HEIGHT * 1, "ASSOC_NOT_AUTHED");
} else if (getWifiDisconnectReason() == 10) {
display->drawString(x, y + FONT_HEIGHT * 1, "DISASSOC_PWRCAP_BAD");
} else if (getWifiDisconnectReason() == 11) {
display->drawString(x, y + FONT_HEIGHT * 1, "DISASSOC_SUPCHAN_BAD");
} else if (getWifiDisconnectReason() == 13) {
display->drawString(x, y + FONT_HEIGHT * 1, "IE_INVALID");
} else if (getWifiDisconnectReason() == 14) {
display->drawString(x, y + FONT_HEIGHT * 1, "MIC_FAILURE");
} else if (getWifiDisconnectReason() == 15) {
display->drawString(x, y + FONT_HEIGHT * 1, "AP Handshake Timeout");
} else if (getWifiDisconnectReason() == 16) {
display->drawString(x, y + FONT_HEIGHT * 1, "GROUP_KEY_UPDATE_TIMEOUT");
} else if (getWifiDisconnectReason() == 17) {
display->drawString(x, y + FONT_HEIGHT * 1, "IE_IN_4WAY_DIFFERS");
} else if (getWifiDisconnectReason() == 18) {
display->drawString(x, y + FONT_HEIGHT * 1, "Invalid Group Cipher");
} else if (getWifiDisconnectReason() == 19) {
display->drawString(x, y + FONT_HEIGHT * 1, "Invalid Pairwise Cipher");
} else if (getWifiDisconnectReason() == 20) {
display->drawString(x, y + FONT_HEIGHT * 1, "AKMP_INVALID");
} else if (getWifiDisconnectReason() == 21) {
display->drawString(x, y + FONT_HEIGHT * 1, "UNSUPP_RSN_IE_VERSION");
} else if (getWifiDisconnectReason() == 22) {
display->drawString(x, y + FONT_HEIGHT * 1, "INVALID_RSN_IE_CAP");
} else if (getWifiDisconnectReason() == 23) {
display->drawString(x, y + FONT_HEIGHT * 1, "802_1X_AUTH_FAILED");
} else if (getWifiDisconnectReason() == 24) {
display->drawString(x, y + FONT_HEIGHT * 1, "CIPHER_SUITE_REJECTED");
} else if (getWifiDisconnectReason() == 200) {
display->drawString(x, y + FONT_HEIGHT * 1, "BEACON_TIMEOUT");
} else if (getWifiDisconnectReason() == 201) {
display->drawString(x, y + FONT_HEIGHT * 1, "AP Not Found");
} else if (getWifiDisconnectReason() == 202) {
display->drawString(x, y + FONT_HEIGHT * 1, "AUTH_FAIL");
} else if (getWifiDisconnectReason() == 203) {
display->drawString(x, y + FONT_HEIGHT * 1, "ASSOC_FAIL");
} else if (getWifiDisconnectReason() == 204) {
display->drawString(x, y + FONT_HEIGHT * 1, "HANDSHAKE_TIMEOUT");
} else if (getWifiDisconnectReason() == 205) {
display->drawString(x, y + FONT_HEIGHT * 1, "Connection Failed");
} else {
display->drawString(x, y + FONT_HEIGHT * 1, "Unknown Status");
}
}
display->drawString(x, y + FONT_HEIGHT * 2, "SSID: " + String(wifiName));
display->drawString(x, y + FONT_HEIGHT * 3, "PWD: " + String(wifiPsw));
/* Display a heartbeat pixel that blinks every time the frame is redrawn */
#ifdef SHOW_REDRAWS
if (heartbeat)
display->setPixel(0, 0);
heartbeat = !heartbeat;
#endif
#endif
}
void DebugInfo::drawFrameSettings(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y)
{
displayedNodeNum = 0; // Not currently showing a node pane
display->setFont(ArialMT_Plain_10);
// The coordinates define the left starting point of the text
display->setTextAlignment(TEXT_ALIGN_LEFT);
char batStr[20];
if (powerStatus->getHasBattery()) {
int batV = powerStatus->getBatteryVoltageMv() / 1000;
int batCv = (powerStatus->getBatteryVoltageMv() % 1000) / 10;
snprintf(batStr, sizeof(batStr), "B %01d.%02dV %3d%% %c%c", batV, batCv, powerStatus->getBatteryChargePercent(),
powerStatus->getIsCharging() ? '+' : ' ', powerStatus->getHasUSB() ? 'U' : ' ');
// Line 1
display->drawString(x, y, batStr);
} else {
// Line 1
display->drawString(x, y, String("USB"));
}
display->drawString(x + SCREEN_WIDTH - display->getStringWidth("Mode " + String(channelSettings.modem_config)), y,
"Mode " + String(channelSettings.modem_config));
// Line 2
uint32_t currentMillis = millis();
uint32_t seconds = currentMillis / 1000;
uint32_t minutes = seconds / 60;
uint32_t hours = minutes / 60;
uint32_t days = hours / 24;
currentMillis %= 1000;
seconds %= 60;
minutes %= 60;
hours %= 24;
display->drawString(x, y + FONT_HEIGHT * 1,
String(days) + "d " + (hours < 10 ? "0" : "") + String(hours) + ":" + (minutes < 10 ? "0" : "") +
String(minutes) + ":" + (seconds < 10 ? "0" : "") + String(seconds));
// Line 3
drawGPSAltitude(display, x, y + FONT_HEIGHT * 2, gpsStatus);
// Line 4
drawGPScoordinates(display, x, y + FONT_HEIGHT * 3, gpsStatus);
/* Display a heartbeat pixel that blinks every time the frame is redrawn */
#ifdef SHOW_REDRAWS
if (heartbeat)
display->setPixel(0, 0);
heartbeat = !heartbeat;
#endif
}
// adjust Brightness cycle trough 1 to 254 as long as attachDuringLongPress is true
void Screen::adjustBrightness()
{
@@ -819,14 +1059,16 @@ int Screen::handleStatusUpdate(const meshtastic::Status *arg)
// DEBUG_MSG("Screen got status update %d\n", arg->getStatusType());
switch (arg->getStatusType()) {
case STATUS_TYPE_NODE:
if (nodeDB.updateTextMessage || nodeStatus->getLastNumTotal() != nodeStatus->getNumTotal())
setFrames();
prevFrame = -1;
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
}
nodeDB.updateGUI = false;
nodeDB.updateTextMessage = false;
break;
}
setPeriod(1); // Update the screen right away
return 0;
}
} // namespace graphics

View File

@@ -10,11 +10,13 @@
#include <SSD1306Wire.h>
#endif
#include "concurrency/PeriodicTask.h"
#include "EInkDisplay.h"
#include "TFTDisplay.h"
#include "TypedQueue.h"
#include "concurrency/LockGuard.h"
#include "power.h"
#include "commands.h"
#include "concurrency/LockGuard.h"
#include "concurrency/PeriodicTask.h"
#include "power.h"
#include <string>
namespace graphics
@@ -44,6 +46,8 @@ class DebugInfo
/// Renders the debug screen.
void drawFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y);
void drawFrameSettings(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y);
void drawFrameWiFi(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y);
std::string channelName;
@@ -53,16 +57,19 @@ class DebugInfo
/**
* @brief This class deals with showing things on the screen of the device.
*
* @details Other than setup(), this class is thread-safe as long as drawFrame is not called
* multiple times simultaneously. All state-changing calls are queued and executed
*
* @details Other than setup(), this class is thread-safe as long as drawFrame is not called
* multiple times simultaneously. All state-changing calls are queued and executed
* when the main loop calls us.
*/
class Screen : public concurrency::PeriodicTask
{
CallbackObserver<Screen, const meshtastic::Status *> powerStatusObserver = CallbackObserver<Screen, const meshtastic::Status *>(this, &Screen::handleStatusUpdate);
CallbackObserver<Screen, const meshtastic::Status *> gpsStatusObserver = CallbackObserver<Screen, const meshtastic::Status *>(this, &Screen::handleStatusUpdate);
CallbackObserver<Screen, const meshtastic::Status *> nodeStatusObserver = CallbackObserver<Screen, const meshtastic::Status *>(this, &Screen::handleStatusUpdate);
CallbackObserver<Screen, const meshtastic::Status *> powerStatusObserver =
CallbackObserver<Screen, const meshtastic::Status *>(this, &Screen::handleStatusUpdate);
CallbackObserver<Screen, const meshtastic::Status *> gpsStatusObserver =
CallbackObserver<Screen, const meshtastic::Status *>(this, &Screen::handleStatusUpdate);
CallbackObserver<Screen, const meshtastic::Status *> nodeStatusObserver =
CallbackObserver<Screen, const meshtastic::Status *>(this, &Screen::handleStatusUpdate);
public:
Screen(uint8_t address, int sda = -1, int scl = -1);
@@ -125,11 +132,12 @@ class Screen : public concurrency::PeriodicTask
}
/// Overrides the default utf8 character conversion, to replace empty space with question marks
static char customFontTableLookup(const uint8_t ch) {
static char customFontTableLookup(const uint8_t ch)
{
// UTF-8 to font table index converter
// Code form http://playground.arduino.cc/Main/Utf8ascii
static uint8_t LASTCHAR;
static bool SKIPREST; // Only display a single unconvertable-character symbol per sequence of unconvertable characters
static bool SKIPREST; // Only display a single unconvertable-character symbol per sequence of unconvertable characters
if (ch < 128) { // Standard ASCII-set 0..0x7F handling
LASTCHAR = 0;
@@ -137,28 +145,38 @@ class Screen : public concurrency::PeriodicTask
return ch;
}
uint8_t last = LASTCHAR; // get last char
uint8_t last = LASTCHAR; // get last char
LASTCHAR = ch;
switch (last) { // conversion depnding on first UTF8-character
case 0xC2: { SKIPREST = false; return (uint8_t) ch; }
case 0xC3: { SKIPREST = false; return (uint8_t) (ch | 0xC0); }
switch (last) { // conversion depnding on first UTF8-character
case 0xC2: {
SKIPREST = false;
return (uint8_t)ch;
}
case 0xC3: {
SKIPREST = false;
return (uint8_t)(ch | 0xC0);
}
}
// We want to strip out prefix chars for two-byte char formats
if (ch == 0xC2 || ch == 0xC3 || ch == 0x82) return (uint8_t) 0;
if (ch == 0xC2 || ch == 0xC3 || ch == 0x82)
return (uint8_t)0;
// If we already returned an unconvertable-character symbol for this unconvertable-character sequence, return NULs for the rest of it
if (SKIPREST) return (uint8_t) 0;
// If we already returned an unconvertable-character symbol for this unconvertable-character sequence, return NULs for the
// rest of it
if (SKIPREST)
return (uint8_t)0;
SKIPREST = true;
return (uint8_t) 191; // otherwise: return ¿ if character can't be converted (note that the font map we're using doesn't stick to standard EASCII codes)
return (uint8_t)191; // otherwise: return ¿ if character can't be converted (note that the font map we're using doesn't
// stick to standard EASCII codes)
}
/// Returns a handle to the DebugInfo screen.
//
// Use this handle to set things like battery status, user count, GPS status, etc.
DebugInfo* debug_info() { return &debugInfo; }
DebugInfo *debug_info() { return &debugInfo; }
int handleStatusUpdate(const meshtastic::Status *arg);
@@ -201,6 +219,10 @@ class Screen : public concurrency::PeriodicTask
/// 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);
static void drawDebugInfoSettingsTrampoline(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y);
static void drawDebugInfoWiFiTrampoline(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y);
/// Queue of commands to execute in doTask.
TypedQueue<ScreenCmd> cmdQueue;
/// Whether we are using a display
@@ -215,8 +237,12 @@ class Screen : public concurrency::PeriodicTask
DebugInfo debugInfo;
/// Display device
/** @todo display abstraction */
#ifdef USE_SH1106
/** FIXME cleanup display abstraction */
#ifdef ST7735_CS
TFTDisplay dispdev;
#elif defined(HAS_EINK)
EInkDisplay dispdev;
#elif defined(USE_SH1106)
SH1106Wire dispdev;
#else
SSD1306Wire dispdev;

View File

@@ -1,17 +0,0 @@
#include "configuration.h"
#ifdef ST7735_CS
#include <SPI.h>
#include <TFT_eSPI.h> // Graphics and font library for ST7735 driver chip
TFT_eSPI tft = TFT_eSPI(); // Invoke library, pins defined in User_Setup.h
void TFTinit()
{
tft.init();
tft.setRotation(1);
tft.fillScreen(TFT_BLUE);
}
#endif

View File

@@ -1,3 +0,0 @@
#pragma once
void TFTinit();

View File

@@ -0,0 +1,61 @@
#include "configuration.h"
#ifdef ST7735_CS
#include "SPILock.h"
#include "TFTDisplay.h"
#include "graphics/configs.h"
#include <SPI.h>
#include <TFT_eSPI.h> // Graphics and font library for ST7735 driver chip
static TFT_eSPI tft = TFT_eSPI(); // Invoke library, pins defined in User_Setup.h
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
}
// Write the buffer to the display memory
void TFTDisplay::display(void)
{
concurrency::LockGuard g(spiLock);
// 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++) {
// get src pixel in the page based ordering the OLED lib uses FIXME, super inefficent
auto b = buffer[x + (y / 8) * SCREEN_WIDTH];
auto isset = b & (1 << (y & 7));
tft.drawPixel(x, y, isset ? TFT_WHITE : TFT_BLACK);
}
}
}
// Send a command to the display (low level function)
void TFTDisplay::sendCommand(uint8_t com)
{
(void)com;
// Drop all commands to device (we just update the buffer)
}
// Connect to the display
bool TFTDisplay::connect()
{
DEBUG_MSG("Doing TFT init\n");
#ifdef ST7735_BACKLIGHT_EN
digitalWrite(ST7735_BACKLIGHT_EN, HIGH);
pinMode(ST7735_BACKLIGHT_EN, OUTPUT);
#endif
tft.init();
tft.setRotation(3); // Orient horizontal and wide underneath the silkscreen name label
tft.fillScreen(TFT_BLACK);
// tft.drawRect(0, 0, 40, 10, TFT_PURPLE); // wide rectangle in upper left
return true;
}
#endif

35
src/graphics/TFTDisplay.h Normal file
View File

@@ -0,0 +1,35 @@
#pragma once
#include <OLEDDisplay.h>
/**
* An adapter class that allows using the TFT_eSPI library as if it was an OLEDDisplay implementation.
*
* Remaining TODO:
* optimize display() to only draw changed pixels (see other OLED subclasses for examples)
* implement displayOn/displayOff to turn off the TFT device (and backlight)
* Use the fast NRF52 SPI API rather than the slow standard arduino version
*
* turn radio back on - currently with both on spi bus is fucked? or are we leaving chip select asserted?
*/
class TFTDisplay : public OLEDDisplay
{
public:
/* constructor
FIXME - the parameters are not used, just a temporary hack to keep working like the old displays
*/
TFTDisplay(uint8_t address, int sda, int scl);
// Write the buffer to the display memory
virtual void display(void);
protected:
// the header size of the buffer used, e.g. for the SPI command header
virtual int getBufferOffset(void) { return 0; }
// Send a command to the display (low level function)
virtual void sendCommand(uint8_t com);
// Connect to the display
virtual bool connect();
};

View File

@@ -21,9 +21,9 @@
*/
#include "Air530GPS.h"
#include "MeshRadio.h"
#include "MeshService.h"
#include "NEMAGPS.h"
#include "NodeDB.h"
#include "PowerFSM.h"
#include "UBloxGPS.h"
@@ -34,10 +34,14 @@
// #include "rom/rtc.h"
#include "DSRRouter.h"
// #include "debug.h"
#include "RTC.h"
#include "SPILock.h"
#include "graphics/Screen.h"
#include "main.h"
#include "meshwifi/meshhttp.h"
#include "meshwifi/meshwifi.h"
#include "sleep.h"
#include "timing.h"
#include "target_specific.h"
#include <OneButton.h>
#include <Wire.h>
// #include <driver/rtc_io.h>
@@ -148,6 +152,8 @@ void userButtonPressedLong()
screen.adjustBrightness();
}
RadioInterface *rIf = NULL;
void setup()
{
#ifdef USE_SEGGER
@@ -210,19 +216,33 @@ void setup()
esp32Setup();
#endif
// Currently only the tbeam has a PMU
power = new Power();
power->setup();
power->setStatusHandler(powerStatus);
powerStatus->observe(&power->newStatus);
#ifdef NRF52_SERIES
nrf52Setup();
#endif
// Currently only the tbeam has a PMU
power = new Power();
power->setStatusHandler(powerStatus);
powerStatus->observe(&power->newStatus);
power->setup(); // Must be after status handler is installed, so that handler gets notified of the initial configuration
// Init our SPI controller (must be before screen and lora)
initSPI();
#ifdef NO_ESP32
SPI.begin();
#else
// ESP32
SPI.begin(RF95_SCK, RF95_MISO, RF95_MOSI, RF95_NSS);
SPI.setFrequency(4000000);
#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");
@@ -241,19 +261,26 @@ void setup()
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 NEMA at 9600 baud.
DEBUG_MSG("Hoping that NEMA might work\n");
// assume NMEA at 9600 baud.
// dumb NMEA access only work for serial GPSes)
DEBUG_MSG("Hoping that NMEA might work\n");
// dumb NEMA access only work for serial GPSes)
gps = new NEMAGPS();
#ifdef HAS_AIR530_GPS
gps = new Air530GPS();
#else
gps = new NMEAGPS();
#endif
gps->setup();
}
}
#else
gps = new NEMAGPS();
gps = new NMEAGPS();
gps->setup();
#endif
gpsStatus->observe(&gps->newStatus);
if (gps)
gpsStatus->observe(&gps->newStatus);
else
DEBUG_MSG("Warning: No GPS found - running without GPS\n");
nodeStatus->observe(&nodeDB.newStatus);
service.init();
@@ -274,17 +301,7 @@ void setup()
digitalWrite(SX1262_ANT_SW, 1);
#endif
// Init our SPI controller
#ifdef NRF52_SERIES
SPI.begin();
#else
// ESP32
SPI.begin(RF95_SCK, RF95_MISO, RF95_MOSI, RF95_NSS);
SPI.setFrequency(4000000);
#endif
// MUST BE AFTER service.init, so we have our radio config settings (from nodedb init)
RadioInterface *rIf = NULL;
// radio init MUST BE AFTER service.init, so we have our radio config settings (from nodedb init)
#if defined(RF95_IRQ)
if (!rIf) {
@@ -319,6 +336,9 @@ void setup()
}
#endif
// Initialize Wifi
initWifi();
if (!rIf)
recordCriticalError(ErrNoRadio);
else
@@ -356,7 +376,8 @@ void loop()
{
uint32_t msecstosleep = 1000 * 30; // How long can we sleep before we again need to service the main loop?
gps->loop(); // FIXME, remove from main, instead block on read
if (gps)
gps->loop(); // FIXME, remove from main, instead block on read
router.loop();
powerFSM.run_machine();
service.loop();
@@ -384,24 +405,28 @@ void loop()
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 && (timing::millis() > 3000)) {
if (showingBootScreen && (millis() > 3000)) {
screen.stopBootScreen();
showingBootScreen = false;
}
#ifdef DEBUG_STACK
static uint32_t lastPrint = 0;
if (timing::millis() - lastPrint > 10 * 1000L) {
lastPrint = timing::millis();
if (millis() - lastPrint > 10 * 1000L) {
lastPrint = millis();
meshtastic::printThreadInfo("main");
}
#endif
// Update the screen last, after we've figured out what to show.
screen.debug_info()->setChannelNameStatus(getChannelName());
// screen.debug()->setPowerStatus(powerStatus);
// 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.
@@ -409,7 +434,10 @@ void loop()
// 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;
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);
}

View File

@@ -10,6 +10,8 @@ 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
@@ -21,6 +23,6 @@ extern graphics::Screen screen;
// Return a human readable string of the form "Meshtastic_ab13"
const char *getDeviceName();
void getMacAddr(uint8_t *dmac);
void nrf52Setup(), esp32Setup(), nrf52Loop(), esp32Loop();
void nrf52Setup(), esp32Setup(), nrf52Loop(), esp32Loop();

View File

@@ -6,56 +6,14 @@
#include "configuration.h"
#include "mesh.pb.h"
// US channel settings
#define CH0_US 903.08f // MHz
#define CH_SPACING_US 2.16f // MHz
#define NUM_CHANNELS_US 13
// EU433 channel settings
#define CH0_EU433 433.175f // MHz
#define CH_SPACING_EU433 0.2f // MHz
#define NUM_CHANNELS_EU433 8
// EU865 channel settings
#define CH0_EU865 865.2f // MHz
#define CH_SPACING_EU865 0.3f // MHz
#define NUM_CHANNELS_EU865 10
// CN channel settings
#define CH0_CN 470.0f // MHz
#define CH_SPACING_CN 2.0f // MHz FIXME, this is just a guess for 470-510
#define NUM_CHANNELS_CN 20
// JP channel settings
#define CH0_JP 920.0f // MHz
#define CH_SPACING_JP 0.5f // MHz FIXME, this is just a guess for 920-925
#define NUM_CHANNELS_JP 10
// FIXME add defs for other regions and use them here
#ifdef HW_VERSION_US
#define CH0 CH0_US
#define CH_SPACING CH_SPACING_US
#define NUM_CHANNELS NUM_CHANNELS_US
#elif defined(HW_VERSION_EU433)
#define CH0 CH0_EU433
#define CH_SPACING CH_SPACING_EU433
#define NUM_CHANNELS NUM_CHANNELS_EU433
#elif defined(HW_VERSION_EU865)
#define CH0 CH0_EU865
#define CH_SPACING CH_SPACING_EU865
#define NUM_CHANNELS NUM_CHANNELS_EU865
#elif defined(HW_VERSION_CN)
#define CH0 CH0_CN
#define CH_SPACING CH_SPACING_CN
#define NUM_CHANNELS NUM_CHANNELS_CN
#elif defined(HW_VERSION_JP)
#define CH0 CH0_JP
#define CH_SPACING CH_SPACING_JP
#define NUM_CHANNELS NUM_CHANNELS_JP
#else
// HW version not set - assume US
#define CH0 CH0_US
#define CH_SPACING CH_SPACING_US
#define NUM_CHANNELS NUM_CHANNELS_US
#endif
// Map from old region names to new region enums
struct RegionInfo {
RegionCode code;
uint8_t numChannels;
uint8_t powerLimit; // Or zero for not set
float freq;
float spacing;
const char *name; // EU433 etc
};
extern const RegionInfo regions[];

View File

@@ -10,10 +10,10 @@
#include "MeshService.h"
#include "NodeDB.h"
#include "PowerFSM.h"
#include "RTC.h"
#include "main.h"
#include "mesh-pb-constants.h"
#include "power.h"
#include "timing.h"
/*
receivedPacketQueue - this is a queue of messages we've received from the mesh, which we are keeping to deliver to the phone.
@@ -53,7 +53,7 @@ static uint32_t sendOwnerCb()
{
service.sendOurOwner();
return radioConfig.preferences.send_owner_interval * radioConfig.preferences.position_broadcast_secs * 1000;
return getPref_send_owner_interval() * getPref_position_broadcast_secs() * 1000;
}
static concurrency::Periodic sendOwnerPeriod(sendOwnerCb);
@@ -68,8 +68,8 @@ void MeshService::init()
sendOwnerPeriod.setup();
nodeDB.init();
assert(gps);
gpsObserver.observe(&gps->newStatus);
if (gps)
gpsObserver.observe(&gps->newStatus);
packetReceivedObserver.observe(&router.notifyPacketReceived);
}
@@ -140,7 +140,7 @@ void MeshService::handleIncomingPosition(const MeshPacket *mp)
tv.tv_sec = secs;
tv.tv_usec = 0;
perhapsSetRTC(&tv);
perhapsSetRTC(RTCQualityFromNet, &tv);
}
} else {
DEBUG_MSG("Ignoring incoming packet - not a position\n");
@@ -151,12 +151,8 @@ int MeshService::handleFromRadio(const MeshPacket *mp)
{
powerFSM.trigger(EVENT_RECEIVED_PACKET); // Possibly keep the node from sleeping
// If it is a position packet, perhaps set our clock (if we don't have a GPS of our own, otherwise wait for that to work)
if (!gps->isConnected)
handleIncomingPosition(mp);
else {
DEBUG_MSG("Ignoring incoming time, because we have a GPS\n");
}
// If it is a position packet, perhaps set our clock
handleIncomingPosition(mp);
if (mp->which_payload == MeshPacket_decoded_tag && mp->decoded.which_payload == SubPacket_user_tag) {
mp = handleFromRadioUser(mp);
@@ -198,12 +194,21 @@ void MeshService::loop()
}
/// The radioConfig object just changed, call this to force the hw to change to the new settings
void MeshService::reloadConfig()
bool MeshService::reloadConfig()
{
// If we can successfully set this radio to these settings, save them to disk
nodeDB.resetRadioConfig(); // Don't let the phone send us fatally bad settings
bool didReset = nodeDB.resetRadioConfig(); // Don't let the phone send us fatally bad settings
configChanged.notifyObservers(NULL);
nodeDB.saveToDisk();
return didReset;
}
/// The owner User record just got updated, update our node DB and broadcast the info into the mesh
void MeshService::reloadOwner()
{
sendOurOwner();
nodeDB.saveToDisk();
}
/**
@@ -292,8 +297,8 @@ int MeshService::onGPSChanged(const meshtastic::GPSStatus *unused)
p->decoded.which_payload = SubPacket_position_tag;
Position &pos = p->decoded.position;
// !zero or !zero lat/long means valid
if (gps->latitude != 0 || gps->longitude != 0) {
if (gps->hasLock()) {
if (gps->altitude != 0)
pos.altitude = gps->altitude;
pos.latitude_i = gps->latitude;
@@ -309,8 +314,8 @@ int MeshService::onGPSChanged(const meshtastic::GPSStatus *unused)
// We limit our GPS broadcasts to a max rate
static uint32_t lastGpsSend;
uint32_t now = timing::millis();
if (lastGpsSend == 0 || now - lastGpsSend > radioConfig.preferences.position_broadcast_secs * 1000) {
uint32_t now = millis();
if (lastGpsSend == 0 || now - lastGpsSend > getPref_position_broadcast_secs() * 1000) {
lastGpsSend = now;
DEBUG_MSG("Sending position to mesh\n");

View File

@@ -63,11 +63,13 @@ class MeshService
*/
void handleToRadio(MeshPacket &p);
/// The radioConfig object just changed, call this to force the hw to change to the new settings
void reloadConfig();
/** The radioConfig object just changed, call this to force the hw to change to the new settings
* @return true if client devices should be sent a new set of radio configs
*/
bool reloadConfig();
/// The owner User record just got updated, update our node DB and broadcast the info into the mesh
void reloadOwner() { sendOurOwner(); }
void reloadOwner();
/// Called when the user wakes up our GUI, normally sends our latest location to the mesh (if we have it), otherwise at least
/// sends our owner

View File

@@ -6,13 +6,16 @@
#include "CryptoEngine.h"
#include "GPS.h"
#include "MeshRadio.h"
#include "NodeDB.h"
#include "PacketHistory.h"
#include "PowerFSM.h"
#include "RTC.h"
#include "Router.h"
#include "configuration.h"
#include "error.h"
#include "mesh-pb-constants.h"
#include "meshwifi/meshwifi.h"
#include <pb_decode.h>
#include <pb_encode.h>
@@ -32,7 +35,14 @@ DeviceState versions used to be defined in the .proto file but really only this
#define DEVICESTATE_CUR_VER 11
#define DEVICESTATE_MIN_VER DEVICESTATE_CUR_VER
#ifndef NO_ESP32
#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
@@ -86,7 +96,7 @@ const char *getChannelName()
static char buf[32];
uint8_t code = 0;
for (int i = 0; i < sizeof(channelSettings.psk.size); i++)
for (int i = 0; i < channelSettings.psk.size; i++)
code ^= channelSettings.psk.bytes[i];
snprintf(buf, sizeof(buf), "#%s-%c", channelSettings.name, 'A' + (code % 26));
@@ -95,33 +105,31 @@ const char *getChannelName()
NodeDB::NodeDB() : nodes(devicestate.node_db), numNodes(&devicestate.node_db_count) {}
void NodeDB::resetRadioConfig()
bool NodeDB::resetRadioConfig()
{
bool didFactoryReset = false;
/// 16 bytes of random PSK for our _public_ default channel that all devices power up on (AES128)
static const uint8_t defaultpsk[] = {0xd4, 0xf1, 0xbb, 0x3a, 0x20, 0x29, 0x07, 0x59,
0xf0, 0xbc, 0xff, 0xab, 0xcf, 0x4e, 0x69, 0xbf};
if (radioConfig.preferences.sds_secs == 0) {
DEBUG_MSG("RadioConfig reset!\n");
radioConfig.preferences.send_owner_interval = 4; // per sw-design.md
radioConfig.preferences.position_broadcast_secs = 15 * 60;
radioConfig.preferences.wait_bluetooth_secs = 120;
radioConfig.preferences.screen_on_secs = 5 * 60;
radioConfig.preferences.mesh_sds_timeout_secs = 2 * 60 * 60;
radioConfig.preferences.phone_sds_timeout_sec = 2 * 60 * 60;
radioConfig.preferences.sds_secs = 365 * 24 * 60 * 60; // one year
radioConfig.preferences.ls_secs = 60 * 60;
radioConfig.preferences.phone_timeout_secs = 15 * 60;
if (radioConfig.preferences.factory_reset) {
DEBUG_MSG("Performing factory reset!\n");
installDefaultDeviceState();
didFactoryReset = true;
} else if (!channelSettings.psk.size) {
DEBUG_MSG("Setting default preferences!\n");
radioConfig.has_channel_settings = true;
radioConfig.has_preferences = true;
// radioConfig.modem_config = RadioConfig_ModemConfig_Bw125Cr45Sf128; // medium range and fast
// channelSettings.modem_config = ChannelSettings_ModemConfig_Bw500Cr45Sf128; // short range and fast, but wide bandwidth
// so incompatible radios can talk together
// channelSettings.modem_config = ChannelSettings_ModemConfig_Bw500Cr45Sf128; // short range and fast, but wide
// bandwidth so incompatible radios can talk together
channelSettings.modem_config = ChannelSettings_ModemConfig_Bw125Cr48Sf4096; // slow and long range
channelSettings.tx_power = 0; // default
memcpy(&channelSettings.psk.bytes, &defaultpsk, sizeof(channelSettings.psk));
memcpy(&channelSettings.psk.bytes, defaultpsk, sizeof(channelSettings.psk));
channelSettings.psk.size = sizeof(defaultpsk);
strcpy(channelSettings.name, "Default");
}
@@ -140,12 +148,20 @@ void NodeDB::resetRadioConfig()
radioConfig.preferences.position_broadcast_secs = 6 * 60;
radioConfig.preferences.ls_secs = 60;
}
return didFactoryReset;
}
void NodeDB::installDefaultDeviceState()
{
// We try to preserve the region setting because it will really bum users out if we discard it
String oldRegion = myNodeInfo.region;
RegionCode oldRegionCode = radioConfig.preferences.region;
memset(&devicestate, 0, sizeof(devicestate));
*numNodes = 0; // Forget node DB
// init our devicestate with valid flags so protobuf writing/reading will work
devicestate.has_my_node = true;
devicestate.has_radio = true;
@@ -174,6 +190,12 @@ void NodeDB::installDefaultDeviceState()
// owner.short_name now
sprintf(owner.long_name, "Unknown %02x%02x", ourMacAddr[4], ourMacAddr[5]);
sprintf(owner.short_name, "?%02X", (unsigned)(myNodeInfo.my_node_num & 0xff));
// Restore region if possible
if (oldRegionCode != RegionCode_Unset)
radioConfig.preferences.region = oldRegionCode;
if (oldRegion.length())
strcpy(myNodeInfo.region, oldRegion.c_str());
}
void NodeDB::init()
@@ -206,13 +228,29 @@ void NodeDB::init()
// We set these _after_ loading from disk - because they come from the build and are more trusted than
// what is stored in flash
strncpy(myNodeInfo.region, optstr(HW_VERSION), sizeof(myNodeInfo.region));
if (xstr(HW_VERSION)[0])
strncpy(myNodeInfo.region, optstr(HW_VERSION), sizeof(myNodeInfo.region));
else
DEBUG_MSG("This build does not specify a HW_VERSION\n"); // Eventually new builds will no longer include this build flag
// Check for the old style of region code strings, if found, convert to the new enum.
// Those strings will look like "1.0-EU433"
if (radioConfig.preferences.region == RegionCode_Unset && strncmp(myNodeInfo.region, "1.0-", 4) == 0) {
const char *regionStr = myNodeInfo.region + 4; // EU433 or whatever
for (const RegionInfo *r = regions; r->code != RegionCode_Unset; r++)
if (strcmp(r->name, regionStr) == 0) {
radioConfig.preferences.region = r->code;
break;
}
}
strncpy(myNodeInfo.firmware_version, optstr(APP_VERSION), sizeof(myNodeInfo.firmware_version));
strncpy(myNodeInfo.hw_model, HW_VENDOR, sizeof(myNodeInfo.hw_model));
resetRadioConfig(); // If bogus settings got saved, then fix them
DEBUG_MSG("NODENUM=0x%x, dbsize=%d\n", myNodeInfo.my_node_num, *numNodes);
DEBUG_MSG("legacy_region=%s, region=%d, NODENUM=0x%x, dbsize=%d\n", myNodeInfo.region, radioConfig.preferences.region,
myNodeInfo.my_node_num, *numNodes);
}
// We reserve a few nodenums for future use
@@ -399,6 +437,12 @@ void NodeDB::updateFrom(const MeshPacket &mp)
updateTextMessage = true;
powerFSM.trigger(EVENT_RECEIVED_TEXT_MSG);
notifyObservers(true); // Force an update whether or not our node counts have changed
// This is going into the wifidev feature branch
// Only update the WebUI if WiFi is enabled
//#if WiFi_MODE != 0
// notifyWebUI();
//#endif
}
}
break;

View File

@@ -46,8 +46,13 @@ class NodeDB
/// write to flash
void saveToDisk();
// Reinit radio config if needed, because sometimes a buggy android app might send us bogus settings
void resetRadioConfig();
/** Reinit radio config if needed, because either:
* a) sometimes a buggy android app might send us bogus settings or
* b) the client set factory_reset
*
* @return true if the config was completely reset, in that case, we should send it back to the client
*/
bool resetRadioConfig();
/// given a subpacket sniffed from the network, update our DB state
/// we updateGUI and updateGUIforNode if we think our this change is big enough for a redraw
@@ -132,4 +137,18 @@ their nodes
*
* https://github.com/meshtastic/Meshtastic-device/issues/269
*/
const char *getChannelName();
const char *getChannelName();
#define PREF_GET(name, defaultVal) \
inline uint32_t getPref_##name() { return radioConfig.preferences.name ? radioConfig.preferences.name : (defaultVal); }
PREF_GET(send_owner_interval, 4)
PREF_GET(position_broadcast_secs, 15 * 60)
PREF_GET(wait_bluetooth_secs, 120)
PREF_GET(screen_on_secs, 60)
PREF_GET(mesh_sds_timeout_secs, 2 * 60 * 60)
PREF_GET(phone_sds_timeout_sec, 2 * 60 * 60)
PREF_GET(sds_secs, 365 * 24 * 60 * 60)
PREF_GET(ls_secs, 60 * 60)
PREF_GET(phone_timeout_secs, 15 * 60)
PREF_GET(min_wake_secs, 10)

View File

@@ -1,7 +1,6 @@
#include "PacketHistory.h"
#include "configuration.h"
#include "mesh-pb-constants.h"
#include "../timing.h"
PacketHistory::PacketHistory()
{
@@ -19,7 +18,7 @@ bool PacketHistory::wasSeenRecently(const MeshPacket *p, bool withUpdate)
return false; // Not a floodable message ID, so we don't care
}
uint32_t now = timing::millis();
uint32_t now = millis();
for (size_t i = 0; i < recentPackets.size();) {
PacketRecord &r = recentPackets[i];

View File

@@ -4,7 +4,6 @@
#include "NodeDB.h"
#include "PowerFSM.h"
#include "RadioInterface.h"
#include "timing.h"
#include <assert.h>
PhoneAPI::PhoneAPI()
@@ -21,7 +20,7 @@ void PhoneAPI::init()
void PhoneAPI::checkConnectionTimeout()
{
if (isConnected) {
bool newConnected = (timing::millis() - lastContactMsec < radioConfig.preferences.phone_timeout_secs * 1000L);
bool newConnected = (millis() - lastContactMsec < getPref_phone_timeout_secs() * 1000L);
if (!newConnected) {
isConnected = false;
onConnectionChanged(isConnected);
@@ -35,7 +34,7 @@ void PhoneAPI::checkConnectionTimeout()
void PhoneAPI::handleToRadio(const uint8_t *buf, size_t bufLength)
{
powerFSM.trigger(EVENT_CONTACT_FROM_PHONE); // As long as the phone keeps talking to us, don't let the radio go to sleep
lastContactMsec = timing::millis();
lastContactMsec = millis();
if (!isConnected) {
isConnected = true;
onConnectionChanged(isConnected);
@@ -110,7 +109,11 @@ size_t PhoneAPI::getFromRadio(uint8_t *buf)
break;
case STATE_SEND_MY_INFO:
myNodeInfo.has_gps = gps && gps->isConnected; // Update with latest GPS connect info
// If the user has specified they don't want our node to share its location, make sure to tell the phone
// app not to send locations on our behalf.
myNodeInfo.has_gps = (radioConfig.preferences.location_share == LocationSharing_LocDisabled)
? true
: (gps && gps->isConnected); // Update with latest GPS connect info
fromRadioScratch.which_variant = FromRadio_my_info_tag;
fromRadioScratch.variant.my_info = myNodeInfo;
state = STATE_SEND_RADIO;
@@ -244,7 +247,10 @@ void PhoneAPI::handleSetRadio(const RadioConfig &r)
{
radioConfig = r;
service.reloadConfig();
bool didReset = service.reloadConfig();
if (didReset) {
state = STATE_SEND_MY_INFO; // Squirt a completely new set of configs to the client
}
}
/**

View File

@@ -3,9 +3,11 @@
#include "RadioLibRF95.h"
#include <configuration.h>
#define MAX_POWER 17
#define MAX_POWER 20
// if we use 20 we are limited to 1% duty cycle or hw might overheat. For continuous operation set a limit of 17
#define POWER_DEFAULT 17 // How much power to use if the user hasn't set a power level
RF95Interface::RF95Interface(RADIOLIB_PIN_TYPE cs, RADIOLIB_PIN_TYPE irq, RADIOLIB_PIN_TYPE rst, SPIClass &spi)
: RadioLibInterface(cs, irq, rst, 0, spi)
{
@@ -32,9 +34,15 @@ bool RF95Interface::init()
RadioLibInterface::init();
applyModemConfig();
if (power == 0)
power = POWER_DEFAULT;
if (power > MAX_POWER) // This chip has lower power limits than some
power = MAX_POWER;
limitPower();
iface = lora = new RadioLibRF95(&module);
#ifdef RF95_TCXO
@@ -150,7 +158,7 @@ void RF95Interface::startReceive()
isReceiving = true;
// Must be done AFTER, starting transmit, because startTransmit clears (possibly stale) interrupt pending register bits
// Must be done AFTER, starting receive, because startReceive clears (possibly stale) interrupt pending register bits
enableInterrupt(isrRxLevel0);
}
@@ -163,7 +171,7 @@ bool RF95Interface::isActivelyReceiving()
bool RF95Interface::sleep()
{
// put chipset into sleep mode
disableInterrupt();
setStandby(); // First cancel any active receving/sending
lora->sleep();
return true;

View File

@@ -6,11 +6,28 @@
#include "assert.h"
#include "configuration.h"
#include "sleep.h"
#include "timing.h"
#include <assert.h>
#include <pb_decode.h>
#include <pb_encode.h>
#define RDEF(name, freq, spacing, num_ch, power_limit) \
{ \
RegionCode_##name, num_ch, power_limit, freq, spacing, #name \
}
const RegionInfo regions[] = {
RDEF(US, 903.08f, 2.16f, 13, 0), RDEF(EU433, 433.175f, 0.2f, 8, 0), RDEF(EU865, 865.2f, 0.3f, 10, 0),
RDEF(CN, 470.0f, 2.0f, 20, 0),
RDEF(JP, 920.0f, 0.5f, 10, 13), // See https://github.com/meshtastic/Meshtastic-device/issues/346 power level 13
RDEF(ANZ, 916.0f, 0.5f, 20, 0), // AU/NZ channel settings 915-928MHz
RDEF(KR, 921.9f, 0.2f, 8, 0), // KR channel settings (KR920-923) Start from TTN download channel
// freq. (921.9f is for download, others are for uplink)
RDEF(TW, 923.0f, 0.2f, 10, 0), // TW channel settings (AS2 bandplan 923-925MHz)
RDEF(Unset, 903.08f, 2.16f, 13, 0) // Assume US freqs if unset, Must be last
};
static const RegionInfo *myRegion;
/**
* ## LoRaWAN for North America
@@ -68,6 +85,9 @@ void printPacket(const char *prefix, const MeshPacket *p)
if (p->rx_time != 0) {
DEBUG_MSG(" rxtime=%u", p->rx_time);
}
if (p->rx_snr != 0.0) {
DEBUG_MSG(" rxSNR=%g", p->rx_snr);
}
DEBUG_MSG(")\n");
}
@@ -75,7 +95,15 @@ RadioInterface::RadioInterface()
{
assert(sizeof(PacketHeader) == 4 || sizeof(PacketHeader) == 16); // make sure the compiler did what we expected
myNodeInfo.num_channels = NUM_CHANNELS;
if (!myRegion) {
const RegionInfo *r = regions;
for (; r->code != RegionCode_Unset && r->code != radioConfig.preferences.region; r++)
;
myRegion = r;
DEBUG_MSG("Wanted region %d, using %s\n", radioConfig.preferences.region, r->name);
myNodeInfo.num_channels = myRegion->numChannels; // Tell our android app how many channels we have
}
// Can't print strings this early - serial not setup yet
// DEBUG_MSG("Set meshradio defaults name=%s\n", channelSettings.name);
@@ -104,7 +132,7 @@ bool RadioInterface::init()
* djb2 by Dan Bernstein.
* http://www.cse.yorku.ca/~oz/hash.html
*/
unsigned long hash(char *str)
unsigned long hash(const char *str)
{
unsigned long hash = 5381;
int c;
@@ -115,10 +143,6 @@ unsigned long hash(char *str)
return hash;
}
#define POWER_DEFAULT 17
/**
* Pull our channel settings etc... from protobufs to the dumb interface settings
*/
@@ -128,15 +152,40 @@ void RadioInterface::applyModemConfig()
// No Sync Words in LORA mode
power = channelSettings.tx_power;
if (power == 0)
power = POWER_DEFAULT;
assert(myRegion); // Should have been found in init
// If user has manually specified a channel num, then use that, otherwise generate one by hashing the name
int channel_num = (channelSettings.channel_num ? channelSettings.channel_num - 1 : hash(channelSettings.name)) % NUM_CHANNELS;
freq = CH0 + CH_SPACING * channel_num;
int channel_num =
(channelSettings.channel_num ? channelSettings.channel_num - 1 : hash(channelSettings.name)) % myRegion->numChannels;
freq = myRegion->freq + myRegion->spacing * channel_num;
DEBUG_MSG("Set radio: name=%s, config=%u, ch=%d, power=%d\n", channelSettings.name, channelSettings.modem_config, channel_num,
power);
DEBUG_MSG("Radio myRegion->freq: %f\n", myRegion->freq);
DEBUG_MSG("Radio myRegion->spacing: %f\n", myRegion->spacing);
DEBUG_MSG("Radio myRegion->numChannels: %d\n", myRegion->numChannels);
DEBUG_MSG("Radio channel_num: %d\n", channel_num);
DEBUG_MSG("Radio frequency: %f\n", freq);
}
/**
* Some regulatory regions limit xmit power.
* This function should be called by subclasses after setting their desired power. It might lower it
*/
void RadioInterface::limitPower()
{
uint8_t maxPower = 255; // No limit
if (myRegion->powerLimit)
maxPower = myRegion->powerLimit;
if (power > maxPower) {
DEBUG_MSG("Lowering transmit power because of regulatory limits\n");
power = maxPower;
}
DEBUG_MSG("Set radio: final power level=%d\n", power);
}
ErrorCode SimRadio::send(MeshPacket *p)
@@ -162,7 +211,7 @@ size_t RadioInterface::beginSending(MeshPacket *p)
// DEBUG_MSG("sending queued packet on mesh (txGood=%d,rxGood=%d,rxBad=%d)\n", rf95.txGood(), rf95.rxGood(), rf95.rxBad());
assert(p->which_payload == MeshPacket_encrypted_tag); // It should have already been encoded by now
lastTxStart = timing::millis();
lastTxStart = millis();
PacketHeader *h = (PacketHeader *)radiobuf;

View File

@@ -119,6 +119,12 @@ class RadioInterface : protected concurrency::NotifiedWorkerThread
virtual void loop() {} // Idle processing
/**
* Some regulatory regions limit xmit power.
* This function should be called by subclasses after setting their desired power. It might lower it
*/
void limitPower();
/**
* Convert our modemConfig enum into wf, sf, etc...
*

View File

@@ -1,6 +1,7 @@
#include "RadioLibInterface.h"
#include "MeshTypes.h"
#include "NodeDB.h"
#include "SPILock.h"
#include "mesh-pb-constants.h"
#include <configuration.h>
#include <pb_decode.h>
@@ -9,6 +10,13 @@
// FIXME, we default to 4MHz SPI, SPI mode 0, check if the datasheet says it can really do that
static SPISettings spiSettings(4000000, MSBFIRST, SPI_MODE0);
void LockingModule::SPItransfer(uint8_t cmd, uint8_t reg, uint8_t *dataOut, uint8_t *dataIn, uint8_t numBytes)
{
concurrency::LockGuard g(spiLock);
Module::SPItransfer(cmd, reg, dataOut, dataIn, numBytes);
}
RadioLibInterface::RadioLibInterface(RADIOLIB_PIN_TYPE cs, RADIOLIB_PIN_TYPE irq, RADIOLIB_PIN_TYPE rst, RADIOLIB_PIN_TYPE busy,
SPIClass &spi, PhysicalLayer *_iface)
: concurrency::PeriodicTask(0), module(cs, irq, rst, busy, spi, spiSettings), iface(_iface)
@@ -126,6 +134,8 @@ bool RadioLibInterface::canSendImmediately()
/// bluetooth comms code. If the txmit queue is empty it might return an error
ErrorCode RadioLibInterface::send(MeshPacket *p)
{
// Sometimes when testing it is useful to be able to never turn on the xmitter
#ifndef LORA_DISABLE_SENDING
printPacket("enqueuing for send", p);
DEBUG_MSG("txGood=%d,rxGood=%d,rxBad=%d\n", txGood, rxGood, rxBad);
ErrorCode res = txQueue.enqueue(p, 0) ? ERRNO_OK : ERRNO_UNKNOWN;
@@ -140,6 +150,10 @@ ErrorCode RadioLibInterface::send(MeshPacket *p)
startTransmitTimer(true);
return res;
#else
packetPool.release(p);
return ERRNO_UNKNOWN;
#endif
}
bool RadioLibInterface::canSleep()

View File

@@ -16,6 +16,49 @@
#define INTERRUPT_ATTR
#endif
/**
* A wrapper for the RadioLib Module class, that adds mutex for SPI bus access
*/
class LockingModule : public Module
{
public:
/*!
\brief Extended SPI-based module constructor.
\param cs Arduino pin to be used as chip select.
\param irq Arduino pin to be used as interrupt/GPIO.
\param rst Arduino pin to be used as hardware reset for the module.
\param gpio Arduino pin to be used as additional interrupt/GPIO.
\param spi SPI interface to be used, can also use software SPI implementations.
\param spiSettings SPI interface settings.
*/
LockingModule(RADIOLIB_PIN_TYPE cs, RADIOLIB_PIN_TYPE irq, RADIOLIB_PIN_TYPE rst, RADIOLIB_PIN_TYPE gpio, SPIClass &spi,
SPISettings spiSettings)
: Module(cs, irq, rst, gpio, spi, spiSettings)
{
}
/*!
\brief SPI single transfer method.
\param cmd SPI access command (read/write/burst/...).
\param reg Address of SPI register to transfer to/from.
\param dataOut Data that will be transfered from master to slave.
\param dataIn Data that was transfered from slave to master.
\param numBytes Number of bytes to transfer.
*/
virtual void SPItransfer(uint8_t cmd, uint8_t reg, uint8_t *dataOut, uint8_t *dataIn, uint8_t numBytes);
};
class RadioLibInterface : public RadioInterface, private concurrency::PeriodicTask
{
/// Used as our notification from the ISR
@@ -49,7 +92,7 @@ class RadioLibInterface : public RadioInterface, private concurrency::PeriodicTa
float currentLimit = 100; // FIXME
uint16_t preambleLength = 32; // 8 is default, but FIXME use longer to increase the amount of sleep time when receiving
Module module; // The HW interface to the radio
LockingModule module; // The HW interface to the radio
/**
* provides lowest common denominator RadioLib API
@@ -94,6 +137,11 @@ class RadioLibInterface : public RadioInterface, private concurrency::PeriodicTa
*/
virtual void startReceive() = 0;
/** are we actively receiving a packet (only called during receiving state)
* This method is only public to facilitate debugging. Do not call.
*/
virtual bool isActivelyReceiving() = 0;
private:
/** if we have something waiting to send, start a short random timer so we can come check for collision before actually doing
* the transmit
@@ -133,9 +181,6 @@ class RadioLibInterface : public RadioInterface, private concurrency::PeriodicTa
/** Could we send right now (i.e. either not actively receiving or transmitting)? */
virtual bool canSendImmediately();
/** are we actively receiving a packet (only called during receiving state) */
virtual bool isActivelyReceiving() = 0;
/**
* Raw ISR handler that just calls our polymorphic method
*/

View File

@@ -2,7 +2,6 @@
#include "MeshTypes.h"
#include "configuration.h"
#include "mesh-pb-constants.h"
#include "timing.h"
// ReliableRouter::ReliableRouter() {}
@@ -163,7 +162,7 @@ PendingPacket *ReliableRouter::startRetransmission(MeshPacket *p)
*/
void ReliableRouter::doRetransmissions()
{
uint32_t now = timing::millis();
uint32_t now = millis();
// FIXME, we should use a better datastructure rather than walking through this map.
// for(auto el: pending) {

View File

@@ -2,7 +2,6 @@
#include "FloodingRouter.h"
#include "../concurrency/PeriodicTask.h"
#include "../timing.h"
#include <unordered_map>
/**
@@ -49,7 +48,7 @@ struct PendingPacket {
PendingPacket() {}
PendingPacket(MeshPacket *p);
void setNextTx() { nextTxMsec = timing::millis() + random(20 * 1000L, 22 * 1000L); }
void setNextTx() { nextTxMsec = millis() + random(20 * 1000L, 22 * 1000L); }
};
class GlobalPacketIdHashFunction

View File

@@ -1,6 +1,6 @@
#include "Router.h"
#include "CryptoEngine.h"
#include "GPS.h"
#include "RTC.h"
#include "configuration.h"
#include "mesh-pb-constants.h"
#include <NodeDB.h>

View File

@@ -37,8 +37,15 @@ bool SX1262Interface::init()
bool useRegulatorLDO = false; // Seems to depend on the connection to pin 9/DCC_SW - if an inductor DCDC?
applyModemConfig();
if (power == 0)
power = 22;
if (power > 22) // This chip has lower power limits than some
power = 22;
limitPower();
int res = lora.begin(freq, bw, sf, cr, syncWord, power, currentLimit, preambleLength, tcxoVoltage, useRegulatorLDO);
DEBUG_MSG("SX1262 init result %d\n", res);
@@ -74,6 +81,10 @@ bool SX1262Interface::reconfigure()
err = lora.setCodingRate(cr);
assert(err == ERR_NONE);
// Hmm - seems to lower SNR when the signal levels are high. Leaving off for now...
// err = lora.setRxGain(true);
// assert(err == ERR_NONE);
err = lora.setSyncWord(syncWord);
assert(err == ERR_NONE);
@@ -123,6 +134,7 @@ void SX1262Interface::setStandby()
*/
void SX1262Interface::addReceiveMetadata(MeshPacket *mp)
{
// DEBUG_MSG("PacketStatus %x\n", lora.getPacketStatus());
mp->rx_snr = lora.getSNR();
}
@@ -167,9 +179,19 @@ void SX1262Interface::startReceive()
/** Could we send right now (i.e. either not actively receving or transmitting)? */
bool SX1262Interface::isActivelyReceiving()
{
// return false; // FIXME
// FIXME this is not correct? - often always true - need to add an extra conditional
return lora.getPacketLength() > 0;
// The IRQ status will be cleared when we start our read operation. Check if we've started a header, but haven't yet
// received and handled the interrupt for reading the packet/handling errors.
// FIXME: it would be better to check for preamble, but we currently have our ISR not set to fire for packets that
// never even get a valid header, so we don't want preamble to get set and stay set due to noise on the network.
uint16_t irq = lora.getIrqStatus();
bool hasPreamble = (irq & SX126X_IRQ_HEADER_VALID);
// this is not correct - often always true - need to add an extra conditional
// size_t bytesPending = lora.getPacketLength();
// if (hasPreamble) DEBUG_MSG("rx hasPreamble\n");
return hasPreamble;
}
bool SX1262Interface::sleep()

View File

@@ -53,7 +53,7 @@ template <class T> class TypedQueue
public:
TypedQueue(int maxElements) {}
// int numFree() { return uxQueueSpacesAvailable(h); }
int numFree() { return 1; } // Always claim 1 free, because we can grow to any size
bool isEmpty() { return q.empty(); }

View File

@@ -6,10 +6,10 @@
#include <pb_decode.h>
#include <pb_encode.h>
#ifdef NO_ESP32
#ifdef ARDUINO_ARCH_NRF52
#include "Adafruit_LittleFS.h"
using namespace Adafruit_LittleFS_Namespace; // To get File type
#endif
#endif
/// helper function for encoding a record as a protobuf, any failures to encode are fatal and we will panic
/// returns the encoded packet size
@@ -49,7 +49,7 @@ bool readcb(pb_istream_t *stream, uint8_t *buf, size_t count)
return count == 0;
}
status = (file->read(buf, count) == (int) count);
status = (file->read(buf, count) == (int)count);
if (file->available() == 0)
stream->bytes_left = 0;

View File

@@ -27,7 +27,7 @@ PB_BIND(MeshPacket, MeshPacket, 2)
PB_BIND(ChannelSettings, ChannelSettings, AUTO)
PB_BIND(RadioConfig, RadioConfig, AUTO)
PB_BIND(RadioConfig, RadioConfig, 2)
PB_BIND(RadioConfig_UserPreferences, RadioConfig_UserPreferences, 2)
@@ -59,3 +59,6 @@ PB_BIND(ManufacturingData, ManufacturingData, AUTO)

View File

@@ -25,6 +25,31 @@ typedef enum _Constants {
Constants_Unused = 0
} Constants;
typedef enum _RegionCode {
RegionCode_Unset = 0,
RegionCode_US = 1,
RegionCode_EU433 = 2,
RegionCode_EU865 = 3,
RegionCode_CN = 4,
RegionCode_JP = 5,
RegionCode_ANZ = 6,
RegionCode_KR = 7,
RegionCode_TW = 8
} RegionCode;
typedef enum _GpsOperation {
GpsOperation_GpsOpUnset = 0,
GpsOperation_GpsOpMobile = 2,
GpsOperation_GpsOpTimeOnly = 3,
GpsOperation_GpsOpDisabled = 4
} GpsOperation;
typedef enum _LocationSharing {
LocationSharing_LocUnset = 0,
LocationSharing_LocEnabled = 1,
LocationSharing_LocDisabled = 2
} LocationSharing;
typedef enum _Data_Type {
Data_Type_OPAQUE = 0,
Data_Type_CLEAR_TEXT = 1,
@@ -108,6 +133,14 @@ typedef struct _RadioConfig_UserPreferences {
char wifi_ssid[33];
char wifi_password[64];
bool wifi_ap_mode;
RegionCode region;
LocationSharing location_share;
GpsOperation gps_operation;
uint32_t gps_update_interval;
uint32_t gps_attempt_time;
bool is_router;
bool is_low_power;
bool factory_reset;
pb_size_t ignore_incoming_count;
uint32_t ignore_incoming[3];
} RadioConfig_UserPreferences;
@@ -230,6 +263,18 @@ typedef struct _ToRadio {
#define _Constants_MAX Constants_Unused
#define _Constants_ARRAYSIZE ((Constants)(Constants_Unused+1))
#define _RegionCode_MIN RegionCode_Unset
#define _RegionCode_MAX RegionCode_TW
#define _RegionCode_ARRAYSIZE ((RegionCode)(RegionCode_TW+1))
#define _GpsOperation_MIN GpsOperation_GpsOpUnset
#define _GpsOperation_MAX GpsOperation_GpsOpDisabled
#define _GpsOperation_ARRAYSIZE ((GpsOperation)(GpsOperation_GpsOpDisabled+1))
#define _LocationSharing_MIN LocationSharing_LocUnset
#define _LocationSharing_MAX LocationSharing_LocDisabled
#define _LocationSharing_ARRAYSIZE ((LocationSharing)(LocationSharing_LocDisabled+1))
#define _Data_Type_MIN Data_Type_OPAQUE
#define _Data_Type_MAX Data_Type_CLEAR_READACK
#define _Data_Type_ARRAYSIZE ((Data_Type)(Data_Type_CLEAR_READACK+1))
@@ -248,7 +293,7 @@ typedef struct _ToRadio {
#define MeshPacket_init_default {0, 0, 0, {SubPacket_init_default}, 0, 0, 0, 0, 0}
#define ChannelSettings_init_default {0, _ChannelSettings_ModemConfig_MIN, {0, {0}}, "", 0, 0, 0, 0}
#define RadioConfig_init_default {false, RadioConfig_UserPreferences_init_default, false, ChannelSettings_init_default}
#define RadioConfig_UserPreferences_init_default {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, "", "", 0, 0, {0, 0, 0}}
#define RadioConfig_UserPreferences_init_default {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, "", "", 0, _RegionCode_MIN, _LocationSharing_MIN, _GpsOperation_MIN, 0, 0, 0, 0, 0, 0, {0, 0, 0}}
#define NodeInfo_init_default {0, false, User_init_default, false, Position_init_default, 0, 0}
#define MyNodeInfo_init_default {0, 0, 0, "", "", "", 0, 0, 0, 0, 0, 0, 0, 0}
#define DeviceState_init_default {false, RadioConfig_init_default, false, MyNodeInfo_init_default, false, User_init_default, 0, {NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default}, 0, {MeshPacket_init_default}, false, MeshPacket_init_default, 0, 0, 0}
@@ -264,7 +309,7 @@ typedef struct _ToRadio {
#define MeshPacket_init_zero {0, 0, 0, {SubPacket_init_zero}, 0, 0, 0, 0, 0}
#define ChannelSettings_init_zero {0, _ChannelSettings_ModemConfig_MIN, {0, {0}}, "", 0, 0, 0, 0}
#define RadioConfig_init_zero {false, RadioConfig_UserPreferences_init_zero, false, ChannelSettings_init_zero}
#define RadioConfig_UserPreferences_init_zero {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, "", "", 0, 0, {0, 0, 0}}
#define RadioConfig_UserPreferences_init_zero {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, "", "", 0, _RegionCode_MIN, _LocationSharing_MIN, _GpsOperation_MIN, 0, 0, 0, 0, 0, 0, {0, 0, 0}}
#define NodeInfo_init_zero {0, false, User_init_zero, false, Position_init_zero, 0, 0}
#define MyNodeInfo_init_zero {0, 0, 0, "", "", "", 0, 0, 0, 0, 0, 0, 0, 0}
#define DeviceState_init_zero {false, RadioConfig_init_zero, false, MyNodeInfo_init_zero, false, User_init_zero, 0, {NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero}, 0, {MeshPacket_init_zero}, false, MeshPacket_init_zero, 0, 0, 0}
@@ -322,7 +367,15 @@ typedef struct _ToRadio {
#define RadioConfig_UserPreferences_wifi_ssid_tag 12
#define RadioConfig_UserPreferences_wifi_password_tag 13
#define RadioConfig_UserPreferences_wifi_ap_mode_tag 14
#define RadioConfig_UserPreferences_ignore_incoming_tag 102
#define RadioConfig_UserPreferences_region_tag 15
#define RadioConfig_UserPreferences_is_router_tag 37
#define RadioConfig_UserPreferences_is_low_power_tag 38
#define RadioConfig_UserPreferences_factory_reset_tag 100
#define RadioConfig_UserPreferences_location_share_tag 32
#define RadioConfig_UserPreferences_gps_operation_tag 33
#define RadioConfig_UserPreferences_gps_update_interval_tag 34
#define RadioConfig_UserPreferences_gps_attempt_time_tag 36
#define RadioConfig_UserPreferences_ignore_incoming_tag 103
#define RouteDiscovery_route_tag 2
#define User_id_tag 1
#define User_long_name_tag 2
@@ -364,7 +417,7 @@ typedef struct _ToRadio {
#define DeviceState_version_tag 8
#define DeviceState_rx_text_message_tag 7
#define DeviceState_no_save_tag 9
#define DeviceState_did_gps_reset_tag 10
#define DeviceState_did_gps_reset_tag 11
#define FromRadio_packet_tag 2
#define FromRadio_my_info_tag 3
#define FromRadio_node_info_tag 4
@@ -477,7 +530,15 @@ X(a, STATIC, SINGULAR, UINT32, min_wake_secs, 11) \
X(a, STATIC, SINGULAR, STRING, wifi_ssid, 12) \
X(a, STATIC, SINGULAR, STRING, wifi_password, 13) \
X(a, STATIC, SINGULAR, BOOL, wifi_ap_mode, 14) \
X(a, STATIC, REPEATED, UINT32, ignore_incoming, 102)
X(a, STATIC, SINGULAR, UENUM, region, 15) \
X(a, STATIC, SINGULAR, UENUM, location_share, 32) \
X(a, STATIC, SINGULAR, UENUM, gps_operation, 33) \
X(a, STATIC, SINGULAR, UINT32, gps_update_interval, 34) \
X(a, STATIC, SINGULAR, UINT32, gps_attempt_time, 36) \
X(a, STATIC, SINGULAR, BOOL, is_router, 37) \
X(a, STATIC, SINGULAR, BOOL, is_low_power, 38) \
X(a, STATIC, SINGULAR, BOOL, factory_reset, 100) \
X(a, STATIC, REPEATED, UINT32, ignore_incoming, 103)
#define RadioConfig_UserPreferences_CALLBACK NULL
#define RadioConfig_UserPreferences_DEFAULT NULL
@@ -519,7 +580,7 @@ X(a, STATIC, REPEATED, MESSAGE, receive_queue, 5) \
X(a, STATIC, OPTIONAL, MESSAGE, rx_text_message, 7) \
X(a, STATIC, SINGULAR, UINT32, version, 8) \
X(a, STATIC, SINGULAR, BOOL, no_save, 9) \
X(a, STATIC, SINGULAR, BOOL, did_gps_reset, 10)
X(a, STATIC, SINGULAR, BOOL, did_gps_reset, 11)
#define DeviceState_CALLBACK NULL
#define DeviceState_DEFAULT NULL
#define DeviceState_radio_MSGTYPE RadioConfig
@@ -613,11 +674,11 @@ extern const pb_msgdesc_t ManufacturingData_msg;
#define SubPacket_size 274
#define MeshPacket_size 313
#define ChannelSettings_size 84
#define RadioConfig_size 277
#define RadioConfig_UserPreferences_size 188
#define RadioConfig_size 308
#define RadioConfig_UserPreferences_size 219
#define NodeInfo_size 132
#define MyNodeInfo_size 110
#define DeviceState_size 5429
#define DeviceState_size 5460
#define DebugString_size 258
#define FromRadio_size 322
#define ToRadio_size 316

793
src/meshwifi/meshhttp.cpp Normal file
View File

@@ -0,0 +1,793 @@
#include "meshwifi/meshhttp.h"
#include "NodeDB.h"
#include "configuration.h"
#include "main.h"
#include "meshwifi/meshwifi.h"
#include <WebServer.h>
#include <WiFi.h>
WebServer webserver(80);
// Maximum number of messages for chat history. Don't make this too big -- it'll use a
// lot of memory!
const uint16_t maxMessages = 50;
struct message_t {
char sender[10];
char message[250];
int32_t gpsLat;
int32_t gpsLong;
uint32_t time;
bool fromMe;
};
struct messages_t {
message_t history[maxMessages];
};
messages_t messages_history;
String something = "";
String sender = "";
void handleWebResponse()
{
if (isWifiAvailable() == 0) {
return;
}
// We're going to handle the DNS responder here so it
// will be ignored by the NRF boards.
handleDNSResponse();
webserver.handleClient();
}
void initWebServer()
{
webserver.onNotFound(handleNotFound);
webserver.on("/json/chat/send/channel", handleJSONChatHistory);
webserver.on("/json/chat/send/user", handleJSONChatHistory);
webserver.on("/json/chat/history/channel", handleJSONChatHistory);
webserver.on("/json/chat/history/dummy", handleJSONChatHistoryDummy);
webserver.on("/json/chat/history/user", handleJSONChatHistory);
webserver.on("/json/stats", handleJSONChatHistory);
webserver.on("/hotspot-detect.html", handleHotspot);
webserver.on("/css/style.css", handleStyleCSS);
webserver.on("/scripts/script.js", handleScriptsScriptJS);
webserver.on("/", handleRoot);
webserver.begin();
}
void handleJSONChatHistory()
{
int i;
String out = "";
out += "{\n";
out += " \"data\" : {\n";
out += " \"chat\" : ";
out += "[";
out += "\"" + sender + "\"";
out += ",";
out += "\"" + something + "\"";
out += "]\n";
for (i = 0; i < maxMessages; i++) {
out += "[";
out += "\"" + String(messages_history.history[i].sender) + "\"";
out += ",";
out += "\"" + String(messages_history.history[i].message) + "\"";
out += "]\n";
}
out += "\n";
out += " }\n";
out += "}\n";
webserver.send(200, "application/json", out);
return;
}
void handleNotFound()
{
String message = "";
message += "File Not Found\n\n";
message += "URI: ";
message += webserver.uri();
message += "\nMethod: ";
message += (webserver.method() == HTTP_GET) ? "GET" : "POST";
message += "\nArguments: ";
message += webserver.args();
message += "\n";
for (uint8_t i = 0; i < webserver.args(); i++) {
message += " " + webserver.argName(i) + ": " + webserver.arg(i) + "\n";
}
Serial.println(message);
webserver.send(404, "text/plain", message);
}
/*
This supports the Apple Captive Network Assistant (CNA) Portal
*/
void handleHotspot()
{
DEBUG_MSG("Hotspot Request\n");
/*
If we don't do a redirect, be sure to return a "Success" message
otherwise iOS will have trouble detecting that the connection to the SoftAP worked.
*/
String out = "";
// out += "Success\n";
out += "<meta http-equiv=\"refresh\" content=\"0;url=http://meshtastic.org/\" />\n";
webserver.send(200, "text/html", out);
return;
}
void notifyWebUI()
{
DEBUG_MSG("************ Got a message! ************\n");
MeshPacket &mp = devicestate.rx_text_message;
NodeInfo *node = nodeDB.getNode(mp.from);
sender = (node && node->has_user) ? node->user.long_name : "???";
static char tempBuf[256]; // mesh.options says this is MeshPacket.encrypted max_size
assert(mp.decoded.which_payload == SubPacket_data_tag);
snprintf(tempBuf, sizeof(tempBuf), "%s", mp.decoded.data.payload.bytes);
something = tempBuf;
}
/*
To convert text to c strings:
https://tomeko.net/online_tools/cpp_text_escape.php?lang=en
*/
void handleRoot()
{
String out = "";
out +=
"<!DOCTYPE html>\n"
"<html lang=\"en\" >\n"
"<!-- Updated 20200923 - Change JSON input -->\n"
"<!-- Updated 20200924 - Replace FontAwesome with SVG -->\n"
"<head>\n"
" <meta charset=\"UTF-8\">\n"
" <title>Meshtastic - Chat</title>\n"
" <link rel=\"stylesheet\" href=\"css/style.css\">\n"
"\n"
"</head>\n"
"<body>\n"
"<center><h1>This area is under development. Please don't file bugs.</h1></center><!-- Add SVG for Symbols -->\n"
"<svg aria-hidden=\"true\" style=\"position: absolute; width: 0; height: 0; overflow: hidden;\" version=\"1.1\" "
"xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\">\n"
"<defs>\n"
"<symbol id=\"icon-map-marker\" viewBox=\"0 0 16 28\">\n"
"<path d=\"M12 10c0-2.203-1.797-4-4-4s-4 1.797-4 4 1.797 4 4 4 4-1.797 4-4zM16 10c0 0.953-0.109 1.937-0.516 2.797l-5.688 "
"12.094c-0.328 0.688-1.047 1.109-1.797 1.109s-1.469-0.422-1.781-1.109l-5.703-12.094c-0.406-0.859-0.516-1.844-0.516-2.797 "
"0-4.422 3.578-8 8-8s8 3.578 8 8z\"></path>\n"
"</symbol>\n"
"<symbol id=\"icon-circle\" viewBox=\"0 0 24 28\">\n"
"<path d=\"M24 14c0 6.625-5.375 12-12 12s-12-5.375-12-12 5.375-12 12-12 12 5.375 12 12z\"></path>\n"
"</symbol>\n"
"</defs>\n"
"</svg>\n"
"<div class=\"grid\">\n"
"\t<div class=\"top\">\n"
"\t\t<div class=\"top-text\">Meshtastic - Chat</div>\n"
"\t</div>\n"
"\n"
"\t<div class=\"side clearfix\">\n"
" <div class=\"channel-list\" id=\"channel-list\">\n"
"\t <div class=\"side-header\">\n"
"\t\t<div class=\"side-text\">Users</div>\n"
"\t </div>\n"
" <ul class=\"list\" id='userlist-id'>\n"
" </ul>\n"
" </div>\n"
" </div>\n"
" <div class=\"content\">\n"
" <div class=\"content-header clearfix\">\n"
"<!-- <div class=\"content-about\"> -->\n"
" <div class=\"content-from\">\n"
"\t\t <span class=\"content-from-highlight\" id=\"content-from-id\">All Users</span>\n"
"\t\t </div>\n"
"<!-- </div> -->\n"
" </div> <!-- end content-header -->\n"
" \n"
" <div class=\"content-history\" id='chat-div-id'>\n"
" <ul id='chat-history-id'>\n"
"\t\t</ul>\n"
" \n"
" </div> <!-- end content-history -->\n"
" \n"
" <div class=\"content-message clearfix\">\n"
" <textarea name=\"message-to-send\" id=\"message-to-send\" placeholder =\"Type your message\" "
"rows=\"3\"></textarea>\n"
" \n"
" \n"
" <button>Send</button>\n"
"\n"
" </div> <!-- end content-message -->\n"
" \n"
" </div> <!-- end content -->\n"
" \n"
" </div> <!-- end container -->\n"
"\n"
"<script src=\"/scripts/script.js\"></script>\n"
"\n"
"</body>\n"
"</html>\n"
"";
webserver.send(200, "text/html", out);
return;
}
void handleStyleCSS()
{
String out = "";
out +=
"/* latin-ext */\n"
"@font-face {\n"
" font-family: 'Lato';\n"
" font-style: normal;\n"
" font-weight: 400;\n"
" src: local('Lato Regular'), local('Lato-Regular'), url(./Google.woff2) format('woff2');\n"
" unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF;\n"
"}\n"
"\n"
"\n"
"*, *:before, *:after {\n"
" box-sizing: border-box;\n"
"}\n"
"\n"
"body {\n"
" background: #C5DDEB;\n"
" font: 14px/20px \"Lato\", Arial, sans-serif;\n"
" padding: 40px 0;\n"
" color: white;\n"
"}\n"
"\n"
"\n"
" \n"
".grid {\n"
" display: grid;\n"
" grid-template-columns:\n"
"\t1fr 4fr;\n"
" grid-template-areas:\n"
"\t\"header header\"\n"
"\t\"sidebar content\";\n"
" margin: 0 auto;\n"
" width: 750px;\n"
" background: #444753;\n"
" border-radius: 5px;\n"
"}\n"
"\n"
".top {grid-area: header;}\n"
".side {grid-area: sidebar;}\n"
".main {grid-area: content;}\n"
"\n"
".top {\n"
" border-bottom: 2px solid white;\n"
"}\n"
".top-text {\n"
" font-weight: bold;\n"
" font-size: 24px;\n"
" text-align: center;\n"
" padding: 20px;\n"
"}\n"
"\n"
".side {\n"
" width: 260px;\n"
" float: left;\n"
"}\n"
".side .side-header {\n"
" padding: 20px;\n"
" border-bottom: 2px solid white;\n"
"}\n"
"\n"
".side .side-header .side-text {\n"
" padding-left: 10px;\n"
" margin-top: 6px;\n"
" font-size: 16px;\n"
" text-align: left;\n"
" font-weight: bold;\n"
" \n"
"}\n"
"\n"
".channel-list ul {\n"
" padding: 20px;\n"
" height: 570px;\n"
" list-style-type: none;\n"
"}\n"
".channel-list ul li {\n"
" padding-bottom: 20px;\n"
"}\n"
"\n"
".channel-list .channel-name {\n"
" font-size: 20px;\n"
" margin-top: 8px;\n"
" padding-left: 8px;\n"
"}\n"
"\n"
".channel-list .message-count {\n"
" padding-left: 16px;\n"
" color: #92959E;\n"
"}\n"
"\n"
".icon {\n"
" display: inline-block;\n"
" width: 1em;\n"
" height: 1em;\n"
" stroke-width: 0;\n"
" stroke: currentColor;\n"
" fill: currentColor;\n"
"}\n"
"\n"
".icon-map-marker {\n"
" width: 0.5714285714285714em;\n"
"}\n"
"\n"
".icon-circle {\n"
" width: 0.8571428571428571em;\n"
"}\n"
"\n"
".content {\n"
" display: flex;\n"
" flex-direction: column;\n"
" flex-wrap: nowrap;\n"
"/* width: 490px; */\n"
" float: left;\n"
" background: #F2F5F8;\n"
"/* border-top-right-radius: 5px;\n"
" border-bottom-right-radius: 5px; */\n"
" color: #434651;\n"
"}\n"
".content .content-header {\n"
" flex-grow: 0;\n"
" padding: 20px;\n"
" border-bottom: 2px solid white;\n"
"}\n"
"\n"
".content .content-header .content-from {\n"
" padding-left: 10px;\n"
" margin-top: 6px;\n"
" font-size: 20px;\n"
" text-align: center;\n"
" font-size: 16px;\n"
"}\n"
".content .content-header .content-from .content-from-highlight {\n"
" font-weight: bold;\n"
"}\n"
".content .content-header .content-num-messages {\n"
" color: #92959E;\n"
"}\n"
"\n"
".content .content-history {\n"
" flex-grow: 1;\n"
" padding: 20px 20px 20px;\n"
" border-bottom: 2px solid white;\n"
" overflow-y: scroll;\n"
" height: 375px;\n"
"}\n"
".content .content-history ul {\n"
" list-style-type: none;\n"
" padding-inline-start: 10px;\n"
"}\n"
".content .content-history .message-data {\n"
" margin-bottom: 10px;\n"
"}\n"
".content .content-history .message-data-time {\n"
" color: #a8aab1;\n"
" padding-left: 6px;\n"
"}\n"
".content .content-history .message {\n"
" color: white;\n"
" padding: 8px 10px;\n"
" line-height: 20px;\n"
" font-size: 14px;\n"
" border-radius: 7px;\n"
" margin-bottom: 30px;\n"
" width: 90%;\n"
" position: relative;\n"
"}\n"
".content .content-history .message:after {\n"
" bottom: 100%;\n"
" left: 7%;\n"
" border: solid transparent;\n"
" content: \" \";\n"
" height: 0;\n"
" width: 0;\n"
" position: absolute;\n"
" pointer-events: none;\n"
" border-bottom-color: #86BB71;\n"
" border-width: 10px;\n"
" margin-left: -10px;\n"
"}\n"
".content .content-history .my-message {\n"
" background: #86BB71;\n"
"}\n"
".content .content-history .other-message {\n"
" background: #94C2ED;\n"
"}\n"
".content .content-history .other-message:after {\n"
" border-bottom-color: #94C2ED;\n"
" left: 93%;\n"
"}\n"
".content .content-message {\n"
" flex-grow: 0;\n"
" padding: 10px;\n"
"}\n"
".content .content-message textarea {\n"
" width: 100%;\n"
" border: none;\n"
" padding: 10px 10px;\n"
" font: 14px/22px \"Lato\", Arial, sans-serif;\n"
" margin-bottom: 10px;\n"
" border-radius: 5px;\n"
" resize: none;\n"
"}\n"
"\n"
".content .content-message button {\n"
" float: right;\n"
" color: #94C2ED;\n"
" font-size: 16px;\n"
" text-transform: uppercase;\n"
" border: none;\n"
" cursor: pointer;\n"
" font-weight: bold;\n"
" background: #F2F5F8;\n"
"}\n"
".content .content-message button:hover {\n"
" color: #75b1e8;\n"
"}\n"
"/* Tooltip container */\n"
".tooltip {\n"
" color: #86BB71;\n"
" position: relative;\n"
" display: inline-block;\n"
" border-bottom: 1px dotted black; /* If you want dots under the hoverable text */\n"
"}\n"
"/* Tooltip text */\n"
".tooltip .tooltiptext {\n"
" visibility: hidden;\n"
" width: 120px;\n"
" background-color: #444753;\n"
" color: #fff;\n"
" text-align: center;\n"
" padding: 5px 0;\n"
" border-radius: 6px;\n"
" /* Position the tooltip text - see examples below! */\n"
" position: absolute;\n"
" z-index: 1;\n"
"}\n"
"\n"
"/* Show the tooltip text when you mouse over the tooltip container */\n"
".tooltip:hover .tooltiptext {\n"
" visibility: visible;\n"
"}\n"
"\n"
".online, .offline, .me {\n"
" margin-right: 3px;\n"
" font-size: 10px;\n"
"}\n"
"\n"
".online {\n"
" color: #86BB71;\n"
"}\n"
"\n"
".offline {\n"
" color: #E38968;\n"
"}\n"
"\n"
".me {\n"
" color: #94C2ED;\n"
"}\n"
"\n"
".align-left {\n"
" text-align: left;\n"
"}\n"
"\n"
".align-right {\n"
" text-align: right;\n"
"}\n"
"\n"
".float-right {\n"
" float: right;\n"
"}\n"
"\n"
".clearfix:after {\n"
" visibility: hidden;\n"
" display: block;\n"
" font-size: 0;\n"
" content: \" \";\n"
" clear: both;\n"
" height: 0;\n"
"}";
webserver.send(200, "text/css", out);
return;
}
void handleScriptsScriptJS()
{
String out = "";
out += "String.prototype.toHHMMSS = function () {\n"
" var sec_num = parseInt(this, 10); // don't forget the second param\n"
" var hours = Math.floor(sec_num / 3600);\n"
" var minutes = Math.floor((sec_num - (hours * 3600)) / 60);\n"
" var seconds = sec_num - (hours * 3600) - (minutes * 60);\n"
"\n"
" if (hours < 10) {hours = \"0\"+hours;}\n"
" if (minutes < 10) {minutes = \"0\"+minutes;}\n"
" if (seconds < 10) {seconds = \"0\"+seconds;}\n"
"// return hours+':'+minutes+':'+seconds;\n"
"\treturn hours+'h'+minutes+'m';\n"
"}\n"
"String.prototype.padLeft = function (length, character) { \n"
" return new Array(length - this.length + 1).join(character || ' ') + this; \n"
"};\n"
"\n"
"Date.prototype.toFormattedString = function () {\n"
" return [String(this.getFullYear()).substr(2, 2),\n"
"\t\t\tString(this.getMonth()+1).padLeft(2, '0'),\n"
" String(this.getDate()).padLeft(2, '0')].join(\"/\") + \" \" +\n"
" [String(this.getHours()).padLeft(2, '0'),\n"
" String(this.getMinutes()).padLeft(2, '0')].join(\":\");\n"
"};\n"
"\n"
"function getData(file) {\n"
"\tfetch(file)\n"
"\t.then(function (response) {\n"
"\t\treturn response.json();\n"
"\t})\n"
"\t.then(function (datafile) {\n"
"\t\tupdateData(datafile);\n"
"\t})\n"
"\t.catch(function (err) {\n"
"\t\tconsole.log('error: ' + err);\n"
"\t});\n"
"}\n"
"\t\n"
"function updateData(datafile) {\n"
"// Update System Details\n"
"\tupdateSystem(datafile);\n"
"//\tUpdate Userlist and message count\n"
"\tupdateUsers(datafile);\n"
"// Update Chat\n"
"\tupdateChat(datafile);\n"
"}\n"
"\n"
"function updateSystem(datafile) {\n"
"// Update System Info \n"
"\tvar sysContainer = document.getElementById(\"content-from-id\");\n"
"\tvar newHTML = datafile.data.system.channel;\n"
"\tvar myDate = new Date( datafile.data.system.timeGPS *1000);\n"
"\tnewHTML += ' @' + myDate.toFormattedString();\n"
"\tvar newSec = datafile.data.system.timeSinceStart;\n"
"\tvar strsecondUp = newSec.toString();\n"
"\tnewHTML += ' Up:' + strsecondUp.toHHMMSS();\n"
"\tsysContainer.innerHTML = newHTML;\n"
"}\n"
"\n"
"function updateUsers(datafile) {\n"
"\tvar mainContainer = document.getElementById(\"userlist-id\");\n"
"\tvar htmlUsers = '';\n"
"\tvar timeBase = datafile.data.system.timeSinceStart;\n"
"//\tvar lookup = {};\n"
" for (var i = 0; i < datafile.data.users.length; i++) {\n"
" htmlUsers += formatUsers(datafile.data.users[i],timeBase);\n"
"\t}\n"
"\tmainContainer.innerHTML = htmlUsers;\n"
"}\n"
"\n"
"function formatUsers(user,timeBase) {\n"
"\tnewHTML = '<li class=\"clearfix\">';\n"
" newHTML += '<div class=\"channel-name clearfix\">' + user.NameLong + '(' + user.NameShort + ')</div>';\n"
" newHTML += '<div class=\"message-count clearfix\">';\n"
"\tvar secondsLS = timeBase - user.lastSeen;\n"
"\tvar strsecondsLS = secondsLS.toString();\n"
"\tnewHTML += '<svg class=\"icon icon-circle '+onlineStatus(secondsLS)+'\"><use "
"xlink:href=\"#icon-circle\"></use></svg></i>Seen: '+strsecondsLS.toHHMMSS()+' ago&nbsp;';\n"
"\tif (user.lat == 0 || user.lon == 0) {\n"
"\t\tnewHTML += '';\n"
"\t} else {\n"
"\t\tnewHTML += '<div class=\"tooltip\"><svg class=\"icon icon-map-marker\"><use "
"xlink:href=\"#icon-map-marker\"></use></svg><span class=\"tooltiptext\">lat:' + user.lat + ' lon:'+ user.lon+ "
"'</span>';\n"
"\t}\n"
" newHTML += '</div></div>';\n"
" newHTML += '</li>';\n"
"\treturn(newHTML);\n"
"}\n"
"\n"
"function onlineStatus(time) {\n"
"\tif (time < 3600) {\n"
"\t\treturn \"online\"\n"
"\t} else {\n"
"\t\treturn \"offline\"\n"
"\t}\n"
"}\n"
"\n"
"function updateChat(datafile) {\n"
"// Update Chat\n"
"\tvar chatContainer = document.getElementById(\"chat-history-id\");\n"
"\tvar htmlChat = '';\n"
"\tvar timeBase = datafile.data.system.timeSinceStart;\n"
"\tfor (var i = 0; i < datafile.data.chat.length; i++) {\n"
"\t\thtmlChat += formatChat(datafile.data.chat[i],timeBase);\n"
"\t}\n"
"\tchatContainer.innerHTML = htmlChat;\n"
"\tscrollHistory();\n"
"}\n"
"\n"
"function formatChat(data,timeBase) {\n"
"\tvar secondsTS = timeBase - data.timestamp;\n"
"\tvar strsecondsTS = secondsTS.toString();\n"
"\tnewHTML = '<li class=\"clearfix\">';\n"
"\tif (data.local == 1) {\n"
"\t\tnewHTML += '<div class=\"message-data\">';\n"
"\t\tnewHTML += '<span class=\"message-data-name\" >' + data.NameLong + '(' + data.NameShort + ')</span>';\n"
"\t\tnewHTML += '<span class=\"message-data-time\" >' + strsecondsTS.toHHMMSS() + ' ago</span>';\n"
"\t\tnewHTML += '</div>';\n"
"\t\tnewHTML += '<div class=\"message my-message\">' + data.chatLine + '</div>';\n"
"\t} else {\n"
"\t\tnewHTML += '<div class=\"message-data align-right\">';\n"
"\t\tnewHTML += '<span class=\"message-data-time\" >' + strsecondsTS.toHHMMSS() + ' ago</span> &nbsp; &nbsp;';\n"
"\t\tnewHTML += '<span class=\"message-data-name\" >' + data.NameLong + '(' + data.NameShort + ')</span>';\n"
"//\t\tnewHTML += '<i class=\"fa fa-circle online\"></i>';\n"
"\t\tnewHTML += '</div>';\n"
"\t\tnewHTML += '<div class=\"message other-message float-right\">' + data.chatLine + '</div>';\n"
"\t}\n"
"\n"
" newHTML += '</li>';\n"
"\treturn(newHTML);\t\n"
"}\n"
"\n"
"function scrollHistory() {\n"
"\tvar chatContainer = document.getElementById(\"chat-div-id\");\n"
"\tchatContainer.scrollTop = chatContainer.scrollHeight;\n"
"}\n"
"\n"
"\n"
"getData('/json/chat/history/dummy');\n"
"\n"
"\n"
"//window.onload=function(){\n"
"//\talert('onload');\n"
"// Async - Run scroll 0.5sec after onload event\n"
"//\tsetTimeout(scrollHistory(),500);\n"
"// }";
webserver.send(200, "text/javascript", out);
return;
}
void handleJSONChatHistoryDummy()
{
String out = "";
out += "{\n"
"\t\"data\": {\n"
"\t\t\"system\": {\n"
"\t\t\t\"timeSinceStart\": 3213544,\n"
"\t\t\t\"timeGPS\": 1600830985,\n"
"\t\t\t\"channel\": \"ourSecretPlace\"\n"
"\t\t},\n"
"\t\t\"users\": [{\n"
"\t\t\t\t\"NameShort\": \"J\",\n"
"\t\t\t\t\"NameLong\": \"John\",\n"
"\t\t\t\t\"lastSeen\": 3207544,\n"
"\t\t\t\t\"lat\" : -2.882243,\n"
"\t\t\t\t\"lon\" : -111.038580\n"
"\t\t\t},\n"
"\t\t\t{\n"
"\t\t\t\t\"NameShort\": \"D\",\n"
"\t\t\t\t\"NameLong\": \"David\",\n"
"\t\t\t\t\"lastSeen\": 3212544,\n"
"\t\t\t\t\"lat\" : -12.24452,\n"
"\t\t\t\t\"lon\" : -61.87351\n"
"\t\t\t},\n"
"\t\t\t{\n"
"\t\t\t\t\"NameShort\": \"P\",\n"
"\t\t\t\t\"NameLong\": \"Peter\",\n"
"\t\t\t\t\"lastSeen\": 3213444,\n"
"\t\t\t\t\"lat\" : 0,\n"
"\t\t\t\t\"lon\" : 0\n"
"\t\t\t},\n"
"\t\t\t{\n"
"\t\t\t\t\"NameShort\": \"M\",\n"
"\t\t\t\t\"NameLong\": \"Mary\",\n"
"\t\t\t\t\"lastSeen\": 3211544,\n"
"\t\t\t\t\"lat\" : 16.45478,\n"
"\t\t\t\t\"lon\" : 11.40166\n"
"\t\t\t}\n"
"\t\t],\n"
"\t\t\"chat\": [{\n"
"\t\t\t\t\"local\": 0,\n"
"\t\t\t\t\"NameShort\": \"J\",\n"
"\t\t\t\t\"NameLong\": \"John\",\n"
"\t\t\t\t\"chatLine\": \"Hello\",\n"
"\t\t\t\t\"timestamp\" : 3203544\n"
"\t\t\t},\n"
"\t\t\t{\n"
"\t\t\t\t\"local\": 0,\n"
"\t\t\t\t\"NameShort\": \"D\",\n"
"\t\t\t\t\"NameLong\": \"David\",\n"
"\t\t\t\t\"chatLine\": \"Hello There\",\n"
"\t\t\t\t\"timestamp\" : 3204544\n"
"\t\t\t},\n"
"\t\t\t{\n"
"\t\t\t\t\"local\": 0,\n"
"\t\t\t\t\"NameShort\": \"J\",\n"
"\t\t\t\t\"NameLong\": \"John\",\n"
"\t\t\t\t\"chatLine\": \"Where you been?\",\n"
"\t\t\t\t\"timestamp\" : 3205544\n"
"\t\t\t},\n"
"\t\t\t{\n"
"\t\t\t\t\"local\": 0,\n"
"\t\t\t\t\"NameShort\": \"D\",\n"
"\t\t\t\t\"NameLong\": \"David\",\n"
"\t\t\t\t\"chatLine\": \"I was on Channel 2\",\n"
"\t\t\t\t\"timestamp\" : 3206544\n"
"\t\t\t},\n"
"\t\t\t{\n"
"\t\t\t\t\"local\": 0,\n"
"\t\t\t\t\"NameShort\": \"J\",\n"
"\t\t\t\t\"NameLong\": \"John\",\n"
"\t\t\t\t\"chatLine\": \"With Mary again?\",\n"
"\t\t\t\t\"timestamp\" : 3207544\n"
"\t\t\t},\n"
"\t\t\t{\n"
"\t\t\t\t\"local\": 0,\n"
"\t\t\t\t\"NameShort\": \"D\",\n"
"\t\t\t\t\"NameLong\": \"David\",\n"
"\t\t\t\t\"chatLine\": \"She's better looking than you\",\n"
"\t\t\t\t\"timestamp\" : 3208544\n"
"\t\t\t},\n"
"\t\t\t{\n"
"\t\t\t\t\"local\": 0,\n"
"\t\t\t\t\"NameShort\": \"M\",\n"
"\t\t\t\t\"NameLong\": \"Mary\",\n"
"\t\t\t\t\"chatLine\": \"Well, Hi\",\n"
"\t\t\t\t\"timestamp\" : 3209544\n"
"\t\t\t},\n"
"\t\t\t{\n"
"\t\t\t\t\"local\": 0,\n"
"\t\t\t\t\"NameShort\": \"D\",\n"
"\t\t\t\t\"NameLong\": \"David\",\n"
"\t\t\t\t\"chatLine\": \"You're Here\",\n"
"\t\t\t\t\"timestamp\" : 3210544\n"
"\t\t\t},\n"
"\t\t\t{\n"
"\t\t\t\t\"local\": 0,\n"
"\t\t\t\t\"NameShort\": \"M\",\n"
"\t\t\t\t\"NameLong\": \"Mary\",\n"
"\t\t\t\t\"chatLine\": \"Wanted to say Howdy.\",\n"
"\t\t\t\t\"timestamp\" : 3211544\n"
"\t\t\t},\n"
"\t\t\t{\n"
"\t\t\t\t\"local\": 0,\n"
"\t\t\t\t\"NameShort\": \"D\",\n"
"\t\t\t\t\"NameLong\": \"David\",\n"
"\t\t\t\t\"chatLine\": \"Better come down and visit sometime\",\n"
"\t\t\t\t\"timestamp\" : 3212544\n"
"\t\t\t},\n"
"\t\t\t{\n"
"\t\t\t\t\"local\": 1,\n"
"\t\t\t\t\"NameShort\": \"P\",\n"
"\t\t\t\t\"NameLong\": \"Peter\",\n"
"\t\t\t\t\"chatLine\": \"Where is everybody?\",\n"
"\t\t\t\t\"timestamp\" : 3213444\n"
"\t\t\t}\n"
"\t\t]\n"
"\t}\n"
"}";
webserver.send(200, "application/json", out);
return;
}

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