mirror of
https://github.com/meshtastic/firmware.git
synced 2025-12-14 14:52:32 +00:00
Compare commits
163 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
13806cce93 | ||
|
|
e7eee0995a | ||
|
|
5687bd09c6 | ||
|
|
f3db895832 | ||
|
|
dd2ffe5d14 | ||
|
|
ec10e784e1 | ||
|
|
649faa1d93 | ||
|
|
669f96b367 | ||
|
|
996821d18e | ||
|
|
2e172b019e | ||
|
|
3df05cd5c1 | ||
|
|
8bb85cdc69 | ||
|
|
9f6e23754c | ||
|
|
5a7cfdffb3 | ||
|
|
caafddfdfa | ||
|
|
ee0e31be97 | ||
|
|
f1da6469a3 | ||
|
|
68e57dd3a7 | ||
|
|
2504311671 | ||
|
|
aa29315624 | ||
|
|
e3bcb87cf0 | ||
|
|
2530dc44c7 | ||
|
|
70a8fe30b7 | ||
|
|
30e538e5ed | ||
|
|
58dbc3c702 | ||
|
|
210c904604 | ||
|
|
27fdab7c8d | ||
|
|
ba3e1abb5e | ||
|
|
ce194e2162 | ||
|
|
595166db8e | ||
|
|
38d9e34a66 | ||
|
|
c1e0977db3 | ||
|
|
b4f32e7645 | ||
|
|
6bb0c95c95 | ||
|
|
4e958c9230 | ||
|
|
64cf1890f2 | ||
|
|
6a857b00db | ||
|
|
6d60a061bc | ||
|
|
6a09ddef18 | ||
|
|
d48e803b7b | ||
|
|
9061b3d8c3 | ||
|
|
2839539c6c | ||
|
|
f77a1798f3 | ||
|
|
664b558a36 | ||
|
|
c1865f127c | ||
|
|
3a69539192 | ||
|
|
cfcaf28ace | ||
|
|
d9e93f3944 | ||
|
|
60470211e5 | ||
|
|
c5851a4a0c | ||
|
|
0c0b2446b7 | ||
|
|
ce9352fd23 | ||
|
|
375ae5fe77 | ||
|
|
665d35196d | ||
|
|
9757f9ae53 | ||
|
|
e45d0c4dcf | ||
|
|
5c9f22bc18 | ||
|
|
d8287e9cdb | ||
|
|
c66e064f42 | ||
|
|
364fc84aaa | ||
|
|
fe4f86bc84 | ||
|
|
982b2e33ff | ||
|
|
8190098bb8 | ||
|
|
c99411311b | ||
|
|
da8a048dce | ||
|
|
b4de495154 | ||
|
|
aaa6af3f38 | ||
|
|
b11b322581 | ||
|
|
60b38215ce | ||
|
|
53e68515dd | ||
|
|
7676ae9184 | ||
|
|
b385feefa5 | ||
|
|
fc50b4abc0 | ||
|
|
0104a2eb3a | ||
|
|
23fdc9d1a9 | ||
|
|
ea4a678308 | ||
|
|
6566c4f15e | ||
|
|
6f39f1740f | ||
|
|
7b61bdbfd2 | ||
|
|
8e65db19b0 | ||
|
|
698fcdad36 | ||
|
|
c25d6e974f | ||
|
|
35aae48932 | ||
|
|
f8afa2703f | ||
|
|
cd903dceb9 | ||
|
|
5843c6f25e | ||
|
|
3005373760 | ||
|
|
7678540a93 | ||
|
|
463666aec5 | ||
|
|
5847f94ece | ||
|
|
09855b76ef | ||
|
|
f4d476d160 | ||
|
|
d88d4be63e | ||
|
|
154114e900 | ||
|
|
03f019dea2 | ||
|
|
a47524b762 | ||
|
|
fd2f5a5ba4 | ||
|
|
e66b2234f6 | ||
|
|
f35b15b09c | ||
|
|
99dac51b9d | ||
|
|
656faf4ce9 | ||
|
|
d9ab6986ad | ||
|
|
514d45c219 | ||
|
|
94b7fc76c1 | ||
|
|
e9923ac257 | ||
|
|
60ad1793e4 | ||
|
|
96a15bec3b | ||
|
|
fba676fc07 | ||
|
|
9ad14ad98b | ||
|
|
477c62082d | ||
|
|
82169d4115 | ||
|
|
362d5452d5 | ||
|
|
92e5a15399 | ||
|
|
114e0c4ada | ||
|
|
075ff26499 | ||
|
|
d65f2ede81 | ||
|
|
4a9248a451 | ||
|
|
1c6092c430 | ||
|
|
0e9ed1e82f | ||
|
|
02ce0558b9 | ||
|
|
63110f0134 | ||
|
|
d8db4449be | ||
|
|
fda98bbf58 | ||
|
|
aadba1f694 | ||
|
|
2c8d152885 | ||
|
|
37c598833c | ||
|
|
e80de4eba0 | ||
|
|
e382a181f7 | ||
|
|
85f69cb908 | ||
|
|
112a94e572 | ||
|
|
13307c502f | ||
|
|
8a1754efe8 | ||
|
|
8caa075bc6 | ||
|
|
d5deb49d20 | ||
|
|
575a15e135 | ||
|
|
dc7469c64b | ||
|
|
f54b18f733 | ||
|
|
db66e4dc00 | ||
|
|
47e614c7d6 | ||
|
|
03cb3c2145 | ||
|
|
a8d4b5479d | ||
|
|
de37e1bbab | ||
|
|
f0b8f10665 | ||
|
|
88b91de197 | ||
|
|
f22bb34c07 | ||
|
|
2d1cb7a43a | ||
|
|
dc169675e2 | ||
|
|
99f8253637 | ||
|
|
f3244caac1 | ||
|
|
6edaadf5d8 | ||
|
|
1f668046a0 | ||
|
|
cb2aa3b29f | ||
|
|
8ccd59a7d8 | ||
|
|
ddfdae64bf | ||
|
|
21a90a42e5 | ||
|
|
712d6e5c1e | ||
|
|
f02a2c0853 | ||
|
|
00d55c9daa | ||
|
|
a05e45f84b | ||
|
|
71a4cfefd5 | ||
|
|
1c63a70673 | ||
|
|
8022f3b920 | ||
|
|
4db176867b |
3
.vscode/settings.json
vendored
3
.vscode/settings.json
vendored
@@ -59,6 +59,7 @@
|
||||
"cfsr",
|
||||
"descs",
|
||||
"ocrypto",
|
||||
"protobufs"
|
||||
"protobufs",
|
||||
"wifi"
|
||||
]
|
||||
}
|
||||
11
README.md
11
README.md
@@ -24,14 +24,17 @@ We currently support three models of radios.
|
||||
|
||||
- TTGO T-Beam
|
||||
|
||||
- [T-Beam V1.0 w/ NEO-M8N](https://www.aliexpress.com/item/33047631119.html) (Recommended)
|
||||
- [T-Beam V1.0 w/ NEO-6M](https://www.aliexpress.com/item/33050391850.html)
|
||||
- [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)
|
||||
- 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
|
||||
|
||||
- 3D printable case
|
||||
- [TTGO LORA32 v1](https://www.thingiverse.com/thing:3385109)
|
||||
|
||||
- [Heltec LoRa 32](https://heltec.org/project/wifi-lora-32/) - No GPS
|
||||
- [3D Printable case](https://www.thingiverse.com/thing:3125854)
|
||||
|
||||
@@ -47,6 +50,8 @@ Getting a version that includes a screen is optional, but highly recommended.
|
||||
|
||||
Prebuilt binaries for the supported radios are available in our [releases](https://github.com/meshtastic/Meshtastic-esp32/releases). Your initial installation has to happen over USB from your Mac, Windows or Linux PC. Once our software is installed, all future software updates happen over bluetooth from your phone.
|
||||
|
||||
Be **very careful** to install the correct load for your board. In particular the popular 'T-BEAM' radio from TTGO is not called 'TTGO-Lora' (that is a different board). So don't install the 'TTGO-Lora' build on a TBEAM, it won't work correctly.
|
||||
|
||||
Please post comments on our [group chat](https://meshtastic.discourse.group/) if you have problems or successes.
|
||||
|
||||
### Installing from a GUI - Windows and Mac
|
||||
@@ -168,7 +173,7 @@ Hard resetting via RTS pin...
|
||||
|
||||
# Meshtastic Android app
|
||||
|
||||
The companion (optional) Meshtastic Android app is [here](https://github.com/meshtastic/Meshtastic-Android). You can also download it on Google Play.
|
||||
The companion (optional) Meshtastic Android app is [here](https://play.google.com/store/apps/details?id=com.geeksville.mesh&referrer=utm_source%3Dgithub-dev-readme). You can also download it on Google Play.
|
||||
|
||||
# Python API
|
||||
|
||||
|
||||
3
bin/dump-ram-users.sh
Executable file
3
bin/dump-ram-users.sh
Executable file
@@ -0,0 +1,3 @@
|
||||
arm-none-eabi-readelf -s -e .pio/build/nrf52dk/firmware.elf | head -80
|
||||
|
||||
nm -CSr --size-sort .pio/build/nrf52dk/firmware.elf | grep '^200'
|
||||
@@ -1,3 +1,3 @@
|
||||
|
||||
|
||||
export VERSION=0.7.4
|
||||
export VERSION=0.7.9
|
||||
@@ -28,7 +28,7 @@ Not all of these features are fully implemented yet - see **important** disclaim
|
||||
- Shows direction and distance to all members of your channel
|
||||
- Directed or broadcast text messages for channel members
|
||||
- Open and extensible codebase supporting multiple hardware vendors - no lock in to one vendor
|
||||
- Communication API for bluetooth devices (such as our Android app) to use the mesh. An iOS application is in the works. And [Meshtastic-python](https://pypi.org/project/meshtastic/) provides access from desktop computers.
|
||||
- Communication API for bluetooth devices (such as our Android app) to use the mesh. An iOS application is in the works. And [Meshtastic-python](https://pypi.org/project/meshtastic/) provides access from desktop computers.
|
||||
- Very easy sharing of private secured channels. Just share a special link or QR code with friends and they can join your encrypted mesh
|
||||
|
||||
This project is currently in beta testing but it is fairly stable and feature complete - if you have questions please [join our discussion forum](https://meshtastic.discourse.group/).
|
||||
@@ -39,6 +39,7 @@ This software is 100% open source and developed by a group of hobbyist experimen
|
||||
|
||||
Note: Updates are happening almost daily, only major updates are listed below. For more details see our forum.
|
||||
|
||||
- 06/24/2020 - 0.7.x Now with over 1000 android users, over 600 people using the radios and translated into 13 languages. Fairly stable and we are working through bugs to get to 1.0.
|
||||
- 06/04/2020 - 0.6.7 Beta releases of both the application and the device code are released. Features are fairly solid now with a sizable number of users.
|
||||
- 04/28/2020 - 0.6.0 [Python API](https://pypi.org/project/meshtastic/) released. Makes it easy to use meshtastic devices as "zero config / just works" mesh transport adapters for other projects.
|
||||
- 04/20/2020 - 0.4.3 Pretty solid now both for the android app and the device code. Many people have donated translations and code. Probably going to call it a beta soon.
|
||||
@@ -51,7 +52,7 @@ Note: Updates are happening almost daily, only major updates are listed below. F
|
||||
|
||||
Our Android application is available here:
|
||||
|
||||
[](https://play.google.com/store/apps/details?id=com.geeksville.mesh&referrer=utm_source%3Dhomepage%26anid%3Dadmob)
|
||||
[](https://play.google.com/store/apps/details?id=com.geeksville.mesh&referrer=utm_source%3Dgithub-homepage)
|
||||
|
||||
The link above will return older more stable releases. We would prefer if you join our alpha-test group, because the application is rapidly improving. Three steps to opt-in to the alpha- test:
|
||||
|
||||
@@ -63,8 +64,9 @@ If you'd like to help with development, the source code is [on github](https://g
|
||||
|
||||
## Supported hardware
|
||||
|
||||
We currently support two brands of radios. The [TTGO T-Beam](https://www.aliexpress.com/item/4000119152086.html) and the [Heltec LoRa 32](https://heltec.org/project/wifi-lora-32/). Most people should buy the T-Beam and a 18650 battery (total cost less than \$35). Make
|
||||
sure to buy the frequency range which is legal for your country. For the USA, you should buy the 915MHz version. Getting a version that include a screen is optional, but highly recommended.
|
||||
We currently support two brands of radios. The [TTGO T-Beam](https://www.aliexpress.com/item/4001178678568.html) and the [Heltec LoRa 32](https://heltec.org/project/wifi-lora-32/). Most people should buy the T-Beam and a 18650 battery (total cost less than \$35). Also, the version of the T-Beam we link to is shipped with Meshtastic **preinstalled** by TTGO, so you don't have to install it yourself.
|
||||
|
||||
Make sure to buy the frequency range which is legal for your country. For the USA, you should buy the 915MHz version. Getting a version that include a screen is optional, but highly recommended.
|
||||
|
||||
Instructions for installing prebuilt firmware can be found [here](https://github.com/meshtastic/Meshtastic-esp32/blob/master/README.md).
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ This project is still pretty young but moving at a pretty good pace. Not all fea
|
||||
Most of these problems should be solved by the beta release (within three months):
|
||||
|
||||
- We don't make these devices and they haven't been tested by UL or the FCC. If you use them you are experimenting and we can't promise they won't burn your house down ;-)
|
||||
- The encryption [implementation](software/crypto.md) has not been reviewed by an expert. (Are you an expert? Please help us)
|
||||
- The encryption implementation is good but see this list of [caveats](software/crypto.md#summary-of-strengthsweaknesses-of-our-current-implementation) to determine risks you might face.
|
||||
- A number of (straightforward) software work items have to be completed before battery life matches our measurements, currently battery life is about three days. Join us on chat if you want the spreadsheet of power measurements/calculations.
|
||||
- The Android API needs to be documented better
|
||||
- No one has written an iOS app yet. But some good souls [are talking about it](https://github.com/meshtastic/Meshtastic-esp32/issues/14) ;-)
|
||||
|
||||
6
docs/hardware/cubecell-TODO.md
Normal file
6
docs/hardware/cubecell-TODO.md
Normal file
@@ -0,0 +1,6 @@
|
||||
|
||||
https://heltec-automation-docs.readthedocs.io/en/latest/cubecell/index.html
|
||||
|
||||
https://github.com/HelTecAutomation/ASR650x-Arduino?utm_source=platformio.org&utm_medium=docs
|
||||
|
||||
* Either portfreertos or make not theaded versions of Lock, WorkerThread, Queue (probably the latter).
|
||||
@@ -1,205 +1,77 @@
|
||||
# High priority
|
||||
# Geeksville's current work queue
|
||||
|
||||
Items to complete soon (next couple of alpha releases).
|
||||
You probably don't care about this section - skip to the next one.
|
||||
|
||||
- lower wait_bluetooth_secs to 30 seconds once we have the GPS power on (but GPS in sleep mode) across light sleep. For the time
|
||||
being I have it set at 2 minutes to ensure enough time for a GPS lock from scratch.
|
||||
- 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
|
||||
|
||||
# Medium priority
|
||||
|
||||
Items to complete before the first beta release.
|
||||
Items to complete before 1.0.
|
||||
|
||||
- Use 32 bits for message IDs
|
||||
- Use fixed32 for node IDs
|
||||
- Remove the "want node" node number arbitration process
|
||||
# Post 1.0 ideas
|
||||
|
||||
- finish DSR for unicast
|
||||
- check fcc rules on duty cycle. we might not need to freq hop. https://www.sunfiretesting.com/LoRa-FCC-Certification-Guide/ . Might need to add enforcement for europe though.
|
||||
- make a no bluetooth configured yet screen - include this screen in the loop if the user hasn't yet paired
|
||||
- if radio params change fundamentally, discard the nodedb
|
||||
- re-enable the bluetooth battery level service on the T-BEAM
|
||||
- provide generalized (but slow) internet message forwarding service if one of our nodes has internet connectivity (MQTT) [ Not a requirement but a personal interest ]
|
||||
|
||||
# Low priority ideas
|
||||
|
||||
Items after the first final candidate release.
|
||||
|
||||
- dynamic frequency scaling could save a lot of power on ESP32, but it seems to corrupt uart (even with ref_tick set correctly)
|
||||
- Change back to using a fixed sized MemoryPool rather than MemoryDynamic (see bug #149)
|
||||
- scan to find channels with low background noise? (Use CAD mode of the RF95 to automatically find low noise channels)
|
||||
- If the phone doesn't read fromradio mailbox within X seconds, assume the phone is gone and we can stop queing location msgs
|
||||
for it (because it will redownload the nodedb when it comes back)
|
||||
- add frequency hopping, dependent on the gps time, make the switch moment far from the time anyone is going to be transmitting
|
||||
- assign every "channel" a random shared 8 bit sync word (per 4.2.13.6 of datasheet) - use that word to filter packets before even checking CRC. This will ensure our CPU will only wake for packets on our "channel"
|
||||
- the BLE stack is leaking about 200 bytes each time we go to light sleep
|
||||
- use fuse bits to store the board type and region. So one load can be used on all boards
|
||||
- Don't store position packets in the to phone fifo if we are disconnected. The phone will get that info for 'free' when it
|
||||
fetches the fresh nodedb.
|
||||
- Use the RFM95 sequencer to stay in idle mode most of the time, then automatically go to receive mode and automatically go from transmit to receive mode. See 4.2.8.2 of manual.
|
||||
- possibly switch to https://github.com/SlashDevin/NeoGPS for gps comms
|
||||
- good source of battery/signal/gps icons https://materialdesignicons.com/
|
||||
- research and implement better mesh algorithm - investigate changing routing to https://github.com/sudomesh/LoRaLayer2 ?
|
||||
- check fcc rules on duty cycle. we might not need to freq hop. https://www.sunfiretesting.com/LoRa-FCC-Certification-Guide/
|
||||
- use fuse bits to store the board type and region. So one load can be used on all boards
|
||||
- the BLE stack is leaking about 200 bytes each time we go to light sleep
|
||||
- rx signal measurements -3 marginal, -9 bad, 10 great, -10 means almost unusable. So scale this into % signal strength. preferably as a graph, with an X indicating loss of comms.
|
||||
- assign every "channel" a random shared 8 bit sync word (per 4.2.13.6 of datasheet) - use that word to filter packets before even checking CRC. This will ensure our CPU will only wake for packets on our "channel"
|
||||
- Note: we do not do address filtering at the chip level, because we might need to route for the mesh
|
||||
is in cleartext (so that nodes will route for other radios that are cryptoed with a key we don't know)
|
||||
- add frequency hopping, dependent on the gps time, make the switch moment far from the time anyone is going to be transmitting
|
||||
- share channel settings over Signal (or qr code) by embedding an an URL which is handled by the MeshUtil app.
|
||||
- publish update articles on the web
|
||||
- Use fixed32 for node IDs, packetIDs, successid, failid, and lat/lon - will require all nodes to be updated, but make messages slightly smaller.
|
||||
- add "store and forward" support for messages, or move to the DB sync model. This would allow messages to be eventually delivered even if nodes are out of contact at the moment.
|
||||
- use variable length Strings in protobufs (instead of current fixed buffers). This would save lots of RAM
|
||||
- use BLEDevice::setPower to lower our BLE transmit power - extra range doesn't help us, it costs amps and it increases snoopability
|
||||
- make a HAM build: just a new frequency list, a bool to say 'never do encryption' and use hte callsign as that node's unique id. -from Girts
|
||||
- don't forward redundant pings or ping responses to the phone, it just wastes phone battery
|
||||
- don't send location packets if we haven't moved significantly
|
||||
- scrub default radio config settings for bandwidth/range/speed
|
||||
- show radio and gps signal strength as an image
|
||||
- only BLE advertise for a short time after the screen is on and button pressed - to save power and prevent people for sniffing for our BT app.
|
||||
- make mesh aware network timing state machine (sync wake windows to gps time) - this can save LOTS of battery
|
||||
- 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)
|
||||
|
||||
# Pre-beta priority
|
||||
Changes related to wifi support on ESP32:
|
||||
|
||||
During the beta timeframe the following improvements 'would be nice' (and yeah - I guess some of these items count as features, but it is a hobby project ;-) )
|
||||
|
||||
- If the phone doesn't read fromradio mailbox within X seconds, assume the phone is gone and we can stop queing location msgs
|
||||
for it (because it will redownload the nodedb when it comes back)
|
||||
- Figure out why the RF95 ISR is never seeing RH_RF95_VALID_HEADER, so it is not protecting our rx packets from getting stomped on by sends
|
||||
- fix the frequency error reading in the RF95 RX code (can't do floating point math in an ISR ;-)
|
||||
- See CustomRF95::send and fix the problem of dropping partially received packets if we want to start sending
|
||||
- make sure main cpu is not woken for packets with bad crc or not addressed to this node - do that in the radio hw
|
||||
- triple check fcc compliance
|
||||
- pick channel center frequency based on channel name? "dolphin" would hash to 900Mhz, "cat" to 905MHz etc? allows us to hide the concept of channel # from hte user.
|
||||
- scan to find channels with low background noise? (Use CAD mode of the RF95 to automatically find low noise channels)
|
||||
- make a no bluetooth configured yet screen - include this screen in the loop if the user hasn't yet paired
|
||||
- if radio params change fundamentally, discard the nodedb
|
||||
- reneable the bluetooth battery level service on the T-BEAM, because we can read battery level there
|
||||
- iram space: https://esp32.com/viewtopic.php?t=8460
|
||||
- set https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-guides/external-ram.html spi ram bss
|
||||
- figure out if iram or bluetooth classic caused ble problems
|
||||
- post bug on esp32-arduino with BLE bug findings
|
||||
|
||||
# Spinoff project ideas
|
||||
|
||||
- an open source version of https://www.burnair.ch/skynet/
|
||||
- a paragliding app like http://airwhere.co.uk/
|
||||
- a version with a solar cell for power, just mounted high to permanently provide routing for nodes in a valley. Someone just pointed me at disaster.radio
|
||||
- How do avalanche beacons work? Could this do that as well? possibly by using beacon mode feature of the RF95?
|
||||
- provide generalized (but slow) internet message forwarding servie if one of our nodes has internet connectivity
|
||||
|
||||
# Low priority
|
||||
|
||||
Items after the first final candidate release.
|
||||
|
||||
- use variable length arduino Strings in protobufs (instead of current fixed buffers)
|
||||
- use BLEDevice::setPower to lower our BLE transmit power - extra range doesn't help us, it costs amps and it increases snoopability
|
||||
- make an install script to let novices install software on their boards
|
||||
- use std::map<NodeInfo\*, std::string> in node db
|
||||
- make a HAM build: yep - that's a great idea. I'll add it to the TODO. should be pretty painless - just a new frequency list, a bool to say 'never do encryption' and use hte callsign as that node's unique id. -from Girts
|
||||
- don't forward redundant pings or ping responses to the phone, it just wastes phone battery
|
||||
- use https://platformio.org/lib/show/1260/OneButton if necessary
|
||||
- don't send location packets if we haven't moved
|
||||
- scrub default radio config settings for bandwidth/range/speed
|
||||
- answer to pings (because some other user is looking at our nodeinfo) with our latest location (not a stale location)
|
||||
- show radio and gps signal strength as an image
|
||||
- only BLE advertise for a short time after the screen is on and button pressed - to save power and prevent people for sniffing for our BT app.
|
||||
- make mesh aware network timing state machine (sync wake windows to gps time)
|
||||
- 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
|
||||
- the AXP debug output says it is trying to charge at 700mA, but the max I've seen is 180mA, so AXP registers probably need to be set to tell them the circuit can only provide 300mAish max. So that the low charge rate kicks in faster and we don't wear out batteries.
|
||||
- increase the max charging rate a bit for 18650s, currently it limits to 180mA (at 4V). Work backwards from the 500mA USB limit (at 5V) and let the AXP charge at that rate.
|
||||
- discard very old nodedb records (> 1wk)
|
||||
- using the genpartitions based table doesn't work on TTGO so for now I stay with my old memory map
|
||||
- We let anyone BLE scan for us (FIXME, perhaps only allow that until we are paired with a phone and configured)
|
||||
- use two different buildenv flags for ttgo vs lora32. https://docs.platformio.org/en/latest/ide/vscode.html#key-bindings
|
||||
- sim gps data for testing nodes that don't have hardware
|
||||
- do debug serial logging to android over bluetooth
|
||||
- break out my bluetooth OTA software as a seperate library so others can use it
|
||||
- Heltec LoRa32 has 8MB flash, use a bigger partition table if needed - TTGO is 4MB but has PSRAM
|
||||
- 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
|
||||
|
||||
# Done
|
||||
|
||||
- change the partition table to take advantage of the 4MB flash on the wroom: http://docs.platformio.org/en/latest/platforms/espressif32.html#partition-tables
|
||||
- wrap in nice MeshRadio class
|
||||
- add mesh send & rx
|
||||
- make message send from android go to service, then to mesh radio
|
||||
- make message receive from radio go through to android
|
||||
- test loopback tx/rx path code without using radio
|
||||
- notify phone when rx packets arrive, currently the phone polls at startup only
|
||||
- figure out if we can use PA_BOOST - yes, it seems to be on both boards
|
||||
- implement new ble characteristics
|
||||
- have MeshService keep a node DB by sniffing user messages
|
||||
- have a state machine return the correct FromRadio packet to the phone, it isn't always going to be a MeshPacket. Do a notify on fromnum to force the radio to read our state machine generated packets
|
||||
- send my_node_num when phone sends WantsNodes
|
||||
- have meshservice periodically send location data on mesh (if device has a GPS)
|
||||
- implement getCurrentTime() - set based off gps but then updated locally
|
||||
- make default owner record have valid usernames
|
||||
- message loop between node 0x28 and 0x7c
|
||||
- check in my radiolib fixes
|
||||
- figure out what is busted with rx
|
||||
- send our owner info at boot, reply if we see anyone send theirs
|
||||
- add manager layers
|
||||
- confirm second device receives that gps message and updates device db
|
||||
- send correct hw vendor in the bluetooth info - needed so the android app can update different radio models
|
||||
- correctly map nodeids to nodenums, currently we just do a proof of concept by always doing a broadcast
|
||||
- add interrupt detach/sleep mode config to lora radio so we can enable deepsleep without panicing
|
||||
- make jtag work on second board
|
||||
- implement regen owner and radio prefs
|
||||
- use a better font
|
||||
- make nice screens (boot, about to sleep, debug info (gps signal, #people), latest text, person info - one frame per person on network)
|
||||
- turn framerate from ui->state.frameState to 1 fps (or less) unless in transition
|
||||
- switch to my gui layout manager
|
||||
- make basic gui. different screens: debug, one page for each user in the user db, last received text message
|
||||
- make button press cycle between screens
|
||||
- save our node db on entry to sleep
|
||||
- fix the logo
|
||||
- sent/received packets (especially if a node was just reset) have variant of zero sometimes - I think there is a bug (race-condtion?) in the radio send/rx path.
|
||||
- DONE dynamic nodenum assignment tasks
|
||||
- make jtag debugger id stable: https://askubuntu.com/questions/49910/how-to-distinguish-between-identical-usb-to-serial-adapters
|
||||
- reported altitude is crap
|
||||
- good tips on which bands might be more free https://github.com/TheThingsNetwork/ttn/issues/119
|
||||
- finish power measurements (GPS on during sleep vs LCD on during sleep vs LORA on during sleep) and est battery life
|
||||
- make screen sleep behavior work
|
||||
- make screen advance only when a new node update arrives, a new text arrives or the user presses a button, turn off screen after a while
|
||||
- after reboot, channel number is getting reset to zero! fix!
|
||||
- send user and location events much less often
|
||||
- send location (or if not available user) when the user wakes the device from display sleep (both for testing and to improve user experience)
|
||||
- make real implementation of getNumOnlineNodes
|
||||
- very occasionally send our position and user packet based on the schedule in the radio info (if for nothing else so that other nodes update last_seen)
|
||||
- show real text info on the text screen
|
||||
- apply radio settings from android land
|
||||
- cope with nodes that have 0xff or 0x00 as the last byte of their mac
|
||||
- allow setting full radio params from android
|
||||
- add receive timestamps to messages, inserted by esp32 when message is received but then shown on the phone
|
||||
- update build to generate both board types
|
||||
- have node info screen show real info (including distance and heading)
|
||||
- blink the power led less often
|
||||
- have radiohead ISR send messages to RX queue directly, to allow that thread to block until we have something to send
|
||||
- move lora rx/tx to own thread and block on IO
|
||||
- keep our pseudo time moving forward even if we enter deep sleep (use esp32 rtc)
|
||||
- for non GPS equipped devices, set time from phone
|
||||
- GUI on oled hangs for a few seconds occasionally, but comes back
|
||||
- update local GPS position (but do not broadcast) at whatever rate the GPS is giving it
|
||||
- don't send our times to other nodes
|
||||
- don't trust times from other nodes
|
||||
- draw compass rose based off local walking track
|
||||
- add requestResponse optional bool - use for location broadcasts when sending tests
|
||||
- post sample video to signal forum
|
||||
- support non US frequencies
|
||||
- send pr https://github.com/ThingPulse/esp8266-oled-ssd1306 to tell them about this project
|
||||
- document rules for sleep wrt lora/bluetooth/screen/gps. also: if I have text messages (only) for the phone, then give a few seconds in the hopes BLE can get it across before we have to go back to sleep.
|
||||
- wake from light sleep as needed for our next scheduled periodic task (needed for gps position broadcasts etc)
|
||||
- turn bluetooth off based on our sleep policy
|
||||
- blink LED while in LS sleep mode
|
||||
- scrolling between screens based on press is busted
|
||||
- Use Neo-M8M API to put it in sleep mode (on hold until my new boards arrive)
|
||||
- update the prebuilt bins for different regulatory regions
|
||||
- don't enter NB state if we've recently talked to the phone (to prevent breaking syncing or bluetooth sw update)
|
||||
- have sw update prevent BLE sleep
|
||||
- manually delete characteristics/descs
|
||||
- leave lora receiver always on
|
||||
- protobufs are sometimes corrupted after sleep!
|
||||
- stay awake while charging
|
||||
- check gps battery voltage
|
||||
- if a position report includes ground truth time and we don't have time yet, set our clock from that. It is better than nothing.
|
||||
- retest BLE software update for both board types
|
||||
- report on wikifactory
|
||||
- send note to the guy who designed the cases
|
||||
- turn light sleep on aggressively (while lora is on but BLE off)
|
||||
- Use the Periodic class for both position and user periodic broadcasts
|
||||
- don't treat north as up, instead adjust shown bearings for our guess at the users heading (i.e. subtract one from the other)
|
||||
- sendToMesh can currently block for a long time, instead have it just queue a packet for a radio freertos thread
|
||||
- don't even power on bluetooth until we have some data to send to the android phone. Most of the time we should be sleeping in a lowpower "listening for lora" only mode. Once we have some packets for the phone, then power on bluetooth
|
||||
until the phone pulls those packets. Ever so often power on bluetooth just so we can see if the phone wants to send some packets. Possibly might need ULP processor to help with this wake process.
|
||||
- do hibernation mode to get power draw down to 2.5uA https://lastminuteengineers.com/esp32-sleep-modes-power-consumption/
|
||||
- fix GPS.zeroOffset calculation it is wrong
|
||||
- (needs testing) fixed the following during a plane flight:
|
||||
Have state machine properly enter deep sleep based on loss of mesh and phone comms.
|
||||
Default to enter deep sleep if no LORA received for two hours (indicates user has probably left the mesh).
|
||||
- (fixed I think) text messages are not showing on local screen if screen was on
|
||||
- add links to todos
|
||||
- link to the kanban page
|
||||
- add a getting started page
|
||||
- finish mesh alg reeval
|
||||
- ublox gps parsing seems a little buggy (we shouldn't be sending out read solution commands, the device is already broadcasting them)
|
||||
- turn on gps https://github.com/sparkfun/SparkFun_Ublox_Arduino_Library/blob/master/examples/Example18_PowerSaveMode/Example18_PowerSaveMode.ino
|
||||
- switch gps to 38400 baud https://github.com/sparkfun/SparkFun_Ublox_Arduino_Library/blob/master/examples/Example11_ResetModule/Example2_FactoryDefaultsviaSerial/Example2_FactoryDefaultsviaSerial.ino
|
||||
- Use Neo-M8M API to put it in sleep mode
|
||||
- use gps sleep mode instead of killing its power (to allow fast position when we wake)
|
||||
- enable fast lock and low power inside the gps chip
|
||||
- Make a FAQ
|
||||
- add a SF12 transmit option for _super_ long range
|
||||
- figure out why this fixme is needed: "FIXME, disable wake due to PMU because it seems to fire all the time?"
|
||||
- "AXP192 interrupt is not firing, remove this temporary polling of battery state"
|
||||
- make debug info screen show real data (including battery level & charging) - close corresponding github issue
|
||||
- remeasure wake time power draws now that we run CPU down at 80MHz
|
||||
|
||||
@@ -10,19 +10,28 @@ This device will work with any MTU size, but it is highly recommended that you c
|
||||
|
||||
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.
|
||||
|
||||
For a reference implementation of a client that uses this service see [RadioInterfaceService](https://github.com/meshtastic/Meshtastic-Android/blob/master/app/src/main/java/com/geeksville/mesh/service/RadioInterfaceService.kt). Typical flow when
|
||||
a phone connects to the device should be the following:
|
||||
For a reference implementation of a client that uses this service see [RadioInterfaceService](https://github.com/meshtastic/Meshtastic-Android/blob/master/app/src/main/java/com/geeksville/mesh/service/RadioInterfaceService.kt).
|
||||
|
||||
Typical flow when a phone connects to the device should be the following (if you want to watch this flow from the python app just run "meshtastic --debug --info" - the flow over BLE is identical):
|
||||
|
||||
- There are only three relevant endpoints (and they have built in BLE documentation - so use a BLE tool of your choice to watch them): FromRadio, FromNum (sends notifies when new data is available in FromRadio) and ToRadio
|
||||
- SetMTU size to 512
|
||||
- Write a ToRadio.startConfig protobuf to the "ToRadio" endpoint" - this tells the radio you are a new connection and you need the entire NodeDB sent down.
|
||||
- Read repeatedly from the "FromRadio" endpoint. Each time you read you will get back a FromRadio protobuf (see Meshtatastic-protobuf). Keep reading from this endpoint until you get back and empty buffer.
|
||||
- See below for the expected sequence for your initial download.
|
||||
- After the initial download, you should subscribe for BLE "notify" on the "FromNum" endpoint. If a notification arrives, that means there are now one or more FromRadio packets waiting inside FromRadio. Read from FromRadio until you get back an empty packet.
|
||||
- Any time you want to send packets to the radio, you should write a ToRadio packet into ToRadio.
|
||||
|
||||
Expected sequence for initial download:
|
||||
|
||||
- After your send startConfig, you will receive a series of FromRadio packets. The sequence of these packets will be as follows (but you are best not counting on this, instead just update your model for whatever packet you receive - based on looking at the type)
|
||||
- Read a RadioConfig from "radio" - used to get the channel and radio settings
|
||||
- Read (and write if incorrect) a User from "user" - to get the username for this node
|
||||
- Read a User from "user" - to get the username for this node
|
||||
- Read a MyNodeInfo from "mynode" to get information about this local device
|
||||
- Write an empty record to "nodeinfo" to restart the nodeinfo reading state machine
|
||||
- Read from "nodeinfo" until it returns empty to build the phone's copy of the current NodeDB for the mesh
|
||||
- Read from "fromradio" until it returns empty to get any messages that arrived for this node while the phone was away
|
||||
- Subscribe to notify on "fromnum" to get notified whenever the device has a new received packet
|
||||
- Read that new packet from "fromradio"
|
||||
- Whenever the phone has a packet to send write to "toradio"
|
||||
- Read a series of NodeInfo packets to build the phone's copy of the current NodeDB for the mesh
|
||||
- Read a endConfig packet that indicates that the entire state you need has been sent.
|
||||
- Read a series of MeshPackets until it returns empty to get any messages that arrived for this node while the phone was away
|
||||
|
||||
For definitions (and documentation) on FromRadio, ToRadio, MyNodeInfo, NodeInfo and User protocol buffers see [mesh.proto](https://github.com/meshtastic/Meshtastic-protobufs/blob/master/mesh.proto)
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ in these instructions I describe use of their command line tool.
|
||||
2. Install [PlatformIO](https://platformio.org/platformio-ide)
|
||||
3. Download this git repo and cd into it
|
||||
4. Run `git submodule update --init --recursive` to pull in dependencies this project needs.
|
||||
5. If you are outside the USA, edit [platformio.ini](/platformio.ini) to set the correct frequency range for your country. The line you need to change starts with `hw_version` and instructions are provided above that line. Options are provided for `EU433`, `EU835`, `CN`, `JP` and `US` (default). Pull-requests eagerly accepted for other countries.
|
||||
5. If you are outside the USA, run "export COUNTRY=EU865" (or whatever) to set the correct frequency range for your country. Options are provided for `EU433`, `EU865`, `CN`, `JP` and `US` (default). Pull-requests eagerly accepted for other countries.
|
||||
6. Plug the radio into your USB port
|
||||
7. Type `pio run --environment XXX -t upload` (This command will fetch dependencies, build the project and install it on the board via USB). For XXX, use the board type you have (either `tbeam`, `heltec`, `ttgo-lora32-v1`, `ttgo-lora32-v2`).
|
||||
8. Platform IO also installs a very nice VisualStudio Code based IDE, see their [tutorial](https://docs.platformio.org/en/latest/tutorials/espressif32/arduino_debugging_unit_testing.html) if you'd like to use it.
|
||||
|
||||
@@ -5,33 +5,47 @@ the project developers are not cryptography experts. Therefore we ask two things
|
||||
|
||||
- If you are a cryptography expert, please review these notes and our questions below. Can you help us by reviewing our
|
||||
notes below and offering advice? We will happily give as much or as little credit as you wish ;-).
|
||||
- Consider our existing solution 'alpha' and probably fairly secure against a not particularly aggressive adversary. But until
|
||||
it is reviewed by someone smarter than us, assume it might have flaws.
|
||||
- Consider our existing solution 'alpha' and probably fairly secure against a not particularly aggressive adversary
|
||||
(but we can't yet make a more confident statement).
|
||||
|
||||
## Notes on implementation
|
||||
## Summary of strengths/weaknesses of our current implementation
|
||||
|
||||
Based on comments from reviewers (see below), here's some tips for usage of these radios. So you can know the level of protection offered:
|
||||
|
||||
* It is pretty likely that the AES256 security is implemented 'correctly' and an observer will not be able to decode your messages.
|
||||
* Warning: If an attacker is able to get one of the radios in their position, they could either a) extract the channel key from that device or b) use that radio to listen to new communications.
|
||||
* Warning: If an attacker is able to get the "Channel QR code/URL" that you share with others - that attacker could then be able to read any messages sent on the channel (either tomorrow or in the past - if they kept a raw copy of those broadcast packets)
|
||||
|
||||
Possible future areas of work (if there is enough interest - post in our [forum](https://meshtastic.discourse.group) if you want this):
|
||||
|
||||
1. Optionally requiring users to provide a PIN to regain access to the mesh. This could be based on: intentionally locking the device, time since last use, or any member could force all members to reauthenticate,
|
||||
2. Until a device reauthenticates, any other access via BLE or USB would be blocked (this would protect against attackers who are not prepared to write custom software to extract and reverse engineer meshtastic flash memory)
|
||||
3. Turning on read-back protection in the device fuse-bits (this would extend protection in #2 to block all but **extremely** advanced attacks involving chip disassembly)
|
||||
4. Time limiting keys used for message transmission and automatically cycling them on a schedule. This would protect past messages from being decoded even if an attacker learns the current key.
|
||||
|
||||
### Notes for reviewers
|
||||
|
||||
If you are reviewing our implementation, this is a brief statement of our method.
|
||||
|
||||
- We do all crypto at the SubPacket (payload) level only, so that all meshtastic nodes will route for others - even those channels which are encrypted with a different key.
|
||||
- Mostly based on reading [Wikipedia](<https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#Counter_(CTR)>) and using the modes the ESP32 provides support for in hardware.
|
||||
- We use AES256-CTR as a stream cypher (with zero padding on the last BLOCK) because it is well supported with hardware acceleration.
|
||||
|
||||
Parameters for our CTR implementation:
|
||||
|
||||
- Our AES key is 128 or 256 bits, shared as part of the 'Channel' specification.
|
||||
- Each SubPacket will be sent as a series of 16 byte BLOCKS.
|
||||
- The node number concatenated with the packet number is used as the NONCE. This counter will be stored in flash in the device and should essentially never repeat. If the user makes a new 'Channel' (i.e. picking a new random 256 bit key), the packet number will start at zero. The packet number is sent
|
||||
in cleartext with each packet. The node number can be derived from the "from" field of each packet.
|
||||
- Each BLOCK for a packet has an incrementing COUNTER. COUNTER starts at zero for the first block of each packet.
|
||||
- The IV for each block is constructed by concatenating the NONCE as the upper 96 bits of the IV and the COUNTER as the bottom 32 bits. Note: since our packets are small counter will really never be higher than 32 (five bits).
|
||||
- The node number concatenated with the packet number is used as the NONCE. This nonce will be stored in flash in the device and should essentially never repeat. If the user makes a new 'Channel' (i.e. picking a new random 256 bit key), the packet number will start at zero.
|
||||
- The packet number is sent in cleartext with each packet. The node number can be derived from the "from" field of each packet. (Cleartext is acceptable because it merely provides IV for each encryption run)
|
||||
- Each 16 byte BLOCK for a packet has an incrementing COUNTER. COUNTER starts at zero for the first block of each packet.
|
||||
- The IV for each block is constructed by concatenating the NONCE as the upper 96 bits of the IV and the COUNTER as the bottom 32 bits. Since our packets are small counter portion will really never be higher than 32 (five bits).
|
||||
|
||||
```
|
||||
You can encrypt separate messages by dividing the nonce_counter buffer in two areas: the first one used for a per-message nonce, handled by yourself, and the second one updated by this function internally.
|
||||
For example, you might reserve the first 12 bytes for the per-message nonce, and the last 4 bytes for internal use. In that case, before calling this function on a new message you need to set the first 12 bytes of nonce_counter to your chosen nonce value, the last 4 to 0, and nc_off to 0 (which will cause stream_block to be ignored). That way, you can encrypt at most 2**96 messages of up to 2**32 blocks each with the same key.
|
||||
## Comments from reviewer #1
|
||||
|
||||
The per-message nonce (or information sufficient to reconstruct it) needs to be communicated with the ciphertext and must be unique. The recommended way to ensure uniqueness is to use a message counter. An alternative is to generate random nonces, but this limits the number of messages that can be securely encrypted: for example, with 96-bit random nonces, you should not encrypt more than 2**32 messages with the same key.
|
||||
This reviewer is a cryptography professional, but would like to remain anonymous. We thank them for their comments ;-):
|
||||
|
||||
Note that for both stategies, sizes are measured in blocks and that an AES block is 16 bytes.
|
||||
```
|
||||
I'm assuming that meshtastic is being used to hike in places where someone capable is trying to break it - like you were going to walk around DefCon using these. I spent about an hour reviewing the encryption, and have the following notes:
|
||||
|
||||
## Remaining todo
|
||||
* The write-up isn't quite as clear as the code.
|
||||
* The code is using AES-CTR mode correctly to ensure confidentiality.
|
||||
* The comment for initNonce really covers the necessary information.
|
||||
* I think the bigger encryption question is "what does the encryption need to do"? As it stands, an attacker who has yet to capture any of the devices cannot reasonably capture text or location data. An attacker who captures any device in the channel/mesh can read everything going to that device, everything stored on that device, and any other communication within the channel that they captured in encrypted form. If that capability basically matches your expectations, it is suitable for whatever adventures this was intended for, then, based on information publicly available or widely disclosed, the encryption is good. If those properties are distressing (like, device history is deliberately limited and you don't want a device captured today to endanger the information sent over the channel yesterday) we could talk about ways to achieve that (most likely synchronizing time and replacing the key with its own SHA256 every X hours, and ensuring the old key is not retained unnecessarily).
|
||||
* Two other things to keep in mind are that AES-CTR does not itself provide authenticity (e.g. an attacker can flip bits in replaying data and scramble the resulting plaintext), and that the current scheme gives some hints about transmission in the size. So, if you worry about an adversary deliberately messing-up messages or knowing the length of a text message, it looks like those might be possible.
|
||||
|
||||
- Have the app change the crypto key when the user generates a new channel
|
||||
I'm guessing that the network behaves somewhat like a store-and-forward network - or, at least, that the goal is to avoid establishing a two-way connection to transmit data. I'm afraid I haven't worked with mesh networks much, but remember studying them briefly in school about ten years ago.
|
||||
16
docs/software/esp32-arduino-build-notes.md
Normal file
16
docs/software/esp32-arduino-build-notes.md
Normal file
@@ -0,0 +1,16 @@
|
||||
# esp32-arduino build instructions
|
||||
|
||||
We build our own custom version of esp32-arduino, in order to get some fixes we've made but haven't yet been merged in master.
|
||||
|
||||
These are a set of currently unformatted notes on how to build and install them. Most developers should not care about this, because
|
||||
you'll automatically get our fixed libraries.
|
||||
|
||||
```
|
||||
last EDF release in arduino is: https://github.com/espressif/arduino-esp32/commit/1977370e6fc069e93ffd8818798fbfda27ae7d99
|
||||
IDF release/v3.3 46b12a560
|
||||
IDF release/v3.3 367c3c09c
|
||||
https://docs.espressif.com/projects/esp-idf/en/release-v3.3/get-started/linux-setup.html
|
||||
kevinh@kevin-server:~/development/meshtastic/esp32-arduino-lib-builder\$ python /home/kevinh/development/meshtastic/esp32-arduino-lib-builder/esp-idf/components/esptool*py/esptool/esptool.py --chip esp32 --port /dev/ttyUSB0 --baud 921600 --before default_reset --after hard_reset write_flash -z --flash_mode dout --flash_freq 40m --flash_size detect 0x1000 /home/kevinh/development/meshtastic/esp32-arduino-lib-builder/build/bootloader/bootloader.bin
|
||||
cp -a out/tools/sdk/* components/arduino/tools/sdk
|
||||
cp -ar components/arduino/* ~/.platformio/packages/framework-arduinoespressif32@src-fba9d33740f719f712e9f8b07da6ea13/
|
||||
```
|
||||
@@ -19,17 +19,18 @@ reliable messaging tasks (stage one for DSR):
|
||||
- DONE once an ack comes in, remove the packet from the retry list and deliver the ack to the original sender
|
||||
- DONE after three retries, deliver a no-ack packet to the original sender (i.e. the phone app or mesh router service)
|
||||
- DONE test one hop ack/nak with the python framework
|
||||
- Do stress test with acks
|
||||
- DONE Do stress test with acks
|
||||
|
||||
dsr tasks
|
||||
|
||||
- oops I might have broken message reception
|
||||
- DONE oops I might have broken message reception
|
||||
- DONE Don't use broadcasts for the network pings (close open github issue)
|
||||
- DONE add ignoreSenders to radioconfig to allow testing different mesh topologies by refusing to see certain senders
|
||||
- test multihop delivery with the python framework
|
||||
- DONE test multihop delivery with the python framework
|
||||
|
||||
optimizations / low priority:
|
||||
|
||||
- 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
|
||||
|
||||
@@ -2,11 +2,24 @@
|
||||
|
||||
## Misc work items
|
||||
|
||||
RAM investigation.
|
||||
nRF52832-QFAA 64KB ram, 512KB flash vs
|
||||
nrf52832-QFAB 32KB ram, 512kb flash
|
||||
nrf52840 256KB RAM, 1MB flash
|
||||
|
||||
platform.json
|
||||
|
||||
"framework-arduinoadafruitnrf52": {
|
||||
"type": "framework",
|
||||
"optional": true,
|
||||
"version": "https://github.com/meshtastic/Adafruit_nRF52_Arduino.git"
|
||||
},
|
||||
|
||||
## Initial work items
|
||||
|
||||
Minimum items needed to make sure hardware is good.
|
||||
|
||||
- set power UICR per https://devzone.nordicsemi.com/f/nordic-q-a/28562/nrf52840-regulator-configuration
|
||||
- DONE set power UICR per https://devzone.nordicsemi.com/f/nordic-q-a/28562/nrf52840-regulator-configuration
|
||||
- switch charge controller into / out of performance mode (see 8.3.1 in datasheet)
|
||||
- write UC1701 wrapper
|
||||
- Test hardfault handler for null ptrs (if one isn't already installed)
|
||||
@@ -59,8 +72,6 @@ Needed to be fully functional at least at the same level of the ESP32 boards. At
|
||||
|
||||
Nice ideas worth considering someday...
|
||||
|
||||
- Use flego to me an iOS/linux app? https://felgo.com/doc/qt/qtbluetooth-index/ or
|
||||
- Use flutter to make an iOS/linux app? https://github.com/Polidea/FlutterBleLib
|
||||
- enable monitor mode debugging (need to use real jlink): https://devzone.nordicsemi.com/nordic/nordic-blog/b/blog/posts/monitor-mode-debugging-with-j-link-and-gdbeclipse
|
||||
- Improve efficiency of PeriodicTimer by only checking the next queued timer event, and carefully sorting based on schedule
|
||||
- make a Mfg Controller and device under test classes as examples of custom app code for third party devs. Make a post about this. Use a custom payload type code. Have device under test send a broadcast with max hopcount of 0 for the 'mfgcontroller' payload type. mfg controller will read SNR and reply. DOT will declare failure/success and switch to the regular app screen.
|
||||
|
||||
27
docs/software/pinetab.md
Normal file
27
docs/software/pinetab.md
Normal file
@@ -0,0 +1,27 @@
|
||||
# Pinetab
|
||||
|
||||
These are **preliminary** notes on support for Meshtastic in the Pinetab.
|
||||
|
||||
A RF95 is connected via a CS341 USB-SPI chip.
|
||||
|
||||
Pin assignments:
|
||||
CS0 from RF95 goes to CS0 on CS341
|
||||
DIO0 from RF95 goes to INT on CS341
|
||||
RST from RF95 goes to RST on CS341
|
||||
|
||||
This linux driver claims to provide USB-SPI support: https://github.com/gschorcht/spi-ch341-usb
|
||||
Notes here on using that driver: https://www.linuxquestions.org/questions/linux-hardware-18/ch341-usb-to-spi-adaptor-driver-doesn%27t-work-4175622736/
|
||||
|
||||
Or if **absolutely** necessary could bitbang: https://www.cnx-software.com/2018/02/16/wch-ch341-usb-to-serial-chip-gets-linux-driver-to-control-gpios-over-usb/
|
||||
|
||||
## Task list
|
||||
|
||||
* Port meshtastic to build (under platformio) for a poxix target. spec: no screen, no gpios, sim network interface, posix threads, posix semaphores & queues, IO to the console only
|
||||
Use ARM linux: https://platformio.org/platforms/linux_arm
|
||||
And linux native: https://platformio.org/platforms/native
|
||||
|
||||
* Test cs341 driver - just test reading/writing a register and detecting interrupts, confirm can see rf95
|
||||
* Make a radiolib spi module that targets the cs341 (and builds on linux)
|
||||
* use new radiolib module to hook pinebook lora to meshtastic, confirm mesh discovery works
|
||||
* Make a subclass of StreamAPI that works as a posix TCP server
|
||||
* Use new TCP endpoint from meshtastic-python
|
||||
202
docs/software/ramusage-nrf52.txt
Normal file
202
docs/software/ramusage-nrf52.txt
Normal file
@@ -0,0 +1,202 @@
|
||||
23K + messages
|
||||
+ heap of 70ish packets, 300ish bytes per packet: 20KB
|
||||
+ 14KB soft device RAM
|
||||
|
||||
With max length Data inside the packet
|
||||
Size of NodeInfo 104
|
||||
Size of SubPacket 272
|
||||
Size of MeshPacket 304
|
||||
|
||||
If Data was smaller: for 70 data packets we would save 7KB. We would need to make SubPacket.data and MeshPacket.encrypted into "type:FT_POINTER" - variably sized mallocs
|
||||
Size of NodeInfo 104
|
||||
Size of SubPacket 96
|
||||
Size of MeshPacket 292 (could have been much smaller but I forgot to shrink MeshPacket.encrypted)
|
||||
|
||||
therefore:
|
||||
a) we should store all ToPhone message queued messages compressed as protobufs (since they will become that anyways)
|
||||
b) shrink packet pool size because none of that storage will be used for ToPhone packets
|
||||
c) don't allocate any storage in RAM for the tophone messages we save inside device state, instead just use nanopb callbacks to save/load those
|
||||
d) a smarter MeshPacket in memory representation would save about 7KB of RAM. call pb_release before freeing each freshly malloced MeshPacket
|
||||
|
||||
- nrf52 free memory https://learn.adafruit.com/bluefruit-nrf52-feather-learning-guide/hathach-memory-map
|
||||
|
||||
2000790c 00003558 B devicestate // 16KB
|
||||
2000b53c 00001000 b _cache_buffer // 4KB flash filesystem support
|
||||
20003b1c 000006b0 B console
|
||||
2000d5f4 00000400 b vApplicationGetTimerTaskMemory::uxTimerTaskStack
|
||||
2000da04 00000400 b _acUpBuffer
|
||||
2000c558 0000036c B Bluefruit
|
||||
2000c8d8 00000358 b _cdcd_itf
|
||||
2000e54c 00000258 B _midid_itf
|
||||
2000d0dc 00000200 b ucStaticTimerQueueStorage.9390
|
||||
2000e044 00000200 b _mscd_buf
|
||||
2000e284 000001cc b _vendord_itf
|
||||
2000d410 00000190 b vApplicationGetIdleTaskMemory::uxIdleTaskStack
|
||||
2000374c 0000016c D __global_locale
|
||||
2000de48 0000012c B USBDevice
|
||||
2000afa4 00000100 b Router::send(_MeshPacket*)::bytes
|
||||
2000aea4 00000100 b Router::perhapsDecode(_MeshPacket*)::bytes
|
||||
200039b0 000000f4 B powerFSM
|
||||
20004258 000000f0 B screen
|
||||
2000cd7c 000000c4 b _dcd
|
||||
2000cc68 000000c0 b _usbd_qdef_buf
|
||||
2000b3c4 000000bc B Wire
|
||||
2000cef4 000000a8 B Serial2
|
||||
2000ce4c 000000a8 B Serial1
|
||||
2000e498 000000a8 B _SEGGER_RTT
|
||||
2000b498 000000a4 B InternalFS
|
||||
2000dfb8 0000008c b _hidd_itf
|
||||
2000b260 00000088 b meshtastic::normalFrames
|
||||
2000cfdc 00000064 b pxReadyTasksLists
|
||||
2000b340 00000060 b meshtastic::drawTextMessageFrame(OLEDDisplay*, OLEDDisplayUiState*, short, short)::tempBuf
|
||||
200036ec 00000060 d impure_data
|
||||
2000b104 00000060 B bledfu
|
||||
2000b0a4 00000060 B blebas
|
||||
20003684 00000058 D _usbd_qdef
|
||||
200038c0 00000058 d tzinfo
|
||||
2000d5a0 00000054 b vApplicationGetTimerTaskMemory::xTimerTaskTCB
|
||||
2000d3bc 00000054 b vApplicationGetIdleTaskMemory::xIdleTaskTCB
|
||||
2000d308 00000050 b xStaticTimerQueue.9389
|
||||
2000b1f4 00000050 B hrmc
|
||||
2000b1a4 00000050 B bslc
|
||||
20004360 0000004c B service
|
||||
2000d374 00000048 b m_cb
|
||||
2000df74 00000042 b _desc_str
|
||||
2000cd3c 00000040 b _usbd_ctrl_buf
|
||||
20004214 00000040 B realRouter
|
||||
2000e244 00000040 b _mscd_itf
|
||||
2000b164 00000040 B bledis
|
||||
20003634 00000038 d _InternalFSConfig
|
||||
2000cc30 00000031 b _usbd_dev
|
||||
2000398c 00000020 B periodicScheduler
|
||||
2000cfa4 00000020 b callbacksInt
|
||||
2000de10 0000001f b fw_str.13525
|
||||
20003974 00000018 b object.9934
|
||||
2000ae68 00000018 B nodeDB
|
||||
2000366c 00000018 d _cache
|
||||
2000b314 00000014 b meshtastic::drawNodeInfo(OLEDDisplay*, OLEDDisplayUiState*, short, short)::signalStr
|
||||
2000b300 00000014 b meshtastic::drawNodeInfo(OLEDDisplay*, OLEDDisplayUiState*, short, short)::lastStr
|
||||
2000b2ec 00000014 b meshtastic::drawNodeInfo(OLEDDisplay*, OLEDDisplayUiState*, short, short)::distStr
|
||||
200041e0 00000014 b getDeviceName()::name
|
||||
2000d0b8 00000014 b xTasksWaitingTermination
|
||||
2000d0a4 00000014 b xSuspendedTaskList
|
||||
2000d08c 00000014 b xPendingReadyList
|
||||
2000d06c 00000014 b xDelayedTaskList2
|
||||
2000d058 00000014 b xDelayedTaskList1
|
||||
2000d2f0 00000014 b xActiveTimerList2
|
||||
2000d2dc 00000014 b xActiveTimerList1
|
||||
2000b480 00000014 B SPI
|
||||
2000c8c4 00000014 B Serial
|
||||
2000cd28 00000014 b _ctrl_xfer
|
||||
2000de30 00000011 b serial_str.13534
|
||||
2000c544 00000010 b BLEAdvertising::_start(unsigned short, unsigned short)::gap_adv
|
||||
20003614 00000010 d meshtastic::btPIN
|
||||
2000434c 00000010 b sendOwnerPeriod
|
||||
2000ae8c 00000010 b staticPool
|
||||
2000e484 00000010 B xQueueRegistry
|
||||
20003b04 00000010 B stateSERIAL
|
||||
20003af4 00000010 B stateSDS
|
||||
20003ae4 00000010 B stateON
|
||||
20003ad4 00000010 B stateNB
|
||||
20003ac4 00000010 B stateLS
|
||||
20003ab4 00000010 B stateDARK
|
||||
20003aa4 00000010 B stateBOOT
|
||||
200041f8 00000010 B ledPeriodic
|
||||
2000b244 00000010 B hrms
|
||||
2000d9f4 00000010 b _acDownBuffer
|
||||
2000b3b8 0000000c B preflightSleep
|
||||
20004208 0000000c B powerStatus
|
||||
2000e540 0000000c B nrf_nvic_state
|
||||
2000b3ac 0000000c B notifySleep
|
||||
2000b3a0 0000000c B notifyDeepSleep
|
||||
2000e463 0000000b b __tzname_std
|
||||
2000e458 0000000b b __tzname_dst
|
||||
2000b338 00000008 b meshtastic::estimatedHeading(double, double)::oldLon
|
||||
2000b330 00000008 b meshtastic::estimatedHeading(double, double)::oldLat
|
||||
200041d0 00000008 b zeroOffsetSecs
|
||||
2000ae80 00000008 b spiSettings
|
||||
200038b8 00000008 D _tzname
|
||||
20003b14 00000008 B noopPrint
|
||||
2000cfc4 00000008 b channelMap
|
||||
2000cf9c 00000008 b callbackDeferred
|
||||
200043ac 00000006 b ourMacAddr
|
||||
2000435c 00000004 b MeshService::onGPSChanged(void*)::lastGpsSend
|
||||
2000b32c 00000004 b meshtastic::estimatedHeading(double, double)::b
|
||||
2000b328 00000004 b meshtastic::drawNodeInfo(OLEDDisplay*, OLEDDisplayUiState*, short, short)::simRadian
|
||||
2000362c 00000004 d meshtastic::Screen::setup()::bootFrames
|
||||
20003628 00000004 d meshtastic::Screen::handleStartBluetoothPinScreen(unsigned long)::btFrames
|
||||
200039ac 00000004 b onEnter()::lastPingMs
|
||||
2000ae9c 00000004 b generatePacketId()::i
|
||||
2000ae88 00000004 B RadioLibInterface::instance
|
||||
2000b2e8 00000004 b meshtastic::nodeIndex
|
||||
20003610 00000004 d meshtastic::targetFramerate
|
||||
2000c554 00000004 B BLEService::lastService
|
||||
200041cc 00000004 b timeStartMsec
|
||||
200036dc 00000004 d sbrk_heap_top
|
||||
2000d364 00000004 b _loopHandle
|
||||
2000c540 00000004 b guard variable for BLEAdvertising::_start(unsigned short, unsigned short)::gap_adv
|
||||
2000d0d0 00000004 b xYieldPending
|
||||
2000d35c 00000004 b xTimerTaskHandle
|
||||
2000d358 00000004 b xTimerQueue
|
||||
2000d0cc 00000004 b xTickCount
|
||||
2000d0a0 00000004 b xSchedulerRunning
|
||||
2000d088 00000004 b xNumOfOverflows
|
||||
2000d084 00000004 b xNextTaskUnblockTime
|
||||
2000d304 00000004 b xLastTime.9343
|
||||
2000d080 00000004 b xIdleTaskHandle
|
||||
2000d054 00000004 b uxTopReadyPriority
|
||||
2000d050 00000004 b uxTaskNumber
|
||||
2000d04c 00000004 b uxSchedulerSuspended
|
||||
2000d048 00000004 b uxPendedTicks
|
||||
2000d044 00000004 b uxDeletedTasksWaitingCleanUp
|
||||
2000d040 00000004 b uxCurrentNumberOfTasks
|
||||
2000d360 00000004 b uxCriticalNesting
|
||||
2000cc64 00000004 b _usbd_q
|
||||
2000e478 00000004 B _timezone
|
||||
200036e0 00000004 D SystemCoreClock
|
||||
2000c53c 00000004 b _sem
|
||||
2000d0d8 00000004 b pxOverflowTimerList
|
||||
2000cfd8 00000004 b pxOverflowDelayedTaskList
|
||||
2000cfd4 00000004 b pxDelayedTaskList
|
||||
2000d0d4 00000004 b pxCurrentTimerList
|
||||
2000cfd0 00000004 B pxCurrentTCB
|
||||
2000e470 00000004 b prev_tzenv
|
||||
2000360c 00000004 D preftmp
|
||||
20003608 00000004 D preffile
|
||||
2000b25c 00000004 B nrf52Bluetooth
|
||||
2000d370 00000004 b m_usbevt_handler
|
||||
2000d36c 00000004 b m_sleepevt_handler
|
||||
2000d368 00000004 b m_pofwarn_handler
|
||||
2000e454 00000004 B __malloc_sbrk_start
|
||||
2000e450 00000004 B __malloc_free_list
|
||||
2000e480 00000004 B MAIN_MonCnt
|
||||
2000e47c 00000004 b initial_env
|
||||
200036e8 00000004 D _impure_ptr
|
||||
200041d8 00000004 B gps
|
||||
2000e7a4 00000004 B errno
|
||||
20003918 00000004 D environ
|
||||
2000cfcc 00000004 b enabled
|
||||
2000ae64 00000004 B displayedNodeNum
|
||||
2000e474 00000004 B _daylight
|
||||
2000b254 00000004 B crypto
|
||||
2000ce44 00000004 B count_duration
|
||||
2000de0c 00000004 b _cb_task
|
||||
2000de08 00000004 b _cb_queue
|
||||
2000de04 00000004 b _cb_qdepth
|
||||
2000de44 00000004 B bootloaderVersion
|
||||
200041f5 00000001 b ledBlinker()::ledOn
|
||||
20003604 00000001 d loop::showingBootScreen
|
||||
200041f4 00000001 b loop::wasPressed
|
||||
2000b494 00000001 b DefaultFontTableLookup(unsigned char)::LASTCHAR
|
||||
2000aea0 00000001 b generatePacketId()::didInit
|
||||
20003624 00000001 d meshtastic::prevFrame
|
||||
2000b258 00000001 b bleOn
|
||||
200041dc 00000001 B timeSetFromGPS
|
||||
20004348 00000001 B ssd1306_found
|
||||
2000ce49 00000001 B pin_sound
|
||||
2000e494 00000001 B nrfx_power_irq_enabled
|
||||
2000ce48 00000001 B no_stop
|
||||
20003630 00000001 D neo6M
|
||||
2000ce40 00000001 b _initialized
|
||||
200036e4 00000001 D __fdlib_version
|
||||
20003970 00000001 b completed.9929
|
||||
@@ -11,7 +11,7 @@
|
||||
*****************************************************************/
|
||||
|
||||
/* Enable support for dynamically allocated fields */
|
||||
/* #define PB_ENABLE_MALLOC 1 */
|
||||
#define PB_ENABLE_MALLOC 1
|
||||
|
||||
/* Define this if your CPU / compiler combination does not support
|
||||
* unaligned memory access to packed structures. */
|
||||
|
||||
18
linker/esp32.extram.bss.ld
Normal file
18
linker/esp32.extram.bss.ld
Normal file
@@ -0,0 +1,18 @@
|
||||
/* This section is only included if CONFIG_SPIRAM_ALLOW_BSS_SEG_EXTERNAL_MEMORY
|
||||
is set, to link some sections to BSS in PSRAM */
|
||||
|
||||
SECTIONS
|
||||
{
|
||||
/* external memory bss, from any global variable with EXT_RAM_ATTR attribute*/
|
||||
.ext_ram.bss (NOLOAD) :
|
||||
{
|
||||
_ext_ram_bss_start = ABSOLUTE(.);
|
||||
*(.ext_ram.bss*)
|
||||
*libnet80211.a:(.dynsbss .sbss .sbss.* .gnu.linkonce.sb.* .scommon .sbss2.* .gnu.linkonce.sb2.* .dynbss .bss .bss.* .share.mem .gnu.linkonce.b.* COMMON)
|
||||
*libpp.a:(.dynsbss .sbss .sbss.* .gnu.linkonce.sb.* .scommon .sbss2.* .gnu.linkonce.sb2.* .dynbss .bss .bss.* .share.mem .gnu.linkonce.b.* COMMON)
|
||||
*liblwip.a:(.dynsbss .sbss .sbss.* .gnu.linkonce.sb.* .scommon .sbss2.* .gnu.linkonce.sb2.* .dynbss .bss .bss.* .share.mem .gnu.linkonce.b.* COMMON)
|
||||
*libbt.a:(EXCLUDE_FILE (libbtdm_app.a) .dynsbss .sbss .sbss.* .gnu.linkonce.sb.* .scommon .sbss2.* .gnu.linkonce.sb2.* .dynbss .bss .bss.* .share.mem .gnu.linkonce.b.* COMMON)
|
||||
. = ALIGN(4);
|
||||
_ext_ram_bss_end = ABSOLUTE(.);
|
||||
} > extern_ram_seg
|
||||
}
|
||||
46
linker/nrf52840_s140_sim832.ld
Normal file
46
linker/nrf52840_s140_sim832.ld
Normal file
@@ -0,0 +1,46 @@
|
||||
/* Linker script to configure memory regions.
|
||||
|
||||
geeksville: modified this to simulate a nrf52832 but with a sd140 soft device. So I can
|
||||
see how the memory footprint works on this lower end CPU. Note: to work with sd140 in my bootloader
|
||||
I need to start ram at 0x6000 (instead of the correct 0x3600 for sd132) - so I have less
|
||||
RAM available than on a real 832.
|
||||
*/
|
||||
|
||||
SEARCH_DIR(.)
|
||||
GROUP(-lgcc -lc -lnosys)
|
||||
|
||||
MEMORY
|
||||
{
|
||||
FLASH (rx) : ORIGIN = 0x26000, LENGTH = 0x6D000 - 0x26000
|
||||
/* FLASH (rx) : ORIGIN = 0x26000, LENGTH = 0xED000 - 0x26000 */
|
||||
|
||||
/* SRAM required by S132 depend on
|
||||
* - Attribute Table Size
|
||||
* - Vendor UUID count
|
||||
* - Max ATT MTU
|
||||
* - Concurrent connection peripheral + central + secure links
|
||||
* - Event Len, HVN queue, Write CMD queue
|
||||
*/
|
||||
/* RAM (rwx) : ORIGIN = 0x20003600, LENGTH = 0x20010000 - 0x20003600 */
|
||||
RAM (rwx) : ORIGIN = 0x20006000, LENGTH = 0x20010000 - 0x20006000
|
||||
}
|
||||
|
||||
SECTIONS
|
||||
{
|
||||
. = ALIGN(4);
|
||||
.svc_data :
|
||||
{
|
||||
PROVIDE(__start_svc_data = .);
|
||||
KEEP(*(.svc_data))
|
||||
PROVIDE(__stop_svc_data = .);
|
||||
} > RAM
|
||||
|
||||
.fs_data :
|
||||
{
|
||||
PROVIDE(__start_fs_data = .);
|
||||
KEEP(*(.fs_data))
|
||||
PROVIDE(__stop_fs_data = .);
|
||||
} > RAM
|
||||
} INSERT AFTER .data;
|
||||
|
||||
INCLUDE "nrf52_common.ld"
|
||||
@@ -17,13 +17,11 @@ default_envs = tbeam ; Note: the github actions CI test build can't yet build NR
|
||||
; REQUIRED environment variables - if not set the specified default will be sued
|
||||
; The following environment variables must be set in the shell if you'd like to override them.
|
||||
; They are used in this ini file as systenv.VARNAME, so in your shell do export "VARNAME=fish"
|
||||
; HW_VERSION (default US)
|
||||
; COUNTRY (default US), i.e. "export COUNTRY=EU865"
|
||||
; APP_VERSION (default emptystring)
|
||||
; HW_VERSION (default emptystring)
|
||||
|
||||
[env]
|
||||
platform = espressif32
|
||||
framework = arduino
|
||||
|
||||
; customize the partition table
|
||||
; http://docs.platformio.org/en/latest/platforms/espressif32.html#partition-tables
|
||||
@@ -69,7 +67,7 @@ debug_tool = jlink
|
||||
lib_deps =
|
||||
https://github.com/meshtastic/esp8266-oled-ssd1306.git ; ESP8266_SSD1306
|
||||
SPI
|
||||
; 1260 ; OneButton - not used yet
|
||||
1260 ; OneButton library for non-blocking button debounce
|
||||
1202 ; CRC32, explicitly needed because dependency is missing in the ble ota update lib
|
||||
Wire ; explicitly needed here because the AXP202 library forgets to add it
|
||||
https://github.com/meshtastic/arduino-fsm.git
|
||||
@@ -79,13 +77,19 @@ lib_deps =
|
||||
|
||||
; 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
|
||||
debug_init_break = tbreak setup
|
||||
build_flags =
|
||||
${env.build_flags} -Wall -Wextra -Isrc/esp32
|
||||
${env.build_flags} -Wall -Wextra -Isrc/esp32 -mfix-esp32-psram-cache-issue
|
||||
# Hmm - this doesn't work yet
|
||||
# board_build.ldscript = linker/esp32.extram.bss.ld
|
||||
lib_ignore = segger_rtt
|
||||
platform_packages =
|
||||
framework-arduinoespressif32 @ https://github.com/meshtastic/arduino-esp32.git#f26c4f96fefd13ed0ed042e27954f8aba6328f6b
|
||||
|
||||
; The 1.0 release of the TBEAM board
|
||||
[env:tbeam]
|
||||
@@ -94,6 +98,7 @@ 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
|
||||
|
||||
@@ -123,6 +128,18 @@ board = ttgo-lora32-v1
|
||||
build_flags =
|
||||
${esp32_base.build_flags} -D TTGO_LORA_V2
|
||||
|
||||
; 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
|
||||
src_filter =
|
||||
${env.src_filter} -<esp32/> -<nrf52/>
|
||||
|
||||
; Common settings for NRF52 based targets
|
||||
[nrf52_base]
|
||||
platform = nordicnrf52
|
||||
@@ -131,7 +148,7 @@ debug_tool = jlink
|
||||
build_type = debug ; I'm debugging with ICE a lot now
|
||||
; note: liboberon provides the AES256 implementation for NRF52 (though not using the hardware acceleration of the NRF52840 - FIXME)
|
||||
build_flags =
|
||||
${env.build_flags} -Wno-unused-variable -Isrc/nrf52 -Isdk-nrfxlib/crypto/nrf_oberon/include -Lsdk-nrfxlib/crypto/nrf_oberon/lib/cortex-m4/hard-float/ -lliboberon_3.0.3
|
||||
${env.build_flags} -Wno-unused-variable -Isrc/nrf52 -Isdk-nrfxlib/crypto/nrf_oberon/include -Lsdk-nrfxlib/crypto/nrf_oberon/lib/cortex-m4/hard-float/ -lliboberon_3.0.3
|
||||
;-DCFG_DEBUG=3
|
||||
src_filter =
|
||||
${env.src_filter} -<esp32/>
|
||||
@@ -154,8 +171,9 @@ debug_init_break =
|
||||
[env:nrf52dk]
|
||||
extends = nrf52_base
|
||||
board = nrf52840_dk_modified
|
||||
lib_deps =
|
||||
UC1701 ; for temp testing
|
||||
|
||||
# For experimenting with RAM sizes
|
||||
# board_build.ldscript = linker/nrf52840_s140_sim832.ld
|
||||
|
||||
; The PPR board
|
||||
[env:ppr]
|
||||
@@ -164,7 +182,7 @@ board = ppr
|
||||
lib_deps =
|
||||
${env.lib_deps}
|
||||
UC1701
|
||||
https://github.com/meshtastic/BQ25703A.git
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
2
proto
2
proto
Submodule proto updated: 3ba76bbe4c...ab281311c4
@@ -26,60 +26,73 @@ static void sdsEnter()
|
||||
|
||||
#include "error.h"
|
||||
|
||||
static uint32_t secsSlept;
|
||||
|
||||
static void lsEnter()
|
||||
{
|
||||
DEBUG_MSG("lsEnter begin, ls_secs=%u\n", radioConfig.preferences.ls_secs);
|
||||
screen.setOn(false);
|
||||
secsSlept = 0; // How long have we been sleeping this time
|
||||
|
||||
DEBUG_MSG("lsEnter end\n");
|
||||
}
|
||||
|
||||
static void lsIdle()
|
||||
{
|
||||
DEBUG_MSG("lsIdle begin ls_secs=%u\n", radioConfig.preferences.ls_secs);
|
||||
// DEBUG_MSG("lsIdle begin ls_secs=%u\n", radioConfig.preferences.ls_secs);
|
||||
|
||||
#ifndef NO_ESP32
|
||||
uint32_t secsSlept = 0;
|
||||
esp_sleep_source_t wakeCause = ESP_SLEEP_WAKEUP_UNDEFINED;
|
||||
bool reached_ls_secs = false;
|
||||
|
||||
while (!reached_ls_secs) {
|
||||
// Do we have more sleeping to do?
|
||||
if (secsSlept < radioConfig.preferences.ls_secs) {
|
||||
// Briefly come out of sleep long enough to blink the led once every few seconds
|
||||
uint32_t sleepTime = 5;
|
||||
uint32_t sleepTime = 30;
|
||||
|
||||
setLed(false); // Never leave led on while in light sleep
|
||||
wakeCause = doLightSleep(sleepTime * 1000LL);
|
||||
if (wakeCause != ESP_SLEEP_WAKEUP_TIMER)
|
||||
break;
|
||||
// If some other service would stall sleep, don't let sleep happen yet
|
||||
if (doPreflightSleep()) {
|
||||
setLed(false); // Never leave led on while in light sleep
|
||||
wakeCause = doLightSleep(sleepTime * 1000LL);
|
||||
|
||||
setLed(true); // briefly turn on led
|
||||
doLightSleep(1);
|
||||
if (wakeCause != ESP_SLEEP_WAKEUP_TIMER)
|
||||
break;
|
||||
if (wakeCause == ESP_SLEEP_WAKEUP_TIMER) {
|
||||
// Normal case: timer expired, we should just go back to sleep ASAP
|
||||
|
||||
secsSlept += sleepTime;
|
||||
reached_ls_secs = secsSlept >= radioConfig.preferences.ls_secs;
|
||||
}
|
||||
setLed(false);
|
||||
setLed(true); // briefly turn on led
|
||||
wakeCause = doLightSleep(1); // leave led on for 1ms
|
||||
|
||||
if (reached_ls_secs) {
|
||||
// stay in LS mode but let loop check whatever it wants
|
||||
DEBUG_MSG("reached ls_secs, servicing loop()\n");
|
||||
} else {
|
||||
DEBUG_MSG("wakeCause %d\n", wakeCause);
|
||||
secsSlept += sleepTime;
|
||||
// DEBUG_MSG("sleeping, flash led!\n");
|
||||
}
|
||||
if (wakeCause == ESP_SLEEP_WAKEUP_UART) {
|
||||
// Not currently used (because uart triggers in hw have problems)
|
||||
powerFSM.trigger(EVENT_SERIAL_CONNECTED);
|
||||
} else {
|
||||
// We woke for some other reason (button press, uart, device interrupt)
|
||||
// uint64_t status = esp_sleep_get_ext1_wakeup_status();
|
||||
DEBUG_MSG("wakeCause %d\n", wakeCause);
|
||||
|
||||
#ifdef BUTTON_PIN
|
||||
bool pressed = !digitalRead(BUTTON_PIN);
|
||||
bool pressed = !digitalRead(BUTTON_PIN);
|
||||
#else
|
||||
bool pressed = false;
|
||||
bool pressed = false;
|
||||
#endif
|
||||
if (pressed) // If we woke because of press, instead generate a PRESS event.
|
||||
{
|
||||
powerFSM.trigger(EVENT_PRESS);
|
||||
if (pressed) // If we woke because of press, instead generate a PRESS event.
|
||||
{
|
||||
powerFSM.trigger(EVENT_PRESS);
|
||||
} else {
|
||||
// Otherwise let the NB state handle the IRQ (and that state will handle stuff like IRQs etc)
|
||||
powerFSM.trigger(EVENT_WAKE_TIMER);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Otherwise let the NB state handle the IRQ (and that state will handle stuff like IRQs etc)
|
||||
powerFSM.trigger(EVENT_WAKE_TIMER);
|
||||
// Someone says we can't sleep now, so just save some power by sleeping the CPU for 100ms or so
|
||||
delay(100);
|
||||
}
|
||||
} else {
|
||||
// Time to stop sleeping!
|
||||
setLed(false);
|
||||
DEBUG_MSG("reached ls_secs, servicing loop()\n");
|
||||
powerFSM.trigger(EVENT_WAKE_TIMER);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
#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);
|
||||
@@ -16,6 +19,15 @@ 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();
|
||||
}
|
||||
}
|
||||
@@ -28,10 +40,10 @@ void NotifiedWorkerThread::notify(uint32_t v, eNotifyAction action)
|
||||
xTaskNotify(taskHandle, v, action);
|
||||
}
|
||||
|
||||
|
||||
|
||||
void NotifiedWorkerThread::block()
|
||||
{
|
||||
xTaskNotifyWait(0, // don't clear notification on entry
|
||||
clearOnRead, ¬ification, portMAX_DELAY); // Wait forever
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -1,4 +1,7 @@
|
||||
#include <Arduino.h>
|
||||
#include "freertosinc.h"
|
||||
|
||||
#ifdef HAS_FREE_RTOS
|
||||
|
||||
class Thread
|
||||
{
|
||||
@@ -15,6 +18,8 @@ class Thread
|
||||
|
||||
virtual ~Thread() { vTaskDelete(taskHandle); }
|
||||
|
||||
uint32_t getStackHighwaterMark() { return uxTaskGetStackHighWaterMark(taskHandle); }
|
||||
|
||||
protected:
|
||||
/**
|
||||
* The method that will be called when start is called.
|
||||
@@ -86,4 +91,6 @@ class NotifiedWorkerThread : public WorkerThread
|
||||
* A method that should block execution - either waiting ona queue/mutex or a "task notification"
|
||||
*/
|
||||
virtual void block();
|
||||
};
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -73,6 +73,20 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#define BUTTON_PIN PIN_BUTTON1
|
||||
|
||||
// FIXME, use variant.h defs for all of this!!! (even on the ESP32 targets)
|
||||
#elif defined(CubeCell_BoardPlus)
|
||||
|
||||
//
|
||||
// Standard definitions for CubeCell targets
|
||||
//
|
||||
|
||||
#define NO_ESP32 // Don't use ESP32 libs (mainly bluetooth)
|
||||
|
||||
// FIXME, not yet ready for NRF52
|
||||
#define RTC_DATA_ATTR
|
||||
|
||||
#define LED_PIN -1 // FIXME totally bogus
|
||||
#define BUTTON_PIN -1
|
||||
|
||||
#else
|
||||
|
||||
//
|
||||
@@ -104,6 +118,10 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
#define SSD1306_ADDRESS 0x3C
|
||||
|
||||
// The SH1106 controller is almost, but not quite, the same as SSD1306
|
||||
// Define this if you know you have that controller or your "SSD1306" misbehaves.
|
||||
//#define USE_SH1106
|
||||
|
||||
// Flip the screen upside down by default as it makes more sense on T-BEAM
|
||||
// devices. Comment this out to not rotate screen 180 degrees.
|
||||
#define FLIP_SCREEN_VERTICALLY
|
||||
@@ -126,7 +144,8 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#define I2C_SDA 21
|
||||
#define I2C_SCL 22
|
||||
|
||||
#define BUTTON_PIN 38
|
||||
#define BUTTON_PIN 38 // The middle button GPIO on the T-Beam
|
||||
#define BUTTON_PIN_ALT 13 // Alternate GPIO for an external button if needed
|
||||
|
||||
#ifndef USE_JTAG
|
||||
#define RESET_GPIO 14
|
||||
@@ -168,6 +187,13 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
// This string must exactly match the case used in release file names or the android updater won't work
|
||||
#define HW_VENDOR "heltec"
|
||||
|
||||
// the default ESP32 Pin of 15 is the Oled SCL, set to 36 and 37 and works fine.
|
||||
// Tested on Neo6m module.
|
||||
#undef GPS_RX_PIN
|
||||
#undef GPS_TX_PIN
|
||||
#define GPS_RX_PIN 36
|
||||
#define GPS_TX_PIN 37
|
||||
|
||||
#ifndef USE_JTAG // gpio15 is TDO for JTAG, so no I2C on this board while doing jtag
|
||||
#define I2C_SDA 4 // I2C pins for this board
|
||||
#define I2C_SCL 15
|
||||
@@ -188,6 +214,10 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#elif defined(TTGO_LORA_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"
|
||||
#undef GPS_RX_PIN
|
||||
#undef GPS_TX_PIN
|
||||
#define GPS_RX_PIN 36
|
||||
#define GPS_TX_PIN 37
|
||||
|
||||
#define I2C_SDA 4 // I2C pins for this board
|
||||
#define I2C_SCL 15
|
||||
@@ -256,9 +286,13 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
#define DEBUG_PORT console // Serial debug port
|
||||
|
||||
#ifdef NO_ESP32
|
||||
// What platforms should use SEGGER?
|
||||
#ifdef NRF52_SERIES
|
||||
#define USE_SEGGER
|
||||
#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__)
|
||||
|
||||
1
src/cubecell/FS.h
Normal file
1
src/cubecell/FS.h
Normal file
@@ -0,0 +1 @@
|
||||
// Placeholder FIXME
|
||||
@@ -76,6 +76,7 @@ class DataCharacteristic : public CallbackCharacteristic
|
||||
crc.update(data, len);
|
||||
Update.write(data, len);
|
||||
updateActualSize += len;
|
||||
powerFSM.trigger(EVENT_RECEIVED_TEXT_MSG); // Not exactly correct, but we want to force the device to not sleep now
|
||||
}
|
||||
};
|
||||
|
||||
@@ -123,8 +124,10 @@ class CRC32Characteristic : public CallbackCharacteristic
|
||||
|
||||
void bluetoothRebootCheck()
|
||||
{
|
||||
if (rebootAtMsec && millis() > rebootAtMsec)
|
||||
if (rebootAtMsec && millis() > rebootAtMsec) {
|
||||
DEBUG_MSG("Rebooting for update\n");
|
||||
ESP.restart();
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
@@ -8,54 +8,6 @@
|
||||
|
||||
SimpleAllocator btPool;
|
||||
|
||||
/**
|
||||
* Create standard device info service
|
||||
**/
|
||||
BLEService *createDeviceInfomationService(BLEServer *server, std::string hwVendor, std::string swVersion,
|
||||
std::string hwVersion = "")
|
||||
{
|
||||
BLEService *deviceInfoService = server->createService(BLEUUID((uint16_t)ESP_GATT_UUID_DEVICE_INFO_SVC));
|
||||
|
||||
BLECharacteristic *swC =
|
||||
new BLECharacteristic(BLEUUID((uint16_t)ESP_GATT_UUID_SW_VERSION_STR), BLECharacteristic::PROPERTY_READ);
|
||||
BLECharacteristic *mfC = new BLECharacteristic(BLEUUID((uint16_t)ESP_GATT_UUID_MANU_NAME), BLECharacteristic::PROPERTY_READ);
|
||||
// BLECharacteristic SerialNumberCharacteristic(BLEUUID((uint16_t) ESP_GATT_UUID_SERIAL_NUMBER_STR),
|
||||
// BLECharacteristic::PROPERTY_READ);
|
||||
|
||||
/*
|
||||
* Mandatory characteristic for device info service?
|
||||
|
||||
BLECharacteristic *m_pnpCharacteristic = m_deviceInfoService->createCharacteristic(ESP_GATT_UUID_PNP_ID,
|
||||
BLECharacteristic::PROPERTY_READ);
|
||||
|
||||
uint8_t sig, uint16_t vid, uint16_t pid, uint16_t version;
|
||||
uint8_t pnp[] = { sig, (uint8_t) (vid >> 8), (uint8_t) vid, (uint8_t) (pid >> 8), (uint8_t) pid, (uint8_t) (version >>
|
||||
8), (uint8_t) version }; m_pnpCharacteristic->setValue(pnp, sizeof(pnp));
|
||||
*/
|
||||
swC->setValue(swVersion);
|
||||
deviceInfoService->addCharacteristic(addBLECharacteristic(swC));
|
||||
mfC->setValue(hwVendor);
|
||||
deviceInfoService->addCharacteristic(addBLECharacteristic(mfC));
|
||||
if (!hwVersion.empty()) {
|
||||
BLECharacteristic *hwvC =
|
||||
new BLECharacteristic(BLEUUID((uint16_t)ESP_GATT_UUID_HW_VERSION_STR), BLECharacteristic::PROPERTY_READ);
|
||||
hwvC->setValue(hwVersion);
|
||||
deviceInfoService->addCharacteristic(addBLECharacteristic(hwvC));
|
||||
}
|
||||
// SerialNumberCharacteristic.setValue("FIXME");
|
||||
// deviceInfoService->addCharacteristic(&SerialNumberCharacteristic);
|
||||
|
||||
// m_manufacturerCharacteristic = m_deviceInfoService->createCharacteristic((uint16_t) 0x2a29,
|
||||
// BLECharacteristic::PROPERTY_READ); m_manufacturerCharacteristic->setValue(name);
|
||||
|
||||
/* add these later?
|
||||
ESP_GATT_UUID_SYSTEM_ID
|
||||
*/
|
||||
|
||||
// caller must call service->start();
|
||||
return deviceInfoService;
|
||||
}
|
||||
|
||||
bool _BLEClientConnected = false;
|
||||
|
||||
class MyServerCallbacks : public BLEServerCallbacks
|
||||
@@ -106,6 +58,54 @@ void addWithDesc(BLEService *service, BLECharacteristic *c, const char *descript
|
||||
addBLEDescriptor(desc);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create standard device info service
|
||||
**/
|
||||
BLEService *createDeviceInfomationService(BLEServer *server, std::string hwVendor, std::string swVersion,
|
||||
std::string hwVersion = "")
|
||||
{
|
||||
BLEService *deviceInfoService = server->createService(BLEUUID((uint16_t)ESP_GATT_UUID_DEVICE_INFO_SVC));
|
||||
|
||||
BLECharacteristic *swC =
|
||||
new BLECharacteristic(BLEUUID((uint16_t)ESP_GATT_UUID_SW_VERSION_STR), BLECharacteristic::PROPERTY_READ);
|
||||
BLECharacteristic *mfC = new BLECharacteristic(BLEUUID((uint16_t)ESP_GATT_UUID_MANU_NAME), BLECharacteristic::PROPERTY_READ);
|
||||
// BLECharacteristic SerialNumberCharacteristic(BLEUUID((uint16_t) ESP_GATT_UUID_SERIAL_NUMBER_STR),
|
||||
// BLECharacteristic::PROPERTY_READ);
|
||||
|
||||
/*
|
||||
* Mandatory characteristic for device info service?
|
||||
|
||||
BLECharacteristic *m_pnpCharacteristic = m_deviceInfoService->createCharacteristic(ESP_GATT_UUID_PNP_ID,
|
||||
BLECharacteristic::PROPERTY_READ);
|
||||
|
||||
uint8_t sig, uint16_t vid, uint16_t pid, uint16_t version;
|
||||
uint8_t pnp[] = { sig, (uint8_t) (vid >> 8), (uint8_t) vid, (uint8_t) (pid >> 8), (uint8_t) pid, (uint8_t) (version >>
|
||||
8), (uint8_t) version }; m_pnpCharacteristic->setValue(pnp, sizeof(pnp));
|
||||
*/
|
||||
swC->setValue(swVersion);
|
||||
deviceInfoService->addCharacteristic(addBLECharacteristic(swC));
|
||||
mfC->setValue(hwVendor);
|
||||
deviceInfoService->addCharacteristic(addBLECharacteristic(mfC));
|
||||
if (!hwVersion.empty()) {
|
||||
BLECharacteristic *hwvC =
|
||||
new BLECharacteristic(BLEUUID((uint16_t)ESP_GATT_UUID_HW_VERSION_STR), BLECharacteristic::PROPERTY_READ);
|
||||
hwvC->setValue(hwVersion);
|
||||
deviceInfoService->addCharacteristic(addBLECharacteristic(hwvC));
|
||||
}
|
||||
// SerialNumberCharacteristic.setValue("FIXME");
|
||||
// deviceInfoService->addCharacteristic(&SerialNumberCharacteristic);
|
||||
|
||||
// m_manufacturerCharacteristic = m_deviceInfoService->createCharacteristic((uint16_t) 0x2a29,
|
||||
// BLECharacteristic::PROPERTY_READ); m_manufacturerCharacteristic->setValue(name);
|
||||
|
||||
/* add these later?
|
||||
ESP_GATT_UUID_SYSTEM_ID
|
||||
*/
|
||||
|
||||
// caller must call service->start();
|
||||
return deviceInfoService;
|
||||
}
|
||||
|
||||
static BLECharacteristic *batteryLevelC;
|
||||
|
||||
/**
|
||||
@@ -122,7 +122,7 @@ BLEService *createBatteryService(BLEServer *server)
|
||||
addWithDesc(pBattery, batteryLevelC, "Percentage 0 - 100");
|
||||
batteryLevelC->addDescriptor(addBLEDescriptor(new BLE2902())); // Needed so clients can request notification
|
||||
|
||||
// I don't think we need to advertise this
|
||||
// I don't think we need to advertise this? and some phones only see the first thing advertised anyways...
|
||||
// server->getAdvertising()->addServiceUUID(pBattery->getUUID());
|
||||
pBattery->start();
|
||||
|
||||
@@ -135,8 +135,8 @@ BLEService *createBatteryService(BLEServer *server)
|
||||
*/
|
||||
void updateBatteryLevel(uint8_t level)
|
||||
{
|
||||
// Pretend to update battery levels - fixme do elsewhere
|
||||
if (batteryLevelC) {
|
||||
DEBUG_MSG("set BLE battery level %u\n", level);
|
||||
batteryLevelC->setValue(&level, 1);
|
||||
batteryLevelC->notify();
|
||||
}
|
||||
@@ -215,7 +215,7 @@ class MySecurity : public BLESecurityCallbacks
|
||||
|
||||
BLEServer *pServer;
|
||||
|
||||
BLEService *pDevInfo, *pUpdate;
|
||||
BLEService *pDevInfo, *pUpdate, *pBattery;
|
||||
|
||||
void deinitBLE()
|
||||
{
|
||||
@@ -223,11 +223,18 @@ void deinitBLE()
|
||||
|
||||
pServer->getAdvertising()->stop();
|
||||
|
||||
destroyUpdateService();
|
||||
if (pUpdate != NULL) {
|
||||
destroyUpdateService();
|
||||
|
||||
pUpdate->stop(); // we delete them below
|
||||
pUpdate->executeDelete();
|
||||
}
|
||||
|
||||
pBattery->stop();
|
||||
pBattery->executeDelete();
|
||||
|
||||
pUpdate->stop();
|
||||
pDevInfo->stop();
|
||||
pUpdate->stop(); // we delete them below
|
||||
pDevInfo->executeDelete();
|
||||
|
||||
// First shutdown bluetooth
|
||||
BLEDevice::deinit(false);
|
||||
@@ -235,14 +242,17 @@ void deinitBLE()
|
||||
// do not delete this - it is dynamically allocated, but only once - statically in BLEDevice
|
||||
// delete pServer->getAdvertising();
|
||||
|
||||
delete pUpdate;
|
||||
if (pUpdate != NULL)
|
||||
delete pUpdate;
|
||||
delete pDevInfo;
|
||||
delete pBattery;
|
||||
delete pServer;
|
||||
|
||||
batteryLevelC = NULL; // Don't let anyone generate bogus notifies
|
||||
|
||||
for (int i = 0; i < numChars; i++)
|
||||
for (int i = 0; i < numChars; i++) {
|
||||
delete chars[i];
|
||||
}
|
||||
numChars = 0;
|
||||
|
||||
for (int i = 0; i < numDescs; i++)
|
||||
@@ -273,18 +283,21 @@ BLEServer *initBLE(StartBluetoothPinScreenCallback startBtPinScreen, StopBluetoo
|
||||
|
||||
pDevInfo = createDeviceInfomationService(pServer, hwVendor, swVersion, hwVersion);
|
||||
|
||||
// We now let users create the battery service only if they really want (not all devices have a battery)
|
||||
// BLEService *pBattery = createBatteryService(pServer);
|
||||
pBattery = createBatteryService(pServer);
|
||||
|
||||
#define BLE_SOFTWARE_UPDATE
|
||||
#ifdef BLE_SOFTWARE_UPDATE
|
||||
pUpdate = createUpdateService(pServer, hwVendor, swVersion,
|
||||
hwVersion); // We need to advertise this so our android ble scan operation can see it
|
||||
|
||||
pUpdate->start();
|
||||
#endif
|
||||
|
||||
// It seems only one service can be advertised - so for now don't advertise our updater
|
||||
// pServer->getAdvertising()->addServiceUUID(pUpdate->getUUID());
|
||||
|
||||
// start all our services (do this after creating all of them)
|
||||
pDevInfo->start();
|
||||
pUpdate->start();
|
||||
|
||||
// FIXME turn on this restriction only after the device is paired with a phone
|
||||
// advert->setScanFilter(false, true); // We let anyone scan for us (FIXME, perhaps only allow that until we are paired with a
|
||||
@@ -293,7 +306,11 @@ BLEServer *initBLE(StartBluetoothPinScreenCallback startBtPinScreen, StopBluetoo
|
||||
static BLESecurity security; // static to avoid allocs
|
||||
BLESecurity *pSecurity = &security;
|
||||
pSecurity->setCapability(ESP_IO_CAP_OUT);
|
||||
|
||||
// FIXME - really should be ESP_LE_AUTH_REQ_SC_BOND but it seems there is a bug right now causing that bonding info to be lost
|
||||
// occasionally?
|
||||
pSecurity->setAuthenticationMode(ESP_LE_AUTH_REQ_SC_BOND);
|
||||
|
||||
pSecurity->setInitEncryptionKey(ESP_BLE_ENC_KEY_MASK | ESP_BLE_ID_KEY_MASK);
|
||||
|
||||
return pServer;
|
||||
|
||||
@@ -35,5 +35,8 @@ 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;
|
||||
|
||||
@@ -41,39 +41,6 @@ class BluetoothPhoneAPI : public PhoneAPI
|
||||
|
||||
BluetoothPhoneAPI *bluetoothPhoneAPI;
|
||||
|
||||
class ProtobufCharacteristic : public CallbackCharacteristic
|
||||
{
|
||||
const pb_msgdesc_t *fields;
|
||||
void *my_struct;
|
||||
|
||||
public:
|
||||
ProtobufCharacteristic(const char *uuid, uint32_t btprops, const pb_msgdesc_t *_fields, void *_my_struct)
|
||||
: CallbackCharacteristic(uuid, btprops), fields(_fields), my_struct(_my_struct)
|
||||
{
|
||||
setCallbacks(this);
|
||||
}
|
||||
|
||||
void onRead(BLECharacteristic *c)
|
||||
{
|
||||
size_t numbytes = pb_encode_to_bytes(trBytes, sizeof(trBytes), fields, my_struct);
|
||||
DEBUG_MSG("pbread from %s returns %d bytes\n", c->getUUID().toString().c_str(), numbytes);
|
||||
c->setValue(trBytes, numbytes);
|
||||
}
|
||||
|
||||
void onWrite(BLECharacteristic *c) { writeToDest(c, my_struct); }
|
||||
|
||||
protected:
|
||||
/// like onWrite, but we provide an different destination to write to, for use by subclasses that
|
||||
/// want to optionally ignore parts of writes.
|
||||
/// returns true for success
|
||||
bool writeToDest(BLECharacteristic *c, void *dest)
|
||||
{
|
||||
// dumpCharacteristic(pCharacteristic);
|
||||
std::string src = c->getValue();
|
||||
DEBUG_MSG("pbwrite to %s of %d bytes\n", c->getUUID().toString().c_str(), src.length());
|
||||
return pb_decode_from_bytes((const uint8_t *)src.c_str(), src.length(), fields, dest);
|
||||
}
|
||||
};
|
||||
|
||||
class ToRadioCharacteristic : public CallbackCharacteristic
|
||||
{
|
||||
@@ -82,8 +49,6 @@ class ToRadioCharacteristic : public CallbackCharacteristic
|
||||
|
||||
void onWrite(BLECharacteristic *c)
|
||||
{
|
||||
DEBUG_MSG("Got on write\n");
|
||||
|
||||
bluetoothPhoneAPI->handleToRadio(c->getData(), c->getValue().length());
|
||||
}
|
||||
};
|
||||
@@ -166,6 +131,7 @@ void stopMeshBluetoothService()
|
||||
{
|
||||
assert(meshService);
|
||||
meshService->stop();
|
||||
meshService->executeDelete();
|
||||
}
|
||||
|
||||
void destroyMeshBluetoothService()
|
||||
|
||||
@@ -4,6 +4,8 @@
|
||||
#include "configuration.h"
|
||||
#include "main.h"
|
||||
#include "power.h"
|
||||
#include "sleep.h"
|
||||
#include "utils.h"
|
||||
#include "target_specific.h"
|
||||
|
||||
bool bluetoothOn;
|
||||
@@ -74,6 +76,15 @@ 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();
|
||||
@@ -154,12 +165,31 @@ void axp192Init()
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
static void printBLEinfo() {
|
||||
int dev_num = esp_ble_get_bond_device_num();
|
||||
|
||||
esp_ble_bond_dev_t *dev_list = (esp_ble_bond_dev_t *)malloc(sizeof(esp_ble_bond_dev_t) * dev_num);
|
||||
esp_ble_get_bond_device_list(&dev_num, dev_list);
|
||||
for (int i = 0; i < dev_num; i++) {
|
||||
// esp_ble_remove_bond_device(dev_list[i].bd_addr);
|
||||
}
|
||||
|
||||
} */
|
||||
|
||||
void esp32Setup()
|
||||
{
|
||||
uint32_t seed = esp_random();
|
||||
DEBUG_MSG("Setting random seed %u\n", seed);
|
||||
randomSeed(seed); // ESP docs say this is fairly random
|
||||
|
||||
DEBUG_MSG("Total heap: %d\n", ESP.getHeapSize());
|
||||
DEBUG_MSG("Free heap: %d\n", ESP.getFreeHeap());
|
||||
DEBUG_MSG("Total PSRAM: %d\n", ESP.getPsramSize());
|
||||
DEBUG_MSG("Free PSRAM: %d\n", ESP.getFreePsram());
|
||||
|
||||
// enableModemSleep();
|
||||
|
||||
#ifdef AXP192_SLAVE_ADDRESS
|
||||
axp192Init();
|
||||
#endif
|
||||
@@ -185,15 +215,6 @@ uint32_t axpDebugRead()
|
||||
Periodic axpDebugOutput(axpDebugRead);
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Per @spattinson
|
||||
* MIN_BAT_MILLIVOLTS seems high. Typical 18650 are different chemistry to LiPo, even for LiPos that chart seems a bit off, other
|
||||
* charts put 3690mV at about 30% for a lipo, for 18650 i think 10% remaining iis in the region of 3.2-3.3V. Reference 1st graph
|
||||
* in [this test report](https://lygte-info.dk/review/batteries2012/Samsung%20INR18650-30Q%203000mAh%20%28Pink%29%20UK.html)
|
||||
* looking at the red line - discharge at 0.2A - he gets a capacity of 2900mah, 90% of 2900 = 2610, that point in the graph looks
|
||||
* to be a shade above 3.2V
|
||||
*/
|
||||
#define MIN_BAT_MILLIVOLTS 3250 // millivolts. 10% per https://blog.ampow.com/lipo-voltage-chart/
|
||||
|
||||
/// loop code specific to ESP32 targets
|
||||
void esp32Loop()
|
||||
|
||||
@@ -1,16 +1,30 @@
|
||||
#pragma once
|
||||
|
||||
// The FreeRTOS includes are in a different directory on ESP32 and I can't figure out how to make that work with platformio gcc options
|
||||
// so this is my quick hack to make things work
|
||||
// The FreeRTOS includes are in a different directory on ESP32 and I can't figure out how to make that work with platformio gcc
|
||||
// options so this is my quick hack to make things work
|
||||
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
#define HAS_FREE_RTOS
|
||||
#include <freertos/FreeRTOS.h>
|
||||
#include <freertos/task.h>
|
||||
#include <freertos/semphr.h>
|
||||
#include <freertos/queue.h>
|
||||
#include <freertos/semphr.h>
|
||||
#include <freertos/task.h>
|
||||
#else
|
||||
// not yet supported on cubecell
|
||||
#ifndef CubeCell_BoardPlus
|
||||
#define HAS_FREE_RTOS
|
||||
#include <FreeRTOS.h>
|
||||
#include <task.h>
|
||||
#include <semphr.h>
|
||||
#include <queue.h>
|
||||
#include <semphr.h>
|
||||
#include <task.h>
|
||||
#else
|
||||
|
||||
#include <Arduino.h>
|
||||
|
||||
typedef uint32_t TickType_t;
|
||||
typedef uint32_t BaseType_t;
|
||||
|
||||
#define portMAX_DELAY UINT32_MAX
|
||||
|
||||
#endif
|
||||
#endif
|
||||
@@ -8,6 +8,9 @@
|
||||
void perhapsSetRTC(const struct timeval *tv);
|
||||
void perhapsSetRTC(struct tm &t);
|
||||
|
||||
// Generate a string representation of DOP
|
||||
const char *getDOPString(uint32_t dop);
|
||||
|
||||
/// Return time since 1970 in secs. Until we have a GPS lock we will be returning time based at zero
|
||||
uint32_t getTime();
|
||||
|
||||
@@ -31,6 +34,8 @@ class GPS : public Observable<void *>
|
||||
public:
|
||||
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)
|
||||
|
||||
bool isConnected = false; // Do we have a GPS we are talking to
|
||||
|
||||
virtual ~GPS() {}
|
||||
|
||||
@@ -53,9 +53,13 @@ void NEMAGPS::loop()
|
||||
latitude = toDegInt(loc.lat);
|
||||
longitude = toDegInt(loc.lng);
|
||||
}
|
||||
// Diminution of precision (an accuracy metric) is reported in 10^2 units, so we need to scale down when we use it
|
||||
if(reader.hdop.isValid()) {
|
||||
dop = reader.hdop.value();
|
||||
}
|
||||
|
||||
// expect gps pos lat=37.520825, lon=-122.309162, alt=158
|
||||
DEBUG_MSG("new NEMA GPS pos lat=%f, lon=%f, alt=%d\n", latitude * 1e-7, longitude * 1e-7, altitude);
|
||||
DEBUG_MSG("new NEMA GPS pos lat=%f, lon=%f, alt=%d, hdop=%f\n", latitude * 1e-7, longitude * 1e-7, altitude, dop * 1e-2);
|
||||
|
||||
hasValidLocation = (latitude != 0) || (longitude != 0); // bogus lat lon is reported as 0,0
|
||||
if (hasValidLocation)
|
||||
|
||||
@@ -86,38 +86,41 @@ void UBloxGPS::doTask()
|
||||
// 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();
|
||||
// DEBUG_MSG("fix type %d\n", fixtype);
|
||||
fixtype = ublox.getFixType(0);
|
||||
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()) {
|
||||
if (ublox.getT(0)) {
|
||||
/* Convert to unix time
|
||||
The Unix epoch (or Unix time or POSIX time or Unix timestamp) is the number of seconds that have elapsed since January 1, 1970
|
||||
(midnight UTC/GMT), not counting leap seconds (in ISO 8601: 1970-01-01T00:00:00Z).
|
||||
*/
|
||||
struct tm t;
|
||||
t.tm_sec = ublox.getSecond();
|
||||
t.tm_min = ublox.getMinute();
|
||||
t.tm_hour = ublox.getHour();
|
||||
t.tm_mday = ublox.getDay();
|
||||
t.tm_mon = ublox.getMonth() - 1;
|
||||
t.tm_year = ublox.getYear() - 1900;
|
||||
t.tm_sec = ublox.getSecond(0);
|
||||
t.tm_min = ublox.getMinute(0);
|
||||
t.tm_hour = ublox.getHour(0);
|
||||
t.tm_mday = ublox.getDay(0);
|
||||
t.tm_mon = ublox.getMonth(0) - 1;
|
||||
t.tm_year = ublox.getYear(0) - 1900;
|
||||
t.tm_isdst = false;
|
||||
perhapsSetRTC(t);
|
||||
}
|
||||
|
||||
if ((fixtype >= 3 && fixtype <= 4) && ublox.getP()) // rd fixes only
|
||||
if ((fixtype >= 3 && fixtype <= 4) && ublox.getP(0)) // rd fixes only
|
||||
{
|
||||
// we only notify if position has changed
|
||||
latitude = ublox.getLatitude();
|
||||
longitude = ublox.getLongitude();
|
||||
altitude = ublox.getAltitude() / 1000; // in mm convert to meters
|
||||
DEBUG_MSG("new gps pos lat=%f, lon=%f, alt=%d\n", latitude * 1e-7, longitude * 1e-7, altitude);
|
||||
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);
|
||||
|
||||
hasValidLocation = (latitude != 0) || (longitude != 0); // bogus lat lon is reported as 0,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);
|
||||
if (hasValidLocation) {
|
||||
wantNewLocation = false;
|
||||
notifyObservers(NULL);
|
||||
|
||||
@@ -4,7 +4,12 @@ const uint8_t SATELLITE_IMAGE[] PROGMEM = {0x00, 0x08, 0x00, 0x1C, 0x00, 0x0E, 0
|
||||
0xF8, 0x00, 0xF0, 0x01, 0xE0, 0x03, 0xC8, 0x01, 0x9C, 0x54,
|
||||
0x0E, 0x52, 0x07, 0x48, 0x02, 0x26, 0x00, 0x10, 0x00, 0x0E};
|
||||
|
||||
const
|
||||
const uint8_t imgUSB[] PROGMEM = { 0x60, 0x60, 0x30, 0x18, 0x18, 0x18, 0x24, 0x42, 0x42, 0x42, 0x42, 0x7E, 0x24, 0x24, 0x24, 0x3C };
|
||||
const uint8_t imgPower[] PROGMEM = { 0x40, 0x40, 0x40, 0x58, 0x48, 0x08, 0x08, 0x08, 0x1C, 0x22, 0x22, 0x41, 0x7F, 0x22, 0x22, 0x22 };
|
||||
const uint8_t imgUser[] PROGMEM = { 0x3C, 0x42, 0x99, 0xA5, 0xA5, 0x99, 0x42, 0x3C };
|
||||
const uint8_t imgPositionEmpty[] PROGMEM = { 0x20, 0x30, 0x28, 0x24, 0x42, 0xFF };
|
||||
const uint8_t imgPositionSolid[] PROGMEM = { 0x20, 0x30, 0x38, 0x3C, 0x7E, 0xFF };
|
||||
|
||||
#include "icon.xbm"
|
||||
|
||||
// We now programmatically draw our compass
|
||||
|
||||
15
src/lock.cpp
15
src/lock.cpp
@@ -5,6 +5,7 @@
|
||||
namespace meshtastic
|
||||
{
|
||||
|
||||
#ifdef configUSE_PREEMPTION
|
||||
Lock::Lock()
|
||||
{
|
||||
handle = xSemaphoreCreateBinary();
|
||||
@@ -21,6 +22,20 @@ void Lock::unlock()
|
||||
{
|
||||
assert(xSemaphoreGive(handle));
|
||||
}
|
||||
#else
|
||||
Lock::Lock()
|
||||
{
|
||||
}
|
||||
|
||||
void Lock::lock()
|
||||
{
|
||||
}
|
||||
|
||||
void Lock::unlock()
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
LockGuard::LockGuard(Lock *lock) : lock(lock)
|
||||
{
|
||||
|
||||
@@ -25,7 +25,9 @@ class Lock
|
||||
void unlock();
|
||||
|
||||
private:
|
||||
#ifdef configUSE_PREEMPTION
|
||||
SemaphoreHandle_t handle;
|
||||
#endif
|
||||
};
|
||||
|
||||
// RAII lock guard.
|
||||
|
||||
102
src/main.cpp
102
src/main.cpp
@@ -33,14 +33,24 @@
|
||||
#include "power.h"
|
||||
// #include "rom/rtc.h"
|
||||
#include "DSRRouter.h"
|
||||
#include "debug.h"
|
||||
#include "main.h"
|
||||
#include "screen.h"
|
||||
#include "sleep.h"
|
||||
#include <Wire.h>
|
||||
#include <OneButton.h>
|
||||
// #include <driver/rtc_io.h>
|
||||
|
||||
#ifndef NO_ESP32
|
||||
#include "BluetoothUtil.h"
|
||||
#include "WiFi.h"
|
||||
#endif
|
||||
|
||||
#include "RF95Interface.h"
|
||||
#include "SX1262Interface.h"
|
||||
|
||||
#ifdef NRF52_SERIES
|
||||
#include "variant.h"
|
||||
#endif
|
||||
|
||||
// We always create a screen object, but we only init it if we find the hardware
|
||||
@@ -116,11 +126,41 @@ static uint32_t ledBlinker()
|
||||
|
||||
Periodic ledPeriodic(ledBlinker);
|
||||
|
||||
#include "RF95Interface.h"
|
||||
#include "SX1262Interface.h"
|
||||
// Prepare for button presses
|
||||
#ifdef BUTTON_PIN
|
||||
OneButton userButton;
|
||||
#endif
|
||||
#ifdef BUTTON_PIN_ALT
|
||||
OneButton userButtonAlt;
|
||||
#endif
|
||||
void userButtonPressed() {
|
||||
powerFSM.trigger(EVENT_PRESS);
|
||||
}
|
||||
void userButtonPressedLong(){
|
||||
screen.adjustBrightness();
|
||||
}
|
||||
|
||||
#ifdef NO_ESP32
|
||||
#include "variant.h"
|
||||
#ifndef NO_ESP32
|
||||
void initWifi()
|
||||
{
|
||||
strcpy(radioConfig.preferences.wifi_ssid, "geeksville");
|
||||
strcpy(radioConfig.preferences.wifi_password, "xxx");
|
||||
if (radioConfig.has_preferences) {
|
||||
const char *wifiName = radioConfig.preferences.wifi_ssid;
|
||||
|
||||
if (*wifiName) {
|
||||
const char *wifiPsw = radioConfig.preferences.wifi_password;
|
||||
if (radioConfig.preferences.wifi_ap_mode) {
|
||||
// DEBUG_MSG("STARTING WIFI AP: ssid=%s, ok=%d\n", wifiName, WiFi.softAP(wifiName, wifiPsw));
|
||||
} else {
|
||||
// WiFi.mode(WIFI_MODE_STA);
|
||||
DEBUG_MSG("JOINING WIFI: ssid=%s\n", wifiName);
|
||||
// WiFi.begin(wifiName, wifiPsw);
|
||||
}
|
||||
}
|
||||
} else
|
||||
DEBUG_MSG("Not using WIFI\n");
|
||||
}
|
||||
#endif
|
||||
|
||||
void setup()
|
||||
@@ -151,12 +191,21 @@ void setup()
|
||||
#else
|
||||
Wire.begin();
|
||||
#endif
|
||||
// i2c still busted on new board
|
||||
#ifndef ARDUINO_NRF52840_PPR
|
||||
scanI2Cdevice();
|
||||
#endif
|
||||
|
||||
// Buttons & LED
|
||||
#ifdef BUTTON_PIN
|
||||
pinMode(BUTTON_PIN, INPUT_PULLUP);
|
||||
digitalWrite(BUTTON_PIN, 1);
|
||||
userButton = OneButton(BUTTON_PIN, true, true);
|
||||
userButton.attachClick(userButtonPressed);
|
||||
userButton.attachDuringLongPress(userButtonPressedLong);
|
||||
#endif
|
||||
#ifdef BUTTON_PIN_ALT
|
||||
userButtonAlt = OneButton(BUTTON_PIN_ALT, true, true);
|
||||
userButtonAlt.attachClick(userButtonPressed);
|
||||
userButton.attachDuringLongPress(userButtonPressedLong);
|
||||
#endif
|
||||
#ifdef LED_PIN
|
||||
pinMode(LED_PIN, OUTPUT);
|
||||
@@ -207,6 +256,11 @@ void setup()
|
||||
|
||||
service.init();
|
||||
|
||||
#ifndef NO_ESP32
|
||||
// Must be after we init the service, because the wifi settings are loaded by NodeDB (oops)
|
||||
initWifi();
|
||||
#endif
|
||||
|
||||
#ifdef SX1262_ANT_SW
|
||||
// make analog PA vs not PA switch on SX1262 eval board work properly
|
||||
pinMode(SX1262_ANT_SW, OUTPUT);
|
||||
@@ -233,7 +287,7 @@ void setup()
|
||||
new SimRadio();
|
||||
#endif
|
||||
|
||||
if (!rIf->init())
|
||||
if (!rIf || !rIf->init())
|
||||
recordCriticalError(ErrNoRadio);
|
||||
else
|
||||
router.addInterface(rIf);
|
||||
@@ -282,29 +336,17 @@ void loop()
|
||||
DEBUG_PORT.loop(); // Send/receive protobufs over the serial port
|
||||
#endif
|
||||
|
||||
// heap_caps_check_integrity_all(true); // FIXME - disable this expensive check
|
||||
|
||||
#ifndef NO_ESP32
|
||||
esp32Loop();
|
||||
#endif
|
||||
|
||||
#ifdef BUTTON_PIN
|
||||
// if user presses button for more than 3 secs, discard our network prefs and reboot (FIXME, use a debounce lib instead of
|
||||
// this boilerplate)
|
||||
static bool wasPressed = false;
|
||||
|
||||
if (!digitalRead(BUTTON_PIN)) {
|
||||
if (!wasPressed) { // just started a new press
|
||||
DEBUG_MSG("pressing\n");
|
||||
|
||||
// doLightSleep();
|
||||
// esp_pm_dump_locks(stdout); // FIXME, do this someplace better
|
||||
wasPressed = true;
|
||||
|
||||
powerFSM.trigger(EVENT_PRESS);
|
||||
}
|
||||
} else if (wasPressed) {
|
||||
// we just did a release
|
||||
wasPressed = false;
|
||||
}
|
||||
userButton.tick();
|
||||
#endif
|
||||
#ifdef BUTTON_PIN_ALT
|
||||
userButtonAlt.tick();
|
||||
#endif
|
||||
|
||||
// Show boot screen for first 3 seconds, then switch to normal operation.
|
||||
@@ -314,12 +356,18 @@ void loop()
|
||||
showingBootScreen = false;
|
||||
}
|
||||
|
||||
#ifdef DEBUG_STACK
|
||||
static uint32_t lastPrint = 0;
|
||||
if (millis() - lastPrint > 10 * 1000L) {
|
||||
lastPrint = millis();
|
||||
meshtastic::printThreadInfo("main");
|
||||
}
|
||||
#endif
|
||||
|
||||
// Update the screen last, after we've figured out what to show.
|
||||
screen.debug()->setNodeNumbersStatus(nodeDB.getNumOnlineNodes(), nodeDB.getNumNodes());
|
||||
screen.debug()->setChannelNameStatus(channelSettings.name);
|
||||
screen.debug()->setPowerStatus(powerStatus);
|
||||
// TODO(#4): use something based on hdop to show GPS "signal" strength.
|
||||
screen.debug()->setGPSStatus(gps->hasLock() ? "good" : "bad");
|
||||
|
||||
// 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.
|
||||
|
||||
@@ -5,12 +5,80 @@
|
||||
|
||||
#include "PointerQueue.h"
|
||||
|
||||
template <class T> class Allocator
|
||||
{
|
||||
|
||||
public:
|
||||
virtual ~Allocator() {}
|
||||
|
||||
/// Return a queable object which has been prefilled with zeros. Panic if no buffer is available
|
||||
/// Note: this method is safe to call from regular OR ISR code
|
||||
T *allocZeroed()
|
||||
{
|
||||
T *p = allocZeroed(0);
|
||||
|
||||
assert(p); // FIXME panic instead
|
||||
return p;
|
||||
}
|
||||
|
||||
/// Return a queable object which has been prefilled with zeros - allow timeout to wait for available buffers (you probably
|
||||
/// don't want this version).
|
||||
T *allocZeroed(TickType_t maxWait)
|
||||
{
|
||||
T *p = alloc(maxWait);
|
||||
|
||||
if (p)
|
||||
memset(p, 0, sizeof(T));
|
||||
return p;
|
||||
}
|
||||
|
||||
/// Return a queable object which is a copy of some other object
|
||||
T *allocCopy(const T &src, TickType_t maxWait = portMAX_DELAY)
|
||||
{
|
||||
T *p = alloc(maxWait);
|
||||
assert(p);
|
||||
|
||||
if (p)
|
||||
*p = src;
|
||||
return p;
|
||||
}
|
||||
|
||||
/// Return a buffer for use by others
|
||||
virtual void release(T *p) = 0;
|
||||
|
||||
protected:
|
||||
// Alloc some storage
|
||||
virtual T *alloc(TickType_t maxWait) = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* An allocator that just uses regular free/malloc
|
||||
*/
|
||||
template <class T> class MemoryDynamic : public Allocator<T>
|
||||
{
|
||||
public:
|
||||
/// Return a buffer for use by others
|
||||
virtual void release(T *p)
|
||||
{
|
||||
assert(p);
|
||||
free(p);
|
||||
}
|
||||
|
||||
protected:
|
||||
// Alloc some storage
|
||||
virtual T *alloc(TickType_t maxWait)
|
||||
{
|
||||
T *p = (T *)malloc(sizeof(T));
|
||||
assert(p);
|
||||
return p;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* A pool based allocator
|
||||
*
|
||||
* Eventually this routine will even be safe for ISR use...
|
||||
*/
|
||||
template <class T> class MemoryPool
|
||||
template <class T> class MemoryPool : public Allocator<T>
|
||||
{
|
||||
PointerQueue<T> dead;
|
||||
|
||||
@@ -30,39 +98,8 @@ template <class T> class MemoryPool
|
||||
|
||||
~MemoryPool() { delete[] buf; }
|
||||
|
||||
/// Return a queable object which has been prefilled with zeros. Panic if no buffer is available
|
||||
/// Note: this method is safe to call from regular OR ISR code
|
||||
T *allocZeroed()
|
||||
{
|
||||
T *p = allocZeroed(0);
|
||||
|
||||
assert(p); // FIXME panic instead
|
||||
return p;
|
||||
}
|
||||
|
||||
/// Return a queable object which has been prefilled with zeros - allow timeout to wait for available buffers (you probably
|
||||
/// don't want this version).
|
||||
T *allocZeroed(TickType_t maxWait)
|
||||
{
|
||||
T *p = dead.dequeuePtr(maxWait);
|
||||
|
||||
if (p)
|
||||
memset(p, 0, sizeof(T));
|
||||
return p;
|
||||
}
|
||||
|
||||
/// Return a queable object which is a copy of some other object
|
||||
T *allocCopy(const T &src, TickType_t maxWait = portMAX_DELAY)
|
||||
{
|
||||
T *p = dead.dequeuePtr(maxWait);
|
||||
|
||||
if (p)
|
||||
*p = src;
|
||||
return p;
|
||||
}
|
||||
|
||||
/// Return a buffer for use by others
|
||||
void release(T *p)
|
||||
virtual void release(T *p)
|
||||
{
|
||||
assert(dead.enqueue(p, 0));
|
||||
assert(p >= buf &&
|
||||
@@ -70,6 +107,7 @@ template <class T> class MemoryPool
|
||||
maxElements); // sanity check to make sure a programmer didn't free something that didn't come from this pool
|
||||
}
|
||||
|
||||
#ifdef HAS_FREE_RTOS
|
||||
/// Return a buffer from an ISR, if higherPriWoken is set to true you have some work to do ;-)
|
||||
void releaseFromISR(T *p, BaseType_t *higherPriWoken)
|
||||
{
|
||||
@@ -78,4 +116,10 @@ template <class T> class MemoryPool
|
||||
(size_t)(p - buf) <
|
||||
maxElements); // sanity check to make sure a programmer didn't free something that didn't come from this pool
|
||||
}
|
||||
#endif
|
||||
|
||||
protected:
|
||||
/// Return a queable object which has been prefilled with zeros - allow timeout to wait for available buffers (you
|
||||
/// probably don't want this version).
|
||||
virtual T *alloc(TickType_t maxWait) { return dead.dequeuePtr(maxWait); }
|
||||
};
|
||||
|
||||
@@ -11,6 +11,8 @@
|
||||
#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
|
||||
|
||||
/*
|
||||
receivedPacketQueue - this is a queue of messages we've received from the mesh, which we are keeping to deliver to the phone.
|
||||
@@ -160,7 +162,7 @@ int MeshService::handleFromRadio(const MeshPacket *mp)
|
||||
|
||||
// If we veto a received User packet, we don't put it into the DB or forward it to the phone (to prevent confusing it)
|
||||
if (mp) {
|
||||
DEBUG_MSG("Forwarding to phone, from=0x%x, rx_time=%u\n", mp->from, mp->rx_time);
|
||||
printPacket("Forwarding to phone", mp);
|
||||
nodeDB.updateFrom(*mp); // update our DB state based off sniffing every RX packet from the radio
|
||||
|
||||
fromNum++;
|
||||
@@ -280,6 +282,8 @@ void MeshService::sendOurPosition(NodeNum dest, bool wantReplies)
|
||||
sendToMesh(p);
|
||||
}
|
||||
|
||||
|
||||
|
||||
int MeshService::onGPSChanged(void *unused)
|
||||
{
|
||||
// DEBUG_MSG("got gps notify\n");
|
||||
@@ -298,6 +302,10 @@ int MeshService::onGPSChanged(void *unused)
|
||||
pos.time = getValidTime();
|
||||
}
|
||||
|
||||
// Include our current battery voltage in our position announcement
|
||||
pos.battery_level = powerStatus.batteryChargePercent;
|
||||
updateBatteryLevel(pos.battery_level);
|
||||
|
||||
// We limit our GPS broadcasts to a max rate
|
||||
static uint32_t lastGpsSend;
|
||||
uint32_t now = millis();
|
||||
|
||||
@@ -29,4 +29,4 @@ typedef uint32_t PacketId; // A packet sequence number
|
||||
typedef int ErrorCode;
|
||||
|
||||
/// Alloc and free packets to our global, ISR safe pool
|
||||
extern MemoryPool<MeshPacket> packetPool;
|
||||
extern Allocator<MeshPacket> &packetPool;
|
||||
@@ -3,7 +3,6 @@
|
||||
#include <assert.h>
|
||||
|
||||
#include "FS.h"
|
||||
#include "SPIFFS.h"
|
||||
|
||||
#include "CryptoEngine.h"
|
||||
#include "GPS.h"
|
||||
@@ -20,7 +19,7 @@
|
||||
NodeDB nodeDB;
|
||||
|
||||
// we have plenty of ram so statically alloc this tempbuf (for now)
|
||||
DeviceState devicestate;
|
||||
EXT_RAM_ATTR DeviceState devicestate;
|
||||
MyNodeInfo &myNodeInfo = devicestate.my_node;
|
||||
RadioConfig &radioConfig = devicestate.radio;
|
||||
ChannelSettings &channelSettings = radioConfig.channel_settings;
|
||||
@@ -30,15 +29,18 @@ DeviceState versions used to be defined in the .proto file but really only this
|
||||
#define here.
|
||||
*/
|
||||
|
||||
#define DEVICESTATE_CUR_VER 9
|
||||
#define DEVICESTATE_CUR_VER 10
|
||||
#define DEVICESTATE_MIN_VER DEVICESTATE_CUR_VER
|
||||
|
||||
#ifndef NO_ESP32
|
||||
// ESP32 version
|
||||
#include "SPIFFS.h"
|
||||
#define FS SPIFFS
|
||||
#define FSBegin() FS.begin(true)
|
||||
#define FILE_O_WRITE "w"
|
||||
#define FILE_O_READ "r"
|
||||
#else
|
||||
// NRF52 version
|
||||
#include "InternalFileSystem.h"
|
||||
#define FS InternalFS
|
||||
#define FSBegin() FS.begin()
|
||||
@@ -101,15 +103,22 @@ void NodeDB::resetRadioConfig()
|
||||
crypto->setKey(channelSettings.psk.size, channelSettings.psk.bytes);
|
||||
|
||||
// temp hack for quicker testing
|
||||
/*
|
||||
radioConfig.preferences.screen_on_secs = 30;
|
||||
radioConfig.preferences.wait_bluetooth_secs = 30;
|
||||
radioConfig.preferences.position_broadcast_secs = 15;
|
||||
*/
|
||||
// devicestate.no_save = true;
|
||||
if (devicestate.no_save) {
|
||||
DEBUG_MSG("***** DEVELOPMENT MODE - DO NOT RELEASE *****\n");
|
||||
|
||||
// Sleep quite frequently to stress test the BLE comms, broadcast position every 6 mins
|
||||
radioConfig.preferences.screen_on_secs = 30;
|
||||
radioConfig.preferences.wait_bluetooth_secs = 30;
|
||||
radioConfig.preferences.position_broadcast_secs = 6 * 60;
|
||||
radioConfig.preferences.ls_secs = 60;
|
||||
}
|
||||
}
|
||||
|
||||
void NodeDB::init()
|
||||
void NodeDB::installDefaultDeviceState()
|
||||
{
|
||||
memset(&devicestate, 0, sizeof(devicestate));
|
||||
|
||||
// init our devicestate with valid flags so protobuf writing/reading will work
|
||||
devicestate.has_my_node = true;
|
||||
devicestate.has_radio = true;
|
||||
@@ -117,14 +126,14 @@ void NodeDB::init()
|
||||
devicestate.radio.has_channel_settings = true;
|
||||
devicestate.radio.has_preferences = true;
|
||||
devicestate.node_db_count = 0;
|
||||
devicestate.receive_queue_count = 0;
|
||||
devicestate.receive_queue_count = 0; // Not yet implemented FIXME
|
||||
|
||||
resetRadioConfig();
|
||||
|
||||
// default to no GPS, until one has been found by probing
|
||||
myNodeInfo.has_gps = false;
|
||||
myNodeInfo.message_timeout_msec = FLOOD_EXPIRE_TIME;
|
||||
myNodeInfo.min_app_version = 167;
|
||||
myNodeInfo.min_app_version = 172;
|
||||
generatePacketId(); // FIXME - ugly way to init current_packet_id;
|
||||
|
||||
// Init our blank owner info to reasonable defaults
|
||||
@@ -137,12 +146,17 @@ void NodeDB::init()
|
||||
pickNewNodeNum(); // Note: we will repick later, just in case the settings are corrupted, but we need a valid
|
||||
// owner.short_name now
|
||||
sprintf(owner.long_name, "Unknown %02x%02x", ourMacAddr[4], ourMacAddr[5]);
|
||||
sprintf(owner.short_name, "?%02X", myNodeInfo.my_node_num & 0xff);
|
||||
sprintf(owner.short_name, "?%02X", (unsigned)(myNodeInfo.my_node_num & 0xff));
|
||||
}
|
||||
|
||||
void NodeDB::init()
|
||||
{
|
||||
installDefaultDeviceState();
|
||||
|
||||
if (!FSBegin()) // FIXME - do this in main?
|
||||
{
|
||||
DEBUG_MSG("ERROR filesystem mount Failed\n");
|
||||
// FIXME - report failure to phone
|
||||
assert(0); // FIXME - report failure to phone
|
||||
}
|
||||
|
||||
// saveToDisk();
|
||||
@@ -208,7 +222,7 @@ const char *preftmp = "/db.proto.tmp";
|
||||
void NodeDB::loadFromDisk()
|
||||
{
|
||||
#ifdef FS
|
||||
static DeviceState scratch;
|
||||
// static DeviceState scratch; We no longer read into a tempbuf because this structure is 15KB of valuable RAM
|
||||
|
||||
auto f = FS.open(preffile);
|
||||
if (f) {
|
||||
@@ -217,16 +231,17 @@ void NodeDB::loadFromDisk()
|
||||
|
||||
// DEBUG_MSG("Preload channel name=%s\n", channelSettings.name);
|
||||
|
||||
memset(&scratch, 0, sizeof(scratch));
|
||||
if (!pb_decode(&stream, DeviceState_fields, &scratch)) {
|
||||
memset(&devicestate, 0, sizeof(devicestate));
|
||||
if (!pb_decode(&stream, DeviceState_fields, &devicestate)) {
|
||||
DEBUG_MSG("Error: can't decode protobuf %s\n", PB_GET_ERROR(&stream));
|
||||
installDefaultDeviceState(); // Our in RAM copy might now be corrupt
|
||||
// FIXME - report failure to phone
|
||||
} else {
|
||||
if (scratch.version < DEVICESTATE_MIN_VER)
|
||||
if (devicestate.version < DEVICESTATE_MIN_VER) {
|
||||
DEBUG_MSG("Warn: devicestate is old, discarding\n");
|
||||
else {
|
||||
DEBUG_MSG("Loaded saved preferences version %d\n", scratch.version);
|
||||
devicestate = scratch;
|
||||
installDefaultDeviceState();
|
||||
} else {
|
||||
DEBUG_MSG("Loaded saved preferences version %d\n", devicestate.version);
|
||||
}
|
||||
|
||||
// DEBUG_MSG("Postload channel name=%s\n", channelSettings.name);
|
||||
@@ -236,6 +251,7 @@ void NodeDB::loadFromDisk()
|
||||
} else {
|
||||
DEBUG_MSG("No saved preferences found\n");
|
||||
}
|
||||
|
||||
#else
|
||||
DEBUG_MSG("ERROR: Filesystem not implemented\n");
|
||||
#endif
|
||||
@@ -244,32 +260,36 @@ void NodeDB::loadFromDisk()
|
||||
void NodeDB::saveToDisk()
|
||||
{
|
||||
#ifdef FS
|
||||
auto f = FS.open(preftmp, FILE_O_WRITE);
|
||||
if (f) {
|
||||
DEBUG_MSG("Writing preferences\n");
|
||||
if (!devicestate.no_save) {
|
||||
auto f = FS.open(preftmp, FILE_O_WRITE);
|
||||
if (f) {
|
||||
DEBUG_MSG("Writing preferences\n");
|
||||
|
||||
pb_ostream_t stream = {&writecb, &f, SIZE_MAX, 0};
|
||||
pb_ostream_t stream = {&writecb, &f, SIZE_MAX, 0};
|
||||
|
||||
// DEBUG_MSG("Presave channel name=%s\n", channelSettings.name);
|
||||
// DEBUG_MSG("Presave channel name=%s\n", channelSettings.name);
|
||||
|
||||
devicestate.version = DEVICESTATE_CUR_VER;
|
||||
if (!pb_encode(&stream, DeviceState_fields, &devicestate)) {
|
||||
DEBUG_MSG("Error: can't write protobuf %s\n", PB_GET_ERROR(&stream));
|
||||
// FIXME - report failure to phone
|
||||
devicestate.version = DEVICESTATE_CUR_VER;
|
||||
if (!pb_encode(&stream, DeviceState_fields, &devicestate)) {
|
||||
DEBUG_MSG("Error: can't write protobuf %s\n", PB_GET_ERROR(&stream));
|
||||
// FIXME - report failure to phone
|
||||
|
||||
f.close();
|
||||
f.close();
|
||||
} else {
|
||||
// Success - replace the old file
|
||||
f.close();
|
||||
|
||||
// brief window of risk here ;-)
|
||||
if (!FS.remove(preffile))
|
||||
DEBUG_MSG("Warning: Can't remove old pref file\n");
|
||||
if (!FS.rename(preftmp, preffile))
|
||||
DEBUG_MSG("Error: can't rename new pref file\n");
|
||||
}
|
||||
} else {
|
||||
// Success - replace the old file
|
||||
f.close();
|
||||
|
||||
// brief window of risk here ;-)
|
||||
if (!FS.remove(preffile))
|
||||
DEBUG_MSG("Warning: Can't remove old pref file\n");
|
||||
if (!FS.rename(preftmp, preffile))
|
||||
DEBUG_MSG("Error: can't rename new pref file\n");
|
||||
DEBUG_MSG("ERROR: can't write prefs\n"); // FIXME report to app
|
||||
}
|
||||
} else {
|
||||
DEBUG_MSG("ERROR: can't write prefs\n"); // FIXME report to app
|
||||
DEBUG_MSG("***** DEVELOPMENT MODE - DO NOT RELEASE - not saving to flash *****\n");
|
||||
}
|
||||
#else
|
||||
DEBUG_MSG("ERROR filesystem not implemented\n");
|
||||
|
||||
@@ -93,6 +93,9 @@ class NodeDB
|
||||
|
||||
/// read our db from flash
|
||||
void loadFromDisk();
|
||||
|
||||
/// Reinit device state from scratch (not loading from disk)
|
||||
void installDefaultDeviceState();
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -2,8 +2,6 @@
|
||||
#include "configuration.h"
|
||||
#include "mesh-pb-constants.h"
|
||||
|
||||
|
||||
|
||||
PacketHistory::PacketHistory()
|
||||
{
|
||||
recentPackets.reserve(MAX_NUM_NODES); // Prealloc the worst case # of records - to prevent heap fragmentation
|
||||
@@ -48,7 +46,7 @@ bool PacketHistory::wasSeenRecently(const MeshPacket *p, bool withUpdate)
|
||||
r.sender = p->from;
|
||||
r.rxTimeMsec = now;
|
||||
recentPackets.push_back(r);
|
||||
DEBUG_MSG("Adding packet record for fr=0x%x,to=0x%x,id=%d\n", p->from, p->to, p->id);
|
||||
printPacket("Adding packet record", p);
|
||||
}
|
||||
|
||||
return false;
|
||||
|
||||
@@ -35,9 +35,8 @@ class PacketRecordOrderFunction
|
||||
// If the timer ticks have rolled over the difference between times will be _enormous_. Handle that case specially
|
||||
uint32_t t1 = p1.rxTimeMsec, t2 = p2.rxTimeMsec;
|
||||
|
||||
if (abs(t1 - t2) >
|
||||
UINT32_MAX /
|
||||
2) { // time must have rolled over, swap them because the new little number is 'bigger' than the old big number
|
||||
if (t1 - t2 > UINT32_MAX / 2) {
|
||||
// time must have rolled over, swap them because the new little number is 'bigger' than the old big number
|
||||
t1 = t2;
|
||||
t2 = p1.rxTimeMsec;
|
||||
}
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
#include "MeshService.h"
|
||||
#include "NodeDB.h"
|
||||
#include "PowerFSM.h"
|
||||
#include "RadioInterface.h"
|
||||
#include "GPS.h"
|
||||
#include <assert.h>
|
||||
|
||||
PhoneAPI::PhoneAPI()
|
||||
@@ -42,8 +44,8 @@ void PhoneAPI::handleToRadio(const uint8_t *buf, size_t bufLength)
|
||||
if (pb_decode_from_bytes(buf, bufLength, ToRadio_fields, &toRadioScratch)) {
|
||||
switch (toRadioScratch.which_variant) {
|
||||
case ToRadio_packet_tag: {
|
||||
// If our phone is sending a position, see if we can use it to set our RTC
|
||||
MeshPacket &p = toRadioScratch.variant.packet;
|
||||
printPacket("PACKET FROM PHONE", &p);
|
||||
service.handleToRadio(p);
|
||||
break;
|
||||
}
|
||||
@@ -77,6 +79,7 @@ void PhoneAPI::handleToRadio(const uint8_t *buf, size_t bufLength)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the next packet we want to send to the phone, or NULL if no such packet is available.
|
||||
*
|
||||
@@ -91,8 +94,12 @@ void PhoneAPI::handleToRadio(const uint8_t *buf, size_t bufLength)
|
||||
*/
|
||||
size_t PhoneAPI::getFromRadio(uint8_t *buf)
|
||||
{
|
||||
if (!available())
|
||||
if (!available()) {
|
||||
DEBUG_MSG("getFromRadio, !available\n");
|
||||
return false;
|
||||
} else {
|
||||
DEBUG_MSG("getFromRadio, state=%d\n", state);
|
||||
}
|
||||
|
||||
// In case we send a FromRadio packet
|
||||
memset(&fromRadioScratch, 0, sizeof(fromRadioScratch));
|
||||
@@ -103,6 +110,7 @@ size_t PhoneAPI::getFromRadio(uint8_t *buf)
|
||||
break;
|
||||
|
||||
case STATE_SEND_MY_INFO:
|
||||
myNodeInfo.has_gps = gps && gps->isConnected; // Update with latest GPS connect info
|
||||
fromRadioScratch.which_variant = FromRadio_my_info_tag;
|
||||
fromRadioScratch.variant.my_info = myNodeInfo;
|
||||
state = STATE_SEND_RADIO;
|
||||
|
||||
@@ -18,6 +18,7 @@ template <class T> class PointerQueue : public TypedQueue<T *>
|
||||
return this->dequeue(&p, maxWait) ? p : nullptr;
|
||||
}
|
||||
|
||||
#ifdef HAS_FREE_RTOS
|
||||
// returns a ptr or null if the queue was empty
|
||||
T *dequeuePtrFromISR(BaseType_t *higherPriWoken)
|
||||
{
|
||||
@@ -25,4 +26,5 @@ template <class T> class PointerQueue : public TypedQueue<T *>
|
||||
|
||||
return this->dequeueFromISR(&p, higherPriWoken) ? p : nullptr;
|
||||
}
|
||||
#endif
|
||||
};
|
||||
|
||||
@@ -24,7 +24,53 @@ separated by 2.16 MHz with respect to the adjacent channels. Channel zero starts
|
||||
// 1kb was too small
|
||||
#define RADIO_STACK_SIZE 4096
|
||||
|
||||
RadioInterface::RadioInterface() : txQueue(MAX_TX_QUEUE)
|
||||
void printPacket(const char *prefix, const MeshPacket *p)
|
||||
{
|
||||
DEBUG_MSG("%s (id=0x%08x Fr0x%02x To0x%02x, WantAck%d, HopLim%d", prefix, p->id, p->from & 0xff, p->to & 0xff, p->want_ack,
|
||||
p->hop_limit);
|
||||
if (p->which_payload == MeshPacket_decoded_tag) {
|
||||
auto &s = p->decoded;
|
||||
switch (s.which_payload) {
|
||||
case SubPacket_data_tag:
|
||||
DEBUG_MSG(" Payload:Data");
|
||||
break;
|
||||
case SubPacket_position_tag:
|
||||
DEBUG_MSG(" Payload:Position");
|
||||
break;
|
||||
case SubPacket_user_tag:
|
||||
DEBUG_MSG(" Payload:User");
|
||||
break;
|
||||
case 0:
|
||||
DEBUG_MSG(" Payload:None");
|
||||
break;
|
||||
default:
|
||||
DEBUG_MSG(" Payload:%d", s.which_payload);
|
||||
break;
|
||||
}
|
||||
if (s.want_response)
|
||||
DEBUG_MSG(" WANTRESP");
|
||||
|
||||
if (s.source != 0)
|
||||
DEBUG_MSG(" source=%08x", s.source);
|
||||
|
||||
if (s.dest != 0)
|
||||
DEBUG_MSG(" dest=%08x", s.dest);
|
||||
|
||||
if (s.which_ack == SubPacket_success_id_tag)
|
||||
DEBUG_MSG(" successId=%08x", s.ack.success_id);
|
||||
else if (s.which_ack == SubPacket_fail_id_tag)
|
||||
DEBUG_MSG(" failId=%08x", s.ack.fail_id);
|
||||
} else {
|
||||
DEBUG_MSG(" encrypted");
|
||||
}
|
||||
|
||||
if (p->rx_time != 0) {
|
||||
DEBUG_MSG(" rxtime=%u", p->rx_time);
|
||||
}
|
||||
DEBUG_MSG(")\n");
|
||||
}
|
||||
|
||||
RadioInterface::RadioInterface()
|
||||
{
|
||||
assert(sizeof(PacketHeader) == 4 || sizeof(PacketHeader) == 16); // make sure the compiler did what we expected
|
||||
|
||||
|
||||
@@ -59,7 +59,6 @@ class RadioInterface : protected NotifiedWorkerThread
|
||||
|
||||
protected:
|
||||
MeshPacket *sendingPacket = NULL; // The packet we are currently sending
|
||||
PointerQueue<MeshPacket> txQueue;
|
||||
uint32_t lastTxStart = 0L;
|
||||
|
||||
/**
|
||||
@@ -163,3 +162,6 @@ class SimRadio : public RadioInterface
|
||||
/// \return true if initialisation succeeded.
|
||||
virtual bool init() { return true; }
|
||||
};
|
||||
|
||||
/// Debug printing for packets
|
||||
void printPacket(const char *prefix, const MeshPacket *p);
|
||||
@@ -114,8 +114,8 @@ bool RadioLibInterface::canSendImmediately()
|
||||
/// bluetooth comms code. If the txmit queue is empty it might return an error
|
||||
ErrorCode RadioLibInterface::send(MeshPacket *p)
|
||||
{
|
||||
DEBUG_MSG("enqueuing for send on mesh fr=0x%x,to=0x%x,id=%d (txGood=%d,rxGood=%d,rxBad=%d)\n", p->from, p->to, p->id, txGood,
|
||||
rxGood, rxBad);
|
||||
printPacket("enqueuing for send", p);
|
||||
DEBUG_MSG("txGood=%d,rxGood=%d,rxBad=%d\n", txGood, rxGood, rxBad);
|
||||
ErrorCode res = txQueue.enqueue(p, 0) ? ERRNO_OK : ERRNO_UNKNOWN;
|
||||
|
||||
if (res != ERRNO_OK) { // we weren't able to queue it, so we must drop it to prevent leaks
|
||||
@@ -134,7 +134,7 @@ bool RadioLibInterface::canSleep()
|
||||
{
|
||||
bool res = txQueue.isEmpty();
|
||||
if (!res) // only print debug messages if we are vetoing sleep
|
||||
DEBUG_MSG("radio wait to sleep, txEmpty=%d\n", txQueue.isEmpty());
|
||||
DEBUG_MSG("radio wait to sleep, txEmpty=%d\n", res);
|
||||
|
||||
return res;
|
||||
}
|
||||
@@ -173,11 +173,13 @@ void RadioLibInterface::loop()
|
||||
case ISR_TX:
|
||||
handleTransmitInterrupt();
|
||||
startReceive();
|
||||
// DEBUG_MSG("tx complete - starting timer\n");
|
||||
startTransmitTimer();
|
||||
break;
|
||||
case ISR_RX:
|
||||
handleReceiveInterrupt();
|
||||
startReceive();
|
||||
// DEBUG_MSG("rx complete - starting timer\n");
|
||||
startTransmitTimer();
|
||||
break;
|
||||
case TRANSMIT_DELAY_COMPLETED:
|
||||
@@ -192,6 +194,8 @@ void RadioLibInterface::loop()
|
||||
assert(txp);
|
||||
startSend(txp);
|
||||
}
|
||||
} else {
|
||||
// DEBUG_MSG("done with txqueue\n");
|
||||
}
|
||||
break;
|
||||
default:
|
||||
@@ -216,7 +220,7 @@ void RadioLibInterface::startTransmitTimer(bool withDelay)
|
||||
uint32_t delay =
|
||||
!withDelay ? 1 : random(MIN_TX_WAIT_MSEC, MAX_TX_WAIT_MSEC); // See documentation for loop() wrt these values
|
||||
// DEBUG_MSG("xmit timer %d\n", delay);
|
||||
|
||||
// DEBUG_MSG("delaying %u\n", delay);
|
||||
setPeriod(delay);
|
||||
}
|
||||
}
|
||||
@@ -233,7 +237,7 @@ void RadioLibInterface::completeSending()
|
||||
{
|
||||
if (sendingPacket) {
|
||||
txGood++;
|
||||
DEBUG_MSG("Completed sending to=0x%x, id=%u\n", sendingPacket->to, sendingPacket->id);
|
||||
printPacket("Completed sending", sendingPacket);
|
||||
|
||||
// We are done sending that packet, release it
|
||||
packetPool.release(sendingPacket);
|
||||
@@ -287,7 +291,7 @@ void RadioLibInterface::handleReceiveInterrupt()
|
||||
memcpy(mp->encrypted.bytes, payload, payloadLen);
|
||||
mp->encrypted.size = payloadLen;
|
||||
|
||||
DEBUG_MSG("Lora RX interrupt from=0x%x, id=%u\n", mp->from, mp->id);
|
||||
printPacket("Lora RX", mp);
|
||||
|
||||
deliverToReceiver(mp);
|
||||
}
|
||||
@@ -297,9 +301,11 @@ void RadioLibInterface::handleReceiveInterrupt()
|
||||
/** start an immediate transmit */
|
||||
void RadioLibInterface::startSend(MeshPacket *txp)
|
||||
{
|
||||
DEBUG_MSG("Starting low level send from=0x%x, to=0x%x, id=%u, want_ack=%d\n", txp->from, txp->to, txp->id, txp->want_ack);
|
||||
printPacket("Starting low level send", txp);
|
||||
setStandby(); // Cancel any already in process receives
|
||||
|
||||
configHardwareForSend(); // must be after setStandby
|
||||
|
||||
size_t numbytes = beginSending(txp);
|
||||
|
||||
int res = iface->startTransmit(radiobuf, numbytes);
|
||||
|
||||
@@ -3,6 +3,10 @@
|
||||
#include "PeriodicTask.h"
|
||||
#include "RadioInterface.h"
|
||||
|
||||
#ifdef CubeCell_BoardPlus
|
||||
#define RADIOLIB_SOFTWARE_SERIAL_UNSUPPORTED
|
||||
#endif
|
||||
|
||||
#include <RadioLib.h>
|
||||
|
||||
// ESP32 has special rules about ISR code
|
||||
@@ -29,6 +33,8 @@ class RadioLibInterface : public RadioInterface, private PeriodicTask
|
||||
*/
|
||||
uint32_t rxBad = 0, rxGood = 0, txGood = 0;
|
||||
|
||||
PointerQueue<MeshPacket> txQueue = PointerQueue<MeshPacket>(MAX_TX_QUEUE);
|
||||
|
||||
protected:
|
||||
float bw = 125;
|
||||
uint8_t sf = 9;
|
||||
@@ -89,9 +95,6 @@ class RadioLibInterface : public RadioInterface, private PeriodicTask
|
||||
virtual void startReceive() = 0;
|
||||
|
||||
private:
|
||||
/** start an immediate transmit */
|
||||
void startSend(MeshPacket *txp);
|
||||
|
||||
/** if we have something waiting to send, start a short random timer so we can come check for collision before actually doing
|
||||
* the transmit
|
||||
*
|
||||
@@ -106,12 +109,20 @@ class RadioLibInterface : public RadioInterface, private PeriodicTask
|
||||
|
||||
virtual void doTask();
|
||||
|
||||
/** start an immediate transmit
|
||||
* This method is virtual so subclasses can hook as needed, subclasses should not call directly
|
||||
*/
|
||||
virtual void startSend(MeshPacket *txp);
|
||||
|
||||
protected:
|
||||
/// Initialise the Driver transport hardware and software.
|
||||
/// Make sure the Driver is properly configured before calling init().
|
||||
/// \return true if initialisation succeeded.
|
||||
virtual bool init();
|
||||
|
||||
|
||||
/** Do any hardware setup needed on entry into send configuration for the radio. Subclasses can customize */
|
||||
virtual void configHardwareForSend() {}
|
||||
|
||||
/**
|
||||
* Convert our modemConfig enum into wf, sf, etc...
|
||||
*
|
||||
|
||||
@@ -27,7 +27,7 @@ ErrorCode ReliableRouter::send(MeshPacket *p)
|
||||
bool ReliableRouter::shouldFilterReceived(const MeshPacket *p)
|
||||
{
|
||||
if (p->to == NODENUM_BROADCAST && p->from == getNodeNum()) {
|
||||
DEBUG_MSG("Received someone rebroadcasting for us fr=0x%x,to=0x%x,id=%d\n", p->from, p->to, p->id);
|
||||
printPacket("Rx someone rebroadcasting for us", p);
|
||||
|
||||
// We are seeing someone rebroadcast one of our broadcast attempts.
|
||||
// If this is the first time we saw this, cancel any retransmissions we have queued up and generate an internal ack for
|
||||
|
||||
@@ -48,7 +48,7 @@ struct PendingPacket {
|
||||
PendingPacket() {}
|
||||
PendingPacket(MeshPacket *p);
|
||||
|
||||
void setNextTx() { nextTxMsec = millis() + random(20 * 1000, 22 * 1000); }
|
||||
void setNextTx() { nextTxMsec = millis() + random(20 * 1000L, 22 * 1000L); }
|
||||
};
|
||||
|
||||
class GlobalPacketIdHashFunction
|
||||
|
||||
@@ -19,18 +19,29 @@
|
||||
4 // max number of packets destined to our queue, we dispatch packets quickly so it doesn't need to be big
|
||||
|
||||
// I think this is right, one packet for each of the three fifos + one packet being currently assembled for TX or RX
|
||||
// And every TX packet might have a retransmission packet or an ack alive at any moment
|
||||
#define MAX_PACKETS \
|
||||
(MAX_RX_TOPHONE + MAX_RX_FROMRADIO + MAX_TX_QUEUE + \
|
||||
(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)
|
||||
|
||||
MemoryPool<MeshPacket> packetPool(MAX_PACKETS);
|
||||
static MemoryPool<MeshPacket> staticPool(MAX_PACKETS);
|
||||
// static MemoryDynamic<MeshPacket> staticPool;
|
||||
|
||||
Allocator<MeshPacket> &packetPool = staticPool;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* Currently we only allow one interface, that may change in the future
|
||||
*/
|
||||
Router::Router() : fromRadioQueue(MAX_RX_FROMRADIO) {}
|
||||
Router::Router() : fromRadioQueue(MAX_RX_FROMRADIO)
|
||||
{
|
||||
// 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));
|
||||
DEBUG_MSG("Size of MeshPacket %d\n", sizeof(MeshPacket)); */
|
||||
}
|
||||
|
||||
/**
|
||||
* do idle processing
|
||||
@@ -146,8 +157,8 @@ ErrorCode Router::send(MeshPacket *p)
|
||||
*/
|
||||
void Router::sniffReceived(const MeshPacket *p)
|
||||
{
|
||||
DEBUG_MSG("FIXME-update-db Sniffing packet fr=0x%x,to=0x%x,id=%d\n", p->from, p->to, p->id);
|
||||
// FIXME, update nodedb
|
||||
DEBUG_MSG("FIXME-update-db Sniffing packet\n");
|
||||
// FIXME, update nodedb here for any packet that passes through us
|
||||
}
|
||||
|
||||
bool Router::perhapsDecode(MeshPacket *p)
|
||||
@@ -198,7 +209,7 @@ void Router::handleReceived(MeshPacket *p)
|
||||
sniffReceived(p);
|
||||
|
||||
if (p->to == NODENUM_BROADCAST || p->to == getNodeNum()) {
|
||||
DEBUG_MSG("Notifying observers of received packet fr=0x%x,to=0x%x,id=%d\n", p->from, p->to, p->id);
|
||||
printPacket("Delivering rx packet", p);
|
||||
notifyPacketReceived.notifyObservers(p);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,7 +14,19 @@ bool SX1262Interface::init()
|
||||
{
|
||||
RadioLibInterface::init();
|
||||
|
||||
float tcxoVoltage = 0; // None - we use an XTAL
|
||||
#ifdef SX1262_RXEN // set not rx or tx mode
|
||||
pinMode(SX1262_RXEN, OUTPUT);
|
||||
#endif
|
||||
#ifdef SX1262_TXEN
|
||||
pinMode(SX1262_TXEN, OUTPUT);
|
||||
#endif
|
||||
|
||||
#ifndef SX1262_E22
|
||||
float tcxoVoltage = 0; // None - we use an XTAL
|
||||
#else
|
||||
float tcxoVoltage =
|
||||
1.8; // E22 uses DIO3 to power tcxo per https://github.com/jgromes/RadioLib/issues/12#issuecomment-520695575
|
||||
#endif
|
||||
bool useRegulatorLDO = false; // Seems to depend on the connection to pin 9/DCC_SW - if an inductor DCDC?
|
||||
|
||||
applyModemConfig();
|
||||
@@ -23,6 +35,12 @@ bool SX1262Interface::init()
|
||||
int res = lora.begin(freq, bw, sf, cr, syncWord, power, currentLimit, preambleLength, tcxoVoltage, useRegulatorLDO);
|
||||
DEBUG_MSG("LORA init result %d\n", res);
|
||||
|
||||
#ifdef SX1262_TXEN
|
||||
// lora.begin sets Dio2 as RF switch control, which is not true if we are manually controlling RX and TX
|
||||
if (res == ERR_NONE)
|
||||
res = lora.setDio2AsRfSwitch(false);
|
||||
#endif
|
||||
|
||||
if (res == ERR_NONE)
|
||||
res = lora.setCRC(SX126X_LORA_CRC_ON);
|
||||
|
||||
@@ -81,6 +99,13 @@ void SX1262Interface::setStandby()
|
||||
int err = lora.standby();
|
||||
assert(err == ERR_NONE);
|
||||
|
||||
#ifdef SX1262_RXEN // we have RXEN/TXEN control - turn off RX and TX power
|
||||
digitalWrite(SX1262_RXEN, LOW);
|
||||
#endif
|
||||
#ifdef SX1262_TXEN
|
||||
digitalWrite(SX1262_TXEN, LOW);
|
||||
#endif
|
||||
|
||||
isReceiving = false; // If we were receiving, not any more
|
||||
disableInterrupt();
|
||||
completeSending(); // If we were sending, not anymore
|
||||
@@ -94,6 +119,18 @@ void SX1262Interface::addReceiveMetadata(MeshPacket *mp)
|
||||
mp->rx_snr = lora.getSNR();
|
||||
}
|
||||
|
||||
/** start an immediate transmit
|
||||
* We override to turn on transmitter power as needed.
|
||||
*/
|
||||
void SX1262Interface::configHardwareForSend()
|
||||
{
|
||||
#ifdef SX1262_TXEN // we have RXEN/TXEN control - turn on TX power / off RX power
|
||||
digitalWrite(SX1262_TXEN, HIGH);
|
||||
#endif
|
||||
|
||||
RadioLibInterface::configHardwareForSend();
|
||||
}
|
||||
|
||||
// For power draw measurements, helpful to force radio to stay sleeping
|
||||
// #define SLEEP_ONLY
|
||||
|
||||
@@ -102,7 +139,13 @@ void SX1262Interface::startReceive()
|
||||
#ifdef SLEEP_ONLY
|
||||
sleep();
|
||||
#else
|
||||
|
||||
setStandby();
|
||||
|
||||
#ifdef SX1262_RXEN // we have RXEN/TXEN control - turn on RX power / off TX power
|
||||
digitalWrite(SX1262_RXEN, HIGH);
|
||||
#endif
|
||||
|
||||
// int err = lora.startReceive();
|
||||
int err = lora.startReceiveDutyCycleAuto(); // We use a 32 bit preamble so this should save some power by letting radio sit in
|
||||
// standby mostly.
|
||||
|
||||
@@ -43,6 +43,12 @@ class SX1262Interface : public RadioLibInterface
|
||||
* Start waiting to receive a message
|
||||
*/
|
||||
virtual void startReceive();
|
||||
|
||||
/**
|
||||
* We override to turn on transmitter power as needed.
|
||||
*/
|
||||
virtual void configHardwareForSend();
|
||||
|
||||
/**
|
||||
* Add SNR data to received messages
|
||||
*/
|
||||
@@ -51,5 +57,4 @@ class SX1262Interface : public RadioLibInterface
|
||||
virtual void setStandby();
|
||||
|
||||
private:
|
||||
|
||||
};
|
||||
@@ -5,6 +5,8 @@
|
||||
|
||||
#include "freertosinc.h"
|
||||
|
||||
#ifdef HAS_FREE_RTOS
|
||||
|
||||
/**
|
||||
* A wrapper for freertos queues. Note: each element object should be small
|
||||
* and POD (Plain Old Data type) as elements are memcpied by value.
|
||||
@@ -35,3 +37,45 @@ template <class T> class TypedQueue
|
||||
|
||||
bool dequeueFromISR(T *p, BaseType_t *higherPriWoken) { return xQueueReceiveFromISR(h, p, higherPriWoken); }
|
||||
};
|
||||
|
||||
#else
|
||||
|
||||
#include <queue>
|
||||
|
||||
/**
|
||||
* A wrapper for freertos queues. Note: each element object should be small
|
||||
* and POD (Plain Old Data type) as elements are memcpied by value.
|
||||
*/
|
||||
template <class T> class TypedQueue
|
||||
{
|
||||
std::queue<T> q;
|
||||
|
||||
public:
|
||||
TypedQueue(int maxElements) {}
|
||||
|
||||
// int numFree() { return uxQueueSpacesAvailable(h); }
|
||||
|
||||
bool isEmpty() { return q.empty(); }
|
||||
|
||||
bool enqueue(T x, TickType_t maxWait = portMAX_DELAY)
|
||||
{
|
||||
q.push(x);
|
||||
return true;
|
||||
}
|
||||
|
||||
// bool enqueueFromISR(T x, BaseType_t *higherPriWoken) { return xQueueSendToBackFromISR(h, &x, higherPriWoken) == pdTRUE; }
|
||||
|
||||
bool dequeue(T *p, TickType_t maxWait = portMAX_DELAY)
|
||||
{
|
||||
if (isEmpty())
|
||||
return false;
|
||||
else {
|
||||
*p = q.front();
|
||||
q.pop();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// bool dequeueFromISR(T *p, BaseType_t *higherPriWoken) { return xQueueReceiveFromISR(h, p, higherPriWoken); }
|
||||
};
|
||||
#endif
|
||||
|
||||
@@ -8,7 +8,9 @@
|
||||
#define member_size(type, member) sizeof(((type *)0)->member)
|
||||
|
||||
/// max number of packets which can be waiting for delivery to android - note, this value comes from mesh.options protobuf
|
||||
#define MAX_RX_TOPHONE (member_size(DeviceState, receive_queue) / member_size(DeviceState, receive_queue[0]))
|
||||
// FIXME - max_count is actually 32 but we save/load this as one long string of preencoded MeshPacket bytes - not a big array in RAM
|
||||
// #define MAX_RX_TOPHONE (member_size(DeviceState, receive_queue) / member_size(DeviceState, receive_queue[0]))
|
||||
#define MAX_RX_TOPHONE 32
|
||||
|
||||
/// max number of nodes allowed in the mesh
|
||||
#define MAX_NUM_NODES (member_size(DeviceState, node_db) / member_size(DeviceState, node_db[0]))
|
||||
|
||||
@@ -39,7 +39,7 @@ PB_BIND(NodeInfo, NodeInfo, AUTO)
|
||||
PB_BIND(MyNodeInfo, MyNodeInfo, AUTO)
|
||||
|
||||
|
||||
PB_BIND(DeviceState, DeviceState, 4)
|
||||
PB_BIND(DeviceState, DeviceState, 2)
|
||||
|
||||
|
||||
PB_BIND(DebugString, DebugString, 2)
|
||||
|
||||
@@ -101,8 +101,9 @@ typedef struct _RadioConfig_UserPreferences {
|
||||
uint32_t sds_secs;
|
||||
uint32_t ls_secs;
|
||||
uint32_t min_wake_secs;
|
||||
bool keep_all_packets;
|
||||
bool promiscuous_mode;
|
||||
char wifi_ssid[33];
|
||||
char wifi_password[64];
|
||||
bool wifi_ap_mode;
|
||||
pb_size_t ignore_incoming_count;
|
||||
uint32_t ignore_incoming[3];
|
||||
} RadioConfig_UserPreferences;
|
||||
@@ -183,10 +184,11 @@ typedef struct _DeviceState {
|
||||
pb_size_t node_db_count;
|
||||
NodeInfo node_db[32];
|
||||
pb_size_t receive_queue_count;
|
||||
MeshPacket receive_queue[32];
|
||||
MeshPacket receive_queue[1];
|
||||
bool has_rx_text_message;
|
||||
MeshPacket rx_text_message;
|
||||
uint32_t version;
|
||||
bool no_save;
|
||||
} DeviceState;
|
||||
|
||||
typedef struct _FromRadio {
|
||||
@@ -241,10 +243,10 @@ typedef struct _ToRadio {
|
||||
#define MeshPacket_init_default {0, 0, 0, {SubPacket_init_default}, 0, 0, 0, 0, 0}
|
||||
#define ChannelSettings_init_default {0, _ChannelSettings_ModemConfig_MIN, {0, {0}}, ""}
|
||||
#define RadioConfig_init_default {false, RadioConfig_UserPreferences_init_default, false, ChannelSettings_init_default}
|
||||
#define RadioConfig_UserPreferences_init_default {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, {0, 0, 0}}
|
||||
#define RadioConfig_UserPreferences_init_default {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, "", "", 0, 0, {0, 0, 0}}
|
||||
#define NodeInfo_init_default {0, false, User_init_default, false, Position_init_default, 0, 0}
|
||||
#define MyNodeInfo_init_default {0, 0, 0, "", "", "", 0, 0, 0, 0, 0, 0, 0, 0}
|
||||
#define DeviceState_init_default {false, RadioConfig_init_default, false, MyNodeInfo_init_default, false, User_init_default, 0, {NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default}, 0, {MeshPacket_init_default, MeshPacket_init_default, MeshPacket_init_default, MeshPacket_init_default, MeshPacket_init_default, MeshPacket_init_default, MeshPacket_init_default, MeshPacket_init_default, MeshPacket_init_default, MeshPacket_init_default, MeshPacket_init_default, MeshPacket_init_default, MeshPacket_init_default, MeshPacket_init_default, MeshPacket_init_default, MeshPacket_init_default, MeshPacket_init_default, MeshPacket_init_default, MeshPacket_init_default, MeshPacket_init_default, MeshPacket_init_default, MeshPacket_init_default, MeshPacket_init_default, MeshPacket_init_default, MeshPacket_init_default, MeshPacket_init_default, MeshPacket_init_default, MeshPacket_init_default, MeshPacket_init_default, MeshPacket_init_default, MeshPacket_init_default, MeshPacket_init_default}, false, MeshPacket_init_default, 0}
|
||||
#define DeviceState_init_default {false, RadioConfig_init_default, false, MyNodeInfo_init_default, false, User_init_default, 0, {NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default}, 0, {MeshPacket_init_default}, false, MeshPacket_init_default, 0, 0}
|
||||
#define DebugString_init_default {""}
|
||||
#define FromRadio_init_default {0, 0, {MeshPacket_init_default}}
|
||||
#define ToRadio_init_default {0, {MeshPacket_init_default}}
|
||||
@@ -257,10 +259,10 @@ typedef struct _ToRadio {
|
||||
#define MeshPacket_init_zero {0, 0, 0, {SubPacket_init_zero}, 0, 0, 0, 0, 0}
|
||||
#define ChannelSettings_init_zero {0, _ChannelSettings_ModemConfig_MIN, {0, {0}}, ""}
|
||||
#define RadioConfig_init_zero {false, RadioConfig_UserPreferences_init_zero, false, ChannelSettings_init_zero}
|
||||
#define RadioConfig_UserPreferences_init_zero {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, {0, 0, 0}}
|
||||
#define RadioConfig_UserPreferences_init_zero {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, "", "", 0, 0, {0, 0, 0}}
|
||||
#define NodeInfo_init_zero {0, false, User_init_zero, false, Position_init_zero, 0, 0}
|
||||
#define MyNodeInfo_init_zero {0, 0, 0, "", "", "", 0, 0, 0, 0, 0, 0, 0, 0}
|
||||
#define DeviceState_init_zero {false, RadioConfig_init_zero, false, MyNodeInfo_init_zero, false, User_init_zero, 0, {NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero}, 0, {MeshPacket_init_zero, MeshPacket_init_zero, MeshPacket_init_zero, MeshPacket_init_zero, MeshPacket_init_zero, MeshPacket_init_zero, MeshPacket_init_zero, MeshPacket_init_zero, MeshPacket_init_zero, MeshPacket_init_zero, MeshPacket_init_zero, MeshPacket_init_zero, MeshPacket_init_zero, MeshPacket_init_zero, MeshPacket_init_zero, MeshPacket_init_zero, MeshPacket_init_zero, MeshPacket_init_zero, MeshPacket_init_zero, MeshPacket_init_zero, MeshPacket_init_zero, MeshPacket_init_zero, MeshPacket_init_zero, MeshPacket_init_zero, MeshPacket_init_zero, MeshPacket_init_zero, MeshPacket_init_zero, MeshPacket_init_zero, MeshPacket_init_zero, MeshPacket_init_zero, MeshPacket_init_zero, MeshPacket_init_zero}, false, MeshPacket_init_zero, 0}
|
||||
#define DeviceState_init_zero {false, RadioConfig_init_zero, false, MyNodeInfo_init_zero, false, User_init_zero, 0, {NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero}, 0, {MeshPacket_init_zero}, false, MeshPacket_init_zero, 0, 0}
|
||||
#define DebugString_init_zero {""}
|
||||
#define FromRadio_init_zero {0, 0, {MeshPacket_init_zero}}
|
||||
#define ToRadio_init_zero {0, {MeshPacket_init_zero}}
|
||||
@@ -308,8 +310,9 @@ typedef struct _ToRadio {
|
||||
#define RadioConfig_UserPreferences_sds_secs_tag 9
|
||||
#define RadioConfig_UserPreferences_ls_secs_tag 10
|
||||
#define RadioConfig_UserPreferences_min_wake_secs_tag 11
|
||||
#define RadioConfig_UserPreferences_keep_all_packets_tag 100
|
||||
#define RadioConfig_UserPreferences_promiscuous_mode_tag 101
|
||||
#define RadioConfig_UserPreferences_wifi_ssid_tag 12
|
||||
#define RadioConfig_UserPreferences_wifi_password_tag 13
|
||||
#define RadioConfig_UserPreferences_wifi_ap_mode_tag 14
|
||||
#define RadioConfig_UserPreferences_ignore_incoming_tag 102
|
||||
#define RouteDiscovery_route_tag 2
|
||||
#define User_id_tag 1
|
||||
@@ -351,6 +354,7 @@ typedef struct _ToRadio {
|
||||
#define DeviceState_receive_queue_tag 5
|
||||
#define DeviceState_version_tag 8
|
||||
#define DeviceState_rx_text_message_tag 7
|
||||
#define DeviceState_no_save_tag 9
|
||||
#define FromRadio_packet_tag 2
|
||||
#define FromRadio_my_info_tag 3
|
||||
#define FromRadio_node_info_tag 4
|
||||
@@ -456,8 +460,9 @@ X(a, STATIC, SINGULAR, UINT32, mesh_sds_timeout_secs, 8) \
|
||||
X(a, STATIC, SINGULAR, UINT32, sds_secs, 9) \
|
||||
X(a, STATIC, SINGULAR, UINT32, ls_secs, 10) \
|
||||
X(a, STATIC, SINGULAR, UINT32, min_wake_secs, 11) \
|
||||
X(a, STATIC, SINGULAR, BOOL, keep_all_packets, 100) \
|
||||
X(a, STATIC, SINGULAR, BOOL, promiscuous_mode, 101) \
|
||||
X(a, STATIC, SINGULAR, STRING, wifi_ssid, 12) \
|
||||
X(a, STATIC, SINGULAR, STRING, wifi_password, 13) \
|
||||
X(a, STATIC, SINGULAR, BOOL, wifi_ap_mode, 14) \
|
||||
X(a, STATIC, REPEATED, UINT32, ignore_incoming, 102)
|
||||
#define RadioConfig_UserPreferences_CALLBACK NULL
|
||||
#define RadioConfig_UserPreferences_DEFAULT NULL
|
||||
@@ -498,7 +503,8 @@ X(a, STATIC, OPTIONAL, MESSAGE, owner, 3) \
|
||||
X(a, STATIC, REPEATED, MESSAGE, node_db, 4) \
|
||||
X(a, STATIC, REPEATED, MESSAGE, receive_queue, 5) \
|
||||
X(a, STATIC, OPTIONAL, MESSAGE, rx_text_message, 7) \
|
||||
X(a, STATIC, SINGULAR, UINT32, version, 8)
|
||||
X(a, STATIC, SINGULAR, UINT32, version, 8) \
|
||||
X(a, STATIC, SINGULAR, BOOL, no_save, 9)
|
||||
#define DeviceState_CALLBACK NULL
|
||||
#define DeviceState_DEFAULT NULL
|
||||
#define DeviceState_radio_MSGTYPE RadioConfig
|
||||
@@ -592,11 +598,11 @@ extern const pb_msgdesc_t ManufacturingData_msg;
|
||||
#define SubPacket_size 274
|
||||
#define MeshPacket_size 313
|
||||
#define ChannelSettings_size 60
|
||||
#define RadioConfig_size 157
|
||||
#define RadioConfig_UserPreferences_size 93
|
||||
#define RadioConfig_size 253
|
||||
#define RadioConfig_UserPreferences_size 188
|
||||
#define NodeInfo_size 132
|
||||
#define MyNodeInfo_size 110
|
||||
#define DeviceState_size 15100
|
||||
#define DeviceState_size 5403
|
||||
#define DebugString_size 258
|
||||
#define FromRadio_size 322
|
||||
#define ToRadio_size 316
|
||||
|
||||
@@ -1,100 +0,0 @@
|
||||
#ifdef ARDUINO_NRF52840_PPR
|
||||
#include "PmuBQ25703A.h"
|
||||
#include <assert.h>
|
||||
|
||||
// Default address for device. Note, it is without read/write bit. When read with analyser,
|
||||
// this will appear 1 bit shifted to the left
|
||||
#define BQ25703ADevaddr 0xD6
|
||||
|
||||
const byte Lorro_BQ25703A::BQ25703Aaddr = BQ25703ADevaddr;
|
||||
|
||||
void PmuBQ25703A::init()
|
||||
{
|
||||
// Set the watchdog timer to not have a timeout
|
||||
regs.chargeOption0.set_WDTMR_ADJ(0);
|
||||
assert(writeRegEx(regs.chargeOption0)); // FIXME, instead log a critical hw failure and reboot
|
||||
delay(15); // FIXME, why are these delays required? - check datasheet
|
||||
|
||||
// Set the ADC on IBAT and PSYS to record values
|
||||
// When changing bitfield values, call the writeRegEx function
|
||||
// This is so you can change all the bits you want before sending out the byte.
|
||||
regs.chargeOption1.set_EN_IBAT(1);
|
||||
regs.chargeOption1.set_EN_PSYS(1);
|
||||
assert(writeRegEx(regs.chargeOption1));
|
||||
delay(15);
|
||||
|
||||
// Set ADC to make continuous readings. (uses more power)
|
||||
regs.aDCOption.set_ADC_CONV(1);
|
||||
// Set individual ADC registers to read. All have default off.
|
||||
regs.aDCOption.set_EN_ADC_VBUS(1);
|
||||
regs.aDCOption.set_EN_ADC_PSYS(1);
|
||||
regs.aDCOption.set_EN_ADC_IDCHG(1);
|
||||
regs.aDCOption.set_EN_ADC_ICHG(1);
|
||||
regs.aDCOption.set_EN_ADC_VSYS(1);
|
||||
regs.aDCOption.set_EN_ADC_VBAT(1);
|
||||
// Once bits have been twiddled, send bytes to device
|
||||
assert(writeRegEx(regs.aDCOption));
|
||||
delay(15);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
/*
|
||||
|
||||
|
||||
//Initialise the device and library
|
||||
Lorro_BQ25703A BQ25703A;
|
||||
|
||||
//Instantiate with reference to global set
|
||||
extern Lorro_BQ25703A::Regt BQ25703Areg;
|
||||
|
||||
uint32_t previousMillis;
|
||||
uint16_t loopInterval = 1000;
|
||||
|
||||
void setup() {
|
||||
|
||||
Serial.begin(115200);
|
||||
|
||||
|
||||
}
|
||||
|
||||
void loop() {
|
||||
|
||||
uint32_t currentMillis = millis();
|
||||
|
||||
if( currentMillis - previousMillis > loopInterval ){
|
||||
previousMillis = currentMillis;
|
||||
|
||||
Serial.print( "Voltage of VBUS: " );
|
||||
Serial.print( BQ25703Areg.aDCVBUSPSYS.get_VBUS() );
|
||||
Serial.println( "mV" );
|
||||
delay( 15 );
|
||||
|
||||
Serial.print( "System power usage: " );
|
||||
Serial.print( BQ25703Areg.aDCVBUSPSYS.get_sysPower() );
|
||||
Serial.println( "W" );
|
||||
delay( 15 );
|
||||
|
||||
Serial.print( "Voltage of VBAT: " );
|
||||
Serial.print( BQ25703Areg.aDCVSYSVBAT.get_VBAT() );
|
||||
Serial.println( "mV" );
|
||||
delay( 15 );
|
||||
|
||||
Serial.print( "Voltage of VSYS: " );
|
||||
Serial.print( BQ25703Areg.aDCVSYSVBAT.get_VSYS() );
|
||||
Serial.println( "mV" );
|
||||
delay( 15 );
|
||||
|
||||
Serial.print( "Charging current: " );
|
||||
Serial.print( BQ25703Areg.aDCIBAT.get_ICHG() );
|
||||
Serial.println( "mA" );
|
||||
delay( 15 );
|
||||
|
||||
Serial.print( "Voltage of VSYS: " );
|
||||
Serial.print( BQ25703Areg.aDCIBAT.get_IDCHG() );
|
||||
Serial.println( "mA" );
|
||||
delay( 15 );
|
||||
|
||||
}
|
||||
|
||||
}*/
|
||||
@@ -1,22 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <Lorro_BQ25703A.h>
|
||||
|
||||
class PmuBQ25703A : private Lorro_BQ25703A
|
||||
{
|
||||
Lorro_BQ25703A::Regt regs;
|
||||
|
||||
public:
|
||||
/**
|
||||
* Configure the PMU for our board
|
||||
*/
|
||||
void init();
|
||||
|
||||
// Methods to have a common API with AXP192
|
||||
bool isBatteryConnect() { return true; } // FIXME
|
||||
bool isVBUSPlug() { return true; }
|
||||
bool isChargeing() { return true; } // FIXME, intentional misspelling
|
||||
|
||||
/// battery voltage in mV
|
||||
int getBattVoltage() { return 3200; }
|
||||
};
|
||||
@@ -1,3 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
// FIXME - make a FS abstraction for NRF52
|
||||
@@ -36,15 +36,18 @@ class UC1701Spi : public OLEDDisplay
|
||||
};
|
||||
|
||||
#include "variant.h"
|
||||
|
||||
#ifdef ERC12864_CS
|
||||
#include <UC1701.h>
|
||||
static UC1701 lcd(PIN_SPI_SCK, PIN_SPI_MOSI, ERC12864_CS, ERC12864_CD);
|
||||
|
||||
void testLCD()
|
||||
{
|
||||
// PCD8544-compatible displays may have a different resolution...
|
||||
lcd.begin();
|
||||
|
||||
void testLCD() {
|
||||
// PCD8544-compatible displays may have a different resolution...
|
||||
lcd.begin();
|
||||
|
||||
// Write a piece of text on the first line...
|
||||
lcd.setCursor(0, 0);
|
||||
lcd.print("Hello, World!");
|
||||
}
|
||||
// Write a piece of text on the first line...
|
||||
lcd.setCursor(0, 0);
|
||||
lcd.print("Hello, World!");
|
||||
}
|
||||
#endif
|
||||
@@ -59,12 +59,6 @@ void setBluetoothEnable(bool on)
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef ARDUINO_NRF52840_PPR
|
||||
#include "PmuBQ25703A.h"
|
||||
|
||||
PmuBQ25703A pmu;
|
||||
#endif
|
||||
|
||||
void nrf52Setup()
|
||||
{
|
||||
|
||||
|
||||
15
src/power.h
15
src/power.h
@@ -1,5 +1,18 @@
|
||||
#pragma once
|
||||
|
||||
/**
|
||||
* Per @spattinson
|
||||
* MIN_BAT_MILLIVOLTS seems high. Typical 18650 are different chemistry to LiPo, even for LiPos that chart seems a bit off, other
|
||||
* charts put 3690mV at about 30% for a lipo, for 18650 i think 10% remaining iis in the region of 3.2-3.3V. Reference 1st graph
|
||||
* in [this test report](https://lygte-info.dk/review/batteries2012/Samsung%20INR18650-30Q%203000mAh%20%28Pink%29%20UK.html)
|
||||
* looking at the red line - discharge at 0.2A - he gets a capacity of 2900mah, 90% of 2900 = 2610, that point in the graph looks
|
||||
* to be a shade above 3.2V
|
||||
*/
|
||||
#define MIN_BAT_MILLIVOLTS 3250 // millivolts. 10% per https://blog.ampow.com/lipo-voltage-chart/
|
||||
|
||||
#define BAT_MILLIVOLTS_FULL 4100
|
||||
#define BAT_MILLIVOLTS_EMPTY 3500
|
||||
|
||||
namespace meshtastic
|
||||
{
|
||||
|
||||
@@ -9,6 +22,8 @@ struct PowerStatus {
|
||||
bool haveBattery;
|
||||
/// Battery voltage in mV, valid if haveBattery is true
|
||||
int batteryVoltageMv;
|
||||
/// Battery charge percentage, either read directly or estimated
|
||||
int batteryChargePercent;
|
||||
/// Whether USB is connected
|
||||
bool usb;
|
||||
/// Whether we are charging the battery
|
||||
|
||||
188
src/screen.cpp
188
src/screen.cpp
@@ -31,10 +31,15 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#include "main.h"
|
||||
#include "mesh-pb-constants.h"
|
||||
#include "screen.h"
|
||||
#include "utils.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
|
||||
@@ -50,6 +55,8 @@ static FrameCallback normalFrames[MAX_NUM_NODES + NUM_EXTRA_FRAMES];
|
||||
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 };
|
||||
|
||||
static void drawBootScreen(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y)
|
||||
{
|
||||
// draw an xbm image.
|
||||
@@ -60,20 +67,33 @@ static void drawBootScreen(OLEDDisplay *display, OLEDDisplayUiState *state, int1
|
||||
display->setFont(ArialMT_Plain_16);
|
||||
display->setTextAlignment(TEXT_ALIGN_CENTER);
|
||||
display->drawString(64 + x, SCREEN_HEIGHT - FONT_HEIGHT_16, "meshtastic.org");
|
||||
display->setFont(ArialMT_Plain_10);
|
||||
const char *region = xstr(HW_VERSION);
|
||||
if(*region && region[3] == '-') // Skip past 1.0- in the 1.0-EU865 string
|
||||
region += 4;
|
||||
char buf[16];
|
||||
snprintf(buf, sizeof(buf), "%s", xstr(APP_VERSION)); // Note: we don't bother printing region or now, it makes the string too long
|
||||
display->drawString(SCREEN_WIDTH - 20, 0, buf);
|
||||
}
|
||||
|
||||
static void drawFrameBluetooth(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y)
|
||||
{
|
||||
display->setTextAlignment(TEXT_ALIGN_CENTER);
|
||||
display->setFont(ArialMT_Plain_16);
|
||||
display->drawString(64 + x, 2 + y, "Bluetooth");
|
||||
display->drawString(64 + x, y, "Bluetooth");
|
||||
|
||||
display->setFont(ArialMT_Plain_10);
|
||||
display->drawString(64 + x, SCREEN_HEIGHT - FONT_HEIGHT + y, "Enter this code");
|
||||
display->drawString(64 + x, FONT_HEIGHT + y + 2, "Enter this code");
|
||||
|
||||
display->setTextAlignment(TEXT_ALIGN_CENTER);
|
||||
display->setFont(ArialMT_Plain_24);
|
||||
display->drawString(64 + x, 22 + y, btPIN);
|
||||
display->drawString(64 + x, 26 + y, btPIN);
|
||||
|
||||
display->setFont(ArialMT_Plain_10);
|
||||
char buf[30];
|
||||
const char *name = "Name: ";
|
||||
strcpy(buf,name);
|
||||
strcat(buf,getDeviceName());
|
||||
display->drawString(64 + x, 48 + y, buf);
|
||||
}
|
||||
|
||||
/// Draw the last text message we received
|
||||
@@ -152,6 +172,47 @@ static uint32_t drawRows(OLEDDisplay *display, int16_t x, int16_t y, const char
|
||||
return yo;
|
||||
}
|
||||
|
||||
// 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 };
|
||||
// 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) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
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) {
|
||||
char usersString[20];
|
||||
sprintf(usersString, "%d/%d", nodesOnline, nodesTotal);
|
||||
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; }
|
||||
}
|
||||
|
||||
/// Ported from my old java code, returns distance in meters along the globe
|
||||
/// surface (by magic?)
|
||||
static float latLongToMeter(double lat_a, double lng_a, double lat_b, double lng_b)
|
||||
@@ -291,6 +352,30 @@ 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)
|
||||
{
|
||||
// display->drawXbm(compassX, compassY, compass_width, compass_height,
|
||||
// (const uint8_t *)compass_bits);
|
||||
|
||||
Point tip(0.0f, 0.5f), tail(0.0f, -0.5f); // pointing up initially
|
||||
float arrowOffsetX = 0.2f, arrowOffsetY = 0.2f;
|
||||
Point leftArrow(tip.x - arrowOffsetX, tip.y - arrowOffsetY), rightArrow(tip.x + arrowOffsetX, tip.y - arrowOffsetY);
|
||||
|
||||
Point *points[] = {&tip, &tail, &leftArrow, &rightArrow};
|
||||
|
||||
for (int i = 0; i < 4; i++) {
|
||||
points[i]->rotate(headingRadian);
|
||||
points[i]->scale(COMPASS_DIAM * 0.6);
|
||||
points[i]->translate(compassX, compassY);
|
||||
}
|
||||
drawLine(display, tip, tail);
|
||||
drawLine(display, leftArrow, tip);
|
||||
drawLine(display, rightArrow, tip);
|
||||
|
||||
display->drawCircle(compassX, compassY, COMPASS_DIAM / 2);
|
||||
}
|
||||
|
||||
/// Convert an integer GPS coords to a floating point
|
||||
#define DegD(i) (i * 1e-7)
|
||||
|
||||
@@ -324,7 +409,7 @@ static void drawNodeInfo(OLEDDisplay *display, OLEDDisplayUiState *state, int16_
|
||||
const char *username = node->has_user ? node->user.long_name : "Unknown Name";
|
||||
|
||||
static char signalStr[20];
|
||||
snprintf(signalStr, sizeof(signalStr), "Signal: %.0f", node->snr);
|
||||
snprintf(signalStr, sizeof(signalStr), "Signal: %d%%", clamp((int)((node->snr + 10) * 5), 0, 100));
|
||||
|
||||
uint32_t agoSecs = sinceLastSeen(node);
|
||||
static char lastStr[20];
|
||||
@@ -335,15 +420,17 @@ static void drawNodeInfo(OLEDDisplay *display, OLEDDisplayUiState *state, int16_
|
||||
else
|
||||
snprintf(lastStr, sizeof(lastStr), "%u hours ago", agoSecs / 60 / 60);
|
||||
|
||||
static float simRadian;
|
||||
simRadian += 0.1; // For testing, have the compass spin unless both
|
||||
// locations are valid
|
||||
|
||||
static char distStr[20];
|
||||
*distStr = 0; // might not have location data
|
||||
float headingRadian = simRadian;
|
||||
float headingRadian;
|
||||
NodeInfo *ourNode = nodeDB.getNode(nodeDB.getNodeNum());
|
||||
if (ourNode && hasPosition(ourNode) && hasPosition(node)) {
|
||||
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;
|
||||
|
||||
if (ourNode && hasPosition(ourNode) && hasPosition(node)) { // display direction toward node
|
||||
Position &op = ourNode->position, &p = node->position;
|
||||
float d = latLongToMeter(DegD(p.latitude_i), DegD(p.longitude_i), DegD(op.latitude_i), DegD(op.longitude_i));
|
||||
if (d < 2000)
|
||||
@@ -356,35 +443,14 @@ static void drawNodeInfo(OLEDDisplay *display, OLEDDisplayUiState *state, int16_
|
||||
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;
|
||||
} else {
|
||||
drawCompass(display, compassX, compassY, headingRadian);
|
||||
} else { // 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->drawCircle(compassX, compassY, COMPASS_DIAM / 2);
|
||||
}
|
||||
|
||||
const char *fields[] = {username, distStr, signalStr, lastStr, NULL};
|
||||
drawColumns(display, x, y, fields);
|
||||
|
||||
// coordinates for the center of the compass
|
||||
int16_t compassX = x + SCREEN_WIDTH - COMPASS_DIAM / 2 - 1, compassY = y + SCREEN_HEIGHT / 2;
|
||||
// display->drawXbm(compassX, compassY, compass_width, compass_height,
|
||||
// (const uint8_t *)compass_bits);
|
||||
|
||||
Point tip(0.0f, 0.5f), tail(0.0f, -0.5f); // pointing up initially
|
||||
float arrowOffsetX = 0.2f, arrowOffsetY = 0.2f;
|
||||
Point leftArrow(tip.x - arrowOffsetX, tip.y - arrowOffsetY), rightArrow(tip.x + arrowOffsetX, tip.y - arrowOffsetY);
|
||||
|
||||
Point *points[] = {&tip, &tail, &leftArrow, &rightArrow};
|
||||
|
||||
for (int i = 0; i < 4; i++) {
|
||||
points[i]->rotate(headingRadian);
|
||||
points[i]->scale(COMPASS_DIAM * 0.6);
|
||||
points[i]->translate(compassX, compassY);
|
||||
}
|
||||
drawLine(display, tip, tail);
|
||||
drawLine(display, leftArrow, tip);
|
||||
drawLine(display, rightArrow, tip);
|
||||
|
||||
display->drawCircle(compassX, compassY, COMPASS_DIAM / 2);
|
||||
}
|
||||
|
||||
#if 0
|
||||
@@ -452,6 +518,9 @@ void Screen::setup()
|
||||
// Store a pointer to Screen so we can get to it from static functions.
|
||||
ui.getUiState()->userData = this;
|
||||
|
||||
// Set the utf8 conversion function
|
||||
dispdev.setFontTableLookupFunction(customFontTableLookup);
|
||||
|
||||
// Add frames.
|
||||
static FrameCallback bootFrames[] = {drawBootScreen};
|
||||
static const int bootFrameCount = sizeof(bootFrames) / sizeof(bootFrames[0]);
|
||||
@@ -644,35 +713,38 @@ void DebugInfo::drawFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16
|
||||
// The coordinates define the left starting point of the text
|
||||
display->setTextAlignment(TEXT_ALIGN_LEFT);
|
||||
|
||||
char usersStr[20];
|
||||
char channelStr[20];
|
||||
char batStr[20];
|
||||
char gpsStr[20];
|
||||
{
|
||||
LockGuard guard(&lock);
|
||||
snprintf(usersStr, sizeof(usersStr), "Users %d/%d", nodesOnline, nodesTotal);
|
||||
snprintf(channelStr, sizeof(channelStr), "%s", channelName.c_str());
|
||||
if (powerStatus.haveBattery) {
|
||||
// TODO: draw a battery icon instead of letter "B".
|
||||
int batV = powerStatus.batteryVoltageMv / 1000;
|
||||
int batCv = (powerStatus.batteryVoltageMv % 1000) / 10;
|
||||
snprintf(batStr, sizeof(batStr), "B %01d.%02dV%c%c", batV, batCv, powerStatus.charging ? '+' : ' ',
|
||||
powerStatus.usb ? 'U' : ' ');
|
||||
} else {
|
||||
snprintf(batStr, sizeof(batStr), "%s", powerStatus.usb ? "USB" : "");
|
||||
}
|
||||
snprintf(channelStr, sizeof(channelStr), "#%s", channelName.c_str());
|
||||
|
||||
if (!gpsStatus.empty()) {
|
||||
snprintf(gpsStr, sizeof(gpsStr), "GPS %s", gpsStatus.c_str());
|
||||
} else {
|
||||
gpsStr[0] = '\0'; // Just show empty string.
|
||||
}
|
||||
// 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);
|
||||
// Display nodes status
|
||||
drawNodes(display, x + (SCREEN_WIDTH * 0.25), y + 2, nodesOnline, nodesTotal);
|
||||
// Display GPS status
|
||||
drawGPS(display, x + (SCREEN_WIDTH * 0.66), y + 2, gps);
|
||||
}
|
||||
|
||||
const char *fields[] = {batStr, gpsStr, usersStr, channelStr, nullptr};
|
||||
uint32_t yo = drawRows(display, x, y, fields);
|
||||
const char *fields[] = {channelStr, nullptr};
|
||||
uint32_t yo = drawRows(display, x, y + FONT_HEIGHT, fields);
|
||||
|
||||
display->drawLogBuffer(x, yo);
|
||||
}
|
||||
|
||||
//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);
|
||||
dispdev.display();
|
||||
dispdev.setBrightness(brightness);
|
||||
}
|
||||
|
||||
} // namespace meshtastic
|
||||
|
||||
44
src/screen.h
44
src/screen.h
@@ -3,7 +3,12 @@
|
||||
#include <cstring>
|
||||
|
||||
#include <OLEDDisplayUi.h>
|
||||
|
||||
#ifdef USE_SH1106
|
||||
#include <SH1106Wire.h>
|
||||
#else
|
||||
#include <SSD1306Wire.h>
|
||||
#endif
|
||||
|
||||
#include "PeriodicTask.h"
|
||||
#include "TypedQueue.h"
|
||||
@@ -112,6 +117,10 @@ class Screen : public PeriodicTask
|
||||
/// Handles a button press.
|
||||
void onPress() { enqueueCmd(CmdItem{.cmd = Cmd::ON_PRESS}); }
|
||||
|
||||
// Implementation to Adjust Brightness
|
||||
void adjustBrightness();
|
||||
int brightness = 150;
|
||||
|
||||
/// Starts showing the Bluetooth PIN screen.
|
||||
//
|
||||
// Switches over to a static frame showing the Bluetooth pairing screen
|
||||
@@ -144,6 +153,37 @@ class Screen : public PeriodicTask
|
||||
}
|
||||
}
|
||||
|
||||
/// Overrides the default utf8 character conversion, to replace empty space with question marks
|
||||
static char customFontTableLookup(const uint8_t ch) {
|
||||
// UTF-8 to font table index converter
|
||||
// Code form http://playground.arduino.cc/Main/Utf8ascii
|
||||
static uint8_t LASTCHAR;
|
||||
static bool SKIPREST; // Only display a single unconvertable-character symbol per sequence of unconvertable characters
|
||||
|
||||
if (ch < 128) { // Standard ASCII-set 0..0x7F handling
|
||||
LASTCHAR = 0;
|
||||
SKIPREST = false;
|
||||
return ch;
|
||||
}
|
||||
|
||||
uint8_t last = LASTCHAR; // get last char
|
||||
LASTCHAR = ch;
|
||||
|
||||
switch (last) { // conversion depnding on first UTF8-character
|
||||
case 0xC2: { SKIPREST = false; return (uint8_t) ch; }
|
||||
case 0xC3: { SKIPREST = false; return (uint8_t) (ch | 0xC0); }
|
||||
}
|
||||
|
||||
// We want to strip out prefix chars for two-byte char formats
|
||||
if (ch == 0xC2 || ch == 0xC3 || ch == 0x82) return (uint8_t) 0;
|
||||
|
||||
// If we already returned an unconvertable-character symbol for this unconvertable-character sequence, return NULs for the rest of it
|
||||
if (SKIPREST) return (uint8_t) 0;
|
||||
SKIPREST = true;
|
||||
|
||||
return (uint8_t) 191; // otherwise: return ¿ if character can't be converted (note that the font map we're using doesn't stick to standard EASCII codes)
|
||||
}
|
||||
|
||||
/// Returns a handle to the DebugInfo screen.
|
||||
//
|
||||
// Use this handle to set things like battery status, user count, GPS status, etc.
|
||||
@@ -211,7 +251,11 @@ class Screen : public PeriodicTask
|
||||
/// Holds state for debug information
|
||||
DebugInfo debugInfo;
|
||||
/// Display device
|
||||
#ifdef USE_SH1106
|
||||
SH1106Wire dispdev;
|
||||
#else
|
||||
SSD1306Wire dispdev;
|
||||
#endif
|
||||
/// UI helper for rendering to frames and switching between them
|
||||
OLEDDisplayUi ui;
|
||||
};
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
#include "esp_pm.h"
|
||||
#include "rom/rtc.h"
|
||||
#include <driver/rtc_io.h>
|
||||
#include <driver/uart.h>
|
||||
|
||||
#include "BluetoothUtil.h"
|
||||
|
||||
@@ -111,8 +112,7 @@ void initDeepSleep()
|
||||
#endif
|
||||
}
|
||||
|
||||
/// return true if sleep is allowed
|
||||
static bool doPreflightSleep()
|
||||
bool doPreflightSleep()
|
||||
{
|
||||
if (preflightSleep.notifyObservers(NULL) != 0)
|
||||
return false; // vetoed
|
||||
@@ -129,7 +129,7 @@ static void waitEnterSleep()
|
||||
|
||||
if (millis() - now > 30 * 1000) { // If we wait too long just report an error and go to sleep
|
||||
recordCriticalError(ErrSleepEnterWait);
|
||||
ESP.restart(); // FIXME - for now we just restart, need to fix bug #167
|
||||
assert(0); // FIXME - for now we just restart, need to fix bug #167
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -257,6 +257,17 @@ esp_sleep_wakeup_cause_t doLightSleep(uint64_t sleepMsec) // FIXME, use a more r
|
||||
gpio_pullup_en((gpio_num_t)BUTTON_PIN);
|
||||
#endif
|
||||
|
||||
#ifdef SERIAL0_RX_GPIO
|
||||
// We treat the serial port as a GPIO for a fast/low power way of waking, if we see a rising edge that means
|
||||
// someone started to send something
|
||||
|
||||
// Alas - doesn't work reliably, instead need to use the uart specific version (which burns a little power)
|
||||
// FIXME: gpio 3 is RXD for serialport 0 on ESP32
|
||||
// Send a few Z characters to wake the port
|
||||
gpio_wakeup_enable((gpio_num_t)SERIAL0_RX_GPIO, GPIO_INTR_LOW_LEVEL);
|
||||
// uart_set_wakeup_threshold(UART_NUM_0, 3);
|
||||
// esp_sleep_enable_uart_wakeup(0);
|
||||
#endif
|
||||
#ifdef BUTTON_PIN
|
||||
gpio_wakeup_enable((gpio_num_t)BUTTON_PIN, GPIO_INTR_LOW_LEVEL); // when user presses, this button goes low
|
||||
#endif
|
||||
@@ -278,9 +289,7 @@ esp_sleep_wakeup_cause_t doLightSleep(uint64_t sleepMsec) // FIXME, use a more r
|
||||
|
||||
return cause;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if 0
|
||||
// not legal on the stock android ESP build
|
||||
|
||||
/**
|
||||
@@ -295,7 +304,7 @@ void enableModemSleep()
|
||||
static esp_pm_config_esp32_t config; // filled with zeros because bss
|
||||
|
||||
config.max_freq_mhz = CONFIG_ESP32_DEFAULT_CPU_FREQ_MHZ;
|
||||
config.min_freq_mhz = 10; // 10Mhz is minimum recommended
|
||||
config.min_freq_mhz = 20; // 10Mhz is minimum recommended
|
||||
config.light_sleep_enable = false;
|
||||
DEBUG_MSG("Sleep request result %x\n", esp_pm_configure(&config));
|
||||
}
|
||||
|
||||
@@ -19,6 +19,9 @@ void initDeepSleep();
|
||||
void setCPUFast(bool on);
|
||||
void setLed(bool ledOn);
|
||||
|
||||
/** return true if sleep is allowed right now */
|
||||
bool doPreflightSleep();
|
||||
|
||||
extern int bootCount;
|
||||
|
||||
// is bluetooth sw currently running?
|
||||
@@ -31,4 +34,6 @@ extern Observable<void *> preflightSleep;
|
||||
extern Observable<void *> notifySleep;
|
||||
|
||||
/// Called to tell observers we are now entering (deep) sleep and you should prepare. Must return 0
|
||||
extern Observable<void *> notifyDeepSleep;
|
||||
extern Observable<void *> notifyDeepSleep;
|
||||
|
||||
void enableModemSleep();
|
||||
7
src/utils.h
Normal file
7
src/utils.h
Normal file
@@ -0,0 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
/// C++ v17+ clamp function, limits a given value to a range defined by lo and hi
|
||||
template<class T>
|
||||
constexpr const T& clamp( const T& v, const T& lo, const T& hi ) {
|
||||
return (v < lo) ? lo : (hi < v) ? hi : v;
|
||||
}
|
||||
@@ -137,6 +137,7 @@ static const uint8_t SCK = PIN_SPI_SCK;
|
||||
// #define SX1262_ANT_SW (32 + 10)
|
||||
#define SX1262_RXEN (22)
|
||||
#define SX1262_TXEN (24)
|
||||
#define SX1262_E22 // Indicates this SX1262 is inside of an ebyte E22 module and special config should be done for that
|
||||
|
||||
// ERC12864-10 LCD
|
||||
#define ERC12864_CS (32 + 4)
|
||||
|
||||
Reference in New Issue
Block a user