Compare commits

...

155 Commits
0.7.9 ... 0.8.1

Author SHA1 Message Date
Kevin Hester
94f03bee01 Merge pull request #289 from geeksville/post1
geeksvilles periodic merging from master
2020-07-18 08:58:13 -07:00
geeksville
859642d2e4 Merge remote-tracking branch 'root/master' into post1
# Conflicts:
#	src/configuration.h
2020-07-17 12:06:57 -07:00
Kevin Hester
8316419a01 Merge pull request #288 from geeksville/post1
Improvements for NRF52 targets
2020-07-17 11:22:20 -07:00
Kevin Hester
96f5069742 Merge branch 'post1' into post1 2020-07-17 11:17:12 -07:00
geeksville
e433249bb1 Default back to tbeam in builds, so it will work for others 2020-07-17 11:14:18 -07:00
geeksville
2a6df797ca NRF52 BLE API now works! 2020-07-17 11:12:05 -07:00
geeksville
28aa48c8d2 NRF52 reads now work, but not long reads 2020-07-17 10:40:03 -07:00
geeksville
582f77e4ec NRF52 BLE now works, except for reads having the wrong payload 2020-07-17 10:02:07 -07:00
geeksville
5700cf96d5 redefine printf to use the segger output system (NRF52) 2020-07-17 09:14:23 -07:00
geeksville
769a98f1f4 nrf52 ble - move service in into the scan info 2020-07-17 09:13:47 -07:00
Kevin Hester
4ee35a0612 Merge pull request #280 from aHVzY2g/master
fix #272 add support for tlora-v2-1-1.6
2020-07-16 11:12:40 -07:00
Kevin Hester
64f6741f82 Merge pull request #282 from slavino/patch-7
Create SupportedHardware.md
2020-07-16 11:11:44 -07:00
Kevin Hester
6ad5abcab2 Merge branch 'post1' into patch-7 2020-07-15 21:28:13 -07:00
Kevin Hester
5480f10184 Merge pull request #283 from slavino/patch-8
Update README.md
2020-07-15 21:27:44 -07:00
Kevin Hester
41dfbdd331 Merge branch 'post1' into patch-8 2020-07-15 21:22:21 -07:00
Kevin Hester
903268c52a Merge pull request #281 from slavino/patch-6
Update README.md
2020-07-15 21:21:50 -07:00
Kevin Hester
b60c630922 Merge branch 'post1' into patch-6 2020-07-15 21:16:22 -07:00
Kevin Hester
95588b420c Merge branch 'post1' into patch-8 2020-07-15 21:15:52 -07:00
Kevin Hester
b141ec2e35 Merge pull request #285 from Professr/issue#284
UBlox GPS now detects loss of lock
2020-07-15 21:15:19 -07:00
geeksville
3d0c611896 android app can now talk to NRF52, but writes are lost WIP 2020-07-15 17:09:09 -07:00
geeksville
6cb92143ec OOM allocing the BLE stack for adafruit 2020-07-15 13:45:57 -07:00
geeksville
f919eb6a64 Install a hardfault handler on NRF52 2020-07-15 13:29:09 -07:00
geeksville
4147786b12 WIP of adding NRF52 bluetooth API, we take a hardfault in Bluefruit init 2020-07-15 13:10:56 -07:00
geeksville
3400bcde85 Merge branch 'master' into post1
# Conflicts:
#	docs/README.md
2020-07-15 11:51:05 -07:00
Professr
e5fac4b78d UBlox GPS now detects loss of lock 2020-07-14 22:53:36 -07:00
Slavomir Hustaty
4d20865c67 Update README.md
the board referenced in that "buy" link has also label reference on board stating that it is V1.1
2020-07-14 18:28:14 +02:00
Slavomir Hustaty
8b4cf91f1f Create SupportedHardware.md
idea on supported hardware matrix - maybe to be referenced from main README.md
2020-07-14 18:20:13 +02:00
Slavomir Hustaty
6a6a10fb9b Update README.md 2020-07-14 17:26:17 +02:00
Marlon Spangenberg
44749470a4 fix #272 add support for tlora-v2-1-1.6 2020-07-14 16:16:49 +02:00
Kevin Hester
8fe714d8b1 Merge pull request #278 from geeksville/master
Fix URL
2020-07-13 17:58:42 -07:00
Kevin Hester
22137ff1bd Merge branch 'master' into master 2020-07-13 17:56:08 -07:00
geeksville
da3b6d1958 Fix URL 2020-07-13 17:55:30 -07:00
geeksville
c7213fb710 Fix URL 2020-07-13 17:54:12 -07:00
Kevin Hester
637960edde Merge pull request #277 from geeksville/master
fix doc typo
2020-07-13 17:51:46 -07:00
geeksville
be7e4fea6a Merge branch 'master' into post1 2020-07-13 17:49:54 -07:00
geeksville
d9209ffaea fix doc typo 2020-07-13 17:47:22 -07:00
Kevin Hester
9fb94796c8 Merge pull request #276 from rezl/master
Added Beginner's Guide to README.md
2020-07-13 17:46:02 -07:00
Rezl
f060f7faad Merge pull request #1 from rezl/rezl-beginners-guide
Added Beginner's Guide
2020-07-13 19:35:09 -05:00
Rezl
55673fcd66 Added Beginner's Guide 2020-07-13 19:34:14 -05:00
geeksville
2ff94cb11d Update device protocol docs 2020-07-13 14:41:04 -07:00
geeksville
e46bebc06f rename docs 2020-07-13 14:21:28 -07:00
geeksville
80e8b4adcc turn off gps debug output for now RAK815 2020-07-13 14:20:49 -07:00
Kevin Hester
e9be03b76c Merge pull request #275 from geeksville/post1
NRF52 / RAK815 work items
2020-07-13 14:14:40 -07:00
geeksville
98dfecdb79 Add external RF switch management for RF95 (needed for RAK815) 2020-07-13 13:18:32 -07:00
geeksville
8ba8278fb5 Merge remote-tracking branch 'root/master' into post1 2020-07-13 12:02:14 -07:00
Kevin Hester
51267379ab Merge pull request #273 from geeksville/dev
0.7.11
2020-07-12 15:28:16 -07:00
geeksville
e2cf2ba4f2 recommend tbeam 1.0 over 0.7 2020-07-12 14:55:50 -07:00
geeksville
4550cce639 0.7.11 2020-07-12 14:42:21 -07:00
geeksville
f2f17c81d4 Merge branch 'dev' into post1 2020-07-11 17:17:27 -07:00
Kevin Hester
7c0d13f00a Merge pull request #268 from geeksville/dev
Dev
2020-07-11 17:16:48 -07:00
geeksville
f78f3232e2 update todo 2020-07-11 17:08:36 -07:00
geeksville
a687aa8e75 update nrf52 todo 2020-07-11 17:08:03 -07:00
geeksville
ed6b89b3b1 RAK-815 GPS now kinda works (must update associated libs). comms to it
still a bit yucky
2020-07-10 20:34:03 -07:00
geeksville
9d3ca0d0f9 fix esp32 build 2020-07-10 18:46:33 -07:00
geeksville
05a0405709 GPS not yet ready RAK815 WIP 2020-07-10 14:57:33 -07:00
geeksville
efd8b70089 RAK815 GPS WIP 2020-07-10 14:54:32 -07:00
geeksville
e12c057c31 RAK815 lora works 2020-07-10 14:37:01 -07:00
geeksville
0b5b18653a Panic if new ever returns NULL 2020-07-10 13:52:54 -07:00
geeksville
dc54e7331f memory is tight on the nrf52832 board (for now) - so don't allocate too much 2020-07-10 13:52:26 -07:00
geeksville
6fbf6b2986 RAK815 WIP - use 115200 for serial to USB on this board, best it can do 2020-07-10 13:24:00 -07:00
geeksville
feb9992d7d on rak815 use serial port for console 2020-07-10 13:11:29 -07:00
geeksville
fb4ac82d45 fix rf95 renaming 2020-07-10 12:47:10 -07:00
geeksville
acbe8c159b move rak815 variant to arduino repo 2020-07-10 12:38:11 -07:00
geeksville
63474dd952 Only try for NEMA if we have serial 2020-07-10 12:16:10 -07:00
geeksville
cc35ed7782 update build instructions for RAK815 2020-07-10 12:12:03 -07:00
geeksville
d9fd227862 have CI build RAK815 2020-07-10 11:44:46 -07:00
geeksville
1415f2bed7 WIP add support for i2C GPS 2020-07-10 11:43:14 -07:00
geeksville
5b07d454b1 WIP RAK815 now builds correctly 2020-07-10 11:29:32 -07:00
geeksville
0c04ba4776 use my fixed nrf52832 support in arduino 2020-07-10 11:28:18 -07:00
geeksville
b00a936f41 WIP RAK815 support 2020-07-10 11:02:29 -07:00
geeksville
5b11c1ca86 Merge branch 'dev' into post1 2020-07-10 10:03:18 -07:00
geeksville
7802d00031 add nrf52832 support 2020-07-10 10:03:08 -07:00
geeksville
40a15248e8 @slavino fixed tbeam in #243, so add it back to the builds 2020-07-09 22:43:04 -07:00
geeksville
9ba9e82706 try to fix adafruit-nrfutil bin being in path? 2020-07-09 21:49:07 -07:00
geeksville
ea6c33f3d2 add adafruit-nrfutil to CI build 2020-07-09 21:37:45 -07:00
geeksville
82e5e1858a build for 2 ESP32 targets and 2 NRF52 targets in the CI build 2020-07-09 21:30:52 -07:00
geeksville
80b14c0a6f add support for adafruit feather nrf52832 - which is close to a RAK815 2020-07-09 21:27:34 -07:00
geeksville
6954d0d5f4 Checkout submodules for CI build 2020-07-09 20:31:16 -07:00
geeksville
7e53731fe6 Use our custom variants files and fixes 2020-07-09 20:22:40 -07:00
geeksville
c7290e6ccc don't turn wifi on yet, because my fixes for arduino-esp32 haven't been pushed 2020-07-09 20:11:33 -07:00
geeksville
284317cb25 add nrf52 to CI builds 2020-07-09 20:08:36 -07:00
geeksville
311d1a56b4 make a bare nrf52840dk build which will work for everyone 2020-07-09 20:05:39 -07:00
geeksville
ed589727d6 Update NRF52 build to work again 2020-07-09 19:57:55 -07:00
geeksville
62c9bad183 WIP TCP API server over wifi 2020-07-07 18:37:42 -07:00
geeksville
616da8228e enable wifi for ESP32 2020-07-07 17:41:35 -07:00
geeksville
3a62453b8b todo updates for 1.0 2020-07-07 17:40:59 -07:00
Kevin Hester
c3f7829255 Merge pull request #262 from grcasanova/graphics
Graphics
2020-07-07 15:08:24 -07:00
grcasanova
37d9fb2dad just a cleanup of the graphics 2020-07-07 10:46:49 +02:00
Kevin Hester
4388e72dec Merge pull request #259 from grcasanova/concurrency
Threading refactored
2020-07-06 14:15:18 -07:00
grcasanova
9803141fe7 merged with master 2020-07-06 21:53:10 +02:00
Kevin Hester
1f0e9cc1c3 Merge pull request #258 from Professr/issue#257
Added sinceLastSeen check to pings generated by node UI
2020-07-06 09:20:41 -07:00
grcasanova
92b30ebec6 fixes now compiles 2020-07-06 10:45:55 +02:00
Professr
ccadb6a43d Added sinceLastSeen check to pings generated by node UI 2020-07-05 19:56:57 -07:00
Ellie Hussey
6f7f540c79 Added the option for forced NodeStatus updates on user change or text message, tweaked compass (#256) 2020-07-05 17:03:12 -07:00
grcasanova
d5b8038457 fixes 2020-07-06 00:54:30 +02:00
grcasanova
0a6059ba13 refactored threading-related classes, code broken 2020-07-05 23:11:40 +02:00
Kevin Hester
aba5b01fa0 Merge pull request #255 from geeksville/dev
fix #254 - a RadioLib (and arduino-esp32) needed to have IRAM attr on for disable interrupt
2020-07-05 12:13:08 -07:00
geeksville
09f4943869 Merge remote-tracking branch 'root/master' into dev 2020-07-05 12:10:25 -07:00
Kevin Hester
29c8543f87 Merge pull request #248 from Professr/issue#199
Issue#199 update - add satellite info, change DOP display, add compass rose
2020-07-05 12:10:03 -07:00
geeksville
7bd4940ed8 fix #254 2020-07-05 12:04:15 -07:00
Kevin Hester
d5116935b5 Merge branch 'master' into issue#199 2020-07-04 12:13:21 -07:00
Kevin Hester
0d320fe29b Merge pull request #251 from mrvdb/sh1106-support
Screen width correction for sh1106 display controller
2020-07-04 12:13:04 -07:00
Marcel van der Boom
4159461a62 Merge remote-tracking branch 'upstream/master' into sh1106-support 2020-07-04 10:45:13 +02:00
Ellie Hussey
f4bd39e3fa Merge pull request #246 from slavino/patch-4
Update platformio.ini
2020-07-03 03:27:41 -07:00
Ellie Hussey
fbc36a2cfd Merge branch 'master' into patch-4 2020-07-03 03:25:09 -07:00
Ellie Hussey
e93ba73adb Merge pull request #245 from slavino/patch-3
Update configuration.h
2020-07-03 03:24:56 -07:00
Ellie Hussey
03301f093d Merge branch 'master' into patch-3 2020-07-03 03:22:45 -07:00
Ellie Hussey
55a5fa6fb5 Merge pull request #247 from slavino/patch-5
Update README.md
2020-07-03 03:22:04 -07:00
Professr
4d04d10135 Merge screen.cpp 2020-07-03 02:58:55 -07:00
Professr
cda423acab Changed GPS DOP display to bars, added satellites display and compass rose 2020-07-03 02:53:56 -07:00
Slavomir Hustaty
0f92678c3b Update README.md
TBeam 0.7 + W.W. LoRa freqs list link
2020-07-03 10:35:42 +02:00
Slavomir Hustaty
8d122f36e3 Update platformio.ini
https://github.com/meshtastic/Meshtastic-device/issues/243#issuecomment-653361142
2020-07-03 07:44:14 +02:00
Slavomir Hustaty
439cdfbb32 Update configuration.h
https://github.com/meshtastic/Meshtastic-device/issues/243#issuecomment-653361142
2020-07-03 07:41:22 +02:00
Kevin Hester
0a6ab31e10 Merge pull request #244 from slavino/patch-2
Update configuration.h to fix TBEAM v07 GPS to work
2020-07-02 10:03:07 -07:00
Marcel van der Boom
0b6486256d Merge remote-tracking branch 'upstream/master' into sh1106-support 2020-07-02 17:36:31 +02:00
Slavomir Hustaty
da12b93f82 Update configuration.h
https://user-images.githubusercontent.com/1584034/86362734-08525e00-bc76-11ea-8a34-8579d1fa2965.jpg

related to issue https://github.com/meshtastic/Meshtastic-device/issues/243
2020-07-02 16:54:24 +02:00
Kevin Hester
6dec6af5dc Merge pull request #240 from tobymurray/master
Add Canada to list of countries that use 915 MHz
2020-07-01 11:59:04 -07:00
Kevin Hester
11444621ae Merge branch 'master' into master 2020-07-01 10:26:31 -07:00
Kevin Hester
98f1b3296c Merge pull request #241 from geeksville/dev
Dev
2020-07-01 10:26:13 -07:00
geeksville
26c43e7091 minor docs 2020-07-01 10:22:17 -07:00
Toby Murray
9b9447858a Add Canada to list of countries that use 915 MHz
Meshtastic prompted me to get a couple boards to try, and I had to figure out what frequency. Canada uses the same US902-928 as the US, add it to the list for simplicity.

Not sure where to find an "official" reference, but there's a reference here: https://www.thethingsnetwork.org/docs/lorawan/frequencies-by-country.html
2020-07-01 13:12:00 -04:00
geeksville
3151cfb064 0.7.10 2020-07-01 10:09:32 -07:00
geeksville
c327fee986 Fix formatting 2020-07-01 10:09:06 -07:00
geeksville
a4f53270e8 fix heltec build (and fix formatting) 2020-07-01 10:08:38 -07:00
geeksville
a7456a1126 all targets are arduino - for now 2020-07-01 10:08:09 -07:00
geeksville
8381512ce4 todo updates 2020-07-01 09:32:01 -07:00
Marcel van der Boom
57d968cdcd Merge remote-tracking branch 'upstream/master' into sh1106-support 2020-06-29 09:26:25 +02:00
Kevin Hester
20a669029b Merge pull request #237 from Professr/issue#182
Switch main display to event-driven model, abstract the three main status info categories, work on power.h
2020-06-28 19:10:42 -07:00
Professr
f2e6c6de58 Fixed filename case sensitivity 2020-06-28 19:03:39 -07:00
Professr
8fa44c3590 Disabled the display heartbeat pixel for pull request 2020-06-28 18:55:51 -07:00
Professr
f5b7c33d4e Refactored status handlers and merged 2020-06-28 18:17:52 -07:00
Kevin Hester
be8e663d39 Merge pull request #236 from geeksville/dev
turn on thread watchdog
2020-06-28 11:24:03 -07:00
geeksville
0d4a9748e3 Merge remote-tracking branch 'root/master' into dev 2020-06-28 11:13:05 -07:00
geeksville
bd477f0fb2 turn on thread watchdog 2020-06-28 11:12:12 -07:00
Professr
5317895a5e Merged 2020-06-27 21:26:57 -07:00
Professr
542b8b26ce Abstracted statuses, made display event-driven 2020-06-27 21:19:49 -07:00
Marcel van der Boom
aaca854620 Merge remote-tracking branch 'upstream/master' into sh1106-support 2020-06-27 10:18:55 +02:00
Kevin Hester
64da384fc1 Merge pull request #234 from geeksville/dev
Dev
2020-06-26 16:16:54 -07:00
geeksville
a595fc4642 Fix #233 - init distance string before drawing it
(and violating my own "no formatting checkins with other changes" rule
to restore proper indentation for this file)
2020-06-26 15:04:22 -07:00
geeksville
ac135be8cd move geeksville's personal todos somewhere else (mostly android) 2020-06-25 15:49:49 -07:00
Marcel van der Boom
ac2d3e2ae0 Correct type of setBrightness parameter 2020-06-25 21:16:35 +02:00
Marcel van der Boom
33946af39f SCREEN_WIDTH is visible area already, not addressable area
- sh1106 starts showing from column 2 (the library handles the offsets) so we don't actually need
the different screen width here.
2020-06-25 21:15:12 +02:00
Kevin Hester
5ea59a1c4d Merge pull request #230 from aHVzY2g/pr/228
fix pr #228 int float casting & made fillRect 2px smaller
2020-06-25 11:41:40 -07:00
Kevin Hester
8bafd87b76 Merge branch 'master' into pr/228 2020-06-25 11:26:37 -07:00
Kevin Hester
6b40e9a5e0 Merge pull request #232 from geeksville/dev
Add optional external GPS for TTGO Lora V2
2020-06-25 11:25:46 -07:00
Kevin Hester
a2f6fd9298 Merge branch 'master' into dev 2020-06-25 10:07:06 -07:00
Marlon Spangenberg
bc604fc9ba Merge branch 'master' into pr/228 2020-06-25 18:56:20 +02:00
geeksville
9baaa13897 Add optional external GPS for TTGO Lora V2
per this request: https://meshtastic.discourse.group/t/gps-pin-assignments-for-custom-boards/501/9?u=geeksville
2020-06-25 09:45:21 -07:00
Kevin Hester
65e53be8b0 Merge pull request #231 from geeksville/dev
Add an initial pull-request template
2020-06-25 09:37:40 -07:00
geeksville
7f5283e95d Add an initial pull-request template 2020-06-25 09:33:29 -07:00
Marlon Spangenberg
ae4ab48ddc fixed int float casting & made fillRect 2px smaller 2020-06-25 18:26:32 +02:00
Kevin Hester
afccf1da02 Merge pull request #229 from meshtastic/geeksville-patch-1
Update issue templates
2020-06-25 09:21:38 -07:00
Kevin Hester
fc07c7c01f Update issue templates 2020-06-25 09:18:54 -07:00
Kevin Hester
aeb906414f Merge pull request #228 from mrvdb/sh1106-support
Take configured SCREEN_WIDTH into account for brightness bar
2020-06-25 08:44:34 -07:00
Marcel van der Boom
a6c6b45576 Take different screen widths into account for brightness bar
width of sh1106 display is 132 for example
2020-06-25 16:20:49 +02:00
110 changed files with 18574 additions and 1203 deletions

View File

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

13
.github/pull_request_template.md vendored Normal file
View File

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

View File

@@ -9,6 +9,8 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@master
- name: Checkout submodules
uses: textbook/git-checkout-submodule-action@master
- name: Setup Python
uses: actions/setup-python@master
with:
@@ -17,5 +19,8 @@ jobs:
run: |
python -m pip install --upgrade pip
pip install -U platformio
- name: Install extra python tools
run: |
pip install -U adafruit-nrfutil
- name: Build
run: platformio run
run: platformio run -e tbeam -e heltec -e nrf52840dk -e rak815

9
.gitignore vendored
View File

@@ -8,3 +8,12 @@ main/credentials.h
!.vscode/tasks.json
!.vscode/extensions.json
*.code-workspace
.DS_Store
Thumbs.db
.autotools
.built
.context
.cproject
.idea/*
.vagrant

View File

@@ -47,7 +47,8 @@
"memory_resource": "cpp",
"optional": "cpp",
"string_view": "cpp",
"cassert": "cpp"
"cassert": "cpp",
"iterator": "cpp"
},
"cSpell.words": [
"Blox",
@@ -61,5 +62,6 @@
"ocrypto",
"protobufs",
"wifi"
]
],
"C_Cpp.dimInactiveRegions": true
}

17
.vscode/tasks.json vendored Normal file
View File

@@ -0,0 +1,17 @@
{
"version": "2.0.0",
"tasks": [
{
"type": "PlatformIO",
"task": "Build",
"problemMatcher": [
"$platformio"
],
"group": {
"kind": "build",
"isDefault": true
},
"label": "PlatformIO: Build"
}
]
}

View File

@@ -22,16 +22,18 @@ This software is 100% open source and developed by a group of hobbyist experimen
We currently support three models of radios.
- TTGO T-Beam
- [T-Beam V1.0 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) (slightly better GPS)
- TTGO T-Beam (usually the recommended choice)
- [T-Beam V1.1 w/ NEO-6M - special Meshtastic version](https://www.aliexpress.com/item/4001178678568.html) (Includes built-in OLED display and they have **preinstalled** the meshtastic software)
- [T-Beam V1.1 w/ NEO-M8N](https://www.aliexpress.com/item/33047631119.html) (slightly better GPS)
- [T-Beam V0.7 w/ NEO-6M](https://www.aliexpress.com/item/4000574335430.html) (will work but **you must use the tbeam0.7 firmware ** - but the T-Beam V1.0 or later are better!)
- board labels "TTGO T22_V07 20180711"
- 3D printable cases
- [T-Beam V0](https://www.thingiverse.com/thing:3773717)
- [T-Beam V1](https://www.thingiverse.com/thing:3830711)
- [TTGO LORA32](https://www.aliexpress.com/item/4000211331316.html) - No GPS
- version 2.1
- board labels "TTGO T3_V1.6 20180606"
- 3D printable case
- [TTGO LORA32 v1](https://www.thingiverse.com/thing:3385109)
@@ -40,9 +42,10 @@ We currently support three models of radios.
**Make sure to get the frequency for your country**
- US/JP/AU/NZ - 915MHz
- US/JP/AU/NZ/CA - 915MHz
- CN - 470MHz
- 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.

View File

@@ -8,7 +8,7 @@ COUNTRIES="US EU433 EU865 CN JP"
#COUNTRIES=US
#COUNTRIES=CN
BOARDS="ttgo-lora32-v2 ttgo-lora32-v1 tbeam heltec"
BOARDS="ttgo-lora32-v2 ttgo-lora32-v1 tbeam heltec tbeam0.7"
#BOARDS=tbeam
OUTDIR=release/latest

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

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

View File

@@ -1,3 +1,3 @@
export VERSION=0.7.9
export VERSION=0.7.11

47
boards/nrf52840_dk.json Normal file
View File

@@ -0,0 +1,47 @@
{
"build": {
"arduino": {
"ldscript": "nrf52840_s140_v6.ld"
},
"core": "nRF5",
"cpu": "cortex-m4",
"extra_flags": "-DARDUINO_NRF52840_PCA10056 -DNRF52840_XXAA",
"f_cpu": "64000000L",
"hwids": [["0x239A", "0x4404"]],
"usb_product": "nrf52840dk",
"mcu": "nrf52840",
"variant": "pca10056",
"variants_dir": "variants",
"bsp": {
"name": "adafruit"
},
"softdevice": {
"sd_flags": "-DS140",
"sd_name": "s140",
"sd_version": "6.1.1",
"sd_fwid": "0x00B6"
},
"bootloader": {
"settings_addr": "0xFF000"
}
},
"connectivity": ["bluetooth"],
"debug": {
"jlink_device": "nRF52840_xxAA",
"onboard_tools": ["jlink"],
"svd_path": "nrf52840.svd"
},
"frameworks": ["arduino"],
"name": "A modified NRF52840-DK devboard (Adafruit BSP)",
"upload": {
"maximum_ram_size": 248832,
"maximum_size": 815104,
"require_upload_port": true,
"speed": 115200,
"protocol": "jlink",
"protocols": ["jlink", "nrfjprog", "stlink"]
},
"url": "https://meshtastic.org/",
"vendor": "Nordic Semi"
}

View File

@@ -8,7 +8,7 @@
"extra_flags": "-DARDUINO_NRF52840_PCA10056 -DNRF52840_XXAA",
"f_cpu": "64000000L",
"hwids": [["0x239A", "0x4404"]],
"usb_product": "SimPPR",
"usb_product": "nrf52840dk",
"mcu": "nrf52840",
"variant": "pca10056-rc-clock",
"variants_dir": "variants",

55
boards/rak815.json Normal file
View File

@@ -0,0 +1,55 @@
{
"build": {
"arduino":{
"ldscript": "nrf52832_s132_v6.ld"
},
"core": "nRF5",
"cpu": "cortex-m4",
"extra_flags": "-DNRF52832_XXAA -DNRF52",
"f_cpu": "64000000L",
"hwids": [
[
"0x10c4",
"0xea60"
]
],
"usb_product": "RAK815",
"mcu": "nrf52832",
"variant": "rak815",
"bsp": {
"name": "adafruit"
},
"softdevice": {
"sd_flags": "-DS132",
"sd_name": "s132",
"sd_version": "6.1.1",
"sd_fwid": "0x00B7"
}
},
"connectivity": [
"bluetooth"
],
"debug": {
"jlink_device": "nRF52832_xxAA",
"svd_path": "nrf52.svd"
},
"frameworks": [
"arduino"
],
"name": "RAK RAK815",
"upload": {
"maximum_ram_size": 65536,
"maximum_size": 524288,
"require_upload_port": true,
"speed": 115200,
"protocol": "nrfutil",
"protocols": [
"jlink",
"nrfjprog",
"nrfutil",
"stlink"
]
},
"url": "https://store.rakwireless.com/products/rak815-hybrid-location-tracker",
"vendor": "RAK"
}

View File

@@ -35,6 +35,10 @@ 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/).
### Beginner's Guide
For an detailed walk-through aimed at beginners, we recommend [meshtastic.letstalkthis.com](https://meshtastic.letstalkthis.com/).
# Updates
Note: Updates are happening almost daily, only major updates are listed below. For more details see our forum.

View File

@@ -0,0 +1,8 @@
| Vendor | Product line | Version | Board labels | Notes | URL |
|---|---|---|---|---|---|
| TTGO | T-Beam | 0.7 | T22_V07 20180711 | LoRa 433/470MHz *OR* LoRa 868/915MHz , <br/>GPS ublox NEO-6M , <br/>battery holder for Li-Ion 18650 | [buy](https://www.aliexpress.com/item/4000574335430.html) |
| TTGO | T-Beam | 1.0 | | | [buy](https://www.aliexpress.com/item/4001178678568.html) |
| TTGO | T-Beam | 1.1 | T22_V11 20191212 | LoRa 433/470MHz *OR* LoRa 868/915MHz *OR* LoRa 923MHz , <br/>GPS ublox NEO-M8N , <br/>battery holder for Li-Ion 18650 | [buy](https://www.aliexpress.com/item/4001178678568.html) |
| TTGO | Lora32 | 2.0 | *missing* | LoRa 433/470MHz *OR* LoRa 868/915MHz , <br/>OLED SSD1306 , <br/>SD card holder | [buy](https://www.aliexpress.com/item/4000211331316.html) |
| TTGO | Lora32 | 2.1 | T3_V1.6 20180606 | LoRa 32 (V2) , <br/>SD card holder | [buy](https://www.aliexpress.com/item/4000119208093.html) |
| Heltec | Lora 32 | V2 | V2 | LoRa 433/470MHz *OR* LoRa 868/915MHz | [buy](https://heltec.org/project/wifi-lora-32/) |

Binary file not shown.

Binary file not shown.

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -2,18 +2,14 @@
You probably don't care about this section - skip to the next one.
- do device release (after testing BLE fixes) https://github.com/meshtastic/Meshtastic-device/pull/218#event-3475395948
- some channel names don't work - check for illegal symbols per forum report
- auto invalidate old BLE handles
- disable software update button after update finishes
- mention translations in main readme
- check BLE handle stability across sleep - stress test sleep/wake - btu_init_core calls gatt_init - which assigns handles global
- @feh123 Sony Xperia Z1 C6903 running Android 5.1.1
- first message sent is still doubled for some people
- Android frontend should refetch the android messages from backend service on Resume
- let users set arbitrary params in android
- implement first cut of router mode: preferentially handle flooding, and change sleep and GPS behaviors
- NRF52 BLE support
- brf52 ble
- update protocol description per cyclomies
- esp32 pairing
- update faq with antennas https://meshtastic.discourse.group/t/range-test-ideas-requested/738/2
- update faq on recommended android version and phones
- add help link inside the app, reference a page on the wiki
- turn on amazon reviews support
- add a tablet layout (with map next to messages) in the android app
# Medium priority
@@ -58,7 +54,6 @@ 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.
- read the PMU battery fault indicators and blink/led/warn user on screen
- discard very old nodedb records (> 1wk)
- add a watchdog timer
- handle millis() rollover in GPS.getTime - otherwise we will break after 50 days
- 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)

14
docs/software/ant.md Normal file
View File

@@ -0,0 +1,14 @@
# ANT protocol notes
SD340 terms are reasonable for NRF52
https://www.thisisant.com/developer/components/nrf52832#tab_protocol_stacks_tab
Profiles to implement:
tracker
https://www.thisisant.com/developer/ant-plus/device-profiles/#4365_tab
ebike
https://www.thisisant.com/developer/ant-plus/device-profiles/#527_tab
no profile for messaging?

View File

@@ -1,12 +1,26 @@
# Bluetooth API
# Device API
The Bluetooth API is design to have only a few characteristics and most polymorphism comes from the flexible set of Google Protocol Buffers which are sent over the wire. We use protocol buffers extensively both for the bluetooth API and for packets inside the mesh or when providing packets to other applications on the phone.
The Device API is design to have only a simple stream of ToRadio and FromRadio packets and all polymorphism comes from the flexible set of Google Protocol Buffers which are sent over the wire. We use protocol buffers extensively both for the bluetooth API and for packets inside the mesh or when providing packets to other applications on the phone.
## A note on MTU sizes
## Streaming version
This device will work with any MTU size, but it is highly recommended that you call your phone's "setMTU function to increase MTU to 512 bytes" as soon as you connect to a service. This will dramatically improve performance when reading/writing packets.
This protocol is **almost** identical when it is deployed over BLE, Serial/USB or TCP (our three currently supported transports for connecting to phone/PC). Most of this document is in terms of the original BLE version, but this section describes the small changes when this API is exposed over a Streaming (non datagram) transport. The streaming version has the following changes:
## MeshBluetoothService
- We assume the stream is reliable (though the protocol will resynchronize if bytes are lost or corrupted). i.e. we do not include CRCs or error correction codes.
- Packets always have a four byte header (described below) prefixed before each packet. This header provides framing characters and length.
- The stream going towards the radio is only a series of ToRadio packets (with the extra 4 byte headers)
- The stream going towards the PC is a stream of FromRadio packets (with the 4 byte headers), or if the receiver state machine does not see valid header bytes it can (optionally) print those bytes as the debug console from the radio. This allows the device to emit regular serial debugging messages (which can be understood by a terminal program) but also switch to a more structured set of protobufs once it sees that the PC client has sent a protobuf towards it.
The 4 byte header is constructed to both provide framing and to not look line 'normal' 7 bit ASCII.
- Byte 0: START1 (0x94)
- Byte 1: START2 (0xc3)
- Byte 2: MSB of protobuf length
- Byte 3: LSB of protobuf length
The receiver will validate length and if >512 it will assume the packet is corrupted and return to looking for START1. While looking for START1 any other characters are printed as "debug output". For small example implementation of this reader see the meshtastic-python implementation.
## MeshBluetoothService (the BLE API)
This is the main bluetooth service for the device and provides the API your app should use to get information about the mesh, send packets or provision the radio.
@@ -71,16 +85,20 @@ Not all messages are kept in the fromradio queue (filtered based on SubPacket):
- No WantNodeNum / DenyNodeNum messages are kept
A variable keepAllPackets, if set to true will suppress this behavior and instead keep everything for forwarding to the phone (for debugging)
## Protobuf API
### A note on MTU sizes
This device will work with any MTU size, but it is highly recommended that you call your phone's "setMTU function to increase MTU to 512 bytes" as soon as you connect to a service. This will dramatically improve performance when reading/writing packets.
### Protobuf API
On connect, you should send a want_config_id protobuf to the device. This will cause the device to send its node DB and radio config via the fromradio endpoint. After sending the full DB, the radio will send a want_config_id to indicate it is done sending the configuration.
## Other bluetooth services
### Other bluetooth services
This document focuses on the core mesh service, but it is worth noting that the following other Bluetooth services are also
This document focuses on the core device protocol, but it is worth noting that the following other Bluetooth services are also
provided by the device.
### BluetoothSoftwareUpdate
#### BluetoothSoftwareUpdate
The software update service. For a sample function that performs a software update using this API see [startUpdate](https://github.com/meshtastic/Meshtastic-Android/blob/master/app/src/main/java/com/geeksville/mesh/service/SoftwareUpdateService.kt).
@@ -98,10 +116,10 @@ Characteristics
| GATT_UUID_MANU_NAME/0x2a29 | read | |
| GATT_UUID_HW_VERSION_STR/0x2a27 | read | |
### DeviceInformationService
#### DeviceInformationService
Implements the standard BLE contract for this service (has software version, hardware model, serial number, etc...)
### BatteryLevelService
#### BatteryLevelService
Implements the standard BLE contract service, provides battery level in a way that most client devices should automatically understand (i.e. it should show in the bluetooth devices screen automatically)

View File

@@ -32,8 +32,8 @@ 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
- 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 Remove NodeNum assignment algorithm (now that we use 4 byte node nums)
- 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
- low priority: think more careful about reliable retransmit intervals
- make ReliableRouter.pending threadsafe

View File

@@ -1,19 +1,88 @@
# NRF52 TODO
## RAK815
TODO:
- i2c gps comms not quite right
- ble: AdafruitBluefruit::begin - adafruit_ble_task was assigned an invalid stack pointer. out of memory?
- measure power draw
### Bootloader
Install our (temporarily hacked up) adafruit bootloader
```
kevinh@kevin-server:~/development/meshtastic/Adafruit_nRF52_Bootloader$ make BOARD=rak815 sd 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.
```
### Appload
tips on installing https://github.com/platformio/platform-nordicnrf52/issues/8#issuecomment-374017768
to see console output over jlink:
```
12:17
in one tab run "bin/nrf52832-gdbserver.sh" - leave this running the whole time while developing/debugging
12:17
~/development/meshtastic/meshtastic-esp32$ bin/nrf52-console.sh
###RTT Client: ************************************************************
###RTT Client: * SEGGER Microcontroller GmbH *
###RTT Client: * Solutions for real time microcontroller applications *
###RTT Client: ************************************************************
###RTT Client: * *
###RTT Client: * (c) 2012 - 2016 SEGGER Microcontroller GmbH *
###RTT Client: * *
###RTT Client: * www.segger.com Support: support@segger.com *
###RTT Client: * *
###RTT Client: ************************************************************
###RTT Client: * *
###RTT Client: * SEGGER J-Link RTT Client Compiled Apr 7 2020 15:01:22 *
###RTT Client: * *
###RTT Client: ************************************************************
###RTT Client: -----------------------------------------------
###RTT Client: Connecting to J-Link RTT Server via localhost:19021 ..............
###RTT Client: Connected.
SEGGER J-Link V6.70c - Real time terminal output
SEGGER J-Link ARM V9.6, SN=69663845
Process: JLinkGDBServerCLExein another tab run:
12:18
On NRF52 I've been using the jlink fake serial console. But since the rak815 has the serial port hooked up we can switch back to that once the basics are working.
```
## Misc work items
RAM investigation.
nRF52832-QFAA 64KB ram, 512KB flash vs
nrf52832-QFAB 32KB ram, 512kb flash
nrf52833 128KB RAM
nrf52840 256KB RAM, 1MB flash
platform.json
Manual hacks needed to build (for now):
"framework-arduinoadafruitnrf52": {
"type": "framework",
"optional": true,
"version": "https://github.com/meshtastic/Adafruit_nRF52_Arduino.git"
},
kevinh@kevin-server:~/.platformio/packages/framework-arduinoadafruitnrf52/variants\$ ln -s ~/development/meshtastic/meshtastic-esp32/variants/\* .
## Initial work items

6
docs/software/rak815.md Normal file
View File

@@ -0,0 +1,6 @@
# RAK815
Notes on trying to get the RAK815 working with meshtastic.
good tutorial: https://www.hackster.io/naresh-krish/getting-started-with-rak815-tracker-module-and-arduino-1c7bc9
(includes software serial link - possibly useful for GPS)

View File

@@ -23,6 +23,8 @@ default_envs = tbeam ; Note: the github actions CI test build can't yet build NR
[env]
framework = arduino
; customize the partition table
; http://docs.platformio.org/en/latest/platforms/espressif32.html#partition-tables
board_build.partitions = partition-table.csv
@@ -72,13 +74,12 @@ lib_deps =
Wire ; explicitly needed here because the AXP202 library forgets to add it
https://github.com/meshtastic/arduino-fsm.git
https://github.com/meshtastic/SparkFun_Ublox_Arduino_Library.git
https://github.com/meshtastic/RadioLib.git
https://github.com/meshtastic/RadioLib.git#d6b12f7eb0a06bd2414c79b437b25d377e3f603f
https://github.com/meshtastic/TinyGPSPlus.git
; Common settings for ESP targes, mixin with extends = esp32_base
[esp32_base]
platform = espressif32
framework = arduino
src_filter =
${env.src_filter} -<nrf52/>
upload_speed = 921600
@@ -89,7 +90,7 @@ build_flags =
# board_build.ldscript = linker/esp32.extram.bss.ld
lib_ignore = segger_rtt
platform_packages =
framework-arduinoespressif32 @ https://github.com/meshtastic/arduino-esp32.git#f26c4f96fefd13ed0ed042e27954f8aba6328f6b
framework-arduinoespressif32 @ https://github.com/meshtastic/arduino-esp32.git#71ed4002c953d8c87f44ed27e34fe0735f99013e
; The 1.0 release of the TBEAM board
[env:tbeam]
@@ -98,42 +99,46 @@ board = ttgo-t-beam
lib_deps =
${env.lib_deps}
https://github.com/meshtastic/AXP202X_Library.git
build_flags =
${esp32_base.build_flags} -D TBEAM_V10
; 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.
;[env:tbeam0.7]
;extends = esp32_base
;board = ttgo-t-beam
;build_flags =
; ${esp32_base.build_flags} -D TBEAM_V07
[env:tbeam0.7]
extends = esp32_base
board = ttgo-t-beam
build_flags =
${esp32_base.build_flags} -D TBEAM_V07
[env:heltec]
;build_type = debug ; to make it possible to step through our jtag debugger
extends = esp32_base
board = heltec_wifi_lora_32_V2
[env:ttgo-lora32-v1]
[env:tlora-v1]
extends = esp32_base
board = ttgo-lora32-v1
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
[env:ttgo-lora32-v2]
[env:tlora-v2]
extends = esp32_base
board = ttgo-lora32-v1
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
; 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.
[env:cubecellplus]
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
; FIXME, bug in cubecell arduino - they are supposed to set ARDUINO
build_flags = ${env.build_flags} -DARDUINO=100 -Isrc/cubecell
@@ -142,8 +147,9 @@ src_filter =
; Common settings for NRF52 based targets
[nrf52_base]
platform = nordicnrf52
framework = arduino
; Instead of the standard nordicnrf52 platform, we use our fork which has our added variant files
; platform = nordicnrf52
platform = https://github.com/meshtastic/platform-nordicnrf52.git#62d185fe61b6c84c554046106529b4fd8f155e2c
debug_tool = jlink
build_type = debug ; I'm debugging with ICE a lot now
; note: liboberon provides the AES256 implementation for NRF52 (though not using the hardware acceleration of the NRF52840 - FIXME)
@@ -168,10 +174,30 @@ debug_init_break =
;debug_init_break = tbreak Reset_Handler
; The NRF52840-dk development board
[env:nrf52dk]
; Note: By default no lora device is created for this build - it uses a simulated interface
[env:nrf52840dk]
extends = nrf52_base
board = nrf52840_dk
; The NRF52840-dk development board, but @geeksville's board - which has a busted oscilliator
[env:nrf52840dk-geeksville]
extends = nrf52_base
board = nrf52840_dk_modified
; Note: By default no lora device is created for this build - it uses a simulated interface
[env:feather_nrf52832]
extends = nrf52_base
board = adafruit_feather_nrf52832
[env:rak815]
extends = nrf52_base
board = rak815
debug_tool = jlink
upload_protocol = jlink
monitor_port = /dev/ttyUSB0
; this board's serial chip can only run at 115200, not faster
monitor_speed = 115200
# For experimenting with RAM sizes
# board_build.ldscript = linker/nrf52840_s140_sim832.ld

16
src/BluetoothCommon.h Normal file
View File

@@ -0,0 +1,16 @@
#pragma once
#include <Arduino.h>
/**
* Common lib functions for all platforms that have bluetooth
*/
#define MESH_SERVICE_UUID "6ba1b218-15a8-461f-9fa8-5dcae273eafd"
#define TORADIO_UUID "f75c76d2-129e-4dad-a1dd-7866124401e7"
#define FROMRADIO_UUID "8ba2bcc2-ee02-4a55-a531-c525c5e454d5"
#define FROMNUM_UUID "ed9da18c-a800-4f66-a670-aa7547e34453"
/// Given a level between 0-100, update the BLE attribute
void updateBatteryLevel(uint8_t level);

126
src/GPSStatus.h Normal file
View File

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

83
src/NodeStatus.h Normal file
View File

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

View File

@@ -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);
}

View File

@@ -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
src/Power.cpp Normal file
View File

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

View File

@@ -5,9 +5,10 @@
#include "NodeDB.h"
#include "configuration.h"
#include "main.h"
#include "screen.h"
#include "graphics/Screen.h"
#include "sleep.h"
#include "target_specific.h"
#include "timing.h"
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
// !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);
}
@@ -130,7 +131,7 @@ static void onEnter()
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 (displayedNodeNum)

103
src/PowerStatus.h Normal file
View File

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

72
src/Status.h Normal file
View File

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

0
src/StatusHandler.h Normal file
View File

View File

@@ -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, &notification, portMAX_DELAY); // Wait forever
}
#endif

View File

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

15
src/commands.h Normal file
View File

@@ -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,
};

23
src/concurrency/Lock.cpp Normal file
View File

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

33
src/concurrency/Lock.h Normal file
View File

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

View File

@@ -0,0 +1,15 @@
#include "LockGuard.h"
namespace concurrency {
LockGuard::LockGuard(Lock *lock) : lock(lock)
{
lock->lock();
}
LockGuard::~LockGuard()
{
lock->unlock();
}
} // namespace concurrency

View File

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

View File

@@ -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, &notification, portMAX_DELAY); // Wait forever
}
} // namespace concurrency

View File

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

View File

@@ -1,12 +1,12 @@
#pragma once
#include "PeriodicTask.h"
#include <Arduino.h>
namespace concurrency {
/**
* Periodically invoke a callback.
*
* This just provides C style callback conventions rather than a virtual function - FIXME, remove?
* @brief Periodically invoke a callback. This just provides C-style callback conventions
* rather than a virtual function - FIXME, remove?
*/
class Periodic : public PeriodicTask
{
@@ -17,5 +17,10 @@ class Periodic : public PeriodicTask
Periodic(uint32_t (*_callback)()) : callback(_callback) {}
protected:
void doTask();
void doTask() {
uint32_t p = callback();
setPeriod(p);
}
};
} // namespace concurrency

View File

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

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,46 @@
#include "Thread.h"
#include "timing.h"
#include <assert.h>
#ifdef ARDUINO_ARCH_ESP32
#include "esp_task_wdt.h"
#endif
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();
}
void Thread::serviceWatchdog()
{
#ifdef ARDUINO_ARCH_ESP32
esp_task_wdt_reset();
#endif
}
void Thread::startWatchdog()
{
#ifdef ARDUINO_ARCH_ESP32
auto r = esp_task_wdt_add(taskHandle);
assert(r == ESP_OK);
#endif
}
void Thread::stopWatchdog()
{
#ifdef ARDUINO_ARCH_ESP32
auto r = esp_task_wdt_delete(taskHandle);
assert(r == ESP_OK);
#endif
}
} // namespace concurrency

47
src/concurrency/Thread.h Normal file
View File

@@ -0,0 +1,47 @@
#pragma once
#include "freertosinc.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();
void startWatchdog();
void stopWatchdog();
private:
static void callRun(void *_this);
};
} // namespace concurrency

View File

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

View File

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

View File

@@ -55,13 +55,17 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
/// Convert a preprocessor name into a quoted string and if that string is empty use "unset"
#define optstr(s) (xstr(s)[0] ? xstr(s) : "unset")
#ifdef NRF52840_XXAA // All of the NRF52 targets are configured using variant.h, so this section shouldn't need to be
#ifdef NRF52_SERIES // All of the NRF52 targets are configured using variant.h, so this section shouldn't need to be
// board specific
//
// Standard definitions for NRF52 targets
//
// Nop definition for these attributes - not used on NRF52
#define EXT_RAM_ATTR
#define IRAM_ATTR
#define NO_ESP32 // Don't use ESP32 libs (mainly bluetooth)
// We bind to the GPS using variant.h instead for this platform (Serial1)
@@ -70,7 +74,11 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#define RTC_DATA_ATTR
#define LED_PIN PIN_LED1 // LED1 on nrf52840-DK
// If the variant filed defines as standard button
#ifdef PIN_BUTTON1
#define BUTTON_PIN PIN_BUTTON1
#endif
// FIXME, use variant.h defs for all of this!!! (even on the ESP32 targets)
#elif defined(CubeCell_BoardPlus)
@@ -105,10 +113,13 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
// LoRa SPI
// -----------------------------------------------------------------------------
#define SCK_GPIO 5
#define MISO_GPIO 19
#define MOSI_GPIO 27
#define NSS_GPIO 18
// NRF52 boards will define this in variant.h
#ifndef RF95_SCK
#define RF95_SCK 5
#define RF95_MISO 19
#define RF95_MOSI 27
#define RF95_NSS 18
#endif
#endif
@@ -148,11 +159,11 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#define BUTTON_PIN_ALT 13 // Alternate GPIO for an external button if needed
#ifndef USE_JTAG
#define RESET_GPIO 14
#define RF95_RESET 14
#endif
#define RF95_IRQ_GPIO 26
#define DIO1_GPIO 33 // Note: not really used on this board
#define DIO2_GPIO 32 // Note: not really used on this board
#define RF95_IRQ 26
#define RF95_DIO1 33 // Note: not really used on this board
#define RF95_DIO2 32 // Note: not really used on this board
// Leave undefined to disable our PMU IRQ handler
#define PMU_IRQ 35
@@ -171,11 +182,11 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#define BUTTON_PIN 39
#ifndef USE_JTAG
#define RESET_GPIO 23
#define RF95_RESET 23
#endif
#define RF95_IRQ_GPIO 26
#define DIO1_GPIO 33 // Note: not really used on this board
#define DIO2_GPIO 32 // Note: not really used on this board
#define RF95_IRQ 26
#define RF95_DIO1 33 // Note: not really used on this board
#define RF95_DIO2 32 // Note: not really used on this board
// This board has different GPS pins than all other boards
#undef GPS_RX_PIN
@@ -206,14 +217,14 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#define BUTTON_PIN 0 // If defined, this will be used for user button presses
#ifndef USE_JTAG
#define RESET_GPIO 14 // If defined, this pin will be used to reset the LORA radio
#define RF95_RESET 14 // If defined, this pin will be used to reset the LORA radio
#endif
#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 DIO2_GPIO 34 // DIO1 & DIO2 are not currently used, but they must be assigned to a pin number
#elif defined(TTGO_LORA_V1)
#define RF95_IRQ 26
#define RF95_DIO1 35 // DIO1 & DIO2 are not currently used, but they must be assigned to a pin number
#define RF95_DIO2 34 // DIO1 & DIO2 are not currently used, but they must be assigned to a pin number
#elif defined(TLORA_V1)
// 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
@@ -228,13 +239,19 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#define LED_PIN 2 // If defined we will blink this LED
#define BUTTON_PIN 0 // If defined, this will be used for user button presses
#define 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 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
#elif defined(TTGO_LORA_V2)
#define RF95_RESET 14 // If defined, this pin will be used to reset the LORA radio
#define RF95_IRQ 26 // IRQ line for the LORA radio
#define RF95_DIO1 35 // DIO1 & DIO2 are not currently used, but they must be assigned to a pin number
#define RF95_DIO2 34 // DIO1 & DIO2 are not currently used, but they must be assigned to a pin number
#elif defined(TLORA_V2)
// 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_SCL 22
@@ -245,12 +262,37 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#define LED_PIN 25 // If defined we will blink this LED
#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
// between this pin and ground
#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 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
#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 RF95_RESET 14 // If defined, this pin will be used to reset the LORA radio
#define RF95_IRQ 26 // IRQ line for the LORA radio
#define RF95_DIO1 35 // DIO1 & DIO2 are not currently used, but they must be assigned to a pin number
#define RF95_DIO2 34 // DIO1 & DIO2 are not currently used, but they must be assigned to a pin number
#endif
#ifdef ARDUINO_NRF52840_PCA10056
@@ -262,25 +304,25 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#undef LED_INVERTED
#define LED_INVERTED 1
// Uncomment to confirm if we can build the RF95 driver for NRF52
#if 0
#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 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
#elif defined(ARDUINO_NRF52840_PPR)
#define HW_VENDOR "ppr"
#elif NRF52_SERIES
#define HW_VENDOR "nrf52unknown" // FIXME - unknown nrf52 board
#endif
// -----------------------------------------------------------------------------
// DEBUG
// -----------------------------------------------------------------------------
#ifdef CONSOLE_MAX_BAUD
#define SERIAL_BAUD CONSOLE_MAX_BAUD
#else
#define SERIAL_BAUD 921600 // Serial debug baud rate
#endif
#include "SerialConsole.h"
@@ -288,13 +330,24 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
// What platforms should use SEGGER?
#ifdef NRF52_SERIES
// Always include the SEGGER code on NRF52 - because useful for debugging
#include "SEGGER_RTT.h"
// Debug printing to segger console
#define SEGGER_MSG(...) SEGGER_RTT_printf(0, __VA_ARGS__)
// nrf52 gets its settings via variant files
#ifndef PIN_SERIAL_RX
// No serial ports on this board - ONLY use segger in memory console
#define USE_SEGGER
#endif
#else
#define SERIAL0_RX_GPIO 3 // Always GPIO3 on ESP32
#endif
#ifdef USE_SEGGER
#include "SEGGER_RTT.h"
#define DEBUG_MSG(...) SEGGER_RTT_printf(0, __VA_ARGS__)
#else
#ifdef DEBUG_PORT

View File

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

View File

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

View File

@@ -3,21 +3,22 @@
#include "CallbackCharacteristic.h"
#include "RadioLibInterface.h"
#include "configuration.h"
#include "lock.h"
#include "../concurrency/LockGuard.h"
#include "../timing.h"
#include <Arduino.h>
#include <BLE2902.h>
#include <CRC32.h>
#include <Update.h>
#include <esp_gatt_defs.h>
using namespace meshtastic;
//using namespace meshtastic;
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 updateExpectedSize, updateActualSize;
Lock *updateLock;
concurrency::Lock *updateLock;
class TotalSizeCharacteristic : public CallbackCharacteristic
{
@@ -30,7 +31,7 @@ class TotalSizeCharacteristic : public CallbackCharacteristic
void onWrite(BLECharacteristic *c)
{
LockGuard g(updateLock);
concurrency::LockGuard g(updateLock);
// Check if there is enough to OTA Update
uint32_t len = getValue32(c, 0);
updateExpectedSize = len;
@@ -65,7 +66,7 @@ class DataCharacteristic : public CallbackCharacteristic
void onWrite(BLECharacteristic *c)
{
LockGuard g(updateLock);
concurrency::LockGuard g(updateLock);
std::string value = c->getValue();
uint32_t len = value.length();
assert(len <= MAX_BLOCKSIZE);
@@ -89,7 +90,7 @@ class CRC32Characteristic : public CallbackCharacteristic
void onWrite(BLECharacteristic *c)
{
LockGuard g(updateLock);
concurrency::LockGuard g(updateLock);
uint32_t expectedCRC = getValue32(c, 0);
uint32_t actualCRC = crc.finalize();
DEBUG_MSG("expected CRC %u\n", expectedCRC);
@@ -106,7 +107,7 @@ class CRC32Characteristic : public CallbackCharacteristic
} else {
if (Update.end()) {
DEBUG_MSG("OTA done, rebooting in 5 seconds!\n");
rebootAtMsec = millis() + 5000;
rebootAtMsec = timing::millis() + 5000;
} else {
DEBUG_MSG("Error Occurred. Error #: %d\n", Update.getError());
}
@@ -124,7 +125,7 @@ class CRC32Characteristic : public CallbackCharacteristic
void bluetoothRebootCheck()
{
if (rebootAtMsec && millis() > rebootAtMsec) {
if (rebootAtMsec && timing::millis() > rebootAtMsec) {
DEBUG_MSG("Rebooting for update\n");
ESP.restart();
}
@@ -137,7 +138,7 @@ See bluetooth-api.md
BLEService *createUpdateService(BLEServer *server, std::string hwVendor, std::string swVersion, std::string hwVersion)
{
if (!updateLock)
updateLock = new Lock();
updateLock = new concurrency::Lock();
// Create the BLE Service
BLEService *service = server->createService(BLEUUID("cb0b9a0b-a84c-4c0d-bdbb-442e3144ee30"), 25, 0);

View File

@@ -35,8 +35,5 @@ BLECharacteristic *addBLECharacteristic(BLECharacteristic *c);
/// Add a characteristic that we will delete when we restart
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()
extern SimpleAllocator btPool;

View File

@@ -5,6 +5,7 @@
#include <assert.h>
#include <esp_gatt_defs.h>
#include "BluetoothCommon.h"
#include "CallbackCharacteristic.h"
#include "GPS.h"
#include "MeshService.h"
@@ -39,26 +40,20 @@ class BluetoothPhoneAPI : public PhoneAPI
}
};
BluetoothPhoneAPI *bluetoothPhoneAPI;
static BluetoothPhoneAPI *bluetoothPhoneAPI;
class ToRadioCharacteristic : public CallbackCharacteristic
{
public:
ToRadioCharacteristic() : CallbackCharacteristic("f75c76d2-129e-4dad-a1dd-7866124401e7", BLECharacteristic::PROPERTY_WRITE) {}
ToRadioCharacteristic() : CallbackCharacteristic(TORADIO_UUID, BLECharacteristic::PROPERTY_WRITE) {}
void onWrite(BLECharacteristic *c)
{
bluetoothPhoneAPI->handleToRadio(c->getData(), c->getValue().length());
}
void onWrite(BLECharacteristic *c) { bluetoothPhoneAPI->handleToRadio(c->getData(), c->getValue().length()); }
};
class FromRadioCharacteristic : public CallbackCharacteristic
{
public:
FromRadioCharacteristic() : CallbackCharacteristic("8ba2bcc2-ee02-4a55-a531-c525c5e454d5", BLECharacteristic::PROPERTY_READ)
{
}
FromRadioCharacteristic() : CallbackCharacteristic(FROMRADIO_UUID, BLECharacteristic::PROPERTY_READ) {}
void onRead(BLECharacteristic *c)
{
@@ -78,8 +73,7 @@ class FromNumCharacteristic : public CallbackCharacteristic
{
public:
FromNumCharacteristic()
: CallbackCharacteristic("ed9da18c-a800-4f66-a670-aa7547e34453", BLECharacteristic::PROPERTY_WRITE |
BLECharacteristic::PROPERTY_READ |
: CallbackCharacteristic(FROMNUM_UUID, BLECharacteristic::PROPERTY_WRITE | BLECharacteristic::PROPERTY_READ |
BLECharacteristic::PROPERTY_NOTIFY)
{
// observe(&service.fromNumChanged);
@@ -100,7 +94,7 @@ BLEService *createMeshBluetoothService(BLEServer *server)
}
// Create the BLE Service, we need more than the default of 15 handles
BLEService *service = server->createService(BLEUUID("6ba1b218-15a8-461f-9fa8-5dcae273eafd"), 30, 0);
BLEService *service = server->createService(BLEUUID(MESH_SERVICE_UUID), 30, 0);
assert(!meshFromNumCharacteristic);
meshFromNumCharacteristic = new FromNumCharacteristic;

View File

@@ -0,0 +1,57 @@
#include "WiFiServerAPI.h"
#include "PowerFSM.h"
#include "configuration.h"
#include <Arduino.h>
WiFiServerAPI::WiFiServerAPI(WiFiClient &_client) : StreamAPI(&client), client(_client)
{
DEBUG_MSG("Incoming connection from %s\n", client.remoteIP().toString().c_str());
}
WiFiServerAPI::~WiFiServerAPI()
{
client.stop();
// FIXME - delete this if the client dropps the connection!
}
/// Hookable to find out when connection changes
void WiFiServerAPI::onConnectionChanged(bool connected)
{
// FIXME - we really should be doing global reference counting to see if anyone is currently using serial or wifi and if so,
// block sleep
if (connected) { // To prevent user confusion, turn off bluetooth while using the serial port api
powerFSM.trigger(EVENT_SERIAL_CONNECTED);
} else {
powerFSM.trigger(EVENT_SERIAL_DISCONNECTED);
}
}
void WiFiServerAPI::loop()
{
if (client.connected()) {
StreamAPI::loop();
} else {
DEBUG_MSG("Client dropped connection, closing UDP server\n");
delete this;
}
}
#define MESHTASTIC_PORTNUM 4403
WiFiServerPort::WiFiServerPort() : WiFiServer(MESHTASTIC_PORTNUM) {}
void WiFiServerPort::init()
{
DEBUG_MSG("Listening on TCP port %d\n", MESHTASTIC_PORTNUM);
begin();
}
void WiFiServerPort::loop()
{
auto client = available();
if (client) {
new WiFiServerAPI(client);
}
}

38
src/esp32/WiFiServerAPI.h Normal file
View File

@@ -0,0 +1,38 @@
#pragma once
#include "StreamAPI.h"
#include <WiFi.h>
/**
* Provides both debug printing and, if the client starts sending protobufs to us, switches to send/receive protobufs
* (and starts dropping debug printing - FIXME, eventually those prints should be encapsulated in protobufs).
*/
class WiFiServerAPI : public StreamAPI
{
private:
WiFiClient client;
public:
WiFiServerAPI(WiFiClient &_client);
virtual ~WiFiServerAPI();
virtual void loop(); // Check for dropped client connections
protected:
/// Hookable to find out when connection changes
virtual void onConnectionChanged(bool connected);
};
/**
* Listens for incoming connections and does accepts and creates instances of WiFiServerAPI as needed
*/
class WiFiServerPort : public WiFiServer
{
public:
WiFiServerPort();
void init();
void loop();
};

View File

@@ -2,11 +2,11 @@
#include "MeshBluetoothService.h"
#include "PowerFSM.h"
#include "configuration.h"
#include "esp_task_wdt.h"
#include "main.h"
#include "power.h"
#include "sleep.h"
#include "utils.h"
#include "target_specific.h"
#include "utils.h"
bool bluetoothOn;
@@ -60,111 +60,6 @@ void getMacAddr(uint8_t *dmac)
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();
// If the AXP192 returns a valid battery percentage, use it
if (axp.getBattPercentage() >= 0) {
powerStatus.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
powerStatus.batteryChargePercent = clamp((int)(((powerStatus.batteryVoltageMv - BAT_MILLIVOLTS_EMPTY) * 1e2) / (BAT_MILLIVOLTS_FULL - BAT_MILLIVOLTS_EMPTY)), 0, 100);
}
DEBUG_MSG("Battery %dmV %d%%\n", powerStatus.batteryVoltageMv, powerStatus.batteryChargePercent);
}
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() {
int dev_num = esp_ble_get_bond_device_num();
@@ -190,9 +85,15 @@ void esp32Setup()
// enableModemSleep();
#ifdef AXP192_SLAVE_ADDRESS
axp192Init();
#endif
// Since we are turning on watchdogs rather late in the release schedule, we really don't want to catch any
// false positives. The wait-to-sleep timeout for shutting down radios is 30 secs, so pick 45 for now.
#define APP_WATCHDOG_SECS 45
auto 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
@@ -215,51 +116,12 @@ uint32_t axpDebugRead()
Periodic axpDebugOutput(axpDebugRead);
#endif
/// loop code specific to ESP32 targets
void esp32Loop()
{
esp_task_wdt_reset(); // service our app level watchdog
loopBLE();
// for debug printing
// 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
}

View File

@@ -1,16 +1,25 @@
#include "GPS.h"
#include "configuration.h"
#include "time.h"
#include "timing.h"
#include <assert.h>
#include <sys/time.h>
#include <time.h>
// If we have a serial GPS port it will not be null
#ifdef GPS_RX_PIN
HardwareSerial _serial_gps_real(GPS_SERIAL_NUM);
HardwareSerial &GPS::_serial_gps = _serial_gps_real;
HardwareSerial *GPS::_serial_gps = &_serial_gps_real;
#elif defined(NRF52840_XXAA)
// Assume NRF52840
HardwareSerial *GPS::_serial_gps = &Serial1;
#else
// Assume NRF52
HardwareSerial &GPS::_serial_gps = Serial1;
HardwareSerial *GPS::_serial_gps = NULL;
#endif
#ifdef GPS_I2C_ADDRESS
uint8_t GPS::i2cAddress = GPS_I2C_ADDRESS;
#else
uint8_t GPS::i2cAddress = 0;
#endif
bool timeSetFromGPS; // We try to set our time from GPS each time we wake from sleep
@@ -27,7 +36,7 @@ void readFromRTC()
struct timeval tv; /* btw settimeofday() is helpfull here too*/
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);
timeStartMsec = now;
@@ -63,16 +72,14 @@ void perhapsSetRTC(struct tm &t)
// DEBUG_MSG("Got time from GPS month=%d, year=%d, unixtime=%ld\n", t.tm_mon, t.tm_year, tv.tv_sec);
if (t.tm_year < 0 || t.tm_year >= 300)
DEBUG_MSG("Ignoring invalid GPS time\n");
DEBUG_MSG("Ignoring invalid GPS month=%d, year=%d, unixtime=%ld\n", t.tm_mon, t.tm_year, tv.tv_sec);
else
perhapsSetRTC(&tv);
}
#include <time.h>
uint32_t getTime()
{
return ((millis() - timeStartMsec) / 1000) + zeroOffsetSecs;
return ((timing::millis() - timeStartMsec) / 1000) + zeroOffsetSecs;
}
uint32_t getValidTime()

View File

@@ -1,7 +1,8 @@
#pragma once
#include "../concurrency/PeriodicTask.h"
#include "GPSStatus.h"
#include "Observer.h"
#include "PeriodicTask.h"
#include "sys/time.h"
/// If we haven't yet set our RTC this boot, set it from a GPS derived time
@@ -29,17 +30,26 @@ class GPS : public Observable<void *>
protected:
bool hasValidLocation = false; // default to false, until we complete our first read
static HardwareSerial &_serial_gps;
public:
/** If !NULL we will use this serial port to construct our GPS */
static HardwareSerial *_serial_gps;
/** If !0 we will attempt to connect to the GPS over I2C */
static uint8_t i2cAddress;
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 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
virtual ~GPS() {}
Observable<const meshtastic::GPSStatus *> newStatus;
/**
* Returns true if we succeeded
*/

View File

@@ -1,5 +1,6 @@
#include "NEMAGPS.h"
#include "configuration.h"
#include "timing.h"
static int32_t toDegInt(RawDegrees d)
{
@@ -12,14 +13,13 @@ static int32_t toDegInt(RawDegrees d)
void NEMAGPS::loop()
{
while (_serial_gps.available() > 0) {
int c = _serial_gps.read();
while (_serial_gps->available() > 0) {
int c = _serial_gps->read();
// Serial.write(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
// serial chars at whatever rate)
lastUpdateMsec = now;
@@ -54,16 +54,26 @@ void NEMAGPS::loop()
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()) {
if (reader.hdop.isValid()) {
dop = reader.hdop.value();
}
if (reader.course.isValid()) {
heading = reader.course.value() * 1e3; //Scale the heading (in degrees * 10^-2) to match the expected degrees * 10^-5
}
if (reader.satellites.isValid()) {
numSatellites = reader.satellites.value();
}
// expect gps pos lat=37.520825, lon=-122.309162, alt=158
DEBUG_MSG("new NEMA GPS pos lat=%f, lon=%f, alt=%d, hdop=%f\n", latitude * 1e-7, longitude * 1e-7, altitude, dop * 1e-2);
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
if (hasValidLocation)
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);
}
}

View File

@@ -2,7 +2,7 @@
#include "GPS.h"
#include "Observer.h"
#include "PeriodicTask.h"
#include "../concurrency/PeriodicTask.h"
#include "TinyGPS++.h"
/**

View File

@@ -2,28 +2,51 @@
#include "sleep.h"
#include <assert.h>
UBloxGPS::UBloxGPS() : PeriodicTask()
UBloxGPS::UBloxGPS() : concurrency::PeriodicTask()
{
notifySleepObserver.observe(&notifySleep);
}
bool UBloxGPS::tryConnect()
{
isConnected = false;
if (_serial_gps)
isConnected = ublox.begin(*_serial_gps);
if (!isConnected && i2cAddress) {
extern bool neo6M; // Super skanky - if we are talking to the device i2c we assume it is a neo7 on a RAK815, which
// supports the newer API
neo6M = true;
isConnected = ublox.begin(Wire, i2cAddress);
}
return isConnected;
}
bool UBloxGPS::setup()
{
if (_serial_gps) {
#ifdef GPS_RX_PIN
_serial_gps.begin(GPS_BAUDRATE, SERIAL_8N1, GPS_RX_PIN, GPS_TX_PIN);
_serial_gps->begin(GPS_BAUDRATE, SERIAL_8N1, GPS_RX_PIN, GPS_TX_PIN);
#else
_serial_gps.begin(GPS_BAUDRATE);
_serial_gps->begin(GPS_BAUDRATE);
#endif
// _serial_gps.setRxBufferSize(1024); // the default is 256
}
#ifdef GPS_POWER_EN
pinMode(GPS_POWER_EN, OUTPUT);
digitalWrite(GPS_POWER_EN, 1);
delay(200); // Give time for the GPS to startup after we gave power
#endif
// ublox.enableDebugging(Serial);
// note: the lib's implementation has the wrong docs for what the return val is
// it is not a bool, it returns zero for success
isConnected = ublox.begin(_serial_gps);
// try a second time, the ublox lib serial parsing is buggy?
if (!isConnected)
isConnected = ublox.begin(_serial_gps);
if (!tryConnect())
tryConnect();
if (isConnected) {
DEBUG_MSG("Connected to UBLOX GPS successfully\n");
@@ -35,14 +58,21 @@ bool UBloxGPS::setup()
// GPS_TX connected)
ublox.factoryReset();
delay(3000);
isConnected = ublox.begin(_serial_gps);
tryConnect();
DEBUG_MSG("Factory reset success=%d\n", isConnected);
ok = ublox.saveConfiguration(3000);
assert(ok);
return false;
} else {
if (_serial_gps) {
ok = ublox.setUART1Output(COM_TYPE_UBX, 500); // Use native API
assert(ok);
}
if (i2cAddress) {
ok = ublox.setI2COutput(COM_TYPE_UBX, 500);
assert(ok);
}
ok = ublox.setNavigationFrequency(1, 500); // Produce 4x/sec to keep the amount of time we stall in getPVT low
assert(ok);
// ok = ublox.setAutoPVT(false); // Not implemented on NEO-6M
@@ -55,7 +85,7 @@ bool UBloxGPS::setup()
ok = ublox.saveConfiguration(3000);
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;
} else {
@@ -80,20 +110,22 @@ void UBloxGPS::doTask()
// Consume all characters that have arrived
// getPVT automatically calls checkUblox
// if using i2c or serial look too see if any chars are ready
ublox.checkUblox(); // See if new data is available. Process bytes as they come in.
// If we don't have a fix (a quick check), don't try waiting for a solution)
// Hmmm my fix type reading returns zeros for fix, which doesn't seem correct, because it is still sptting out positions
// turn off for now
fixtype = ublox.getFixType(0);
uint16_t maxWait = i2cAddress ? 300 : 0; // If using i2c we must poll with wait
fixtype = ublox.getFixType(maxWait);
DEBUG_MSG("GPS fix type %d\n", fixtype);
// DEBUG_MSG("sec %d\n", ublox.getSecond());
// DEBUG_MSG("lat %d\n", ublox.getLatitude());
// any fix that has time
if (ublox.getT(0)) {
if (ublox.getT(maxWait)) {
/* Convert to unix time
The Unix epoch (or Unix time or POSIX time or Unix timestamp) is the number of seconds that have elapsed since January 1, 1970
(midnight UTC/GMT), not counting leap seconds (in ISO 8601: 1970-01-01T00:00:00Z).
@@ -109,18 +141,20 @@ The Unix epoch (or Unix time or POSIX time or Unix timestamp) is the number of s
perhapsSetRTC(t);
}
if ((fixtype >= 3 && fixtype <= 4) && ublox.getP(0)) // rd fixes only
{
// we only notify if position has changed
latitude = ublox.getLatitude(0);
longitude = ublox.getLongitude(0);
altitude = ublox.getAltitude(0) / 1000; // in mm convert to meters
dop = ublox.getPDOP(0); // PDOP (an accuracy metric) is reported in 10^2 units so we have to scale down when we use it
DEBUG_MSG("new gps pos lat=%f, lon=%f, alt=%d, pdop=%f\n", latitude * 1e-7, longitude * 1e-7, altitude, dop * 1e-2);
heading = ublox.getHeading(0);
numSatellites = ublox.getSIV(0);
// bogus lat lon is reported as 0 or 0 (can be bogus just for one)
// Also: apparently when the GPS is initially reporting lock it can output a bogus latitude > 90 deg!
hasValidLocation = (latitude != 0) && (longitude != 0) && (latitude <= 900000000 && latitude >= -900000000);
hasValidLocation = (latitude != 0) && (longitude != 0) && (latitude <= 900000000 && latitude >= -900000000) && (numSatellites > 0);
// we only notify if position has changed due to a new fix
if ((fixtype >= 3 && fixtype <= 4) && ublox.getP(maxWait)) // rd fixes only
{
if (hasValidLocation) {
wantNewLocation = false;
notifyObservers(NULL);
@@ -129,6 +163,11 @@ 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
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
// the serial
setPeriod(hasValidLocation && !wantNewLocation ? 30 * 1000 : 10 * 1000);

View File

@@ -2,7 +2,7 @@
#include "GPS.h"
#include "Observer.h"
#include "PeriodicTask.h"
#include "../concurrency/PeriodicTask.h"
#include "SparkFun_Ublox_Arduino_Library.h"
/**
@@ -10,7 +10,7 @@
*
* 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;
@@ -38,4 +38,7 @@ class UBloxGPS : public GPS, public PeriodicTask
/// Prepare the GPS for the cpu entering deep or light sleep, expect to be gone for at least 100s of msecs
/// always returns 0 to indicate okay to sleep
int prepareSleep(void *unused);
/// Attempt to connect to our GPS, returns false if no gps is present
bool tryConnect();
};

View File

@@ -26,28 +26,16 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#include "MeshService.h"
#include "NodeDB.h"
#include "configuration.h"
#include "fonts.h"
#include "images.h"
#include "graphics/images.h"
#include "main.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
#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
using namespace meshtastic; /** @todo remove */
#define NUM_EXTRA_FRAMES 2 // text message and debug frame
namespace meshtastic
namespace graphics
{
// A text message frame + debug frame + all the node infos
@@ -56,6 +44,13 @@ static uint32_t targetFramerate = IDLE_FRAMERATE;
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)
{
@@ -69,10 +64,11 @@ static void drawBootScreen(OLEDDisplay *display, OLEDDisplayUiState *state, int1
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
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
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);
}
@@ -91,8 +87,8 @@ static void drawFrameBluetooth(OLEDDisplay *display, OLEDDisplayUiState *state,
display->setFont(ArialMT_Plain_10);
char buf[30];
const char *name = "Name: ";
strcpy(buf,name);
strcat(buf,getDeviceName());
strcpy(buf, name);
strcat(buf, getDeviceName());
display->drawString(64 + x, 48 + y, buf);
}
@@ -142,10 +138,11 @@ 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
/// @return the max y we ended up printing to
static uint32_t drawRows(OLEDDisplay *display, int16_t x, int16_t y, const char **fields)
{
#if 0
/// Draw a series of fields in a row, wrapping to multiple rows if needed
/// @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);
@@ -170,47 +167,77 @@ static uint32_t drawRows(OLEDDisplay *display, int16_t x, int16_t y, const char
}
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, 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 };
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->charging) {
if (powerStatus->getIsCharging()) {
memcpy(imgBuffer + 3, lightning, 8);
// If not charging, Draw power bars
} else {
for (int i = 0; i < 4; i++) {
if(powerStatus->batteryChargePercent >= 25 * i) memcpy(imgBuffer + 1 + (i * 3), powerBar, 3);
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, int nodesOnline, int nodesTotal) {
static void drawNodes(OLEDDisplay *display, int16_t x, int16_t y, NodeStatus *nodeStatus)
{
char usersString[20];
sprintf(usersString, "%d/%d", nodesOnline, nodesTotal);
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, GPS *gps) {
if(!gps->isConnected) { display->drawString(x, y - 2, "No GPS"); return; }
display->drawFastImage(x, y, 6, 8, gps->hasLock() ? imgPositionSolid : imgPositionEmpty );
if(!gps->hasLock()) { display->drawString(x + 8, y - 2, "No sats"); return; }
if(gps->dop <= 100) { display->drawString(x + 8, y - 2, "Ideal"); return; }
if(gps->dop <= 200) { display->drawString(x + 8, y - 2, "Exc."); return; }
if(gps->dop <= 500) { display->drawString(x + 8, y - 2, "Good"); return; }
if(gps->dop <= 1000) { display->drawString(x + 8, y - 2, "Mod."); return; }
if(gps->dop <= 2000) { display->drawString(x + 8, y - 2, "Fair"); return; }
if(gps->dop > 0) { display->drawString(x + 8, y - 2, "Poor"); return; }
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
@@ -352,28 +379,41 @@ static bool hasPosition(NodeInfo *n)
static size_t nodeIndex;
static int8_t prevFrame = -1;
// Draw the compass and arrow pointing to location
static void drawCompass(OLEDDisplay *display, int16_t compassX, int16_t compassY, float headingRadian)
// Draw the arrow pointing to a node's location
static void drawNodeHeading(OLEDDisplay *display, int16_t compassX, int16_t compassY, float headingRadian)
{
// 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};
Point *arrowPoints[] = {&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);
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);
}
display->drawCircle(compassX, compassY, COMPASS_DIAM / 2);
// 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
@@ -393,11 +433,14 @@ static void drawNodeInfo(OLEDDisplay *display, OLEDDisplayUiState *state, int16_
nodeIndex = (nodeIndex + 1) % nodeDB.getNumNodes();
n = nodeDB.getNodeByIndex(nodeIndex);
}
// We just changed to a new node screen, ask that node for updated state
displayedNodeNum = n->num;
// 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);
@@ -421,17 +464,26 @@ static void drawNodeInfo(OLEDDisplay *display, OLEDDisplayUiState *state, int16_
snprintf(lastStr, sizeof(lastStr), "%u hours ago", agoSecs / 60 / 60);
static char distStr[20];
*distStr = 0; // might not have location data
strcpy(distStr, "? km"); // might not have location data
float headingRadian;
NodeInfo *ourNode = nodeDB.getNode(nodeDB.getNodeNum());
const char *fields[] = {username, distStr, signalStr, lastStr, NULL};
drawColumns(display, x, y, fields);
// coordinates for the center of the compass/circle
int16_t compassX = x + SCREEN_WIDTH - COMPASS_DIAM / 2 - 1, compassY = y + SCREEN_HEIGHT / 2;
int16_t compassX = x + SCREEN_WIDTH - COMPASS_DIAM / 2 - 5, compassY = y + SCREEN_HEIGHT / 2;
bool hasNodeHeading = false;
if (ourNode && hasPosition(ourNode) && hasPosition(node)) { // display direction toward node
Position &op = ourNode->position, &p = node->position;
if(ourNode && hasPosition(ourNode))
{
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);
@@ -441,16 +493,20 @@ static void drawNodeInfo(OLEDDisplay *display, OLEDDisplayUiState *state, int16_
// 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));
float myHeading = estimatedHeading(DegD(p.latitude_i), DegD(p.longitude_i));
headingRadian = bearingToOther - myHeading;
drawCompass(display, compassX, compassY, headingRadian);
} else { // direction to node is unknown so display question mark
drawNodeHeading(display, compassX, compassY, headingRadian);
}
}
if(!hasNodeHeading)
// direction to node is unknown so display question mark
// Debug info for gps lock errors
// DEBUG_MSG("ourNode %d, ourPos %d, theirPos %d\n", !!ourNode, ourNode && hasPosition(ourNode), hasPosition(node));
display->drawString(compassX - FONT_HEIGHT/4, compassY - FONT_HEIGHT/2, "?");
display->drawString(compassX - FONT_HEIGHT / 4, compassY - FONT_HEIGHT / 2, "?");
display->drawCircle(compassX, compassY, COMPASS_DIAM / 2);
}
// Must be after distStr is populated
drawColumns(display, x, y, fields);
}
#if 0
@@ -498,7 +554,7 @@ void Screen::handleSetOn(bool on)
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
// is never found when probing i2c and therefore we don't call setup and never want to do (invalid) accesses to this device.
@@ -545,6 +601,11 @@ void Screen::setup()
// twice initially.
ui.update();
ui.update();
// Subscribe to status updates
powerStatusObserver.observe(&powerStatus->onNewStatus);
gpsStatusObserver.observe(&gpsStatus->onNewStatus);
nodeStatusObserver.observe(&nodeStatus->onNewStatus);
}
void Screen::doTask()
@@ -557,7 +618,7 @@ void Screen::doTask()
// Process incoming commands.
for (;;) {
CmdItem cmd;
ScreenCmd cmd;
if (!cmdQueue.dequeue(&cmd, 0)) {
break;
}
@@ -606,14 +667,7 @@ void Screen::doTask()
// While showing the bootscreen or Bluetooth pair screen all of our
// standard screen switching is stopped.
if (showingNormalScreen) {
// TODO(girts): decouple nodeDB from screen.
// 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;
}
// standard screen loop handling here
}
ui.update();
@@ -638,8 +692,8 @@ void Screen::setFrames()
DEBUG_MSG("showing standard frames\n");
showingNormalScreen = true;
size_t numnodes = nodeDB.getNumNodes();
// 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)
numnodes--;
@@ -715,36 +769,61 @@ void DebugInfo::drawFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16
char channelStr[20];
{
LockGuard guard(&lock);
concurrency::LockGuard guard(&lock);
snprintf(channelStr, sizeof(channelStr), "#%s", channelName.c_str());
// Display power status
if (powerStatus.haveBattery) drawBattery(display, x, y + 2, imgBattery, &powerStatus); else display->drawFastImage(x, y + 2, 16, 8, powerStatus.usb ? imgUSB : imgPower);
if (powerStatus->getHasBattery())
drawBattery(display, x, y + 2, imgBattery, powerStatus);
else
display->drawFastImage(x, y + 2, 16, 8, powerStatus->getHasUSB() ? imgUSB : imgPower);
// Display nodes status
drawNodes(display, x + (SCREEN_WIDTH * 0.25), y + 2, nodesOnline, nodesTotal);
drawNodes(display, x + (SCREEN_WIDTH * 0.25), y + 2, nodeStatus);
// Display GPS status
drawGPS(display, x + (SCREEN_WIDTH * 0.66), y + 2, gps);
drawGPS(display, x + (SCREEN_WIDTH * 0.63), y + 2, gpsStatus);
}
const char *fields[] = {channelStr, nullptr};
uint32_t yo = drawRows(display, x, y + FONT_HEIGHT, fields);
display->drawString(x, y + FONT_HEIGHT, channelStr);
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
}
//adjust Brightness cycle trough 1 to 254 as long as attachDuringLongPress is true
void Screen::adjustBrightness(){
if (brightness == 254)
{
// 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 / 1.984375;
dispdev.drawRect( 0, 30, 128, 4);
dispdev.fillRect(0, 30, width, 4);
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);
}
} // namespace meshtastic
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

View File

@@ -10,13 +10,14 @@
#include <SSD1306Wire.h>
#endif
#include "PeriodicTask.h"
#include "concurrency/PeriodicTask.h"
#include "TypedQueue.h"
#include "lock.h"
#include "concurrency/LockGuard.h"
#include "power.h"
#include "commands.h"
#include <string>
namespace meshtastic
namespace graphics
{
// Forward declarations
@@ -29,40 +30,13 @@ class DebugInfo
DebugInfo(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.
void setChannelNameStatus(const char *name)
{
LockGuard guard(&lock);
concurrency::LockGuard guard(&lock);
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:
friend Screen;
@@ -71,28 +45,25 @@ class DebugInfo
/// Renders the debug screen.
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 gpsStatus;
/// Protects all of internal state.
Lock lock;
concurrency::Lock lock;
};
/// 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.
//
// This class is thread-safe (as long as drawFrame is not called multiple times
// simultaneously).
class Screen : public PeriodicTask
/**
* @brief This class deals with showing things on the screen of the device.
*
* @details Other than setup(), this class is thread-safe as long as drawFrame is not called
* multiple times simultaneously. All state-changing calls are queued and executed
* when the main loop calls us.
*/
class Screen : public concurrency::PeriodicTask
{
CallbackObserver<Screen, const meshtastic::Status *> powerStatusObserver = CallbackObserver<Screen, const meshtastic::Status *>(this, &Screen::handleStatusUpdate);
CallbackObserver<Screen, const meshtastic::Status *> gpsStatusObserver = CallbackObserver<Screen, const meshtastic::Status *>(this, &Screen::handleStatusUpdate);
CallbackObserver<Screen, const meshtastic::Status *> nodeStatusObserver = CallbackObserver<Screen, const meshtastic::Status *>(this, &Screen::handleStatusUpdate);
public:
Screen(uint8_t address, int sda = -1, int scl = -1);
@@ -111,15 +82,15 @@ class Screen : public PeriodicTask
handleSetOn(
false); // We handle off commands immediately, because they might be called because the CPU is shutting down
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.
void onPress() { enqueueCmd(CmdItem{.cmd = Cmd::ON_PRESS}); }
void onPress() { enqueueCmd(ScreenCmd{.cmd = Cmd::ON_PRESS}); }
// Implementation to Adjust Brightness
void adjustBrightness();
int brightness = 150;
uint8_t brightness = 150;
/// Starts showing the Bluetooth PIN screen.
//
@@ -127,22 +98,22 @@ class Screen : public PeriodicTask
// with the PIN.
void startBluetoothPinScreen(uint32_t pin)
{
CmdItem cmd;
ScreenCmd cmd;
cmd.cmd = Cmd::START_BLUETOOTH_PIN_SCREEN;
cmd.bluetooth_pin = pin;
enqueueCmd(cmd);
}
/// 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.
void stopBootScreen() { enqueueCmd(CmdItem{.cmd = Cmd::STOP_BOOT_SCREEN}); }
void stopBootScreen() { enqueueCmd(ScreenCmd{.cmd = Cmd::STOP_BOOT_SCREEN}); }
/// Writes a string to the screen.
void print(const char *text)
{
CmdItem cmd;
ScreenCmd cmd;
cmd.cmd = Cmd::PRINT;
// 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
@@ -187,7 +158,9 @@ class Screen : public PeriodicTask
/// Returns a handle to the DebugInfo screen.
//
// 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:
/// Updates the UI.
@@ -196,17 +169,7 @@ class Screen : public PeriodicTask
void doTask() final;
private:
enum class Cmd {
INVALID,
SET_ON,
SET_OFF,
ON_PRESS,
START_BLUETOOTH_PIN_SCREEN,
STOP_BLUETOOTH_PIN_SCREEN,
STOP_BOOT_SCREEN,
PRINT,
};
struct CmdItem {
struct ScreenCmd {
Cmd cmd;
union {
uint32_t bluetooth_pin;
@@ -215,7 +178,7 @@ class Screen : public PeriodicTask
};
/// Enques given command item to be processed by main loop().
bool enqueueCmd(const CmdItem &cmd)
bool enqueueCmd(const ScreenCmd &cmd)
{
if (!useDisplay)
return true; // claim success if our display is not in use
@@ -239,7 +202,7 @@ class Screen : public PeriodicTask
static void drawDebugInfoTrampoline(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y);
/// Queue of commands to execute in doTask.
TypedQueue<CmdItem> cmdQueue;
TypedQueue<ScreenCmd> cmdQueue;
/// Whether we are using a display
bool useDisplay = false;
/// Whether the display is currently powered
@@ -250,7 +213,9 @@ class Screen : public PeriodicTask
/// Holds state for debug information
DebugInfo debugInfo;
/// Display device
/** @todo display abstraction */
#ifdef USE_SH1106
SH1106Wire dispdev;
#else
@@ -260,4 +225,4 @@ class Screen : public PeriodicTask
OLEDDisplayUi ui;
};
} // namespace meshtastic
} // namespace graphics

17
src/graphics/configs.h Normal file
View File

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

View File

@@ -1,3 +1,5 @@
#pragma once
const uint8_t Custom_ArialMT_Plain_10[] PROGMEM = {
0x0A, // Width: 10
0x0A, // Height: 10

View File

@@ -1,3 +1,5 @@
#pragma once
#define SATELLITE_IMAGE_WIDTH 16
#define SATELLITE_IMAGE_HEIGHT 15
const uint8_t SATELLITE_IMAGE[] PROGMEM = {0x00, 0x08, 0x00, 0x1C, 0x00, 0x0E, 0x20, 0x07, 0x70, 0x02,
@@ -10,12 +12,12 @@ const uint8_t imgUser[] PROGMEM = { 0x3C, 0x42, 0x99, 0xA5, 0xA5, 0x99
const uint8_t imgPositionEmpty[] PROGMEM = { 0x20, 0x30, 0x28, 0x24, 0x42, 0xFF };
const uint8_t imgPositionSolid[] PROGMEM = { 0x20, 0x30, 0x38, 0x3C, 0x7E, 0xFF };
#include "icon.xbm"
#include "img/icon.xbm"
// We now programmatically draw our compass
#if 0
const
#include "compass.xbm"
#include "img/compass.xbm"
#endif
#if 0

View File

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

View File

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

View File

@@ -25,20 +25,21 @@
#include "MeshService.h"
#include "NEMAGPS.h"
#include "NodeDB.h"
#include "Periodic.h"
#include "PowerFSM.h"
#include "UBloxGPS.h"
#include "concurrency/Periodic.h"
#include "configuration.h"
#include "error.h"
#include "power.h"
// #include "rom/rtc.h"
#include "DSRRouter.h"
#include "debug.h"
// #include "debug.h"
#include "graphics/Screen.h"
#include "main.h"
#include "screen.h"
#include "sleep.h"
#include <Wire.h>
#include "timing.h"
#include <OneButton.h>
#include <Wire.h>
// #include <driver/rtc_io.h>
#ifndef NO_ESP32
@@ -54,10 +55,16 @@
#endif
// 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
meshtastic::PowerStatus powerStatus;
// Global power status
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 axp192_found;
@@ -121,45 +128,53 @@ static uint32_t ledBlinker()
setLed(ledOn);
// have a very sparse duty cycle of LED being on, unless charging, then blink 0.5Hz square wave rate to indicate that
return powerStatus.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;
OneButton userButton;
#endif
#ifdef BUTTON_PIN_ALT
OneButton userButtonAlt;
OneButton userButtonAlt;
#endif
void userButtonPressed() {
void userButtonPressed()
{
powerFSM.trigger(EVENT_PRESS);
}
void userButtonPressedLong(){
void userButtonPressedLong()
{
screen.adjustBrightness();
}
#ifndef NO_ESP32
void initWifi()
{
strcpy(radioConfig.preferences.wifi_ssid, "geeksville");
strcpy(radioConfig.preferences.wifi_password, "xxx");
#if 0
// strcpy(radioConfig.preferences.wifi_ssid, "xxx");
// 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));
DEBUG_MSG("STARTING WIFI AP: ssid=%s, ok=%d\n", wifiName, WiFi.softAP(wifiName, wifiPsw));
} else {
// WiFi.mode(WIFI_MODE_STA);
WiFi.mode(WIFI_MODE_STA);
DEBUG_MSG("JOINING WIFI: ssid=%s\n", wifiName);
// WiFi.begin(wifiName, wifiPsw);
if (WiFi.begin(wifiName, wifiPsw) == WL_CONNECTED) {
DEBUG_MSG("MY IP ADDRESS: %s\n", WiFi.localIP().toString().c_str());
} else {
DEBUG_MSG("FAILED JOINING WIFI\n");
}
}
}
} else
DEBUG_MSG("Not using WIFI\n");
#endif
}
#endif
@@ -225,6 +240,14 @@ void setup()
esp32Setup();
#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
nrf52Setup();
#endif
@@ -242,20 +265,27 @@ void setup()
// Init GPS - first try ublox
gps = new UBloxGPS();
if (!gps->setup()) {
DEBUG_MSG("ERROR: No UBLOX GPS found\n");
if (GPS::_serial_gps) {
// Some boards might have only the TX line from the GPS connected, in that case, we can't configure it at all. Just
// assume NEMA at 9600 baud.
DEBUG_MSG("ERROR: No UBLOX GPS found, hoping that NEMA might work\n");
DEBUG_MSG("Hoping that NEMA might work\n");
delete gps;
// dumb NEMA access only work for serial GPSes)
gps = new NEMAGPS();
gps->setup();
}
}
#else
gps = new NEMAGPS();
gps->setup();
#endif
gpsStatus->observe(&gps->newStatus);
nodeStatus->observe(&nodeDB.newStatus);
service.init();
#ifndef NO_ESP32
// Must be after we init the service, because the wifi settings are loaded by NodeDB (oops)
initWifi();
@@ -272,15 +302,15 @@ void setup()
SPI.begin();
#else
// ESP32
SPI.begin(SCK_GPIO, MISO_GPIO, MOSI_GPIO, NSS_GPIO);
SPI.begin(RF95_SCK, RF95_MISO, RF95_MOSI, RF95_NSS);
SPI.setFrequency(4000000);
#endif
// MUST BE AFTER service.init, so we have our radio config settings (from nodedb init)
RadioInterface *rIf =
#if defined(RF95_IRQ_GPIO)
#if defined(RF95_IRQ)
// new CustomRF95(); old Radiohead based driver
new RF95Interface(NSS_GPIO, RF95_IRQ_GPIO, RESET_GPIO, SPI);
new RF95Interface(RF95_NSS, RF95_IRQ, RF95_RESET, SPI);
#elif defined(SX1262_CS)
new SX1262Interface(SX1262_CS, SX1262_DIO1, SX1262_RESET, SX1262_BUSY, SPI);
#else
@@ -316,7 +346,7 @@ uint32_t axpDebugRead()
return 30 * 1000;
}
Periodic axpDebugOutput(axpDebugRead);
concurrency::Periodic axpDebugOutput(axpDebugRead);
axpDebugOutput.setup();
#endif
@@ -329,7 +359,7 @@ void loop()
powerFSM.run_machine();
service.loop();
periodicScheduler.loop();
concurrency::periodicScheduler.loop();
// axpDebugOutput.loop();
#ifdef DEBUG_PORT
@@ -341,6 +371,9 @@ void loop()
#ifndef NO_ESP32
esp32Loop();
#endif
#ifdef TBEAM_V10
power->loop();
#endif
#ifdef BUTTON_PIN
userButton.tick();
@@ -351,23 +384,22 @@ void loop()
// Show boot screen for first 3 seconds, then switch to normal operation.
static bool showingBootScreen = true;
if (showingBootScreen && (millis() > 3000)) {
if (showingBootScreen && (timing::millis() > 3000)) {
screen.stopBootScreen();
showingBootScreen = false;
}
#ifdef DEBUG_STACK
static uint32_t lastPrint = 0;
if (millis() - lastPrint > 10 * 1000L) {
lastPrint = millis();
if (timing::millis() - lastPrint > 10 * 1000L) {
lastPrint = timing::millis();
meshtastic::printThreadInfo("main");
}
#endif
// Update the screen last, after we've figured out what to show.
screen.debug()->setNodeNumbersStatus(nodeDB.getNumOnlineNodes(), nodeDB.getNumNodes());
screen.debug()->setChannelNameStatus(channelSettings.name);
screen.debug()->setPowerStatus(powerStatus);
screen.debug_info()->setChannelNameStatus(channelSettings.name);
// screen.debug()->setPowerStatus(powerStatus);
// No GPS lock yet, let the OS put the main CPU in low power mode for 100ms (or until another interrupt comes in)
// i.e. don't just keep spinning in loop as fast as we can.

View File

@@ -1,6 +1,9 @@
#pragma once
#include "screen.h"
#include "graphics/Screen.h"
#include "PowerStatus.h"
#include "GPSStatus.h"
#include "NodeStatus.h"
extern bool axp192_found;
extern bool ssd1306_found;
@@ -8,7 +11,12 @@ extern bool isCharging;
extern bool isUSBPowered;
// 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"
const char *getDeviceName();

View File

@@ -1,7 +1,7 @@
#pragma once
#include "PacketHistory.h"
#include "PeriodicTask.h"
#include "../concurrency/PeriodicTask.h"
#include "Router.h"
/**

View File

@@ -5,14 +5,15 @@
#include "GPS.h"
//#include "MeshBluetoothService.h"
#include "../concurrency/Periodic.h"
#include "BluetoothCommon.h" // needed for updateBatteryLevel, FIXME, eventually when we pull mesh out into a lib we shouldn't be whacking bluetooth from here
#include "MeshService.h"
#include "NodeDB.h"
#include "Periodic.h"
#include "PowerFSM.h"
#include "main.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.
@@ -55,7 +56,7 @@ static uint32_t sendOwnerCb()
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)
{
@@ -282,8 +283,6 @@ void MeshService::sendOurPosition(NodeNum dest, bool wantReplies)
sendToMesh(p);
}
int MeshService::onGPSChanged(void *unused)
{
// DEBUG_MSG("got gps notify\n");
@@ -303,12 +302,12 @@ int MeshService::onGPSChanged(void *unused)
}
// Include our current battery voltage in our position announcement
pos.battery_level = powerStatus.batteryChargePercent;
pos.battery_level = powerStatus->getBatteryChargePercent();
updateBatteryLevel(pos.battery_level);
// We limit our GPS broadcasts to a max rate
static uint32_t lastGpsSend;
uint32_t now = millis();
uint32_t now = timing::millis();
if (lastGpsSend == 0 || now - lastGpsSend > radioConfig.preferences.position_broadcast_secs * 1000) {
lastGpsSend = now;
DEBUG_MSG("Sending position to mesh\n");

View File

@@ -339,12 +339,8 @@ void NodeDB::updateFrom(const MeshPacket &mp)
const SubPacket &p = mp.decoded;
DEBUG_MSG("Update DB node 0x%x, rx_time=%u\n", mp.from, mp.rx_time);
int oldNumNodes = *numNodes;
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
info->has_position = true; // at least the time is valid
info->position.time = mp.rx_time;
@@ -360,6 +356,7 @@ void NodeDB::updateFrom(const MeshPacket &mp)
info->position.time = oldtime;
info->has_position = true;
updateGUIforNode = info;
notifyObservers(true); //Force an update whether or not our node counts have changed
break;
}
@@ -374,6 +371,7 @@ void NodeDB::updateFrom(const MeshPacket &mp)
devicestate.has_rx_text_message = true;
updateTextMessage = true;
powerFSM.trigger(EVENT_RECEIVED_TEXT_MSG);
notifyObservers(true); //Force an update whether or not our node counts have changed
}
}
break;
@@ -392,6 +390,7 @@ void NodeDB::updateFrom(const MeshPacket &mp)
if (changed) {
updateGUIforNode = info;
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
// We just changed something important about the user, store our DB
@@ -399,6 +398,10 @@ void NodeDB::updateFrom(const MeshPacket &mp)
}
break;
}
default: {
notifyObservers(); //If the node counts have changed, notify observers
}
}
}
}

View File

@@ -2,9 +2,11 @@
#include <Arduino.h>
#include <assert.h>
#include "Observer.h"
#include "MeshTypes.h"
#include "mesh-pb-constants.h"
#include "NodeStatus.h"
extern DeviceState devicestate;
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
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
Observable<const meshtastic::NodeStatus *> newStatus;
/// 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
@@ -91,6 +94,13 @@ class NodeDB
/// Find a node in our DB, create an empty NodeInfo if missing
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
void loadFromDisk();

View File

@@ -1,6 +1,7 @@
#include "PacketHistory.h"
#include "configuration.h"
#include "mesh-pb-constants.h"
#include "../timing.h"
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
}
uint32_t now = millis();
uint32_t now = timing::millis();
for (size_t i = 0; i < recentPackets.size();) {
PacketRecord &r = recentPackets[i];

View File

@@ -4,6 +4,7 @@
#include "PowerFSM.h"
#include "RadioInterface.h"
#include "GPS.h"
#include "timing.h"
#include <assert.h>
PhoneAPI::PhoneAPI()
@@ -20,7 +21,7 @@ void PhoneAPI::init()
void PhoneAPI::checkConnectionTimeout()
{
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) {
isConnected = false;
onConnectionChanged(isConnected);
@@ -34,7 +35,7 @@ void PhoneAPI::checkConnectionTimeout()
void PhoneAPI::handleToRadio(const uint8_t *buf, size_t bufLength)
{
powerFSM.trigger(EVENT_CONTACT_FROM_PHONE); // As long as the phone keeps talking to us, don't let the radio go to sleep
lastContactMsec = millis();
lastContactMsec = timing::millis();
if (!isConnected) {
isConnected = true;
onConnectionChanged(isConnected);

View File

@@ -12,6 +12,18 @@ RF95Interface::RF95Interface(RADIOLIB_PIN_TYPE cs, RADIOLIB_PIN_TYPE irq, RADIOL
// FIXME - we assume devices never get destroyed
}
/** Some boards require GPIO control of tx vs rx paths */
void RF95Interface::setTransmitEnable(bool txon)
{
#ifdef RF95_TXEN
digitalWrite(RF95_TXEN, txon ? 1 : 0);
#endif
#ifdef RF95_RXEN
digitalWrite(RF95_RXEN, txon ? 0 : 1);
#endif
}
/// Initialise the Driver transport hardware and software.
/// Make sure the Driver is properly configured before calling init().
/// \return true if initialisation succeeded.
@@ -24,8 +36,30 @@ bool RF95Interface::init()
power = MAX_POWER;
iface = lora = new RadioLibRF95(&module);
#ifdef RF95_TCXO
pinMode(RF95_TCXO, OUTPUT);
digitalWrite(RF95_TCXO, 1);
#endif
/*
#define RF95_TXEN (22) // If defined, this pin should be set high prior to transmit (controls an external analog switch)
#define RF95_RXEN (23) // If defined, this pin should be set high prior to receive (controls an external analog switch)
*/
#ifdef RF95_TXEN
pinMode(RF95_TXEN, OUTPUT);
digitalWrite(RF95_TXEN, 0);
#endif
#ifdef RF95_RXEN
pinMode(RF95_RXEN, OUTPUT);
digitalWrite(RF95_RXEN, 1);
#endif
setTransmitEnable(false);
int res = lora->begin(freq, bw, sf, cr, syncWord, power, currentLimit, preambleLength);
DEBUG_MSG("LORA init result %d\n", res);
DEBUG_MSG("RF95 init result %d\n", res);
if (res == ERR_NONE)
res = lora->setCRC(SX126X_LORA_CRC_ON);
@@ -98,8 +132,18 @@ void RF95Interface::setStandby()
completeSending(); // If we were sending, not anymore
}
/** We override to turn on transmitter power as needed.
*/
void RF95Interface::configHardwareForSend()
{
setTransmitEnable(true);
RadioLibInterface::configHardwareForSend();
}
void RF95Interface::startReceive()
{
setTransmitEnable(false);
setStandby();
int err = lora->startReceive();
assert(err == ERR_NONE);

View File

@@ -52,4 +52,13 @@ class RF95Interface : public RadioLibInterface
virtual void addReceiveMetadata(MeshPacket *mp);
virtual void setStandby();
/**
* We override to turn on transmitter power as needed.
*/
virtual void configHardwareForSend();
private:
/** Some boards require GPIO control of tx vs rx paths */
void setTransmitEnable(bool txon);
};

View File

@@ -6,6 +6,7 @@
#include "assert.h"
#include "configuration.h"
#include "sleep.h"
#include "timing.h"
#include <assert.h>
#include <pb_decode.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());
assert(p->which_payload == MeshPacket_encrypted_tag); // It should have already been encoded by now
lastTxStart = millis();
lastTxStart = timing::millis();
PacketHeader *h = (PacketHeader *)radiobuf;

View File

@@ -4,7 +4,7 @@
#include "MeshTypes.h"
#include "Observer.h"
#include "PointerQueue.h"
#include "WorkerThread.h"
#include "../concurrency/NotifiedWorkerThread.h"
#include "mesh.pb.h"
#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)
*/
class RadioInterface : protected NotifiedWorkerThread
class RadioInterface : protected concurrency::NotifiedWorkerThread
{
friend class MeshRadio; // for debugging we let that class touch pool
PointerQueue<MeshPacket> *rxDest = NULL;
@@ -161,6 +161,11 @@ class SimRadio : public RadioInterface
/// Make sure the Driver is properly configured before calling init().
/// \return true if initialisation succeeded.
virtual bool init() { return true; }
/// Apply any radio provisioning changes
/// Make sure the Driver is properly configured before calling init().
/// \return true if initialisation succeeded.
virtual bool reconfigure() { return true; }
};
/// Debug printing for packets

View File

@@ -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,
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
instance = this;

View File

@@ -1,6 +1,6 @@
#pragma once
#include "PeriodicTask.h"
#include "../concurrency/PeriodicTask.h"
#include "RadioInterface.h"
#ifdef CubeCell_BoardPlus
@@ -16,7 +16,7 @@
#define INTERRUPT_ATTR
#endif
class RadioLibInterface : public RadioInterface, private PeriodicTask
class RadioLibInterface : public RadioInterface, private concurrency::PeriodicTask
{
/// Used as our notification from the ISR
enum PendingISR { ISR_NONE = 0, ISR_RX, ISR_TX, TRANSMIT_DELAY_COMPLETED };

View File

@@ -3,6 +3,10 @@
#define RF95_CHIP_VERSION 0x12
#define RF95_ALT_VERSION 0x11 // Supposedly some versions of the chip have id 0x11
// From datasheet but radiolib doesn't know anything about this
#define SX127X_REG_TCXO 0x4B
RadioLibRF95::RadioLibRF95(Module *mod) : SX1278(mod) {}
int16_t RadioLibRF95::begin(float freq, float bw, uint8_t sf, uint8_t cr, uint8_t syncWord, int8_t power, uint8_t currentLimit,
@@ -18,6 +22,11 @@ int16_t RadioLibRF95::begin(float freq, float bw, uint8_t sf, uint8_t cr, uint8_
state = config();
RADIOLIB_ASSERT(state);
#ifdef RF95_TCXO
state = _mod->SPIsetRegValue(SX127X_REG_TCXO, 0x10 | _mod->SPIgetRegValue(SX127X_REG_TCXO));
RADIOLIB_ASSERT(state);
#endif
// configure publicly accessible settings
state = setFrequency(freq);
RADIOLIB_ASSERT(state);

View File

@@ -2,6 +2,7 @@
#include "MeshTypes.h"
#include "configuration.h"
#include "mesh-pb-constants.h"
#include "timing.h"
// ReliableRouter::ReliableRouter() {}
@@ -162,7 +163,7 @@ PendingPacket *ReliableRouter::startRetransmission(MeshPacket *p)
*/
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.
// for(auto el: pending) {

View File

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

View File

@@ -24,8 +24,8 @@
(MAX_RX_TOPHONE + MAX_RX_FROMRADIO + 2 * MAX_TX_QUEUE + \
2) // max number of packets which can be in flight (either queued from reception or queued for sending)
static MemoryPool<MeshPacket> staticPool(MAX_PACKETS);
// static MemoryDynamic<MeshPacket> staticPool;
// static MemoryPool<MeshPacket> staticPool(MAX_PACKETS);
static MemoryDynamic<MeshPacket> staticPool;
Allocator<MeshPacket> &packetPool = staticPool;
@@ -36,7 +36,7 @@ Allocator<MeshPacket> &packetPool = staticPool;
*/
Router::Router() : fromRadioQueue(MAX_RX_FROMRADIO)
{
// This is called pre main(), don't touch anything here, the following code is not safe
// This is called pre main(), don't touch anything here, the following code is not safe
/* DEBUG_MSG("Size of NodeInfo %d\n", sizeof(NodeInfo));
DEBUG_MSG("Size of SubPacket %d\n", sizeof(SubPacket));

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