mirror of
https://github.com/alexhopeoconnor/firmware.git
synced 2026-10-17 00:51:30 +10:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
68490336b8 | ||
|
|
20ac8d71fd | ||
|
|
d248c6be4b | ||
|
|
39b0a89821 | ||
|
|
d9f43d3e2f | ||
|
|
08c77caaa9 | ||
|
|
cfedc97cd0 | ||
|
|
cfad226b2b | ||
|
|
4ee35a0612 | ||
|
|
44749470a4 | ||
|
|
8fe714d8b1 | ||
|
|
22137ff1bd | ||
|
|
da3b6d1958 | ||
|
|
637960edde | ||
|
|
d9209ffaea | ||
|
|
9fb94796c8 | ||
|
|
f060f7faad | ||
|
|
55673fcd66 | ||
|
|
51267379ab | ||
|
|
e2cf2ba4f2 | ||
|
|
4550cce639 | ||
|
|
7c0d13f00a | ||
|
|
f78f3232e2 | ||
|
|
7802d00031 | ||
|
|
40a15248e8 | ||
|
|
3a62453b8b | ||
|
|
c3f7829255 | ||
|
|
37d9fb2dad | ||
|
|
4388e72dec | ||
|
|
9803141fe7 | ||
|
|
1f0e9cc1c3 | ||
|
|
92b30ebec6 | ||
|
|
ccadb6a43d | ||
|
|
6f7f540c79 | ||
|
|
d5b8038457 | ||
|
|
0a6059ba13 | ||
|
|
aba5b01fa0 | ||
|
|
09f4943869 | ||
|
|
29c8543f87 | ||
|
|
7bd4940ed8 | ||
|
|
d5116935b5 | ||
|
|
0d320fe29b | ||
|
|
4159461a62 | ||
|
|
f4bd39e3fa | ||
|
|
fbc36a2cfd | ||
|
|
e93ba73adb | ||
|
|
03301f093d | ||
|
|
55a5fa6fb5 | ||
|
|
4d04d10135 | ||
|
|
cda423acab | ||
|
|
0f92678c3b | ||
|
|
8d122f36e3 | ||
|
|
439cdfbb32 | ||
|
|
0a6ab31e10 | ||
|
|
0b6486256d | ||
|
|
da12b93f82 | ||
|
|
6dec6af5dc | ||
|
|
11444621ae | ||
|
|
98f1b3296c | ||
|
|
26c43e7091 | ||
|
|
9b9447858a | ||
|
|
3151cfb064 | ||
|
|
c327fee986 | ||
|
|
a4f53270e8 | ||
|
|
a7456a1126 | ||
|
|
8381512ce4 | ||
|
|
57d968cdcd | ||
|
|
20a669029b | ||
|
|
f2e6c6de58 | ||
|
|
8fa44c3590 | ||
|
|
f5b7c33d4e | ||
|
|
be8e663d39 | ||
|
|
0d4a9748e3 | ||
|
|
bd477f0fb2 | ||
|
|
5317895a5e | ||
|
|
542b8b26ce | ||
|
|
aaca854620 | ||
|
|
64da384fc1 | ||
|
|
a595fc4642 | ||
|
|
ac135be8cd | ||
|
|
ac2d3e2ae0 | ||
|
|
33946af39f | ||
|
|
5ea59a1c4d | ||
|
|
8bafd87b76 | ||
|
|
6b40e9a5e0 | ||
|
|
a2f6fd9298 | ||
|
|
bc604fc9ba | ||
|
|
9baaa13897 | ||
|
|
65e53be8b0 | ||
|
|
7f5283e95d | ||
|
|
ae4ab48ddc | ||
|
|
afccf1da02 | ||
|
|
fc07c7c01f | ||
|
|
aeb906414f | ||
|
|
a6c6b45576 | ||
|
|
13806cce93 | ||
|
|
e7eee0995a | ||
|
|
5687bd09c6 | ||
|
|
f3db895832 | ||
|
|
dd2ffe5d14 | ||
|
|
ec10e784e1 | ||
|
|
649faa1d93 | ||
|
|
669f96b367 | ||
|
|
996821d18e | ||
|
|
2e172b019e | ||
|
|
3df05cd5c1 | ||
|
|
8bb85cdc69 | ||
|
|
9f6e23754c | ||
|
|
5a7cfdffb3 | ||
|
|
caafddfdfa | ||
|
|
ee0e31be97 | ||
|
|
f1da6469a3 | ||
|
|
68e57dd3a7 | ||
|
|
2504311671 | ||
|
|
aa29315624 | ||
|
|
e3bcb87cf0 | ||
|
|
2530dc44c7 | ||
|
|
70a8fe30b7 | ||
|
|
30e538e5ed | ||
|
|
58dbc3c702 | ||
|
|
210c904604 | ||
|
|
27fdab7c8d | ||
|
|
ba3e1abb5e | ||
|
|
ce194e2162 | ||
|
|
595166db8e | ||
|
|
38d9e34a66 | ||
|
|
c1e0977db3 | ||
|
|
b4f32e7645 | ||
|
|
6bb0c95c95 | ||
|
|
4e958c9230 | ||
|
|
64cf1890f2 | ||
|
|
6a857b00db | ||
|
|
6d60a061bc | ||
|
|
6a09ddef18 | ||
|
|
d48e803b7b | ||
|
|
9061b3d8c3 | ||
|
|
2839539c6c | ||
|
|
f77a1798f3 | ||
|
|
664b558a36 | ||
|
|
c1865f127c | ||
|
|
3a69539192 | ||
|
|
cfcaf28ace | ||
|
|
d9e93f3944 | ||
|
|
60470211e5 | ||
|
|
c5851a4a0c | ||
|
|
0c0b2446b7 | ||
|
|
ce9352fd23 | ||
|
|
375ae5fe77 | ||
|
|
665d35196d | ||
|
|
9757f9ae53 | ||
|
|
e45d0c4dcf | ||
|
|
5c9f22bc18 | ||
|
|
d8287e9cdb | ||
|
|
c66e064f42 | ||
|
|
364fc84aaa | ||
|
|
fe4f86bc84 | ||
|
|
982b2e33ff | ||
|
|
8190098bb8 | ||
|
|
c99411311b | ||
|
|
da8a048dce | ||
|
|
b4de495154 |
@@ -0,0 +1,38 @@
|
|||||||
|
---
|
||||||
|
name: Bug report or feature proposal
|
||||||
|
about: Create a report to help us improve
|
||||||
|
title: ''
|
||||||
|
labels: ''
|
||||||
|
assignees: ''
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
Please - if you just have a question (i.e. not a bug report or a feature proposal), post in our [forum](https://meshtastic.discourse.group/) instead.
|
||||||
|
|
||||||
|
**Describe the bug**
|
||||||
|
A clear and concise description of what the bug is.
|
||||||
|
|
||||||
|
**To Reproduce**
|
||||||
|
Steps to reproduce the behavior:
|
||||||
|
1. Go to '...'
|
||||||
|
2. Click on '....'
|
||||||
|
3. Scroll down to '....'
|
||||||
|
4. See error
|
||||||
|
|
||||||
|
**Expected behavior**
|
||||||
|
A clear and concise description of what you expected to happen.
|
||||||
|
|
||||||
|
**Screenshots**
|
||||||
|
If applicable, add screenshots to help explain your problem.
|
||||||
|
|
||||||
|
**Device info:**
|
||||||
|
- Device model: [e.g. TBEAM]
|
||||||
|
- Software Version [e.g. 0.7.8]
|
||||||
|
|
||||||
|
**Smartphone information (if relevant):**
|
||||||
|
- Device: [e.g. iPhone6]
|
||||||
|
- OS: [e.g. iOS8.1]
|
||||||
|
- App Version [e.g. 0.7.2]
|
||||||
|
|
||||||
|
**Additional context**
|
||||||
|
Add any other context about the problem here.
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
## Thank you for sending in a pull request, here's some tips to get started!
|
||||||
|
|
||||||
|
(Please delete all these tips and replace with your text)
|
||||||
|
|
||||||
|
- Before starting on some new big chunk of code, it it is optional but highly recommended to open an issue first
|
||||||
|
to say "hey, I think this idea X should be implemented and I'm starting work on it. My general plan is Y, any feedback
|
||||||
|
is appreciated." This will allow other devs to potentially save you time by not accidentially duplicating work etc...
|
||||||
|
- Please do not check in files that don't have real changes
|
||||||
|
- Please do not reformat lines that you didn't have to change the code on
|
||||||
|
- We recommend using the [Visual Studio Code](https://platformio.org/install/ide?install=vscode) editor,
|
||||||
|
because automatically follows our indentation rules and it's auto reformatting will not cause spurious changes to lines.
|
||||||
|
- If your PR fixes a bug, mention "fixes #bugnum" somewhere in your pull request description.
|
||||||
|
- If your other co-developers have comments on your PR please tweak as needed.
|
||||||
+10
-1
@@ -7,4 +7,13 @@ main/credentials.h
|
|||||||
!.vscode/settings.json
|
!.vscode/settings.json
|
||||||
!.vscode/tasks.json
|
!.vscode/tasks.json
|
||||||
!.vscode/extensions.json
|
!.vscode/extensions.json
|
||||||
*.code-workspace
|
*.code-workspace
|
||||||
|
|
||||||
|
.DS_Store
|
||||||
|
Thumbs.db
|
||||||
|
.autotools
|
||||||
|
.built
|
||||||
|
.context
|
||||||
|
.cproject
|
||||||
|
.idea/*
|
||||||
|
.vagrant
|
||||||
|
|||||||
Vendored
+4
-2
@@ -59,6 +59,8 @@
|
|||||||
"cfsr",
|
"cfsr",
|
||||||
"descs",
|
"descs",
|
||||||
"ocrypto",
|
"ocrypto",
|
||||||
"protobufs"
|
"protobufs",
|
||||||
]
|
"wifi"
|
||||||
|
],
|
||||||
|
"C_Cpp.dimInactiveRegions": true
|
||||||
}
|
}
|
||||||
Vendored
+17
@@ -0,0 +1,17 @@
|
|||||||
|
{
|
||||||
|
"version": "2.0.0",
|
||||||
|
"tasks": [
|
||||||
|
{
|
||||||
|
"type": "PlatformIO",
|
||||||
|
"task": "Build",
|
||||||
|
"problemMatcher": [
|
||||||
|
"$platformio"
|
||||||
|
],
|
||||||
|
"group": {
|
||||||
|
"kind": "build",
|
||||||
|
"isDefault": true
|
||||||
|
},
|
||||||
|
"label": "PlatformIO: Build"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -22,24 +22,28 @@ This software is 100% open source and developed by a group of hobbyist experimen
|
|||||||
|
|
||||||
We currently support three models of radios.
|
We currently support three models of radios.
|
||||||
|
|
||||||
- TTGO T-Beam
|
- 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.0 w/ NEO-M8N](https://www.aliexpress.com/item/33047631119.html) (Recommended)
|
- [T-Beam V1.0 w/ NEO-M8N](https://www.aliexpress.com/item/33047631119.html) (slightly better GPS)
|
||||||
- [T-Beam V1.0 w/ NEO-6M](https://www.aliexpress.com/item/33050391850.html)
|
- [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!)
|
||||||
- 3D printable cases
|
- 3D printable cases
|
||||||
- [T-Beam V0](https://www.thingiverse.com/thing:3773717)
|
- [T-Beam V0](https://www.thingiverse.com/thing:3773717)
|
||||||
- [T-Beam V1](https://www.thingiverse.com/thing:3830711)
|
- [T-Beam V1](https://www.thingiverse.com/thing:3830711)
|
||||||
|
|
||||||
- [TTGO LORA32](https://www.aliexpress.com/item/4000211331316.html) - No GPS
|
- [TTGO LORA32](https://www.aliexpress.com/item/4000211331316.html) - No GPS
|
||||||
|
|
||||||
|
- 3D printable case
|
||||||
|
- [TTGO LORA32 v1](https://www.thingiverse.com/thing:3385109)
|
||||||
|
|
||||||
- [Heltec LoRa 32](https://heltec.org/project/wifi-lora-32/) - No GPS
|
- [Heltec LoRa 32](https://heltec.org/project/wifi-lora-32/) - No GPS
|
||||||
- [3D Printable case](https://www.thingiverse.com/thing:3125854)
|
- [3D Printable case](https://www.thingiverse.com/thing:3125854)
|
||||||
|
|
||||||
**Make sure to get the frequency for your country**
|
**Make sure to get the frequency for your country**
|
||||||
|
|
||||||
- US/JP/AU/NZ - 915MHz
|
- US/JP/AU/NZ/CA - 915MHz
|
||||||
- CN - 470MHz
|
- CN - 470MHz
|
||||||
- EU - 868MHz, 433MHz
|
- EU - 868MHz, 433MHz
|
||||||
|
- full list of LoRa frequencies per region is available [here](https://www.thethingsnetwork.org/docs/lorawan/frequencies-by-country.html)
|
||||||
|
|
||||||
Getting a version that includes a screen is optional, but highly recommended.
|
Getting a version that includes a screen is optional, but highly recommended.
|
||||||
|
|
||||||
@@ -47,6 +51,8 @@ Getting a version that includes a screen is optional, but highly recommended.
|
|||||||
|
|
||||||
Prebuilt binaries for the supported radios are available in our [releases](https://github.com/meshtastic/Meshtastic-esp32/releases). Your initial installation has to happen over USB from your Mac, Windows or Linux PC. Once our software is installed, all future software updates happen over bluetooth from your phone.
|
Prebuilt binaries for the supported radios are available in our [releases](https://github.com/meshtastic/Meshtastic-esp32/releases). Your initial installation has to happen over USB from your Mac, Windows or Linux PC. Once our software is installed, all future software updates happen over bluetooth from your phone.
|
||||||
|
|
||||||
|
Be **very careful** to install the correct load for your board. In particular the popular 'T-BEAM' radio from TTGO is not called 'TTGO-Lora' (that is a different board). So don't install the 'TTGO-Lora' build on a TBEAM, it won't work correctly.
|
||||||
|
|
||||||
Please post comments on our [group chat](https://meshtastic.discourse.group/) if you have problems or successes.
|
Please post comments on our [group chat](https://meshtastic.discourse.group/) if you have problems or successes.
|
||||||
|
|
||||||
### Installing from a GUI - Windows and Mac
|
### Installing from a GUI - Windows and Mac
|
||||||
|
|||||||
+1
-1
@@ -8,7 +8,7 @@ COUNTRIES="US EU433 EU865 CN JP"
|
|||||||
#COUNTRIES=US
|
#COUNTRIES=US
|
||||||
#COUNTRIES=CN
|
#COUNTRIES=CN
|
||||||
|
|
||||||
BOARDS="ttgo-lora32-v2 ttgo-lora32-v1 tbeam heltec"
|
BOARDS="tlora-v2 tlora-v1 tlora-v2-1-1.6 tbeam heltec tbeam0.7"
|
||||||
#BOARDS=tbeam
|
#BOARDS=tbeam
|
||||||
|
|
||||||
OUTDIR=release/latest
|
OUTDIR=release/latest
|
||||||
|
|||||||
Executable
+3
@@ -0,0 +1,3 @@
|
|||||||
|
|
||||||
|
|
||||||
|
JLinkGDBServerCLExe -if SWD -select USB -port 2331 -device NRF52832_XXAA
|
||||||
+1
-1
@@ -1,3 +1,3 @@
|
|||||||
|
|
||||||
|
|
||||||
export VERSION=0.7.8
|
export VERSION=0.8.2
|
||||||
+8
-2
@@ -35,10 +35,15 @@ This project is currently in beta testing but it is fairly stable and feature co
|
|||||||
|
|
||||||
This software is 100% open source and developed by a group of hobbyist experimenters. No warranty is provided, if you'd like to improve it - we'd love your help. Please post in the [forum](https://meshtastic.discourse.group/).
|
This software is 100% open source and developed by a group of hobbyist experimenters. No warranty is provided, if you'd like to improve it - we'd love your help. Please post in the [forum](https://meshtastic.discourse.group/).
|
||||||
|
|
||||||
|
### Beginner's Guide
|
||||||
|
|
||||||
|
For an detailed walk-through aimed at beginners, we recommend [meshtastic.letstalkthis.com](https://meshtastic.letstalkthis.com/).
|
||||||
|
|
||||||
# Updates
|
# Updates
|
||||||
|
|
||||||
Note: Updates are happening almost daily, only major updates are listed below. For more details see our forum.
|
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.
|
||||||
- 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.
|
- 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/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.
|
- 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.
|
||||||
@@ -63,8 +68,9 @@ If you'd like to help with development, the source code is [on github](https://g
|
|||||||
|
|
||||||
## Supported hardware
|
## Supported hardware
|
||||||
|
|
||||||
We currently support two brands of radios. The [TTGO T-Beam](https://www.aliexpress.com/item/4000119152086.html) and the [Heltec LoRa 32](https://heltec.org/project/wifi-lora-32/). Most people should buy the T-Beam and a 18650 battery (total cost less than \$35). Make
|
We currently support two brands of radios. The [TTGO T-Beam](https://www.aliexpress.com/item/4001178678568.html) and the [Heltec LoRa 32](https://heltec.org/project/wifi-lora-32/). Most people should buy the T-Beam and a 18650 battery (total cost less than \$35). Also, the version of the T-Beam we link to is shipped with Meshtastic **preinstalled** by TTGO, so you don't have to install it yourself.
|
||||||
sure to buy the frequency range which is legal for your country. For the USA, you should buy the 915MHz version. Getting a version that include a screen is optional, but highly recommended.
|
|
||||||
|
Make sure to buy the frequency range which is legal for your country. For the USA, you should buy the 915MHz version. Getting a version that include a screen is optional, but highly recommended.
|
||||||
|
|
||||||
Instructions for installing prebuilt firmware can be found [here](https://github.com/meshtastic/Meshtastic-esp32/blob/master/README.md).
|
Instructions for installing prebuilt firmware can be found [here](https://github.com/meshtastic/Meshtastic-esp32/blob/master/README.md).
|
||||||
|
|
||||||
|
|||||||
@@ -2,14 +2,6 @@
|
|||||||
|
|
||||||
You probably don't care about this section - skip to the next one.
|
You probably don't care about this section - skip to the next one.
|
||||||
|
|
||||||
- bluetooth toggle enable stress test, we are not properly restarting our connect
|
|
||||||
- make new android release
|
|
||||||
- check in our modified arduino binaries
|
|
||||||
- post bug on esp32-arduino
|
|
||||||
- implement first cut of router mode: preferentially handle flooding, and change sleep and GPS behaviors
|
|
||||||
- let users set arbitrary params in android
|
|
||||||
- NRF52 BLE support
|
|
||||||
|
|
||||||
# Medium priority
|
# Medium priority
|
||||||
|
|
||||||
Items to complete before 1.0.
|
Items to complete before 1.0.
|
||||||
@@ -53,11 +45,17 @@ Items after the first final candidate release.
|
|||||||
- split out the software update utility so other projects can use it. Have the appload specify the URL for downloads.
|
- split out the software update utility so other projects can use it. Have the appload specify the URL for downloads.
|
||||||
- read the PMU battery fault indicators and blink/led/warn user on screen
|
- read the PMU battery fault indicators and blink/led/warn user on screen
|
||||||
- discard very old nodedb records (> 1wk)
|
- discard very old nodedb records (> 1wk)
|
||||||
- add a watchdog timer
|
|
||||||
- handle millis() rollover in GPS.getTime - otherwise we will break after 50 days
|
- handle millis() rollover in GPS.getTime - otherwise we will break after 50 days
|
||||||
- report esp32 device code bugs back to the mothership via android
|
- report esp32 device code bugs back to the mothership via android
|
||||||
- change BLE bonding to something more secure. see comment by pSecurity->setAuthenticationMode(ESP_LE_AUTH_BOND)
|
- change BLE bonding to something more secure. see comment by pSecurity->setAuthenticationMode(ESP_LE_AUTH_BOND)
|
||||||
|
|
||||||
|
Changes related to wifi support on ESP32:
|
||||||
|
|
||||||
|
- iram space: https://esp32.com/viewtopic.php?t=8460
|
||||||
|
- set https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-guides/external-ram.html spi ram bss
|
||||||
|
- figure out if iram or bluetooth classic caused ble problems
|
||||||
|
- post bug on esp32-arduino with BLE bug findings
|
||||||
|
|
||||||
# Spinoff project ideas
|
# Spinoff project ideas
|
||||||
|
|
||||||
- an open source version of https://www.burnair.ch/skynet/
|
- an open source version of https://www.burnair.ch/skynet/
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
We build our own custom version of esp32-arduino, in order to get some fixes we've made but haven't yet been merged in master.
|
We build our own custom version of esp32-arduino, in order to get some fixes we've made but haven't yet been merged in master.
|
||||||
|
|
||||||
These are a set of currently unformatted notes on how to build and install them. Most developers should not care about this, because
|
These are a set of currently unformatted notes on how to build and install them. Most developers should not care about this, because
|
||||||
you'll automatically get our fixed libraries.
|
you'll automatically get our fixed libraries.
|
||||||
|
|
||||||
```
|
```
|
||||||
@@ -12,5 +12,5 @@ you'll automatically get our fixed libraries.
|
|||||||
https://docs.espressif.com/projects/esp-idf/en/release-v3.3/get-started/linux-setup.html
|
https://docs.espressif.com/projects/esp-idf/en/release-v3.3/get-started/linux-setup.html
|
||||||
kevinh@kevin-server:~/development/meshtastic/esp32-arduino-lib-builder\$ python /home/kevinh/development/meshtastic/esp32-arduino-lib-builder/esp-idf/components/esptool*py/esptool/esptool.py --chip esp32 --port /dev/ttyUSB0 --baud 921600 --before default_reset --after hard_reset write_flash -z --flash_mode dout --flash_freq 40m --flash_size detect 0x1000 /home/kevinh/development/meshtastic/esp32-arduino-lib-builder/build/bootloader/bootloader.bin
|
kevinh@kevin-server:~/development/meshtastic/esp32-arduino-lib-builder\$ python /home/kevinh/development/meshtastic/esp32-arduino-lib-builder/esp-idf/components/esptool*py/esptool/esptool.py --chip esp32 --port /dev/ttyUSB0 --baud 921600 --before default_reset --after hard_reset write_flash -z --flash_mode dout --flash_freq 40m --flash_size detect 0x1000 /home/kevinh/development/meshtastic/esp32-arduino-lib-builder/build/bootloader/bootloader.bin
|
||||||
cp -a out/tools/sdk/* components/arduino/tools/sdk
|
cp -a out/tools/sdk/* components/arduino/tools/sdk
|
||||||
cp -ar components/arduino/ ~/.platformio/packages/framework-arduinoespressif32@src-fba9d33740f719f712e9f8b07da6ea13/
|
cp -ar components/arduino/* ~/.platformio/packages/framework-arduinoespressif32@src-fba9d33740f719f712e9f8b07da6ea13/
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -19,20 +19,21 @@ reliable messaging tasks (stage one for DSR):
|
|||||||
- DONE once an ack comes in, remove the packet from the retry list and deliver the ack to the original sender
|
- DONE once an ack comes in, remove the packet from the retry list and deliver the ack to the original sender
|
||||||
- DONE after three retries, deliver a no-ack packet to the original sender (i.e. the phone app or mesh router service)
|
- DONE after three retries, deliver a no-ack packet to the original sender (i.e. the phone app or mesh router service)
|
||||||
- DONE test one hop ack/nak with the python framework
|
- DONE test one hop ack/nak with the python framework
|
||||||
- Do stress test with acks
|
- DONE Do stress test with acks
|
||||||
|
|
||||||
dsr tasks
|
dsr tasks
|
||||||
|
|
||||||
- oops I might have broken message reception
|
- DONE oops I might have broken message reception
|
||||||
- DONE Don't use broadcasts for the network pings (close open github issue)
|
- DONE Don't use broadcasts for the network pings (close open github issue)
|
||||||
- DONE add ignoreSenders to radioconfig to allow testing different mesh topologies by refusing to see certain senders
|
- DONE add ignoreSenders to radioconfig to allow testing different mesh topologies by refusing to see certain senders
|
||||||
- test multihop delivery with the python framework
|
- DONE test multihop delivery with the python framework
|
||||||
|
|
||||||
optimizations / low priority:
|
optimizations / low priority:
|
||||||
|
|
||||||
|
- read this [this](http://pages.cs.wisc.edu/~suman/pubs/nadv-mobihoc05.pdf) paper and others and make our naive flood routing less naive
|
||||||
- read @cyclomies long email with good ideas on optimizations and reply
|
- read @cyclomies long email with good ideas on optimizations and reply
|
||||||
- Remove NodeNum assignment algorithm (now that we use 4 byte node nums)
|
- DONE Remove NodeNum assignment algorithm (now that we use 4 byte node nums)
|
||||||
- make android app warn if firmware is too old or too new to talk to
|
- DONE make android app warn if firmware is too old or too new to talk to
|
||||||
- change nodenums and packetids in protobuf to be fixed32
|
- change nodenums and packetids in protobuf to be fixed32
|
||||||
- low priority: think more careful about reliable retransmit intervals
|
- low priority: think more careful about reliable retransmit intervals
|
||||||
- make ReliableRouter.pending threadsafe
|
- make ReliableRouter.pending threadsafe
|
||||||
|
|||||||
@@ -1,5 +1,36 @@
|
|||||||
# NRF52 TODO
|
# NRF52 TODO
|
||||||
|
|
||||||
|
|
||||||
|
## RAK815
|
||||||
|
|
||||||
|
### Bootloader
|
||||||
|
Installing the adafruit bootloader is optional - I think the stock bootloader will work okay for most.
|
||||||
|
|
||||||
|
```
|
||||||
|
kevinh@kevin-server:~/development/meshtastic/Adafruit_nRF52_Bootloader$ make BOARD=rak815 flash
|
||||||
|
LD rak815_bootloader-0.3.2-111-g9478eb7-dirty.out
|
||||||
|
text data bss dec hex filename
|
||||||
|
20888 1124 15006 37018 909a _build/build-rak815/rak815_bootloader-0.3.2-111-g9478eb7-dirty.out
|
||||||
|
Create rak815_bootloader-0.3.2-111-g9478eb7-dirty.hex
|
||||||
|
Create rak815_bootloader-0.3.2-111-g9478eb7-dirty-nosd.hex
|
||||||
|
Flashing: rak815_bootloader-0.3.2-111-g9478eb7-dirty-nosd.hex
|
||||||
|
nrfjprog --program _build/build-rak815/rak815_bootloader-0.3.2-111-g9478eb7-dirty-nosd.hex --sectoranduicrerase -f nrf52 --reset
|
||||||
|
Parsing hex file.
|
||||||
|
Erasing page at address 0x0.
|
||||||
|
Erasing page at address 0x74000.
|
||||||
|
Erasing page at address 0x75000.
|
||||||
|
Erasing page at address 0x76000.
|
||||||
|
Erasing page at address 0x77000.
|
||||||
|
Erasing page at address 0x78000.
|
||||||
|
Erasing page at address 0x79000.
|
||||||
|
Erasing UICR flash area.
|
||||||
|
Applying system reset.
|
||||||
|
Checking that the area to write is not protected.
|
||||||
|
Programming device.
|
||||||
|
Applying system reset.
|
||||||
|
Run.
|
||||||
|
```
|
||||||
|
|
||||||
## Misc work items
|
## Misc work items
|
||||||
|
|
||||||
RAM investigation.
|
RAM investigation.
|
||||||
|
|||||||
@@ -0,0 +1,18 @@
|
|||||||
|
/* This section is only included if CONFIG_SPIRAM_ALLOW_BSS_SEG_EXTERNAL_MEMORY
|
||||||
|
is set, to link some sections to BSS in PSRAM */
|
||||||
|
|
||||||
|
SECTIONS
|
||||||
|
{
|
||||||
|
/* external memory bss, from any global variable with EXT_RAM_ATTR attribute*/
|
||||||
|
.ext_ram.bss (NOLOAD) :
|
||||||
|
{
|
||||||
|
_ext_ram_bss_start = ABSOLUTE(.);
|
||||||
|
*(.ext_ram.bss*)
|
||||||
|
*libnet80211.a:(.dynsbss .sbss .sbss.* .gnu.linkonce.sb.* .scommon .sbss2.* .gnu.linkonce.sb2.* .dynbss .bss .bss.* .share.mem .gnu.linkonce.b.* COMMON)
|
||||||
|
*libpp.a:(.dynsbss .sbss .sbss.* .gnu.linkonce.sb.* .scommon .sbss2.* .gnu.linkonce.sb2.* .dynbss .bss .bss.* .share.mem .gnu.linkonce.b.* COMMON)
|
||||||
|
*liblwip.a:(.dynsbss .sbss .sbss.* .gnu.linkonce.sb.* .scommon .sbss2.* .gnu.linkonce.sb2.* .dynbss .bss .bss.* .share.mem .gnu.linkonce.b.* COMMON)
|
||||||
|
*libbt.a:(EXCLUDE_FILE (libbtdm_app.a) .dynsbss .sbss .sbss.* .gnu.linkonce.sb.* .scommon .sbss2.* .gnu.linkonce.sb2.* .dynbss .bss .bss.* .share.mem .gnu.linkonce.b.* COMMON)
|
||||||
|
. = ALIGN(4);
|
||||||
|
_ext_ram_bss_end = ABSOLUTE(.);
|
||||||
|
} > extern_ram_seg
|
||||||
|
}
|
||||||
+23
-16
@@ -23,6 +23,8 @@ default_envs = tbeam ; Note: the github actions CI test build can't yet build NR
|
|||||||
|
|
||||||
[env]
|
[env]
|
||||||
|
|
||||||
|
framework = arduino
|
||||||
|
|
||||||
; customize the partition table
|
; customize the partition table
|
||||||
; http://docs.platformio.org/en/latest/platforms/espressif32.html#partition-tables
|
; http://docs.platformio.org/en/latest/platforms/espressif32.html#partition-tables
|
||||||
board_build.partitions = partition-table.csv
|
board_build.partitions = partition-table.csv
|
||||||
@@ -67,27 +69,28 @@ debug_tool = jlink
|
|||||||
lib_deps =
|
lib_deps =
|
||||||
https://github.com/meshtastic/esp8266-oled-ssd1306.git ; ESP8266_SSD1306
|
https://github.com/meshtastic/esp8266-oled-ssd1306.git ; ESP8266_SSD1306
|
||||||
SPI
|
SPI
|
||||||
; 1260 ; OneButton - not used yet
|
1260 ; OneButton library for non-blocking button debounce
|
||||||
1202 ; CRC32, explicitly needed because dependency is missing in the ble ota update lib
|
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
|
Wire ; explicitly needed here because the AXP202 library forgets to add it
|
||||||
https://github.com/meshtastic/arduino-fsm.git
|
https://github.com/meshtastic/arduino-fsm.git
|
||||||
https://github.com/meshtastic/SparkFun_Ublox_Arduino_Library.git
|
https://github.com/meshtastic/SparkFun_Ublox_Arduino_Library.git
|
||||||
https://github.com/meshtastic/RadioLib.git
|
https://github.com/meshtastic/RadioLib.git#6aa38a85856012c99c4e9b4e7cee35e37671a4bc
|
||||||
https://github.com/meshtastic/TinyGPSPlus.git
|
https://github.com/meshtastic/TinyGPSPlus.git
|
||||||
|
|
||||||
; Common settings for ESP targes, mixin with extends = esp32_base
|
; Common settings for ESP targes, mixin with extends = esp32_base
|
||||||
[esp32_base]
|
[esp32_base]
|
||||||
platform = espressif32
|
platform = espressif32
|
||||||
framework = arduino
|
|
||||||
src_filter =
|
src_filter =
|
||||||
${env.src_filter} -<nrf52/>
|
${env.src_filter} -<nrf52/>
|
||||||
upload_speed = 921600
|
upload_speed = 921600
|
||||||
debug_init_break = tbreak setup
|
debug_init_break = tbreak setup
|
||||||
build_flags =
|
build_flags =
|
||||||
${env.build_flags} -Wall -Wextra -Isrc/esp32
|
${env.build_flags} -Wall -Wextra -Isrc/esp32 -mfix-esp32-psram-cache-issue
|
||||||
|
# Hmm - this doesn't work yet
|
||||||
|
# board_build.ldscript = linker/esp32.extram.bss.ld
|
||||||
lib_ignore = segger_rtt
|
lib_ignore = segger_rtt
|
||||||
platform_packages =
|
platform_packages =
|
||||||
framework-arduinoespressif32 @ https://github.com/meshtastic/arduino-esp32.git
|
framework-arduinoespressif32 @ https://github.com/meshtastic/arduino-esp32.git#b7f106cd11a04573b902228ea97025c8b5814dd9
|
||||||
|
|
||||||
; The 1.0 release of the TBEAM board
|
; The 1.0 release of the TBEAM board
|
||||||
[env:tbeam]
|
[env:tbeam]
|
||||||
@@ -101,36 +104,41 @@ build_flags =
|
|||||||
|
|
||||||
; The original TBEAM board without the AXP power chip and a few other changes
|
; The original TBEAM board without the AXP power chip and a few other changes
|
||||||
; Note: I've heard reports this didn't work. Disabled until someone with a 0.7 can test and debug.
|
; Note: I've heard reports this didn't work. Disabled until someone with a 0.7 can test and debug.
|
||||||
;[env:tbeam0.7]
|
[env:tbeam0.7]
|
||||||
;extends = esp32_base
|
extends = esp32_base
|
||||||
;board = ttgo-t-beam
|
board = ttgo-t-beam
|
||||||
;build_flags =
|
build_flags =
|
||||||
; ${esp32_base.build_flags} -D TBEAM_V07
|
${esp32_base.build_flags} -D TBEAM_V07
|
||||||
|
|
||||||
[env:heltec]
|
[env:heltec]
|
||||||
;build_type = debug ; to make it possible to step through our jtag debugger
|
;build_type = debug ; to make it possible to step through our jtag debugger
|
||||||
extends = esp32_base
|
extends = esp32_base
|
||||||
board = heltec_wifi_lora_32_V2
|
board = heltec_wifi_lora_32_V2
|
||||||
|
|
||||||
[env:ttgo-lora32-v1]
|
[env:tlora-v1]
|
||||||
extends = esp32_base
|
extends = esp32_base
|
||||||
board = ttgo-lora32-v1
|
board = ttgo-lora32-v1
|
||||||
build_flags =
|
build_flags =
|
||||||
${esp32_base.build_flags} -D TTGO_LORA_V1
|
${esp32_base.build_flags} -D TLORA_V1
|
||||||
|
|
||||||
; note: the platformio definition for lora32-v2 seems stale, it is missing a pins_arduino.h file, therefore I don't think it works
|
; note: the platformio definition for lora32-v2 seems stale, it is missing a pins_arduino.h file, therefore I don't think it works
|
||||||
[env:ttgo-lora32-v2]
|
[env:tlora-v2]
|
||||||
extends = esp32_base
|
extends = esp32_base
|
||||||
board = ttgo-lora32-v1
|
board = ttgo-lora32-v1
|
||||||
build_flags =
|
build_flags =
|
||||||
${esp32_base.build_flags} -D TTGO_LORA_V2
|
${esp32_base.build_flags} -D TLORA_V2
|
||||||
|
|
||||||
|
[env:tlora-v2-1-1.6]
|
||||||
|
extends = esp32_base
|
||||||
|
board = ttgo-lora32-v1
|
||||||
|
build_flags =
|
||||||
|
${esp32_base.build_flags} -D TLORA_V2_1_16
|
||||||
|
|
||||||
; The Heltec Cubecell plus
|
; The Heltec Cubecell plus
|
||||||
; IMPORTANT NOTE: This target doesn't yet work and probably won't ever work. I'm keeping it around for now.
|
; IMPORTANT NOTE: This target doesn't yet work and probably won't ever work. I'm keeping it around for now.
|
||||||
; For more details see my post in the forum.
|
; For more details see my post in the forum.
|
||||||
[env:cubecellplus]
|
[env:cubecellplus]
|
||||||
platform = https://github.com/HelTecAutomation/platform-asrmicro650x.git ; we use top-of-tree because stable version has too many bugs - asrmicro650x
|
platform = https://github.com/HelTecAutomation/platform-asrmicro650x.git ; we use top-of-tree because stable version has too many bugs - asrmicro650x
|
||||||
framework = arduino
|
|
||||||
board = cubecell_board_plus
|
board = cubecell_board_plus
|
||||||
; FIXME, bug in cubecell arduino - they are supposed to set ARDUINO
|
; FIXME, bug in cubecell arduino - they are supposed to set ARDUINO
|
||||||
build_flags = ${env.build_flags} -DARDUINO=100 -Isrc/cubecell
|
build_flags = ${env.build_flags} -DARDUINO=100 -Isrc/cubecell
|
||||||
@@ -140,7 +148,6 @@ src_filter =
|
|||||||
; Common settings for NRF52 based targets
|
; Common settings for NRF52 based targets
|
||||||
[nrf52_base]
|
[nrf52_base]
|
||||||
platform = nordicnrf52
|
platform = nordicnrf52
|
||||||
framework = arduino
|
|
||||||
debug_tool = jlink
|
debug_tool = jlink
|
||||||
build_type = debug ; I'm debugging with ICE a lot now
|
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)
|
; note: liboberon provides the AES256 implementation for NRF52 (though not using the hardware acceleration of the NRF52840 - FIXME)
|
||||||
|
|||||||
+1
-1
Submodule proto updated: 72cbde93ff...0523977d1f
+126
@@ -0,0 +1,126 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <Arduino.h>
|
||||||
|
#include "Status.h"
|
||||||
|
#include "configuration.h"
|
||||||
|
|
||||||
|
namespace meshtastic {
|
||||||
|
|
||||||
|
/// 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 &);
|
||||||
|
|
||||||
|
void observe(Observable<const GPSStatus *> *source)
|
||||||
|
{
|
||||||
|
statusObserver.observe(source);
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
extern meshtastic::GPSStatus *gpsStatus;
|
||||||
@@ -0,0 +1,83 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <Arduino.h>
|
||||||
|
#include "Status.h"
|
||||||
|
#include "configuration.h"
|
||||||
|
|
||||||
|
namespace meshtastic {
|
||||||
|
|
||||||
|
/// Describes the state of the NodeDB system.
|
||||||
|
class NodeStatus : public Status
|
||||||
|
{
|
||||||
|
|
||||||
|
private:
|
||||||
|
CallbackObserver<NodeStatus, const NodeStatus *> statusObserver = CallbackObserver<NodeStatus, const NodeStatus *>(this, &NodeStatus::updateStatus);
|
||||||
|
|
||||||
|
uint8_t numOnline = 0;
|
||||||
|
uint8_t numTotal = 0;
|
||||||
|
|
||||||
|
uint8_t lastNumTotal = 0;
|
||||||
|
|
||||||
|
public:
|
||||||
|
bool forceUpdate = false;
|
||||||
|
|
||||||
|
NodeStatus() {
|
||||||
|
statusType = STATUS_TYPE_NODE;
|
||||||
|
}
|
||||||
|
NodeStatus( uint8_t numOnline, uint8_t numTotal, bool forceUpdate = false ) : Status()
|
||||||
|
{
|
||||||
|
this->forceUpdate = forceUpdate;
|
||||||
|
this->numOnline = numOnline;
|
||||||
|
this->numTotal = numTotal;
|
||||||
|
}
|
||||||
|
NodeStatus(const NodeStatus &);
|
||||||
|
NodeStatus &operator=(const NodeStatus &);
|
||||||
|
|
||||||
|
void observe(Observable<const NodeStatus *> *source)
|
||||||
|
{
|
||||||
|
statusObserver.observe(source);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t getNumOnline() const
|
||||||
|
{
|
||||||
|
return numOnline;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t getNumTotal() const
|
||||||
|
{
|
||||||
|
return numTotal;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t getLastNumTotal() const
|
||||||
|
{
|
||||||
|
return lastNumTotal;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool matches(const NodeStatus *newStatus) const
|
||||||
|
{
|
||||||
|
return (
|
||||||
|
newStatus->getNumOnline() != numOnline ||
|
||||||
|
newStatus->getNumTotal() != numTotal
|
||||||
|
);
|
||||||
|
}
|
||||||
|
int updateStatus(const NodeStatus *newStatus) {
|
||||||
|
// Only update the status if values have actually changed
|
||||||
|
lastNumTotal = numTotal;
|
||||||
|
bool isDirty;
|
||||||
|
{
|
||||||
|
isDirty = matches(newStatus);
|
||||||
|
initialized = true;
|
||||||
|
numOnline = newStatus->getNumOnline();
|
||||||
|
numTotal = newStatus->getNumTotal();
|
||||||
|
}
|
||||||
|
if(isDirty || newStatus->forceUpdate) {
|
||||||
|
DEBUG_MSG("Node status update: %d online, %d total\n", numOnline, numTotal);
|
||||||
|
onNewStatus.notifyObservers(this);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
extern meshtastic::NodeStatus *nodeStatus;
|
||||||
@@ -1,43 +0,0 @@
|
|||||||
#include "PeriodicTask.h"
|
|
||||||
#include "Periodic.h"
|
|
||||||
PeriodicScheduler periodicScheduler;
|
|
||||||
|
|
||||||
PeriodicTask::PeriodicTask(uint32_t initialPeriod) : period(initialPeriod) {}
|
|
||||||
|
|
||||||
void PeriodicTask::setup()
|
|
||||||
{
|
|
||||||
periodicScheduler.schedule(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// call this from loop
|
|
||||||
void PeriodicScheduler::loop()
|
|
||||||
{
|
|
||||||
meshtastic::LockGuard lg(&lock);
|
|
||||||
|
|
||||||
uint32_t now = millis();
|
|
||||||
for (auto t : tasks) {
|
|
||||||
if (t->period && (now - t->lastMsec) >= t->period) {
|
|
||||||
|
|
||||||
t->doTask();
|
|
||||||
t->lastMsec = now;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void PeriodicScheduler::schedule(PeriodicTask *t)
|
|
||||||
{
|
|
||||||
meshtastic::LockGuard lg(&lock);
|
|
||||||
tasks.insert(t);
|
|
||||||
}
|
|
||||||
|
|
||||||
void PeriodicScheduler::unschedule(PeriodicTask *t)
|
|
||||||
{
|
|
||||||
meshtastic::LockGuard lg(&lock);
|
|
||||||
tasks.erase(t);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Periodic::doTask()
|
|
||||||
{
|
|
||||||
uint32_t p = callback();
|
|
||||||
setPeriod(p);
|
|
||||||
}
|
|
||||||
@@ -1,85 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include "lock.h"
|
|
||||||
#include <Arduino.h>
|
|
||||||
#include <cstdint>
|
|
||||||
#include <unordered_set>
|
|
||||||
|
|
||||||
class PeriodicTask;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Runs all PeriodicTasks in the system.
|
|
||||||
*
|
|
||||||
* Currently called from main loop() but eventually should be its own thread blocked on a freertos timer.
|
|
||||||
*/
|
|
||||||
class PeriodicScheduler
|
|
||||||
{
|
|
||||||
friend class PeriodicTask;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This really should be some form of heap, and when the period gets changed on a task it should get
|
|
||||||
* rescheduled in that heap. Currently it is just a dumb array and everytime we run loop() we check
|
|
||||||
* _every_ tasks. If it was a heap we'd only have to check the first task.
|
|
||||||
*/
|
|
||||||
std::unordered_set<PeriodicTask *> tasks;
|
|
||||||
|
|
||||||
// Protects the above variables.
|
|
||||||
meshtastic::Lock lock;
|
|
||||||
|
|
||||||
public:
|
|
||||||
/// Run any next tasks which are due for execution
|
|
||||||
void loop();
|
|
||||||
|
|
||||||
private:
|
|
||||||
void schedule(PeriodicTask *t);
|
|
||||||
void unschedule(PeriodicTask *t);
|
|
||||||
};
|
|
||||||
|
|
||||||
extern PeriodicScheduler periodicScheduler;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A base class for tasks that want their doTask() method invoked periodically
|
|
||||||
*
|
|
||||||
* FIXME: currently just syntatic sugar for polling in loop (you must call .loop), but eventually
|
|
||||||
* generalize with the freertos scheduler so we can save lots of power by having everything either in
|
|
||||||
* something like this or triggered off of an irq.
|
|
||||||
*/
|
|
||||||
class PeriodicTask
|
|
||||||
{
|
|
||||||
friend class PeriodicScheduler;
|
|
||||||
|
|
||||||
uint32_t lastMsec = 0;
|
|
||||||
uint32_t period = 1; // call soon after creation
|
|
||||||
|
|
||||||
public:
|
|
||||||
virtual ~PeriodicTask() { periodicScheduler.unschedule(this); }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Constructor (will schedule with the global PeriodicScheduler)
|
|
||||||
*/
|
|
||||||
PeriodicTask(uint32_t initialPeriod = 1);
|
|
||||||
|
|
||||||
/** MUST be be called once at startup (but after threading is running - i.e. not from a constructor)
|
|
||||||
*/
|
|
||||||
void setup();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set a new period in msecs (can be called from doTask or elsewhere and the scheduler will cope)
|
|
||||||
* While zero this task is disabled and will not run
|
|
||||||
*/
|
|
||||||
void setPeriod(uint32_t p)
|
|
||||||
{
|
|
||||||
lastMsec = millis(); // reset starting from now
|
|
||||||
period = p;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint32_t getPeriod() const { return period; }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Syntatic sugar for suspending tasks
|
|
||||||
*/
|
|
||||||
void disable() { setPeriod(0); }
|
|
||||||
|
|
||||||
protected:
|
|
||||||
virtual void doTask() = 0;
|
|
||||||
};
|
|
||||||
+179
@@ -0,0 +1,179 @@
|
|||||||
|
#include "power.h"
|
||||||
|
#include "PowerFSM.h"
|
||||||
|
#include "main.h"
|
||||||
|
#include "utils.h"
|
||||||
|
#include "sleep.h"
|
||||||
|
|
||||||
|
#ifdef TBEAM_V10
|
||||||
|
|
||||||
|
// FIXME. nasty hack cleanup how we load axp192
|
||||||
|
#undef AXP192_SLAVE_ADDRESS
|
||||||
|
#include "axp20x.h"
|
||||||
|
AXP20X_Class axp;
|
||||||
|
bool pmu_irq = false;
|
||||||
|
|
||||||
|
Power *power;
|
||||||
|
|
||||||
|
bool Power::setup()
|
||||||
|
{
|
||||||
|
|
||||||
|
axp192Init();
|
||||||
|
concurrency::PeriodicTask::setup(); // We don't start our periodic task unless we actually found the device
|
||||||
|
setPeriod(1);
|
||||||
|
|
||||||
|
return axp192_found;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Reads power status to powerStatus singleton.
|
||||||
|
//
|
||||||
|
// TODO(girts): move this and other axp stuff to power.h/power.cpp.
|
||||||
|
void Power::readPowerStatus()
|
||||||
|
{
|
||||||
|
bool hasBattery = axp.isBatteryConnect();
|
||||||
|
int batteryVoltageMv = 0;
|
||||||
|
uint8_t batteryChargePercent = 0;
|
||||||
|
if (hasBattery) {
|
||||||
|
batteryVoltageMv = axp.getBattVoltage();
|
||||||
|
// If the AXP192 returns a valid battery percentage, use it
|
||||||
|
if (axp.getBattPercentage() >= 0) {
|
||||||
|
batteryChargePercent = axp.getBattPercentage();
|
||||||
|
} else {
|
||||||
|
// If the AXP192 returns a percentage less than 0, the feature is either not supported or there is an error
|
||||||
|
// In that case, we compute an estimate of the charge percent based on maximum and minimum voltages defined in power.h
|
||||||
|
batteryChargePercent = clamp((int)(((batteryVoltageMv - BAT_MILLIVOLTS_EMPTY) * 1e2) / (BAT_MILLIVOLTS_FULL - BAT_MILLIVOLTS_EMPTY)), 0, 100);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Notify any status instances that are observing us
|
||||||
|
const meshtastic::PowerStatus powerStatus = meshtastic::PowerStatus(hasBattery, axp.isVBUSPlug(), axp.isChargeing(), batteryVoltageMv, batteryChargePercent);
|
||||||
|
newStatus.notifyObservers(&powerStatus);
|
||||||
|
|
||||||
|
// If we have a battery at all and it is less than 10% full, force deep sleep
|
||||||
|
if (powerStatus.getHasBattery() && !powerStatus.getHasUSB() &&
|
||||||
|
axp.getBattVoltage() < MIN_BAT_MILLIVOLTS)
|
||||||
|
powerFSM.trigger(EVENT_LOW_BATTERY);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Power::doTask()
|
||||||
|
{
|
||||||
|
readPowerStatus();
|
||||||
|
|
||||||
|
// Only read once every 20 seconds once the power status for the app has been initialized
|
||||||
|
if(statusHandler && statusHandler->isInitialized())
|
||||||
|
setPeriod(1000 * 20);
|
||||||
|
}
|
||||||
|
#endif // TBEAM_V10
|
||||||
|
|
||||||
|
#ifdef AXP192_SLAVE_ADDRESS
|
||||||
|
/**
|
||||||
|
* Init the power manager chip
|
||||||
|
*
|
||||||
|
* axp192 power
|
||||||
|
DCDC1 0.7-3.5V @ 1200mA max -> OLED // If you turn this off you'll lose comms to the axp192 because the OLED and the axp192
|
||||||
|
share the same i2c bus, instead use ssd1306 sleep mode DCDC2 -> unused DCDC3 0.7-3.5V @ 700mA max -> ESP32 (keep this on!) LDO1
|
||||||
|
30mA -> charges GPS backup battery // charges the tiny J13 battery by the GPS to power the GPS ram (for a couple of days), can
|
||||||
|
not be turned off LDO2 200mA -> LORA LDO3 200mA -> GPS
|
||||||
|
*/
|
||||||
|
void Power::axp192Init()
|
||||||
|
{
|
||||||
|
if (axp192_found) {
|
||||||
|
if (!axp.begin(Wire, AXP192_SLAVE_ADDRESS)) {
|
||||||
|
DEBUG_MSG("AXP192 Begin PASS\n");
|
||||||
|
|
||||||
|
// axp.setChgLEDMode(LED_BLINK_4HZ);
|
||||||
|
DEBUG_MSG("DCDC1: %s\n", axp.isDCDC1Enable() ? "ENABLE" : "DISABLE");
|
||||||
|
DEBUG_MSG("DCDC2: %s\n", axp.isDCDC2Enable() ? "ENABLE" : "DISABLE");
|
||||||
|
DEBUG_MSG("LDO2: %s\n", axp.isLDO2Enable() ? "ENABLE" : "DISABLE");
|
||||||
|
DEBUG_MSG("LDO3: %s\n", axp.isLDO3Enable() ? "ENABLE" : "DISABLE");
|
||||||
|
DEBUG_MSG("DCDC3: %s\n", axp.isDCDC3Enable() ? "ENABLE" : "DISABLE");
|
||||||
|
DEBUG_MSG("Exten: %s\n", axp.isExtenEnable() ? "ENABLE" : "DISABLE");
|
||||||
|
DEBUG_MSG("----------------------------------------\n");
|
||||||
|
|
||||||
|
axp.setPowerOutPut(AXP192_LDO2, AXP202_ON); // LORA radio
|
||||||
|
axp.setPowerOutPut(AXP192_LDO3, AXP202_ON); // GPS main power
|
||||||
|
axp.setPowerOutPut(AXP192_DCDC2, AXP202_ON);
|
||||||
|
axp.setPowerOutPut(AXP192_EXTEN, AXP202_ON);
|
||||||
|
axp.setPowerOutPut(AXP192_DCDC1, AXP202_ON);
|
||||||
|
axp.setDCDC1Voltage(3300); // for the OLED power
|
||||||
|
|
||||||
|
DEBUG_MSG("DCDC1: %s\n", axp.isDCDC1Enable() ? "ENABLE" : "DISABLE");
|
||||||
|
DEBUG_MSG("DCDC2: %s\n", axp.isDCDC2Enable() ? "ENABLE" : "DISABLE");
|
||||||
|
DEBUG_MSG("LDO2: %s\n", axp.isLDO2Enable() ? "ENABLE" : "DISABLE");
|
||||||
|
DEBUG_MSG("LDO3: %s\n", axp.isLDO3Enable() ? "ENABLE" : "DISABLE");
|
||||||
|
DEBUG_MSG("DCDC3: %s\n", axp.isDCDC3Enable() ? "ENABLE" : "DISABLE");
|
||||||
|
DEBUG_MSG("Exten: %s\n", axp.isExtenEnable() ? "ENABLE" : "DISABLE");
|
||||||
|
|
||||||
|
axp.setChargeControlCur(AXP1XX_CHARGE_CUR_1320MA); // actual limit (in HW) on the tbeam is 450mA
|
||||||
|
#if 0
|
||||||
|
|
||||||
|
// Not connected
|
||||||
|
//val = 0xfc;
|
||||||
|
//axp._writeByte(AXP202_VHTF_CHGSET, 1, &val); // Set temperature protection
|
||||||
|
|
||||||
|
//not used
|
||||||
|
//val = 0x46;
|
||||||
|
//axp._writeByte(AXP202_OFF_CTL, 1, &val); // enable bat detection
|
||||||
|
#endif
|
||||||
|
axp.debugCharging();
|
||||||
|
|
||||||
|
#ifdef PMU_IRQ
|
||||||
|
pinMode(PMU_IRQ, INPUT);
|
||||||
|
attachInterrupt(
|
||||||
|
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,
|
||||||
|
1);
|
||||||
|
|
||||||
|
axp.clearIRQ();
|
||||||
|
#endif
|
||||||
|
readPowerStatus();
|
||||||
|
} else {
|
||||||
|
DEBUG_MSG("AXP192 Begin FAIL\n");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
DEBUG_MSG("AXP192 not found\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void Power::loop()
|
||||||
|
{
|
||||||
|
|
||||||
|
#ifdef PMU_IRQ
|
||||||
|
if (pmu_irq) {
|
||||||
|
pmu_irq = false;
|
||||||
|
axp.readIRQ();
|
||||||
|
|
||||||
|
DEBUG_MSG("pmu irq!\n");
|
||||||
|
|
||||||
|
if (axp.isChargingIRQ()) {
|
||||||
|
DEBUG_MSG("Battery start charging\n");
|
||||||
|
}
|
||||||
|
if (axp.isChargingDoneIRQ()) {
|
||||||
|
DEBUG_MSG("Battery fully charged\n");
|
||||||
|
}
|
||||||
|
if (axp.isVbusRemoveIRQ()) {
|
||||||
|
DEBUG_MSG("USB unplugged\n");
|
||||||
|
}
|
||||||
|
if (axp.isVbusPlugInIRQ()) {
|
||||||
|
DEBUG_MSG("USB plugged In\n");
|
||||||
|
}
|
||||||
|
if (axp.isBattPlugInIRQ()) {
|
||||||
|
DEBUG_MSG("Battery inserted\n");
|
||||||
|
}
|
||||||
|
if (axp.isBattRemoveIRQ()) {
|
||||||
|
DEBUG_MSG("Battery removed\n");
|
||||||
|
}
|
||||||
|
if (axp.isPEKShortPressIRQ()) {
|
||||||
|
DEBUG_MSG("PEK short button press\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
readPowerStatus();
|
||||||
|
axp.clearIRQ();
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // T_BEAM_V10
|
||||||
|
|
||||||
|
}
|
||||||
+4
-3
@@ -5,9 +5,10 @@
|
|||||||
#include "NodeDB.h"
|
#include "NodeDB.h"
|
||||||
#include "configuration.h"
|
#include "configuration.h"
|
||||||
#include "main.h"
|
#include "main.h"
|
||||||
#include "screen.h"
|
#include "graphics/Screen.h"
|
||||||
#include "sleep.h"
|
#include "sleep.h"
|
||||||
#include "target_specific.h"
|
#include "target_specific.h"
|
||||||
|
#include "timing.h"
|
||||||
|
|
||||||
static void sdsEnter()
|
static void sdsEnter()
|
||||||
{
|
{
|
||||||
@@ -15,7 +16,7 @@ static void sdsEnter()
|
|||||||
|
|
||||||
// Don't deepsleep if we have USB power or if the user as pressed a button recently
|
// 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
|
// !isUSBPowered <- doesn't work yet because the axp192 isn't letting the battery fully charge when we are awake - FIXME
|
||||||
if (millis() - lastPressMs > radioConfig.preferences.mesh_sds_timeout_secs)
|
if (timing::millis() - lastPressMs > radioConfig.preferences.mesh_sds_timeout_secs)
|
||||||
{
|
{
|
||||||
doDeepSleep(radioConfig.preferences.sds_secs);
|
doDeepSleep(radioConfig.preferences.sds_secs);
|
||||||
}
|
}
|
||||||
@@ -130,7 +131,7 @@ static void onEnter()
|
|||||||
|
|
||||||
static uint32_t lastPingMs;
|
static uint32_t lastPingMs;
|
||||||
|
|
||||||
uint32_t now = millis();
|
uint32_t now = timing::millis();
|
||||||
|
|
||||||
if (now - lastPingMs > 30 * 1000) { // if more than a minute since our last press, ask other nodes to update their state
|
if (now - lastPingMs > 30 * 1000) { // if more than a minute since our last press, ask other nodes to update their state
|
||||||
if (displayedNodeNum)
|
if (displayedNodeNum)
|
||||||
|
|||||||
@@ -0,0 +1,103 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <Arduino.h>
|
||||||
|
#include "Status.h"
|
||||||
|
#include "configuration.h"
|
||||||
|
|
||||||
|
namespace meshtastic {
|
||||||
|
|
||||||
|
/// Describes the state of the GPS system.
|
||||||
|
class PowerStatus : public Status
|
||||||
|
{
|
||||||
|
|
||||||
|
private:
|
||||||
|
CallbackObserver<PowerStatus, const PowerStatus *> statusObserver = CallbackObserver<PowerStatus, const PowerStatus *>(this, &PowerStatus::updateStatus);
|
||||||
|
|
||||||
|
/// Whether we have a battery connected
|
||||||
|
bool hasBattery;
|
||||||
|
/// Battery voltage in mV, valid if haveBattery is true
|
||||||
|
int batteryVoltageMv;
|
||||||
|
/// Battery charge percentage, either read directly or estimated
|
||||||
|
uint8_t batteryChargePercent;
|
||||||
|
/// Whether USB is connected
|
||||||
|
bool hasUSB;
|
||||||
|
/// Whether we are charging the battery
|
||||||
|
bool isCharging;
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
PowerStatus() {
|
||||||
|
statusType = STATUS_TYPE_POWER;
|
||||||
|
}
|
||||||
|
PowerStatus( bool hasBattery, bool hasUSB, bool isCharging, int batteryVoltageMv, uint8_t batteryChargePercent ) : Status()
|
||||||
|
{
|
||||||
|
this->hasBattery = hasBattery;
|
||||||
|
this->hasUSB = hasUSB;
|
||||||
|
this->isCharging = isCharging;
|
||||||
|
this->batteryVoltageMv = batteryVoltageMv;
|
||||||
|
this->batteryChargePercent = batteryChargePercent;
|
||||||
|
}
|
||||||
|
PowerStatus(const PowerStatus &);
|
||||||
|
PowerStatus &operator=(const PowerStatus &);
|
||||||
|
|
||||||
|
void observe(Observable<const PowerStatus *> *source)
|
||||||
|
{
|
||||||
|
statusObserver.observe(source);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool getHasBattery() const
|
||||||
|
{
|
||||||
|
return hasBattery;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool getHasUSB() const
|
||||||
|
{
|
||||||
|
return hasUSB;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool getIsCharging() const
|
||||||
|
{
|
||||||
|
return isCharging;
|
||||||
|
}
|
||||||
|
|
||||||
|
int getBatteryVoltageMv() const
|
||||||
|
{
|
||||||
|
return batteryVoltageMv;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t getBatteryChargePercent() const
|
||||||
|
{
|
||||||
|
return batteryChargePercent;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool matches(const PowerStatus *newStatus) const
|
||||||
|
{
|
||||||
|
return (
|
||||||
|
newStatus->getHasBattery() != hasBattery ||
|
||||||
|
newStatus->getHasUSB() != hasUSB ||
|
||||||
|
newStatus->getBatteryVoltageMv() != batteryVoltageMv
|
||||||
|
);
|
||||||
|
}
|
||||||
|
int updateStatus(const PowerStatus *newStatus) {
|
||||||
|
// Only update the status if values have actually changed
|
||||||
|
bool isDirty;
|
||||||
|
{
|
||||||
|
isDirty = matches(newStatus);
|
||||||
|
initialized = true;
|
||||||
|
hasBattery = newStatus->getHasBattery();
|
||||||
|
batteryVoltageMv = newStatus->getBatteryVoltageMv();
|
||||||
|
batteryChargePercent = newStatus->getBatteryChargePercent();
|
||||||
|
hasUSB = newStatus->getHasUSB();
|
||||||
|
isCharging = newStatus->getIsCharging();
|
||||||
|
}
|
||||||
|
if(isDirty) {
|
||||||
|
DEBUG_MSG("Battery %dmV %d%%\n", batteryVoltageMv, batteryChargePercent);
|
||||||
|
onNewStatus.notifyObservers(this);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
extern meshtastic::PowerStatus *powerStatus;
|
||||||
@@ -0,0 +1,72 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "Observer.h"
|
||||||
|
|
||||||
|
// Constants for the various status types, so we can tell subclass instances apart
|
||||||
|
#define STATUS_TYPE_BASE 0
|
||||||
|
#define STATUS_TYPE_POWER 1
|
||||||
|
#define STATUS_TYPE_GPS 2
|
||||||
|
#define STATUS_TYPE_NODE 3
|
||||||
|
|
||||||
|
|
||||||
|
namespace meshtastic
|
||||||
|
{
|
||||||
|
|
||||||
|
// A base class for observable status
|
||||||
|
class Status
|
||||||
|
{
|
||||||
|
protected:
|
||||||
|
// Allows us to observe an Observable
|
||||||
|
CallbackObserver<Status, const Status *> statusObserver = CallbackObserver<Status, const Status *>(this, &Status::updateStatus);
|
||||||
|
bool initialized = false;
|
||||||
|
// Workaround for no typeid support
|
||||||
|
int statusType;
|
||||||
|
|
||||||
|
public:
|
||||||
|
// Allows us to generate observable events
|
||||||
|
Observable<const Status *> onNewStatus;
|
||||||
|
|
||||||
|
// Enable polymorphism ?
|
||||||
|
virtual ~Status() = default;
|
||||||
|
|
||||||
|
Status() {
|
||||||
|
if (!statusType)
|
||||||
|
{
|
||||||
|
statusType = STATUS_TYPE_BASE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prevent object copy/move
|
||||||
|
Status(const Status &) = delete;
|
||||||
|
Status &operator=(const Status &) = delete;
|
||||||
|
|
||||||
|
// Start observing a source of data
|
||||||
|
void observe(Observable<const Status *> *source)
|
||||||
|
{
|
||||||
|
statusObserver.observe(source);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Determines whether or not existing data matches the data in another Status instance
|
||||||
|
bool matches(const Status *otherStatus) const
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isInitialized() const
|
||||||
|
{
|
||||||
|
return initialized;
|
||||||
|
}
|
||||||
|
|
||||||
|
int getStatusType() const
|
||||||
|
{
|
||||||
|
return statusType;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Called when the Observable we're observing generates a new notification
|
||||||
|
int updateStatus(const Status *newStatus)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
};
|
||||||
@@ -1,49 +0,0 @@
|
|||||||
#include "WorkerThread.h"
|
|
||||||
#include "debug.h"
|
|
||||||
#include <assert.h>
|
|
||||||
|
|
||||||
#ifdef configUSE_PREEMPTION
|
|
||||||
|
|
||||||
void Thread::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 WorkerThread::doRun()
|
|
||||||
{
|
|
||||||
while (!wantExit) {
|
|
||||||
block();
|
|
||||||
|
|
||||||
#ifdef DEBUG_STACK
|
|
||||||
static uint32_t lastPrint = 0;
|
|
||||||
if (millis() - lastPrint > 10 * 1000L) {
|
|
||||||
lastPrint = millis();
|
|
||||||
meshtastic::printThreadInfo("net");
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
loop();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Notify this thread so it can run
|
|
||||||
*/
|
|
||||||
void NotifiedWorkerThread::notify(uint32_t v, eNotifyAction action)
|
|
||||||
{
|
|
||||||
xTaskNotify(taskHandle, v, action);
|
|
||||||
}
|
|
||||||
|
|
||||||
void NotifiedWorkerThread::block()
|
|
||||||
{
|
|
||||||
xTaskNotifyWait(0, // don't clear notification on entry
|
|
||||||
clearOnRead, ¬ification, portMAX_DELAY); // Wait forever
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
@@ -1,96 +0,0 @@
|
|||||||
#include <Arduino.h>
|
|
||||||
#include "freertosinc.h"
|
|
||||||
|
|
||||||
#ifdef HAS_FREE_RTOS
|
|
||||||
|
|
||||||
class Thread
|
|
||||||
{
|
|
||||||
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;
|
|
||||||
|
|
||||||
private:
|
|
||||||
static void callRun(void *_this);
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This wraps threading (FreeRTOS for now) with a blocking API intended for efficiently converting onlyschool arduino loop() code.
|
|
||||||
*
|
|
||||||
* Use as a mixin base class for the classes you want to convert.
|
|
||||||
*
|
|
||||||
* https://www.freertos.org/RTOS_Task_Notification_As_Mailbox.html
|
|
||||||
*/
|
|
||||||
class WorkerThread : public Thread
|
|
||||||
{
|
|
||||||
protected:
|
|
||||||
/**
|
|
||||||
* A method that should block execution - either waiting ona queue/mutex or a "task notification"
|
|
||||||
*/
|
|
||||||
virtual void block() = 0;
|
|
||||||
|
|
||||||
virtual void loop() = 0;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The method that will be called when start is called.
|
|
||||||
*/
|
|
||||||
virtual void doRun();
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A worker thread that waits on a freertos notification
|
|
||||||
*/
|
|
||||||
class NotifiedWorkerThread : public WorkerThread
|
|
||||||
{
|
|
||||||
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:
|
|
||||||
/**
|
|
||||||
* 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();
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif
|
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
/**
|
||||||
|
* @brief This class enables on the fly software and hardware setup.
|
||||||
|
* It will contain all command messages to change internal settings.
|
||||||
|
*/
|
||||||
|
|
||||||
|
enum class Cmd {
|
||||||
|
INVALID,
|
||||||
|
SET_ON,
|
||||||
|
SET_OFF,
|
||||||
|
ON_PRESS,
|
||||||
|
START_BLUETOOTH_PIN_SCREEN,
|
||||||
|
STOP_BLUETOOTH_PIN_SCREEN,
|
||||||
|
STOP_BOOT_SCREEN,
|
||||||
|
PRINT,
|
||||||
|
};
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
#include "Lock.h"
|
||||||
|
#include <cassert>
|
||||||
|
|
||||||
|
namespace concurrency {
|
||||||
|
|
||||||
|
Lock::Lock()
|
||||||
|
{
|
||||||
|
handle = xSemaphoreCreateBinary();
|
||||||
|
assert(handle);
|
||||||
|
assert(xSemaphoreGive(handle));
|
||||||
|
}
|
||||||
|
|
||||||
|
void Lock::lock()
|
||||||
|
{
|
||||||
|
assert(xSemaphoreTake(handle, portMAX_DELAY));
|
||||||
|
}
|
||||||
|
|
||||||
|
void Lock::unlock()
|
||||||
|
{
|
||||||
|
assert(xSemaphoreGive(handle));
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace concurrency
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "../freertosinc.h"
|
||||||
|
|
||||||
|
namespace concurrency {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Simple wrapper around FreeRTOS API for implementing a mutex lock
|
||||||
|
*/
|
||||||
|
class Lock
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
Lock();
|
||||||
|
|
||||||
|
Lock(const Lock &) = delete;
|
||||||
|
Lock &operator=(const Lock &) = delete;
|
||||||
|
|
||||||
|
/// Locks the lock.
|
||||||
|
//
|
||||||
|
// Must not be called from an ISR.
|
||||||
|
void lock();
|
||||||
|
|
||||||
|
// Unlocks the lock.
|
||||||
|
//
|
||||||
|
// Must not be called from an ISR.
|
||||||
|
void unlock();
|
||||||
|
|
||||||
|
private:
|
||||||
|
SemaphoreHandle_t handle;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace concurrency
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
#include "LockGuard.h"
|
||||||
|
|
||||||
|
namespace concurrency {
|
||||||
|
|
||||||
|
LockGuard::LockGuard(Lock *lock) : lock(lock)
|
||||||
|
{
|
||||||
|
lock->lock();
|
||||||
|
}
|
||||||
|
|
||||||
|
LockGuard::~LockGuard()
|
||||||
|
{
|
||||||
|
lock->unlock();
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace concurrency
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "Lock.h"
|
||||||
|
|
||||||
|
namespace concurrency {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief RAII lock guard
|
||||||
|
*/
|
||||||
|
class LockGuard
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
LockGuard(Lock *lock);
|
||||||
|
~LockGuard();
|
||||||
|
|
||||||
|
LockGuard(const LockGuard &) = delete;
|
||||||
|
LockGuard &operator=(const LockGuard &) = delete;
|
||||||
|
|
||||||
|
private:
|
||||||
|
Lock *lock;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace concurrency
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
#include "NotifiedWorkerThread.h"
|
||||||
|
|
||||||
|
namespace concurrency {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Notify this thread so it can run
|
||||||
|
*/
|
||||||
|
void NotifiedWorkerThread::notify(uint32_t v, eNotifyAction action)
|
||||||
|
{
|
||||||
|
xTaskNotify(taskHandle, v, action);
|
||||||
|
}
|
||||||
|
|
||||||
|
void NotifiedWorkerThread::block()
|
||||||
|
{
|
||||||
|
xTaskNotifyWait(0, // don't clear notification on entry
|
||||||
|
clearOnRead, ¬ification, portMAX_DELAY); // Wait forever
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace concurrency
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "WorkerThread.h"
|
||||||
|
|
||||||
|
namespace concurrency {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief A worker thread that waits on a freertos notification
|
||||||
|
*/
|
||||||
|
class NotifiedWorkerThread : public WorkerThread
|
||||||
|
{
|
||||||
|
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:
|
||||||
|
/**
|
||||||
|
* 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();
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace concurrency
|
||||||
@@ -1,12 +1,12 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "PeriodicTask.h"
|
#include "PeriodicTask.h"
|
||||||
#include <Arduino.h>
|
|
||||||
|
namespace concurrency {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Periodically invoke a callback.
|
* @brief Periodically invoke a callback. This just provides C-style callback conventions
|
||||||
*
|
* rather than a virtual function - FIXME, remove?
|
||||||
* This just provides C style callback conventions rather than a virtual function - FIXME, remove?
|
|
||||||
*/
|
*/
|
||||||
class Periodic : public PeriodicTask
|
class Periodic : public PeriodicTask
|
||||||
{
|
{
|
||||||
@@ -17,5 +17,10 @@ class Periodic : public PeriodicTask
|
|||||||
Periodic(uint32_t (*_callback)()) : callback(_callback) {}
|
Periodic(uint32_t (*_callback)()) : callback(_callback) {}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void doTask();
|
void doTask() {
|
||||||
|
uint32_t p = callback();
|
||||||
|
setPeriod(p);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
} // namespace concurrency
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
#include "PeriodicScheduler.h"
|
||||||
|
#include "PeriodicTask.h"
|
||||||
|
#include "LockGuard.h"
|
||||||
|
#include "../timing.h"
|
||||||
|
|
||||||
|
namespace concurrency {
|
||||||
|
|
||||||
|
/// call this from loop
|
||||||
|
void PeriodicScheduler::loop()
|
||||||
|
{
|
||||||
|
LockGuard lg(&lock);
|
||||||
|
|
||||||
|
uint32_t now = timing::millis();
|
||||||
|
for (auto t : tasks) {
|
||||||
|
if (t->period && (now - t->lastMsec) >= t->period) {
|
||||||
|
|
||||||
|
t->doTask();
|
||||||
|
t->lastMsec = now;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void PeriodicScheduler::schedule(PeriodicTask *t)
|
||||||
|
{
|
||||||
|
LockGuard lg(&lock);
|
||||||
|
tasks.insert(t);
|
||||||
|
}
|
||||||
|
|
||||||
|
void PeriodicScheduler::unschedule(PeriodicTask *t)
|
||||||
|
{
|
||||||
|
LockGuard lg(&lock);
|
||||||
|
tasks.erase(t);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace concurrency
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "Lock.h"
|
||||||
|
#include <cstdint>
|
||||||
|
#include <unordered_set>
|
||||||
|
|
||||||
|
namespace concurrency {
|
||||||
|
|
||||||
|
class PeriodicTask;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Runs all PeriodicTasks in the system. Currently called from main loop()
|
||||||
|
* but eventually should be its own thread blocked on a freertos timer.
|
||||||
|
*/
|
||||||
|
class PeriodicScheduler
|
||||||
|
{
|
||||||
|
friend class PeriodicTask;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This really should be some form of heap, and when the period gets changed on a task it should get
|
||||||
|
* rescheduled in that heap. Currently it is just a dumb array and everytime we run loop() we check
|
||||||
|
* _every_ tasks. If it was a heap we'd only have to check the first task.
|
||||||
|
*/
|
||||||
|
std::unordered_set<PeriodicTask *> tasks;
|
||||||
|
|
||||||
|
// Protects the above variables.
|
||||||
|
Lock lock;
|
||||||
|
|
||||||
|
public:
|
||||||
|
/// Run any next tasks which are due for execution
|
||||||
|
void loop();
|
||||||
|
|
||||||
|
private:
|
||||||
|
void schedule(PeriodicTask *t);
|
||||||
|
void unschedule(PeriodicTask *t);
|
||||||
|
};
|
||||||
|
|
||||||
|
extern PeriodicScheduler periodicScheduler;
|
||||||
|
|
||||||
|
} // namespace concurrency
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
#include "PeriodicTask.h"
|
||||||
|
#include "Periodic.h"
|
||||||
|
#include "LockGuard.h"
|
||||||
|
|
||||||
|
namespace concurrency {
|
||||||
|
|
||||||
|
PeriodicScheduler periodicScheduler;
|
||||||
|
|
||||||
|
PeriodicTask::PeriodicTask(uint32_t initialPeriod) : period(initialPeriod) {}
|
||||||
|
|
||||||
|
void PeriodicTask::setup()
|
||||||
|
{
|
||||||
|
periodicScheduler.schedule(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace concurrency
|
||||||
@@ -0,0 +1,56 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "PeriodicScheduler.h"
|
||||||
|
#include "timing.h"
|
||||||
|
|
||||||
|
namespace concurrency {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief A base class for tasks that want their doTask() method invoked periodically
|
||||||
|
*
|
||||||
|
* @todo currently just syntatic sugar for polling in loop (you must call .loop), but eventually
|
||||||
|
* generalize with the freertos scheduler so we can save lots of power by having everything either in
|
||||||
|
* something like this or triggered off of an irq.
|
||||||
|
*/
|
||||||
|
class PeriodicTask
|
||||||
|
{
|
||||||
|
friend class PeriodicScheduler;
|
||||||
|
|
||||||
|
uint32_t lastMsec = 0;
|
||||||
|
uint32_t period = 1; // call soon after creation
|
||||||
|
|
||||||
|
public:
|
||||||
|
virtual ~PeriodicTask() { periodicScheduler.unschedule(this); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor (will schedule with the global PeriodicScheduler)
|
||||||
|
*/
|
||||||
|
PeriodicTask(uint32_t initialPeriod = 1);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* MUST be be called once at startup (but after threading is running - i.e. not from a constructor)
|
||||||
|
*/
|
||||||
|
void setup();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set a new period in msecs (can be called from doTask or elsewhere and the scheduler will cope)
|
||||||
|
* While zero this task is disabled and will not run
|
||||||
|
*/
|
||||||
|
void setPeriod(uint32_t p)
|
||||||
|
{
|
||||||
|
lastMsec = timing::millis(); // reset starting from now
|
||||||
|
period = p;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t getPeriod() const { return period; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Syntatic sugar for suspending tasks
|
||||||
|
*/
|
||||||
|
void disable() { setPeriod(0); }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual void doTask() = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace concurrency
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
#include "Thread.h"
|
||||||
|
#include "timing.h"
|
||||||
|
|
||||||
|
namespace concurrency {
|
||||||
|
|
||||||
|
void Thread::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();
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace concurrency
|
||||||
@@ -0,0 +1,56 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "freertosinc.h"
|
||||||
|
#include "esp_task_wdt.h"
|
||||||
|
|
||||||
|
namespace concurrency {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Base threading
|
||||||
|
*/
|
||||||
|
class Thread
|
||||||
|
{
|
||||||
|
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() { esp_task_wdt_reset(); }
|
||||||
|
void startWatchdog()
|
||||||
|
{
|
||||||
|
auto r = esp_task_wdt_add(taskHandle);
|
||||||
|
assert(r == ESP_OK);
|
||||||
|
}
|
||||||
|
void stopWatchdog()
|
||||||
|
{
|
||||||
|
auto r = esp_task_wdt_delete(taskHandle);
|
||||||
|
assert(r == ESP_OK);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
static void callRun(void *_this);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
} // namespace concurrency
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
#include "WorkerThread.h"
|
||||||
|
#include "timing.h"
|
||||||
|
|
||||||
|
namespace concurrency {
|
||||||
|
|
||||||
|
void WorkerThread::doRun()
|
||||||
|
{
|
||||||
|
startWatchdog();
|
||||||
|
|
||||||
|
while (!wantExit) {
|
||||||
|
stopWatchdog();
|
||||||
|
block();
|
||||||
|
startWatchdog();
|
||||||
|
|
||||||
|
// no need - startWatchdog is guaranteed to give us one full watchdog interval
|
||||||
|
// serviceWatchdog(); // Let our loop worker have one full watchdog interval (at least) to run
|
||||||
|
|
||||||
|
#ifdef DEBUG_STACK
|
||||||
|
static uint32_t lastPrint = 0;
|
||||||
|
if (timing::millis() - lastPrint > 10 * 1000L) {
|
||||||
|
lastPrint = timing::millis();
|
||||||
|
meshtastic::printThreadInfo("net");
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
loop();
|
||||||
|
}
|
||||||
|
|
||||||
|
stopWatchdog();
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace concurrency
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "Thread.h"
|
||||||
|
|
||||||
|
namespace concurrency {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief This wraps threading (FreeRTOS for now) with a blocking API intended for efficiently converting
|
||||||
|
* old-school arduino loop() code. Use as a mixin base class for the classes you want to convert.
|
||||||
|
*
|
||||||
|
* @link https://www.freertos.org/RTOS_Task_Notification_As_Mailbox.html
|
||||||
|
*/
|
||||||
|
class WorkerThread : public Thread
|
||||||
|
{
|
||||||
|
protected:
|
||||||
|
/**
|
||||||
|
* A method that should block execution - either waiting ona queue/mutex or a "task notification"
|
||||||
|
*/
|
||||||
|
virtual void block() = 0;
|
||||||
|
|
||||||
|
virtual void loop() = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The method that will be called when start is called.
|
||||||
|
*/
|
||||||
|
virtual void doRun();
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace concurrency
|
||||||
+39
-8
@@ -144,7 +144,8 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|||||||
#define I2C_SDA 21
|
#define I2C_SDA 21
|
||||||
#define I2C_SCL 22
|
#define I2C_SCL 22
|
||||||
|
|
||||||
#define BUTTON_PIN 38
|
#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
|
||||||
|
|
||||||
#ifndef USE_JTAG
|
#ifndef USE_JTAG
|
||||||
#define RESET_GPIO 14
|
#define RESET_GPIO 14
|
||||||
@@ -210,9 +211,13 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|||||||
#define RF95_IRQ_GPIO 26
|
#define RF95_IRQ_GPIO 26
|
||||||
#define DIO1_GPIO 35 // DIO1 & DIO2 are not currently used, but they must be assigned to a pin number
|
#define DIO1_GPIO 35 // DIO1 & DIO2 are not currently used, but they must be assigned to a pin number
|
||||||
#define DIO2_GPIO 34 // DIO1 & DIO2 are not currently used, but they must be assigned to a pin number
|
#define DIO2_GPIO 34 // DIO1 & DIO2 are not currently used, but they must be assigned to a pin number
|
||||||
#elif defined(TTGO_LORA_V1)
|
#elif defined(TLORA_V1)
|
||||||
// This string must exactly match the case used in release file names or the android updater won't work
|
// This string must exactly match the case used in release file names or the android updater won't work
|
||||||
#define HW_VENDOR "ttgo-lora32-v1"
|
#define HW_VENDOR "tlora-v1"
|
||||||
|
#undef GPS_RX_PIN
|
||||||
|
#undef GPS_TX_PIN
|
||||||
|
#define GPS_RX_PIN 36
|
||||||
|
#define GPS_TX_PIN 37
|
||||||
|
|
||||||
#define I2C_SDA 4 // I2C pins for this board
|
#define I2C_SDA 4 // I2C pins for this board
|
||||||
#define I2C_SCL 15
|
#define I2C_SCL 15
|
||||||
@@ -227,9 +232,14 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|||||||
#define RF95_IRQ_GPIO 26 // IRQ line for the LORA radio
|
#define RF95_IRQ_GPIO 26 // IRQ line for the LORA radio
|
||||||
#define DIO1_GPIO 35 // DIO1 & DIO2 are not currently used, but they must be assigned to a pin number
|
#define DIO1_GPIO 35 // DIO1 & DIO2 are not currently used, but they must be assigned to a pin number
|
||||||
#define DIO2_GPIO 34 // DIO1 & DIO2 are not currently used, but they must be assigned to a pin number
|
#define DIO2_GPIO 34 // DIO1 & DIO2 are not currently used, but they must be assigned to a pin number
|
||||||
#elif defined(TTGO_LORA_V2)
|
#elif defined(TLORA_V2)
|
||||||
// This string must exactly match the case used in release file names or the android updater won't work
|
// This string must exactly match the case used in release file names or the android updater won't work
|
||||||
#define HW_VENDOR "ttgo-lora32-v2"
|
#define HW_VENDOR "tlora-v2"
|
||||||
|
|
||||||
|
#undef GPS_RX_PIN
|
||||||
|
#undef GPS_TX_PIN
|
||||||
|
#define GPS_RX_PIN 36
|
||||||
|
#define GPS_TX_PIN 13 // per @eugene
|
||||||
|
|
||||||
#define I2C_SDA 21 // I2C pins for this board
|
#define I2C_SDA 21 // I2C pins for this board
|
||||||
#define I2C_SCL 22
|
#define I2C_SCL 22
|
||||||
@@ -238,14 +248,35 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|||||||
|
|
||||||
#define VEXT_ENABLE 21 // active low, powers the oled display and the lora antenna boost
|
#define VEXT_ENABLE 21 // active low, powers the oled display and the lora antenna boost
|
||||||
#define LED_PIN 25 // If defined we will blink this LED
|
#define LED_PIN 25 // If defined we will blink this LED
|
||||||
#define BUTTON_PIN \
|
#define BUTTON_PIN 0 // If defined, this will be used for user button presses, if your board doesn't have a physical switch, you can wire one between this pin and ground
|
||||||
0 // If defined, this will be used for user button presses, if your board doesn't have a physical switch, you can wire one
|
|
||||||
// between this pin and ground
|
|
||||||
|
|
||||||
#define RESET_GPIO 14 // If defined, this pin will be used to reset the LORA radio
|
#define RESET_GPIO 14 // If defined, this pin will be used to reset the LORA radio
|
||||||
#define RF95_IRQ_GPIO 26 // IRQ line for the LORA radio
|
#define RF95_IRQ_GPIO 26 // IRQ line for the LORA radio
|
||||||
#define DIO1_GPIO 35 // DIO1 & DIO2 are not currently used, but they must be assigned to a pin number
|
#define DIO1_GPIO 35 // DIO1 & DIO2 are not currently used, but they must be assigned to a pin number
|
||||||
#define DIO2_GPIO 34 // DIO1 & DIO2 are not currently used, but they must be assigned to a pin number
|
#define DIO2_GPIO 34 // DIO1 & DIO2 are not currently used, but they must be assigned to a pin number
|
||||||
|
|
||||||
|
#elif defined(TLORA_V2_1_16)
|
||||||
|
// This string must exactly match the case used in release file names or the android updater won't work
|
||||||
|
#define HW_VENDOR "tlora-v2-1-1.6"
|
||||||
|
|
||||||
|
#undef GPS_RX_PIN
|
||||||
|
#undef GPS_TX_PIN
|
||||||
|
#define GPS_RX_PIN 36
|
||||||
|
#define GPS_TX_PIN 39
|
||||||
|
|
||||||
|
#define I2C_SDA 21 // I2C pins for this board
|
||||||
|
#define I2C_SCL 22
|
||||||
|
|
||||||
|
#define RESET_OLED 16 // If defined, this pin will be used to reset the display controller
|
||||||
|
|
||||||
|
#define VEXT_ENABLE 21 // active low, powers the oled display and the lora antenna boost
|
||||||
|
#define LED_PIN 25 // If defined we will blink this LED
|
||||||
|
#define BUTTON_PIN 12 // If defined, this will be used for user button presses, if your board doesn't have a physical switch, you can wire one between this pin and ground
|
||||||
|
|
||||||
|
#define RESET_GPIO 23 // If defined, this pin will be used to reset the LORA radio
|
||||||
|
#define RF95_IRQ_GPIO 26 // IRQ line for the LORA radio
|
||||||
|
#define DIO1_GPIO 35 // DIO1 & DIO2 are not currently used, but they must be assigned to a pin number
|
||||||
|
#define DIO2_GPIO 34 // DIO1 & DIO2 are not currently used, but they must be assigned to a pin number
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef ARDUINO_NRF52840_PCA10056
|
#ifdef ARDUINO_NRF52840_PCA10056
|
||||||
|
|||||||
@@ -1,20 +0,0 @@
|
|||||||
#include "debug.h"
|
|
||||||
|
|
||||||
#include <cstdint>
|
|
||||||
|
|
||||||
#include "freertosinc.h"
|
|
||||||
#include "configuration.h"
|
|
||||||
|
|
||||||
namespace meshtastic
|
|
||||||
{
|
|
||||||
|
|
||||||
void printThreadInfo(const char *extra)
|
|
||||||
{
|
|
||||||
#ifndef NO_ESP32
|
|
||||||
uint32_t taskHandle = reinterpret_cast<uint32_t>(xTaskGetCurrentTaskHandle());
|
|
||||||
DEBUG_MSG("printThreadInfo(%s) task: %" PRIx32 " core id: %u min free stack: %u\n", extra, taskHandle, xPortGetCoreID(),
|
|
||||||
uxTaskGetStackHighWaterMark(nullptr));
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace meshtastic
|
|
||||||
-10
@@ -1,10 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
namespace meshtastic
|
|
||||||
{
|
|
||||||
|
|
||||||
/// Dumps out which core we are running on, and min level of remaining stack
|
|
||||||
/// seen.
|
|
||||||
void printThreadInfo(const char *extra);
|
|
||||||
|
|
||||||
} // namespace meshtastic
|
|
||||||
@@ -3,21 +3,22 @@
|
|||||||
#include "CallbackCharacteristic.h"
|
#include "CallbackCharacteristic.h"
|
||||||
#include "RadioLibInterface.h"
|
#include "RadioLibInterface.h"
|
||||||
#include "configuration.h"
|
#include "configuration.h"
|
||||||
#include "lock.h"
|
#include "../concurrency/LockGuard.h"
|
||||||
|
#include "../timing.h"
|
||||||
#include <Arduino.h>
|
#include <Arduino.h>
|
||||||
#include <BLE2902.h>
|
#include <BLE2902.h>
|
||||||
#include <CRC32.h>
|
#include <CRC32.h>
|
||||||
#include <Update.h>
|
#include <Update.h>
|
||||||
#include <esp_gatt_defs.h>
|
#include <esp_gatt_defs.h>
|
||||||
|
|
||||||
using namespace meshtastic;
|
//using namespace meshtastic;
|
||||||
|
|
||||||
CRC32 crc;
|
CRC32 crc;
|
||||||
uint32_t rebootAtMsec = 0; // If not zero we will reboot at this time (used to reboot shortly after the update completes)
|
uint32_t rebootAtMsec = 0; // If not zero we will reboot at this time (used to reboot shortly after the update completes)
|
||||||
|
|
||||||
uint32_t updateExpectedSize, updateActualSize;
|
uint32_t updateExpectedSize, updateActualSize;
|
||||||
|
|
||||||
Lock *updateLock;
|
concurrency::Lock *updateLock;
|
||||||
|
|
||||||
class TotalSizeCharacteristic : public CallbackCharacteristic
|
class TotalSizeCharacteristic : public CallbackCharacteristic
|
||||||
{
|
{
|
||||||
@@ -30,7 +31,7 @@ class TotalSizeCharacteristic : public CallbackCharacteristic
|
|||||||
|
|
||||||
void onWrite(BLECharacteristic *c)
|
void onWrite(BLECharacteristic *c)
|
||||||
{
|
{
|
||||||
LockGuard g(updateLock);
|
concurrency::LockGuard g(updateLock);
|
||||||
// Check if there is enough to OTA Update
|
// Check if there is enough to OTA Update
|
||||||
uint32_t len = getValue32(c, 0);
|
uint32_t len = getValue32(c, 0);
|
||||||
updateExpectedSize = len;
|
updateExpectedSize = len;
|
||||||
@@ -65,7 +66,7 @@ class DataCharacteristic : public CallbackCharacteristic
|
|||||||
|
|
||||||
void onWrite(BLECharacteristic *c)
|
void onWrite(BLECharacteristic *c)
|
||||||
{
|
{
|
||||||
LockGuard g(updateLock);
|
concurrency::LockGuard g(updateLock);
|
||||||
std::string value = c->getValue();
|
std::string value = c->getValue();
|
||||||
uint32_t len = value.length();
|
uint32_t len = value.length();
|
||||||
assert(len <= MAX_BLOCKSIZE);
|
assert(len <= MAX_BLOCKSIZE);
|
||||||
@@ -76,6 +77,7 @@ class DataCharacteristic : public CallbackCharacteristic
|
|||||||
crc.update(data, len);
|
crc.update(data, len);
|
||||||
Update.write(data, len);
|
Update.write(data, len);
|
||||||
updateActualSize += len;
|
updateActualSize += len;
|
||||||
|
powerFSM.trigger(EVENT_RECEIVED_TEXT_MSG); // Not exactly correct, but we want to force the device to not sleep now
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -88,7 +90,7 @@ class CRC32Characteristic : public CallbackCharacteristic
|
|||||||
|
|
||||||
void onWrite(BLECharacteristic *c)
|
void onWrite(BLECharacteristic *c)
|
||||||
{
|
{
|
||||||
LockGuard g(updateLock);
|
concurrency::LockGuard g(updateLock);
|
||||||
uint32_t expectedCRC = getValue32(c, 0);
|
uint32_t expectedCRC = getValue32(c, 0);
|
||||||
uint32_t actualCRC = crc.finalize();
|
uint32_t actualCRC = crc.finalize();
|
||||||
DEBUG_MSG("expected CRC %u\n", expectedCRC);
|
DEBUG_MSG("expected CRC %u\n", expectedCRC);
|
||||||
@@ -105,7 +107,7 @@ class CRC32Characteristic : public CallbackCharacteristic
|
|||||||
} else {
|
} else {
|
||||||
if (Update.end()) {
|
if (Update.end()) {
|
||||||
DEBUG_MSG("OTA done, rebooting in 5 seconds!\n");
|
DEBUG_MSG("OTA done, rebooting in 5 seconds!\n");
|
||||||
rebootAtMsec = millis() + 5000;
|
rebootAtMsec = timing::millis() + 5000;
|
||||||
} else {
|
} else {
|
||||||
DEBUG_MSG("Error Occurred. Error #: %d\n", Update.getError());
|
DEBUG_MSG("Error Occurred. Error #: %d\n", Update.getError());
|
||||||
}
|
}
|
||||||
@@ -123,8 +125,10 @@ class CRC32Characteristic : public CallbackCharacteristic
|
|||||||
|
|
||||||
void bluetoothRebootCheck()
|
void bluetoothRebootCheck()
|
||||||
{
|
{
|
||||||
if (rebootAtMsec && millis() > rebootAtMsec)
|
if (rebootAtMsec && timing::millis() > rebootAtMsec) {
|
||||||
|
DEBUG_MSG("Rebooting for update\n");
|
||||||
ESP.restart();
|
ESP.restart();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -134,7 +138,7 @@ See bluetooth-api.md
|
|||||||
BLEService *createUpdateService(BLEServer *server, std::string hwVendor, std::string swVersion, std::string hwVersion)
|
BLEService *createUpdateService(BLEServer *server, std::string hwVendor, std::string swVersion, std::string hwVersion)
|
||||||
{
|
{
|
||||||
if (!updateLock)
|
if (!updateLock)
|
||||||
updateLock = new Lock();
|
updateLock = new concurrency::Lock();
|
||||||
|
|
||||||
// Create the BLE Service
|
// Create the BLE Service
|
||||||
BLEService *service = server->createService(BLEUUID("cb0b9a0b-a84c-4c0d-bdbb-442e3144ee30"), 25, 0);
|
BLEService *service = server->createService(BLEUUID("cb0b9a0b-a84c-4c0d-bdbb-442e3144ee30"), 25, 0);
|
||||||
|
|||||||
@@ -122,7 +122,7 @@ BLEService *createBatteryService(BLEServer *server)
|
|||||||
addWithDesc(pBattery, batteryLevelC, "Percentage 0 - 100");
|
addWithDesc(pBattery, batteryLevelC, "Percentage 0 - 100");
|
||||||
batteryLevelC->addDescriptor(addBLEDescriptor(new BLE2902())); // Needed so clients can request notification
|
batteryLevelC->addDescriptor(addBLEDescriptor(new BLE2902())); // Needed so clients can request notification
|
||||||
|
|
||||||
// I don't think we need to advertise this
|
// I don't think we need to advertise this? and some phones only see the first thing advertised anyways...
|
||||||
// server->getAdvertising()->addServiceUUID(pBattery->getUUID());
|
// server->getAdvertising()->addServiceUUID(pBattery->getUUID());
|
||||||
pBattery->start();
|
pBattery->start();
|
||||||
|
|
||||||
@@ -135,8 +135,8 @@ BLEService *createBatteryService(BLEServer *server)
|
|||||||
*/
|
*/
|
||||||
void updateBatteryLevel(uint8_t level)
|
void updateBatteryLevel(uint8_t level)
|
||||||
{
|
{
|
||||||
// Pretend to update battery levels - fixme do elsewhere
|
|
||||||
if (batteryLevelC) {
|
if (batteryLevelC) {
|
||||||
|
DEBUG_MSG("set BLE battery level %u\n", level);
|
||||||
batteryLevelC->setValue(&level, 1);
|
batteryLevelC->setValue(&level, 1);
|
||||||
batteryLevelC->notify();
|
batteryLevelC->notify();
|
||||||
}
|
}
|
||||||
@@ -215,7 +215,7 @@ class MySecurity : public BLESecurityCallbacks
|
|||||||
|
|
||||||
BLEServer *pServer;
|
BLEServer *pServer;
|
||||||
|
|
||||||
BLEService *pDevInfo, *pUpdate;
|
BLEService *pDevInfo, *pUpdate, *pBattery;
|
||||||
|
|
||||||
void deinitBLE()
|
void deinitBLE()
|
||||||
{
|
{
|
||||||
@@ -230,6 +230,9 @@ void deinitBLE()
|
|||||||
pUpdate->executeDelete();
|
pUpdate->executeDelete();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pBattery->stop();
|
||||||
|
pBattery->executeDelete();
|
||||||
|
|
||||||
pDevInfo->stop();
|
pDevInfo->stop();
|
||||||
pDevInfo->executeDelete();
|
pDevInfo->executeDelete();
|
||||||
|
|
||||||
@@ -242,6 +245,7 @@ void deinitBLE()
|
|||||||
if (pUpdate != NULL)
|
if (pUpdate != NULL)
|
||||||
delete pUpdate;
|
delete pUpdate;
|
||||||
delete pDevInfo;
|
delete pDevInfo;
|
||||||
|
delete pBattery;
|
||||||
delete pServer;
|
delete pServer;
|
||||||
|
|
||||||
batteryLevelC = NULL; // Don't let anyone generate bogus notifies
|
batteryLevelC = NULL; // Don't let anyone generate bogus notifies
|
||||||
@@ -279,10 +283,9 @@ BLEServer *initBLE(StartBluetoothPinScreenCallback startBtPinScreen, StopBluetoo
|
|||||||
|
|
||||||
pDevInfo = createDeviceInfomationService(pServer, hwVendor, swVersion, hwVersion);
|
pDevInfo = createDeviceInfomationService(pServer, hwVendor, swVersion, hwVersion);
|
||||||
|
|
||||||
// We now let users create the battery service only if they really want (not all devices have a battery)
|
pBattery = createBatteryService(pServer);
|
||||||
// BLEService *pBattery = createBatteryService(pServer);
|
|
||||||
|
|
||||||
// #define BLE_SOFTWARE_UPDATE
|
#define BLE_SOFTWARE_UPDATE
|
||||||
#ifdef BLE_SOFTWARE_UPDATE
|
#ifdef BLE_SOFTWARE_UPDATE
|
||||||
pUpdate = createUpdateService(pServer, hwVendor, swVersion,
|
pUpdate = createUpdateService(pServer, hwVendor, swVersion,
|
||||||
hwVersion); // We need to advertise this so our android ble scan operation can see it
|
hwVersion); // We need to advertise this so our android ble scan operation can see it
|
||||||
|
|||||||
@@ -35,5 +35,8 @@ BLECharacteristic *addBLECharacteristic(BLECharacteristic *c);
|
|||||||
/// Add a characteristic that we will delete when we restart
|
/// Add a characteristic that we will delete when we restart
|
||||||
BLEDescriptor *addBLEDescriptor(BLEDescriptor *c);
|
BLEDescriptor *addBLEDescriptor(BLEDescriptor *c);
|
||||||
|
|
||||||
|
/// Given a level between 0-100, update the BLE attribute
|
||||||
|
void updateBatteryLevel(uint8_t level);
|
||||||
|
|
||||||
/// Any bluetooth objects you allocate _must_ come from this pool if you want to be able to call deinitBLE()
|
/// Any bluetooth objects you allocate _must_ come from this pool if you want to be able to call deinitBLE()
|
||||||
extern SimpleAllocator btPool;
|
extern SimpleAllocator btPool;
|
||||||
|
|||||||
+20
-149
@@ -2,10 +2,13 @@
|
|||||||
#include "MeshBluetoothService.h"
|
#include "MeshBluetoothService.h"
|
||||||
#include "PowerFSM.h"
|
#include "PowerFSM.h"
|
||||||
#include "configuration.h"
|
#include "configuration.h"
|
||||||
|
#include "esp_task_wdt.h"
|
||||||
#include "main.h"
|
#include "main.h"
|
||||||
#include "power.h"
|
|
||||||
#include "sleep.h"
|
#include "sleep.h"
|
||||||
#include "target_specific.h"
|
#include "target_specific.h"
|
||||||
|
#include "utils.h"
|
||||||
|
#include <nvs.h>
|
||||||
|
#include <nvs_flash.h>
|
||||||
|
|
||||||
bool bluetoothOn;
|
bool bluetoothOn;
|
||||||
|
|
||||||
@@ -59,102 +62,6 @@ void getMacAddr(uint8_t *dmac)
|
|||||||
assert(esp_efuse_mac_get_default(dmac) == ESP_OK);
|
assert(esp_efuse_mac_get_default(dmac) == ESP_OK);
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef TBEAM_V10
|
|
||||||
|
|
||||||
// FIXME. nasty hack cleanup how we load axp192
|
|
||||||
#undef AXP192_SLAVE_ADDRESS
|
|
||||||
#include "axp20x.h"
|
|
||||||
AXP20X_Class axp;
|
|
||||||
bool pmu_irq = false;
|
|
||||||
|
|
||||||
/// Reads power status to powerStatus singleton.
|
|
||||||
//
|
|
||||||
// TODO(girts): move this and other axp stuff to power.h/power.cpp.
|
|
||||||
void readPowerStatus()
|
|
||||||
{
|
|
||||||
powerStatus.haveBattery = axp.isBatteryConnect();
|
|
||||||
if (powerStatus.haveBattery) {
|
|
||||||
powerStatus.batteryVoltageMv = axp.getBattVoltage();
|
|
||||||
}
|
|
||||||
powerStatus.usb = axp.isVBUSPlug();
|
|
||||||
powerStatus.charging = axp.isChargeing();
|
|
||||||
}
|
|
||||||
#endif // TBEAM_V10
|
|
||||||
|
|
||||||
#ifdef AXP192_SLAVE_ADDRESS
|
|
||||||
/**
|
|
||||||
* Init the power manager chip
|
|
||||||
*
|
|
||||||
* axp192 power
|
|
||||||
DCDC1 0.7-3.5V @ 1200mA max -> OLED // If you turn this off you'll lose comms to the axp192 because the OLED and the axp192
|
|
||||||
share the same i2c bus, instead use ssd1306 sleep mode DCDC2 -> unused DCDC3 0.7-3.5V @ 700mA max -> ESP32 (keep this on!) LDO1
|
|
||||||
30mA -> charges GPS backup battery // charges the tiny J13 battery by the GPS to power the GPS ram (for a couple of days), can
|
|
||||||
not be turned off LDO2 200mA -> LORA LDO3 200mA -> GPS
|
|
||||||
*/
|
|
||||||
void axp192Init()
|
|
||||||
{
|
|
||||||
if (axp192_found) {
|
|
||||||
if (!axp.begin(Wire, AXP192_SLAVE_ADDRESS)) {
|
|
||||||
DEBUG_MSG("AXP192 Begin PASS\n");
|
|
||||||
|
|
||||||
// axp.setChgLEDMode(LED_BLINK_4HZ);
|
|
||||||
DEBUG_MSG("DCDC1: %s\n", axp.isDCDC1Enable() ? "ENABLE" : "DISABLE");
|
|
||||||
DEBUG_MSG("DCDC2: %s\n", axp.isDCDC2Enable() ? "ENABLE" : "DISABLE");
|
|
||||||
DEBUG_MSG("LDO2: %s\n", axp.isLDO2Enable() ? "ENABLE" : "DISABLE");
|
|
||||||
DEBUG_MSG("LDO3: %s\n", axp.isLDO3Enable() ? "ENABLE" : "DISABLE");
|
|
||||||
DEBUG_MSG("DCDC3: %s\n", axp.isDCDC3Enable() ? "ENABLE" : "DISABLE");
|
|
||||||
DEBUG_MSG("Exten: %s\n", axp.isExtenEnable() ? "ENABLE" : "DISABLE");
|
|
||||||
DEBUG_MSG("----------------------------------------\n");
|
|
||||||
|
|
||||||
axp.setPowerOutPut(AXP192_LDO2, AXP202_ON); // LORA radio
|
|
||||||
axp.setPowerOutPut(AXP192_LDO3, AXP202_ON); // GPS main power
|
|
||||||
axp.setPowerOutPut(AXP192_DCDC2, AXP202_ON);
|
|
||||||
axp.setPowerOutPut(AXP192_EXTEN, AXP202_ON);
|
|
||||||
axp.setPowerOutPut(AXP192_DCDC1, AXP202_ON);
|
|
||||||
axp.setDCDC1Voltage(3300); // for the OLED power
|
|
||||||
|
|
||||||
DEBUG_MSG("DCDC1: %s\n", axp.isDCDC1Enable() ? "ENABLE" : "DISABLE");
|
|
||||||
DEBUG_MSG("DCDC2: %s\n", axp.isDCDC2Enable() ? "ENABLE" : "DISABLE");
|
|
||||||
DEBUG_MSG("LDO2: %s\n", axp.isLDO2Enable() ? "ENABLE" : "DISABLE");
|
|
||||||
DEBUG_MSG("LDO3: %s\n", axp.isLDO3Enable() ? "ENABLE" : "DISABLE");
|
|
||||||
DEBUG_MSG("DCDC3: %s\n", axp.isDCDC3Enable() ? "ENABLE" : "DISABLE");
|
|
||||||
DEBUG_MSG("Exten: %s\n", axp.isExtenEnable() ? "ENABLE" : "DISABLE");
|
|
||||||
|
|
||||||
axp.setChargeControlCur(AXP1XX_CHARGE_CUR_1320MA); // actual limit (in HW) on the tbeam is 450mA
|
|
||||||
#if 0
|
|
||||||
|
|
||||||
// Not connected
|
|
||||||
//val = 0xfc;
|
|
||||||
//axp._writeByte(AXP202_VHTF_CHGSET, 1, &val); // Set temperature protection
|
|
||||||
|
|
||||||
//not used
|
|
||||||
//val = 0x46;
|
|
||||||
//axp._writeByte(AXP202_OFF_CTL, 1, &val); // enable bat detection
|
|
||||||
#endif
|
|
||||||
axp.debugCharging();
|
|
||||||
|
|
||||||
#ifdef PMU_IRQ
|
|
||||||
pinMode(PMU_IRQ, INPUT);
|
|
||||||
attachInterrupt(
|
|
||||||
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,
|
|
||||||
1);
|
|
||||||
|
|
||||||
axp.clearIRQ();
|
|
||||||
#endif
|
|
||||||
readPowerStatus();
|
|
||||||
} else {
|
|
||||||
DEBUG_MSG("AXP192 Begin FAIL\n");
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
DEBUG_MSG("AXP192 not found\n");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
static void printBLEinfo() {
|
static void printBLEinfo() {
|
||||||
int dev_num = esp_ble_get_bond_device_num();
|
int dev_num = esp_ble_get_bond_device_num();
|
||||||
@@ -178,11 +85,23 @@ void esp32Setup()
|
|||||||
DEBUG_MSG("Total PSRAM: %d\n", ESP.getPsramSize());
|
DEBUG_MSG("Total PSRAM: %d\n", ESP.getPsramSize());
|
||||||
DEBUG_MSG("Free PSRAM: %d\n", ESP.getFreePsram());
|
DEBUG_MSG("Free PSRAM: %d\n", ESP.getFreePsram());
|
||||||
|
|
||||||
|
nvs_stats_t nvs_stats;
|
||||||
|
auto res = nvs_get_stats(NULL, &nvs_stats);
|
||||||
|
assert(res == ESP_OK);
|
||||||
|
DEBUG_MSG("NVS: UsedEntries %d, FreeEntries %d, AllEntries %d\n", nvs_stats.used_entries, nvs_stats.free_entries,
|
||||||
|
nvs_stats.total_entries);
|
||||||
|
|
||||||
// enableModemSleep();
|
// enableModemSleep();
|
||||||
|
|
||||||
#ifdef AXP192_SLAVE_ADDRESS
|
// Since we are turning on watchdogs rather late in the release schedule, we really don't want to catch any
|
||||||
axp192Init();
|
// false positives. The wait-to-sleep timeout for shutting down radios is 30 secs, so pick 45 for now.
|
||||||
#endif
|
#define APP_WATCHDOG_SECS 45
|
||||||
|
|
||||||
|
res = esp_task_wdt_init(APP_WATCHDOG_SECS, true);
|
||||||
|
assert(res == ESP_OK);
|
||||||
|
|
||||||
|
res = esp_task_wdt_add(NULL);
|
||||||
|
assert(res == ESP_OK);
|
||||||
}
|
}
|
||||||
|
|
||||||
#if 0
|
#if 0
|
||||||
@@ -205,60 +124,12 @@ uint32_t axpDebugRead()
|
|||||||
Periodic axpDebugOutput(axpDebugRead);
|
Periodic axpDebugOutput(axpDebugRead);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/**
|
|
||||||
* Per @spattinson
|
|
||||||
* MIN_BAT_MILLIVOLTS seems high. Typical 18650 are different chemistry to LiPo, even for LiPos that chart seems a bit off, other
|
|
||||||
* charts put 3690mV at about 30% for a lipo, for 18650 i think 10% remaining iis in the region of 3.2-3.3V. Reference 1st graph
|
|
||||||
* in [this test report](https://lygte-info.dk/review/batteries2012/Samsung%20INR18650-30Q%203000mAh%20%28Pink%29%20UK.html)
|
|
||||||
* looking at the red line - discharge at 0.2A - he gets a capacity of 2900mah, 90% of 2900 = 2610, that point in the graph looks
|
|
||||||
* to be a shade above 3.2V
|
|
||||||
*/
|
|
||||||
#define MIN_BAT_MILLIVOLTS 3250 // millivolts. 10% per https://blog.ampow.com/lipo-voltage-chart/
|
|
||||||
|
|
||||||
/// loop code specific to ESP32 targets
|
/// loop code specific to ESP32 targets
|
||||||
void esp32Loop()
|
void esp32Loop()
|
||||||
{
|
{
|
||||||
|
esp_task_wdt_reset(); // service our app level watchdog
|
||||||
loopBLE();
|
loopBLE();
|
||||||
|
|
||||||
// for debug printing
|
// for debug printing
|
||||||
// radio.radioIf.canSleep();
|
// radio.radioIf.canSleep();
|
||||||
|
|
||||||
#ifdef PMU_IRQ
|
|
||||||
if (pmu_irq) {
|
|
||||||
pmu_irq = false;
|
|
||||||
axp.readIRQ();
|
|
||||||
|
|
||||||
DEBUG_MSG("pmu irq!\n");
|
|
||||||
|
|
||||||
if (axp.isChargingIRQ()) {
|
|
||||||
DEBUG_MSG("Battery start charging\n");
|
|
||||||
}
|
|
||||||
if (axp.isChargingDoneIRQ()) {
|
|
||||||
DEBUG_MSG("Battery fully charged\n");
|
|
||||||
}
|
|
||||||
if (axp.isVbusRemoveIRQ()) {
|
|
||||||
DEBUG_MSG("USB unplugged\n");
|
|
||||||
}
|
|
||||||
if (axp.isVbusPlugInIRQ()) {
|
|
||||||
DEBUG_MSG("USB plugged In\n");
|
|
||||||
}
|
|
||||||
if (axp.isBattPlugInIRQ()) {
|
|
||||||
DEBUG_MSG("Battery inserted\n");
|
|
||||||
}
|
|
||||||
if (axp.isBattRemoveIRQ()) {
|
|
||||||
DEBUG_MSG("Battery removed\n");
|
|
||||||
}
|
|
||||||
if (axp.isPEKShortPressIRQ()) {
|
|
||||||
DEBUG_MSG("PEK short button press\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
readPowerStatus();
|
|
||||||
axp.clearIRQ();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (powerStatus.haveBattery && !powerStatus.usb &&
|
|
||||||
axp.getBattVoltage() < MIN_BAT_MILLIVOLTS) // If we have a battery at all and it is less than 10% full, force deep sleep
|
|
||||||
powerFSM.trigger(EVENT_LOW_BATTERY);
|
|
||||||
|
|
||||||
#endif // T_BEAM_V10
|
|
||||||
}
|
}
|
||||||
+4
-6
@@ -1,9 +1,9 @@
|
|||||||
|
|
||||||
#include "GPS.h"
|
#include "GPS.h"
|
||||||
#include "configuration.h"
|
#include "configuration.h"
|
||||||
#include "time.h"
|
#include "timing.h"
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <sys/time.h>
|
#include <time.h>
|
||||||
|
|
||||||
#ifdef GPS_RX_PIN
|
#ifdef GPS_RX_PIN
|
||||||
HardwareSerial _serial_gps_real(GPS_SERIAL_NUM);
|
HardwareSerial _serial_gps_real(GPS_SERIAL_NUM);
|
||||||
@@ -27,7 +27,7 @@ void readFromRTC()
|
|||||||
struct timeval tv; /* btw settimeofday() is helpfull here too*/
|
struct timeval tv; /* btw settimeofday() is helpfull here too*/
|
||||||
|
|
||||||
if (!gettimeofday(&tv, NULL)) {
|
if (!gettimeofday(&tv, NULL)) {
|
||||||
uint32_t now = millis();
|
uint32_t now = timing::millis();
|
||||||
|
|
||||||
DEBUG_MSG("Read RTC time as %ld (cur millis %u) valid=%d\n", tv.tv_sec, now, timeSetFromGPS);
|
DEBUG_MSG("Read RTC time as %ld (cur millis %u) valid=%d\n", tv.tv_sec, now, timeSetFromGPS);
|
||||||
timeStartMsec = now;
|
timeStartMsec = now;
|
||||||
@@ -68,11 +68,9 @@ void perhapsSetRTC(struct tm &t)
|
|||||||
perhapsSetRTC(&tv);
|
perhapsSetRTC(&tv);
|
||||||
}
|
}
|
||||||
|
|
||||||
#include <time.h>
|
|
||||||
|
|
||||||
uint32_t getTime()
|
uint32_t getTime()
|
||||||
{
|
{
|
||||||
return ((millis() - timeStartMsec) / 1000) + zeroOffsetSecs;
|
return ((timing::millis() - timeStartMsec) / 1000) + zeroOffsetSecs;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t getValidTime()
|
uint32_t getValidTime()
|
||||||
|
|||||||
+11
-1
@@ -1,13 +1,17 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "Observer.h"
|
#include "Observer.h"
|
||||||
#include "PeriodicTask.h"
|
#include "GPSStatus.h"
|
||||||
|
#include "../concurrency/PeriodicTask.h"
|
||||||
#include "sys/time.h"
|
#include "sys/time.h"
|
||||||
|
|
||||||
/// If we haven't yet set our RTC this boot, set it from a GPS derived time
|
/// 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(const struct timeval *tv);
|
||||||
void perhapsSetRTC(struct tm &t);
|
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
|
/// Return time since 1970 in secs. Until we have a GPS lock we will be returning time based at zero
|
||||||
uint32_t getTime();
|
uint32_t getTime();
|
||||||
|
|
||||||
@@ -31,10 +35,16 @@ class GPS : public Observable<void *>
|
|||||||
public:
|
public:
|
||||||
int32_t latitude = 0, longitude = 0; // as an int mult by 1e-7 to get value as double
|
int32_t latitude = 0, longitude = 0; // as an int mult by 1e-7 to get value as double
|
||||||
int32_t altitude = 0;
|
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; // Heading of motion, in degrees * 10^-5
|
||||||
|
uint32_t numSatellites = 0;
|
||||||
|
|
||||||
bool isConnected = false; // Do we have a GPS we are talking to
|
bool isConnected = false; // Do we have a GPS we are talking to
|
||||||
|
|
||||||
virtual ~GPS() {}
|
virtual ~GPS() {}
|
||||||
|
|
||||||
|
Observable<const meshtastic::GPSStatus *> newStatus;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns true if we succeeded
|
* Returns true if we succeeded
|
||||||
*/
|
*/
|
||||||
|
|||||||
+17
-2
@@ -1,5 +1,6 @@
|
|||||||
#include "NEMAGPS.h"
|
#include "NEMAGPS.h"
|
||||||
#include "configuration.h"
|
#include "configuration.h"
|
||||||
|
#include "timing.h"
|
||||||
|
|
||||||
static int32_t toDegInt(RawDegrees d)
|
static int32_t toDegInt(RawDegrees d)
|
||||||
{
|
{
|
||||||
@@ -19,7 +20,7 @@ void NEMAGPS::loop()
|
|||||||
reader.encode(c);
|
reader.encode(c);
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t now = millis();
|
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
|
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)
|
// serial chars at whatever rate)
|
||||||
lastUpdateMsec = now;
|
lastUpdateMsec = now;
|
||||||
@@ -53,13 +54,27 @@ void NEMAGPS::loop()
|
|||||||
latitude = toDegInt(loc.lat);
|
latitude = toDegInt(loc.lat);
|
||||||
longitude = toDegInt(loc.lng);
|
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
|
// expect gps pos lat=37.520825, lon=-122.309162, alt=158
|
||||||
DEBUG_MSG("new NEMA GPS pos lat=%f, lon=%f, alt=%d\n", latitude * 1e-7, longitude * 1e-7, altitude);
|
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);
|
||||||
|
|
||||||
hasValidLocation = (latitude != 0) || (longitude != 0); // bogus lat lon is reported as 0,0
|
hasValidLocation = (latitude != 0) || (longitude != 0); // bogus lat lon is reported as 0,0
|
||||||
if (hasValidLocation)
|
if (hasValidLocation)
|
||||||
notifyObservers(NULL);
|
notifyObservers(NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+1
-1
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
#include "GPS.h"
|
#include "GPS.h"
|
||||||
#include "Observer.h"
|
#include "Observer.h"
|
||||||
#include "PeriodicTask.h"
|
#include "../concurrency/PeriodicTask.h"
|
||||||
#include "TinyGPS++.h"
|
#include "TinyGPS++.h"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
+18
-12
@@ -2,7 +2,7 @@
|
|||||||
#include "sleep.h"
|
#include "sleep.h"
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
|
|
||||||
UBloxGPS::UBloxGPS() : PeriodicTask()
|
UBloxGPS::UBloxGPS() : concurrency::PeriodicTask()
|
||||||
{
|
{
|
||||||
notifySleepObserver.observe(¬ifySleep);
|
notifySleepObserver.observe(¬ifySleep);
|
||||||
}
|
}
|
||||||
@@ -55,7 +55,7 @@ bool UBloxGPS::setup()
|
|||||||
ok = ublox.saveConfiguration(3000);
|
ok = ublox.saveConfiguration(3000);
|
||||||
assert(ok);
|
assert(ok);
|
||||||
|
|
||||||
PeriodicTask::setup(); // We don't start our periodic task unless we actually found the device
|
concurrency::PeriodicTask::setup(); // We don't start our periodic task unless we actually found the device
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
@@ -99,12 +99,12 @@ The Unix epoch (or Unix time or POSIX time or Unix timestamp) is the number of s
|
|||||||
(midnight UTC/GMT), not counting leap seconds (in ISO 8601: 1970-01-01T00:00:00Z).
|
(midnight UTC/GMT), not counting leap seconds (in ISO 8601: 1970-01-01T00:00:00Z).
|
||||||
*/
|
*/
|
||||||
struct tm t;
|
struct tm t;
|
||||||
t.tm_sec = ublox.getSecond();
|
t.tm_sec = ublox.getSecond(0);
|
||||||
t.tm_min = ublox.getMinute();
|
t.tm_min = ublox.getMinute(0);
|
||||||
t.tm_hour = ublox.getHour();
|
t.tm_hour = ublox.getHour(0);
|
||||||
t.tm_mday = ublox.getDay();
|
t.tm_mday = ublox.getDay(0);
|
||||||
t.tm_mon = ublox.getMonth() - 1;
|
t.tm_mon = ublox.getMonth(0) - 1;
|
||||||
t.tm_year = ublox.getYear() - 1900;
|
t.tm_year = ublox.getYear(0) - 1900;
|
||||||
t.tm_isdst = false;
|
t.tm_isdst = false;
|
||||||
perhapsSetRTC(t);
|
perhapsSetRTC(t);
|
||||||
}
|
}
|
||||||
@@ -112,10 +112,12 @@ The Unix epoch (or Unix time or POSIX time or Unix timestamp) is the number of s
|
|||||||
if ((fixtype >= 3 && fixtype <= 4) && ublox.getP(0)) // rd fixes only
|
if ((fixtype >= 3 && fixtype <= 4) && ublox.getP(0)) // rd fixes only
|
||||||
{
|
{
|
||||||
// we only notify if position has changed
|
// we only notify if position has changed
|
||||||
latitude = ublox.getLatitude();
|
latitude = ublox.getLatitude(0);
|
||||||
longitude = ublox.getLongitude();
|
longitude = ublox.getLongitude(0);
|
||||||
altitude = ublox.getAltitude() / 1000; // in mm convert to meters
|
altitude = ublox.getAltitude(0) / 1000; // in mm convert to meters
|
||||||
DEBUG_MSG("new gps pos lat=%f, lon=%f, alt=%d\n", latitude * 1e-7, longitude * 1e-7, altitude);
|
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)
|
// 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!
|
// Also: apparently when the GPS is initially reporting lock it can output a bogus latitude > 90 deg!
|
||||||
@@ -128,6 +130,10 @@ The Unix epoch (or Unix time or POSIX time or Unix timestamp) is the number of s
|
|||||||
} else // we didn't get a location update, go back to sleep and hope the characters show up
|
} else // we didn't get a location update, go back to sleep and hope the characters show up
|
||||||
wantNewLocation = true;
|
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);
|
||||||
|
|
||||||
// Once we have sent a location once we only poll the GPS rarely, otherwise check back every 1s until we have something over
|
// Once we have sent a location once we only poll the GPS rarely, otherwise check back every 1s until we have something over
|
||||||
// the serial
|
// the serial
|
||||||
setPeriod(hasValidLocation && !wantNewLocation ? 30 * 1000 : 10 * 1000);
|
setPeriod(hasValidLocation && !wantNewLocation ? 30 * 1000 : 10 * 1000);
|
||||||
|
|||||||
+2
-2
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
#include "GPS.h"
|
#include "GPS.h"
|
||||||
#include "Observer.h"
|
#include "Observer.h"
|
||||||
#include "PeriodicTask.h"
|
#include "../concurrency/PeriodicTask.h"
|
||||||
#include "SparkFun_Ublox_Arduino_Library.h"
|
#include "SparkFun_Ublox_Arduino_Library.h"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -10,7 +10,7 @@
|
|||||||
*
|
*
|
||||||
* When new data is available it will notify observers.
|
* When new data is available it will notify observers.
|
||||||
*/
|
*/
|
||||||
class UBloxGPS : public GPS, public PeriodicTask
|
class UBloxGPS : public GPS, public concurrency::PeriodicTask
|
||||||
{
|
{
|
||||||
SFE_UBLOX_GPS ublox;
|
SFE_UBLOX_GPS ublox;
|
||||||
|
|
||||||
|
|||||||
@@ -26,27 +26,16 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|||||||
#include "MeshService.h"
|
#include "MeshService.h"
|
||||||
#include "NodeDB.h"
|
#include "NodeDB.h"
|
||||||
#include "configuration.h"
|
#include "configuration.h"
|
||||||
#include "fonts.h"
|
#include "graphics/images.h"
|
||||||
#include "images.h"
|
|
||||||
#include "main.h"
|
#include "main.h"
|
||||||
#include "mesh-pb-constants.h"
|
#include "mesh-pb-constants.h"
|
||||||
#include "screen.h"
|
#include "Screen.h"
|
||||||
|
#include "utils.h"
|
||||||
|
#include "configs.h"
|
||||||
|
|
||||||
#define FONT_HEIGHT 14 // actually 13 for "ariel 10" but want a little extra space
|
using namespace meshtastic; /** @todo remove */
|
||||||
#define FONT_HEIGHT_16 (ArialMT_Plain_16[1] + 1)
|
|
||||||
#ifdef USE_SH1106
|
|
||||||
#define SCREEN_WIDTH 132
|
|
||||||
#else
|
|
||||||
#define SCREEN_WIDTH 128
|
|
||||||
#endif
|
|
||||||
#define SCREEN_HEIGHT 64
|
|
||||||
#define TRANSITION_FRAMERATE 30 // fps
|
|
||||||
#define IDLE_FRAMERATE 10 // in fps
|
|
||||||
#define COMPASS_DIAM 44
|
|
||||||
|
|
||||||
#define NUM_EXTRA_FRAMES 2 // text message and debug frame
|
namespace graphics
|
||||||
|
|
||||||
namespace meshtastic
|
|
||||||
{
|
{
|
||||||
|
|
||||||
// A text message frame + debug frame + all the node infos
|
// A text message frame + debug frame + all the node infos
|
||||||
@@ -54,6 +43,15 @@ static FrameCallback normalFrames[MAX_NUM_NODES + NUM_EXTRA_FRAMES];
|
|||||||
static uint32_t targetFramerate = IDLE_FRAMERATE;
|
static uint32_t targetFramerate = IDLE_FRAMERATE;
|
||||||
static char btPIN[16] = "888888";
|
static char btPIN[16] = "888888";
|
||||||
|
|
||||||
|
uint8_t imgBattery[16] = { 0xFF, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0xE7, 0x3C };
|
||||||
|
uint8_t imgSatellite[8] = { 0x70, 0x71, 0x22, 0xFA, 0xFA, 0x22, 0x71, 0x70 };
|
||||||
|
|
||||||
|
uint32_t dopThresholds[5] = { 2000, 1000, 500, 200, 100 };
|
||||||
|
|
||||||
|
#ifdef SHOW_REDRAWS
|
||||||
|
static bool heartbeat = false;
|
||||||
|
#endif
|
||||||
|
|
||||||
static void drawBootScreen(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y)
|
static void drawBootScreen(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y)
|
||||||
{
|
{
|
||||||
// draw an xbm image.
|
// draw an xbm image.
|
||||||
@@ -64,20 +62,34 @@ static void drawBootScreen(OLEDDisplay *display, OLEDDisplayUiState *state, int1
|
|||||||
display->setFont(ArialMT_Plain_16);
|
display->setFont(ArialMT_Plain_16);
|
||||||
display->setTextAlignment(TEXT_ALIGN_CENTER);
|
display->setTextAlignment(TEXT_ALIGN_CENTER);
|
||||||
display->drawString(64 + x, SCREEN_HEIGHT - FONT_HEIGHT_16, "meshtastic.org");
|
display->drawString(64 + x, SCREEN_HEIGHT - FONT_HEIGHT_16, "meshtastic.org");
|
||||||
|
display->setFont(ArialMT_Plain_10);
|
||||||
|
const char *region = xstr(HW_VERSION);
|
||||||
|
if (*region && region[3] == '-') // Skip past 1.0- in the 1.0-EU865 string
|
||||||
|
region += 4;
|
||||||
|
char buf[16];
|
||||||
|
snprintf(buf, sizeof(buf), "%s",
|
||||||
|
xstr(APP_VERSION)); // Note: we don't bother printing region or now, it makes the string too long
|
||||||
|
display->drawString(SCREEN_WIDTH - 20, 0, buf);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void drawFrameBluetooth(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y)
|
static void drawFrameBluetooth(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y)
|
||||||
{
|
{
|
||||||
display->setTextAlignment(TEXT_ALIGN_CENTER);
|
display->setTextAlignment(TEXT_ALIGN_CENTER);
|
||||||
display->setFont(ArialMT_Plain_16);
|
display->setFont(ArialMT_Plain_16);
|
||||||
display->drawString(64 + x, 2 + y, "Bluetooth");
|
display->drawString(64 + x, y, "Bluetooth");
|
||||||
|
|
||||||
display->setFont(ArialMT_Plain_10);
|
display->setFont(ArialMT_Plain_10);
|
||||||
display->drawString(64 + x, SCREEN_HEIGHT - FONT_HEIGHT + y, "Enter this code");
|
display->drawString(64 + x, FONT_HEIGHT + y + 2, "Enter this code");
|
||||||
|
|
||||||
display->setTextAlignment(TEXT_ALIGN_CENTER);
|
|
||||||
display->setFont(ArialMT_Plain_24);
|
display->setFont(ArialMT_Plain_24);
|
||||||
display->drawString(64 + x, 22 + y, btPIN);
|
display->drawString(64 + x, 26 + y, btPIN);
|
||||||
|
|
||||||
|
display->setFont(ArialMT_Plain_10);
|
||||||
|
char buf[30];
|
||||||
|
const char *name = "Name: ";
|
||||||
|
strcpy(buf, name);
|
||||||
|
strcat(buf, getDeviceName());
|
||||||
|
display->drawString(64 + x, 48 + y, buf);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Draw the last text message we received
|
/// Draw the last text message we received
|
||||||
@@ -126,34 +138,106 @@ static void drawColumns(OLEDDisplay *display, int16_t x, int16_t y, const char *
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Draw a series of fields in a row, wrapping to multiple rows if needed
|
#if 0
|
||||||
/// @return the max y we ended up printing to
|
/// Draw a series of fields in a row, wrapping to multiple rows if needed
|
||||||
static uint32_t drawRows(OLEDDisplay *display, int16_t x, int16_t y, const char **fields)
|
/// @return the max y we ended up printing to
|
||||||
{
|
static uint32_t drawRows(OLEDDisplay *display, int16_t x, int16_t y, const char **fields)
|
||||||
// The coordinates define the left starting point of the text
|
{
|
||||||
display->setTextAlignment(TEXT_ALIGN_LEFT);
|
// The coordinates define the left starting point of the text
|
||||||
|
display->setTextAlignment(TEXT_ALIGN_LEFT);
|
||||||
|
|
||||||
const char **f = fields;
|
const char **f = fields;
|
||||||
int xo = x, yo = y;
|
int xo = x, yo = y;
|
||||||
const int COLUMNS = 2; // hardwired for two columns per row....
|
const int COLUMNS = 2; // hardwired for two columns per row....
|
||||||
int col = 0; // track which column we are on
|
int col = 0; // track which column we are on
|
||||||
while (*f) {
|
while (*f) {
|
||||||
display->drawString(xo, yo, *f);
|
display->drawString(xo, yo, *f);
|
||||||
xo += SCREEN_WIDTH / COLUMNS;
|
xo += SCREEN_WIDTH / COLUMNS;
|
||||||
// Wrap to next row, if needed.
|
// Wrap to next row, if needed.
|
||||||
if (++col >= COLUMNS) {
|
if (++col >= COLUMNS) {
|
||||||
xo = x;
|
xo = x;
|
||||||
|
yo += FONT_HEIGHT;
|
||||||
|
col = 0;
|
||||||
|
}
|
||||||
|
f++;
|
||||||
|
}
|
||||||
|
if (col != 0) {
|
||||||
|
// Include last incomplete line in our total.
|
||||||
yo += FONT_HEIGHT;
|
yo += FONT_HEIGHT;
|
||||||
col = 0;
|
|
||||||
}
|
}
|
||||||
f++;
|
|
||||||
}
|
|
||||||
if (col != 0) {
|
|
||||||
// Include last incomplete line in our total.
|
|
||||||
yo += FONT_HEIGHT;
|
|
||||||
}
|
|
||||||
|
|
||||||
return yo;
|
return yo;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Draw power bars or a charging indicator on an image of a battery, determined by battery charge voltage or percentage.
|
||||||
|
static void drawBattery(OLEDDisplay *display, int16_t x, int16_t y, uint8_t *imgBuffer, const PowerStatus *powerStatus)
|
||||||
|
{
|
||||||
|
static const uint8_t powerBar[3] = {0x81, 0xBD, 0xBD};
|
||||||
|
static const uint8_t lightning[8] = {0xA1, 0xA1, 0xA5, 0xAD, 0xB5, 0xA5, 0x85, 0x85};
|
||||||
|
// Clear the bar area on the battery image
|
||||||
|
for (int i = 1; i < 14; i++) {
|
||||||
|
imgBuffer[i] = 0x81;
|
||||||
|
}
|
||||||
|
// If charging, draw a charging indicator
|
||||||
|
if (powerStatus->getIsCharging()) {
|
||||||
|
memcpy(imgBuffer + 3, lightning, 8);
|
||||||
|
// If not charging, Draw power bars
|
||||||
|
} else {
|
||||||
|
for (int i = 0; i < 4; i++) {
|
||||||
|
if (powerStatus->getBatteryChargePercent() >= 25 * i)
|
||||||
|
memcpy(imgBuffer + 1 + (i * 3), powerBar, 3);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
display->drawFastImage(x, y, 16, 8, imgBuffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Draw nodes status
|
||||||
|
static void drawNodes(OLEDDisplay *display, int16_t x, int16_t y, NodeStatus *nodeStatus)
|
||||||
|
{
|
||||||
|
char usersString[20];
|
||||||
|
sprintf(usersString, "%d/%d", nodeStatus->getNumOnline(), nodeStatus->getNumTotal());
|
||||||
|
display->drawFastImage(x, y, 8, 8, imgUser);
|
||||||
|
display->drawString(x + 10, y - 2, usersString);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Draw GPS status summary
|
||||||
|
static void drawGPS(OLEDDisplay *display, int16_t x, int16_t y, const GPSStatus *gps)
|
||||||
|
{
|
||||||
|
if (!gps->getIsConnected())
|
||||||
|
{
|
||||||
|
display->drawString(x, y - 2, "No GPS");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
display->drawFastImage(x, y, 6, 8, gps->getHasLock() ? imgPositionSolid : imgPositionEmpty);
|
||||||
|
if (!gps->getHasLock())
|
||||||
|
{
|
||||||
|
display->drawString(x + 8, y - 2, "No sats");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
char satsString[3];
|
||||||
|
uint8_t bar[2] = { 0 };
|
||||||
|
|
||||||
|
//Draw DOP signal bars
|
||||||
|
for(int i = 0; i < 5; i++)
|
||||||
|
{
|
||||||
|
if (gps->getDOP() <= dopThresholds[i])
|
||||||
|
bar[0] = ~((1 << (5 - i)) - 1);
|
||||||
|
else
|
||||||
|
bar[0] = 0b10000000;
|
||||||
|
//bar[1] = bar[0];
|
||||||
|
display->drawFastImage(x + 9 + (i * 2), y, 2, 8, bar);
|
||||||
|
}
|
||||||
|
|
||||||
|
//Draw satellite image
|
||||||
|
display->drawFastImage(x + 24, y, 8, 8, imgSatellite);
|
||||||
|
|
||||||
|
//Draw the number of satellites
|
||||||
|
sprintf(satsString, "%d", gps->getNumSatellites());
|
||||||
|
display->drawString(x + 34, y - 2, satsString);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Ported from my old java code, returns distance in meters along the globe
|
/// Ported from my old java code, returns distance in meters along the globe
|
||||||
@@ -295,6 +379,43 @@ static bool hasPosition(NodeInfo *n)
|
|||||||
static size_t nodeIndex;
|
static size_t nodeIndex;
|
||||||
static int8_t prevFrame = -1;
|
static int8_t prevFrame = -1;
|
||||||
|
|
||||||
|
// Draw the arrow pointing to a node's location
|
||||||
|
static void drawNodeHeading(OLEDDisplay *display, int16_t compassX, int16_t compassY, float headingRadian)
|
||||||
|
{
|
||||||
|
Point tip(0.0f, 0.5f), tail(0.0f, -0.5f); // pointing up initially
|
||||||
|
float arrowOffsetX = 0.2f, arrowOffsetY = 0.2f;
|
||||||
|
Point leftArrow(tip.x - arrowOffsetX, tip.y - arrowOffsetY), rightArrow(tip.x + arrowOffsetX, tip.y - arrowOffsetY);
|
||||||
|
|
||||||
|
|
||||||
|
Point *arrowPoints[] = {&tip, &tail, &leftArrow, &rightArrow};
|
||||||
|
|
||||||
|
for (int i = 0; i < 4; i++) {
|
||||||
|
arrowPoints[i]->rotate(headingRadian);
|
||||||
|
arrowPoints[i]->scale(COMPASS_DIAM * 0.6);
|
||||||
|
arrowPoints[i]->translate(compassX, compassY);
|
||||||
|
}
|
||||||
|
drawLine(display, tip, tail);
|
||||||
|
drawLine(display, leftArrow, tip);
|
||||||
|
drawLine(display, rightArrow, tip);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Draw the compass heading
|
||||||
|
static void drawCompassHeading(OLEDDisplay *display, int16_t compassX, int16_t compassY, float myHeading)
|
||||||
|
{
|
||||||
|
Point N1(-0.04f, -0.65f), N2( 0.04f, -0.65f);
|
||||||
|
Point N3(-0.04f, -0.55f), N4( 0.04f, -0.55f);
|
||||||
|
Point *rosePoints[] = {&N1, &N2, &N3, &N4};
|
||||||
|
|
||||||
|
for (int i = 0; i < 4; i++) {
|
||||||
|
rosePoints[i]->rotate(myHeading);
|
||||||
|
rosePoints[i]->scale(COMPASS_DIAM);
|
||||||
|
rosePoints[i]->translate(compassX, compassY);
|
||||||
|
}
|
||||||
|
drawLine(display, N1, N3);
|
||||||
|
drawLine(display, N2, N4);
|
||||||
|
drawLine(display, N1, N4);
|
||||||
|
}
|
||||||
|
|
||||||
/// Convert an integer GPS coords to a floating point
|
/// Convert an integer GPS coords to a floating point
|
||||||
#define DegD(i) (i * 1e-7)
|
#define DegD(i) (i * 1e-7)
|
||||||
|
|
||||||
@@ -312,10 +433,13 @@ static void drawNodeInfo(OLEDDisplay *display, OLEDDisplayUiState *state, int16_
|
|||||||
nodeIndex = (nodeIndex + 1) % nodeDB.getNumNodes();
|
nodeIndex = (nodeIndex + 1) % nodeDB.getNumNodes();
|
||||||
n = nodeDB.getNodeByIndex(nodeIndex);
|
n = nodeDB.getNodeByIndex(nodeIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
// We just changed to a new node screen, ask that node for updated state
|
|
||||||
displayedNodeNum = n->num;
|
displayedNodeNum = n->num;
|
||||||
service.sendNetworkPing(displayedNodeNum, true);
|
|
||||||
|
// We just changed to a new node screen, ask that node for updated state if it's older than 2 minutes
|
||||||
|
if(sinceLastSeen(n) > 120)
|
||||||
|
{
|
||||||
|
service.sendNetworkPing(displayedNodeNum, true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
NodeInfo *node = nodeDB.getNodeByIndex(nodeIndex);
|
NodeInfo *node = nodeDB.getNodeByIndex(nodeIndex);
|
||||||
@@ -328,7 +452,7 @@ static void drawNodeInfo(OLEDDisplay *display, OLEDDisplayUiState *state, int16_
|
|||||||
const char *username = node->has_user ? node->user.long_name : "Unknown Name";
|
const char *username = node->has_user ? node->user.long_name : "Unknown Name";
|
||||||
|
|
||||||
static char signalStr[20];
|
static char signalStr[20];
|
||||||
snprintf(signalStr, sizeof(signalStr), "Signal: %.0f", node->snr);
|
snprintf(signalStr, sizeof(signalStr), "Signal: %d%%", clamp((int)((node->snr + 10) * 5), 0, 100));
|
||||||
|
|
||||||
uint32_t agoSecs = sinceLastSeen(node);
|
uint32_t agoSecs = sinceLastSeen(node);
|
||||||
static char lastStr[20];
|
static char lastStr[20];
|
||||||
@@ -339,56 +463,50 @@ static void drawNodeInfo(OLEDDisplay *display, OLEDDisplayUiState *state, int16_
|
|||||||
else
|
else
|
||||||
snprintf(lastStr, sizeof(lastStr), "%u hours ago", agoSecs / 60 / 60);
|
snprintf(lastStr, sizeof(lastStr), "%u hours ago", agoSecs / 60 / 60);
|
||||||
|
|
||||||
static float simRadian;
|
|
||||||
simRadian += 0.1; // For testing, have the compass spin unless both
|
|
||||||
// locations are valid
|
|
||||||
|
|
||||||
static char distStr[20];
|
static char distStr[20];
|
||||||
*distStr = 0; // might not have location data
|
strcpy(distStr, "? km"); // might not have location data
|
||||||
float headingRadian = simRadian;
|
float headingRadian;
|
||||||
NodeInfo *ourNode = nodeDB.getNode(nodeDB.getNodeNum());
|
NodeInfo *ourNode = nodeDB.getNode(nodeDB.getNodeNum());
|
||||||
if (ourNode && hasPosition(ourNode) && hasPosition(node)) {
|
const char *fields[] = {username, distStr, signalStr, lastStr, NULL};
|
||||||
Position &op = ourNode->position, &p = node->position;
|
|
||||||
float d = latLongToMeter(DegD(p.latitude_i), DegD(p.longitude_i), DegD(op.latitude_i), DegD(op.longitude_i));
|
|
||||||
if (d < 2000)
|
|
||||||
snprintf(distStr, sizeof(distStr), "%.0f m", d);
|
|
||||||
else
|
|
||||||
snprintf(distStr, sizeof(distStr), "%.1f km", d / 1000);
|
|
||||||
|
|
||||||
// FIXME, also keep the guess at the operators heading and add/substract
|
// coordinates for the center of the compass/circle
|
||||||
// it. currently we don't do this and instead draw north up only.
|
int16_t compassX = x + SCREEN_WIDTH - COMPASS_DIAM / 2 - 5, compassY = y + SCREEN_HEIGHT / 2;
|
||||||
float bearingToOther = bearing(DegD(p.latitude_i), DegD(p.longitude_i), DegD(op.latitude_i), DegD(op.longitude_i));
|
bool hasNodeHeading = false;
|
||||||
float myHeading = estimatedHeading(DegD(p.latitude_i), DegD(p.longitude_i));
|
|
||||||
headingRadian = bearingToOther - myHeading;
|
if(ourNode && hasPosition(ourNode))
|
||||||
} else {
|
{
|
||||||
|
Position &op = ourNode->position;
|
||||||
|
float myHeading = estimatedHeading(DegD(op.latitude_i), DegD(op.longitude_i));
|
||||||
|
drawCompassHeading(display, compassX, compassY, myHeading);
|
||||||
|
|
||||||
|
if(hasPosition(node))
|
||||||
|
{
|
||||||
|
// display direction toward node
|
||||||
|
hasNodeHeading = true;
|
||||||
|
Position &p = node->position;
|
||||||
|
float d = latLongToMeter(DegD(p.latitude_i), DegD(p.longitude_i), DegD(op.latitude_i), DegD(op.longitude_i));
|
||||||
|
if (d < 2000)
|
||||||
|
snprintf(distStr, sizeof(distStr), "%.0f m", d);
|
||||||
|
else
|
||||||
|
snprintf(distStr, sizeof(distStr), "%.1f km", d / 1000);
|
||||||
|
|
||||||
|
// FIXME, also keep the guess at the operators heading and add/substract
|
||||||
|
// it. currently we don't do this and instead draw north up only.
|
||||||
|
float bearingToOther = bearing(DegD(p.latitude_i), DegD(p.longitude_i), DegD(op.latitude_i), DegD(op.longitude_i));
|
||||||
|
headingRadian = bearingToOther - myHeading;
|
||||||
|
drawNodeHeading(display, compassX, compassY, headingRadian);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(!hasNodeHeading)
|
||||||
|
// direction to node is unknown so display question mark
|
||||||
// Debug info for gps lock errors
|
// Debug info for gps lock errors
|
||||||
// DEBUG_MSG("ourNode %d, ourPos %d, theirPos %d\n", !!ourNode, ourNode && hasPosition(ourNode), hasPosition(node));
|
// DEBUG_MSG("ourNode %d, ourPos %d, theirPos %d\n", !!ourNode, ourNode && hasPosition(ourNode), hasPosition(node));
|
||||||
}
|
display->drawString(compassX - FONT_HEIGHT / 4, compassY - FONT_HEIGHT / 2, "?");
|
||||||
|
|
||||||
const char *fields[] = {username, distStr, signalStr, lastStr, NULL};
|
|
||||||
drawColumns(display, x, y, fields);
|
|
||||||
|
|
||||||
// coordinates for the center of the compass
|
|
||||||
int16_t compassX = x + SCREEN_WIDTH - COMPASS_DIAM / 2 - 1, compassY = y + SCREEN_HEIGHT / 2;
|
|
||||||
// display->drawXbm(compassX, compassY, compass_width, compass_height,
|
|
||||||
// (const uint8_t *)compass_bits);
|
|
||||||
|
|
||||||
Point tip(0.0f, 0.5f), tail(0.0f, -0.5f); // pointing up initially
|
|
||||||
float arrowOffsetX = 0.2f, arrowOffsetY = 0.2f;
|
|
||||||
Point leftArrow(tip.x - arrowOffsetX, tip.y - arrowOffsetY), rightArrow(tip.x + arrowOffsetX, tip.y - arrowOffsetY);
|
|
||||||
|
|
||||||
Point *points[] = {&tip, &tail, &leftArrow, &rightArrow};
|
|
||||||
|
|
||||||
for (int i = 0; i < 4; i++) {
|
|
||||||
points[i]->rotate(headingRadian);
|
|
||||||
points[i]->scale(COMPASS_DIAM * 0.6);
|
|
||||||
points[i]->translate(compassX, compassY);
|
|
||||||
}
|
|
||||||
drawLine(display, tip, tail);
|
|
||||||
drawLine(display, leftArrow, tip);
|
|
||||||
drawLine(display, rightArrow, tip);
|
|
||||||
|
|
||||||
display->drawCircle(compassX, compassY, COMPASS_DIAM / 2);
|
display->drawCircle(compassX, compassY, COMPASS_DIAM / 2);
|
||||||
|
|
||||||
|
|
||||||
|
// Must be after distStr is populated
|
||||||
|
drawColumns(display, x, y, fields);
|
||||||
}
|
}
|
||||||
|
|
||||||
#if 0
|
#if 0
|
||||||
@@ -436,7 +554,7 @@ void Screen::handleSetOn(bool on)
|
|||||||
|
|
||||||
void Screen::setup()
|
void Screen::setup()
|
||||||
{
|
{
|
||||||
PeriodicTask::setup();
|
concurrency::PeriodicTask::setup();
|
||||||
|
|
||||||
// We don't set useDisplay until setup() is called, because some boards have a declaration of this object but the device
|
// We don't set useDisplay until setup() is called, because some boards have a declaration of this object but the device
|
||||||
// is never found when probing i2c and therefore we don't call setup and never want to do (invalid) accesses to this device.
|
// is never found when probing i2c and therefore we don't call setup and never want to do (invalid) accesses to this device.
|
||||||
@@ -456,6 +574,9 @@ void Screen::setup()
|
|||||||
// Store a pointer to Screen so we can get to it from static functions.
|
// Store a pointer to Screen so we can get to it from static functions.
|
||||||
ui.getUiState()->userData = this;
|
ui.getUiState()->userData = this;
|
||||||
|
|
||||||
|
// Set the utf8 conversion function
|
||||||
|
dispdev.setFontTableLookupFunction(customFontTableLookup);
|
||||||
|
|
||||||
// Add frames.
|
// Add frames.
|
||||||
static FrameCallback bootFrames[] = {drawBootScreen};
|
static FrameCallback bootFrames[] = {drawBootScreen};
|
||||||
static const int bootFrameCount = sizeof(bootFrames) / sizeof(bootFrames[0]);
|
static const int bootFrameCount = sizeof(bootFrames) / sizeof(bootFrames[0]);
|
||||||
@@ -480,6 +601,11 @@ void Screen::setup()
|
|||||||
// twice initially.
|
// twice initially.
|
||||||
ui.update();
|
ui.update();
|
||||||
ui.update();
|
ui.update();
|
||||||
|
|
||||||
|
// Subscribe to status updates
|
||||||
|
powerStatusObserver.observe(&powerStatus->onNewStatus);
|
||||||
|
gpsStatusObserver.observe(&gpsStatus->onNewStatus);
|
||||||
|
nodeStatusObserver.observe(&nodeStatus->onNewStatus);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Screen::doTask()
|
void Screen::doTask()
|
||||||
@@ -492,7 +618,7 @@ void Screen::doTask()
|
|||||||
|
|
||||||
// Process incoming commands.
|
// Process incoming commands.
|
||||||
for (;;) {
|
for (;;) {
|
||||||
CmdItem cmd;
|
ScreenCmd cmd;
|
||||||
if (!cmdQueue.dequeue(&cmd, 0)) {
|
if (!cmdQueue.dequeue(&cmd, 0)) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -541,14 +667,7 @@ void Screen::doTask()
|
|||||||
// While showing the bootscreen or Bluetooth pair screen all of our
|
// While showing the bootscreen or Bluetooth pair screen all of our
|
||||||
// standard screen switching is stopped.
|
// standard screen switching is stopped.
|
||||||
if (showingNormalScreen) {
|
if (showingNormalScreen) {
|
||||||
// TODO(girts): decouple nodeDB from screen.
|
// standard screen loop handling here
|
||||||
// standard screen loop handling ehre
|
|
||||||
// If the # nodes changes, we need to regen our list of screens
|
|
||||||
if (nodeDB.updateGUI || nodeDB.updateTextMessage) {
|
|
||||||
setFrames();
|
|
||||||
nodeDB.updateGUI = false;
|
|
||||||
nodeDB.updateTextMessage = false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ui.update();
|
ui.update();
|
||||||
@@ -573,8 +692,8 @@ void Screen::setFrames()
|
|||||||
DEBUG_MSG("showing standard frames\n");
|
DEBUG_MSG("showing standard frames\n");
|
||||||
showingNormalScreen = true;
|
showingNormalScreen = true;
|
||||||
|
|
||||||
size_t numnodes = nodeDB.getNumNodes();
|
|
||||||
// We don't show the node info our our node (if we have it yet - we should)
|
// We don't show the node info our our node (if we have it yet - we should)
|
||||||
|
size_t numnodes = nodeStatus->getNumTotal();
|
||||||
if (numnodes > 0)
|
if (numnodes > 0)
|
||||||
numnodes--;
|
numnodes--;
|
||||||
|
|
||||||
@@ -648,35 +767,63 @@ void DebugInfo::drawFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16
|
|||||||
// The coordinates define the left starting point of the text
|
// The coordinates define the left starting point of the text
|
||||||
display->setTextAlignment(TEXT_ALIGN_LEFT);
|
display->setTextAlignment(TEXT_ALIGN_LEFT);
|
||||||
|
|
||||||
char usersStr[20];
|
|
||||||
char channelStr[20];
|
char channelStr[20];
|
||||||
char batStr[20];
|
|
||||||
char gpsStr[20];
|
|
||||||
{
|
{
|
||||||
LockGuard guard(&lock);
|
concurrency::LockGuard guard(&lock);
|
||||||
snprintf(usersStr, sizeof(usersStr), "Users %d/%d", nodesOnline, nodesTotal);
|
snprintf(channelStr, sizeof(channelStr), "#%s", channelName.c_str());
|
||||||
snprintf(channelStr, sizeof(channelStr), "%s", channelName.c_str());
|
|
||||||
if (powerStatus.haveBattery) {
|
|
||||||
// TODO: draw a battery icon instead of letter "B".
|
|
||||||
int batV = powerStatus.batteryVoltageMv / 1000;
|
|
||||||
int batCv = (powerStatus.batteryVoltageMv % 1000) / 10;
|
|
||||||
snprintf(batStr, sizeof(batStr), "B %01d.%02dV%c%c", batV, batCv, powerStatus.charging ? '+' : ' ',
|
|
||||||
powerStatus.usb ? 'U' : ' ');
|
|
||||||
} else {
|
|
||||||
snprintf(batStr, sizeof(batStr), "%s", powerStatus.usb ? "USB" : "");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!gpsStatus.empty()) {
|
// Display power status
|
||||||
snprintf(gpsStr, sizeof(gpsStr), "%s", gpsStatus.c_str());
|
if (powerStatus->getHasBattery())
|
||||||
} else {
|
drawBattery(display, x, y + 2, imgBattery, powerStatus);
|
||||||
gpsStr[0] = '\0'; // Just show empty string.
|
else
|
||||||
}
|
display->drawFastImage(x, y + 2, 16, 8, powerStatus->getHasUSB() ? imgUSB : imgPower);
|
||||||
|
// Display nodes status
|
||||||
|
drawNodes(display, x + (SCREEN_WIDTH * 0.25), y + 2, nodeStatus);
|
||||||
|
// Display GPS status
|
||||||
|
drawGPS(display, x + (SCREEN_WIDTH * 0.63), y + 2, gpsStatus);
|
||||||
}
|
}
|
||||||
|
|
||||||
const char *fields[] = {batStr, gpsStr, usersStr, channelStr, nullptr};
|
display->drawString(x, y + FONT_HEIGHT, channelStr);
|
||||||
uint32_t yo = drawRows(display, x, y, fields);
|
|
||||||
|
|
||||||
display->drawLogBuffer(x, yo);
|
display->drawLogBuffer(x, y + (FONT_HEIGHT * 2));
|
||||||
|
|
||||||
|
/* Display a heartbeat pixel that blinks every time the frame is redrawn */
|
||||||
|
#ifdef SHOW_REDRAWS
|
||||||
|
if (heartbeat)
|
||||||
|
display->setPixel(0, 0);
|
||||||
|
heartbeat = !heartbeat;
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace meshtastic
|
// adjust Brightness cycle trough 1 to 254 as long as attachDuringLongPress is true
|
||||||
|
void Screen::adjustBrightness()
|
||||||
|
{
|
||||||
|
if (brightness == 254) {
|
||||||
|
brightness = 0;
|
||||||
|
} else {
|
||||||
|
brightness++;
|
||||||
|
}
|
||||||
|
int width = brightness / (254.00 / SCREEN_WIDTH);
|
||||||
|
dispdev.drawRect(0, 30, SCREEN_WIDTH, 4);
|
||||||
|
dispdev.fillRect(0, 31, width, 2);
|
||||||
|
dispdev.display();
|
||||||
|
dispdev.setBrightness(brightness);
|
||||||
|
}
|
||||||
|
|
||||||
|
int Screen::handleStatusUpdate(const meshtastic::Status *arg)
|
||||||
|
{
|
||||||
|
//DEBUG_MSG("Screen got status update %d\n", arg->getStatusType());
|
||||||
|
switch(arg->getStatusType())
|
||||||
|
{
|
||||||
|
case STATUS_TYPE_NODE:
|
||||||
|
if (nodeDB.updateTextMessage || nodeStatus->getLastNumTotal() != nodeStatus->getNumTotal())
|
||||||
|
setFrames();
|
||||||
|
prevFrame = -1;
|
||||||
|
nodeDB.updateGUI = false;
|
||||||
|
nodeDB.updateTextMessage = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
setPeriod(1); // Update the screen right away
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
} // namespace graphics
|
||||||
@@ -10,13 +10,14 @@
|
|||||||
#include <SSD1306Wire.h>
|
#include <SSD1306Wire.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include "PeriodicTask.h"
|
#include "concurrency/PeriodicTask.h"
|
||||||
#include "TypedQueue.h"
|
#include "TypedQueue.h"
|
||||||
#include "lock.h"
|
#include "concurrency/LockGuard.h"
|
||||||
#include "power.h"
|
#include "power.h"
|
||||||
|
#include "commands.h"
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
namespace meshtastic
|
namespace graphics
|
||||||
{
|
{
|
||||||
|
|
||||||
// Forward declarations
|
// Forward declarations
|
||||||
@@ -29,40 +30,13 @@ class DebugInfo
|
|||||||
DebugInfo(const DebugInfo &) = delete;
|
DebugInfo(const DebugInfo &) = delete;
|
||||||
DebugInfo &operator=(const DebugInfo &) = delete;
|
DebugInfo &operator=(const DebugInfo &) = delete;
|
||||||
|
|
||||||
/// Sets user statistics.
|
|
||||||
void setNodeNumbersStatus(int online, int total)
|
|
||||||
{
|
|
||||||
LockGuard guard(&lock);
|
|
||||||
nodesOnline = online;
|
|
||||||
nodesTotal = total;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Sets the name of the channel.
|
/// Sets the name of the channel.
|
||||||
void setChannelNameStatus(const char *name)
|
void setChannelNameStatus(const char *name)
|
||||||
{
|
{
|
||||||
LockGuard guard(&lock);
|
concurrency::LockGuard guard(&lock);
|
||||||
channelName = name;
|
channelName = name;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Sets battery/charging/etc status.
|
|
||||||
//
|
|
||||||
void setPowerStatus(const PowerStatus &status)
|
|
||||||
{
|
|
||||||
LockGuard guard(&lock);
|
|
||||||
powerStatus = status;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Sets GPS status.
|
|
||||||
//
|
|
||||||
// If this function never gets called, we assume GPS does not exist on this
|
|
||||||
// device.
|
|
||||||
// TODO(girts): figure out what the format should be.
|
|
||||||
void setGPSStatus(const char *status)
|
|
||||||
{
|
|
||||||
LockGuard guard(&lock);
|
|
||||||
gpsStatus = status;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
friend Screen;
|
friend Screen;
|
||||||
|
|
||||||
@@ -71,28 +45,25 @@ class DebugInfo
|
|||||||
/// Renders the debug screen.
|
/// Renders the debug screen.
|
||||||
void drawFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y);
|
void drawFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y);
|
||||||
|
|
||||||
int nodesOnline = 0;
|
|
||||||
int nodesTotal = 0;
|
|
||||||
|
|
||||||
PowerStatus powerStatus;
|
|
||||||
|
|
||||||
std::string channelName;
|
std::string channelName;
|
||||||
|
|
||||||
std::string gpsStatus;
|
|
||||||
|
|
||||||
/// Protects all of internal state.
|
/// Protects all of internal state.
|
||||||
Lock lock;
|
concurrency::Lock lock;
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Deals with showing things on the screen of the device.
|
/**
|
||||||
//
|
* @brief This class deals with showing things on the screen of the device.
|
||||||
// Other than setup(), this class is thread-safe. All state-changing calls are
|
*
|
||||||
// queued and executed when the main loop calls us.
|
* @details Other than setup(), this class is thread-safe as long as drawFrame is not called
|
||||||
//
|
* multiple times simultaneously. All state-changing calls are queued and executed
|
||||||
// This class is thread-safe (as long as drawFrame is not called multiple times
|
* when the main loop calls us.
|
||||||
// simultaneously).
|
*/
|
||||||
class Screen : public PeriodicTask
|
class Screen : public concurrency::PeriodicTask
|
||||||
{
|
{
|
||||||
|
CallbackObserver<Screen, const meshtastic::Status *> powerStatusObserver = CallbackObserver<Screen, const meshtastic::Status *>(this, &Screen::handleStatusUpdate);
|
||||||
|
CallbackObserver<Screen, const meshtastic::Status *> gpsStatusObserver = CallbackObserver<Screen, const meshtastic::Status *>(this, &Screen::handleStatusUpdate);
|
||||||
|
CallbackObserver<Screen, const meshtastic::Status *> nodeStatusObserver = CallbackObserver<Screen, const meshtastic::Status *>(this, &Screen::handleStatusUpdate);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Screen(uint8_t address, int sda = -1, int scl = -1);
|
Screen(uint8_t address, int sda = -1, int scl = -1);
|
||||||
|
|
||||||
@@ -111,11 +82,15 @@ class Screen : public PeriodicTask
|
|||||||
handleSetOn(
|
handleSetOn(
|
||||||
false); // We handle off commands immediately, because they might be called because the CPU is shutting down
|
false); // We handle off commands immediately, because they might be called because the CPU is shutting down
|
||||||
else
|
else
|
||||||
enqueueCmd(CmdItem{.cmd = on ? Cmd::SET_ON : Cmd::SET_OFF});
|
enqueueCmd(ScreenCmd{.cmd = on ? Cmd::SET_ON : Cmd::SET_OFF});
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Handles a button press.
|
/// Handles a button press.
|
||||||
void onPress() { enqueueCmd(CmdItem{.cmd = Cmd::ON_PRESS}); }
|
void onPress() { enqueueCmd(ScreenCmd{.cmd = Cmd::ON_PRESS}); }
|
||||||
|
|
||||||
|
// Implementation to Adjust Brightness
|
||||||
|
void adjustBrightness();
|
||||||
|
uint8_t brightness = 150;
|
||||||
|
|
||||||
/// Starts showing the Bluetooth PIN screen.
|
/// Starts showing the Bluetooth PIN screen.
|
||||||
//
|
//
|
||||||
@@ -123,22 +98,22 @@ class Screen : public PeriodicTask
|
|||||||
// with the PIN.
|
// with the PIN.
|
||||||
void startBluetoothPinScreen(uint32_t pin)
|
void startBluetoothPinScreen(uint32_t pin)
|
||||||
{
|
{
|
||||||
CmdItem cmd;
|
ScreenCmd cmd;
|
||||||
cmd.cmd = Cmd::START_BLUETOOTH_PIN_SCREEN;
|
cmd.cmd = Cmd::START_BLUETOOTH_PIN_SCREEN;
|
||||||
cmd.bluetooth_pin = pin;
|
cmd.bluetooth_pin = pin;
|
||||||
enqueueCmd(cmd);
|
enqueueCmd(cmd);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Stops showing the bluetooth PIN screen.
|
/// Stops showing the bluetooth PIN screen.
|
||||||
void stopBluetoothPinScreen() { enqueueCmd(CmdItem{.cmd = Cmd::STOP_BLUETOOTH_PIN_SCREEN}); }
|
void stopBluetoothPinScreen() { enqueueCmd(ScreenCmd{.cmd = Cmd::STOP_BLUETOOTH_PIN_SCREEN}); }
|
||||||
|
|
||||||
/// Stops showing the boot screen.
|
/// Stops showing the boot screen.
|
||||||
void stopBootScreen() { enqueueCmd(CmdItem{.cmd = Cmd::STOP_BOOT_SCREEN}); }
|
void stopBootScreen() { enqueueCmd(ScreenCmd{.cmd = Cmd::STOP_BOOT_SCREEN}); }
|
||||||
|
|
||||||
/// Writes a string to the screen.
|
/// Writes a string to the screen.
|
||||||
void print(const char *text)
|
void print(const char *text)
|
||||||
{
|
{
|
||||||
CmdItem cmd;
|
ScreenCmd cmd;
|
||||||
cmd.cmd = Cmd::PRINT;
|
cmd.cmd = Cmd::PRINT;
|
||||||
// TODO(girts): strdup() here is scary, but we can't use std::string as
|
// TODO(girts): strdup() here is scary, but we can't use std::string as
|
||||||
// FreeRTOS queue is just dumbly copying memory contents. It would be
|
// FreeRTOS queue is just dumbly copying memory contents. It would be
|
||||||
@@ -149,10 +124,43 @@ class Screen : public PeriodicTask
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Overrides the default utf8 character conversion, to replace empty space with question marks
|
||||||
|
static char customFontTableLookup(const uint8_t ch) {
|
||||||
|
// UTF-8 to font table index converter
|
||||||
|
// Code form http://playground.arduino.cc/Main/Utf8ascii
|
||||||
|
static uint8_t LASTCHAR;
|
||||||
|
static bool SKIPREST; // Only display a single unconvertable-character symbol per sequence of unconvertable characters
|
||||||
|
|
||||||
|
if (ch < 128) { // Standard ASCII-set 0..0x7F handling
|
||||||
|
LASTCHAR = 0;
|
||||||
|
SKIPREST = false;
|
||||||
|
return ch;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t last = LASTCHAR; // get last char
|
||||||
|
LASTCHAR = ch;
|
||||||
|
|
||||||
|
switch (last) { // conversion depnding on first UTF8-character
|
||||||
|
case 0xC2: { SKIPREST = false; return (uint8_t) ch; }
|
||||||
|
case 0xC3: { SKIPREST = false; return (uint8_t) (ch | 0xC0); }
|
||||||
|
}
|
||||||
|
|
||||||
|
// We want to strip out prefix chars for two-byte char formats
|
||||||
|
if (ch == 0xC2 || ch == 0xC3 || ch == 0x82) return (uint8_t) 0;
|
||||||
|
|
||||||
|
// If we already returned an unconvertable-character symbol for this unconvertable-character sequence, return NULs for the rest of it
|
||||||
|
if (SKIPREST) return (uint8_t) 0;
|
||||||
|
SKIPREST = true;
|
||||||
|
|
||||||
|
return (uint8_t) 191; // otherwise: return ¿ if character can't be converted (note that the font map we're using doesn't stick to standard EASCII codes)
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns a handle to the DebugInfo screen.
|
/// Returns a handle to the DebugInfo screen.
|
||||||
//
|
//
|
||||||
// Use this handle to set things like battery status, user count, GPS status, etc.
|
// Use this handle to set things like battery status, user count, GPS status, etc.
|
||||||
DebugInfo *debug() { return &debugInfo; }
|
DebugInfo* debug_info() { return &debugInfo; }
|
||||||
|
|
||||||
|
int handleStatusUpdate(const meshtastic::Status *arg);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
/// Updates the UI.
|
/// Updates the UI.
|
||||||
@@ -161,17 +169,7 @@ class Screen : public PeriodicTask
|
|||||||
void doTask() final;
|
void doTask() final;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
enum class Cmd {
|
struct ScreenCmd {
|
||||||
INVALID,
|
|
||||||
SET_ON,
|
|
||||||
SET_OFF,
|
|
||||||
ON_PRESS,
|
|
||||||
START_BLUETOOTH_PIN_SCREEN,
|
|
||||||
STOP_BLUETOOTH_PIN_SCREEN,
|
|
||||||
STOP_BOOT_SCREEN,
|
|
||||||
PRINT,
|
|
||||||
};
|
|
||||||
struct CmdItem {
|
|
||||||
Cmd cmd;
|
Cmd cmd;
|
||||||
union {
|
union {
|
||||||
uint32_t bluetooth_pin;
|
uint32_t bluetooth_pin;
|
||||||
@@ -180,7 +178,7 @@ class Screen : public PeriodicTask
|
|||||||
};
|
};
|
||||||
|
|
||||||
/// Enques given command item to be processed by main loop().
|
/// Enques given command item to be processed by main loop().
|
||||||
bool enqueueCmd(const CmdItem &cmd)
|
bool enqueueCmd(const ScreenCmd &cmd)
|
||||||
{
|
{
|
||||||
if (!useDisplay)
|
if (!useDisplay)
|
||||||
return true; // claim success if our display is not in use
|
return true; // claim success if our display is not in use
|
||||||
@@ -204,7 +202,7 @@ class Screen : public PeriodicTask
|
|||||||
static void drawDebugInfoTrampoline(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y);
|
static void drawDebugInfoTrampoline(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y);
|
||||||
|
|
||||||
/// Queue of commands to execute in doTask.
|
/// Queue of commands to execute in doTask.
|
||||||
TypedQueue<CmdItem> cmdQueue;
|
TypedQueue<ScreenCmd> cmdQueue;
|
||||||
/// Whether we are using a display
|
/// Whether we are using a display
|
||||||
bool useDisplay = false;
|
bool useDisplay = false;
|
||||||
/// Whether the display is currently powered
|
/// Whether the display is currently powered
|
||||||
@@ -215,7 +213,9 @@ class Screen : public PeriodicTask
|
|||||||
|
|
||||||
/// Holds state for debug information
|
/// Holds state for debug information
|
||||||
DebugInfo debugInfo;
|
DebugInfo debugInfo;
|
||||||
|
|
||||||
/// Display device
|
/// Display device
|
||||||
|
/** @todo display abstraction */
|
||||||
#ifdef USE_SH1106
|
#ifdef USE_SH1106
|
||||||
SH1106Wire dispdev;
|
SH1106Wire dispdev;
|
||||||
#else
|
#else
|
||||||
@@ -225,4 +225,4 @@ class Screen : public PeriodicTask
|
|||||||
OLEDDisplayUi ui;
|
OLEDDisplayUi ui;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace meshtastic
|
} // namespace graphics
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "fonts.h"
|
||||||
|
|
||||||
|
#define FONT_HEIGHT 14 // actually 13 for "Arial 10" but want a little extra space
|
||||||
|
#define FONT_HEIGHT_16 (ArialMT_Plain_16[1] + 1)
|
||||||
|
// This means the *visible* area (sh1106 can address 132, but shows 128 for example)
|
||||||
|
#define SCREEN_WIDTH 128
|
||||||
|
#define SCREEN_HEIGHT 64
|
||||||
|
#define TRANSITION_FRAMERATE 30 // fps
|
||||||
|
#define IDLE_FRAMERATE 1 // in fps
|
||||||
|
#define COMPASS_DIAM 44
|
||||||
|
|
||||||
|
// DEBUG
|
||||||
|
#define NUM_EXTRA_FRAMES 2 // text message and debug frame
|
||||||
|
// if defined a pixel will blink to show redraws
|
||||||
|
// #define SHOW_REDRAWS
|
||||||
@@ -1,3 +1,5 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
const uint8_t Custom_ArialMT_Plain_10[] PROGMEM = {
|
const uint8_t Custom_ArialMT_Plain_10[] PROGMEM = {
|
||||||
0x0A, // Width: 10
|
0x0A, // Width: 10
|
||||||
0x0A, // Height: 10
|
0x0A, // Height: 10
|
||||||
@@ -1,16 +1,23 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
#define SATELLITE_IMAGE_WIDTH 16
|
#define SATELLITE_IMAGE_WIDTH 16
|
||||||
#define SATELLITE_IMAGE_HEIGHT 15
|
#define SATELLITE_IMAGE_HEIGHT 15
|
||||||
const uint8_t SATELLITE_IMAGE[] PROGMEM = {0x00, 0x08, 0x00, 0x1C, 0x00, 0x0E, 0x20, 0x07, 0x70, 0x02,
|
const uint8_t SATELLITE_IMAGE[] PROGMEM = {0x00, 0x08, 0x00, 0x1C, 0x00, 0x0E, 0x20, 0x07, 0x70, 0x02,
|
||||||
0xF8, 0x00, 0xF0, 0x01, 0xE0, 0x03, 0xC8, 0x01, 0x9C, 0x54,
|
0xF8, 0x00, 0xF0, 0x01, 0xE0, 0x03, 0xC8, 0x01, 0x9C, 0x54,
|
||||||
0x0E, 0x52, 0x07, 0x48, 0x02, 0x26, 0x00, 0x10, 0x00, 0x0E};
|
0x0E, 0x52, 0x07, 0x48, 0x02, 0x26, 0x00, 0x10, 0x00, 0x0E};
|
||||||
|
|
||||||
const
|
const uint8_t imgUSB[] PROGMEM = { 0x60, 0x60, 0x30, 0x18, 0x18, 0x18, 0x24, 0x42, 0x42, 0x42, 0x42, 0x7E, 0x24, 0x24, 0x24, 0x3C };
|
||||||
#include "icon.xbm"
|
const uint8_t imgPower[] PROGMEM = { 0x40, 0x40, 0x40, 0x58, 0x48, 0x08, 0x08, 0x08, 0x1C, 0x22, 0x22, 0x41, 0x7F, 0x22, 0x22, 0x22 };
|
||||||
|
const uint8_t imgUser[] PROGMEM = { 0x3C, 0x42, 0x99, 0xA5, 0xA5, 0x99, 0x42, 0x3C };
|
||||||
|
const uint8_t imgPositionEmpty[] PROGMEM = { 0x20, 0x30, 0x28, 0x24, 0x42, 0xFF };
|
||||||
|
const uint8_t imgPositionSolid[] PROGMEM = { 0x20, 0x30, 0x38, 0x3C, 0x7E, 0xFF };
|
||||||
|
|
||||||
|
#include "img/icon.xbm"
|
||||||
|
|
||||||
// We now programmatically draw our compass
|
// We now programmatically draw our compass
|
||||||
#if 0
|
#if 0
|
||||||
const
|
const
|
||||||
#include "compass.xbm"
|
#include "img/compass.xbm"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if 0
|
#if 0
|
||||||
@@ -1,50 +0,0 @@
|
|||||||
#include "lock.h"
|
|
||||||
|
|
||||||
#include <cassert>
|
|
||||||
|
|
||||||
namespace meshtastic
|
|
||||||
{
|
|
||||||
|
|
||||||
#ifdef configUSE_PREEMPTION
|
|
||||||
Lock::Lock()
|
|
||||||
{
|
|
||||||
handle = xSemaphoreCreateBinary();
|
|
||||||
assert(handle);
|
|
||||||
assert(xSemaphoreGive(handle));
|
|
||||||
}
|
|
||||||
|
|
||||||
void Lock::lock()
|
|
||||||
{
|
|
||||||
assert(xSemaphoreTake(handle, portMAX_DELAY));
|
|
||||||
}
|
|
||||||
|
|
||||||
void Lock::unlock()
|
|
||||||
{
|
|
||||||
assert(xSemaphoreGive(handle));
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
Lock::Lock()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void Lock::lock()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void Lock::unlock()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
LockGuard::LockGuard(Lock *lock) : lock(lock)
|
|
||||||
{
|
|
||||||
lock->lock();
|
|
||||||
}
|
|
||||||
|
|
||||||
LockGuard::~LockGuard()
|
|
||||||
{
|
|
||||||
lock->unlock();
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace meshtastic
|
|
||||||
-47
@@ -1,47 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include "freertosinc.h"
|
|
||||||
|
|
||||||
namespace meshtastic
|
|
||||||
{
|
|
||||||
|
|
||||||
// Simple wrapper around FreeRTOS API for implementing a mutex lock.
|
|
||||||
class Lock
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
Lock();
|
|
||||||
|
|
||||||
Lock(const Lock &) = delete;
|
|
||||||
Lock &operator=(const Lock &) = delete;
|
|
||||||
|
|
||||||
/// Locks the lock.
|
|
||||||
//
|
|
||||||
// Must not be called from an ISR.
|
|
||||||
void lock();
|
|
||||||
|
|
||||||
// Unlocks the lock.
|
|
||||||
//
|
|
||||||
// Must not be called from an ISR.
|
|
||||||
void unlock();
|
|
||||||
|
|
||||||
private:
|
|
||||||
#ifdef configUSE_PREEMPTION
|
|
||||||
SemaphoreHandle_t handle;
|
|
||||||
#endif
|
|
||||||
};
|
|
||||||
|
|
||||||
// RAII lock guard.
|
|
||||||
class LockGuard
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
LockGuard(Lock *lock);
|
|
||||||
~LockGuard();
|
|
||||||
|
|
||||||
LockGuard(const LockGuard &) = delete;
|
|
||||||
LockGuard &operator=(const LockGuard &) = delete;
|
|
||||||
|
|
||||||
private:
|
|
||||||
Lock *lock;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace meshtastic
|
|
||||||
+89
-37
@@ -25,7 +25,7 @@
|
|||||||
#include "MeshService.h"
|
#include "MeshService.h"
|
||||||
#include "NEMAGPS.h"
|
#include "NEMAGPS.h"
|
||||||
#include "NodeDB.h"
|
#include "NodeDB.h"
|
||||||
#include "Periodic.h"
|
#include "concurrency/Periodic.h"
|
||||||
#include "PowerFSM.h"
|
#include "PowerFSM.h"
|
||||||
#include "UBloxGPS.h"
|
#include "UBloxGPS.h"
|
||||||
#include "configuration.h"
|
#include "configuration.h"
|
||||||
@@ -35,13 +35,16 @@
|
|||||||
#include "DSRRouter.h"
|
#include "DSRRouter.h"
|
||||||
#include "debug.h"
|
#include "debug.h"
|
||||||
#include "main.h"
|
#include "main.h"
|
||||||
#include "screen.h"
|
#include "graphics/Screen.h"
|
||||||
#include "sleep.h"
|
#include "sleep.h"
|
||||||
|
#include "timing.h"
|
||||||
|
#include <OneButton.h>
|
||||||
#include <Wire.h>
|
#include <Wire.h>
|
||||||
// #include <driver/rtc_io.h>
|
// #include <driver/rtc_io.h>
|
||||||
|
|
||||||
#ifndef NO_ESP32
|
#ifndef NO_ESP32
|
||||||
#include "BluetoothUtil.h"
|
#include "BluetoothUtil.h"
|
||||||
|
#include "WiFi.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include "RF95Interface.h"
|
#include "RF95Interface.h"
|
||||||
@@ -52,10 +55,16 @@
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
// We always create a screen object, but we only init it if we find the hardware
|
// We always create a screen object, but we only init it if we find the hardware
|
||||||
meshtastic::Screen screen(SSD1306_ADDRESS);
|
graphics::Screen screen(SSD1306_ADDRESS);
|
||||||
|
|
||||||
// Global power status singleton
|
// Global power status
|
||||||
meshtastic::PowerStatus powerStatus;
|
meshtastic::PowerStatus *powerStatus = new meshtastic::PowerStatus();
|
||||||
|
|
||||||
|
// Global GPS status
|
||||||
|
meshtastic::GPSStatus *gpsStatus = new meshtastic::GPSStatus();
|
||||||
|
|
||||||
|
// Global Node status
|
||||||
|
meshtastic::NodeStatus *nodeStatus = new meshtastic::NodeStatus();
|
||||||
|
|
||||||
bool ssd1306_found;
|
bool ssd1306_found;
|
||||||
bool axp192_found;
|
bool axp192_found;
|
||||||
@@ -119,12 +128,49 @@ static uint32_t ledBlinker()
|
|||||||
setLed(ledOn);
|
setLed(ledOn);
|
||||||
|
|
||||||
// have a very sparse duty cycle of LED being on, unless charging, then blink 0.5Hz square wave rate to indicate that
|
// have a very sparse duty cycle of LED being on, unless charging, then blink 0.5Hz square wave rate to indicate that
|
||||||
return powerStatus.charging ? 1000 : (ledOn ? 2 : 1000);
|
return powerStatus->getIsCharging() ? 1000 : (ledOn ? 2 : 1000);
|
||||||
}
|
}
|
||||||
|
|
||||||
Periodic ledPeriodic(ledBlinker);
|
concurrency::Periodic ledPeriodic(ledBlinker);
|
||||||
|
|
||||||
|
// Prepare for button presses
|
||||||
|
#ifdef BUTTON_PIN
|
||||||
|
OneButton userButton;
|
||||||
|
#endif
|
||||||
|
#ifdef BUTTON_PIN_ALT
|
||||||
|
OneButton userButtonAlt;
|
||||||
|
#endif
|
||||||
|
void userButtonPressed()
|
||||||
|
{
|
||||||
|
powerFSM.trigger(EVENT_PRESS);
|
||||||
|
}
|
||||||
|
void userButtonPressedLong()
|
||||||
|
{
|
||||||
|
screen.adjustBrightness();
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifndef NO_ESP32
|
||||||
|
void initWifi()
|
||||||
|
{
|
||||||
|
strcpy(radioConfig.preferences.wifi_ssid, "geeksville");
|
||||||
|
strcpy(radioConfig.preferences.wifi_password, "xxx");
|
||||||
|
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);
|
||||||
|
// WiFi.begin(wifiName, wifiPsw);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else
|
||||||
|
DEBUG_MSG("Not using WIFI\n");
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
void setup()
|
void setup()
|
||||||
{
|
{
|
||||||
@@ -161,8 +207,14 @@ void setup()
|
|||||||
|
|
||||||
// Buttons & LED
|
// Buttons & LED
|
||||||
#ifdef BUTTON_PIN
|
#ifdef BUTTON_PIN
|
||||||
pinMode(BUTTON_PIN, INPUT_PULLUP);
|
userButton = OneButton(BUTTON_PIN, true, true);
|
||||||
digitalWrite(BUTTON_PIN, 1);
|
userButton.attachClick(userButtonPressed);
|
||||||
|
userButton.attachDuringLongPress(userButtonPressedLong);
|
||||||
|
#endif
|
||||||
|
#ifdef BUTTON_PIN_ALT
|
||||||
|
userButtonAlt = OneButton(BUTTON_PIN_ALT, true, true);
|
||||||
|
userButtonAlt.attachClick(userButtonPressed);
|
||||||
|
userButton.attachDuringLongPress(userButtonPressedLong);
|
||||||
#endif
|
#endif
|
||||||
#ifdef LED_PIN
|
#ifdef LED_PIN
|
||||||
pinMode(LED_PIN, OUTPUT);
|
pinMode(LED_PIN, OUTPUT);
|
||||||
@@ -182,6 +234,14 @@ void setup()
|
|||||||
esp32Setup();
|
esp32Setup();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef TBEAM_V10
|
||||||
|
// Currently only the tbeam has a PMU
|
||||||
|
power = new Power();
|
||||||
|
power->setup();
|
||||||
|
power->setStatusHandler(powerStatus);
|
||||||
|
powerStatus->observe(&power->newStatus);
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifdef NRF52_SERIES
|
#ifdef NRF52_SERIES
|
||||||
nrf52Setup();
|
nrf52Setup();
|
||||||
#endif
|
#endif
|
||||||
@@ -210,8 +270,14 @@ void setup()
|
|||||||
gps = new NEMAGPS();
|
gps = new NEMAGPS();
|
||||||
gps->setup();
|
gps->setup();
|
||||||
#endif
|
#endif
|
||||||
|
gpsStatus->observe(&gps->newStatus);
|
||||||
|
nodeStatus->observe(&nodeDB.newStatus);
|
||||||
|
|
||||||
service.init();
|
service.init();
|
||||||
|
#ifndef NO_ESP32
|
||||||
|
// Must be after we init the service, because the wifi settings are loaded by NodeDB (oops)
|
||||||
|
initWifi();
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifdef SX1262_ANT_SW
|
#ifdef SX1262_ANT_SW
|
||||||
// make analog PA vs not PA switch on SX1262 eval board work properly
|
// make analog PA vs not PA switch on SX1262 eval board work properly
|
||||||
@@ -268,7 +334,7 @@ uint32_t axpDebugRead()
|
|||||||
return 30 * 1000;
|
return 30 * 1000;
|
||||||
}
|
}
|
||||||
|
|
||||||
Periodic axpDebugOutput(axpDebugRead);
|
concurrency::Periodic axpDebugOutput(axpDebugRead);
|
||||||
axpDebugOutput.setup();
|
axpDebugOutput.setup();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@@ -281,7 +347,7 @@ void loop()
|
|||||||
powerFSM.run_machine();
|
powerFSM.run_machine();
|
||||||
service.loop();
|
service.loop();
|
||||||
|
|
||||||
periodicScheduler.loop();
|
concurrency::periodicScheduler.loop();
|
||||||
// axpDebugOutput.loop();
|
// axpDebugOutput.loop();
|
||||||
|
|
||||||
#ifdef DEBUG_PORT
|
#ifdef DEBUG_PORT
|
||||||
@@ -293,49 +359,35 @@ void loop()
|
|||||||
#ifndef NO_ESP32
|
#ifndef NO_ESP32
|
||||||
esp32Loop();
|
esp32Loop();
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef TBEAM_V10
|
||||||
|
power->loop();
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifdef BUTTON_PIN
|
#ifdef BUTTON_PIN
|
||||||
// if user presses button for more than 3 secs, discard our network prefs and reboot (FIXME, use a debounce lib instead of
|
userButton.tick();
|
||||||
// this boilerplate)
|
#endif
|
||||||
static bool wasPressed = false;
|
#ifdef BUTTON_PIN_ALT
|
||||||
|
userButtonAlt.tick();
|
||||||
if (!digitalRead(BUTTON_PIN)) {
|
|
||||||
if (!wasPressed) { // just started a new press
|
|
||||||
DEBUG_MSG("pressing\n");
|
|
||||||
|
|
||||||
// doLightSleep();
|
|
||||||
// esp_pm_dump_locks(stdout); // FIXME, do this someplace better
|
|
||||||
wasPressed = true;
|
|
||||||
|
|
||||||
powerFSM.trigger(EVENT_PRESS);
|
|
||||||
}
|
|
||||||
} else if (wasPressed) {
|
|
||||||
// we just did a release
|
|
||||||
wasPressed = false;
|
|
||||||
}
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Show boot screen for first 3 seconds, then switch to normal operation.
|
// Show boot screen for first 3 seconds, then switch to normal operation.
|
||||||
static bool showingBootScreen = true;
|
static bool showingBootScreen = true;
|
||||||
if (showingBootScreen && (millis() > 3000)) {
|
if (showingBootScreen && (timing::millis() > 3000)) {
|
||||||
screen.stopBootScreen();
|
screen.stopBootScreen();
|
||||||
showingBootScreen = false;
|
showingBootScreen = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef DEBUG_STACK
|
#ifdef DEBUG_STACK
|
||||||
static uint32_t lastPrint = 0;
|
static uint32_t lastPrint = 0;
|
||||||
if (millis() - lastPrint > 10 * 1000L) {
|
if (timing::millis() - lastPrint > 10 * 1000L) {
|
||||||
lastPrint = millis();
|
lastPrint = timing::millis();
|
||||||
meshtastic::printThreadInfo("main");
|
meshtastic::printThreadInfo("main");
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Update the screen last, after we've figured out what to show.
|
// Update the screen last, after we've figured out what to show.
|
||||||
screen.debug()->setNodeNumbersStatus(nodeDB.getNumOnlineNodes(), nodeDB.getNumNodes());
|
screen.debug_info()->setChannelNameStatus(channelSettings.name);
|
||||||
screen.debug()->setChannelNameStatus(channelSettings.name);
|
// screen.debug()->setPowerStatus(powerStatus);
|
||||||
screen.debug()->setPowerStatus(powerStatus);
|
|
||||||
// TODO(#4): use something based on hdop to show GPS "signal" strength.
|
|
||||||
screen.debug()->setGPSStatus(gps->isConnected ? (gps->hasLock() ? "GPS ok" : "No Sats") : "No GPS");
|
|
||||||
|
|
||||||
// No GPS lock yet, let the OS put the main CPU in low power mode for 100ms (or until another interrupt comes in)
|
// 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.
|
// i.e. don't just keep spinning in loop as fast as we can.
|
||||||
|
|||||||
+10
-2
@@ -1,6 +1,9 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "screen.h"
|
#include "graphics/Screen.h"
|
||||||
|
#include "PowerStatus.h"
|
||||||
|
#include "GPSStatus.h"
|
||||||
|
#include "NodeStatus.h"
|
||||||
|
|
||||||
extern bool axp192_found;
|
extern bool axp192_found;
|
||||||
extern bool ssd1306_found;
|
extern bool ssd1306_found;
|
||||||
@@ -8,7 +11,12 @@ extern bool isCharging;
|
|||||||
extern bool isUSBPowered;
|
extern bool isUSBPowered;
|
||||||
|
|
||||||
// Global Screen singleton.
|
// Global Screen singleton.
|
||||||
extern meshtastic::Screen screen;
|
extern graphics::Screen screen;
|
||||||
|
//extern Observable<meshtastic::PowerStatus> newPowerStatus; //TODO: move this to main-esp32.cpp somehow or a helper class
|
||||||
|
|
||||||
|
//extern meshtastic::PowerStatus *powerStatus;
|
||||||
|
//extern meshtastic::GPSStatus *gpsStatus;
|
||||||
|
//extern meshtastic::NodeStatusHandler *nodeStatusHandler;
|
||||||
|
|
||||||
// Return a human readable string of the form "Meshtastic_ab13"
|
// Return a human readable string of the form "Meshtastic_ab13"
|
||||||
const char *getDeviceName();
|
const char *getDeviceName();
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "PacketHistory.h"
|
#include "PacketHistory.h"
|
||||||
#include "PeriodicTask.h"
|
#include "../concurrency/PeriodicTask.h"
|
||||||
#include "Router.h"
|
#include "Router.h"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -7,10 +7,13 @@
|
|||||||
//#include "MeshBluetoothService.h"
|
//#include "MeshBluetoothService.h"
|
||||||
#include "MeshService.h"
|
#include "MeshService.h"
|
||||||
#include "NodeDB.h"
|
#include "NodeDB.h"
|
||||||
#include "Periodic.h"
|
#include "../concurrency/Periodic.h"
|
||||||
#include "PowerFSM.h"
|
#include "PowerFSM.h"
|
||||||
#include "main.h"
|
#include "main.h"
|
||||||
#include "mesh-pb-constants.h"
|
#include "mesh-pb-constants.h"
|
||||||
|
#include "power.h"
|
||||||
|
#include "BluetoothUtil.h" // needed for updateBatteryLevel, FIXME, eventually when we pull mesh out into a lib we shouldn't be whacking bluetooth from here
|
||||||
|
#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.
|
receivedPacketQueue - this is a queue of messages we've received from the mesh, which we are keeping to deliver to the phone.
|
||||||
@@ -53,7 +56,7 @@ static uint32_t sendOwnerCb()
|
|||||||
return radioConfig.preferences.send_owner_interval * radioConfig.preferences.position_broadcast_secs * 1000;
|
return radioConfig.preferences.send_owner_interval * radioConfig.preferences.position_broadcast_secs * 1000;
|
||||||
}
|
}
|
||||||
|
|
||||||
static Periodic sendOwnerPeriod(sendOwnerCb);
|
static concurrency::Periodic sendOwnerPeriod(sendOwnerCb);
|
||||||
|
|
||||||
MeshService::MeshService() : toPhoneQueue(MAX_RX_TOPHONE)
|
MeshService::MeshService() : toPhoneQueue(MAX_RX_TOPHONE)
|
||||||
{
|
{
|
||||||
@@ -280,6 +283,8 @@ void MeshService::sendOurPosition(NodeNum dest, bool wantReplies)
|
|||||||
sendToMesh(p);
|
sendToMesh(p);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
int MeshService::onGPSChanged(void *unused)
|
int MeshService::onGPSChanged(void *unused)
|
||||||
{
|
{
|
||||||
// DEBUG_MSG("got gps notify\n");
|
// DEBUG_MSG("got gps notify\n");
|
||||||
@@ -298,9 +303,13 @@ int MeshService::onGPSChanged(void *unused)
|
|||||||
pos.time = getValidTime();
|
pos.time = getValidTime();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Include our current battery voltage in our position announcement
|
||||||
|
pos.battery_level = powerStatus->getBatteryChargePercent();
|
||||||
|
updateBatteryLevel(pos.battery_level);
|
||||||
|
|
||||||
// We limit our GPS broadcasts to a max rate
|
// We limit our GPS broadcasts to a max rate
|
||||||
static uint32_t lastGpsSend;
|
static uint32_t lastGpsSend;
|
||||||
uint32_t now = millis();
|
uint32_t now = timing::millis();
|
||||||
if (lastGpsSend == 0 || now - lastGpsSend > radioConfig.preferences.position_broadcast_secs * 1000) {
|
if (lastGpsSend == 0 || now - lastGpsSend > radioConfig.preferences.position_broadcast_secs * 1000) {
|
||||||
lastGpsSend = now;
|
lastGpsSend = now;
|
||||||
DEBUG_MSG("Sending position to mesh\n");
|
DEBUG_MSG("Sending position to mesh\n");
|
||||||
|
|||||||
+40
-30
@@ -19,7 +19,7 @@
|
|||||||
NodeDB nodeDB;
|
NodeDB nodeDB;
|
||||||
|
|
||||||
// we have plenty of ram so statically alloc this tempbuf (for now)
|
// we have plenty of ram so statically alloc this tempbuf (for now)
|
||||||
DeviceState devicestate;
|
EXT_RAM_ATTR DeviceState devicestate;
|
||||||
MyNodeInfo &myNodeInfo = devicestate.my_node;
|
MyNodeInfo &myNodeInfo = devicestate.my_node;
|
||||||
RadioConfig &radioConfig = devicestate.radio;
|
RadioConfig &radioConfig = devicestate.radio;
|
||||||
ChannelSettings &channelSettings = radioConfig.channel_settings;
|
ChannelSettings &channelSettings = radioConfig.channel_settings;
|
||||||
@@ -103,13 +103,16 @@ void NodeDB::resetRadioConfig()
|
|||||||
crypto->setKey(channelSettings.psk.size, channelSettings.psk.bytes);
|
crypto->setKey(channelSettings.psk.size, channelSettings.psk.bytes);
|
||||||
|
|
||||||
// temp hack for quicker testing
|
// temp hack for quicker testing
|
||||||
|
// devicestate.no_save = true;
|
||||||
|
if (devicestate.no_save) {
|
||||||
|
DEBUG_MSG("***** DEVELOPMENT MODE - DO NOT RELEASE *****\n");
|
||||||
|
|
||||||
/*
|
// Sleep quite frequently to stress test the BLE comms, broadcast position every 6 mins
|
||||||
radioConfig.preferences.screen_on_secs = 30;
|
radioConfig.preferences.screen_on_secs = 30;
|
||||||
radioConfig.preferences.wait_bluetooth_secs = 30;
|
radioConfig.preferences.wait_bluetooth_secs = 30;
|
||||||
radioConfig.preferences.position_broadcast_secs = 6 * 60;
|
radioConfig.preferences.position_broadcast_secs = 6 * 60;
|
||||||
radioConfig.preferences.ls_secs = 60;
|
radioConfig.preferences.ls_secs = 60;
|
||||||
*/
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void NodeDB::installDefaultDeviceState()
|
void NodeDB::installDefaultDeviceState()
|
||||||
@@ -257,32 +260,36 @@ void NodeDB::loadFromDisk()
|
|||||||
void NodeDB::saveToDisk()
|
void NodeDB::saveToDisk()
|
||||||
{
|
{
|
||||||
#ifdef FS
|
#ifdef FS
|
||||||
auto f = FS.open(preftmp, FILE_O_WRITE);
|
if (!devicestate.no_save) {
|
||||||
if (f) {
|
auto f = FS.open(preftmp, FILE_O_WRITE);
|
||||||
DEBUG_MSG("Writing preferences\n");
|
if (f) {
|
||||||
|
DEBUG_MSG("Writing preferences\n");
|
||||||
|
|
||||||
pb_ostream_t stream = {&writecb, &f, SIZE_MAX, 0};
|
pb_ostream_t stream = {&writecb, &f, SIZE_MAX, 0};
|
||||||
|
|
||||||
// DEBUG_MSG("Presave channel name=%s\n", channelSettings.name);
|
// DEBUG_MSG("Presave channel name=%s\n", channelSettings.name);
|
||||||
|
|
||||||
devicestate.version = DEVICESTATE_CUR_VER;
|
devicestate.version = DEVICESTATE_CUR_VER;
|
||||||
if (!pb_encode(&stream, DeviceState_fields, &devicestate)) {
|
if (!pb_encode(&stream, DeviceState_fields, &devicestate)) {
|
||||||
DEBUG_MSG("Error: can't write protobuf %s\n", PB_GET_ERROR(&stream));
|
DEBUG_MSG("Error: can't write protobuf %s\n", PB_GET_ERROR(&stream));
|
||||||
// FIXME - report failure to phone
|
// FIXME - report failure to phone
|
||||||
|
|
||||||
f.close();
|
f.close();
|
||||||
|
} else {
|
||||||
|
// Success - replace the old file
|
||||||
|
f.close();
|
||||||
|
|
||||||
|
// brief window of risk here ;-)
|
||||||
|
if (!FS.remove(preffile))
|
||||||
|
DEBUG_MSG("Warning: Can't remove old pref file\n");
|
||||||
|
if (!FS.rename(preftmp, preffile))
|
||||||
|
DEBUG_MSG("Error: can't rename new pref file\n");
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
// Success - replace the old file
|
DEBUG_MSG("ERROR: can't write prefs\n"); // FIXME report to app
|
||||||
f.close();
|
|
||||||
|
|
||||||
// brief window of risk here ;-)
|
|
||||||
if (!FS.remove(preffile))
|
|
||||||
DEBUG_MSG("Warning: Can't remove old pref file\n");
|
|
||||||
if (!FS.rename(preftmp, preffile))
|
|
||||||
DEBUG_MSG("Error: can't rename new pref file\n");
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
DEBUG_MSG("ERROR: can't write prefs\n"); // FIXME report to app
|
DEBUG_MSG("***** DEVELOPMENT MODE - DO NOT RELEASE - not saving to flash *****\n");
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
DEBUG_MSG("ERROR filesystem not implemented\n");
|
DEBUG_MSG("ERROR filesystem not implemented\n");
|
||||||
@@ -332,12 +339,8 @@ void NodeDB::updateFrom(const MeshPacket &mp)
|
|||||||
const SubPacket &p = mp.decoded;
|
const SubPacket &p = mp.decoded;
|
||||||
DEBUG_MSG("Update DB node 0x%x, rx_time=%u\n", mp.from, mp.rx_time);
|
DEBUG_MSG("Update DB node 0x%x, rx_time=%u\n", mp.from, mp.rx_time);
|
||||||
|
|
||||||
int oldNumNodes = *numNodes;
|
|
||||||
NodeInfo *info = getOrCreateNode(mp.from);
|
NodeInfo *info = getOrCreateNode(mp.from);
|
||||||
|
|
||||||
if (oldNumNodes != *numNodes)
|
|
||||||
updateGUI = true; // we just created a nodeinfo
|
|
||||||
|
|
||||||
if (mp.rx_time) { // if the packet has a valid timestamp use it to update our last_seen
|
if (mp.rx_time) { // if the packet has a valid timestamp use it to update our last_seen
|
||||||
info->has_position = true; // at least the time is valid
|
info->has_position = true; // at least the time is valid
|
||||||
info->position.time = mp.rx_time;
|
info->position.time = mp.rx_time;
|
||||||
@@ -353,6 +356,7 @@ void NodeDB::updateFrom(const MeshPacket &mp)
|
|||||||
info->position.time = oldtime;
|
info->position.time = oldtime;
|
||||||
info->has_position = true;
|
info->has_position = true;
|
||||||
updateGUIforNode = info;
|
updateGUIforNode = info;
|
||||||
|
notifyObservers(true); //Force an update whether or not our node counts have changed
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -367,6 +371,7 @@ void NodeDB::updateFrom(const MeshPacket &mp)
|
|||||||
devicestate.has_rx_text_message = true;
|
devicestate.has_rx_text_message = true;
|
||||||
updateTextMessage = true;
|
updateTextMessage = true;
|
||||||
powerFSM.trigger(EVENT_RECEIVED_TEXT_MSG);
|
powerFSM.trigger(EVENT_RECEIVED_TEXT_MSG);
|
||||||
|
notifyObservers(true); //Force an update whether or not our node counts have changed
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@@ -385,6 +390,7 @@ void NodeDB::updateFrom(const MeshPacket &mp)
|
|||||||
if (changed) {
|
if (changed) {
|
||||||
updateGUIforNode = info;
|
updateGUIforNode = info;
|
||||||
powerFSM.trigger(EVENT_NODEDB_UPDATED);
|
powerFSM.trigger(EVENT_NODEDB_UPDATED);
|
||||||
|
notifyObservers(true); //Force an update whether or not our node counts have changed
|
||||||
|
|
||||||
// Not really needed - we will save anyways when we go to sleep
|
// Not really needed - we will save anyways when we go to sleep
|
||||||
// We just changed something important about the user, store our DB
|
// We just changed something important about the user, store our DB
|
||||||
@@ -392,6 +398,10 @@ void NodeDB::updateFrom(const MeshPacket &mp)
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
default: {
|
||||||
|
notifyObservers(); //If the node counts have changed, notify observers
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,9 +2,11 @@
|
|||||||
|
|
||||||
#include <Arduino.h>
|
#include <Arduino.h>
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
|
#include "Observer.h"
|
||||||
|
|
||||||
#include "MeshTypes.h"
|
#include "MeshTypes.h"
|
||||||
#include "mesh-pb-constants.h"
|
#include "mesh-pb-constants.h"
|
||||||
|
#include "NodeStatus.h"
|
||||||
|
|
||||||
extern DeviceState devicestate;
|
extern DeviceState devicestate;
|
||||||
extern MyNodeInfo &myNodeInfo;
|
extern MyNodeInfo &myNodeInfo;
|
||||||
@@ -32,6 +34,7 @@ class NodeDB
|
|||||||
bool updateGUI = false; // we think the gui should definitely be redrawn, screen will clear this once handled
|
bool updateGUI = false; // we think the gui should definitely be redrawn, screen will clear this once handled
|
||||||
NodeInfo *updateGUIforNode = NULL; // if currently showing this node, we think you should update the GUI
|
NodeInfo *updateGUIforNode = NULL; // if currently showing this node, we think you should update the GUI
|
||||||
bool updateTextMessage = false; // if true, the GUI should show a new text message
|
bool updateTextMessage = false; // if true, the GUI should show a new text message
|
||||||
|
Observable<const meshtastic::NodeStatus *> newStatus;
|
||||||
|
|
||||||
/// don't do mesh based algoritm for node id assignment (initially)
|
/// don't do mesh based algoritm for node id assignment (initially)
|
||||||
/// instead just store in flash - possibly even in the initial alpha release do this hack
|
/// instead just store in flash - possibly even in the initial alpha release do this hack
|
||||||
@@ -91,6 +94,13 @@ class NodeDB
|
|||||||
/// Find a node in our DB, create an empty NodeInfo if missing
|
/// Find a node in our DB, create an empty NodeInfo if missing
|
||||||
NodeInfo *getOrCreateNode(NodeNum n);
|
NodeInfo *getOrCreateNode(NodeNum n);
|
||||||
|
|
||||||
|
/// Notify observers of changes to the DB
|
||||||
|
void notifyObservers(bool forceUpdate = false) {
|
||||||
|
// Notify observers of the current node state
|
||||||
|
const meshtastic::NodeStatus status = meshtastic::NodeStatus(getNumOnlineNodes(), getNumNodes(), forceUpdate);
|
||||||
|
newStatus.notifyObservers(&status);
|
||||||
|
}
|
||||||
|
|
||||||
/// read our db from flash
|
/// read our db from flash
|
||||||
void loadFromDisk();
|
void loadFromDisk();
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
#include "PacketHistory.h"
|
#include "PacketHistory.h"
|
||||||
#include "configuration.h"
|
#include "configuration.h"
|
||||||
#include "mesh-pb-constants.h"
|
#include "mesh-pb-constants.h"
|
||||||
|
#include "../timing.h"
|
||||||
|
|
||||||
PacketHistory::PacketHistory()
|
PacketHistory::PacketHistory()
|
||||||
{
|
{
|
||||||
@@ -18,7 +19,7 @@ bool PacketHistory::wasSeenRecently(const MeshPacket *p, bool withUpdate)
|
|||||||
return false; // Not a floodable message ID, so we don't care
|
return false; // Not a floodable message ID, so we don't care
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t now = millis();
|
uint32_t now = timing::millis();
|
||||||
for (size_t i = 0; i < recentPackets.size();) {
|
for (size_t i = 0; i < recentPackets.size();) {
|
||||||
PacketRecord &r = recentPackets[i];
|
PacketRecord &r = recentPackets[i];
|
||||||
|
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
#include "PowerFSM.h"
|
#include "PowerFSM.h"
|
||||||
#include "RadioInterface.h"
|
#include "RadioInterface.h"
|
||||||
#include "GPS.h"
|
#include "GPS.h"
|
||||||
|
#include "timing.h"
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
|
|
||||||
PhoneAPI::PhoneAPI()
|
PhoneAPI::PhoneAPI()
|
||||||
@@ -20,7 +21,7 @@ void PhoneAPI::init()
|
|||||||
void PhoneAPI::checkConnectionTimeout()
|
void PhoneAPI::checkConnectionTimeout()
|
||||||
{
|
{
|
||||||
if (isConnected) {
|
if (isConnected) {
|
||||||
bool newConnected = (millis() - lastContactMsec < radioConfig.preferences.phone_timeout_secs * 1000L);
|
bool newConnected = (timing::millis() - lastContactMsec < radioConfig.preferences.phone_timeout_secs * 1000L);
|
||||||
if (!newConnected) {
|
if (!newConnected) {
|
||||||
isConnected = false;
|
isConnected = false;
|
||||||
onConnectionChanged(isConnected);
|
onConnectionChanged(isConnected);
|
||||||
@@ -34,7 +35,7 @@ void PhoneAPI::checkConnectionTimeout()
|
|||||||
void PhoneAPI::handleToRadio(const uint8_t *buf, size_t bufLength)
|
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
|
powerFSM.trigger(EVENT_CONTACT_FROM_PHONE); // As long as the phone keeps talking to us, don't let the radio go to sleep
|
||||||
lastContactMsec = millis();
|
lastContactMsec = timing::millis();
|
||||||
if (!isConnected) {
|
if (!isConnected) {
|
||||||
isConnected = true;
|
isConnected = true;
|
||||||
onConnectionChanged(isConnected);
|
onConnectionChanged(isConnected);
|
||||||
|
|||||||
@@ -6,6 +6,7 @@
|
|||||||
#include "assert.h"
|
#include "assert.h"
|
||||||
#include "configuration.h"
|
#include "configuration.h"
|
||||||
#include "sleep.h"
|
#include "sleep.h"
|
||||||
|
#include "timing.h"
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <pb_decode.h>
|
#include <pb_decode.h>
|
||||||
#include <pb_encode.h>
|
#include <pb_encode.h>
|
||||||
@@ -155,7 +156,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());
|
// DEBUG_MSG("sending queued packet on mesh (txGood=%d,rxGood=%d,rxBad=%d)\n", rf95.txGood(), rf95.rxGood(), rf95.rxBad());
|
||||||
assert(p->which_payload == MeshPacket_encrypted_tag); // It should have already been encoded by now
|
assert(p->which_payload == MeshPacket_encrypted_tag); // It should have already been encoded by now
|
||||||
|
|
||||||
lastTxStart = millis();
|
lastTxStart = timing::millis();
|
||||||
|
|
||||||
PacketHeader *h = (PacketHeader *)radiobuf;
|
PacketHeader *h = (PacketHeader *)radiobuf;
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
#include "MeshTypes.h"
|
#include "MeshTypes.h"
|
||||||
#include "Observer.h"
|
#include "Observer.h"
|
||||||
#include "PointerQueue.h"
|
#include "PointerQueue.h"
|
||||||
#include "WorkerThread.h"
|
#include "../concurrency/NotifiedWorkerThread.h"
|
||||||
#include "mesh.pb.h"
|
#include "mesh.pb.h"
|
||||||
|
|
||||||
#define MAX_TX_QUEUE 16 // max number of packets which can be waiting for transmission
|
#define MAX_TX_QUEUE 16 // max number of packets which can be waiting for transmission
|
||||||
@@ -43,7 +43,7 @@ typedef enum {
|
|||||||
*
|
*
|
||||||
* This defines the SOLE API for talking to radios (because soon we will have alternate radio implementations)
|
* This defines the SOLE API for talking to radios (because soon we will have alternate radio implementations)
|
||||||
*/
|
*/
|
||||||
class RadioInterface : protected NotifiedWorkerThread
|
class RadioInterface : protected concurrency::NotifiedWorkerThread
|
||||||
{
|
{
|
||||||
friend class MeshRadio; // for debugging we let that class touch pool
|
friend class MeshRadio; // for debugging we let that class touch pool
|
||||||
PointerQueue<MeshPacket> *rxDest = NULL;
|
PointerQueue<MeshPacket> *rxDest = NULL;
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ static SPISettings spiSettings(4000000, MSBFIRST, SPI_MODE0);
|
|||||||
|
|
||||||
RadioLibInterface::RadioLibInterface(RADIOLIB_PIN_TYPE cs, RADIOLIB_PIN_TYPE irq, RADIOLIB_PIN_TYPE rst, RADIOLIB_PIN_TYPE busy,
|
RadioLibInterface::RadioLibInterface(RADIOLIB_PIN_TYPE cs, RADIOLIB_PIN_TYPE irq, RADIOLIB_PIN_TYPE rst, RADIOLIB_PIN_TYPE busy,
|
||||||
SPIClass &spi, PhysicalLayer *_iface)
|
SPIClass &spi, PhysicalLayer *_iface)
|
||||||
: PeriodicTask(0), module(cs, irq, rst, busy, spi, spiSettings), iface(_iface)
|
: concurrency::PeriodicTask(0), module(cs, irq, rst, busy, spi, spiSettings), iface(_iface)
|
||||||
{
|
{
|
||||||
assert(!instance); // We assume only one for now
|
assert(!instance); // We assume only one for now
|
||||||
instance = this;
|
instance = this;
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "PeriodicTask.h"
|
#include "../concurrency/PeriodicTask.h"
|
||||||
#include "RadioInterface.h"
|
#include "RadioInterface.h"
|
||||||
|
|
||||||
#ifdef CubeCell_BoardPlus
|
#ifdef CubeCell_BoardPlus
|
||||||
@@ -16,7 +16,7 @@
|
|||||||
#define INTERRUPT_ATTR
|
#define INTERRUPT_ATTR
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
class RadioLibInterface : public RadioInterface, private PeriodicTask
|
class RadioLibInterface : public RadioInterface, private concurrency::PeriodicTask
|
||||||
{
|
{
|
||||||
/// Used as our notification from the ISR
|
/// Used as our notification from the ISR
|
||||||
enum PendingISR { ISR_NONE = 0, ISR_RX, ISR_TX, TRANSMIT_DELAY_COMPLETED };
|
enum PendingISR { ISR_NONE = 0, ISR_RX, ISR_TX, TRANSMIT_DELAY_COMPLETED };
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
#include "MeshTypes.h"
|
#include "MeshTypes.h"
|
||||||
#include "configuration.h"
|
#include "configuration.h"
|
||||||
#include "mesh-pb-constants.h"
|
#include "mesh-pb-constants.h"
|
||||||
|
#include "timing.h"
|
||||||
|
|
||||||
// ReliableRouter::ReliableRouter() {}
|
// ReliableRouter::ReliableRouter() {}
|
||||||
|
|
||||||
@@ -162,7 +163,7 @@ PendingPacket *ReliableRouter::startRetransmission(MeshPacket *p)
|
|||||||
*/
|
*/
|
||||||
void ReliableRouter::doRetransmissions()
|
void ReliableRouter::doRetransmissions()
|
||||||
{
|
{
|
||||||
uint32_t now = millis();
|
uint32_t now = timing::millis();
|
||||||
|
|
||||||
// FIXME, we should use a better datastructure rather than walking through this map.
|
// FIXME, we should use a better datastructure rather than walking through this map.
|
||||||
// for(auto el: pending) {
|
// for(auto el: pending) {
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "FloodingRouter.h"
|
#include "FloodingRouter.h"
|
||||||
#include "PeriodicTask.h"
|
#include "../concurrency/PeriodicTask.h"
|
||||||
|
#include "../timing.h"
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -48,7 +49,7 @@ struct PendingPacket {
|
|||||||
PendingPacket() {}
|
PendingPacket() {}
|
||||||
PendingPacket(MeshPacket *p);
|
PendingPacket(MeshPacket *p);
|
||||||
|
|
||||||
void setNextTx() { nextTxMsec = millis() + random(20 * 1000L, 22 * 1000L); }
|
void setNextTx() { nextTxMsec = timing::millis() + random(20 * 1000L, 22 * 1000L); }
|
||||||
};
|
};
|
||||||
|
|
||||||
class GlobalPacketIdHashFunction
|
class GlobalPacketIdHashFunction
|
||||||
|
|||||||
+36
-18
@@ -45,6 +45,10 @@ typedef struct _ChannelSettings {
|
|||||||
ChannelSettings_ModemConfig modem_config;
|
ChannelSettings_ModemConfig modem_config;
|
||||||
ChannelSettings_psk_t psk;
|
ChannelSettings_psk_t psk;
|
||||||
char name[12];
|
char name[12];
|
||||||
|
uint32_t bandwidth;
|
||||||
|
uint32_t spread_factor;
|
||||||
|
uint32_t coding_rate;
|
||||||
|
uint32_t channel_num;
|
||||||
} ChannelSettings;
|
} ChannelSettings;
|
||||||
|
|
||||||
typedef PB_BYTES_ARRAY_T(240) Data_payload_t;
|
typedef PB_BYTES_ARRAY_T(240) Data_payload_t;
|
||||||
@@ -101,8 +105,9 @@ typedef struct _RadioConfig_UserPreferences {
|
|||||||
uint32_t sds_secs;
|
uint32_t sds_secs;
|
||||||
uint32_t ls_secs;
|
uint32_t ls_secs;
|
||||||
uint32_t min_wake_secs;
|
uint32_t min_wake_secs;
|
||||||
bool keep_all_packets;
|
char wifi_ssid[33];
|
||||||
bool promiscuous_mode;
|
char wifi_password[64];
|
||||||
|
bool wifi_ap_mode;
|
||||||
pb_size_t ignore_incoming_count;
|
pb_size_t ignore_incoming_count;
|
||||||
uint32_t ignore_incoming[3];
|
uint32_t ignore_incoming[3];
|
||||||
} RadioConfig_UserPreferences;
|
} RadioConfig_UserPreferences;
|
||||||
@@ -187,6 +192,7 @@ typedef struct _DeviceState {
|
|||||||
bool has_rx_text_message;
|
bool has_rx_text_message;
|
||||||
MeshPacket rx_text_message;
|
MeshPacket rx_text_message;
|
||||||
uint32_t version;
|
uint32_t version;
|
||||||
|
bool no_save;
|
||||||
} DeviceState;
|
} DeviceState;
|
||||||
|
|
||||||
typedef struct _FromRadio {
|
typedef struct _FromRadio {
|
||||||
@@ -239,12 +245,12 @@ typedef struct _ToRadio {
|
|||||||
#define RouteDiscovery_init_default {0, {0, 0, 0, 0, 0, 0, 0, 0}}
|
#define RouteDiscovery_init_default {0, {0, 0, 0, 0, 0, 0, 0, 0}}
|
||||||
#define SubPacket_init_default {0, {Position_init_default}, 0, 0, 0, 0, {0}, 0}
|
#define SubPacket_init_default {0, {Position_init_default}, 0, 0, 0, 0, {0}, 0}
|
||||||
#define MeshPacket_init_default {0, 0, 0, {SubPacket_init_default}, 0, 0, 0, 0, 0}
|
#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}}, ""}
|
#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_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, 0}}
|
#define RadioConfig_UserPreferences_init_default {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, "", "", 0, 0, {0, 0, 0}}
|
||||||
#define NodeInfo_init_default {0, false, User_init_default, false, Position_init_default, 0, 0}
|
#define NodeInfo_init_default {0, false, User_init_default, false, Position_init_default, 0, 0}
|
||||||
#define MyNodeInfo_init_default {0, 0, 0, "", "", "", 0, 0, 0, 0, 0, 0, 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}
|
#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}
|
||||||
#define DebugString_init_default {""}
|
#define DebugString_init_default {""}
|
||||||
#define FromRadio_init_default {0, 0, {MeshPacket_init_default}}
|
#define FromRadio_init_default {0, 0, {MeshPacket_init_default}}
|
||||||
#define ToRadio_init_default {0, {MeshPacket_init_default}}
|
#define ToRadio_init_default {0, {MeshPacket_init_default}}
|
||||||
@@ -255,12 +261,12 @@ typedef struct _ToRadio {
|
|||||||
#define RouteDiscovery_init_zero {0, {0, 0, 0, 0, 0, 0, 0, 0}}
|
#define RouteDiscovery_init_zero {0, {0, 0, 0, 0, 0, 0, 0, 0}}
|
||||||
#define SubPacket_init_zero {0, {Position_init_zero}, 0, 0, 0, 0, {0}, 0}
|
#define SubPacket_init_zero {0, {Position_init_zero}, 0, 0, 0, 0, {0}, 0}
|
||||||
#define MeshPacket_init_zero {0, 0, 0, {SubPacket_init_zero}, 0, 0, 0, 0, 0}
|
#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}}, ""}
|
#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_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, 0}}
|
#define RadioConfig_UserPreferences_init_zero {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, "", "", 0, 0, {0, 0, 0}}
|
||||||
#define NodeInfo_init_zero {0, false, User_init_zero, false, Position_init_zero, 0, 0}
|
#define NodeInfo_init_zero {0, false, User_init_zero, false, Position_init_zero, 0, 0}
|
||||||
#define MyNodeInfo_init_zero {0, 0, 0, "", "", "", 0, 0, 0, 0, 0, 0, 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}
|
#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}
|
||||||
#define DebugString_init_zero {""}
|
#define DebugString_init_zero {""}
|
||||||
#define FromRadio_init_zero {0, 0, {MeshPacket_init_zero}}
|
#define FromRadio_init_zero {0, 0, {MeshPacket_init_zero}}
|
||||||
#define ToRadio_init_zero {0, {MeshPacket_init_zero}}
|
#define ToRadio_init_zero {0, {MeshPacket_init_zero}}
|
||||||
@@ -269,6 +275,10 @@ typedef struct _ToRadio {
|
|||||||
/* Field tags (for use in manual encoding/decoding) */
|
/* Field tags (for use in manual encoding/decoding) */
|
||||||
#define ChannelSettings_tx_power_tag 1
|
#define ChannelSettings_tx_power_tag 1
|
||||||
#define ChannelSettings_modem_config_tag 3
|
#define ChannelSettings_modem_config_tag 3
|
||||||
|
#define ChannelSettings_bandwidth_tag 6
|
||||||
|
#define ChannelSettings_spread_factor_tag 7
|
||||||
|
#define ChannelSettings_coding_rate_tag 8
|
||||||
|
#define ChannelSettings_channel_num_tag 9
|
||||||
#define ChannelSettings_psk_tag 4
|
#define ChannelSettings_psk_tag 4
|
||||||
#define ChannelSettings_name_tag 5
|
#define ChannelSettings_name_tag 5
|
||||||
#define Data_typ_tag 1
|
#define Data_typ_tag 1
|
||||||
@@ -308,8 +318,9 @@ typedef struct _ToRadio {
|
|||||||
#define RadioConfig_UserPreferences_sds_secs_tag 9
|
#define RadioConfig_UserPreferences_sds_secs_tag 9
|
||||||
#define RadioConfig_UserPreferences_ls_secs_tag 10
|
#define RadioConfig_UserPreferences_ls_secs_tag 10
|
||||||
#define RadioConfig_UserPreferences_min_wake_secs_tag 11
|
#define RadioConfig_UserPreferences_min_wake_secs_tag 11
|
||||||
#define RadioConfig_UserPreferences_keep_all_packets_tag 100
|
#define RadioConfig_UserPreferences_wifi_ssid_tag 12
|
||||||
#define RadioConfig_UserPreferences_promiscuous_mode_tag 101
|
#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_ignore_incoming_tag 102
|
||||||
#define RouteDiscovery_route_tag 2
|
#define RouteDiscovery_route_tag 2
|
||||||
#define User_id_tag 1
|
#define User_id_tag 1
|
||||||
@@ -351,6 +362,7 @@ typedef struct _ToRadio {
|
|||||||
#define DeviceState_receive_queue_tag 5
|
#define DeviceState_receive_queue_tag 5
|
||||||
#define DeviceState_version_tag 8
|
#define DeviceState_version_tag 8
|
||||||
#define DeviceState_rx_text_message_tag 7
|
#define DeviceState_rx_text_message_tag 7
|
||||||
|
#define DeviceState_no_save_tag 9
|
||||||
#define FromRadio_packet_tag 2
|
#define FromRadio_packet_tag 2
|
||||||
#define FromRadio_my_info_tag 3
|
#define FromRadio_my_info_tag 3
|
||||||
#define FromRadio_node_info_tag 4
|
#define FromRadio_node_info_tag 4
|
||||||
@@ -432,7 +444,11 @@ X(a, STATIC, SINGULAR, BOOL, want_ack, 11)
|
|||||||
X(a, STATIC, SINGULAR, INT32, tx_power, 1) \
|
X(a, STATIC, SINGULAR, INT32, tx_power, 1) \
|
||||||
X(a, STATIC, SINGULAR, UENUM, modem_config, 3) \
|
X(a, STATIC, SINGULAR, UENUM, modem_config, 3) \
|
||||||
X(a, STATIC, SINGULAR, BYTES, psk, 4) \
|
X(a, STATIC, SINGULAR, BYTES, psk, 4) \
|
||||||
X(a, STATIC, SINGULAR, STRING, name, 5)
|
X(a, STATIC, SINGULAR, STRING, name, 5) \
|
||||||
|
X(a, STATIC, SINGULAR, UINT32, bandwidth, 6) \
|
||||||
|
X(a, STATIC, SINGULAR, UINT32, spread_factor, 7) \
|
||||||
|
X(a, STATIC, SINGULAR, UINT32, coding_rate, 8) \
|
||||||
|
X(a, STATIC, SINGULAR, UINT32, channel_num, 9)
|
||||||
#define ChannelSettings_CALLBACK NULL
|
#define ChannelSettings_CALLBACK NULL
|
||||||
#define ChannelSettings_DEFAULT NULL
|
#define ChannelSettings_DEFAULT NULL
|
||||||
|
|
||||||
@@ -456,8 +472,9 @@ X(a, STATIC, SINGULAR, UINT32, mesh_sds_timeout_secs, 8) \
|
|||||||
X(a, STATIC, SINGULAR, UINT32, sds_secs, 9) \
|
X(a, STATIC, SINGULAR, UINT32, sds_secs, 9) \
|
||||||
X(a, STATIC, SINGULAR, UINT32, ls_secs, 10) \
|
X(a, STATIC, SINGULAR, UINT32, ls_secs, 10) \
|
||||||
X(a, STATIC, SINGULAR, UINT32, min_wake_secs, 11) \
|
X(a, STATIC, SINGULAR, UINT32, min_wake_secs, 11) \
|
||||||
X(a, STATIC, SINGULAR, BOOL, keep_all_packets, 100) \
|
X(a, STATIC, SINGULAR, STRING, wifi_ssid, 12) \
|
||||||
X(a, STATIC, SINGULAR, BOOL, promiscuous_mode, 101) \
|
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, REPEATED, UINT32, ignore_incoming, 102)
|
||||||
#define RadioConfig_UserPreferences_CALLBACK NULL
|
#define RadioConfig_UserPreferences_CALLBACK NULL
|
||||||
#define RadioConfig_UserPreferences_DEFAULT NULL
|
#define RadioConfig_UserPreferences_DEFAULT NULL
|
||||||
@@ -498,7 +515,8 @@ X(a, STATIC, OPTIONAL, MESSAGE, owner, 3) \
|
|||||||
X(a, STATIC, REPEATED, MESSAGE, node_db, 4) \
|
X(a, STATIC, REPEATED, MESSAGE, node_db, 4) \
|
||||||
X(a, STATIC, REPEATED, MESSAGE, receive_queue, 5) \
|
X(a, STATIC, REPEATED, MESSAGE, receive_queue, 5) \
|
||||||
X(a, STATIC, OPTIONAL, MESSAGE, rx_text_message, 7) \
|
X(a, STATIC, OPTIONAL, MESSAGE, rx_text_message, 7) \
|
||||||
X(a, STATIC, SINGULAR, UINT32, version, 8)
|
X(a, STATIC, SINGULAR, UINT32, version, 8) \
|
||||||
|
X(a, STATIC, SINGULAR, BOOL, no_save, 9)
|
||||||
#define DeviceState_CALLBACK NULL
|
#define DeviceState_CALLBACK NULL
|
||||||
#define DeviceState_DEFAULT NULL
|
#define DeviceState_DEFAULT NULL
|
||||||
#define DeviceState_radio_MSGTYPE RadioConfig
|
#define DeviceState_radio_MSGTYPE RadioConfig
|
||||||
@@ -591,12 +609,12 @@ extern const pb_msgdesc_t ManufacturingData_msg;
|
|||||||
#define RouteDiscovery_size 88
|
#define RouteDiscovery_size 88
|
||||||
#define SubPacket_size 274
|
#define SubPacket_size 274
|
||||||
#define MeshPacket_size 313
|
#define MeshPacket_size 313
|
||||||
#define ChannelSettings_size 60
|
#define ChannelSettings_size 84
|
||||||
#define RadioConfig_size 157
|
#define RadioConfig_size 277
|
||||||
#define RadioConfig_UserPreferences_size 93
|
#define RadioConfig_UserPreferences_size 188
|
||||||
#define NodeInfo_size 132
|
#define NodeInfo_size 132
|
||||||
#define MyNodeInfo_size 110
|
#define MyNodeInfo_size 110
|
||||||
#define DeviceState_size 5305
|
#define DeviceState_size 5427
|
||||||
#define DebugString_size 258
|
#define DebugString_size 258
|
||||||
#define FromRadio_size 322
|
#define FromRadio_size 322
|
||||||
#define ToRadio_size 316
|
#define ToRadio_size 316
|
||||||
|
|||||||
+34
-14
@@ -1,20 +1,40 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
#include "concurrency/PeriodicTask.h"
|
||||||
|
#include "PowerStatus.h"
|
||||||
|
|
||||||
namespace meshtastic
|
/**
|
||||||
|
* Per @spattinson
|
||||||
|
* MIN_BAT_MILLIVOLTS seems high. Typical 18650 are different chemistry to LiPo, even for LiPos that chart seems a bit off, other
|
||||||
|
* charts put 3690mV at about 30% for a lipo, for 18650 i think 10% remaining iis in the region of 3.2-3.3V. Reference 1st graph
|
||||||
|
* in [this test report](https://lygte-info.dk/review/batteries2012/Samsung%20INR18650-30Q%203000mAh%20%28Pink%29%20UK.html)
|
||||||
|
* looking at the red line - discharge at 0.2A - he gets a capacity of 2900mah, 90% of 2900 = 2610, that point in the graph looks
|
||||||
|
* to be a shade above 3.2V
|
||||||
|
*/
|
||||||
|
#define MIN_BAT_MILLIVOLTS 3250 // millivolts. 10% per https://blog.ampow.com/lipo-voltage-chart/
|
||||||
|
|
||||||
|
#define BAT_MILLIVOLTS_FULL 4100
|
||||||
|
#define BAT_MILLIVOLTS_EMPTY 3500
|
||||||
|
|
||||||
|
class Power : public concurrency::PeriodicTask
|
||||||
{
|
{
|
||||||
|
|
||||||
/// Describes the state of the power system.
|
public:
|
||||||
struct PowerStatus {
|
|
||||||
/// Whether we have a battery connected
|
Observable<const meshtastic::PowerStatus *> newStatus;
|
||||||
bool haveBattery;
|
|
||||||
/// Battery voltage in mV, valid if haveBattery is true
|
void readPowerStatus();
|
||||||
int batteryVoltageMv;
|
void loop();
|
||||||
/// Whether USB is connected
|
virtual bool setup();
|
||||||
bool usb;
|
virtual void doTask();
|
||||||
/// Whether we are charging the battery
|
void setStatusHandler(meshtastic::PowerStatus *handler)
|
||||||
bool charging;
|
{
|
||||||
|
statusHandler = handler;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
meshtastic::PowerStatus *statusHandler;
|
||||||
|
virtual void axp192Init();
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace meshtastic
|
extern Power *power;
|
||||||
|
|
||||||
extern meshtastic::PowerStatus powerStatus;
|
|
||||||
+3
-3
@@ -5,7 +5,7 @@
|
|||||||
#include "NodeDB.h"
|
#include "NodeDB.h"
|
||||||
#include "configuration.h"
|
#include "configuration.h"
|
||||||
#include "error.h"
|
#include "error.h"
|
||||||
|
#include "timing.h"
|
||||||
#include "main.h"
|
#include "main.h"
|
||||||
#include "target_specific.h"
|
#include "target_specific.h"
|
||||||
|
|
||||||
@@ -123,11 +123,11 @@ bool doPreflightSleep()
|
|||||||
/// Tell devices we are going to sleep and wait for them to handle things
|
/// Tell devices we are going to sleep and wait for them to handle things
|
||||||
static void waitEnterSleep()
|
static void waitEnterSleep()
|
||||||
{
|
{
|
||||||
uint32_t now = millis();
|
uint32_t now = timing::millis();
|
||||||
while (!doPreflightSleep()) {
|
while (!doPreflightSleep()) {
|
||||||
delay(100); // Kinda yucky - wait until radio says say we can shutdown (finished in process sends/receives)
|
delay(100); // Kinda yucky - wait until radio says say we can shutdown (finished in process sends/receives)
|
||||||
|
|
||||||
if (millis() - now > 30 * 1000) { // If we wait too long just report an error and go to sleep
|
if (timing::millis() - now > 30 * 1000) { // If we wait too long just report an error and go to sleep
|
||||||
recordCriticalError(ErrSleepEnterWait);
|
recordCriticalError(ErrSleepEnterWait);
|
||||||
assert(0); // FIXME - for now we just restart, need to fix bug #167
|
assert(0); // FIXME - for now we just restart, need to fix bug #167
|
||||||
break;
|
break;
|
||||||
|
|||||||
@@ -0,0 +1,10 @@
|
|||||||
|
#include "timing.h"
|
||||||
|
#include "freertosinc.h"
|
||||||
|
|
||||||
|
namespace timing {
|
||||||
|
|
||||||
|
uint32_t millis() {
|
||||||
|
return xTaskGetTickCount();
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace timing
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
|
namespace timing {
|
||||||
|
|
||||||
|
uint32_t millis();
|
||||||
|
|
||||||
|
} // namespace timing
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
/// C++ v17+ clamp function, limits a given value to a range defined by lo and hi
|
||||||
|
template<class T>
|
||||||
|
constexpr const T& clamp( const T& v, const T& lo, const T& hi ) {
|
||||||
|
return (v < lo) ? lo : (hi < v) ? hi : v;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user