Compare commits

...

197 Commits
0.9.6 ... 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
geeksville
46ba36511a Merge branch 'portduino' into dev
# Conflicts:
#	platformio.ini
2020-09-07 16:31:17 -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
geeksville
fefd3d78f3 Portduino WIP now compiles but does not link 2020-09-05 12:34:48 -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
105 changed files with 42826 additions and 891 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,7 +25,7 @@ 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://de.aliexpress.com/item/4001287221970.html) (slightly better GPS + LoRa)
- [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"
@@ -103,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.6
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.
@@ -77,13 +78,14 @@ 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 looking cases:
- 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)
- 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.

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

@@ -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 # or if you'd like to change the default to something like lora-relay-v1 put that here
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 # or if you'd like to change the default to something like
[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,25 +59,38 @@ 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#7989a269be590a5d4914ac04069b58f4930c45c1
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
@@ -87,6 +99,10 @@ lib_ignore = segger_rtt
platform_packages =
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
; -DBOARD_HAS_PSRAM
@@ -99,7 +115,7 @@ platform_packages =
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#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,9 +229,23 @@ 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
@@ -233,11 +264,15 @@ build_flags = ${nrf52_base.build_flags} -Ivariants/lora_relay_v1
-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: ce422b7c44...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

@@ -66,7 +66,10 @@ 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;
@@ -119,6 +122,7 @@ void Power::readPowerStatus()
const PowerStatus powerStatus =
PowerStatus(hasBattery ? OptTrue : OptFalse, batteryLevel->isVBUSPlug() ? OptTrue : OptFalse,
batteryLevel->isChargeing() ? OptTrue : OptFalse, batteryVoltageMv, batteryChargePercent);
DEBUG_MSG("Read power stat %d\n", powerStatus.getHasUSB());
newStatus.notifyObservers(&powerStatus);
// If we have a battery at all and it is less than 10% full, force deep sleep
@@ -168,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);
@@ -200,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();
@@ -237,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

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

@@ -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
@@ -338,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"
@@ -102,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());
}
@@ -128,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,15 +32,12 @@ 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?
@@ -56,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;
@@ -103,7 +96,7 @@ 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);
@@ -120,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.getAltitudeMSL(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,22 @@ 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)
{
@@ -250,7 +268,7 @@ static void drawGPScoordinates(OLEDDisplay *display, int16_t x, int16_t y, const
displayLine = "No GPS Lock";
display->drawString(x + (SCREEN_WIDTH - (display->getStringWidth(displayLine))) / 2, y, displayLine);
} else {
char coordinateLine[20];
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);
}
@@ -708,6 +726,11 @@ void Screen::drawDebugInfoSettingsTrampoline(OLEDDisplay *display, OLEDDisplayUi
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()
@@ -739,6 +762,11 @@ void Screen::setFrames()
// 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();
@@ -797,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 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);
}
// 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
@@ -827,6 +855,135 @@ void DebugInfo::drawFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16
}
// 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
@@ -837,30 +994,22 @@ void DebugInfo::drawFrameSettings(OLEDDisplay *display, OLEDDisplayUiState *stat
display->setTextAlignment(TEXT_ALIGN_LEFT);
char batStr[20];
if (powerStatus->getHasBattery())
{
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' : ' ');
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
{
} else {
// Line 1
display->drawString(x, y, String("USB"));
}
}
//TODO: Display status of the BT radio
// display->drawString(x + SCREEN_WIDTH - display->getStringWidth("BT On"), y, "BT On");
display->drawString(x + SCREEN_WIDTH - display->getStringWidth("Mode " + String(channelSettings.modem_config)), y,
"Mode " + String(channelSettings.modem_config));
// Line 2
uint32_t currentMillis = millis();
@@ -873,20 +1022,16 @@ void DebugInfo::drawFrameSettings(OLEDDisplay *display, OLEDDisplayUiState *stat
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));
display->drawString(x + SCREEN_WIDTH - display->getStringWidth("Mode " + String(channelSettings.modem_config)), y + FONT_HEIGHT * 1, "Mode " + String(channelSettings.modem_config));
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
// TODO: Use this line for WiFi information.
// display->drawString(x + (SCREEN_WIDTH - (display->getStringWidth("WiFi: 192.168.0.100"))) / 2, y + FONT_HEIGHT * 2, "WiFi: 192.168.0.100");
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)

View File

@@ -10,7 +10,8 @@
#include <SSD1306Wire.h>
#endif
#include "TFT.h"
#include "EInkDisplay.h"
#include "TFTDisplay.h"
#include "TypedQueue.h"
#include "commands.h"
#include "concurrency/LockGuard.h"
@@ -46,7 +47,7 @@ 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;
@@ -220,6 +221,8 @@ class Screen : public concurrency::PeriodicTask
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
@@ -237,6 +240,8 @@ class Screen : public concurrency::PeriodicTask
/** FIXME cleanup display abstraction */
#ifdef ST7735_CS
TFTDisplay dispdev;
#elif defined(HAS_EINK)
EInkDisplay dispdev;
#elif defined(USE_SH1106)
SH1106Wire dispdev;
#else

View File

@@ -2,7 +2,7 @@
#ifdef ST7735_CS
#include "SPILock.h"
#include "TFT.h"
#include "TFTDisplay.h"
#include "graphics/configs.h"
#include <SPI.h>
#include <TFT_eSPI.h> // Graphics and font library for ST7735 driver chip
@@ -20,7 +20,6 @@ void TFTDisplay::display(void)
{
concurrency::LockGuard g(spiLock);
#if 1
// 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++) {
@@ -32,7 +31,6 @@ void TFTDisplay::display(void)
tft.drawPixel(x, y, isset ? TFT_WHITE : TFT_BLACK);
}
}
#endif
}
// Send a command to the display (low level function)
@@ -52,12 +50,10 @@ bool TFTDisplay::connect()
pinMode(ST7735_BACKLIGHT_EN, OUTPUT);
#endif
#if 1
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
#endif
return true;
}

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,11 +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>
@@ -149,6 +152,8 @@ void userButtonPressedLong()
screen.adjustBrightness();
}
RadioInterface *rIf = NULL;
void setup()
{
#ifdef USE_SEGGER
@@ -211,19 +216,19 @@ 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 NRF52_SERIES
#ifdef NO_ESP32
SPI.begin();
#else
// ESP32
@@ -232,7 +237,7 @@ void setup()
#endif
// Initialize the screen first so we can show the logo while we start up everything else.
#ifdef ST7735_CS
#if defined(ST7735_CS) || defined(HAS_EINK)
screen.setup();
#else
if (ssd1306_found)
@@ -256,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();
@@ -289,8 +301,7 @@ void setup()
digitalWrite(SX1262_ANT_SW, 1);
#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) {
@@ -325,6 +336,9 @@ void setup()
}
#endif
// Initialize Wifi
initWifi();
if (!rIf)
recordCriticalError(ErrNoRadio);
else
@@ -362,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();
@@ -390,31 +405,39 @@ 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());
// No GPS lock yet, let the OS put the main CPU in low power mode for 100ms (or until another interrupt comes in)
// i.e. don't just keep spinning in loop as fast as we can.
// DEBUG_MSG("msecs %d\n", msecstosleep);
// FIXME - until button press handling is done by interrupt (see polling above) we can't sleep very long at all or buttons
// feel slow
msecstosleep = 10;
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,66 +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
// 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
};
// 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 (AS1 bandplan)
#define CH0_JP 920.0f // MHz
#define CH_SPACING_JP 0.5f
#define NUM_CHANNELS_JP 10
// TW channel settings (AS2 bandplan 923-925MHz)
#define CH0_TW 923.0f // MHz
#define CH_SPACING_TW 0.2
#define NUM_CHANNELS_TW 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)
// Also called AS1 bandplan
#define CH0 CH0_JP
#define CH_SPACING CH_SPACING_JP
#define NUM_CHANNELS NUM_CHANNELS_JP
#elif defined(HW_VERSION_TW)
// Also called AS2 bandplan
#define CH0 CH0_TW
#define CH_SPACING CH_SPACING_TW
#define NUM_CHANNELS NUM_CHANNELS_TW
#else
// HW version not set - assume US
#define CH0 CH0_US
#define CH_SPACING CH_SPACING_US
#define NUM_CHANNELS NUM_CHANNELS_US
#endif
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
@@ -95,29 +105,27 @@ 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
@@ -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
@@ -78,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);
@@ -107,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;
@@ -118,8 +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
*/
@@ -129,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)
@@ -163,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

@@ -137,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
@@ -176,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);
@@ -172,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
@@ -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
@@ -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;
}

22
src/meshwifi/meshhttp.h Normal file
View File

@@ -0,0 +1,22 @@
#pragma once
#include <Arduino.h>
#include <functional>
void initWebServer();
void handleNotFound();
void handleWebResponse();
void handleJSONChatHistory();
void notifyWebUI();
void handleHotspot();
void handleStyleCSS();
void handleRoot();
void handleScriptsScriptJS();
void handleJSONChatHistoryDummy();

269
src/meshwifi/meshwifi.cpp Normal file
View File

@@ -0,0 +1,269 @@
#include "meshwifi.h"
#include "NodeDB.h"
#include "WiFiServerAPI.h"
#include "configuration.h"
#include "main.h"
#include "meshwifi/meshhttp.h"
#include "target_specific.h"
#include <DNSServer.h>
#include <WiFi.h>
static void WiFiEvent(WiFiEvent_t event);
// DNS Server for the Captive Portal
DNSServer dnsServer;
static WiFiServerPort *apiPort;
uint8_t wifiDisconnectReason = 0;
// Stores the last 4 of our hardware ID, to make finding the device for pairing easier
static char ourHost[16];
bool isWifiAvailable()
{
const char *wifiName = radioConfig.preferences.wifi_ssid;
const char *wifiPsw = radioConfig.preferences.wifi_password;
// strcpy(radioConfig.preferences.wifi_ssid, "");
// strcpy(radioConfig.preferences.wifi_password, "");
if (*wifiName && *wifiPsw) {
// Once every 10 seconds, try to reconnect.
return 1;
} else {
return 0;
}
}
// Disable WiFi
void deinitWifi()
{
/*
Note from Jm (jm@casler.org - Sept 16, 2020):
A bug in the ESP32 SDK was introduced in Oct 2019 that keeps the WiFi radio from
turning back on after it's shut off. See:
https://github.com/espressif/arduino-esp32/issues/3522
Until then, WiFi should only be allowed when there's no power
saving on the 2.4g transceiver.
*/
WiFi.mode(WIFI_MODE_NULL);
DEBUG_MSG("WiFi Turned Off\n");
// WiFi.printDiag(Serial);
}
// Startup WiFi
void initWifi()
{
if (isWifiAvailable() == 0) {
return;
}
if (radioConfig.has_preferences) {
const char *wifiName = radioConfig.preferences.wifi_ssid;
const char *wifiPsw = radioConfig.preferences.wifi_password;
if (*wifiName && *wifiPsw) {
if (radioConfig.preferences.wifi_ap_mode) {
IPAddress apIP(192, 168, 42, 1);
WiFi.onEvent(WiFiEvent);
WiFi.softAPConfig(apIP, apIP, IPAddress(255, 255, 255, 0));
DEBUG_MSG("STARTING WIFI AP: ssid=%s, ok=%d\n", wifiName, WiFi.softAP(wifiName, wifiPsw));
DEBUG_MSG("MY IP ADDRESS: %s\n", WiFi.softAPIP().toString().c_str());
dnsServer.start(53, "*", apIP);
} else {
uint8_t dmac[6];
getMacAddr(dmac);
sprintf(ourHost, "Meshtastic-%02x%02x", dmac[4], dmac[5]);
Serial.println(ourHost);
WiFi.mode(WIFI_MODE_STA);
WiFi.setHostname(ourHost);
WiFi.onEvent(WiFiEvent);
// esp_wifi_set_ps(WIFI_PS_NONE); // Disable power saving
// WiFiEventId_t eventID = WiFi.onEvent(
WiFi.onEvent(
[](WiFiEvent_t event, WiFiEventInfo_t info) {
Serial.print("\nWiFi lost connection. Reason: ");
Serial.println(info.disconnected.reason);
/*
If we are disconnected from the AP for some reason,
save the error code.
For a reference to the codes:
https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-guides/wifi.html#wi-fi-reason-code
*/
wifiDisconnectReason = info.disconnected.reason;
},
WiFiEvent_t::SYSTEM_EVENT_STA_DISCONNECTED);
DEBUG_MSG("JOINING WIFI: ssid=%s\n", wifiName);
if (WiFi.begin(wifiName, wifiPsw) == WL_CONNECTED) {
DEBUG_MSG("MY IP ADDRESS: %s\n", WiFi.localIP().toString().c_str());
} else {
DEBUG_MSG("Started Joining WIFI\n");
}
}
}
} else
DEBUG_MSG("Not using WIFI\n");
}
/// Perform idle loop processing required by the wifi layer
void loopWifi()
{
// FIXME, once we have coroutines - just use a coroutine instead of this nasty loopWifi()
if (apiPort)
apiPort->loop();
}
static void initApiServer()
{
// Start API server on port 4403
if (!apiPort) {
apiPort = new WiFiServerPort();
apiPort->init();
}
}
// Called by the Espressif SDK to
static void WiFiEvent(WiFiEvent_t event)
{
DEBUG_MSG("************ [WiFi-event] event: %d ************\n", event);
switch (event) {
case SYSTEM_EVENT_WIFI_READY:
DEBUG_MSG("WiFi interface ready\n");
break;
case SYSTEM_EVENT_SCAN_DONE:
DEBUG_MSG("Completed scan for access points\n");
break;
case SYSTEM_EVENT_STA_START:
DEBUG_MSG("WiFi client started\n");
break;
case SYSTEM_EVENT_STA_STOP:
DEBUG_MSG("WiFi clients stopped\n");
break;
case SYSTEM_EVENT_STA_CONNECTED:
DEBUG_MSG("Connected to access point\n");
break;
case SYSTEM_EVENT_STA_DISCONNECTED:
DEBUG_MSG("Disconnected from WiFi access point\n");
// Event 5
reconnectWiFi();
break;
case SYSTEM_EVENT_STA_AUTHMODE_CHANGE:
DEBUG_MSG("Authentication mode of access point has changed\n");
break;
case SYSTEM_EVENT_STA_GOT_IP:
DEBUG_MSG("Obtained IP address: \n");
Serial.println(WiFi.localIP());
// Start web server
initWebServer();
initApiServer();
break;
case SYSTEM_EVENT_STA_LOST_IP:
DEBUG_MSG("Lost IP address and IP address is reset to 0\n");
break;
case SYSTEM_EVENT_STA_WPS_ER_SUCCESS:
DEBUG_MSG("WiFi Protected Setup (WPS): succeeded in enrollee mode\n");
break;
case SYSTEM_EVENT_STA_WPS_ER_FAILED:
DEBUG_MSG("WiFi Protected Setup (WPS): failed in enrollee mode\n");
break;
case SYSTEM_EVENT_STA_WPS_ER_TIMEOUT:
DEBUG_MSG("WiFi Protected Setup (WPS): timeout in enrollee mode\n");
break;
case SYSTEM_EVENT_STA_WPS_ER_PIN:
DEBUG_MSG("WiFi Protected Setup (WPS): pin code in enrollee mode\n");
break;
case SYSTEM_EVENT_AP_START:
DEBUG_MSG("WiFi access point started\n");
Serial.println(WiFi.softAPIP());
// Start web server
initWebServer();
initApiServer();
break;
case SYSTEM_EVENT_AP_STOP:
DEBUG_MSG("WiFi access point stopped\n");
break;
case SYSTEM_EVENT_AP_STACONNECTED:
DEBUG_MSG("Client connected\n");
break;
case SYSTEM_EVENT_AP_STADISCONNECTED:
DEBUG_MSG("Client disconnected\n");
break;
case SYSTEM_EVENT_AP_STAIPASSIGNED:
DEBUG_MSG("Assigned IP address to client\n");
break;
case SYSTEM_EVENT_AP_PROBEREQRECVED:
DEBUG_MSG("Received probe request\n");
break;
case SYSTEM_EVENT_GOT_IP6:
DEBUG_MSG("IPv6 is preferred\n");
break;
case SYSTEM_EVENT_ETH_START:
DEBUG_MSG("Ethernet started\n");
break;
case SYSTEM_EVENT_ETH_STOP:
DEBUG_MSG("Ethernet stopped\n");
break;
case SYSTEM_EVENT_ETH_CONNECTED:
DEBUG_MSG("Ethernet connected\n");
break;
case SYSTEM_EVENT_ETH_DISCONNECTED:
DEBUG_MSG("Ethernet disconnected\n");
break;
case SYSTEM_EVENT_ETH_GOT_IP:
DEBUG_MSG("Obtained IP address\n");
break;
default:
break;
}
}
void handleDNSResponse()
{
if (radioConfig.preferences.wifi_ap_mode) {
dnsServer.processNextRequest();
}
}
void reconnectWiFi()
{
const char *wifiName = radioConfig.preferences.wifi_ssid;
const char *wifiPsw = radioConfig.preferences.wifi_password;
if (radioConfig.has_preferences) {
if (*wifiName && *wifiPsw) {
DEBUG_MSG("... Reconnecting to WiFi access point");
WiFi.mode(WIFI_MODE_STA);
WiFi.begin(wifiName, wifiPsw);
}
}
}
uint8_t getWifiDisconnectReason()
{
return wifiDisconnectReason;
}

25
src/meshwifi/meshwifi.h Normal file
View File

@@ -0,0 +1,25 @@
#pragma once
#include "configuration.h"
#include <Arduino.h>
#include <functional>
#ifdef HAS_WIFI
#include <DNSServer.h>
#include <WiFi.h>
#endif
void initWifi();
void deinitWifi();
/// Perform idle loop processing required by the wifi layer
void loopWifi();
bool isWifiAvailable();
void handleDNSResponse();
void reconnectWiFi();
uint8_t getWifiDisconnectReason();

View File

@@ -1,10 +1,9 @@
#include "BluetoothUtil.h"
#include "BluetoothSoftwareUpdate.h"
#include "NimbleBluetoothAPI.h"
#include "NodeDB.h" // FIXME - we shouldn't really douch this here - we are using it only because we currently do wifi setup when ble gets turned on
#include "PhoneAPI.h"
#include "PowerFSM.h"
#include "WiFi.h"
#include <WiFi.h>
#include "configuration.h"
#include "esp_bt.h"
#include "host/util/util.h"
@@ -13,6 +12,7 @@
#include "services/gap/ble_svc_gap.h"
#include "services/gatt/ble_svc_gatt.h"
#include <Arduino.h>
#include "meshwifi/meshwifi.h"
static bool pinShowing;
@@ -503,33 +503,8 @@ void reinitBluetooth()
nimble_port_freertos_init(ble_host_task);
}
void initWifi()
{
// Note: Wifi is not yet supported ;-)
strcpy(radioConfig.preferences.wifi_ssid, "");
strcpy(radioConfig.preferences.wifi_password, "");
if (radioConfig.has_preferences) {
const char *wifiName = radioConfig.preferences.wifi_ssid;
if (*wifiName) {
const char *wifiPsw = radioConfig.preferences.wifi_password;
if (radioConfig.preferences.wifi_ap_mode) {
DEBUG_MSG("STARTING WIFI AP: ssid=%s, ok=%d\n", wifiName, WiFi.softAP(wifiName, wifiPsw));
} else {
WiFi.mode(WIFI_MODE_STA);
DEBUG_MSG("JOINING WIFI: ssid=%s\n", wifiName);
if (WiFi.begin(wifiName, wifiPsw) == WL_CONNECTED) {
DEBUG_MSG("MY IP ADDRESS: %s\n", WiFi.localIP().toString().c_str());
} else {
DEBUG_MSG("Started Joining WIFI\n");
}
}
}
} else
DEBUG_MSG("Not using WIFI\n");
}
bool bluetoothOn;
bool firstTime = 1;
// Enable/disable bluetooth.
void setBluetoothEnable(bool on)
@@ -542,11 +517,29 @@ void setBluetoothEnable(bool on)
Serial.printf("Pre BT: %u heap size\n", ESP.getFreeHeap());
// ESP_ERROR_CHECK( heap_trace_start(HEAP_TRACE_LEAKS) );
reinitBluetooth();
initWifi();
// Don't try to reconnect wifi before bluetooth is configured.
// WiFi is initialized from main.cpp in setup() .
if (firstTime) {
firstTime = 0;
} else {
initWifi();
}
} else {
/*
// If WiFi is in use, disable shutting down the radio.
if (isWifiAvailable()) {
return;
}
*/
// shutdown wifi
deinitWifi();
// We have to totally teardown our bluetooth objects to prevent leaks
deinitBLE();
WiFi.mode(WIFI_MODE_NULL); // shutdown wifi
Serial.printf("Shutdown BT: %u heap size\n", ESP.getFreeHeap());
// ESP_ERROR_CHECK( heap_trace_stop() );
// heap_trace_dump();

View File

@@ -1,6 +1,6 @@
#include "NRF52Bluetooth.h"
#include "configuration.h"
#include "graphics/TFT.h"
#include "graphics/TFTDisplay.h"
#include <assert.h>
#include <ble_gap.h>
#include <memory.h>
@@ -49,14 +49,19 @@ void getMacAddr(uint8_t *dmac)
NRF52Bluetooth *nrf52Bluetooth;
static bool bleOn = false;
static const bool enableBle = true; // Set to false for easier debugging
void setBluetoothEnable(bool on)
{
if (on != bleOn) {
if (on) {
if (!nrf52Bluetooth) {
// DEBUG_MSG("DISABLING NRF52 BLUETOOTH WHILE DEBUGGING\n");
nrf52Bluetooth = new NRF52Bluetooth();
nrf52Bluetooth->setup();
if (!enableBle)
DEBUG_MSG("DISABLING NRF52 BLUETOOTH WHILE DEBUGGING\n");
else {
nrf52Bluetooth = new NRF52Bluetooth();
nrf52Bluetooth->setup();
}
}
} else {
DEBUG_MSG("FIXME: implement BLE disable\n");

5
src/nrf52/pgmspace.h Normal file
View File

@@ -0,0 +1,5 @@
#pragma once
// dummy file to keep old arduino code happy
#define PROGMEM
#define pgm_read_byte(addr) (*((unsigned const char *)addr))

16
src/nrf52/wifi-stubs.cpp Normal file
View File

@@ -0,0 +1,16 @@
#include "meshwifi/meshhttp.h"
#include "meshwifi/meshwifi.h"
void initWifi() {}
void deinitWifi() {}
bool isWifiAvailable()
{
return false;
}
void handleWebResponse() {}
/// Perform idle loop processing required by the wifi layer
void loopWifi() {}

View File

@@ -0,0 +1,32 @@
#include "CryptoEngine.h"
#include "target_specific.h"
#include <Utility.h>
// FIXME - move getMacAddr/setBluetoothEnable into a HALPlatform class
uint32_t hwId; // fixme move into portduino
void getMacAddr(uint8_t *dmac)
{
if (!hwId) {
notImplemented("getMacAddr");
hwId = random();
}
dmac[0] = 0x80;
dmac[1] = 0;
dmac[2] = 0;
dmac[3] = hwId >> 16;
dmac[4] = hwId >> 8;
dmac[5] = hwId & 0xff;
}
void setBluetoothEnable(bool on)
{
notImplemented("setBluetoothEnable");
}
// FIXME - implement real crypto for linux
CryptoEngine *crypto = new CryptoEngine();
void updateBatteryLevel(uint8_t level) NOT_IMPLEMENTED("updateBatteryLevel");

View File

@@ -0,0 +1 @@
../nrf52/wifi-stubs.cpp

View File

@@ -7,7 +7,6 @@
#include "error.h"
#include "main.h"
#include "target_specific.h"
#include "timing.h"
#ifndef NO_ESP32
#include "esp32/pm.h"
@@ -123,11 +122,11 @@ bool doPreflightSleep()
/// Tell devices we are going to sleep and wait for them to handle things
static void waitEnterSleep()
{
uint32_t now = timing::millis();
uint32_t now = millis();
while (!doPreflightSleep()) {
delay(100); // Kinda yucky - wait until radio says say we can shutdown (finished in process sends/receives)
if (timing::millis() - now > 30 * 1000) { // If we wait too long just report an error and go to sleep
if (millis() - now > 30 * 1000) { // If we wait too long just report an error and go to sleep
recordCriticalError(ErrSleepEnterWait);
assert(0); // FIXME - for now we just restart, need to fix bug #167
break;
@@ -164,20 +163,25 @@ void doDeepSleep(uint64_t msecToWake)
digitalWrite(VEXT_ENABLE, 1); // turn off the display power
#endif
// Kill GPS power completely (even if previously we just had it in sleep mode)
setGPSPower(false);
setLed(false);
#ifdef TBEAM_V10
if (axp192_found) {
// Obsolete comment: from back when we we used to receive lora packets while CPU was in deep sleep.
// We no longer do that, because our light-sleep current draws are low enough and it provides fast start/low cost
// wake. We currently use deep sleep only for 'we want our device to actually be off - because our battery is
// critically low'. So in deep sleep we DO shut down power to LORA (and when we boot later we completely reinit it)
//
// No need to turn this off if the power draw in sleep mode really is just 0.2uA and turning it off would
// leave floating input for the IRQ line
// If we want to leave the radio receving in would be 11.5mA current draw, but most of the time it is just waiting
// in its sequencer (true?) so the average power draw should be much lower even if we were listinging for packets
// all the time.
// axp.setPowerOutPut(AXP192_LDO2, AXP202_OFF); // LORA radio
setGPSPower(false);
axp.setPowerOutPut(AXP192_LDO2, AXP202_OFF); // LORA radio
}
#endif
@@ -261,12 +265,20 @@ esp_sleep_wakeup_cause_t doLightSleep(uint64_t sleepMsec) // FIXME, use a more r
// We treat the serial port as a GPIO for a fast/low power way of waking, if we see a rising edge that means
// someone started to send something
// Alas - doesn't work reliably, instead need to use the uart specific version (which burns a little power)
// FIXME: gpio 3 is RXD for serialport 0 on ESP32
// gpio 3 is RXD for serialport 0 on ESP32
// Send a few Z characters to wake the port
gpio_wakeup_enable((gpio_num_t)SERIAL0_RX_GPIO, GPIO_INTR_LOW_LEVEL);
// uart_set_wakeup_threshold(UART_NUM_0, 3);
// esp_sleep_enable_uart_wakeup(0);
// 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);
// doesn't help - I think the USB-UART chip losing power is pulling the signal llow
// gpio_pullup_en((gpio_num_t)SERIAL0_RX_GPIO);
// alas - can only work if using the refclock, which is limited to about 9600 bps
// assert(uart_set_wakeup_threshold(UART_NUM_0, 3) == ESP_OK);
// assert(esp_sleep_enable_uart_wakeup(0) == ESP_OK);
#endif
#ifdef BUTTON_PIN
gpio_wakeup_enable((gpio_num_t)BUTTON_PIN, GPIO_INTR_LOW_LEVEL); // when user presses, this button goes low
@@ -275,7 +287,7 @@ esp_sleep_wakeup_cause_t doLightSleep(uint64_t sleepMsec) // FIXME, use a more r
gpio_wakeup_enable((gpio_num_t)RF95_IRQ_GPIO, GPIO_INTR_HIGH_LEVEL); // RF95 interrupt, active high
#endif
#ifdef PMU_IRQ
// FIXME, disable wake due to PMU because it seems to fire all the time?
// wake due to PMU can happen repeatedly if there is no battery installed or the battery fills
if (axp192_found)
gpio_wakeup_enable((gpio_num_t)PMU_IRQ, GPIO_INTR_LOW_LEVEL); // pmu irq
#endif

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